pax_global_header00006660000000000000000000000064150350437070014516gustar00rootroot0000000000000052 comment=df6300e0ba3e90058f90b09c4d646279366d3516 rtabmap-0.22.1/000077500000000000000000000000001503504370700132265ustar00rootroot00000000000000rtabmap-0.22.1/.appveyor.yml000066400000000000000000000150441503504370700157000ustar00rootroot00000000000000 branches: only: - master - devel os: Visual Studio 2015 clone_folder: c:\projects\rtabmap platform: x64 configuration: Release init: - cmake --version - call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 install: # To download from google drive - set PATH=C:\Python38-x64;C:\Python38-x64\Scripts;%PATH% - ps: py -m pip --disable-pip-version-check install gdown>=5.1.0 # Qt - set QTDIR=C:\Qt\5.10.1\msvc2015_64 # make sure Qt bin path is before cmake bin path to avoid copying qt5 dlls from cmake before qt installation - set PATH=%QTDIR%\bin;%PATH% # Boost - set PATH=%PATH%;C:\Libraries\boost_1_62_0\lib64-msvc-14.0 # Openni2 - ps: wget 'https://dl.dropboxusercontent.com/s/d98jv79l6oy9fxz/OpenNI2.exe?dl=0' -outfile OpenNI2.exe - cmd: OpenNI2.exe -o"C:\Program Files" -y - ECHO "Installed OpenNI2:" - ps: "ls \"C:/Program Files/OpenNI2\"" - set PATH=%PATH%;C:\Program Files\OpenNI2\Redist - set OPENNI2_INCLUDE64=C:\Program Files\OpenNI2\Include - set OPENNI2_LIB64=C:\Program Files\OpenNI2\Lib - set OPENNI2_REDIST64=C:\Program Files\OpenNI2\Redist # OpenCV #- appveyor-retry appveyor DownloadFile http://downloads.sourceforge.net/project/opencvlibrary/4.5.2/opencv-4.5.2-vc14_vc15.exe #- cmd: opencv-4.5.2-vc14_vc15.exe -o"C:\Program Files" -y #- ECHO "Installed OpenCV:" #- ps: "ls \"C:/Program Files/opencv/build\"" #- set PATH=%PATH%;C:\Program Files\opencv\build\x64\vc14\bin - ps: wget 'https://dl.dropboxusercontent.com/s/o6ofn491bc0jso1/opencv450_vc14.exe?dl=0' -outfile opencv.exe - cmd: opencv.exe -o"C:\Program Files" -y - ECHO "Installed OpenCV:" - ps: "ls \"C:/Program Files/opencv\"" - set PATH=%PATH%;C:\Program Files\opencv\x64\vc14\bin # VTK (including QVTK) - ps: wget 'https://dl.dropboxusercontent.com/s/1l33b5l3f3y52gf/VTK-6_3-msvc140.exe?dl=0' -outfile VTK-6_3.exe - cmd: VTK-6_3.exe -o"C:\Program Files" -y - ECHO "Installed VTK:" - ps: "ls \"C:/Program Files/VTK\"" - set PATH=%PATH%;C:\Program Files\VTK\bin # QHull - ps: wget 'https://dl.dropboxusercontent.com/s/9widnk9msdsh2b8/Qhull-msvc140.exe?dl=0' -outfile Qhull.exe - cmd: Qhull.exe -o"C:\Program Files" -y - ECHO "Installed QHull:" - ps: "ls \"C:/Program Files/Qhull\"" - set PATH=%PATH%;C:\Program Files\Qhull\bin # FLANN - ps: wget 'https://dl.dropboxusercontent.com/s/7k58jbmqa51sxmh/FLANN-msvc140.exe?dl=0' -outfile FLANN.exe - cmd: FLANN.exe -o"C:\Program Files" -y - ECHO "Installed FLANN:" - ps: "ls \"C:/Program Files/FLANN\"" - set PATH=%PATH%;C:\Program Files\FLANN\bin # Eigen - ps: wget 'https://dl.dropboxusercontent.com/s/3v6i9i8dxj4o8ji/Eigen.exe?dl=0' -outfile Eigen.exe - cmd: Eigen.exe -o"C:\Program Files" -y - ECHO "Installed Eigen:" - ps: "ls \"C:/Program Files/Eigen\"" # PCL - ps: wget 'https://dl.dropboxusercontent.com/s/2iayr4lyqa50i9j/PCL_181_August2018_x64_vc14.exe?dl=0' -outfile PCL_1.8.1.exe - cmd: PCL_1.8.1.exe -o"C:\Program Files" -y - ECHO "Installed PCL:" - ps: "ls \"C:/Program Files/PCL\"" - set PATH=%PATH%;C:\Program Files\PCL\bin # zlib - ps: gdown -q 0B46akLGdg-uaYm9MTTI4MUtUcmc - ps: Expand-Archive zlib-1.2.8-vc2010-x64.zip -DestinationPath 'C:\Program Files' - ECHO "Installed zlib:" - ps: "ls \"C:/Program Files/zlib\"" - set PATH=%PATH%;C:\Program Files\zlib\bin # g2o - ps: wget 'https://dl.dropboxusercontent.com/s/ht74s5pa21wokzw/g2o.exe?dl=0' -outfile g2o.exe - cmd: g2o.exe -o"C:\Program Files" -y - ECHO "Installed g2o:" - ps: "ls \"C:/Program Files/g2o\"" - set PATH=%PATH%;C:\Program Files\g2o\bin # GTSAM - ps: wget 'https://dl.dropboxusercontent.com/s/0fpr6r4cgsqmvhf/GTSAM-4_0_0_alpha2-msvc140.exe?dl=0' -outfile GTSAM.exe - cmd: GTSAM.exe -o"C:\Program Files" -y - ECHO "Installed GTSAM:" - ps: "ls \"C:/Program Files/GTSAM\"" - set PATH=%PATH%;C:\Program Files\GTSAM\bin # OctoMap - ps: wget 'https://dl.dropboxusercontent.com/s/6jpxu0nm8ne6e54/octomap_x64_vc14.exe?dl=0' -outfile octomap.exe - cmd: octomap.exe -o"C:\Program Files" -y - ECHO "Installed OctoMap:" - ps: "ls \"C:/Program Files/octomap-distribution\"" - set PATH=%PATH%;C:\Program Files\octomap-distribution\bin # CPU-TSDF - ps: wget 'https://dl.dropboxusercontent.com/s/mgges9va1uzxr0q/cpu_tsdf_sept2015_x64_vc14.exe?dl=0' -outfile cpu_tsdf.exe - cmd: cpu_tsdf.exe -o"C:\Program Files" -y - ECHO "Installed CPU-TSDF:" - ps: "ls \"C:/Program Files/cpu_tsdf\"" - set PATH=%PATH%;C:\Program Files\cpu_tsdf\bin # Open Chisel - ps: wget 'https://dl.dropboxusercontent.com/s/0aaphcde4acrinm/open_chisel_x64_vc14.exe?dl=0' -outfile open_chisel.exe - cmd: open_chisel.exe -o"C:\Program Files" -y - ECHO "Installed Open Chisel:" - ps: "ls \"C:/Program Files/open_chisel\"" - set PATH=%PATH%;C:\Program Files\open_chisel\bin # yaml-cpp - ps: wget 'https://dl.dropboxusercontent.com/s/22qfvftwj6zq8tj/yaml-cpp_x64_vc14.exe?dl=0' -outfile yaml-cpp.exe - cmd: yaml-cpp.exe -o"C:\Program Files" -y - ECHO "Installed yaml-cpp:" - ps: "ls \"C:/Program Files/yaml-cpp\"" # RealSense2 - ps: wget 'https://github.com/IntelRealSense/librealsense/releases/download/v2.40.0/Intel.RealSense.SDK-WIN10-2.40.0.2482.exe' -outfile realsense2.exe - cmd: realsense2.exe /VERYSILENT - ECHO "Installed RealSense2:" - ps: "ls \"C:/Program Files (x86)/Intel RealSense SDK 2.0\"" - set PATH=%PATH%;C:\Program Files (x86)\Intel RealSense SDK 2.0\bin\x64 - set RealSense2_ROOT_DIR=C:\Program Files (x86)\Intel RealSense SDK 2.0 # Kinect 4 Azure - ps: wget 'https://download.microsoft.com/download/3/d/6/3d6d9e99-a251-4cf3-8c6a-8e108e960b4b/Azure%20Kinect%20SDK%201.4.1.exe' -outfile azure.exe - cmd: azure.exe /quiet - ECHO "Installed Kinect For Azure:" - ps: "ls \"C:/Program Files/Azure Kinect SDK v1.4.1\"" - set PATH=%PATH%;C:\Program Files\Azure Kinect SDK v1.4.1\tools - set K4A_ROOT_DIR=C:\Program Files\Azure Kinect SDK v1.4.1 before_build: - cd c:\projects\rtabmap\build - ECHO %PROGRAMFILES% - ECHO %PATH% - cmake -G "Visual Studio 14 2015 Win64" -DOpenCV_DIR="C:\Program Files\opencv\build" -DPCL_DIR="C:\Program Files\PCL\cmake" -DCPUTSDF_DIR="C:\Program Files\cpu_tsdf\share\cpu_tsdf" -Dyaml-cpp_DIR="C:\Program Files\yaml-cpp\CMake" -DBUILD_AS_BUNDLE=ON .. after_build : - cmake --build . --config Release --target package artifacts: - path: build\RTABMap-* notifications: - provider: Email to: - matlabbe@gmail.com on_build_success: false on_build_failure: false on_build_status_changed: true rtabmap-0.22.1/.cproject000066400000000000000000001411131503504370700150410ustar00rootroot00000000000000 cmake -E chdir build/ cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE=Debug -D BUILD_TESTS=ON ../ true false true cmake -E chdir build/ cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE=Release -D BUILD_TESTS=ON ../ true false true cmake -E chdir build/ cmake -G "Unix Makefiles" -D CMAKE_BUILD_TYPE=Debug -D BUILD_TESTS=OFF ../ true false true cmake -E chdir build/ cmake -G "Unix Makefiles" -D CMAKE_BUILD_TYPE=Release -D BUILD_TESTS=OFF ../ true false true cmake -E chdir build/ cmake -G "NMake Makefiles" -D CMAKE_BUILD_TYPE=Debug -D BUILD_TESTS=ON ../ true false true cmake -G "NMake Makefiles" -D CMAKE_BUILD_TYPE=Release ../ true false true rtabmap-0.22.1/.devcontainer/000077500000000000000000000000001503504370700157655ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/android/000077500000000000000000000000001503504370700174055ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/android/Dockerfile000066400000000000000000000013421503504370700213770ustar00rootroot00000000000000FROM introlab3it/rtabmap:android-noble-deps # remove ubuntu user RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu RUN apt-get update && apt-get install -y sudo && \ apt-get clean && rm -rf /var/lib/apt/lists/ ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=1000 RUN set -ex && \ groupadd --gid ${USER_GID} ${USERNAME} && \ useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \ usermod -a -G sudo ${USERNAME} && \ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \ chmod 0440 /etc/sudoers.d/${USERNAME} RUN chmod +x /opt/android-sdk/tools/android RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc rtabmap-0.22.1/.devcontainer/android/devcontainer.json000066400000000000000000000011211503504370700227540ustar00rootroot00000000000000{ "build": { "dockerfile": "Dockerfile" }, "customizations": { "vscode": { "extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools", "vscjava.vscode-java-pack"] } }, "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind", "workspaceFolder": "/home/vscode/rtabmap", "postStartCommand": "./.devcontainer/android/init.sh", "settings": { "terminal.integrated.defaultProfile.linux": "bash" }, "remoteUser": "vscode", "runArgs": ["--privileged", "--network=host"] } rtabmap-0.22.1/.devcontainer/android/init.sh000077500000000000000000000015021503504370700207050ustar00rootroot00000000000000#!/bin/bash set -e echo "Running post-start initialization..." # copy required jars cp /opt/android/lib/*.jar app/android/libs/. mkdir -p build_android/arm64-v8a # resource tool cd build_android cmake -DANDROID_PREBUILD=ON .. make echo -e "\nTo build the APK, do (adjust API number):" echo -e '\nexport ANDROID_API=30 && cd build_android/arm64-v8a && cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=$ANDROID_API -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_EXAMPLES=OFF -DBUILD_TOOLS=OFF -DOpenCV_DIR=/opt/android/arm64-v8a/sdk/native/jni ../..\nmake -j6\n'rtabmap-0.22.1/.devcontainer/bionic/000077500000000000000000000000001503504370700172305ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/bionic/devcontainer.json000066400000000000000000000002731503504370700226060ustar00rootroot00000000000000{ "image": "introlab3it/rtabmap:18.04", "customizations": { "vscode": { "extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"] } } } rtabmap-0.22.1/.devcontainer/focal/000077500000000000000000000000001503504370700170515ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/focal/devcontainer.json000066400000000000000000000002731503504370700224270ustar00rootroot00000000000000{ "image": "introlab3it/rtabmap:20.04", "customizations": { "vscode": { "extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"] } } } rtabmap-0.22.1/.devcontainer/jammy/000077500000000000000000000000001503504370700171025ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/jammy/devcontainer.json000066400000000000000000000002731503504370700224600ustar00rootroot00000000000000{ "image": "introlab3it/rtabmap:22.04", "customizations": { "vscode": { "extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"] } } } rtabmap-0.22.1/.devcontainer/noble/000077500000000000000000000000001503504370700170645ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/noble/devcontainer.json000066400000000000000000000002731503504370700224420ustar00rootroot00000000000000{ "image": "introlab3it/rtabmap:24.04", "customizations": { "vscode": { "extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"] } } } rtabmap-0.22.1/.devcontainer/rolling/000077500000000000000000000000001503504370700174335ustar00rootroot00000000000000rtabmap-0.22.1/.devcontainer/rolling/Dockerfile000066400000000000000000000050431503504370700214270ustar00rootroot00000000000000 FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive # Install ROS2 RUN apt update && \ apt install software-properties-common -y && \ add-apt-repository universe && \ apt update && \ apt install curl -y && \ curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null && \ apt-get clean && rm -rf /var/lib/apt/lists/ # Install build dependencies RUN apt-get update && \ apt upgrade -y && \ apt-get install -y \ git \ wget \ libtbb-dev \ libproj-dev \ libpcl-dev \ liboctomap-dev \ libfreenect-dev \ ros-rolling-ros-base \ ros-dev-tools \ ros-rolling-cv-bridge \ ros-rolling-image-geometry \ ros-rolling-laser-geometry \ ros-rolling-pcl-conversions \ ros-rolling-rviz-common \ ros-rolling-rviz-rendering \ ros-rolling-rviz-default-plugins \ ros-rolling-pcl-ros \ ros-rolling-imu-filter-madgwick \ ros-rolling-image-transport \ ros-rolling-octomap-msgs \ ros-rolling-libg2o \ ros-rolling-gtsam \ ros-rolling-libpointmatcher \ ros-rolling-qt-gui-cpp \ ros-rolling-diagnostic-updater && \ apt-get clean && rm -rf /var/lib/apt/lists/ WORKDIR /root/ RUN rm /bin/sh && ln -s /bin/bash /bin/sh RUN echo -e '#!/bin/bash\nset -e\n\n# setup ros2 environment\nsource "/opt/ros/rolling/setup.bash" --\nexec "$@"' > /ros_entrypoint.sh RUN chmod +x /ros_entrypoint.sh ENTRYPOINT [ "/ros_entrypoint.sh" ] # ros2 seems not sourcing by default its multi-arch folders ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/ros/rolling/lib/x86_64-linux-gnu # For devcontainer # remove ubuntu user RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu RUN apt-get update && apt-get install -y sudo && \ apt-get clean && rm -rf /var/lib/apt/lists/ ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=1000 RUN set -ex && \ groupadd --gid ${USER_GID} ${USERNAME} && \ useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \ usermod -a -G sudo ${USERNAME} && \ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \ chmod 0440 /etc/sudoers.d/${USERNAME} RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc rtabmap-0.22.1/.devcontainer/rolling/devcontainer.json000066400000000000000000000007661503504370700230200ustar00rootroot00000000000000{ "build": { "dockerfile": "Dockerfile" }, "customizations": { "vscode": { "extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"] } }, "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind", "workspaceFolder": "/home/vscode/rtabmap", "settings": { "terminal.integrated.defaultProfile.linux": "bash" }, "remoteUser": "vscode", "runArgs": ["--privileged", "--network=host"] } rtabmap-0.22.1/.dockerignore000066400000000000000000000000201503504370700156720ustar00rootroot00000000000000build/* build_* rtabmap-0.22.1/.github/000077500000000000000000000000001503504370700145665ustar00rootroot00000000000000rtabmap-0.22.1/.github/workflows/000077500000000000000000000000001503504370700166235ustar00rootroot00000000000000rtabmap-0.22.1/.github/workflows/cmake-ros.yml000066400000000000000000000051421503504370700212310ustar00rootroot00000000000000name: CMake-ROS on: push: branches: - master pull_request: branches: - '**' env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix name: Build on ros ${{ matrix.ros_distribution }} and ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: ros_distribution: [ humble, jazzy, kilted, rolling ] include: - ros_distribution: 'humble' os: ubuntu-22.04 - ros_distribution: 'jazzy' os: ubuntu-24.04 - ros_distribution: 'kilted' os: ubuntu-24.04 - ros_distribution: 'rolling' os: ubuntu-24.04 steps: - name: Setup ROS2 # https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debs.html run: | sudo apt install software-properties-common sudo add-apt-repository universe sudo apt update && sudo apt install curl -y export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" sudo apt install /tmp/ros2-apt-source.deb sudo apt update - uses: ros-tooling/setup-ros@v0.7 with: required-ros-distributions: ${{ matrix.ros_distribution }} - uses: actions/checkout@v4 - name: Install dependencies run: | source /opt/ros/${{ matrix.ros_distribution }}/setup.bash rosdep update rosdep install --from-paths ${{github.workspace}} -y - name: Configure CMake run: | source /opt/ros/${{ matrix.ros_distribution }}/setup.bash cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Info working-directory: ${{github.workspace}}/build/bin run: | source /opt/ros/${{ matrix.ros_distribution }}/setup.bash ./rtabmap-console --version rtabmap-0.22.1/.github/workflows/cmake.yml000066400000000000000000000027121503504370700204300ustar00rootroot00000000000000name: CMake on: push: branches: - master pull_request: branches: - '**' env: BUILD_TYPE: Release jobs: build: name: ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-24.04, ubuntu-22.04] include: - os: ubuntu-22.04 extra_deps: "libunwind-dev libceres-dev" extra_cmake_def: "" - os: ubuntu-24.04 extra_deps: "libg2o-dev libceres-dev" extra_cmake_def: "-DWITH_CERES=ON" steps: - name: Install dependencies run: | DEBIAN_FRONTEND=noninteractive sudo apt-get update sudo apt-get -y install libopencv-dev libpcl-dev git cmake software-properties-common libyaml-cpp-dev ${{ matrix.extra_deps }} - uses: actions/checkout@v4 - name: Configure CMake run: | cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{ matrix.extra_cmake_def }} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Info working-directory: ${{github.workspace}}/build/bin run: | ./rtabmap-console --version # - name: Test # working-directory: ${{github.workspace}}/build # # Execute tests defined by the CMake configuration. # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # run: ctest -C ${{env.BUILD_TYPE}} rtabmap-0.22.1/.github/workflows/docker.yml000066400000000000000000000141671503504370700206260ustar00rootroot00000000000000name: docker on: push: branches: - 'master' jobs: docker_deps: # Disabling ###-deps step from CI because it is too flaky (seg faults, arm64 build timeout...) # Only way I was able to build all images is to do it from a ubuntu 20.04 computer with: # $ sudo add-apt-repository ppa:canonical-server/server-backports # $ sudo apt-get update # $ sudo apt-get upgrade qemu-user-static # $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -c yes # More info: https://github.com/introlab/rtabmap/issues/1454 # if: false runs-on: ubuntu-latest strategy: fail-fast: false matrix: docker_tag: [focal-deps, jammy-deps, noble-deps, noble-kilted-deps] include: - docker_tag: focal-deps docker_tags: | introlab3it/rtabmap:focal-deps docker_platforms: | linux/amd64 linux/arm64 docker_path: 'focal/deps' - docker_tag: jammy-deps docker_tags: | introlab3it/rtabmap:jammy-deps docker_platforms: | linux/amd64 linux/arm64 docker_path: 'jammy/deps' - docker_tag: noble-deps docker_tags: | introlab3it/rtabmap:noble-deps docker_platforms: | linux/amd64 linux/arm64 docker_path: 'noble/deps' - docker_tag: noble-kilted-deps docker_tags: | introlab3it/rtabmap:noble-kilted-deps docker_platforms: | linux/amd64 linux/arm64 docker_path: 'noble-kilted/deps' steps: - name: Checkout uses: actions/checkout@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: platforms: all - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v6 with: context: . push: true platforms: ${{ matrix.docker_platforms }} file: ./docker/${{ matrix.docker_path }}/Dockerfile tags: ${{ matrix.docker_tags }} cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_tag }} cache-to: type=inline docker: needs: docker_deps runs-on: ubuntu-latest strategy: fail-fast: false matrix: docker_tag: [bionic, focal, jammy, noble, noble-kilted, android23, android24, android26, android30] include: - docker_tag: bionic docker_tags: | introlab3it/rtabmap:bionic introlab3it/rtabmap:18.04 docker_args: | NOT_USED=0 docker_platforms: | linux/amd64 linux/arm64 docker_path: 'bionic' - docker_tag: focal docker_tags: | introlab3it/rtabmap:focal introlab3it/rtabmap:20.04 introlab3it/rtabmap:latest docker_args: | NOT_USED=0 docker_platforms: | linux/amd64 linux/arm64 docker_path: 'focal' - docker_tag: jammy docker_tags: | introlab3it/rtabmap:jammy introlab3it/rtabmap:22.04 docker_args: | NOT_USED=0 docker_platforms: | linux/amd64 linux/arm64 docker_path: 'jammy' - docker_tag: noble docker_tags: | introlab3it/rtabmap:noble introlab3it/rtabmap:24.04 docker_args: | NOT_USED=0 docker_platforms: | linux/amd64 linux/arm64 docker_path: 'noble' - docker_tag: noble-kilted docker_tags: | introlab3it/rtabmap:noble-kilted docker_args: | NOT_USED=0 docker_platforms: | linux/amd64 linux/arm64 docker_path: 'noble-kilted' - docker_tag: android23 docker_tags: | introlab3it/rtabmap:android23 introlab3it/rtabmap:tango docker_args: | API_VERSION=23 docker_platforms: | linux/amd64 docker_path: 'noble/android/rtabmap_apiXX' - docker_tag: android24 docker_tags: | introlab3it/rtabmap:android24 docker_args: | API_VERSION=24 docker_platforms: | linux/amd64 docker_path: 'noble/android/rtabmap_apiXX' - docker_tag: android26 docker_tags: | introlab3it/rtabmap:android26 docker_args: | API_VERSION=26 docker_platforms: | linux/amd64 docker_path: 'noble/android/rtabmap_apiXX' - docker_tag: android30 docker_tags: | introlab3it/rtabmap:android30 docker_args: | API_VERSION=30 docker_platforms: | linux/amd64 docker_path: 'noble/android/rtabmap_apiXX' steps: - name: Checkout uses: actions/checkout@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: platforms: all - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v6 with: context: . push: true platforms: ${{ matrix.docker_platforms }} file: ./docker/${{ matrix.docker_path }}/Dockerfile build-args: | ${{ matrix.docker_args }} tags: ${{ matrix.docker_tags }} cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_tag }} cache-to: type=inline rtabmap-0.22.1/.github/workflows/scheduled-stats.yml000066400000000000000000000010111503504370700224330ustar00rootroot00000000000000name: RTAB-Map Scheduled Stats Extraction From GitHub on: workflow_dispatch: schedule: - cron: '0 5 * * *' jobs: get_stats: runs-on: ubuntu-latest steps: - name: Update Stats uses: introlab/github-stats-action@v1 with: github-stats-token: ${{ secrets.STATS_TOKEN }} google-application-credentials: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} spreadsheet-id: ${{ secrets.SPREADSHEET_ID }} rtabmap-0.22.1/.gitignore000066400000000000000000000003271503504370700152200ustar00rootroot00000000000000/lib .DS_Store .settings/language.settings.xml .idea/ .vscode cmake-build-debug/ app/android/.classpath app/android/.project app/android/AndroidManifest.xml app/android/res/raw/ compile_flags.txt tags build_* *.bak rtabmap-0.22.1/.project000066400000000000000000000046471503504370700147100ustar00rootroot00000000000000 rtabmap org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments -j4 -C ${ProjDirPath}/build VERBOSE=true org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.buildLocation ${workspace_loc:/RTAB-Map} org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd false org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.core.cnature org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature rtabmap-0.22.1/.settings/000077500000000000000000000000001503504370700151445ustar00rootroot00000000000000rtabmap-0.22.1/.settings/org.eclipse.cdt.codan.core.prefs000066400000000000000000000241271503504370700232100ustar00rootroot00000000000000eclipse.preferences.version=1 org.eclipse.cdt.codan.checkers.errnoreturn=Warning org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.checkers.errreturnvalue=Error org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.checkers.noreturn=Error org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>true,empty_case_param\=>false} org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} rtabmap-0.22.1/.settings/org.eclipse.cdt.core.prefs000066400000000000000000000002621503504370700221170ustar00rootroot00000000000000eclipse.preferences.version=1 environment/project/0.1790260204.1906025362.1064002412/append=true environment/project/0.1790260204.1906025362.1064002412/appendContributed=true rtabmap-0.22.1/CMakeLists.txt000066400000000000000000002003001503504370700157610ustar00rootroot00000000000000# Top-Level CmakeLists.txt cmake_minimum_required(VERSION 3.14) PROJECT( RTABMap ) SET(PROJECT_PREFIX rtabmap) # Catkin doesn't support multiarch library path, # fix to "lib" if not set by user. OPTION(MULTI_ARCH "Activate multi-arch lib directory (debian)" OFF) IF(NOT MULTI_ARCH AND NOT DEFINED CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR "lib") ENDIF(NOT MULTI_ARCH AND NOT DEFINED CMAKE_INSTALL_LIBDIR) INCLUDE(GNUInstallDirs) ####### local cmake modules ####### SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules") ####################### # VERSION ####################### SET(RTABMAP_MAJOR_VERSION 0) SET(RTABMAP_MINOR_VERSION 22) SET(RTABMAP_PATCH_VERSION 1) SET(RTABMAP_VERSION ${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION}) SET(PROJECT_VERSION "${RTABMAP_VERSION}") SET(PROJECT_VERSION_MAJOR ${RTABMAP_MAJOR_VERSION}) SET(PROJECT_VERSION_MINOR ${RTABMAP_MINOR_VERSION}) SET(PROJECT_VERSION_PATCH ${RTABMAP_PATCH_VERSION}) SET(PROJECT_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") ####### COMPILATION PARAMS ####### # In case of Makefiles if the user does not setup CMAKE_BUILD_TYPE, assume it's Release: IF(${CMAKE_GENERATOR} MATCHES ".*Makefiles") IF("${CMAKE_BUILD_TYPE}" STREQUAL "") set(CMAKE_BUILD_TYPE Release) ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "") ENDIF(${CMAKE_GENERATOR} MATCHES ".*Makefiles") SET(MOBILE_BUILD OFF) IF(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS") SET(MOBILE_BUILD ON) ENDIF(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS") MESSAGE(STATUS "MOBILE_BUILD=${MOBILE_BUILD}") IF(NOT MOBILE_BUILD) SET(CMAKE_DEBUG_POSTFIX "d") option(FLANN_KDTREE_MEM_OPT "Disable multi-threaded FLANN kd-tree to minimize memory allocations" OFF) ELSE() option(FLANN_KDTREE_MEM_OPT "Disable multi-threaded FLANN kd-tree to minimize memory allocations" ON) ENDIF() IF(FLANN_KDTREE_MEM_OPT) ADD_DEFINITIONS("-DFLANN_KDTREE_MEM_OPT") ENDIF(FLANN_KDTREE_MEM_OPT) IF(WIN32 AND NOT MINGW) ADD_DEFINITIONS("-DNOMINMAX") ADD_DEFINITIONS("-wd4100 -wd4127 -wd4150 -wd4191 -wd4242 -wd4244 -wd4251 -wd4305 -wd4365 -wd4512 -wd4514 -wd4548 -wd4571 -wd4619 -wd4625 -wd4626 -wd4628 -wd4668 -wd4710 -wd4711 -wd4738 -wd4820 -wd4946 -wd4986") ELSE () ADD_DEFINITIONS( "-Wall -Wtype-limits" ) ADD_DEFINITIONS("-Wno-unknown-pragmas") ENDIF() if(POLICY CMP0020) cmake_policy(SET CMP0020 NEW) endif() if(POLICY CMP0043) cmake_policy(SET CMP0043 NEW) endif() # To suppress g2o related opengl warning if(POLICY CMP0072) cmake_policy(SET CMP0072 NEW) endif() IF(MINGW) # Hide the --enable-auto-import warning SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-auto-import") SET(CMAKE_MODULE_LINKER_FLAGS "-Wl,--enable-auto-import") SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import") ENDIF(MINGW) # GCC 4 required IF(UNIX OR MINGW) EXEC_PROGRAM( gcc ARGS "-dumpversion" OUTPUT_VARIABLE GCC_VERSION ) IF(GCC_VERSION VERSION_LESS "4.0.0") MESSAGE(FATAL_ERROR "GCC ${GCC_VERSION} found, but version 4.x.x minimum is required") ENDIF(GCC_VERSION VERSION_LESS "4.0.0") ENDIF(UNIX OR MINGW) #The CDT Error Parser cannot handle error messages that span #more than one line, which is the default gcc behavior. #In order to force gcc to generate single line error messages with no line wrapping IF(CMAKE_COMPILER_IS_GNUCC) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0") ENDIF(CMAKE_COMPILER_IS_GNUCC) IF(CMAKE_COMPILER_IS_GNUCXX) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmessage-length=0") ENDIF(CMAKE_COMPILER_IS_GNUCXX) if(MSVC) if(MSVC_VERSION GREATER 1500 AND ${CMAKE_VERSION} VERSION_GREATER "2.8.6") include(ProcessorCount) ProcessorCount(N) if(NOT N EQUAL 0) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP${N}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP${N}") endif() endif() add_compile_options("/bigobj") endif() # [Eclipse] Automatic Discovery of Include directories (Optional, but handy) #SET(CMAKE_VERBOSE_MAKEFILE ON) #Other paths... IF(APPLE) # For Mac ports SET(CMAKE_INCLUDE_PATH "/opt/local/include;${CMAKE_INCLUDE_PATH}") SET(CMAKE_LIBRARY_PATH "/opt/local/lib;${CMAKE_LIBRARY_PATH}") ENDIF() ####### Build libraries as shared or static ####### OPTION( BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON ) ####### OUTPUT DIR ####### SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) # Avoid Visual Studio bin/Release and bin/Debug sub directories SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}") SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}") ####### INSTALL DIR ####### set(INSTALL_INCLUDE_DIR include/${PROJECT_PREFIX}-${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}) if(WIN32 AND NOT CYGWIN) set(DEF_INSTALL_CMAKE_DIR CMake) else() set(DEF_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_PREFIX}-${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}) endif() set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR}) ####### BUILD OPTIONS ####### include (GenerateExportHeader) # ANDROID_PREBUILD (early exit if true) OPTION( ANDROID_PREBUILD "Set to ON to build rtabmap resource build tool (required for android build)" OFF ) IF(ANDROID_PREBUILD) MESSAGE("Option ANDROID_PREBUILD is set, only rtabmap resource tool will be built. You can use android toolchain after that.") ADD_SUBDIRECTORY( utilite ) return() ENDIF(ANDROID_PREBUILD) IF(APPLE OR WIN32) OPTION(BUILD_AS_BUNDLE "Set to ON to build as bundle with all embedded dependencies (DragNDrop for Mac, installer for Windows)" OFF) ENDIF(APPLE OR WIN32) OPTION(BUILD_APP "Build main application" ON) OPTION(BUILD_TOOLS "Build tools" ON) OPTION(BUILD_EXAMPLES "Build examples" ON) ####### DEPENDENCIES ####### IF(MOBILE_BUILD) option(WITH_QT "Include Qt support" OFF) ELSE() option(WITH_QT "Include Qt support" ON) ENDIF() option(WITH_ORB_OCTREE "Include ORB Octree feature support" ON) option(WITH_TORCH "Include Torch support (SuperPoint)" OFF) option(WITH_PYTHON "Include Python3 support (PyMatcher, PyDetector)" OFF) option(WITH_PYTHON_THREADING "Use more than one Python interpreter." OFF) option(WITH_PDAL "Include PDAL support" ON) option(WITH_LIBLAS "Include libLAS support" OFF) option(WITH_CUDASIFT "Include CudaSift support (this fork https://github.com/matlabbe/CudaSift)" OFF) option(WITH_FREENECT "Include Freenect support" ON) option(WITH_FREENECT2 "Include Freenect2 support" ON) option(WITH_K4W2 "Include Kinect for Windows v2 support" ON) option(WITH_K4A "Include Kinect for Azure support" ON) option(WITH_OPENNI "Include OpenNI support" ON) option(WITH_OPENNI2 "Include OpenNI2 support" ON) option(WITH_DC1394 "Include dc1394 support" ON) option(WITH_G2O "Include g2o support" ON) option(WITH_GTSAM "Include GTSAM support" ON) option(WITH_TORO "Include TORO support" ON) option(WITH_CERES "Include Ceres support" OFF) option(WITH_MRPT "Include MRPT support" ON) option(WITH_VERTIGO "Include Vertigo support" ON) option(WITH_CVSBA "Include cvsba support" OFF) option(WITH_POINTMATCHER "Include libpointmatcher support" ON) option(WITH_CCCORELIB "Include CCCoreLib support" OFF) option(WITH_OPEN3D "Include Open3D support" OFF) option(WITH_LOAM "Include LOAM support" OFF) option(WITH_FLOAM "Include FLOAM support" OFF) option(WITH_FLYCAPTURE2 "Include FlyCapture2/Triclops support" ON) option(WITH_ZED "Include ZED sdk support" ON) option(WITH_ZEDOC "Include ZED Open Capture support" ON) option(WITH_REALSENSE "Include RealSense support" ON) option(WITH_REALSENSE_SLAM "Include RealSenseSlam support" ON) option(WITH_REALSENSE2 "Include RealSense support" ON) option(WITH_MYNTEYE "Include mynteye-s support" ON) option(WITH_DEPTHAI "Include depthai-core support" OFF) option(WITH_XVSDK "Include XVisio SDK support" OFF) option(WITH_OCTOMAP "Include OctoMap support" ON) option(WITH_GRIDMAP "Include GridMap support" OFF) option(WITH_CPUTSDF "Include CPUTSDF support" OFF) option(WITH_OPENCHISEL "Include open_chisel support" OFF) option(WITH_ALICE_VISION "Include AliceVision support" OFF) option(WITH_FOVIS "Include FOVIS supp++ort" OFF) option(WITH_VISO2 "Include VISO2 support" OFF) option(WITH_DVO "Include DVO support" OFF) option(WITH_ORB_SLAM "Include ORB_SLAM2 or ORB_SLAM3 support" OFF) option(WITH_OKVIS "Include OKVIS support" OFF) option(WITH_MSCKF_VIO "Include MSCKF_VIO support" OFF) option(WITH_VINS "Include VINS-Fusion support" OFF) option(WITH_OPENVINS "Include OpenVINS support" OFF) option(WITH_MADGWICK "Include Madgwick IMU filtering support" ON) option(WITH_FASTCV "Include FastCV support" ON) option(WITH_OPENMP "Include OpenMP support" ON) option(WITH_OPENGV "Include OpenGV support" ON) IF(MOBILE_BUILD) option(PCL_OMP "With PCL OMP implementations" OFF) ELSE() option(PCL_OMP "With PCL OMP implementations" ON) ENDIF() option(BUILD_WITH_RPATH_NOT_RUNPATH "Explicitly disable usage of RUNPATH for the build tree on linux systems by adding --disable-new-dtags to linker (using old RPATH behavior instead)" OFF) set(RTABMAP_QT_VERSION AUTO CACHE STRING "Force a specific Qt version.") set_property(CACHE RTABMAP_QT_VERSION PROPERTY STRINGS AUTO 4 5 6) FIND_PACKAGE(OpenCV REQUIRED QUIET COMPONENTS core calib3d imgproc highgui stitching photo video videoio OPTIONAL_COMPONENTS aruco objdetect xfeatures2d nonfree gpu cudafeatures2d cudaoptflow cudaimgproc) IF(WITH_QT) FIND_PACKAGE(PCL 1.7 REQUIRED QUIET COMPONENTS common io kdtree search surface filters registration sample_consensus segmentation visualization) ELSE() FIND_PACKAGE(PCL 1.7 REQUIRED QUIET COMPONENTS common io kdtree search surface filters registration sample_consensus segmentation ) ENDIF() if(PCL_COMPILE_OPTIONS) if("${PCL_COMPILE_OPTIONS}" MATCHES "-march=native") MESSAGE(WARNING "PCL compile options contain \"-march=native\", make sure all libraries using Eigen are also compiled with that flag to avoid some segmentation faults (with gdb referring to some Eigen functions).") else() MESSAGE(STATUS "PCL compile options don't contain \"-march=native\", make sure all libraries using Eigen are also compiled without that flag to avoid some segmentation faults (with gdb referring to some Eigen functions).") endif() else() if("${PCL_DEFINITIONS}" MATCHES "-march=native") MESSAGE(WARNING "PCL definitions contain \"-march=native\", make sure all libraries using Eigen are also compiled with that flag to avoid some segmentation faults (with gdb referring to some Eigen functions).") else() MESSAGE(STATUS "PCL definitions don't contain \"-march=native\", make sure all libraries using Eigen are also compiled without that flag to avoid some segmentation faults (with gdb referring to some Eigen functions).") endif() endif() FIND_PACKAGE(ZLIB REQUIRED QUIET) FIND_PACKAGE(SQLite3 QUIET) IF(SQLite3_FOUND) MESSAGE(STATUS "Found SQLite3: ${SQLite3_INCLUDE_DIRS} ${SQLite3_LIBRARIES}") ENDIF(SQLite3_FOUND) if(NOT "${PCL_LIBRARIES}" STREQUAL "") # fix libproj.so not found on Xenial list(REMOVE_ITEM PCL_LIBRARIES "vtkproj4") endif() # OpenMP ("-fopenmp" should be added for flann included in PCL) # the gcc-4.2.1 coming with MacOS X is not compatible with the OpenMP pragmas we use, so disabling OpenMP for it if(((NOT APPLE) OR (NOT CMAKE_COMPILER_IS_GNUCXX) OR (GCC_VERSION VERSION_GREATER 4.2.1) OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) AND WITH_OPENMP) find_package(OpenMP COMPONENTS C CXX) endif() if(OPENMP_FOUND) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_INSTALL_OPENMP_LIBRARIES TRUE) message (STATUS "Found OpenMP: ${OpenMP_CXX_LIBRARIES}") if(PCL_OMP) message (STATUS "Add PCL_OMP to definitions") add_definitions(-DPCL_OMP) endif(PCL_OMP) elseif(WITH_OPENMP) message (STATUS "Not found OpenMP") endif() IF(OpenCV_FOUND) MESSAGE(STATUS "Found OpenCV: ${OpenCV_INCLUDE_DIRS}") ENDIF(OpenCV_FOUND) IF(PCL_FOUND) MESSAGE(STATUS "Found PCL: ${PCL_INCLUDE_DIRS}") ENDIF(PCL_FOUND) IF(ZLIB_FOUND) MESSAGE(STATUS "Found ZLIB: ${ZLIB_INCLUDE_DIRS}") ENDIF(ZLIB_FOUND) SET(ADD_VTK_GUI_SUPPORT_QT_TO_CONF FALSE) IF(WITH_QT) FIND_PACKAGE(VTK) IF(NOT VTK_FOUND) MESSAGE(FATAL_ERROR "VTK is required when using Qt. Set -DWITH_QT=OFF if you don't want gui tools.") ENDIF(NOT VTK_FOUND) # If Qt is here, the GUI will be built IF(NOT(${VTK_MAJOR_VERSION} LESS 9)) IF(NOT VTK_QT_VERSION) MESSAGE(FATAL_ERROR "WITH_QT option is ON, but VTK ${VTK_MAJOR_VERSION} has not been built with Qt support, disabling Qt.") ENDIF() option(VTK_GLOBAL_WARNING_DISPLAY "Show VTK warning display on runtime" OFF) IF(NOT VTK_GLOBAL_WARNING_DISPLAY) ADD_DEFINITIONS(-DVTK_GLOBAL_WARNING_DISPLAY_OFF) ENDIF() MESSAGE(STATUS "VTK>=9 detected, will use VTK_QT_VERSION=${VTK_QT_VERSION} for Qt version.") IF(${VTK_QT_VERSION} EQUAL 6) FIND_PACKAGE(Qt6 COMPONENTS Widgets Core Gui OpenGL PrintSupport QUIET OPTIONAL_COMPONENTS Svg) ELSEIF(${VTK_QT_VERSION} EQUAL 5) FIND_PACKAGE(Qt5 COMPONENTS Widgets Core Gui OpenGL PrintSupport QUIET OPTIONAL_COMPONENTS Svg) ELSE() FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui OPTIONAL_COMPONENTS QtSvg) ENDIF() ELSE() # look for Qt5 (if vtk>5 is installed) before Qt4 IF("${VTK_MAJOR_VERSION}" GREATER 5) IF(RTABMAP_QT_VERSION STREQUAL "AUTO" OR RTABMAP_QT_VERSION STREQUAL "5") FIND_PACKAGE(Qt5 COMPONENTS Widgets Core Gui OpenGL PrintSupport QUIET OPTIONAL_COMPONENTS Svg) ENDIF(RTABMAP_QT_VERSION STREQUAL "AUTO" OR RTABMAP_QT_VERSION STREQUAL "5") ENDIF("${VTK_MAJOR_VERSION}" GREATER 5) IF(NOT Qt5_FOUND) IF(RTABMAP_QT_VERSION STREQUAL "AUTO" OR RTABMAP_QT_VERSION STREQUAL "4") FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui OPTIONAL_COMPONENTS QtSvg) ENDIF(RTABMAP_QT_VERSION STREQUAL "AUTO" OR RTABMAP_QT_VERSION STREQUAL "4") ENDIF(NOT Qt5_FOUND) ENDIF() IF(QT4_FOUND OR Qt5_FOUND OR Qt6_FOUND) # For VCPKG build, set those global variables to off, # we will enable them for jsut specific targets set(CMAKE_AUTOMOC OFF) set(CMAKE_AUTORCC OFF) set(CMAKE_AUTOUIC OFF) IF("${VTK_MAJOR_VERSION}" EQUAL 5) FIND_PACKAGE(QVTK REQUIRED) # only for VTK 5 ELSE() list(FIND PCL_LIBRARIES VTK::GUISupportQt value) IF(value EQUAL -1) list(FIND PCL_LIBRARIES vtkGUISupportQt value) IF(value EQUAL -1) IF(NOT(${VTK_MAJOR_VERSION} LESS 9)) SET(PCL_LIBRARIES "${PCL_LIBRARIES};VTK::GUISupportQt") ELSE() SET(PCL_LIBRARIES "${PCL_LIBRARIES};vtkGUISupportQt") ENDIF() SET(ADD_VTK_GUI_SUPPORT_QT_TO_CONF TRUE) ENDIF(value EQUAL -1) ENDIF(value EQUAL -1) MESSAGE(STATUS "VTK_RENDERING_BACKEND=${VTK_RENDERING_BACKEND}") IF(VTK_RENDERING_BACKEND STREQUAL "OpenGL2") ADD_DEFINITIONS("-DVTK_OPENGL2") # see issue #525 list(FIND PCL_LIBRARIES vtkRenderingVolumeOpenGL2 value) IF(value EQUAL -1) SET(PCL_LIBRARIES "${PCL_LIBRARIES};vtkRenderingVolumeOpenGL2") ENDIF(value EQUAL -1) ELSEIF(VTK_RENDERING_BACKEND STREQUAL "OpenGL") # PCL 1.9 not adding vtkRenderingVolumeOpenGL as dependency anymore, so add it for us list(FIND PCL_LIBRARIES vtkRenderingVolumeOpenGL value) IF(value EQUAL -1) SET(PCL_LIBRARIES "${PCL_LIBRARIES};vtkRenderingVolumeOpenGL") ENDIF(value EQUAL -1) ELSEIF("${VTK_MAJOR_VERSION}" EQUAL 9) list(FIND PCL_LIBRARIES VTK::RenderingOpenGL2 value) IF(NOT value EQUAL -1) list(FIND PCL_LIBRARIES VTK::RenderingVolumeOpenGL2 value) IF(value EQUAL -1) SET(PCL_LIBRARIES "${PCL_LIBRARIES};VTK::RenderingVolumeOpenGL2") ENDIF(value EQUAL -1) ENDIF(NOT value EQUAL -1) ENDIF() ENDIF() ADD_DEFINITIONS(-DQT_NO_KEYWORDS) # To avoid conflicts with boost signals/foreach and Qt macros ENDIF(QT4_FOUND OR Qt5_FOUND OR Qt6_FOUND) ENDIF(WITH_QT) IF(NOT VTK_FOUND) # Newest PCL versions won't set -DDISABLE_VTK IF(NOT "${PCL_DEFINITIONS}" MATCHES "-DDISABLE_VTK") SET(PCL_DEFINITIONS "${PCL_DEFINITIONS};-DDISABLE_VTK") ENDIF() ENDIF(NOT VTK_FOUND) IF(WITH_TORCH) FIND_PACKAGE(Torch QUIET) IF(TORCH_FOUND) MESSAGE(STATUS "Found Torch: ${TORCH_INCLUDE_DIRS}") ENDIF(TORCH_FOUND) ENDIF(WITH_TORCH) IF(WITH_PYTHON) FIND_PACKAGE(Python3 COMPONENTS Interpreter Development NumPy) IF(Python3_FOUND) MESSAGE(STATUS "Found Python3") FIND_PACKAGE(pybind11 REQUIRED) ENDIF(Python3_FOUND) ENDIF(WITH_PYTHON) IF(WITH_PDAL) FIND_PACKAGE(PDAL QUIET) IF(PDAL_FOUND) MESSAGE(STATUS "Found PDAL ${PDAL_VERSION}: ${PDAL_INCLUDE_DIRS}") ENDIF(PDAL_FOUND) ENDIF(WITH_PDAL) IF(WITH_LIBLAS) FIND_PACKAGE(libLAS QUIET) IF(libLAS_FOUND) MESSAGE(STATUS "Found libLAS ${libLAS_VERSION}: ${libLAS_INCLUDE_DIRS}") ENDIF(libLAS_FOUND) ENDIF(WITH_LIBLAS) IF(WITH_CUDASIFT) FIND_PACKAGE(CudaSift 3 QUIET) IF(CudaSift_FOUND) MESSAGE(STATUS "Found CudaSift") ENDIF(CudaSift_FOUND) ENDIF(WITH_CUDASIFT) IF(WITH_FREENECT) FIND_PACKAGE(Freenect QUIET) IF(Freenect_FOUND) MESSAGE(STATUS "Found Freenect: ${Freenect_INCLUDE_DIRS}") ENDIF(Freenect_FOUND) ENDIF(WITH_FREENECT) IF(WITH_FREENECT2) FIND_PACKAGE(freenect2 QUIET) IF(freenect2_FOUND) IF(NOT freenect2_INCLUDE_DIRS) SET(freenect2_INCLUDE_DIRS ${freenect2_INCLUDE_DIR}) ENDIF(NOT freenect2_INCLUDE_DIRS) MESSAGE(STATUS "Found freenect2: ${freenect2_INCLUDE_DIRS}") # Explicitly link to OpenCL (in case of CUDA installed) FIND_PACKAGE(OpenCL QUIET) IF(OpenCL_FOUND) SET(freenect2_LIBRARIES ${OpenCL_LIBRARIES} ${freenect2_LIBRARIES} ) ENDIF(OpenCL_FOUND) ENDIF(freenect2_FOUND) ENDIF(WITH_FREENECT2) IF(WITH_K4W2 AND WIN32) FIND_PACKAGE(KinectSDK2 QUIET) IF(KinectSDK2_FOUND) MESSAGE(STATUS "Found Kinect for Windows 2: ${KinectSDK2_INCLUDE_DIRS}") ENDIF(KinectSDK2_FOUND) ENDIF(WITH_K4W2 AND WIN32) IF(WITH_K4A) IF(WIN32) FIND_PACKAGE(K4A QUIET) ELSE() FIND_PACKAGE(k4a QUIET) FIND_PACKAGE(k4arecord QUIET) IF(NOT (k4a_FOUND AND k4arecord_FOUND)) SET(k4a_FOUND FALSE) ENDIF(NOT (k4a_FOUND AND k4arecord_FOUND)) ENDIF() IF(k4a_FOUND) MESSAGE(STATUS "Found Kinect for Azure: ${k4a_INCLUDE_DIRS}") ENDIF(k4a_FOUND) ENDIF(WITH_K4A) # IF PCL depends on OpenNI2 (already found), ignore WITH_OPENNI2 IF(WITH_OPENNI2 OR OpenNI2_FOUND) FIND_PACKAGE(OpenNI2 QUIET) IF(OpenNI2_FOUND) MESSAGE(STATUS "Found OpenNI2: ${OpenNI2_INCLUDE_DIRS}") ENDIF(OpenNI2_FOUND) ENDIF(WITH_OPENNI2 OR OpenNI2_FOUND) IF(WITH_DC1394) FIND_PACKAGE(DC1394 QUIET) IF(DC1394_FOUND) MESSAGE(STATUS "Found DC1394: ${DC1394_INCLUDE_DIRS}") ENDIF(DC1394_FOUND) ENDIF(WITH_DC1394) IF(WITH_G2O) FIND_PACKAGE(g2o NO_MODULE) IF(g2o_FOUND) MESSAGE(STATUS "Found g2o (targets)") SET(G2O_FOUND ${g2o_FOUND}) get_target_property(G2O_INCLUDES g2o::core INTERFACE_INCLUDE_DIRECTORIES) MESSAGE(STATUS "g2o include dir: ${G2O_INCLUDES}") FIND_FILE(G2O_FACTORY_FILE g2o/core/factory.h PATHS ${G2O_INCLUDES} NO_DEFAULT_PATH) FILE(READ ${G2O_FACTORY_FILE} TMPTXT) STRING(FIND "${TMPTXT}" "shared_ptr" matchres) IF(${matchres} EQUAL -1) MESSAGE(STATUS "Old g2o factory version detected without shared ptr (factory file: ${G2O_FACTORY_FILE}).") SET(G2O_CPP11 2) ELSE() MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).") SET(G2O_CPP11 1) ENDIF() ELSE() FIND_PACKAGE(G2O QUIET) IF(G2O_FOUND) MESSAGE(STATUS "Found g2o: ${G2O_INCLUDE_DIRS}") FIND_FILE(G2O_FACTORY_FILE g2o/core/factory.h PATHS ${G2O_INCLUDES} NO_DEFAULT_PATH) FILE(READ ${G2O_FACTORY_FILE} TMPTXT) STRING(FIND "${TMPTXT}" "shared_ptr" matchres) IF(NOT ${matchres} EQUAL -1) MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).") SET(G2O_CPP11 1) ENDIF() ENDIF(G2O_FOUND) ENDIF() ENDIF(WITH_G2O) IF(WITH_GTSAM) # Force config mode to ignore PCL's findGTSAM.cmake file FIND_PACKAGE(GTSAM CONFIG QUIET) ENDIF(WITH_GTSAM) IF(WITH_MRPT) FIND_PACKAGE(MRPT COMPONENTS poses QUIET) IF(MRPT_FOUND) message(STATUS "MRPT_VERSION: ${MRPT_VERSION}") message(STATUS "MRPT_LIBRARIES: ${MRPT_LIBRARIES}") ENDIF(MRPT_FOUND) ENDIF(WITH_MRPT) IF(WITH_FLYCAPTURE2) FIND_PACKAGE(FlyCapture2 QUIET) IF(FlyCapture2_FOUND) MESSAGE(STATUS "Found FlyCapture2: ${FlyCapture2_INCLUDE_DIRS}") ENDIF(FlyCapture2_FOUND) ENDIF(WITH_FLYCAPTURE2) IF(WITH_CVSBA) FIND_PACKAGE(cvsba QUIET) IF(cvsba_FOUND) MESSAGE(STATUS "Found cvsba: ${cvsba_INCLUDE_DIRS}") ENDIF(cvsba_FOUND) ENDIF(WITH_CVSBA) IF(WITH_POINTMATCHER) find_package(libpointmatcher QUIET) IF(libpointmatcher_FOUND) MESSAGE(STATUS "Found libpointmatcher: ${libpointmatcher_INCLUDE_DIRS}") string(FIND "${libpointmatcher_LIBRARIES}" "libnabo" value) IF(value EQUAL -1) # Find libnabo (Issue #1117): find_package(libnabo REQUIRED PATHS ${LIBNABO_INSTALL_DIR}) message(STATUS "libnabo found, version ${libnabo_VERSION} (Config mode)") SET(libpointmatcher_LIBRARIES "${libpointmatcher_LIBRARIES};libnabo::nabo") ENDIF(value EQUAL -1) string(FIND "${libpointmatcher_LIBRARIES}" "yaml-cpp::yaml-cpp" value) IF(NOT value EQUAL -1) # Find yaml-cpp (Issue #1268): find_package(yaml-cpp QUIET) ENDIF(NOT value EQUAL -1) ENDIF(libpointmatcher_FOUND) ENDIF(WITH_POINTMATCHER) IF(libpointmatcher_FOUND OR GTSAM_FOUND) find_package(Boost COMPONENTS thread filesystem system program_options date_time REQUIRED) IF(Boost_MINOR_VERSION GREATER 47) find_package(Boost COMPONENTS thread filesystem system program_options date_time chrono timer serialization REQUIRED) ENDIF(Boost_MINOR_VERSION GREATER 47) IF(WIN32) MESSAGE(STATUS "Boost_LIBRARY_DIRS=${Boost_LIBRARY_DIRS}") link_directories(${Boost_LIBRARY_DIRS}) ENDIF(WIN32) ENDIF(libpointmatcher_FOUND OR GTSAM_FOUND) IF(WITH_CCCORELIB) find_package(CCCoreLib QUIET) IF(CCCoreLib_FOUND) MESSAGE(STATUS "Found CCCoreLib: ${CCCoreLib_INCLUDE_DIRS}") ENDIF(CCCoreLib_FOUND) ENDIF(WITH_CCCORELIB) IF(WITH_OPEN3D) IF(${CMAKE_VERSION} VERSION_LESS "3.19.0") MESSAGE(WARNING "Open3D requires CMake version >=3.19 (current is ${CMAKE_VERSION})") ELSE() # Build Open3D like this to avoid linker errors in rtabmap: # cmake -DBUILD_SHARED_LIBS=ON -DGLIBCXX_USE_CXX11_ABI=ON -DCMAKE_BUILD_TYPE=Release .. find_package(Open3D QUIET) IF(Open3D_FOUND) MESSAGE(STATUS "Found Open3D: ${Open3DINCLUDE_DIRS}") ENDIF(Open3D_FOUND) ENDIF() ENDIF(WITH_OPEN3D) IF(WITH_LOAM) find_package(loam_velodyne QUIET) IF(loam_velodyne_FOUND) MESSAGE(STATUS "Found loam_velodyne: ${loam_velodyne_INCLUDE_DIRS}") ENDIF(loam_velodyne_FOUND) ENDIF(WITH_LOAM) IF(WITH_FLOAM) find_package(floam QUIET) IF(floam_FOUND) MESSAGE(STATUS "Found floam: ${floam_INCLUDE_DIRS}") FIND_PACKAGE(Ceres QUIET REQUIRED) ENDIF(floam_FOUND) ENDIF(WITH_FLOAM) SET(ZED_FOUND FALSE) IF(WITH_ZED) find_package(ZED 2 QUIET) IF(ZED_FOUND) MESSAGE(STATUS "Found ZED sdk: ${ZED_INCLUDE_DIRS}") ## look for CUDA find_package(CUDA) IF(CUDA_FOUND) MESSAGE(STATUS "Found CUDA: ${CUDA_INCLUDE_DIRS}") ELSE() MESSAGE(FATAL_ERROR "CUDA is required to build with Zed sdk! Set -DWITH_ZED=OFF if you don't have CUDA.") ENDIF() ENDIF(ZED_FOUND) ENDIF(WITH_ZED) IF(WITH_ZEDOC) find_package(ZEDOC QUIET) IF(ZEDOC_FOUND) MESSAGE(STATUS "Found ZED Open Capture: ${ZEDOC_INCLUDE_DIRS}") ## look for HIDAPI find_package(HIDAPI) IF(HIDAPI_FOUND) MESSAGE(STATUS "Found HIDAPI: ${HIDAPI_INCLUDE_DIRS}") ELSE() MESSAGE(FATAL_ERROR "HIDAPI is required to build with Zed Open Capture! Set -DWITH_ZEDOC=OFF if you don't have HIDAPI.") ENDIF() ENDIF(ZEDOC_FOUND) ENDIF(WITH_ZEDOC) IF(WITH_REALSENSE) IF(WITH_REALSENSE_SLAM) FIND_PACKAGE(RealSense QUIET COMPONENTS slam) ELSE() FIND_PACKAGE(RealSense QUIET) ENDIF() IF(RealSense_FOUND) MESSAGE(STATUS "Found RealSense: ${RealSense_INCLUDE_DIRS}") ENDIF(RealSense_FOUND) IF(RealSenseSlam_FOUND) MESSAGE(STATUS "Found RealSenseSlam: ${RealSense_INCLUDE_DIRS}") ENDIF(RealSenseSlam_FOUND) ENDIF(WITH_REALSENSE) IF(WITH_REALSENSE2) IF(WIN32) FIND_PACKAGE(RealSense2 QUIET) ELSE() FIND_PACKAGE(realsense2 QUIET) ENDIF() IF(realsense2_FOUND) MESSAGE(STATUS "Found RealSense2: ${realsense2_INCLUDE_DIRS}") ENDIF(realsense2_FOUND) ENDIF(WITH_REALSENSE2) IF(WITH_MYNTEYE) FIND_PACKAGE(mynteye QUIET) IF(mynteye_FOUND) MESSAGE(STATUS "Found mynteye-s: ${mynteye_INCLUDE_DIRS}") ENDIF(mynteye_FOUND) ENDIF(WITH_MYNTEYE) IF(WITH_DEPTHAI) FIND_PACKAGE(depthai 2.24 QUIET) IF(depthai_FOUND) MESSAGE(STATUS "Found depthai-core (targets)") ENDIF(depthai_FOUND) ENDIF(WITH_DEPTHAI) IF(WITH_XVSDK) FIND_PACKAGE(xvsdk QUIET) IF(xvsdk_FOUND) MESSAGE(STATUS "Found xvsdk (targets)") ENDIF(xvsdk_FOUND) ENDIF(WITH_XVSDK) IF(WITH_OCTOMAP) FIND_PACKAGE(octomap QUIET) IF(octomap_FOUND) MESSAGE(STATUS "Found octomap ${octomap_VERSION}: ${OCTOMAP_INCLUDE_DIRS}") IF(octomap_VERSION VERSION_LESS 1.8) ADD_DEFINITIONS("-DOCTOMAP_PRE_18") ENDIF(octomap_VERSION VERSION_LESS 1.8) ENDIF(octomap_FOUND) ENDIF(WITH_OCTOMAP) IF(WITH_GRIDMAP) FIND_PACKAGE(grid_map_core QUIET) IF(grid_map_core_FOUND) MESSAGE(STATUS "Found grid_map_core ${grid_map_core_VERSION}: ${grid_map_core_INCLUDE_DIRS}") ENDIF(grid_map_core_FOUND) ENDIF(WITH_GRIDMAP) IF(WITH_CPUTSDF) FIND_PACKAGE(CPUTSDF QUIET) IF(CPUTSDF_FOUND) MESSAGE(STATUS "Found CPUTSDF: ${CPUTSDF_INCLUDE_DIRS}") ENDIF(CPUTSDF_FOUND) ENDIF(WITH_CPUTSDF) IF(WITH_OPENCHISEL) find_package(open_chisel QUIET) if(open_chisel_FOUND) MESSAGE(STATUS "Found open_chisel: ${open_chisel_INCLUDE_DIRS}") endif(open_chisel_FOUND) ENDIF(WITH_OPENCHISEL) IF(WITH_ALICE_VISION) find_package(AliceVision CONFIG QUIET) IF(AliceVision_FOUND) IF(${AliceVision_VERSION} VERSION_LESS_EQUAL "2.2") find_package(Boost COMPONENTS log log_setup container REQUIRED) ENDIF(${AliceVision_VERSION} VERSION_LESS_EQUAL "2.2") SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/local/lib/cmake/modules") find_package(Geogram REQUIRED QUIET) find_package(assimp QUIET) add_definitions("-DRTABMAP_ALICE_VISION_MAJOR=${AliceVision_VERSION_MAJOR}") add_definitions("-DRTABMAP_ALICE_VISION_MINOR=${AliceVision_VERSION_MINOR}") add_definitions("-DRTABMAP_ALICE_VISION_PATCH=${AliceVision_VERSION_PATCH}") ENDIF(AliceVision_FOUND) ENDIF(WITH_ALICE_VISION) IF(WITH_FOVIS) FIND_PACKAGE(libfovis QUIET) IF(libfovis_FOUND) MESSAGE(STATUS "Found libfovis: ${libfovis_INCLUDE_DIRS}") ENDIF(libfovis_FOUND) ENDIF(WITH_FOVIS) IF(WITH_VISO2) FIND_PACKAGE(libviso2 QUIET) IF(libviso2_FOUND) MESSAGE(STATUS "Found libviso2: ${libviso2_INCLUDE_DIRS}") ENDIF(libviso2_FOUND) ENDIF(WITH_VISO2) IF(WITH_DVO) FIND_PACKAGE(dvo_core QUIET) IF(dvo_core_FOUND) MESSAGE(STATUS "Found dvo_core: ${dvo_core_INCLUDE_DIRS}") ENDIF(dvo_core_FOUND) ENDIF(WITH_DVO) IF(WITH_OKVIS) FIND_PACKAGE(okvis 1.1 QUIET) IF(okvis_FOUND) MESSAGE(STATUS "Found okvis: ${OKVIS_INCLUDE_DIRS}") find_package(brisk 2 REQUIRED) MESSAGE(STATUS "Found brisk: ${BRISK_INCLUDE_DIRS}") find_package(opengv REQUIRED) MESSAGE(STATUS "Found opengv: ${OPENGV_INCLUDE_DIRS}") find_package(Ceres 1.9.0 REQUIRED EXACT) # OKVIS requires this specific version MESSAGE(STATUS "Found ceres ${Ceres_VERSION}: ${CERES_INCLUDE_DIRS}") ENDIF(okvis_FOUND) ENDIF(WITH_OKVIS) # If built with okvis, we found already ceres above IF(WITH_CERES) IF(NOT okvis_FOUND AND NOT floam_FOUND) FIND_PACKAGE(Ceres QUIET) MESSAGE(STATUS "Found ceres ${Ceres_VERSION}: ${CERES_INCLUDE_DIRS}") ENDIF(NOT okvis_FOUND AND NOT floam_FOUND) ELSEIF(Ceres_FOUND) MESSAGE(WARNING "WITH_CERES is OFF, but it still included by dependencies Okvis or FLOAM") ENDIF() IF(WITH_MSCKF_VIO) FIND_PACKAGE(msckf_vio QUIET) IF(msckf_vio_FOUND) MESSAGE(STATUS "Found msckf_vio: ${msckf_vio_INCLUDE_DIRS}") ENDIF(msckf_vio_FOUND) ENDIF(WITH_MSCKF_VIO) IF(WITH_VINS) FIND_PACKAGE(vins QUIET) IF(vins_FOUND) MESSAGE(STATUS "Found vins: ${vins_INCLUDE_DIRS}") IF(okvis_FOUND) MESSAGE(WARNING "VINS and OKVIS will be both linked to project, make sure VINS has been built with against same Ceres version than OKVIS to avoid some crashes.") ENDIF(okvis_FOUND) ENDIF(vins_FOUND) ENDIF(WITH_VINS) IF(WITH_OPENVINS) FIND_PACKAGE(ov_msckf QUIET) # On ROS2, the indirect includes and libraries # are not forwarded by ov_msckf target, append them manually FIND_PACKAGE(ov_core QUIET) FIND_PACKAGE(ov_init QUIET) IF(ov_msckf_FOUND AND ov_core_FOUND AND ov_init_FOUND) SET(ov_msckf_INCLUDE_DIRS ${ov_msckf_INCLUDE_DIRS} ${ov_core_INCLUDE_DIRS} ${ov_init_INCLUDE_DIRS}) SET(ov_msckf_LIBRARIES ${ov_msckf_LIBRARIES} ${ov_core_LIBRARIES} ${ov_init_LIBRARIES}) MESSAGE(STATUS "Found OpenVINS: ${ov_msckf_INCLUDE_DIRS}") ENDIF() ENDIF(WITH_OPENVINS) IF(WITH_FASTCV) FIND_PACKAGE(FastCV QUIET) IF(FastCV_FOUND) MESSAGE(STATUS "Found FastCV: ${FastCV_INCLUDE_DIRS}") ENDIF(FastCV_FOUND) ENDIF(WITH_FASTCV) IF(WITH_OPENGV) FIND_PACKAGE(opengv QUIET) IF(opengv_FOUND) MESSAGE(STATUS "Found OpenGV: ${opengv_INCLUDE_DIRS}") ENDIF(opengv_FOUND) ENDIF(WITH_OPENGV) IF(WITH_ORB_SLAM AND NOT G2O_FOUND) FIND_PACKAGE(ORB_SLAM QUIET) IF(ORB_SLAM_FOUND) MESSAGE(STATUS "Found ORB_SLAM${ORB_SLAM_VERSION}: ${ORB_SLAM_INCLUDE_DIRS}") ENDIF(ORB_SLAM_FOUND) ENDIF(WITH_ORB_SLAM AND NOT G2O_FOUND) SET(DISABLE_NEW_DTAGS_FLAG "--disable-new-dtags") IF(NOT (APPLE OR WIN32) AND BUILD_WITH_RPATH_NOT_RUNPATH) ADD_LINK_OPTIONS(LINKER:${DISABLE_NEW_DTAGS_FLAG}) ENDIF() IF(NOT MSVC) IF(Qt6_FOUND OR (G2O_FOUND AND G2O_CPP11 EQUAL 1) OR TORCH_FOUND OR MRPT_FOUND) # Qt6 requires c++17 include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) IF(COMPILER_SUPPORTS_CXX17) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") set(CMAKE_CXX_STANDARD 17) ELSE() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++17 support. Please use a different C++ compiler if you want to use Qt6.") ENDIF() ENDIF() IF((NOT (${CMAKE_CXX_STANDARD} STREQUAL "17")) AND (msckf_vio_FOUND OR loam_velodyne_FOUND OR floam_FOUND OR PCL_VERSION VERSION_GREATER "1.9.1" OR G2O_FOUND OR CCCoreLib_FOUND OR Open3D_FOUND)) #MSCKF_VIO, LOAM, PCL>=1.10, latest g2o and CCCoreLib require c++14 include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) IF(COMPILER_SUPPORTS_CXX14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") set(CMAKE_CXX_STANDARD 14) ELSE() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler if you want to use LOAM, latest PCL or g2o.") ENDIF() ENDIF() IF(NOT ("${CMAKE_CXX_STANDARD}" STREQUAL "17") AND NOT ("${CMAKE_CXX_STANDARD}" STREQUAL "14")) #Newest versions require std11 include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) IF(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") ELSEIF(COMPILER_SUPPORTS_CXX0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") ELSE() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") ENDIF() ENDIF() ENDIF() ####### OSX BUNDLE CMAKE_INSTALL_PREFIX ####### IF(APPLE AND BUILD_AS_BUNDLE) IF(Qt6_FOUND OR Qt5_FOUND OR (QT4_FOUND AND QT_QTCORE_FOUND AND QT_QTGUI_FOUND)) # Required when packaging, and set CMAKE_INSTALL_PREFIX to "/". SET(CMAKE_INSTALL_PREFIX "/") SET(CPACK_SET_DESTDIR TRUE) SET(CMAKE_BUNDLE_NAME "${PROJECT_NAME}") SET(CMAKE_BUNDLE_LOCATION "${CMAKE_INSTALL_PREFIX}") # make sure CMAKE_INSTALL_PREFIX ends in / STRING(LENGTH "${CMAKE_INSTALL_PREFIX}" LEN) MATH(EXPR LEN "${LEN} -1" ) STRING(SUBSTRING "${CMAKE_INSTALL_PREFIX}" ${LEN} 1 ENDCH) IF(NOT "${ENDCH}" STREQUAL "/") SET(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/") ENDIF(NOT "${ENDCH}" STREQUAL "/") SET(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}${CMAKE_BUNDLE_NAME}.app/Contents") ELSE() #If Qt is not here, no need to build a bundle SET(BUILD_AS_BUNDLE OFF) ENDIF() ENDIF(APPLE AND BUILD_AS_BUNDLE) ####### SOURCES (Projects) ####### # OpenCV2 has nonfree if OPENCV_NONFREE_FOUND # OpenCV<=3.4.2 has nonfree if OPENCV_XFEATURES2D_FOUND # OpenCV>3.4.2 has nonfree if OPENCV_XFEATURES2D_FOUND and OPENCV_ENABLE_NONFREE is defined IF(NOT (OPENCV_NONFREE_FOUND OR OPENCV_XFEATURES2D_FOUND)) SET(NONFREE "//") ELSEIF(OpenCV_VERSION VERSION_GREATER "3.4.2") FIND_FILE(OpenCV_MODULES_HPP opencv2/opencv_modules.hpp PATHS ${OpenCV_INCLUDE_DIRS} NO_DEFAULT_PATH) FILE(READ ${OpenCV_MODULES_HPP} TMPTXT) STRING(FIND "${TMPTXT}" "#define OPENCV_ENABLE_NONFREE" matchres) IF(${matchres} EQUAL -1) SET(NONFREE "//") ENDIF(${matchres} EQUAL -1) ENDIF() IF(NOT G2O_FOUND) SET(G2O "//") SET(G2O_CPP_CONF "//") ELSE() IF(NOT G2O_CPP11) SET(G2O_CPP_CONF "//") ENDIF(NOT G2O_CPP11) ENDIF() IF(NOT GTSAM_FOUND) SET(GTSAM "//") ENDIF() IF(NOT MRPT_FOUND) SET(MRPT "//") ENDIF(NOT MRPT_FOUND) IF(NOT WITH_CERES OR NOT CERES_FOUND) SET(CERES "//") ENDIF(NOT WITH_CERES OR NOT CERES_FOUND) IF(NOT WITH_TORO) SET(TORO "//") ENDIF(NOT WITH_TORO) IF(NOT WITH_VERTIGO) SET(VERTIGO "//") ENDIF(NOT WITH_VERTIGO) IF(NOT cvsba_FOUND) SET(CVSBA "//") ENDIF() IF(NOT libpointmatcher_FOUND) SET(POINTMATCHER "//") ENDIF(NOT libpointmatcher_FOUND) IF(NOT CCCoreLib_FOUND) SET(CCCORELIB "//") ENDIF(NOT CCCoreLib_FOUND) IF(NOT Open3D_FOUND) SET(OPEN3D "//") ENDIF(NOT Open3D_FOUND) IF(NOT FastCV_FOUND) SET(FASTCV "//") ENDIF(NOT FastCV_FOUND) IF(NOT opengv_FOUND OR NOT WITH_OPENGV) SET(OPENGV "//") ENDIF(NOT opengv_FOUND OR NOT WITH_OPENGV) IF(NOT PDAL_FOUND) SET(PDAL "//") ENDIF(NOT PDAL_FOUND) IF(NOT libLAS_FOUND) SET(LIBLAS "//") ENDIF(NOT libLAS_FOUND) IF(NOT CudaSift_FOUND) SET(CUDASIFT "//") ENDIF(NOT CudaSift_FOUND) IF(NOT loam_velodyne_FOUND) SET(LOAM "//") ENDIF(NOT loam_velodyne_FOUND) IF(NOT floam_FOUND) SET(FLOAM "//") ENDIF(NOT floam_FOUND) IF(NOT Freenect_FOUND) SET(FREENECT "//") ENDIF() IF(NOT freenect2_FOUND) SET(FREENECT2 "//") ENDIF() IF(NOT KinectSDK2_FOUND) SET(K4W2 "//") ENDIF() IF(NOT k4a_FOUND) SET(K4A "//") SET(CONF_WITH_K4A 0) ELSE() SET(CONF_WITH_K4A 1) IF(WIN32) install( FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindK4A.cmake" DESTINATION ${INSTALL_CMAKE_DIR}/Modules/. COMPONENT devel ) ENDIF(WIN32) ENDIF() IF(NOT (OpenNI_FOUND AND WITH_OPENNI)) SET(OPENNI "//") ENDIF() IF(NOT OpenNI2_FOUND) SET(OPENNI2 "//") ENDIF() IF(NOT DC1394_FOUND) SET(DC1394 "//") ENDIF() IF(NOT FlyCapture2_FOUND) SET(FLYCAPTURE2 "//") ENDIF() IF(NOT ZED_FOUND) SET(ZED "//") ENDIF() IF(NOT ZEDOC_FOUND) SET(ZEDOC "//") ENDIF() IF(NOT RealSense_FOUND) SET(REALSENSE "//") ENDIF() IF(NOT RealSenseSlam_FOUND) SET(REALSENSESLAM "//") ENDIF(NOT RealSenseSlam_FOUND) IF(NOT realsense2_FOUND) SET(REALSENSE2 "//") SET(CONF_WITH_REALSENSE2 0) ELSE() SET(CONF_WITH_REALSENSE2 1) IF(WIN32) install( FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindRealSense2.cmake" DESTINATION ${INSTALL_CMAKE_DIR}/Modules/. COMPONENT devel ) ENDIF(WIN32) ENDIF() IF(NOT mynteye_FOUND) SET(MYNTEYE "//") ENDIF(NOT mynteye_FOUND) IF(NOT depthai_FOUND) SET(CONF_WITH_DEPTH_AI 0) SET(DEPTHAI "//") ELSE() SET(CONF_WITH_DEPTH_AI 1) ENDIF() IF(NOT xvsdk_FOUND) SET(XVSDK "//") SET(CONF_WITH_XVSDK 0) ELSE() SET(CONF_WITH_XVSDK 1) ENDIF() IF(NOT octomap_FOUND) SET(OCTOMAP "//") SET(CONF_WITH_OCTOMAP 0) ELSE() SET(CONF_WITH_OCTOMAP 1) ENDIF() IF(NOT grid_map_core_FOUND) SET(GRIDMAP "//") ENDIF() IF(NOT CPUTSDF_FOUND) SET(CPUTSDF "//") ENDIF() IF(NOT open_chisel_FOUND) SET(OPENCHISEL "//") ENDIF() IF(NOT AliceVision_FOUND) SET(ALICE_VISION "//") ENDIF(NOT AliceVision_FOUND) IF(NOT libfovis_FOUND) SET(FOVIS "//") ENDIF() IF(NOT libviso2_FOUND) SET(VISO2 "//") ENDIF() IF(NOT dvo_core_FOUND) SET(DVO "//") ENDIF() IF(NOT okvis_FOUND) SET(OKVIS "//") ENDIF() IF(NOT msckf_vio_FOUND) SET(MSCKF_VIO "//") ENDIF() IF(NOT vins_FOUND) SET(VINS "//") ENDIF() IF(NOT ov_msckf_FOUND) SET(OPENVINS "//") ENDIF() IF(NOT ORB_SLAM_FOUND) SET(ORB_SLAM "//") ENDIF() IF(NOT WITH_ORB_OCTREE) SET(ORB_OCTREE "//") ENDIF() IF(NOT TORCH_FOUND) SET(TORCH "//") ENDIF() IF(NOT WITH_PYTHON OR NOT Python3_FOUND) SET(PYTHON "//") SET(CONF_WITH_PYTHON 0) ELSE() SET(CONF_WITH_PYTHON 1) ENDIF() IF(ADD_VTK_GUI_SUPPORT_QT_TO_CONF) SET(CONF_VTK_QT true) ELSE() SET(CONF_VTK_QT false) ENDIF() IF(NOT WITH_MADGWICK) SET(MADGWICK "//") ENDIF() CONFIGURE_FILE(Version.h.in ${CMAKE_CURRENT_BINARY_DIR}/corelib/src/include/${PROJECT_PREFIX}/core/Version.h) ADD_SUBDIRECTORY( utilite ) ADD_SUBDIRECTORY( corelib ) IF(ANDROID) IF(BUILD_APP) ADD_SUBDIRECTORY( app ) ENDIF(BUILD_APP) ELSEIF(WITH_QT) IF(Qt6_FOUND OR Qt5_FOUND OR (QT4_FOUND AND QT_QTCORE_FOUND AND QT_QTGUI_FOUND)) ADD_SUBDIRECTORY( guilib ) IF(BUILD_APP) ADD_SUBDIRECTORY( app ) ENDIF(BUILD_APP) ELSE() MESSAGE(WARNING "Qt not found, the GUI lib and the stand-alone application will not be compiled...") ENDIF() ENDIF() IF(BUILD_TOOLS) ADD_SUBDIRECTORY( tools ) ENDIF(BUILD_TOOLS) IF(BUILD_EXAMPLES) ADD_SUBDIRECTORY( examples ) ENDIF(BUILD_EXAMPLES) ####################### # Uninstall target, for "make uninstall" ####################### IF (NOT TARGET uninstall) CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") ENDIF() #### # Global Export Target #### add_library(rtabmap INTERFACE) add_library(rtabmap::rtabmap ALIAS rtabmap) IF(WITH_QT AND (QT4_FOUND OR Qt5_FOUND OR Qt6_FOUND)) set(CONF_WITH_GUI ON) IF(QT4_FOUND) set(CONF_QT_VERSION 4) ELSEIF(Qt5_FOUND) set(CONF_QT_VERSION 5) ELSE() set(CONF_QT_VERSION 6) ENDIF() target_link_libraries(rtabmap INTERFACE rtabmap_utilite rtabmap_core rtabmap_gui) ELSE() set(CONF_WITH_GUI OFF) target_link_libraries(rtabmap INTERFACE rtabmap_utilite rtabmap_core) ENDIF() install(TARGETS rtabmap EXPORT rtabmapTargets) export(EXPORT rtabmapTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" NAMESPACE rtabmap:: ) install(EXPORT rtabmapTargets FILE ${PROJECT_NAME}Targets.cmake DESTINATION ${INSTALL_CMAKE_DIR} NAMESPACE rtabmap:: COMPONENT devel ) #### # Setup RTABMapConfig.cmake #### include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) # Build tree: SET(CONF_MODULES_DIR "../cmake_modules") configure_file( ${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" @ONLY ) # Install tree: SET(CONF_MODULES_DIR "Modules") configure_file( ${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" @ONLY ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION ${INSTALL_CMAKE_DIR} COMPONENT devel ) #### ### Install package.xml for catkin install(FILES package.xml DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_PREFIX}" COMPONENT devel) ####################### # CPACK (Packaging) ####################### IF(BUILD_AS_BUNDLE) SET(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT runtime) INCLUDE(InstallRequiredSystemLibraries) ENDIF(BUILD_AS_BUNDLE) SET(CPACK_PACKAGE_NAME "${PROJECT_NAME}") SET(CPACK_PACKAGE_VENDOR "${PROJECT_NAME} project") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "RTAB-MAP is a Real-Time Appearance-Based Mapping approach.") #SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/Description.txt") #SET(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.txt") SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") #SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}") #SET(CPACK_PACKAGE_EXECUTABLES "") #SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") SET(CPACK_PACKAGE_CONTACT "matlabbe@gmail.com") set(CPACK_SOURCE_IGNORE_FILES "\\\\.svn/" "\\\\.settings/" "${PROJECT_SOURCE_DIR}/build/[a-zA-Z0-9_]+" "~$" "${PROJECT_SOURCE_DIR}/bin/.*${PROJECT_PREFIX}" "${PROJECT_SOURCE_DIR}/bin/.*${PROJECT_NAME}" "${PROJECT_SOURCE_DIR}/bin/.*[tT]est" "${PROJECT_SOURCE_DIR}/bin/.*[eE]xample" "${PROJECT_SOURCE_DIR}/bin/.*uresourcegenerator" "\\\\.DS_Store" ) IF(WIN32) SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") IF(CMAKE_CL_64) SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") ELSE() SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") ENDIF() IF(BUILD_AS_BUNDLE) SET(CPACK_GENERATOR "ZIP;NSIS") ELSE() SET(CPACK_GENERATOR "ZIP") ENDIF() SET(CPACK_SOURCE_GENERATOR "ZIP") SET(CPACK_NSIS_PACKAGE_NAME "${PROJECT_NAME}") SET(ICON_PATH "${PROJECT_SOURCE_DIR}/app/src/${PROJECT_NAME}.ico") SET(CPACK_NSIS_MUI_ICON ${ICON_PATH}) SET(CPACK_NSIS_MUI_UNIICON ${ICON_PATH}) SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}") #SET(CPACK_PACKAGE_ICON ${ICON_PATH}) #SET(CPACK_NSIS_INSTALLED_ICON_NAME ${ICON_PATH}) #SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.${PROJECT_PREFIX}.googlecode.com") #SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.${PROJECT_PREFIX}.googlecode.com") SET(CPACK_NSIS_DISPLAY_NAME "${PROJECT_NAME}") SET(CPACK_NSIS_CONTACT ${CPACK_PACKAGE_CONTACT}) # Set the icon used for the Windows "Add or Remove Programs" tool. SET(CPACK_NSIS_INSTALLED_ICON_NAME bin\\\\${PROJECT_NAME}.exe) # Desktop link ("executableName" "linkName") SET(CPACK_PACKAGE_EXECUTABLES "${PROJECT_NAME}" "${PROJECT_NAME}" ${CPACK_PACKAGE_EXECUTABLES}) SET(CPACK_CREATE_DESKTOP_LINKS "${PROJECT_NAME}" ${CPACK_CREATE_DESKTOP_LINKS}) SET(CPACK_NSIS_MODIFY_PATH ON) ELSEIF(APPLE) # The project is created as a bundle over the main app (see ./app/src). # Here we package only this bundle. Note that we set # CMAKE_INSTALL_PREFIX to "/" when packaging... IF(BUILD_AS_BUNDLE) SET(CPACK_GENERATOR "DragNDrop") ELSE() SET(CPACK_GENERATOR "PackageMaker;TBZ2") ENDIF() SET(CPACK_SOURCE_GENERATOR "ZIP") SET(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/app/src/${PROJECT_NAME}.icns") ELSE() SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") SET(CPACK_GENERATOR "TBZ2") SET(CPACK_SOURCE_GENERATOR "ZIP") ENDIF() INCLUDE(CPack) MESSAGE(STATUS "--------------------------------------------") MESSAGE(STATUS "Info :") MESSAGE(STATUS " RTAB-Map Version = ${RTABMAP_VERSION}") MESSAGE(STATUS " CMAKE_VERSION = ${CMAKE_VERSION}") MESSAGE(STATUS " CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") MESSAGE(STATUS " CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") MESSAGE(STATUS " CMAKE_INSTALL_LIBDIR = ${CMAKE_INSTALL_LIBDIR}") MESSAGE(STATUS " BUILD_APP = ${BUILD_APP}") MESSAGE(STATUS " BUILD_TOOLS = ${BUILD_TOOLS}") MESSAGE(STATUS " BUILD_EXAMPLES = ${BUILD_EXAMPLES}") IF(NOT WIN32) # see comment above for the BUILD_SHARED_LIBS option on Windows MESSAGE(STATUS " BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}") ENDIF(NOT WIN32) IF(APPLE OR WIN32) MESSAGE(STATUS " BUILD_AS_BUNDLE = ${BUILD_AS_BUNDLE}") ENDIF(APPLE OR WIN32) MESSAGE(STATUS " CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}") IF(NOT (APPLE OR WIN32) AND BUILD_WITH_RPATH_NOT_RUNPATH) MESSAGE(STATUS " LINKER FLAGS = ${DISABLE_NEW_DTAGS_FLAG}") ENDIF() MESSAGE(STATUS " FLANN_KDTREE_MEM_OPT = ${FLANN_KDTREE_MEM_OPT}") MESSAGE(STATUS " PCL_DEFINITIONS = ${PCL_DEFINITIONS}") MESSAGE(STATUS " PCL_VERSION = ${PCL_VERSION}") IF(PCL_COMPILE_OPTIONS) MESSAGE(STATUS " PCL_COMPILE_OPTIONS = ${PCL_COMPILE_OPTIONS}") ENDIF(PCL_COMPILE_OPTIONS) MESSAGE(STATUS "") MESSAGE(STATUS "Optional dependencies ('*' affects some default parameters) :") IF(OpenCV_FOUND) IF(OpenCV_VERSION_MAJOR EQUAL 2) IF(OPENCV_NONFREE_FOUND) MESSAGE(STATUS " *With OpenCV 2 nonfree module (SIFT/SURF) = YES (License: Non commercial)") ELSE() MESSAGE(STATUS " *With OpenCV 2 nonfree module (SIFT/SURF) = NO (not found, License: BSD)") ENDIF() ELSE() IF(OPENCV_XFEATURES2D_FOUND) IF(NONFREE STREQUAL "//") IF((OpenCV_VERSION_MAJOR LESS 4) OR ((OpenCV_VERSION_MAJOR EQUAL 4) AND (OpenCV_VERSION_MINOR LESS 5))) MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = NO (License: BSD)") ELSE() MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = NO (License: Apache 2)") ENDIF() ELSE() MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = YES (License: Non commercial)") ENDIF() ELSE() IF((OpenCV_VERSION_MAJOR LESS 4) OR ((OpenCV_VERSION_MAJOR EQUAL 4) AND (OpenCV_VERSION_MINOR LESS 5))) MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = NO, nonfree = NO (License: BSD)") ELSE() MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = NO, nonfree = NO (License: Apache 2)") ENDIF() ENDIF() ENDIF() ENDIF(OpenCV_FOUND) IF(WITH_QT AND QT4_FOUND) MESSAGE(STATUS " With Qt4 = YES (License: Open Source or Commercial)") MESSAGE(STATUS " With VTK ${VTK_MAJOR_VERSION}.${VTK_MINOR_VERSION} = YES (License: BSD)") ELSEIF(WITH_QT AND Qt5_FOUND) MESSAGE(STATUS " With Qt ${Qt5_VERSION} = YES (License: Open Source or Commercial)") MESSAGE(STATUS " With VTK ${VTK_MAJOR_VERSION}.${VTK_MINOR_VERSION} = YES (License: BSD)") ELSEIF(WITH_QT AND Qt6_FOUND) MESSAGE(STATUS " With Qt ${Qt6_VERSION} = YES (License: Open Source or Commercial)") MESSAGE(STATUS " With VTK ${VTK_MAJOR_VERSION}.${VTK_MINOR_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_QT) MESSAGE(STATUS " With Qt = NO (WITH_QT=OFF)") ELSE() MESSAGE(STATUS " With Qt = NO (Qt not found)") ENDIF() IF(SQLite3_FOUND) MESSAGE(STATUS " With external SQLite3 = YES (License: Public Domain)") ELSE() MESSAGE(STATUS " With external SQLite3 = NO (SQLite3 not found, internal version is used for convenience)") ENDIF() IF(WITH_ORB_OCTREE) MESSAGE(STATUS " With ORB OcTree = YES (License: GPLv3)") ELSE() MESSAGE(STATUS " With ORB OcTree = NO (WITH_ORB_OCTREE=OFF)") ENDIF() IF(TORCH_FOUND) MESSAGE(STATUS " With SuperPoint = YES (License: GPLv3) libtorch=${Torch_VERSION}") ELSEIF(NOT WITH_TORCH) MESSAGE(STATUS " With SuperPoint = NO (WITH_TORCH=OFF)") ELSE() MESSAGE(STATUS " With SuperPoint = NO (libtorch not found)") ENDIF() IF(WITH_PYTHON AND Python3_FOUND) MESSAGE(STATUS " With Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} = YES (License: PSF)") ELSEIF(NOT WITH_PYTHON) MESSAGE(STATUS " With Python3 = NO (WITH_PYTHON=OFF)") ELSE() MESSAGE(STATUS " With Python3 = NO (python not found)") ENDIF() IF(WITH_MADGWICK) MESSAGE(STATUS " With Madgwick = YES (License: GPL)") ELSE() MESSAGE(STATUS " With Madgwick = NO (WITH_MADGWICK=OFF)") ENDIF() IF(FastCV_FOUND) MESSAGE(STATUS " With FastCV = YES (License: Apache v2)") ELSEIF(NOT WITH_FASTCV) MESSAGE(STATUS " With FastCV = NO (WITH_FASTCV=OFF)") ELSE() MESSAGE(STATUS " With FastCV = NO (FastCV not found)") ENDIF() IF(PDAL_FOUND) MESSAGE(STATUS " With PDAL ${PDAL_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_PDAL) MESSAGE(STATUS " With PDAL = NO (WITH_PDAL=OFF)") ELSE() MESSAGE(STATUS " With PDAL = NO (PDAL not found)") ENDIF() IF(libLAS_FOUND) MESSAGE(STATUS " With libLAS ${libLAS_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_LIBLAS) MESSAGE(STATUS " With libLAS = NO (WITH_LIBLAS=OFF)") ELSE() MESSAGE(STATUS " With libLAS = NO (libLAS not found)") ENDIF() IF(CudaSift_FOUND) MESSAGE(STATUS " With CudaSift = YES (License: MIT)") ELSEIF(NOT WITH_CUDASIFT) MESSAGE(STATUS " With CudaSift = NO (WITH_CUDASIFT=OFF)") ELSE() MESSAGE(STATUS " With CudaSift = NO (CudaSift not found, use https://github.com/matlabbe/CudaSift fork)") ENDIF() MESSAGE(STATUS "") MESSAGE(STATUS " Solvers:") IF(WITH_TORO) MESSAGE(STATUS " With TORO = YES (License: Creative Commons [Attribution-NonCommercial-ShareAlike])") ELSE() MESSAGE(STATUS " With TORO = NO (WITH_TORO=OFF)") ENDIF() IF(G2O_FOUND) MESSAGE(STATUS " *With g2o ${g2o_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_G2O) MESSAGE(STATUS " *With g2o = NO (WITH_G2O=OFF)") ELSE() MESSAGE(STATUS " *With g2o = NO (g2o not found)") ENDIF() IF(GTSAM_FOUND) MESSAGE(STATUS " *With GTSAM ${GTSAM_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_GTSAM) MESSAGE(STATUS " *With GTSAM = NO (WITH_GTSAM=OFF)") ELSE() MESSAGE(STATUS " *With GTSAM = NO (GTSAM not found)") ENDIF() IF(WITH_CERES AND CERES_FOUND) MESSAGE(STATUS " *With Ceres ${Ceres_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_CERES) MESSAGE(STATUS " *With Ceres = NO (WITH_CERES=OFF)") ELSE() MESSAGE(STATUS " *With Ceres = NO (Ceres not found)") ENDIF() IF(MRPT_FOUND) MESSAGE(STATUS " With MRPT ${MRPT_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_MRPT) MESSAGE(STATUS " With MRPT = NO (WITH_MRPT=OFF)") ELSE() MESSAGE(STATUS " With MRPT = NO (MRPT not found)") ENDIF() IF(G2O_FOUND OR GTSAM_FOUND) IF(WITH_VERTIGO) MESSAGE(STATUS " With VERTIGO = YES (License: GPLv3)") ELSE() MESSAGE(STATUS " With VERTIGO = NO (WITH_VERTIGO=OFF)") ENDIF() ELSE() MESSAGE(STATUS " With VERTIGO = NO (GTSAM or g2o required)") ENDIF() IF(cvsba_FOUND) MESSAGE(STATUS " With cvsba = YES (License: GPLv2)") ELSEIF(NOT WITH_CVSBA) MESSAGE(STATUS " With cvsba = NO (WITH_CVSBA=OFF)") ELSE() MESSAGE(STATUS " With cvsba = NO (cvsba not found)") ENDIF() IF(libpointmatcher_FOUND) MESSAGE(STATUS " *With libpointmatcher ${libpointmatcher_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_POINTMATCHER) MESSAGE(STATUS " *With libpointmatcher = NO (WITH_POINTMATCHER=OFF)") ELSE() MESSAGE(STATUS " *With libpointmatcher = NO (libpointmatcher not found)") ENDIF() IF(CCCoreLib_FOUND) MESSAGE(STATUS " With CCCoreLib = YES (License: GPLv2)") ELSEIF(NOT WITH_CCCORELIB) MESSAGE(STATUS " With CCCoreLib = NO (WITH_CCCORELIB=OFF)") ELSE() MESSAGE(STATUS " With CCCoreLib = NO (CCCoreLib not found)") ENDIF() IF(Open3D_FOUND) MESSAGE(STATUS " With Open3D = YES (License: MIT)") ELSEIF(NOT WITH_OPEN3D) MESSAGE(STATUS " With Open3D = NO (WITH_OPEN3D=OFF)") ELSEIF(${CMAKE_VERSION} VERSION_LESS "3.19.0") MESSAGE(STATUS " With Open3D = NO (Open3D requires CMake>=3.19)") ELSE() MESSAGE(STATUS " With Open3D = NO (Open3D not found)") ENDIF() IF(opengv_FOUND AND WITH_OPENGV) MESSAGE(STATUS " With OpenGV ${opengv_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_OPENGV) MESSAGE(STATUS " With OpenGV = NO (WITH_OPENGV=OFF)") ELSE() MESSAGE(STATUS " With OpenGV = NO (OpenGV not found)") ENDIF() MESSAGE(STATUS "") MESSAGE(STATUS " Reconstruction Approaches:") IF(octomap_FOUND) MESSAGE(STATUS " With OctoMap ${octomap_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_OCTOMAP) MESSAGE(STATUS " With OctoMap = NO (WITH_OCTOMAP=OFF)") ELSE() MESSAGE(STATUS " With OctoMap = NO (octomap not found)") ENDIF() IF(grid_map_core_FOUND) MESSAGE(STATUS " With GridMap ${grid_map_core_VERSION} = YES (License: BSD)") ELSEIF(NOT WITH_OCTOMAP) MESSAGE(STATUS " With GridMap = NO (WITH_GRIDMAP=OFF)") ELSE() MESSAGE(STATUS " With GridMap = NO (grid_map_core not found)") ENDIF() IF(CPUTSDF_FOUND) MESSAGE(STATUS " With CPUTSDF = YES (License: BSD)") ELSEIF(NOT WITH_CPUTSDF) MESSAGE(STATUS " With CPUTSDF = NO (WITH_CPUTSDF=OFF)") ELSE() MESSAGE(STATUS " With CPUTSDF = NO (CPUTSDF not found)") ENDIF() IF(open_chisel_FOUND) MESSAGE(STATUS " With OpenChisel = YES (License: ???)") ELSEIF(NOT WITH_OPENCHISEL) MESSAGE(STATUS " With OpenChisel = NO (WITH_OPENCHISEL=OFF)") ELSE() MESSAGE(STATUS " With OpenChisel = NO (open_chisel not found)") ENDIF() IF(AliceVision_FOUND) MESSAGE(STATUS " With AliceVision ${AliceVision_VERSION} = YES (License: MPLv2)") ELSEIF(NOT WITH_ALICE_VISION) MESSAGE(STATUS " With AliceVision = NO (WITH_ALICE_VISION=OFF)") ELSE() MESSAGE(STATUS " With AliceVision = NO (AliceVision not found)") ENDIF() MESSAGE(STATUS "") MESSAGE(STATUS " Camera Drivers:") IF(Freenect_FOUND) MESSAGE(STATUS " With Freenect = YES (License: Apache v2 and/or GPLv2)") ELSEIF(NOT WITH_FREENECT) MESSAGE(STATUS " With Freenect = NO (WITH_FREENECT=OFF)") ELSE() MESSAGE(STATUS " With Freenect = NO (libfreenect not found)") ENDIF() IF(WITH_OPENNI AND OpenNI_FOUND) MESSAGE(STATUS " With OpenNI = YES (License: Apache v2)") ELSEIF(NOT WITH_OPENNI) MESSAGE(STATUS " With OpenNI = NO (WITH_OPENNI=OFF)") ELSE() MESSAGE(STATUS " With OpenNI = NO (OpenNI not found)") ENDIF() IF(OpenNI2_FOUND) MESSAGE(STATUS " With OpenNI2 = YES (License: Apache v2)") ELSEIF(NOT WITH_OPENNI2) MESSAGE(STATUS " With OpenNI2 = NO (WITH_OPENNI2=OFF)") ELSE() MESSAGE(STATUS " With OpenNI2 = NO (OpenNI2 not found)") ENDIF() IF(freenect2_FOUND) MESSAGE(STATUS " With Freenect2 = YES (License: Apache v2 and/or GPLv2)") ELSEIF(NOT WITH_FREENECT2) MESSAGE(STATUS " With Freenect2 = NO (WITH_FREENECT2=OFF)") ELSE() MESSAGE(STATUS " With Freenect2 = NO (libfreenect2 not found)") ENDIF() IF(KinectSDK2_FOUND) MESSAGE(STATUS " With Kinect for Windows 2 = YES (License: Apache v2 and/or GPLv2)") ELSEIF(NOT WITH_K4W2) MESSAGE(STATUS " With Kinect for Windows 2 = NO (WITH_K4W2=OFF)") ELSE() MESSAGE(STATUS " With Kinect for Windows 2 = NO (Kinect for Windows 2 SDK not found)") ENDIF() IF(k4a_FOUND) MESSAGE(STATUS " With Kinect for Azure = YES (License: MIT)") ELSEIF(NOT WITH_K4A) MESSAGE(STATUS " With Kinect for Azure = NO (WITH_K4A=OFF)") ELSE() MESSAGE(STATUS " With Kinect for Azure = NO (Kinect for Azure SDK not found)") ENDIF() IF(DC1394_FOUND) MESSAGE(STATUS " With dc1394 = YES (License: LGPL)") ELSEIF(NOT WITH_DC1394) MESSAGE(STATUS " With dc1394 = NO (WITH_DC1394=OFF)") ELSE() MESSAGE(STATUS " With dc1394 = NO (dc1394 not found)") ENDIF() IF(FlyCapture2_FOUND) MESSAGE(STATUS " With FlyCapture2/Triclops = YES") ELSEIF(NOT WITH_FLYCAPTURE2) MESSAGE(STATUS " With FlyCapture2/Triclops = NO (WITH_FLYCAPTURE2=OFF)") ELSE() MESSAGE(STATUS " With FlyCapture2/Triclops = NO (Point Grey SDK not found)") ENDIF() IF(ZED_FOUND AND CUDA_FOUND) MESSAGE(STATUS " With ZED = YES") ELSEIF(NOT WITH_ZED) MESSAGE(STATUS " With ZED = NO (WITH_ZED=OFF)") ELSE() MESSAGE(STATUS " With ZED = NO (ZED sdk and/or cuda not found)") ENDIF() IF(ZEDOC_FOUND) MESSAGE(STATUS " With ZEDOC = YES") ELSEIF(NOT WITH_ZEDOC) MESSAGE(STATUS " With ZEDOC = NO (WITH_ZEDOC=OFF)") ELSE() MESSAGE(STATUS " With ZEDOC = NO (ZED Open Capture not found)") ENDIF() IF(RealSense_FOUND) MESSAGE(STATUS " With RealSense = YES (License: Apache-2)") IF(RealSenseSlam_FOUND) MESSAGE(STATUS " With RealSenseSlam = YES") ELSEIF(NOT WITH_REALSENSE) MESSAGE(STATUS " With RealSenseSlam = NO (librealsense_slam not found)") ELSE() MESSAGE(STATUS " With RealSenseSlam = NO (WITH_REALSENSE_SLAM=OFF)") ENDIF() ELSEIF(NOT WITH_REALSENSE) MESSAGE(STATUS " With RealSense = NO (WITH_REALSENSE=OFF)") ELSE() MESSAGE(STATUS " With RealSense = NO (librealsense not found)") ENDIF() IF(realsense2_FOUND) IF(WIN32) MESSAGE(STATUS " With RealSense2 = YES (License: Apache-2)") ELSE() MESSAGE(STATUS " With RealSense2 ${realsense2_VERSION} = YES (License: Apache-2)") ENDIF() ELSEIF(NOT WITH_REALSENSE2) MESSAGE(STATUS " With RealSense2 = NO (WITH_REALSENSE2=OFF)") ELSE() MESSAGE(STATUS " With RealSense2 = NO (librealsense2 not found)") ENDIF() IF(mynteye_FOUND) MESSAGE(STATUS " With MyntEyeS = YES (License: Apache-2)") ELSEIF(NOT WITH_MYNTEYE) MESSAGE(STATUS " With MyntEyeS = NO (WITH_MYNTEYE=OFF)") ELSE() MESSAGE(STATUS " With MyntEyeS = NO (mynteye s sdk not found)") ENDIF() IF(depthai_FOUND) MESSAGE(STATUS " With DepthAI ${depthai_VERSION} = YES (License: MIT)") ELSEIF(NOT WITH_DEPTHAI) MESSAGE(STATUS " With DepthAI = NO (WITH_DEPTHAI=OFF)") ELSE() MESSAGE(STATUS " With DepthAI = NO (depthai-core not found)") ENDIF() IF(xvsdk_FOUND) MESSAGE(STATUS " With XVisio SDK ${xvsdk_VERSION} = YES") ELSEIF(NOT WITH_XVSDK) MESSAGE(STATUS " With XVisio SDK = NO (WITH_XVSDK=OFF)") ELSE() MESSAGE(STATUS " With XVisio SDK = NO (xvsdk not found)") ENDIF() MESSAGE(STATUS "") MESSAGE(STATUS " Odometry Approaches:") IF(loam_velodyne_FOUND) MESSAGE(STATUS " With loam_velodyne = YES (License: BSD)") ELSEIF(NOT WITH_LOAM) MESSAGE(STATUS " With loam_velodyne = NO (WITH_LOAM=OFF)") ELSE() MESSAGE(STATUS " With loam_velodyne = NO (loam_velodyne not found)") ENDIF() IF(floam_FOUND) MESSAGE(STATUS " With floam = YES (License: BSD)") ELSEIF(NOT WITH_FLOAM) MESSAGE(STATUS " With floam = NO (WITH_FLOAM=OFF)") ELSE() MESSAGE(STATUS " With floam = NO (floam not found)") ENDIF() IF(libfovis_FOUND) MESSAGE(STATUS " With libfovis = YES (License: GPLv2)") ELSEIF(NOT WITH_FOVIS) MESSAGE(STATUS " With libfovis = NO (WITH_FOVIS=OFF)") ELSE() MESSAGE(STATUS " With libfovis = NO (libfovis not found)") ENDIF() IF(libviso2_FOUND) MESSAGE(STATUS " With libviso2 = YES (License: GPLv3)") ELSEIF(NOT WITH_VISO2) MESSAGE(STATUS " With libviso2 = NO (WITH_VISO2=OFF)") ELSE() MESSAGE(STATUS " With libviso2 = NO (libviso2 not found)") ENDIF() IF(dvo_core_FOUND) MESSAGE(STATUS " With dvo_core = YES (License: GPLv3)") ELSEIF(NOT WITH_DVO) MESSAGE(STATUS " With dvo_core = NO (WITH_DVO=OFF)") ELSE() MESSAGE(STATUS " With dvo_core = NO (dvo_core not found)") ENDIF() IF(okvis_FOUND) MESSAGE(STATUS " With okvis = YES (License: BSD)") ELSEIF(NOT WITH_OKVIS) MESSAGE(STATUS " With okvis = NO (WITH_OKVIS=OFF)") ELSE() MESSAGE(STATUS " With okvis = NO (okvis not found)") ENDIF() IF(msckf_vio_FOUND) MESSAGE(STATUS " With msckf_vio = YES (License: Penn Software License)") ELSEIF(NOT WITH_MSCKF_VIO) MESSAGE(STATUS " With msckf_vio = NO (WITH_MSCKF_VIO=OFF)") ELSE() MESSAGE(STATUS " With msckf_vio = NO (msckf_vio not found)") ENDIF() IF(vins_FOUND) MESSAGE(STATUS " With VINS-Fusion = YES (License: GPLv3)") ELSEIF(NOT WITH_VINS) MESSAGE(STATUS " With VINS-Fusion = NO (WITH_VINS=OFF)") ELSE() MESSAGE(STATUS " With VINS-Fusion = NO (VINS-Fusion not found)") ENDIF() IF(ov_msckf_FOUND) MESSAGE(STATUS " With OpenVINS = YES (License: GPLv3)") ELSEIF(NOT WITH_OPENVINS) MESSAGE(STATUS " With OpenVINS = NO (WITH_OPENVINS=OFF)") ELSE() MESSAGE(STATUS " With OpenVINS = NO (ov_msckf not found)") ENDIF() IF(ORB_SLAM_FOUND) MESSAGE(STATUS " With ORB_SLAM${ORB_SLAM_VERSION} = YES (License: GPLv3)") ELSEIF(NOT WITH_ORB_SLAM) MESSAGE(STATUS " With ORB_SLAM = NO (WITH_ORB_SLAM=OFF)") ELSEIF(G2O_FOUND) MESSAGE(STATUS " With ORB_SLAM = NO (WITH_G2O should be OFF as ORB_SLAM uses its own g2o version)") ELSE() MESSAGE(STATUS " With ORB_SLAM = NO (ORB_SLAM2 and ORB_SLAM3 not found, make sure environment variable ORB_SLAM_ROOT_DIR is set)") ENDIF() MESSAGE(STATUS "Show all options with: cmake -LA | grep WITH_") MESSAGE(STATUS "--------------------------------------------") IF(NOT GTSAM_FOUND AND NOT G2O_FOUND AND NOT WITH_TORO AND NOT WITH_CERES AND NOT CERES_FOUND) MESSAGE(SEND_ERROR "No graph optimizer found! You should have at least one of these options: g2o (https://github.com/RainerKuemmerle/g2o) GTSAM (https://collab.cc.gatech.edu/borg/gtsam) Ceres (http://ceres-solver.org) set -DWITH_TORO=ON") ENDIF(NOT GTSAM_FOUND AND NOT G2O_FOUND AND NOT WITH_TORO AND NOT WITH_CERES AND NOT CERES_FOUND) # vim: set et ft=cmake fenc=utf-8 ff=unix sts=0 sw=2 ts=2 : rtabmap-0.22.1/LICENSE000066400000000000000000000031461503504370700142370ustar00rootroot00000000000000RTAB-Map - https://github.com/introlab/rtabmap Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke, all rights reserved. Copyright (c) XXX, contributors, all rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holders nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. rtabmap-0.22.1/README.md000066400000000000000000000077121503504370700145140ustar00rootroot00000000000000rtabmap ======= [![RTAB-Map Logo](https://raw.githubusercontent.com/introlab/rtabmap/master/guilib/src/images/RTAB-Map100.png)](http://introlab.github.io/rtabmap) [![Release][release-image]][releases] [![Downloads][downloads-image]][downloads] [![License][license-image]][license] [release-image]: https://img.shields.io/badge/release-0.21.4-green.svg?style=flat [releases]: https://github.com/introlab/rtabmap/releases [downloads-image]: https://img.shields.io/github/downloads/introlab/rtabmap/total?label=downloads [downloads]: https://github.com/introlab/rtabmap/releases [license-image]: https://img.shields.io/badge/license-BSD-green.svg?style=flat [license]: https://github.com/introlab/rtabmap/blob/master/LICENSE RTAB-Map library and standalone application. * For more information (e.g., papers, major updates), visit [RTAB-Map's home page](http://introlab.github.io/rtabmap). * For installation instructions and examples, visit [RTAB-Map's wiki](https://github.com/introlab/rtabmap/wiki). To use RTAB-Map under ROS, visit the [rtabmap](http://wiki.ros.org/rtabmap) page on the ROS wiki. ### Acknowledgements This project is supported by [IntRoLab - Intelligent / Interactive / Integrated / Interdisciplinary Robot Lab](https://introlab.3it.usherbrooke.ca/), Sherbrooke, Québec, Canada. IntRoLab #### CI Latest
Linux Build Status
Build Status
Build Status
Windows Build Status
#### ROS Binaries `ros-$ROS_DISTRO-rtabmap`
ROS 1 Noetic Build Status
ROS 2 Humble Build Status
Jazzy Build Status
Rolling Build Status
Docker rtabmap Docker Pulls
rtabmap-0.22.1/RTABMapConfig.cmake.in000066400000000000000000000062421503504370700171550ustar00rootroot00000000000000include(CMakeFindDependencyMacro) # Mandatory dependencies find_dependency(OpenCV COMPONENTS core calib3d imgproc highgui stitching photo video OPTIONAL_COMPONENTS aruco objdetect xfeatures2d nonfree gpu cudafeatures2d) if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/RTABMap_guiTargets.cmake") find_dependency(PCL 1.7 COMPONENTS common io kdtree search surface filters registration sample_consensus segmentation visualization) if(@CONF_QT_VERSION@ EQUAL 6) find_dependency(Qt6 COMPONENTS Widgets Core Gui OpenGL) elseif(@CONF_QT_VERSION@ EQUAL 5) find_dependency(Qt5 COMPONENTS Widgets Core Gui OpenGL) else() # Qt4 find_dependency(Qt4 COMPONENTS QtCore QtGui) endif() set(RTABMap_QT_VERSION @CONF_QT_VERSION@) ELSE() find_dependency(PCL 1.7 COMPONENTS common io kdtree search surface filters registration sample_consensus segmentation) ENDIF() set(RTABMap_DEFINITIONS ${PCL_DEFINITIONS}) add_definitions(${RTABMap_DEFINITIONS}) # To include -march=native if set # Optional dependencies IF(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@CONF_MODULES_DIR@") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/@CONF_MODULES_DIR@") ENDIF() IF(@CONF_WITH_REALSENSE2@) IF(WIN32) find_dependency(RealSense2) ELSE() find_dependency(realsense2) ENDIF() ENDIF() IF(@CONF_WITH_K4A@) IF(WIN32) find_dependency(K4A) ELSE() find_dependency(k4a) find_dependency(k4arecord) ENDIF() ENDIF() IF(@CONF_WITH_DEPTH_AI@) find_dependency(depthai 2.24) ENDIF() IF(@CONF_WITH_XVSDK@) find_dependency(xvsdk) ENDIF() IF(@CONF_WITH_OCTOMAP@) find_dependency(octomap) ENDIF() IF(@CONF_WITH_PYTHON@) find_dependency(Python3 COMPONENTS Interpreter Development NumPy) ENDIF() # Provide those for backward compatibilities (e.g., catkin requires them to propagate dependencies) set(RTABMap_INCLUDE_DIRS "") set(RTABMap_LIBRARIES "") set(RTABMap_TARGETS "") set(_RTABMap_supported_components utilite core gui) foreach(_comp ${_RTABMap_supported_components}) if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/RTABMap_${_comp}Targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/RTABMap_${_comp}Targets.cmake") set(RTABMap_${_comp}_FOUND True) set(RTABMap_TARGETS ${RTABMap_TARGETS} rtabmap::${_comp}) get_target_property(RTABMap_${_comp}_INCLUDE_DIRS rtabmap::${_comp} INTERFACE_INCLUDE_DIRECTORIES) get_target_property(RTABMap_${_comp}_LIBRARIES rtabmap::${_comp} INTERFACE_LINK_LIBRARIES) set(RTABMap_INCLUDE_DIRS ${RTABMap_INCLUDE_DIRS} ${RTABMap_${_comp}_INCLUDE_DIRS}) set(RTABMap_LIBRARIES ${RTABMap_LIBRARIES} rtabmap::${_comp}) if(RTABMap_${_comp}_LIBRARIES) set(RTABMap_LIBRARIES ${RTABMap_LIBRARIES} ${RTABMap_${_comp}_LIBRARIES}) endif() else() set(RTABMap_${_comp}_FOUND False) endif() endforeach() include("${CMAKE_CURRENT_LIST_DIR}/RTABMapTargets.cmake") foreach(_comp ${RTABMap_FIND_COMPONENTS}) if (NOT RTABMap_${_comp}_FOUND) if(${RTABMap_FIND_REQUIRED_${_comp}}) set(RTABMap_FOUND False) set(RTABMap_NOT_FOUND_MESSAGE "Unsupported or not found required component: ${_comp}") endif() endif() endforeach() rtabmap-0.22.1/Version.h.in000066400000000000000000000100471503504370700154330ustar00rootroot00000000000000/* Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERTY DE SHERBROOKE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VERSION_H_ #define VERSION_H_ // This is auto-generated! #define RTABMAP_VERSION "@PROJECT_VERSION@" #define RTABMAP_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ #define RTABMAP_VERSION_MINOR @PROJECT_VERSION_MINOR@ #define RTABMAP_VERSION_PATCH @PROJECT_VERSION_PATCH@ #define RTABMAP_VERSION_COMPARE(major, minor, patch) (major>=@PROJECT_VERSION_MAJOR@ || (major==@PROJECT_VERSION_MAJOR@ && minor>=@PROJECT_VERSION_MINOR@) || (major==@PROJECT_VERSION_MAJOR@ && minor==@PROJECT_VERSION_MINOR@ && patch >=@PROJECT_VERSION_PATCH@)) @NONFREE@#define RTABMAP_NONFREE @TORO@#define RTABMAP_TORO @G2O@#define RTABMAP_G2O @G2O_CPP_CONF@#define RTABMAP_G2O_CPP11 @G2O_CPP11@ @GTSAM@#define RTABMAP_GTSAM @CERES@#define RTABMAP_CERES @MRPT@#define RTABMAP_MRPT @VERTIGO@#define RTABMAP_VERTIGO @OPENNI@#define RTABMAP_OPENNI @OPENNI2@#define RTABMAP_OPENNI2 @FREENECT@#define RTABMAP_FREENECT @FREENECT2@#define RTABMAP_FREENECT2 @K4W2@#define RTABMAP_K4W2 @K4A@#define RTABMAP_K4A @CVSBA@#define RTABMAP_CVSBA @POINTMATCHER@#define RTABMAP_POINTMATCHER @CCCORELIB@#define RTABMAP_CCCORELIB @OPEN3D@#define RTABMAP_OPEN3D @FASTCV@#define RTABMAP_FASTCV @OPENGV@#define RTABMAP_OPENGV @PDAL@#define RTABMAP_PDAL @LIBLAS@#define RTABMAP_LIBLAS @CUDASIFT@#define RTABMAP_CUDASIFT @LOAM@#define RTABMAP_LOAM @FLOAM@#define RTABMAP_FLOAM @DC1394@#define RTABMAP_DC1394 @FLYCAPTURE2@#define RTABMAP_FLYCAPTURE2 @ZED@#define RTABMAP_ZED @ZEDOC@#define RTABMAP_ZEDOC @REALSENSE@#define RTABMAP_REALSENSE @REALSENSESLAM@#define RTABMAP_REALSENSE_SLAM @REALSENSE2@#define RTABMAP_REALSENSE2 @MYNTEYE@#define RTABMAP_MYNTEYE @DEPTHAI@#define RTABMAP_DEPTHAI @XVSDK@#define RTABMAP_XVSDK @OCTOMAP@#define RTABMAP_OCTOMAP @GRIDMAP@#define RTABMAP_GRIDMAP @CPUTSDF@#define RTABMAP_CPUTSDF @ALICE_VISION@#define RTABMAP_ALICE_VISION @OPENCHISEL@#define RTABMAP_OPENCHISEL @FOVIS@#define RTABMAP_FOVIS @VISO2@#define RTABMAP_VISO2 @DVO@#define RTABMAP_DVO @OKVIS@#define RTABMAP_OKVIS @MSCKF_VIO@#define RTABMAP_MSCKF_VIO @VINS@#define RTABMAP_VINS @OPENVINS@#define RTABMAP_OPENVINS @ORB_SLAM@#define RTABMAP_ORB_SLAM @ORB_SLAM_VERSION@ @ORB_OCTREE@#define RTABMAP_ORB_OCTREE @TORCH@#define RTABMAP_TORCH @PYTHON@#define RTABMAP_PYTHON @MADGWICK@#define RTABMAP_MADGWICK #include #if PCL_VERSION_COMPARE(>, 1, 11, 1) #include #define RTABMAP_PCL_INDEX pcl::index_t #elif PCL_VERSION_COMPARE(>=, 1, 10, 0) #define RTABMAP_PCL_INDEX std::uint32_t #else #include #define RTABMAP_PCL_INDEX pcl::uint32_t #endif #endif /* VERSION_H_ */ rtabmap-0.22.1/app/000077500000000000000000000000001503504370700140065ustar00rootroot00000000000000rtabmap-0.22.1/app/CMakeLists.txt000066400000000000000000000001251503504370700165440ustar00rootroot00000000000000 IF(ANDROID) ADD_SUBDIRECTORY( android ) ELSE() ADD_SUBDIRECTORY( src ) ENDIF()rtabmap-0.22.1/app/android/000077500000000000000000000000001503504370700154265ustar00rootroot00000000000000rtabmap-0.22.1/app/android/.gitignore000066400000000000000000000000141503504370700174110ustar00rootroot00000000000000/bin/ /gen/ rtabmap-0.22.1/app/android/.settings/000077500000000000000000000000001503504370700173445ustar00rootroot00000000000000rtabmap-0.22.1/app/android/.settings/org.eclipse.jdt.core.prefs000066400000000000000000000011131503504370700243220ustar00rootroot00000000000000eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.source=1.6 rtabmap-0.22.1/app/android/AndroidManifest.xml.in000066400000000000000000000102671503504370700216320ustar00rootroot00000000000000 rtabmap-0.22.1/app/android/CMakeLists.txt000066400000000000000000000141241503504370700201700ustar00rootroot00000000000000 option(WITH_TANGO "Include Tango support" ON) option(WITH_ARCORE "Include ARCore support" ON) option(WITH_ARENGINE "Include AREngine support" ON) option(DISABLE_LOG "Disable Android logging (should be true in release)" ON) option(DEPTH_TEST "Enable depth test on ARCore" OFF) # Google Tango needs access to system shared # libraries (e.g. libbinder.so) that are not accessible # with android >=24 IF(WITH_TANGO AND ${ANDROID_NATIVE_API_LEVEL} LESS 24) FIND_PACKAGE(Tango QUIET) IF(Tango_FOUND) MESSAGE(STATUS "Found Tango: ${Tango_INCLUDE_DIRS}") ENDIF(Tango_FOUND) ENDIF(WITH_TANGO AND ${ANDROID_NATIVE_API_LEVEL} LESS 24) IF(WITH_ARCORE AND ${ANDROID_NATIVE_API_LEVEL} GREATER 22) FIND_PACKAGE(ARCore QUIET) IF(ARCore_FOUND) MESSAGE(STATUS "Found ARCore: ${ARCore_INCLUDE_DIRS}") ENDIF(ARCore_FOUND) ENDIF(WITH_ARCORE AND ${ANDROID_NATIVE_API_LEVEL} GREATER 22) IF(WITH_ARENGINE AND ${ANDROID_NATIVE_API_LEVEL} GREATER 23) FIND_PACKAGE(AREngine QUIET) IF(AREngine_FOUND) MESSAGE(STATUS "Found AREngine: ${AREngine_INCLUDE_DIRS}") ENDIF(AREngine_FOUND) ENDIF(WITH_ARENGINE AND ${ANDROID_NATIVE_API_LEVEL} GREATER 23) IF(NOT Tango_FOUND) SET(TANGO "//") ENDIF(NOT Tango_FOUND) IF(NOT ARCore_FOUND) SET(ARCORE "//") ENDIF(NOT ARCore_FOUND) IF(NOT AREngine_FOUND) SET(ARENGINE "//") ENDIF(NOT AREngine_FOUND) CONFIGURE_FILE(CameraAvailability.h.in ${CMAKE_CURRENT_SOURCE_DIR}/jni/CameraAvailability.h) IF(DISABLE_LOG) ADD_DEFINITIONS(-DDISABLE_LOG) ENDIF(DISABLE_LOG) IF(DEPTH_TEST) ADD_DEFINITIONS(-DDEPTH_TEST) ENDIF(DEPTH_TEST) MESSAGE(STATUS "--------------------------------------------") MESSAGE(STATUS "Android build info:") MESSAGE(STATUS " DISABLE_LOG = ${DISABLE_LOG}") MESSAGE(STATUS " DEPTH_TEST = ${DEPTH_TEST}") IF(Tango_FOUND) MESSAGE(STATUS " With Tango = YES") ELSEIF(NOT WITH_TANGO) MESSAGE(STATUS " With Tango = NO (WITH_TANGO=OFF)") ELSE() IF(${ANDROID_NATIVE_API_LEVEL} GREATER 23) MESSAGE(STATUS " With Tango = NO (ANDROID_NATIVE_API_LEVEL should be <= 23)") ELSE() MESSAGE(STATUS " With Tango = NO (tango not found)") ENDIF() ENDIF() IF(ARCore_FOUND) MESSAGE(STATUS " With ARCore = YES") ELSEIF(NOT WITH_ARCORE) MESSAGE(STATUS " With ARCore = NO (WITH_ARCORE=OFF)") ELSE() IF(${ANDROID_NATIVE_API_LEVEL} LESS 23) MESSAGE(STATUS " With ARCore = NO (ANDROID_NATIVE_API_LEVEL should be >= 23)") ELSE() MESSAGE(STATUS " With ARCore = NO (ARCore not found)") ENDIF() ENDIF() IF(AREngine_FOUND) MESSAGE(STATUS " With AREngine = YES") ELSEIF(NOT WITH_ARENGINE) MESSAGE(STATUS " With AREngine = NO (WITH_ARENGINE=OFF)") ELSE() IF(${ANDROID_NATIVE_API_LEVEL} LESS 24) MESSAGE(STATUS " With AREngine = NO (ANDROID_NATIVE_API_LEVEL should be >= 24)") ELSE() MESSAGE(STATUS " With AREngine = NO (AREngine not found)") ENDIF() ENDIF() MESSAGE(STATUS " ANDROID_NATIVE_API_LEVEL = ${ANDROID_NATIVE_API_LEVEL}") MESSAGE(STATUS " ANDROID_COMPILER_FLAGS_RELEASE = ${ANDROID_COMPILER_FLAGS_RELEASE}") MESSAGE(STATUS " ANDROID_TOOLCHAIN_PREFIX = ${ANDROID_TOOLCHAIN_PREFIX}") IF(DISABLE_LOG) SET(ANDROID_DEBUGGABLE false) ELSE() SET(ANDROID_DEBUGGABLE true) ENDIF() add_subdirectory(jni) ###### # Packaging ###### # find android find_host_program(ANDROID_EXECUTABLE NAMES android DOC "The android command-line tool") if(NOT ANDROID_EXECUTABLE) message(FATAL_ERROR "Can not find android command line tool: android") endif() configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.in" "${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml" @ONLY) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml" "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" COPYONLY) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/info.txt.in" "${CMAKE_CURRENT_SOURCE_DIR}/res/raw/info.txt") configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/ant.properties.in" "${CMAKE_CURRENT_BINARY_DIR}/ant.properties" @ONLY) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/custom_rules.xml" "${CMAKE_CURRENT_BINARY_DIR}/custom_rules.xml" COPYONLY) add_custom_target(NativeRTABMap-ant-configure ALL COMMAND "${ANDROID_EXECUTABLE}" update project --name RTABMap --path "${CMAKE_CURRENT_SOURCE_DIR}" --target "android-${ANDROID_NATIVE_API_LEVEL}" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/build.xml" "${CMAKE_CURRENT_BINARY_DIR}/build.xml" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/local.properties" "${CMAKE_CURRENT_BINARY_DIR}/local.properties" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/project.properties" "${CMAKE_CURRENT_BINARY_DIR}/project.properties" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/proguard-project.txt" "${CMAKE_CURRENT_BINARY_DIR}/proguard-project.txt" COMMAND "${CMAKE_COMMAND}" -E remove "${CMAKE_CURRENT_SOURCE_DIR}/build.xml" "${CMAKE_CURRENT_SOURCE_DIR}/local.properties" "${CMAKE_CURRENT_SOURCE_DIR}/project.properties" "${CMAKE_CURRENT_SOURCE_DIR}/proguard-project.txt" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") add_dependencies(NativeRTABMap-ant-configure NativeRTABMap) #find ant find_host_program(ANT_EXECUTABLE NAMES ant DOC "The ant build tool") if(NOT ANT_EXECUTABLE) message(FATAL_ERROR "Can not find ant build tool: ant") endif() add_custom_target(NativeRTABMap-apk-release ALL COMMAND ${ANT_EXECUTABLE} -file "${CMAKE_CURRENT_BINARY_DIR}/build.xml" release) add_dependencies(NativeRTABMap-apk-release NativeRTABMap-ant-configure NativeRTABMap) add_custom_target(NativeRTABMap-apk-debug ALL COMMAND ${ANT_EXECUTABLE} -file "${CMAKE_CURRENT_BINARY_DIR}/build.xml" debug) add_dependencies(NativeRTABMap-apk-debug # NativeRTABMap-apk-release NativeRTABMap-ant-configure NativeRTABMap) rtabmap-0.22.1/app/android/CameraAvailability.h.in000066400000000000000000000033731503504370700217350ustar00rootroot00000000000000/* Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERTY DE SHERBROOKE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CAMERAAVAILABILITY_H_ #define CAMERAAVAILABILITY_H_ // This is auto-generated! @TANGO@#define RTABMAP_TANGO @ARCORE@#define RTABMAP_ARCORE @ARENGINE@#define RTABMAP_ARENGINE #endif /* CAMERAAVAILABILITY_H_ */ rtabmap-0.22.1/app/android/ant.properties.in000066400000000000000000000005161503504370700207350ustar00rootroot00000000000000builddir=@CMAKE_CURRENT_BINARY_DIR@ srcdir=@CMAKE_CURRENT_SOURCE_DIR@ android.abi=@ANDROID_ABI@ source.dir=${srcdir}/src gen.dir=${builddir}/gen out.dir=${builddir}/bin asset.dir=${builddir}/assets resource.absolute.dir=${srcdir}/res jar.libs.dir=${builddir}/libs external.libs.dir=${builddir}/libs native.libs.dir=${builddir}/libs rtabmap-0.22.1/app/android/custom_rules.xml000066400000000000000000000007141503504370700206760ustar00rootroot00000000000000 rtabmap-0.22.1/app/android/ic_launcher-web.png000066400000000000000000001773671503504370700212100ustar00rootroot00000000000000‰PNG  IHDRôxÔú€IDATxÚì]˜TE³í'’3+’s%J’ "A²’D ‚ HÉ‚ äœ$Šä%H0 I$gÉúõ©Û÷ßa™™½3;áîRõ}ç{>dw{§»OW:%Ç‹éê)|¦Ð…Áxñ©B…†P_á…R ÅPT!¯B…¤ I4k$TH _!®B…Ø ±b*¼¬ðMþŽÚ —ž0/8+ÜV¸eŽ)rÀA… ‹¦)LÕ˜¢0Qá;…! ú(tÒÄãc…÷j(TÑD"³BMÒS+¤TH®‰E"…xš40Yàààð*Z*

Ix¬³z(S ¼wTa§Â¯ ¡ 4Aè©ÐH¡²BI…üºìLš„—49ø?& L FÔ&Oô~GÙáªÂ)Eا°Ua•ÂH…O„¡G(®M“‚˜á“& #à‰.1@—ðŸ0DŽGuæ`²B;ahJ+äFwCL)x‰Iƒ}³49@ÇÃaˆѽPDÂCt'ÄÕÄ “& #úf n(ìQ˜­ðµÂ‡ÂðG€ØðUÖ‰ð²&/1!àà`À`0¢PB¸" =º)4†IRF]6ˆ# b„+!ppp0`0Ñ l€–Å k…ÑyÐJgàWT— b;!œ!àà°x饗dœ8qdâĉeÒ¤IŒ I’$‘ &”qãÆ¥}bÇŽ-cĈÁÀ5€¨.‰­…áO€.ƒaØ#;‚L88‚Lp¨åÌ™SVªTIÖªU‹Áx¡P½zuùöÛoË2eÊÈ’%KFˆbÅŠÉ ÈܹsË\¹r°²dÉ"3dÈ Ó§Oÿ?¤J•J&Ož\†„„ÁŽ?>˜1cÊÿû¿ÿ‹Îd§…a›<]ޤ¦Õ™èâér£~€‰G @²dÉè >|¸œ6mƒñBaêÔ©rüøñräÈ‘ò»ï¾‹Ø'ƒ ’_ýµìÛ·/¡W¯^²k×®²C‡²]»v„6mÚÈ?üP6jÔHÖ­[WÖ¨QƒˆF¹råä›o¾) *$óæÍKD"[¶l2sæÌDR¦LI{d!…':3€–Ã_z)TÒ%‚ÂðHä@brV€ƒ#€/”-ZÈ¥K—ÊßÿÝgøí·?äæÍÈ ¶ÊU«¶ÉŸ~Ú),Ø%çÍÛM˜?—\²d§\¹r»\¿~+ýÙ_ýç߃]Ÿ?/~nüüX¬‡¹6X'¬×êÕÛhý¶lùƒÖóÅY—?m¿.¿ýöÛÿ°eËõýlkÖ¬QßÛjÂÊ•+iO-Z´Hý óåìÙ³‰pLž}d÷îÝeçÎeëÖ­eãÆe:udåÊ•)3Q´hQ™/_>Ê4d̘‘2 (_˜Ù„—_~™Ê(çÙ˜0˜Vǘ‡0ÎA/]Ó1Ûãsf€ƒ#@àÕW_•}ô‘üùçŸåü)üþûrÓ¦?Õ¸ì±c÷Ëþý¨ƒí¸lÙò”:ØÎʆ ÏðÏ}tJvìxB½¦ŽÈÑ£È~Ø£Îôwà`ì÷c'àçÙ¸ñOúùðsŽu@½¨Wã µ§i=40Ö¦I“³²U«SòóÏË« c¿üñÇÝ´®›7ÿIë×eÎc]¾úêè ±. ›6mR„g½ %ÒðÓO?)¢³@­Å" ÈNŒ1Bí£þòË/¿¤,C«V­(³P³fMY¡B*K¼öÚk2S¦L2EŠ2Q¢D2^¼xTÞ‹+‘›ƒ':+°BSk*†Uqêpd ¶“̇]€yñ/]ºSN˜°_TÇdÓ¦gd¥J—åo\—ٲݖiÒÜS‡Ó™<¹ü3þ]–,wdÁ‚×eÅŠ—åœQ/¡cDðúûå—¨Ùáûß°áO¹xñ.9fÌùÅÿ¨Ÿó¬|ë­Ë²@ôó;[›´iïÉìÙoÓúUªtI6kvZöêuTNœ¸Oý®@’¶FéµÁÅuY´h‘¿nÝþQ—ÚYu¡9®Ë}—ëR¸ðuõB¾$›7[—eË¢þº¸# È,,[¶L}–+âó£œ1ce† "{öìIä yóæò½÷Þ£ìA©R¥ÔÞ*(³fÍJûÜÔ!€ [`“¬ÀU]Àp£÷„ÑR˜Km…©t™š¸:+ÀD€ƒÃ.`Ë–­ôz›4iŸìÔ鄬^ý¢Ì—ï]j‰=’±c?Q‡ÍSõµ¤S¨sHHOd„dªT÷åë¯ß”U«^R‡ÙIº0—/ßA_#ªêø~7oÞJ—5^µíÛŸ”Uª\R/µ›ôsâçÅÏŸßÙº¨›z¹=¥õÃ:b=óç¿¡^}e—.ÇåäÉû(M޵‰šë²SŽyP¶kw’.r¬KÊ”÷e‚ž­ CÍšd×®ÿÈ)SöÉU«¢Þºx·–¿«µÜLåìa”=˜8q"• QèÔ©“"ÍHë DŒÐ [R€RÊAÌ€À_`ƒÂ`a 1*«O]iuVÀÔ ˜å&Á 8ÈñBGª)Û÷Þ;/såº%“%{èöwîøo“&}(sä¸-«U»¨^wÇ(5Œ×bT!ø>ׯß&gÏÞ+{ô8F„¯Ö$IŒµÁÏééÚ`=qé…„<¤Ë²^½sT:A <ªdJÌu™5k/eB@ˆŒuy$cÆŒüº€<Ö¯NöëwDΟ¿›J Ñ)àIÖÀ$(-¬X±B.\¸J 2‚tìØQ6iÒDí±j$J„1mÚ´¤-@é HL:<«°Qa˜BS… ÂuœS!ƒ0ăIÊL88Ip¨®[·^¡mÛžTÈêï¹ïõ!üòS"Hõ~üñ)9~ü~u˜m·ýŽïoÍšírìØ¤{(Xðü<¾Zˆ)îË’%¯ÉO?=)§NÝG«×)¬ ²:Ð~àÕîuA¡T)¬Ë µ.Ù~]<ù\!«²‡}°jÕu±ï ì4Ø(ÃAHé*[±âÆÕŸ]Gzh &Mš$‡ ¦ˆjÒ@WBÁ!:$H ßœYç5@Fà…·Š*ä† qJa´ÆçŒG€¢µk·Ñ׸ñõr¸%ãÇì6Í™C=nÜǤ!xÿýsòûïÒ%b×ßãáÃÑ uì8qûä‚{þåû”Òåyòܔ͚!‚„KÀž—ÿVº°¾ýö¬[÷¼Ìœëòįë‚, ôФ€DÕK—:J=È´ágù替eŸ>G)ƒ‚RP×®Çe÷îÿP6Ÿ;”@.ÜEû$¢2ˆÙÁHˆÈ€tëÖM6mÚ”Z¡#@÷L“Ðu@"€áD–(tU¨/Œ6Â7^†h0¥Îpi€ƒÃß— j·HùgÈpW¾¿øÃ¯DÔÀkÔ¸ §C¶$ø~P{ÆÒêüø¾ý¹.Æ«÷©:œïဘ¥»­ .°Áƒÿ–ï¼s‘^èY—'2S¦;T@ÖÁnëâ¿þºU½ò·Q·>ïè”ÁÏQ¾üÊ(åÌy›Èe¦Lw Y³Þ!"^´è¤§1‡¨räÈ$²´Z" kÇü•D‡N™2E}¦¿¡Å °¢Bh@ÔzˆÒFÃa°‹Bc…jÂh#Ì/ _ÇŒ—88|MPS…B¯¸ôéïúý w„_©Sß“µj] L€^»8\q`ã$.9|¿Zü@Æ%¿'û¬Ëvzù›— ×äm„“'ÿE¯i{×î‹æÌ½²waóòÍ7¯QÆå0dM°~Î2'fÙ Ù8tL@CS®Üùᇧ‰”"+äM{-ôÎ;Wí½ïÉôèý÷ß—%J” 1!t°H€nà¦0¬†Ç*tVh¢P]¼.21˜p0ˆ$@:qîÜ=ÔŽ†×G ^þ®tŒ¨ïBMn‡ƒ­gè‚Àhbd™”J ;€0Ð*x¬ ÒÖÈN¤Kw/hë¡aëÖÿÚf]œ§ú·R«è A‡éóöO¦¸q½Ô‚(€ ¤OO–.}U~òÉ¿”!2»j¼í:@¹¢B :”ZÑv/X!ÃÀÏ0úWC:£k ªB ]0G£k ŽxÖPˆƒƒ €'ô÷£Å/‘@ä& øFÝF1h% v)_žCæÌy‹RÏÁZè ºN¯GÄ‚¹6øÚH?ãâ±Ãº)òÕ΃½.®:#fÌø‹ ³Ê”¹Jå._i$@ ‰9¬Qã"­:$"› ~]87¦OŸNöÈp1„f {ö씀߀ŸˆÀS Ø-ŒY=>Qx_¡²óH' . p0ð– †ˆ×jÛè_÷‡xË“ú.´2e®È#½¶‹Ã{èÐC²dÉ«êÅüµA«!zâá’ÌRÖuÿ%®ÑºkMÌuI–ì¬Uë<‰%íT" ß\áóoj‘±C§:G>û쵨új X»v-µŽ=šZ 1d öÅð@yÀODà¡Î,†‘„‚†pq…ÂèHÄ$€ã…&8Ü`㊶)èÑ0Ü_’¸ÀA <þ;;¦þ网‚)NåÊ—Éà ‚IXð6n|–²-[ž¦Ë™ x \Ô»÷1Ù¿ÿ9`À²ì1â7î퓉÷Ó d ^È¢¡¤‚N•¡Cÿ–•*]¦6<»eEÐÉ‚Òͼy{|Þèi6 oß¾²V­Z4t(nܸ¾Ê`²àq…5 suIÀll­PGër+¤fÀñBX…¢_ºbÅ+¶êÿg0|•aA&„Ÿq3Sò€ ŒÒ¦½KDó'0ÿ"W®Ûòõ×oɼyo’%6Ê+ð€‘(UêMëa®Yó" ­©¡@I—+&õÁ›ßŽå´„ SçÆ¨QÁõH@Û`hh( µiÓ†¼0yÐG$m‚'Ö*̆ðH…¯>uм®Åð 0g 0 àx±ÚÜÞz‹ ƒa%k2î ¼ð±g9C;&J*‹Âˆ(C´¬fEЖˆ9(uÛ&"AX £e°R¥J¾, a¸šÜ&Èñbôףϯ;¼8¼0£¢7«ÀaŒÿ§ÀáŽÚº;# oõ"™2Ý¥¡V0ÿ²ÃÀ$dÐ)0lØ0ùî»ïªï/“Œ'Ž/²Èü£Ë µ.ÆAC„aÔL“€ B„1^˜IÇ‹£ÀˆÛjÕ.ÑeéÛç©"ÿrø¬R¥Kÿ«áF„>8+ëÕ;O©Zü|ži^¤~Ó¤¹ok`Ž„…Ê®|V×* s<šÀg¢2»ë¢žú”H£ì¹Ð  È“@0hhìØ±²Y³fd„‘Ã>ð 8¬IÚçc–À0…îš`²  e G¸À½ „@³fí¥YîHcÚõ@Ç+Y xòCÅ õvD€ ë‡öKüØ=¦·õèqŒê¾vt9èXؽ6hpN~üñ)ÙºuÄÀŸCͺQ#ƒ$yÔ¼+V¼Lõò@yÛ{{q¢M2ê“ß’OdÆêÔ¹@Ý?Áºo¡Ü@c‡;tè@]pô @wÀß ¡ ‹~ÐâÀo…a„BU…Ñ&È$€ãÅ!ަ%hí²c[p¼Vñ¢‡âÚ“W þ,Z‘éðIÍœ¹W¾ÿþ9z)Ûñ"1ûÝ!>Ñ÷½53ŸÔ®25gN˜¢Ý °& bï¼sÑ–ÖѦ?Ê7øLÃfIø¿Y³Þ!±Ö €‘²!¯¼ò0²I’<"ü÷¨õ£—ߎn‡‘–„ŽÜ`Ø$GÔ%°iÓ&9þ|E¾{È2eÊøŠ˜>«5 @‡ÀM @›à;áH4ÜÀ} €ÞgØÝÂøÆŽ©n¾xm€&Coðu~úi'TÅ¥aGr„)|°ãE¦ÂÓÞnü| Hxz\ pZ„zÞžN€Oh¬52[øl÷êuŒ\ðà ˆÏ à £ ´Ö¯Ž2ðèQã"e› ö/Wî*©ÿßxã:ÙQc­_{í–Ì™Ó †/Â]RýƒH Ãå*;g@Þyçµ4Ú„ù“lQu‰ìÓ§¬P¡‘€Hj0Ið¦Ân…šÌÓ$m‚=ZèL@!­ €00“ŽhMà•Ït8—Åk¿”n¬XOe‘"×倇ý>Ì$< ”î×ï¥ØíHŽPš([ö ùÎr$0æ$ôí{D(pöëR®Ü9|¸1ÇÌê`ð½Ã™JÔÃa„lÈŒ‘.% qãöS›,þÌ[@æÞ_}u”ÈD—.ÇÉF–Ñ(© ›Y;g Œ À%õ3Ú—8Š1]°J•*¾È€`€ÐV…å$`ªPP„up‹ Gô%¦5dÉìözÁ« “ maê(’Ä«v+àÕ‰Ò.®@Ös±.xAV¯~ÑvvÀ过t“&gIâëuAæ??œA.Ö®ÝNó`„LȪ]­£M ü 0ÏÁÎÀ, `¨Ð·ß~«HK5õyOÙ6A€ ¿ Ã6Ø‘`†À—$ €&I8Àm 4¼ˆ03üµ×nÚêE‡K~üpi FÛ’YÀ –¹v"GHs#-ýå—ǨŒèuó\ôìV0{ÝqãbôèhíØmttø™ œ¥Œ‡ÝD€®HLƒÐ&ˆ©‚iÓ¦ì0!xü«°AágåMŸ€t ‰;rDG`^HwâE”ƺÂVuäÈÀ¦¸ÃwAŠ5a;K†` ™‘‰÷‘ŸC0Ö©qLi´Ëºà‚ƒ˜*wdn6mÚ@¯{c®¾¶öPxÑ(f  C’ìà`•`Ž2 ©S§Žl&–Á‡µ(p©ƒ0б;fA„áhÚ›S98¢ÀA€6jš£j‡&+˜–`ZëÁ:¬ÌN ˆÞ ‹×௠²4°ªECl‚±6X´Zâ2±Ëº˜Yˆ"Qãäºàk-\¸›:jì;&ù eùí dvÄ—å€o¾ùFV­ZU¦H‘BS1"Cî*ìTXé„À'Ž Ê*äRH©@!&g8¢0\·RkF†âuÌ _µÿ*U.Sf"˜¾åf†þ阙Up׿©N㞣:n uá×åûï’øÍë“›† ÏQKd0Ö¥h;Î@F bßo¿=Dœ¨D +V¬(CBB"ÛpMá7-  €Y|&) Rè PO¡¤Bv…ä‚=8¢#À  i¨í‹ý/¨uLô_*tã/•`§*M2$¨/+å  w˜ ü· ÝÔµÁ×FJx¼¼ƒ½.(G@©u &!*_þª-M’@ª«W¿@D?˜Ä1²c…—.]JóJ•*EŽ‘ÔœÑzˆaŽãú+´U¨¥PT!³B2rD;`¦uÍÞ÷}/½ÔA¼]h MÀº@ÁŽ×µÝ×—'&4bˆ²g¯¾z? fIX¿téîRù¨hÑkDL_~ù‰Û?Ÿ,Ùƒÿe  À÷Uö¥©èر£|ýõ×eìØ±½Í<Ò]+JÐÌW˜¦õ]ÞW(% €W¸Àm @øËnîÜÝd&‚vA¤¿S¤xðÜËÎöá‚k]Ù²Weݺç¨f‹C¯ \QÁܪø > PV##€µÁˆY\\øù³e»C¾ùpÍ0b^Ç*\!âÐ©Ó ²×ÅÅo¢èµ.ð.0×DÇÕº  Ô\dV×%*"|ŸØ p„F C·ˆLº°¦Xg¦þýÓÈã\¹n‘_‚«rŒQ–3´(=õî}”†E•ÌÜæÍ›Ÿ!›5k¦~þ ‘ÑÜÒ‚Àe.ôu)þ……ј„KÑš<»ÙŒôw×®Çé Ÿ@Ý7gÎ[”-À!ÑÜ?î‘«Ví ö¾¨Øzd½lò'¹Â¡]¢8\|È`­ ðÏ8`1ŠÂG´xqEE5¶7ë‚ô¸³uéÖí8e†×"ª® H3~æ¡Cÿ–µkŸ'‚M€?ºaÀtF86kv†ö´<È´aш’„»ïY;ü=¸¢%Ùøøêw€ÏÖä0<ðï#s6üòË/rìØ±²N:¤ˆ„Sàq…5áH€©«ð•BK…*”Z—Ø%#úSíŒ:oáÂן#xý—,i8Ö!k€Íí¯K5@l| _¸°bŹdÉZu‘­SßzùÃðÏøwK—†ÒŸ±Û÷ío,_îz].ôïºàs²víZ…dž è³´iÓ&zQBe Z|Æ"“ €£å¨QÕÅ|šsâÄFéÌD.tè,P^CÇÈÔ©ûžÔâò†–™;rti |ç®,L:`qŒ,Ž·\ìÈ$àÌ@VÄRø>á! t¹€ÂŒ dÄß·'ç†93 ÿþêü)€û‚Àðzø`|0Z?Ƽ€\ à` T#Òþkùûõ¶~ýzõÚ™ ^8]ÕáמÁp‰O?ýTvîÜY]h=ÈNÖ}ûöU„u°>|¸=z´œ4i¥”çÍ›G¦3Ø?›­ZµŠH>w V;$ c!Jèf@ )RöÞ”ÝpñCSçN”Ø ÑA©Ä™ÁÙª‰.®]ÿ±” pì@{§Ñ)°ÓÒžÆ×ß ÝN>²aýú‘;ž ÌB½zçe­Zi iå»ï^ ‰6mþ%1âðá©3„Á“ü>.\H¿ïL™2y[ €7À9…_²?k=À<] ,Œyu„aœQw°A€@€eË–‘ø'oÞ¼Ô Ì`¸BºtédæÌ™e®\¹H,†ÏŒ 8ÊÁZ¶téÒ4l¦Zµj”JnРº°šËV­ZÑ¥Ò¥KÙ«W/šL7nÜ89kÖ, Á£ä™gĺðÂEW \q £6Ó nabŸ«N \Ò¸ñ2‡Ç.o¼äqbNJ&+WF\.1¿d ±€Ã$2u @Àif~øÁ½‹ ¾¼âAx hEK$º @$ࢉN¢W^y(“&}Hã“ è‰P&ΈGÑ"qÌ“3æ3€“RÆñâÅS—qj'K’$ ] ¨'§L™’ÄeÙ³g'ÂP¼xqõ’›HBÓ¦Me»víÈŸ~àÀ”A˜>}:d -€Rݱ«fÁ‚]j Œ.:øô£<€1ذì…h]ø¿ ¸ð!š,Tè:eЗ2jýh‰ôD[ƒ”<ö/̾0[_;¢löxHÈCjwE+æèÑôL­Ï´Bä‹lÚ|aˆ„R.yWÝ&Ù‚Ÿ;wî[ŠŒ]"²Vô(„††Rf§páÂÞ–ÐxEáWa8†/L×]]…1:S10(©àAL˜0¢ðŠt$h5YQAHŸ>½º¬rÓ…S®\9Y³fMR¤£ä€ÒÊTsçÎ%BÁ–-¿Ó+)nˆô°W ŠD§Räm õ>È.Ò>:E¸XÇŒ9@õrh "#ªE6`É’]¤ÕÁ×A6@÷F6i{6ÑŒ—9ˆ^ý —L\ü 2°…ö¶û_%:u.ȯ¾:JeŽˆ  @~üñGÙ¢E‹È”p¶ ' 0»&* ÆÔÀê‚@Ì `o&L/ 9À%‚€lȈAÖ¬Yåo¼AÙ‚† ª÷gjß "B€ jÕªÕê2ûõ‘Ü¢E»é‚Gª@ ºnÐQ€6öTdóáÛ{Íl@™2WÈ·ÀÝ¥mjйNé}\úõ!Ý_£Æzñƒ(øªÛÁô†@FÄ$ ßoDÖØ7nTßÛ(r ŒD)Y€ß²æè`sj }©ÐT¡Œ`o&L G‚€¬²Ð"äÉ“‡æÙ£|жm[uq O{èYpi¡\™Îo=@>FŒ8Dm„"à®S%x}`¯ãâ/Vìé ÐJè¯9 0ÍjÐà•!PNq—€.Ã,Ä×›ßßc…cN² 0ÂÀ ¡Â˜A ¼Ò‡rp0`À`„Y‚¤I“ÊŒ3R†‚5h@𹆠­ˆ"f % hLm€»y¦ÿœé˜1;‚Xи³.™š3gé4@¾¼œpMápYGo€q }…166Á¹µ 0 9˜0`0Üf 8(R¤é0æýìS§N%Ý”í"س?ýd̹€m7TùxÙc¦AD$Ù ”Üiàû0dÈêðˆ?¾·mG\dŸ+ÔU(®I cs€ƒ #B2q¡™@ÚúÝwß%‹ñãÇS«!J æ<‡)Sþ"ñaùòWh “¦ÆŠõ”œa1a «³Yè->þøãÈÿÓZ€enýZ)TF[`JÁmL˜0ž©j|®Ñ]_!j[´h¹Z1!ò……3ˆ˜ál@DFüødÉ’W©;>®JhÅüî»ïHŒ™0aBo³E˜; cÀÂ!mTBmI8 ÀÁ€ ƒáu‰ qâÄäA“"”† &çÏŸODÀß3`v `,3\ í@ A€ 2ô0CaqåGG˜†åÈ‘ÃÛ,´¿8ÉÀ!ðGè¯ÐZs³ÜÀÁ€ ƒáÀ¥…6 ‰Ê”)#[¶lIÖÅИFCþÌCÀvÊÖ­ÿ¥ÑÞqì¾ð„t ݺý#—-Ûá2 J)ÐXx™€9ÐîpbÀŸµ Y€™ ß)t†9c€-‚9^<³& †ï»R¥JEÂ6¸ÂúƒpüMàćC3Þ ˆâßj{ | `‹Œ®WYsX¬œQVñ2 _€õN²(˜s`ô‰Â; yu g8¢<0‡|à €f·‡'h*Vì?Ù·ïQ²)E›ˆR‰L ßô¶Ãl¯Úž={ÊÙ³g“êÝúx  €‹6aÂG¶šYX)cVÆŽ»k ħZµjQç…ëþ@“W¸É|/Œi E˜E0 ∺æpÞÂ40Lé2íCa >ˆ¸U«vQvêtœþ,Æ~BL„Ì£8#rÀK6$$„üš4iBƒŠ/^L)oßNÞÜFn ܰUK ™€õpãÆgåܹ{Ü>60' [·ndÈçF/Ä€'B]hæ‰0‹`G-À«‚}8¢“Fí’%;Éò“Çpñ—.}•Fš"Ý^ d8v=!KÑ9nËâů©ÊÙ¶íIRcXDE¾ LŒ°lf”/_žfà¥ëK‘àÒ¥;)ýv@;]þ&•À„E8º+=";‚qÏè¬ð2 pCC‚–…Cx-€Ù_¸&ìÈURý°7n?©+UºLC90¶D³ÌQ#Dy%Ôè@J•º*›7?#>,çÏß©a&"þ ø<Ã縨×öŽpW§h»Áý; h“ @Ç&bFãÅÙ’2€ðûùG€  Q’ìÛ÷ˆ[‹`C˰‚:ÐYá…Ÿ1´®®}&ˆ0_Œ Î)xFGT ¸‘îÇ ’^½ŽÉZµ.Èœ9oÑòˆ.}w):¤ AÒ¥»G„6mþ¥ÖdpÀØ”àÒøK ¬bÆZP†S`6 ÃfõO`›Â.…}˜åî ‡„áê†)oÇÂá„¿ §4Îê×Þ-M.:[g0fBï½÷ž:t(͈Œ@/ê±c÷Ó(b\´v$8_²f½£.ö¤Qr÷ó <‚±Í•*U¢ ^< .(lÏv˜YGwÀÎÂp,¤Vð¤@;\þ`ϸ˜?ùä_šøò€^ò¾êû…f ^¼Ç´Yñ¢6ìmXoH@À#…íNÒ}Î`Š–è—ÀB}Ìg¸Ä…`‘,Öëï?9`©ÆÏN~Ÿ0}Ù °I.p ;öè HÂmM l™ Àþ‡ù ”ï ,Pùf¯À† Û¨|q¯ÝêÿŽŒÔ©ïÉ-NËE‹vE8.gGûöíe¶lÙ¼ÉÜÖŸ…N>ƒ?cFÀW  cR`V…d‚8ìJBC·Sûf‘ã‚öåXOgD:Ôíz÷>J³É=Õ؈˜­@¸„` 2[¿& £5h¢N 2¼ÃDIzM§c0ÍKLט¡ë¶À¬p˜­1Gãâ«ÉÈJM¶ë J÷õçæI°Ë È ç½P¡Bd „ÙÞèÖ®ÝN]=ùóß´  3$Oþ€&BQi¶ÊcÆŒñ6 `Š×:9 ÌøŒ Sè¨P]!¿B*ÁöÀv#ÆËÿOùÝweíÚçiÌ'Òýþwò’2iÒ‡²L™«²gÏc$6ô„؈,u¨ÿáR£7?ÔÀ_ë—#rè«×nki öC¾Æ8Wü®¾Õi[`„Ä\ßkŒÒíüžÇj’2M“?¼ ·(Ö:‡ f L ð7nLf8°Æõ„¬Y³]‘ôcòõ×oÙš„„<”uëž'½BD??Ü1…ÑË—õïy¹‹³`Žþ|ôTh$ c r4âà>@Í­zØ<éÓß}®·ßßvžØ¸8Oo­–lBí@§éËf„·Ö)Àf Mš0¼BS½†XË´¸êc½¾Ÿx6mÚ+tÐ/µÏôïÍj¸˜òE7Þ¾Ðè®ÑCð@oý{ÿZ“ïõçY‚ —"Ú¯þf À3 Q£FD<É€@ôÚkч˜3Ð6Y²dI"I®)t!{ųó³?êLÕ ýY…00ZY Èaµ?Rf°ø„R?/gxzÃdYd#¢0ÿX}´F ú€Ë)”V(% c†ç(¥×°¬^Ï Â˜½þ¶>X=EeªÂplCж†BM…Zµê(¼«ðžsÕÓ¨¯¿°|m¨_x5QIi©ÉI;M"úéÒDDžѵð4˜$`„ –3( ~õÕQ2Û±3xå•T¾„€Ù €0æI 6TçO Ê–xXÀï2ÔEFp‘.#}¯I$>C…Ò‰01 Žàl¼¸{õ:* þ/¨ _d`,Ô¬Ùiµ)÷Xr´ 0ûgˆ°‰`¸Ê+äSÈ. 7°LŒHk˜E‹©°¦9ry‰Üx‘½& ËÖ|ºN Ð(¨ñ†>¼MQ(ªPL£¸Nñš$¥‚&'U5¹¨­ÉÂ'º,4_‹È®Šw€ åýÁP&`ݺu’x xX¾ñÆu*ÛÙ•àѤÉj1¶ZÞX¹r¥ìÑ£‡|íµ×¼1º¨5 Ë]dÍ–À> Í5yÍ&ž2 à@êí=0êI–ìAÐ70D‡… ]'=Ú£pý žü©~A¢õ ¯èºì@1¼Bb ¬cR}€!z}½Er’Rh¤tD[©Òh¤ ‡t2j¢b”\š`äÕd¢°& oé¬Bý:DVàV I& š$™”Ü æ }Úwƒ‘!´Ú€ò%º—`Zd• %~ U«V• $ð¦ à¬àg‡saºÖ› TMMžÀ\€:;Dwè›Í‘ãV@ëþî61ÚëÔ¹ gÌØ¡ ÐÀq£OÕâ²6:õÿš¾`âkÑOL†OK#v$G#®â¹@üpH ˆJ’pääUM$Òj’MdÊèRÂpN~H%KÙ¢E r tgŒ}8eÊ>Y£ÆEjáµ#Àù£²/¾øG®Y³Íß“ßÉ:b@8)z¸ŽOµØs•p- Fip¤Ö¼¯3Fôç…ËÁ!xýÃÞ·råKdòc—ÚôØÈ­^½=ª€ùš Òi^Óý+™¾dL¦Ïˆ<^ bXÄËáàŒ¤˜$#¾& I41H©³Yô«%‚yº,®”råÊ%Û´i#øá¹eË—¥Â v+²pJ)lI@LJ–¼J¾"ÈXxÒæøË/¿¨ÿn‰½(ÀÕr½›³bÀ‰Zô‘ÎåБ=8O°¡1Øçóϓ˟^ÿŽH’ä¡lØðœœ5k¯[-€M @k]ÿÍ©ÓÕ¼Éíþ&(Ž„!–&ñtö ±¾ Òè²:Ð^v=xñâÉ ¨³às¹hÑ"—Ž ã(Ía¾‡Ýtx¼@øî»çåôéyì'‚,J"I“&õÆx{ú éåÁ"Ì€Ë'PþÏœù)faža7e/êŒ%K^S¬üor!‹¢ ‡&ÜóËÄ"}ºS=À† ÊþýÓŒŒü¶‹f(C†»êœ;­ÎŠ=^GÖcÞ¼y4@É‹2Ü7º)˜]Bpž„ñºÐ‚®6â“ÿé§Ô.ƒ'>»Î÷†1 Q²`Àñ’\ ¾¥/¿ŠÑ3gNÙ¹sg:3—@ÄaÖ¸ñ™6í=[œ <¢×?†­]»-Rc׬Y#;tè@-’–ð{Ù*Ü…™¦@ð ‘|$ åÙ#Õa7jtV½²ØÖÙ ¦DØØN„’Ž˜@7Ä%þ.@X¾|y—¥¤Ø‡ ù›öf°»‡PûG©°]»“Ô¥àíëßÑpÈ!²H‘"Þ”` ìJ`šaB i TVž‰ùŒàÀ&™1ã/êµO–ì¡m]½ ,Zô?9xðßäDÆ€ã”`&4[§œýFpnÀwÚ´iÏ63ˆ;—ùòÝ”qã> šN(eÊû²~ýórâÄ}·þ¹<jÖ¬)“$Iâéºîu8#0Àô KäkºÌG°+ G €iê‘$É#[XpÄej Ç  Â3õäëþ,Àà³Ï>£ÖÀß~û-œ`n+µè~ôÑ)j ´…8.´ ÃÃäÛo‘k¨7Êg@D»vídÆŒ=-ÜîuhÄÈiØ…c>Ü ÑöÉ#‚9˜¸"ýû3ààp|žòëËÄo™€„ ÊråÊÑ´¼µk×:¾MN˜°_~ðÁY™5뀑è0B¼T©«4=tÅ ëÓC­šõïß_*TÈÓ2ü"Òü ž »pvä˜6í/Y«ÖEÚHv&Pt˜KÏ“ˆÈPsö‹0]éÒ¥“Íš5“sæÌyÎ $Ä|ìØDP‹Gg€?5¨ùÉút1¡çß——¿©À|´D&J”ÈÓ³b¿x~.€£í€pÄtH¸¢0+Ÿ#"}W¯Þyrϲ+ˆë‰,Qâõöº2bÀñÚǪèKÇo‚ÀâÅ‹ËÈÐÐP§Þ È 7ï úº;¤ä]KU«^¢ÑÄK–슴èÏPîøñÇÉç§ë…6ÍS k„µv@ Â(jL¥tÔppø“¾ÞÍ›Ÿ–©Sß·m¼½+V¼L/ŒM›¸ €ƒÃI uŒ¾òkþʤM›V6iÒ„¬rÙ›å<*:u:N¯ó´iï’[`äÏ–§êÕÿ„&•(pC6n|–jþ?ÿ¼Ó/—¿ãˆ`³k`q½àxYaƒˆXðÂçÂÐ`R¤i Ì:ÿlXÔÍ>ûì„Ì–íiØ‘ EŠüGŽqã>özv²€Y³Þ–Õª]”ݺý#§OßK_§ýŸou\/ûöíK3<ÔÜÆxgWÀQð¥0t¦@B&~'jê×.|=à ^«H—î®lÓæ_Jõ¹Rø2àà ³ úâñùð ¼€S§NM)ñ¹sç:>Ÿ‘«½yZ–/…Ž¡vÛqh{ðè@™Äp•)€h½zç( ¸råv—†`¾F#3FV¬X‘ĬD™¿»9+ 0§š:Œ„ÎÂgGÀ{ n÷Î;—lÙ €C)¿¾}ÈÐÐí< €ƒ#âRzËÏúcĉ‡Ìqúõë'7lØ`ÁV×° Æì ‚`dG¥K_¥îxÀŠm„Ð"9›H ·ÁO?=ávˆ?ÈN£FdòäÉ=Y«» ;„sC eáΊ!ÂÐ`tx.Á:Ž@.Ü-[µ:eK;`Ôÿ«U»D$©E&nŸ/¨É'éKÈç‘hÚ´)ùX÷×ÿƒô;% ÝÚ¿ûî 8ð0 ùz÷>JüÐÒñ`øs $äÍY¸pW@ °lÙ2Ù¾}{òð@€V@Lp\å¦ °H„ùtÆx`x;¤<˜#ÀËiºbÅþ£:&¦KwOm¾“TStgðÁ€ƒã×#DeûüQ ˆ7®,S¦ MÌs&´B‡­7J80”ìÇwË–-OÑk?üYå?êÿh[ö•ÑU ë¡wïÞ2_¾|dŒ$<ëîuŽãë*¼!Œ1Ð ë8A°gÌØK3ì4ÞÖ¢¥J]“#F¸žÀ€ƒã¹À¥ó;ësƒ ¼‚³eË&»uëFCs|g¼ó§ìÕë¨Ì•ëÖsgJo¾iœ›6m (€!Ј#déÒ¥‰üë…ëNǹã…1BÀ7õïÎ4âó‚ÿŒzÕªí䦅,€Æ{†ö<%/Þ!ëgÀÁñL`^@†öyþø c wb@ÏêíÐ_¼ø5§‚äܹoA@¶ ã§L™"«U«&$HàÉ:]UØäæÌÀyñ£.×@ØL„ “ðyÁ`fæÌÙC³´Ó§¿«øÓ ¦þáïÞƒñG<܃ ÇsY€Œú…yß×û!FŒê±PL:”^Ⱦºl‘âGª?qâçuiÒÜ£Ñå@H‚3oÞ<Š”4iROÖ 3~€,ÐçµƸgœ!šÄñyÁá@­;jÔY½:6àà”ð5!ü+Tè†üâ‹,«~™pp8Í4T¸à={` ËÁÞóU­¾0&C)2üÙ(‘á2{ö^õõ[ÀÏøñǓ҃Á@ „™®:LC  ß £ŽŽy’ C˃?Æ!H³C¥ e.¬w& üŒo”êCßÿ¼y{,÷û2ààpyô%äó2@üøñe5ääÉ“)Mî‹‹¢À.]ŽÓ<ðI0ªPáŠ=ú@Àup„æ!gΜ”ýÖ[÷*¬î;f Ã5…1àɱ€ƒÃÿÀl „½&6aÑ¢ÿi?ïÀ¼üqù£/´ E$ücÀÁa@LÖ/¢óÁ¼üòËêŒ(*LŽy¾¸hÑê‹¡_ðÀ…^T°à Ù¯ßûH€)ˆ˜ øÆoxÒ Q+ 9£º*¼'ŒN€4‚Gsš˜SÑ’óùçÇIyòЯ6Á¸üá †—³fg¨ç.bž´û0ààpø¼aÒÜeá§n€Î;ËåË—ûhßV9nÜ~ùöÛ—)~üoæÌwd‡'ä²eÕlܸQŽ9R–-[ÖK`œÇ„ë¡@Žã„a Ü@¡˜0F³%0Gà €ÉÄçÏß-»wÿG¾õÖeà„g侚é ÁëÖ§ääÉû( èi¯/—¾òµÂ¦@pǃ)¦æùB€NØ7hpNýÝž{(À)ƒ€ð@ ¤ü¦N*ßyçOZápR¸ö0Ï Ó­€MJ*da­€L8KL€¼Aƒþ–õ럓¯½v“„8¾p Äf†é|þQ׃àoöì=$Dôfc3ààp±ôåâ.ÅI“&©ób³÷ð·]»“Ô^ƒ„La¥J—ÉEY´âÁ€N!N0±nݺžÌÀzŸQX'Üwà̘¢K5*”϶2à<0SrNš´¦bó! ‡ôœ3Ïn+Þþ¸øS§¾Gž°÷6 c=wáð–Õ3ààpp™»ãë};vlj„ÀÕ„@Ï'ðm“}ú¥ùäx~ÄŒù„ !CR¯^Çh˜Za RàaA , l‡‡gÌ9…õnÎ Ñ 8Ma x¶0™àV@Ž`ÇŸË–í `çï¼sQæÍ{ƒ.ò »$ðÀÿ†Ö>LƒÈ¯\¹+4ÝóæíöúÕÏ€ƒÃrÀcþ/á?€ìÙ³«Ë¸ )å}‘Àƒó ºþœ)™Ù&Œs#ÌsåºMÄJ–¼&kÔ¸ [´8-{ö<&ÇŽÝO-…ûªT°dÉjL“&'k7À_" f+ †aSe…Ü"l(wp„7 BŠnêÔ¿ˆy·ný/¥ðÑ·¾4€Ë†>ùòÝ s¸ú›ó•|qñ3àà°H'~ФL™R]¼-è‚ôV€s—5jûPùשsAfÌx÷¹ €«r¢ùÈ€n gÎÛ4zdåKd|A0¨cÇŽ2sæÌž¬Ä—# f+ ¼>U¨®ðºÂ« q™p؆8–Ö®Ýö?¡ ÚsÂg®Ë‘ãMœ0aM»Çb_ x˜pp¸ ¤’{èϱO÷ÜñêÕ«G¶Àž2³‹(~ÿýAº´‹ÿ^øxý{ãC‚Ž%hÒ§¿G™ˆÿÓŸÃåÂ… "§<«Ž€¸€¡#jÙò”Ì›÷&uù¢½Øß\M ¸A…fÌø‹Î.Ï\ 7ËQ£FÉR¥JyBqù]¸ž`šÍS£Ë4¦P:&L˜ppø"R(L>ö€%0<ò¿øâ jÉ_dÖ¬½¤ Ê“ç&¥ü1sÄtE÷RÛ¶ÿÊ~Øã‘wŽ(s”/_ÞÓ@Û" ?i0V¡§B#…â"Ì Í€8˜0ààˆT`Ä,„f…;Ò§O/Û·o/W¬X¡Ó:€`ç‹î ¸qŸøýâwÖFX°àuÙ¹óqjmöD7À·ß~›l-®Ï-}n¬»ŽWè­ÐX¡„B&L˜ppø" Ãm÷Zq¾D4xÍÃäƒÆpzܸc&š B7“Õsfúôé$xDÖÃâÚÜö€LÏÚglÌÁ€ ‡õä.úUêÓýó¥Y³frÑ¢E. FøÂQ†=É’=ø¨ñð%˜+‹sh¬žO3gΔիW'݃ŵÁHà]Î ÌøJ¡©B)Áó8˜0ààðQÀTV³—|½?0¨qãÆrþüùò·ß~sªú_ºt§ìÔé¸Ì–í¶_§‹zâ€ù&ݺýc¹0kÖ,Y«V-'N«ksOa¯ÂJ‹ ¯B3…Ò ™uÖ† &‘ |þ0oþ_ïì» ȹsç:%8'ÆŒÙO½þ < úåoN¥5k^¤ä(Ñ93{ölY§NO&Z!Kõ¹1Iák…æ eâ`À€ƒÃGv2øÌïóõþH–,MÊà 9¼ ^Öxawéò òñg»Ÿ7:7Ó+"Î`" \$HàI `w%s :4'fåsƒÃö`äȃ²L×N€ƒ3àÌaPQ¿H}n\»vmÉ…'°ß>ý/uqž£Ú¿].Sˆ!e ž£ÖĈÚ‘á@¦#Q¢Džˆwx@ú+|¤P^!Ÿ¶&ðõÇ„Àòå¯:݉v›3àÌü€`9ý"õéþH’$‰¬Y³&µÉmÙ²å¹ñ¾ß|ó7©îñ(°ÐP¦ÌU9|ø!r'twÎÌ›7ÏS`¥ Ð$S(´T¨ ]#ùÜà°'»?~¿¬Xñ²ŒëÉsé5¸|}ýõKõ5&~ Ô‘ Û>`éôÜgÏnñŸ³2@îÜ·H ¸rå_€ […{# G0PBÍ·˜pD +€ú&¶!¯)„ŠüöÛVÀÓ¬Ù™2åý ¶þ¹³ N›ö+Çè`˜ù°€Y["87˜p0`ÀÁáw€)sýAÐ7mÚ´g48Ðg_«ÖõgÙîò7u!!eݺçiP»‘Á^ˆ­Lä&~ S°Sø¡ àÝwß•3fÌx†`ßC#ôöÛ—É}ÏŽÀ  ´(Nœ¸ß­^É‹6@O‹9˜0ààðKÀP¦šÂ!_ïY¿~}9gΜg¦þ¡M¸lÙ«>°þ}ªþßEž$HðXaWˆ¬¸;«ÐæˆR‡N€—uÆÅ*@` MÔИ„Ï Ž(I §­[²W¯£rÆ?=ºÁ€ƒÃç[¡¾ÂIá# F‘HÎÑêúÒ¥¯ÒÔ?ß¿Þ}C¨PáŠ5ê Û1ÁÈpT«VMÆŒÓêÚ\²H€ààhÄN€ö"¸ø×¬ÙNŸž=É"Eþ£xqM† wåœQ뀜;w\±b;1l_“&Fa¸ÌóõþH‘"…lÞ¼¹\¼xñ3³@FŒ8Dmvþ!Òg€·ÞŠ˜@äX¹reO¦Z!lÌau,^ò 즴Þ矗 œ•o¾yMýý÷:|!í—9óY²äUùî»çeÛ¶'å A“éÆêÕÛ‰Hø‚ 0ààˆ0â)´S¸æË½qÀiÓ¦U{»ísã€q™âRÅë:XÓÿ¬ aÂGêb¿,Çsm b3yòdEÞ¢ŸÙâú\PøÅð•”Qð0 Ž`Ç‹†Í›Ÿ–eË^‘9rÜ–É“? í®¿ÿ @’&}(3f¼+‹û¦p}ùå19mÚ_DÐ.Ä€ƒÃ¯aN¼é˽#F ™5kVÙ¥K¹fÍšçŒÂ&NÜ'«U»HB;»Ì@§Î#W](mŒ?^–+W΀lËz çæ°Àf€‘RøŸ|ò/¥òÒ¥»G53oL=^zIÊ8qžÈ)Ènè×ïõßâµàm6€ G„ˤ§0üé}¶7Pýõ×e¯^½äÚµkŸÙ—°Ö3gYíâ±`G|Oð(hÒ䌜7o·ËÇü ÆŒ#K—.m•Y„vìx‚ú…×­Ûæ `ÀÁa@P•ù_î ŒÆ-Z´¨4h\¿~ýsƒ€~úi§l×î_ÊþÙic†2kÖÛtýüóN—çÏæÍ›åÈ‘#eÉ’%­€'Â\†z@z)| ð&Ž \þ³gï•:Px£Æo¸pT ]nacÇ¡¡Û=&L88" ˜Ê Wxì˽?~|ª‹=Znܸñ¹½ ±ðW_¥Œ_øy!v"1÷ïX®]ëzÀ/¿ü"‡ &‹+f•àÌ8ª°:‚sc‰Â<…qš4R(®^€—™pŒi»½ŠµŸ”… ]§Ú?™;²( dÉr‡D…£G è `ÀÁa¤ÒŸÍ§¾Ü‰'&sœð&@Ž {ˆì@öíF'~$«W¿HíÌî<PÞèׯŸÌŸ?¿Up_a¿ÂJ‹`¬Â— Š)¤†nƒ G`.ÿÅ‹wÊO?=©>è7¨Ö¨º|2eº#6 \ ¯ CæÓ€äÉ“ËfÍšÉE‹=ÓVØJâV­N‘ç¾txØ 4ѦͿêûßEß««3fåÊ•²[·n2GŽV €9 p¹0WaŒBwax5QHË€#`jþ Î/Xð†Œ7ð£;‘"DÛ`Ó¦gh†¸Õ1ÂL88Ü.‘šÂ¥ù´ S¦L²cÇŽrùòå.÷'´=C†üMåÄð†aÁ\®œ1 c‹#:cÚ´i#Ó¥Kgu}®+üææÌ0ÏÅ ?(ŒTè¦PWá …4 ñ™p„ í>lØ!òíFª.XL59h ÊY²d§¥R·Ï^[áã€X±b©ÇBAÙ·o_êöq…ýG&᯲ø`T†î&d(ÜM,X ›4iâÉ¿…- À…ï:+ÔV( K6ðnˆÁ€Ã¯/í3öR+Lš4wÕæxÔ Ý¼†ý›ÜĘppD*â £à‰/÷E¼xñdÅŠ©=nÓ¦Mn÷(^Øß~ [àk¶ „G;05‹¨ÜˆÒæ¼÷Þ{2a„ž Ú€ ð"…Y ß*tP¨®W!…þ½1àðÀ {ÅŠäÙÑ^àö˜Ñ}—JVØ9·Ïß4áãú?†5lØÆä:Îp•@K`ûö'eΜ·ƒZ À—'Ï-Ù¥Ëq¹tiÄYFˆ§L™"«V­Jmš\7X f(|£³4Uò($†}3Ÿþ#xý£¿^½sêÏÙǬp D`ýú?™ppx9¶úrOÀõÿ:Xn/ÆY3yò>2K—îîsóC|M¤þ5:+gÎÜkIg„ìÆ¨Q£ÈeaÍz‹µÀMÎ*´R¨¨ÏŒa pâ3ƒÃ}º}û‘o¼q.];9t¥N}_¶n}J+t™ppxø^>*THöïßÿ9@w€Ö¥ØÃÏGÑȘþ¤N}OÖ¬yA~ÿýAËF08;.sæ¼åqwÚ $xLš‚ܹoQY³mÛ“Tâ„Ã)¾†7“F1høðá4ÄÇÂaØÒá~@x˜uÏz˜&@þ!³fí¥ºXHÈC[^þ挀7ß¼¦Û·1àà°¸<* c$­ÏöTðPÿ8.ÇÈ€°‹öOÒúàòv–@?Y²‡j? ó À?£“ S¦»2oÞ›²\¹+òý÷ÏQÿÈ‘äÂ…»èïõæâë’Z£¾¯ž$ô `—p?À±pºÂPa5ñûÊ­ðŠ`¤Ó§NÝ'kÖ¼Hu2»¤ó ¾®œÃ”.dÀÁa)’êÏáM_î…dÉ’Ñô¿Ù³g{,þs¼Ò[·þ—4?ÏŸOdŽ·dÕª—d½zçå{ïÀ?ý“K{õ:F®~xÔ,_¾“.þˆ ĬÔÿqf¶mÛV¦OŸÞª¿Gpù‡o„ÀG å²éß{pø—L™²Fa&IòÈÖ"žþý¸œ×Í€ƒã™@Ý8¿0„h…{ÿsæÌ);wîìÖûßó‹v+éТçld0(¸ðGŽCÊY»vÀžâ¤ÿ¸ €ƒ#ÂÔJýùûÏׯLþ9r¤Ú‡}vÉ¢Nt>ÄÈÎÒÿxœ ýqÂþzé»ÂªU«d§NdÖ¬Y­ÖÿÑm±MD\ÿwœ`{&4‡q‡ÿ6jgmÛþK*Z»Ù›€9¼»1°hËöààpŽUÕŸUŸ}þa‹Kb¸Å‹G8ùÏSõ?f”(qÍIúÿ)y” ;°fͶ€^þ.X°@6mÚT¦L™ÒêZÝÖ €L àÉ „1Èq 9üK€ÐÐí¤žÍ—ïfPÇsºÚ|à±;rp¸}ý§W­pϯÿï¿ÿ^nذÁ§-ÌÈ>ÿü8ÕùÃg!‘þGùoÈ¿]’@&L ãĉ[]«³ k„5 Ç+ôÆ Xg,äûFë Rì0ѰÛåÆ Ýºý#W¬ØÁ³88Ü¿þë)÷ågß±öß_¾þ‘þŸ3glØð¬L–ìSõ­ZÈ4Ð鯿þZ,XГÀDÄõSh:šÀo(¤,䀨föì½jž³¥ý¿eÊ\•Ç"²Â€ƒÃe }¼PøØõ/I’$4õoôèÑ>5þ1 Ë àð³H0 C‚Ú´ù—<yùK—.%·Côÿ[ôÿGúÿOa­þßÓ …á j‰0À¸‚ëÿ æhN´Ø`ê.\;ø‚êxîÜ=$ZdÀÁá4õ"Œir÷…]ÿòåË'{ôè!W¬XaÉøÇlËCÊ~Æ?åêÕ;Hk´xñ.¹pánêÛ_œ;pêëÔ ö¿·Ÿ«ÿƒ ý·ÿÃnÉ¿¿êÿ“'Oö4ýNa½ðÌh°sÌ¡ìÈ`Ž?~?ù@qk—,Úÿòç¿áÖˆ _þ"™BKaÐøtâ¦ß5nÜXΜ9“jâøE—júPõqPöî}”|-[ž&Ǿ>8«þïYÙ¢ÅiÙ¾ýIrþ+_þŠÓséCü»/àíèÿïÛ·/M<„ÒÂz¡Ýò°Â*áYýÿ+…f eE˜Ÿ#`ë`äðÏΓç]¼v¨ý¿úêY¿þ9ªFt0àxA/ÿ„ ï)>ìùâĉ#K•*%‡ ¢øZ—{{ÝéBOÔ¹óqjéCéþü™3ß!{ß”)ОN‘âLú>¥÷ñ¿%O~ÿ¹á?èHÊá®"§ÔÞÞðúÿ²eËd«V­Ô÷Æõÿ…ÂZÿÿl…‘ _£þ_TpýŸ#Àtâš8qõÛ&Oþ èY€xñ“ú·oß#.ýÿ™p¼àZ1¼ã× ûEê¿O?ýÔeÛ.e¤æ,ØM.~­Z’*\!Ñ.Îìa<&\%ø÷¸èµ£# %Éž=‘û_ Óÿ3fÌ ÝC‚ ¬®ÙEa´ÿy2h˜B…Â0zU„Õÿ™pŽZ€í²_¿#Ô7nð:°ù³e»C Ì?¬ ô`Àñ‚ÚÄ CHvKøXõ—oݺuåÔ©SåæÍ›ª÷W¯ÞN¥C âA^" >¢×|d¨ÿƒtî|‚´l„Ðñ›o¾!õ?ÖBXSÿ£ó"TXïÿŸ¤Ð_—nÞRÈ©K9fÿ?ŽÀ3•6ß¡ÃIš¼+VàK¥N}ì‰ÇŽ=`YüÀ㠤ˆ³ CA~Eø¸îið¾:t(Yá:»üQç<øoÊBÀ³®ð"¾ÈžèJªXñ2ù”à\  @ú¿C‡2mÚ´V× ž {V kõÓÿÿK…F"lP"Mìøòç<0˜±N¦Lwª@*©ÃJ•.‘ñ^VkL8^@z8—0üãqùû¬îË?~üø²hÑ¢²gÏžê’_þœêè…¾þú]Î0éBç?J†È$¼òÊÊHvêt‚4þ&(uL›6Æc-,®~[„uÿÇö¿: …„QÿgÿŽàƒl•&ì'sØp"çoM?B•*]¦©0ýñd–7Ž$í^ñ™ w|ý‡ÙMžGÉЛsÂÖ­[G-%J” HaMüw^aƒ°æþ·HgoF(tVxWö¿i…Ñþǯûl|´‚q#-ÿþûçd¾|7èð&#€ƒº¤Q^xûíËÔ7à`ñ¾Hå1àˆ&C¿ú?RبpMø¡æË/(þ»uëFSþœYýB;M Ek`©R×äС‡"5)¯ÿU«VÉîÝ»Ë×_Z!-®å…u_ÿ‹æÃü§‡BC]ÞùO>8¢$01kÖ^æ8«fÍzGöèñ ‰üiàÁ€#нöQóM©PN¡§Âþ¼ø…nõK:µ¬V­š4hP„ã}Qžƒÿk¯Ýô©ËŸ¯LÃ0H¨mÛI£àí¹ò»cÿàì°øú‡ú—¯óL€ž£B5aø9à÷öNþÃÁ€ G4¤yQï…ð«Âh…ýÂê~g—úôéeíÚµÉâ—¿³?Ïî©]ä’*Õ}_à¾!!!ÕÅ}žZ•½Í*®^½ZO=d¾|ùêûOE.~¼dcÇŽMÓì*T¨@—ܬY³Ô%¹ÁÒåoÖÿaþ… Ö~ÿ‘Ë %ËÐ¥ŸO•ÿ&LUªT‘‰%²º¶¦ñÏ*aÝùÙž‘ Ý…1ù]óyÀÁ€ ‡½_óHÓ¦Æø]\ò0o©ªPOï;ü®Ñ ¾Sá°0Ã\Õ—Å€ ßÙ«5íFQ½{ÃY¿; Í­»Ù²Ýñã+>ò娓ôÑiu®íôHù5AßæÌ™­ºþIMä6 ÏZÿàü7X—~`çüº0Äèü`ñ&Äÿyˆ—€õ‡ð*½0„xèÃ.ªPF§fñz_Ö»mº*LF½w«0Œ_N+\VøOá¦0|õºw¤±ñê¯\¹2õ÷Ïœ9“|ý#ª÷;ÃÊ•;d׮ǩ׎épmÚô ­Ï8Ù GŒA®qãÆµº¾÷„Ñ¢iåõ³`±ÎAè‰.&úsfþañ€(FvéÀpI¬Ðu?¤ý¾Uè¨PK¿qé¼¢7"ý²ŒÎH¤ÓœI½Òê„!–z3Jè˺¾ÂÀdµ–ÂpY릴ë× Ã„!­G»õaŽ ³ÜocªÛ#ýjúe.@~ûÎf|Ø1c>•ùòÝ”}úu;ë³fͲ?.W®•M,šþ<Ò%=+ÿkÿŽ¢ï¿0ló+¤æ×?& †¥v¾øñãË 2ÐÔ¾¦M›Ríýü¨c£Ý¿ãÔOìëš5/ÈD‰Ù’À °lÙ«òûïÒP2w©Lû«[·.ÍBˆ#†ÕßÃEaXþ.·@ú_¨_ÿß C„ »ç’Âè8Áë?Ž0”ÿL8˜0`0žOóc¯¢¾_©R%ùñÇS/ÿüùóåºuë\Nîó0d§U«SÔkÓ»€W^y .õótþ¸š’„ÔÛ¶meΜ9=qü{¨Sÿ+„ç®ýô™\ECXùÏÁ€ ƒvÙã%Š )A‚”â/X° |çwèÒïß¿?M©[¾|9½öué;iõ¾}Rš¶»v³Αã¶ìÔéµ,ºú`}Œ’ Ê'¨þ!ü;$¬÷üãõ¿@Ý+Ã:+ÔU(& ×?îûç`À€ñ¢_ø̃Ô~òäÉÕ–C¾ù曲f͚Կ߯_?JUãÅ Ÿ_ý†ò ½ o~ì!øÿñïñ¿{; Ïý0 ­ÿëÈ›÷†ítPT¾ü9bÄA¹qãŸ.Ì/$’„Ý/|,Öýѽ‚vß.Gáß…±Â˜ø÷¡Â[Â0šzU°ë&Œ«†o^ö¸| ÞÏ’% ™õÀ©¯I“&²K—.”ÞÇK—þÚµkå–-¿’*|¼l.ÜEûkÒ¤ýrìØêB; Ç; þÿ}ôïñ¿Ãµÿ]dÉþûM›¶Ê%KvÊo¾ù›öuªT÷ÔÏcŸ @ŒOÉ¡íóçï&²ò¼ˆq þàÌK—.'v¿èùß!¬©þ…ð»¢ÐAæ_0±J'ØóŸƒ Fô^öH/'L˜P&K–L]˜©ÈcízeË–•µk×–Íš5#ïy¤öÇÿÿì]xTW½H€A‹[ÐâîîîVZþÐâÅŠ»»»wIpwwwwæ¿gÞ{eY6Én²Ù,É…$»Ëfß›sgΜCK—.•~ƒ~Ò÷ⓤ2eQ©Rø¿øsíÚw¹vízE‰s4wî þyöôÑŽ¶´²çåÉÝC4uêi>qãTŸ!Ã+f·»º~á“7 °o |ü=¾ŽïÃ÷ljó2f|Å×b§NWiÚ´Süøx¿Zþ û-Xp‚Á: ¸–ùát$jÔ¸O“'Ÿ¦;üPüñž7Ž».h6¬üAðçˆ Ä?ÓÖ?¤¯ÛëÄ¿|fÄ?uúW¡@Pü»@¤ÂÞ4XÕ*CgbÞŽÏ: ¸‘(˜£-Ÿ*U*J:5õŒ3R¶lÙøôŽV}éÒ¥YSÅ{÷`ãC9®G4hÐ 1b?ž‰z ,`>Ì€l†S= =ÚùÖìçãĽråQêßÿ¢,h÷(}úW|ªG1háÅÏáçcÄø(ï%Õ¬y ¸H«Våçóè7nÜYúý÷[ò½xÁ§~ÌØ}ޝœÁEü+Vì1+ؘw7¶f̘Á6ȸÀ$ɆÖÿyZÿ8ýcçžÐÿº Íí¯¸Ìt2ã¨Ö¿ °î3räHîà®2tfƒ ÈÃÃZ´hñ_b¼}ûö<†aNŸ>}˜qN‹Ðч²Zõ Ž-_¾œ;J˜Ñ£eoˆîØÏ*Zþ˜ó7o~ƒrç~F±bù]tJ”ÃãæÉóL¾7hÞ¼“ÿqŒSÿâÅÇé￯PÉ’Ú¬Êz~­Ý¡3`ŒëƒÃ$‚l¢FýD >aÕ?èþ›ÿÀfÏžÍÀ @φ╿«2·ÙPüMYÿýd6“Y^fñMï_µþU(à(€“ÌP@®Z´h‘ÊPšøýãd˜i¢˜$¢ £M OxßZóA©®·sçAyJ=ÅviÓ¾"W×ÏAÖjÇãB-?þ¸%AÎ)&ù(8~üjÚôeÏþ‚OÖ¾êQä£DùĦ@Õ«ßç1l‚Ë–}(ï)‚œ  3æGÊ—ïõèq™]ÿÌçþ¸ö/^Ì›ôéÓÛ¢ó•¿{Âzµ?ÓÖ?`õ Ö ¡íü'jç_…Ž*U:sjÅÿ´ÛS§~í°Ýz<ϯ¿¾’ ã x‘Oý+>`#H‘¾øY|qêÏ—ï)w¦L9Í«‰ÿþ{„†=O•+ß§dÉÞÊǰ?ˆÁãá~Â%ó=dõê‹?ÿèÖ`Tƒ­‹È‘#Û²ïÿL¦·°^í¬Ø{ƒõ?Zhr¿0û)*3­Þú¬Zÿ*P@¥J“õ¾Nþüó&Ïû-¬ƒk3Uª×|’Î’åvßNï(¾èL¸»¿e 5À¥KñØ@나Ás–6¼- ï Šûì<ÀEÀû©_Ìü}+þجÀ¶•À±²ø›’þ6ÚPü ­ÿ‰2{ËüŸÌ²&­7½õ¯Š¿ŠÐªVºKÑ"¾%7ñ’bЧ[<æÌžâõjw’vnØKÞžÞ´ÏG•v:U{ù÷./Ú»e7í]¿ƒö®ÝF{×lÕþ»~'íݼ›¿Žï Î׉Â"öúsäxÎmùàZ¡ÃsûÅð7¸xèÀpãÆCÖíöѶm™Ë€W… x¤v=ž' @€㈔)ßPÉ’¨M›k¼!¡m2ø¾îa%ÖýoežÖ»ü™Îý§È,³¥Ì*2sé¬ÿ˜&¬*B8ÀI`·¼ù®ÛNK†n¡vEwP—íT]¬¤úb5s8ÛÇE³*N&¯¾“hßÄùä5 íýw»¼1ï•ᣠ™JÛŠ¾·ym•ùFòž±ŒöœIû{¥þ¡-Ðæý´ÿÊ?ãïñu|ßÞeùçðóŽnýoÝz…}PÔÀÐwFccT4é[êÖí -\xü¿S¿_ª @¡°]»kÜ1@‡!A‚÷\Ìñ˜¾‘ñ> kmƒxñ>0W¡téGÃgÐÞÅëÈ{ûÚç­ ›J ©§7yÉS¾×Œå´¿ÏX:ò{W:]áwº”¿]ÏT˜n¥ÌE·“f£Û‰³ðñçë ó×OWhÂß@€ŸÇãàññº±~‡ûFnS’$oíÎö·'Ëâ? Ü‘Å÷-­ú°a í^½.SãÆ·˜(˜;÷s.ì'$IòŽ%zÇ:à@ÐhÔ¯‡É…è–€/áímÙÝź (þ6ÚûBìç®Ì=6þV餿YB3úù[h+Å„¶òO±þU„| /<ïžäµdßHOÔmM—òV”7Û¬ôÈ- ½ƒÞ‹ˆôE„¥¯"Ìwù%LXú&½ GMDwg¦Ë¹ËÑÉÚ-è@Qä5o5ƒ ÕPi6}<½Èûß­ä3nnՇΖm@7Ò¤±SÒóˆ¿Ð»°‘és˜ðôUV1Næ¿ÿÇßãëÏ#Ææï¿‘®ÿ<Ç+?(ÇR›7¢Þ½/q¡YÎÙŠ?Úõ(ÊeÊ<´êÔo‰| 0³‡¤0ÀDß¾©K—«Ü!hÓæ:µm{…Šð¾Œqމ‘+Vã óV¿ùÉݺu¼Â ÍÜÓl8ù›*ýÙbñ Òß¡Iýöù»ÌÒ23 Íæ×M!à&¼GÞ„×l¦}çÓñ†íéš<Í?ü%%½q‰&o²ál¾ëàgÞº¸Ñ£XÉézæ¢4§}C¦÷ªMü\Š' rŸ·œ[w‘ÏŒ¥t¸Ý:_ªÝN‘žG‰GÂFäBoËgߟÃÏßNž‹Ç÷Þ²‹Ÿ/(õÀD/öì­õ œÂ7¾McÇžåS¿_8 ã¸B¯@xä¦M‡™Cr¤··5ï¥7ß» áP¢D vS @ñ÷²¡ø£õˆýŒ—ÙGhûþúÍ.3±øfô£Xÿ*B @ñ—§~ïy«èPÇÁt¡xmº—(½•Oú½á1Þ¹D¡‡qSÑÅÂÕèpÛþä3{¹6P TÏù½×m£ýÿL¥“µZÐÍôèy´ô1œí…ßÀãàñð¸x|<÷º­vçà$=dÈ^£s¶Ó?ÀHܸïY…'p´Ü}œôšÃž?î[\†3|l(þX÷{ [ÿ ý-šØtþ[Íå/·ÐöýAú‹¤Š¿Š Püwì!ŸiKè˜Çß|ê=} ëb÷»ZµÏ£Æ£™ ÓñÆÉgÒ>ý©‘@hdö{“ך-t Ï:_ò7z0-½u‰* ·}eçðxx\<þù¿ñóy­ÞÂÏo¯ ë@hKžüÓ™ê@ø­ÁÈY?k‚U2 .ÌlOþ…í»þ(þ‹dNÕÿmdÖ4!ýÅQ¤?!àäï3u1hÔžn¥ÍK¯#Ç¢/h÷[Ý –ÞDŠNwRå SuZѾ óÈ{ÛnÕ ek}^«7ÓÁ^£éb¡jô4VyZèS¿ßÝ€ü< U•Ï;ŠŸß^kƒhÿW¯~×꜑õ9_t-:î´Å.ŠÝ»w§B… ±ÏƒÅ'ÿ}8ù/ÒÿCevYWf3ÒŸ*þ*B&ðÙ+Oa³Vðiüvê\ô.R4ú&è…¿qC~!*ÝMž•NÖkM>S“÷8†–õ¾·Ñ¾ãèB‘ô÷W¤?!ðxÕ&:òWOº™6?½èd'0ßA@º*'kÚ…¼®uøî¶Jdzý1òÙ?|-]žÆN riàÆPáèi¬¤tN>?^‡6‚ œáœþ2g~锫†ð^”öÀWpÉdÍÒŽ‹p÷Ì‘#[4Û8óhcñ7„~°ë{ß‘âÛº_)™™MHÊâWEÈÞ›vÒþÞcxoÿMÄè-þ¦ àm7Þã>Ô~ymØ¡ŠdH>ýïÞK>Sѩңø©ƒ„gbÏ‹çÇëÀëñ D÷ 6»XuƒÞ¾³ ÿàuáõÁ¯7¸?†´/ÔýªW¯Î®~0ö±áäg¿ûúÌ? Å¸Ð4þ«â¯"T´Û}&, ³Ó³(ñíÂô̆À‹ÈqèbÑêä3zަ¨ŠeÈ\÷[±Ž¶ìM·Óä¦÷á#kUÄóßN›Ž¶èů+ ëXykÕê‹Þ8cñ7â<íÚ]çל§~´üá䨵kW*V¬Å‹Ïä™·dzÚXüW˜´ýQü»è'ÿÒªø«=€oÄénÄ)r23?¸ïNx ÷¤ã-¯¥ëƒdW[e0Ÿþwì¡ýæñüýeÔ¸vgû¤ûôÒ-.¿¼.¼¾€ìÿCÇÃã&Kâ:3H”è½*×åÉûH°úaÓŒ–óæÍ¹å-Z4[Zþ†¶ÿE¡Éû„í?Mh¿¦Å?«Ì$zñWë~*B6€Ê߾ѳè|‰Úô2Ê/Ns‡zÁ.¬Bû†NÕôTÑ A³òZ¶Ž5íJ÷’e¢Oá"8Åg›x=ÇþèBÞËÖûºŽŠ½yÈåîÜy[è8E£ð/^|œFŽý+ê×ï"ø8j·¿GT±bEfùGŽÙ––¿Aö»,4G?[È~†«ß™e’Ù^h"?ÅuÂ_ÕöWºÚÿÿn¡Ã-û°³ß§°œîN¡–‡qSÓ™ÚÒÑñS鸼‰$O,\H'gϦÓS§:_N™Bg&N¤³cÆÐ¹#èüðáN—  Ë={ÒÕ®]í’—ê{д¹ÙÊ+ä³01i¡KC*ṄÿÌÞö(ò?S¡÷-\òçÆF@ÖZÿ¤ÝïééIkÖ¬¡áò3Ô¸qcÊœ9s@ˆ~˜÷¿‘y\Ÿ÷¯¶‘ý°æK_¸úÁا¥ÌZ2 ËÌ 3‘"ü©u²§>‹ÖÒ©º-éñ/îôE„sº;ëDŠF~ÍJwËU¤{µj(ïW«FË—§ÇÅ‹;]>)VŒž(@Ïs椗Y²ÐKy“t¶|•>=½I•ŠÞ¤LiŸL˜„Þ¸7q~²IDAT»º1ÏÃ+$ì­·ˆÒT^l âÃO_ðÍ3FŒT¥Ê}–, # üÐîŸ"Ám«V­ØÈ'A‚>|x[ ¿ÑòÀ–¿1ïÙk~ÝezȬ"3¿Ì42ãËŒ&ÔªŸŠP<½É{Êb:W®>¯?9[+ö? ‹Ä§H‘écŒ˜ô1vì€e¬XôÉÍ>GŽì|éêJ_"F¤¯áÂQˆ«4?i~–`øÈIÄ\r/mn¯»¸|a€3Žðz’&}KÍšÝdR£½Oý;vì … ²ƒ_•*Uè×_¥¨Q£ÚzꇤïK™çeîÖ³ü–?æýóõyÿ¡1ý›È,/4W¿T2ã MÛ_Ùúª};Þ#ç°ùÊÛÈ1Ô_¥J“<'ÒRk1Žb‹Ç¾RúÈ‘?S´hŸxí/~ü÷äîþ–ù2¼¤xñÞó÷8[û?W®ç,WŒ5F{ ú Ý?jÔ(jÒ¤‰|Ž\'Nùïw±õÔÿ^晇…u+~Fá7ZþKôyÿ™d¶3!ûeš¥ï/2£ÈtQÅ_Eè;=É{ðtº˜»›þ¨›¾J•ßò²HIÅ0J î±v> t¢Gÿ$‹Ú¶ÓM•ê eÉò‚ |JåÊ=¤ÚµïRÓ¦·xµ®GËÔ¾ýU*Qâ1ÅŽýÁiºxxý5jܧٳO²p‘= ÿêÕ«™Ýß®];ùo.AÉ’%“ï—«­$?œúŸé§~[‰~FË¡Ðd}G›Íû‹ÊÌ$4¦,™‘õâV¡ì`àTº˜£½‹è¦nú*Ušäõ°Éip´T ÅeÊšõ*ô„*TxÈ»þhŸCï,zÿ`UpÁ‚ã,´qãaÚºõ­Xq”:v¼ÊÝŒœáŸ…× xvÿÏž=\øÇGmÚ´á~´ûAò³qµÏØí¿!s¿°]ا~°üçê-ˆûtÓçýUešoB²_x¡\ýT( @Îò¨€J?xò†®Âç(QŸ"ËÿºD¤¯NN§¿)-ÏÑúýµŸ º@cÆœ¥iÓNÑ¢EÇiíÚ#\ä!÷?OÏý,dJ¨Û³g?µj÷å5ü]pâÅÃéÿ͘qŠ_s@™ýk×®åÂߺuk*]º4¥L™’ ?H~6žú‘¯dž¶Éù§þ&§þ±2û mů‘>ïÏ¥Ïûã ÍÎW‘ýT(ðßàŸt1o%z)ºóŸ0aéc¤(ô6N|z›$ ½MšÔötw§×iÒЫ èUƌΕ™21ûÿy®\ô,_>z–?¿Ó%6ÊÞƒJ•èAåÊÊ{%ËÑÃ_³Ò»HÎÝuzÓöÿÖ‰¶/ÜÊ«r(ðh™£È[ÜÇ÷lÜxˆúö½Dyó>£(Q>ÀóF‰ò‰Wÿ0ûß´éMì~œø1ã;v,k÷/^œÜåu…}~ ~Æjtü¯Êô‘¹Yؾ޷D?õOšªNýÍdÖÐ[þ÷²_l5ïW¡€9ØíEÞãæÓ…µéu”XN{#þèâJSe¡kÕêÑÕ–mèZ»v6çÕŽér¼Ï~qà@çÊAƒèüСtVÞXOOžÌºÎ–§fÌ sçÒ‰ ‡ÇÏ 3u›Óãx)œwóD¾®‡ñ¥#ö$ÏnI аté1jÙò¥K÷*ˆä­#þeÈðŠÚ´¹NË–ã×eMÑÇ:Xý+V¬`Ý~^éƒ|¯Úý¦sþ7z»_ ¿ÁðÇz\ü°ÛßWfèWVh,ÿ_UË_…~­îõ&ïÙ+èLµ¦ô,F¢`µöëFü*Ê/t©DM:4|2Zµ–ެ[gsÞ°mÙB·o×íœ,ìÚEû½B‡ÔñÞ­žtðïaì=ñQ¸8%ø$ÂÓùúuʯ70ÿÞ]»Ðô駨Aƒ;¼!B¡#ÿ9x>(6lx›[ÿx=þIö‚Ø·A^3sæÌ¡¤6mÚ” ?È}Pð3-ü`÷¹MضÚgü`â3[hŠ~óý[æÿdV“YDhª~î2ãè-µâ§B‹ÀGóc?þGgºŸ ­SØ[2z;9¬×š|­!e üÓ§·žû†M£+ÙJÑÛpQàu]ÍV’öý3U{½bÊï£mÛÒøñgÙC&<Žxž¤Ißñ†Â„ gøuXjý§}Øó.[¶L¾ÖñÔ©S'ªQ£åÎ[>FRŠ%J@[ý(üwe•¹#€s~£Ý?EæH™½õS™eôSýÔI_ÅòW¡€ðÏ `£A–°.tË=;jݼ×nQ4DXï#¯éËèLÉzôÓôv?öúáà×ÔSDÕòW¡€5`§'í9“.®N¯]c9ÝLöKTºœ»í<™¼¶íQÅ3¤ŒVn¦c;ÒtžœL‚¯çAü4t¬Qù:7ÙQ%oŸ”ûÈè7„|àØwRæn¡IøZ+æcZø1络Р|ºê÷»ß„fß›ËdÖ¯Ný*°ÀÀ{á:Q¿-=Š›Êé´ÙŸÄHB§jüIÞ³—3gAÏ2Àó@¯Ñt-K1 ò¢8èŒB×3¥=G‘÷Ž=v–ÊÝGÛ·äY|ëÖשpá'”0á;»tŒS?¯H‘'Lø›5ë$»ýáy1Û¡»ûÓ¦M£¾}ûR£Fx¶5¾1bP„rÚ7mõ_—yDoõo¶«ø…¦ÌñúœþÍ…æÞ‡Õ>èøÃÄ (úE5;õ«â¯Bkߌ7ïä›ñ•¬%è½Kdç!b…u¡ëé С.CÉkÃvU8CRzû׌åtºJSz+©ÓO¼¼ž3•›’÷Ìeü:íï’§i¬Zu”†=/‹ðm¶Nà=¹º~ùÏuÐÚ¢ï‡J! ?Ö 7¾MÆ“˜vîô¦-[¶ÒÒ¥KiâĉԻwo¾”+WŽù Õ1bÄÀýO2Ÿmß[æ+‹¾ù‰™É‰ß(ü=d¶»ß(üPóƒ”/öúÁðwU…_…Ú6ÀJ:Y®€Î±š…×ðÜ-®æAÞÓ—²q‘*œ!¬ þI×at#szÁ9È€xx=ØRÀë Ê?€„„°& ðçŸ7¨T©G¬©a8öÁgê}ðpqùf2„¿Ç×ñ}øþ2eR³f×iðàS4gŽ-[ÿ\¶ámß¾=ýöÛoT¸pažëÇ‹/ s}Ó¢ÿZoó±ÏXç3È}óMZýCôK½ðWš’_&}ÎϤÝoÊðWÅ_…Z`è?ž.ç.Oo]¢+Àss6Ca:ØsymÚ¡ fL€:ŸYËèT­æô0~jîøwÇé‘|x=x]ŽÞÞÚXÒÁ`êÃGÀÃã&+ö•,ùˆ xJ¹s?gŸO¡‰÷Ø2Û7VùŒùþ,¡©÷ao€Nîk¡Ÿø+ê…?«Ð$|AðƒyO s~UüU(`À7 ØO\HgË7¢Ç¿$§ÏaÃÃÚ_xz+)/Y‡ö ›NÞ;÷ªB’»^Þä½l=iÝ—nd(DoƒÁ•§gabOŒ’´²ÒPÚ8}y{/èØ»wíØáMë×ï¦åË·H@ǽyŒá‚ïáÑœj֬͒¼3f¤„ RôèљȈ¢o¬ï=šZŠþ^™Û…ßlþý˜í/Úÿt}¾Ù^8õušto½Õ_ÐBáW?*øD¶e—<§KEªÑ³˜‰ ¾„ G/¢Ç§Ëù+ÒÁ#É{Ý6‡ŸÂTC±Û-ç¬åt¢IGVßûàâêPðFD&Ï0…¨—Û0ú«˜v’×õ|‚á³g0õ×­[Ç+zÓ§O§aÆQ—.]è?þ *UªP¡B…¸à'I’„Oùèqqq lÑÿ ó±Ð´ùOèE-þÂ2ŸiÑ_evÚ‡jÄ{°Ã?Xoó·–ù»Ð4û±Îr_f½ÕoZø#©Â¯B…£ƒò^³…vA— Vf‰`G€.þÑâÑ•M:•FŽÉ»ø`éãtÒÚùY²dá>Tø\]]YÅ>'|Ó‚¾{2Ï Á¿Koío°²è¯1+úKô¢o´ø'šÌöáÎ×Væ2kÉ,gÒæO-3±ÐÔû°ÎYµúU¨p`ìi¯ÛÎZè⹕2'½vÉ'u²Àc½àFw’e¥Ó•ÿ ý&’ך­ŠôšÇ{¼hïìUt´yOºœ«,=‰™Të؉ˆÇù.ÝŠèN«"Ô¢?ÃN¥T⹈ÿ}@VëZµºN«Va–¾-§úíÛ·ËŸ[E³fÍ¢qãÆÑàÁƒ©[·nÌÌÇ©¾R¥JÜÆÏ–-¥I“†[ù`éC}/\¸p)ò¦ä=¬êA‘ï•ÐäxOÉ<(sðWß¼à¯1›é«{ óÅõ½a2û Íœ§µ^ôÓgûEeæÔOûîB[å3=í6½ŠÜ§B…SÜŒÑ Ø¼“|&-äòÅü•èQÜ”LÔÂÊT@:øŒÞFt£Çq’Óåì¥é˜Çßä3~ymÜ¡Ì~T2ðZ¼ŽW@ÏTjB·Rç¦Ñâ3è–~?ǹ™*7yækF}²Í§,±®P„0øìÙCœ§W¯K´qãa«ù0Ô®>öïqª‡¦>Xù(ô0ÔÁµ-Z´À²ó-îqÀnþsý„N/ø{õ¶¾µ­YÁ_®·ö±¶7O/ú˜écuo¨ÞÞ‹Æ<Ðç¡+|ÅeæÕgû©ü8í«Â¯B…3c>ËÛK×ÓA“èdVt9gºŸ(=½t‹ÇZýÃDð³U‹¯} ã»ý/£Æ¡ ÓÒÕì%éT­¿è`ïÑä½ø_ùžŠð§òûí€õÛÉgâ|:Òº+U—n¦ÉÃ"o"F§a#ÑgÞWÊ@S~ß÷V~?~?®t]~¼-CÓà.‡©h¡GòäýÉ¢Â^ô蟨téG4bÄ9‹F:¾‘ø6nÜH&L ÊŸ??³ó1··SÁGÿƒ~²*ó¡~¿hRð· ëÈ{¦s|K§üÙ&­ýÑBSèë«Ïô[™œôM‹>v÷áÌ—TŸíƒÔ“¨ö™ï𫢯B…ÓSÏ€­»É{ÎJ :ö{g:W².]L_œÎÇÌNæ¡["1ÝñèˆËù0lõ€Á_[h;ûæE?™^ôcËŒæÇi_… ? øN6xë.ò^ºŽ¼Çϣͭ'Д\#¨¤4Bt¢‰¢MÍ8çFiF[2zбj-èhót Ç(ò+oÀKþ%ï-;5¥5uêWiÍ( @`ÝV^ô4™ÁÀ‰&ètÕÿÑÙRõéláÚt¶PMí¿%ëóßãëø¾}òûlÊŸ 0ôý÷îÝO‹—ׯMJ™ò …ÿ£+þ_)pîÜ“´gÏ«I€žžžÜ 0€@5X?f̘~ÍúQø_ Mxç‚ÐVò|„FÖÛ¡·ó-íãÿëÇì~•^ìW˜ÌðM þ4³‚?Pf/™õy¾‡ÐÔù°¶M~0øóèí}¿Š¾ší«P’€éMÙgÏ^Z:{ý¯úJïv…Ò‰³”U£œâgùû¨m´eª k·Ï¶]¼ómï¿1w={6ïM‡Æ„—û¼yódA[dUÂDµ5kÖð{‡Ä:Ú–-[hÛ¶m¼‹Ž†BæT @}vî!ïÛÉkùÚ;{%í¸˜öŽY@{GÍÓþ‹?Ë¿Ç×ñ}ø~Þ*±6QЧO?M5kÞc]ýpá~‘"}¡L™^R»v×hÙ²£6m@Àèüý÷ßT±bEæ€ôçË*Nýáñþ«îE߼ح|ˆð,Ô‹ý¡¹ìMÕ[ú(øÃõ‚ßÓ¬à7”YS?å—š?ˆ|…ÆàG{?žÞÞ7о9¡OöU¨‰ÀÈÅ‹Óo¿Ý¥èÑ>RXñ…‰ÏÿeÊd¯¨}›+´eÓÁ =éãæ:`ÀªZµ*¯Q…ÆÙ ìòš5kRíÚµýLÀŽÏDóæÍ©E‹œXGCêÕ«õïߟ†JcÇŽ¥I“&ÑŒ3ä x®ü}/f;Yœl±ÃŽ™w°¬ÙI0É À<2­|];v ‘#ÏQÙ²)vìÜú7ç€'§½ž=/Óºu‡m 2Ö7oÞÌV¼mÚ´áßÖý"GŽl‰?ÔøŽßI|¦<+Ä÷d=œìa°ÂÞx“bÍ}÷zè-}üÿé'|£à—Úº ~&ý”ï.3‘̸Bc𻩓¾  0ˆíG"•»û[>5@”…§X0®±J%ìêþé'I0Ëqªô/!&ïwXÁÆÿ¿Lœ81¥H‘‚ÍcpBÍ!eÍš•rçÎÍNreË–¥jÕªQÆ 8@nРA¼êp€ßVàpâ0ø™Æ ›7¢/PÑ¢OØeÏÜŽŽã“üúcÖäÇ÷›ƒüÙÛ{?wÌo|¿A„èOÛ¶m©dÉ’¼!€ \Û×}éŹ~ÊŸ©·òG ¬×_?Ùƒ°×Nhzû(ö„ÆÖ¯*³¬ÐÖô0ÇÏ&3ƒÐXûhë'ù‹YÁÇ)?‚P ~*P ô€ HÎ8àó„ÝuˆÖ4kÖŒýæá;:3»ví⓯Ӌyãst„ºv½Â®{X4aÃ~¥xñÞS¥JhìØ³L ŠøŒ¯]{Dþ»Ó¼y'iæÌS4cÆ)š5ëÍŸ‚–/?ƃmÛòØÁË ãOþìŽ=šß;l ˆYÐò/DÀÓ:À°Þ]£Ÿüêsü‘â›ê^+½Ø£•¨ïAˆ¤½Bú ?½IÁ‡ o½à£­Ť໨¢¯B… ¨ü/AfÃÉÄ6€ÜÐÚF— sçÎ\Ü q 0°gÏçâüÀ%ÙÏŸç-nPúô¯$èùq3dÉÞRݺwÙ©oîÜ4fÌY ´lyCþ»ïPÍš÷©rå ªV}@µjÝ£&MnËÓþ5ê×ï"Mštš–-;Æ×Åž=AìdÝÕ«WßChüCÐB7à¦ÌCBóY£·ýÑîÇ^~_¡)ï5–Y]f¡9ë¡ØgÕO÷hçCz7‰~Â7/ø®&m}K_}*P@å÷‰S+4ê1b€n}Ú´i©hÑ¢Ì7èÑ£·¼!…‹1³ŽpšÇé½aÃÛüùµD ÄÊ`ªTo¨B…L,Vì±,Ú/(yò·Ü!ˆë# 1c~¤Ø±?ò:!¶ ²gÎ\lôï‘fÏ>I7¢Ý»}hëÖm4gÎêÚµ«|ì ”:ujKDA¨ü½Ú†º üÍÒOÿ0Úi¦ÿbú ?^ðù=H{Ñõ–¾q¨Nø*T( €J»œ`16@[§ZxÕƒ;0dÈÖÃ!Nµrè³O”ƒ4~üªVí¾üL}ÿ–¯?ðPèAAƈ Œ/â˜ø{€ tp­$Mú–rç~FuêÜ¥>}.ñ˜Ï»gÏ^¾N!$„÷ ’Á $°¤1 û2¢_o¡îÔÔ@6½½Ÿ@|“ÞU'|*P@¥c;ÀÙ îvuëÖåm¬/b<àL<€ü† ¹À¤?xK…&€¶ X/DÇ@#‚Q£Îц ‡%(òaîÄòåËy,P§N&dL™qÐ x#4cŸzZü˜ýƒà‡Y?Xü˜ïÿ¢ŸúMWõTÁW¡BT:ŽPˆÓ,œîЀ8N÷îÝyGÎ2)pýúÃÔ©ÓUY|_Ió%H\‰ ’$yÇ£¾}/ÉÏóQ& â½€&ô-:tè@ÅŠãÑŠn¡ùß±ú(÷ÿ@úÃü?…Ðöö£é]¡öôU¨P@«¥Y¿8(¿ZÈ ÐWâ8Ð!O`ýúõ<nÂàθ +×3ruý$àøJnnŸ¸оý5¾® ÕAtI ÉиqcÞ¼ÀûfAMV¿Ge.Ú öü!Û‹ ùd4éDÕGª B… øSü MžÕÚ¼ÀÄÏÞÓŸï‰Ib ä/ÈÄÂæµÞþÅMóàOúIð§ (dÑ£G§_ý•µ <„¢@#ƒ Ø Áž×[’ ŠnÖsäxÎ× ®-OÏýÿ a,î¶+ (Q"K+ƒ¸À/z/úýØû/%3—Ð6°òCuT¨P@ÿØŽ mõʚܬç&½-kä+?õ·mzÂÝm§ÐŒ_àçî)4ahăvRhnpׄfñPÏu ðÁ 8-ÀÉ6]ºtT½zuÖX°`·Á9ÀÉ{Þ¼Ô¬ÙMJ“æuµÿ}KpÐ èÒå*­Xq”×Me¯ñy‡J#¸x¯|áà÷ï%4ùß!z7Î}à`5:þî¾tT¨P¡€â›,+v¯×ù“¦ºìÐd7Õc_D‰Ç†Á ÖÁVëÏo€€¬Šíš© @4æaûTï8(ÀŒŸY(6iÒ„†. á Ú½{ww Ú‡¢Û±ãUÊ–íÏçYüNÖA@0à"¯ šª /ˆ–-[2©MTÑ):'4‚à½éßúBSÌ­w`äƒÕ@ën€  (0`¨³¡0ÏßôØ!Ñ:1r’ž“õçÑË4=ñÜ3õSà|ý5-×_ãf½‹`ƒÛz§à³p~†°5ò[ëÖ­™€±@Pu¾±ÿÏónôèÌòlbe0A‚wT½ú=š2å´?~ðÀHŠ‹p,Q¢„oÁOúX ¥éz7 «Ð6ª mSA‚F@dñý–€ *PÀÊâ¿R?™£øŽÕo¸}…¦ÉÞ#ˆ²§~ºë£?4àáá—·Áúk*4«×B[£(É-Ñ‹F çõñÁ'g @XÞ0{êׯ‹ ÁoÀÞŸ#ÌÛçÌ9IõëßñÕБ m´i_IðsV­:jÑ€#&ÿùçªR¥ › YÐ ø¢w|ë pŒþYA7ܘÿ@3› r3 „U·X*PÀ°ZoùC™ †,½ô›,f¯p[«'³®³¾þØÐ|‡ìïúóá„ç¡>þšL+ýõÀ¦£Ð4ã»é¯³Ÿ@[ ‚3ú¨ ØE… ‹v7 t°6Ëb{P\Ñj‡¤/Ôú‚£õo)q]•(ñ˜F>K{öì÷ÕipëÖ­,T¿~}&SFŠÉ’Ÿl†/ëŸ×)úïÜ€&2+ÍèWñ h:P¡B…¶€¶m¯‡°V?ý/ÐOÖ}ôß dY±‹_h3× È<úÍÏQPoëšË[1ý„Ow°À1û-/³’Þ®¥‰:xøŸ:èÿ èEäKp˜¥L™’õ (¸víZ»¨ ‚h7kÖI–öçC°µþ- á:!fE~ýÀ‘˜7oµjÕŠræÌÉ„J ¼Gúøg¾ P@¬-¾é¸ ¹àª B…?äҥǸe Ò’ùÍ+iÒwòftCž¬‡€Ùÿ\ý¦Š“Õoz1†ãšaÀ’XÏDvHã±ð¸I…¶ãí®?Ú¹)…&›Zhd/œîÒ mæ›I¿ÑçÔöÄ ë€@Îqد¡„æúXa™Þ¶Æ˜w/^œuó/^h‚à¶m‡Ø¨'gÎçM€‚3±P²äcš<ù4“ýÞ`ØÃÆBx_àÒˆ®‰€Äéq½c5AÿÝð5Òbýs‚`4Õ P¡B€6§ÝfÍtóWâÄï诿n² j(˜ÿƒ`7[o£·Ö h6½HÇÒo¤Qƒ ÝLÏ]O´qcêKÏØúɳÞ:H¢ƒ‡úMß YdæÐ» …õ.:-õ¢í‚§"˜H‚`¾çÊ•‹5ó¡БÚÿ+W“×È-Þùǩ۾Eük  éÒ½âñ€ŠÿÝŒ½´jÕ*^£N¾€¬†žÓ?»ÓôÏm7}tTMï&e0ùüAµ% B…ûhÅŠc¬c޶©ù+aÂ÷Ô´é-ys=ÊÒª¡àtŒùÿ?zKµœ~úcÒJ5LXì.fÁ,#êIOWý†Õ8€!¶þšãé!©ÞM@jrÙõÆØ>8©Î ÀÛW„¦_1G|OüM|#&×?QMF ¨PZ‘p/›1ãûÃÍü¦õË/©B…‡4tèyZ´è¸|þ#ü3¦Â&!ük†è-ó2úi:–øžUÆÖŸ §§)€0 æ!†þï@ç ¡ 0^¡©Ð6® óÀzO–,Õ¬Y“Fmóª ÈuøŒæÍû,È_í4øL•*=`û`kÁ4@Àºuë¨oß¾<.ÁõကD¦ Â`G³‘@½kM(á *BÀªËæÎ=Á7M“ ] Só›n¦)R¼¡%1OÐVÇM F+¾±šC‘®´~jŽ©ß0©uê`0¦àÀUïfƒ_ôñÆm…¶9ðR/ a„T¹re^‰Cá³”þý÷n³÷êŸoéâò• |JcÇžýÏ#Àºî†7¿è@>8Nœ8¾è?<ÒÇ:Ëô‘Àp³‘@!“‘€¹p *B*À‰")S§žâ›eíÚw)þ§\à±`in &54ÔÀÀ÷æÉóŒjÔ¸Ç.k“&f à±)„€?áÒH0€ "é€ º>.W:ð5p¨Â >ëeÊ”¡ðçÝ€Ï`óæ7xþxöÿW»úͯ¥,Y^РA°Ø6âÐ@ÀÀù½Á{ä Àï êÇ„¶Ò:Ód$ÐJ `K ‹ÞR¼*B2@»qÛ¶ƒ,Òµë*Wî!ýúëkž•âTbË Ó ©R½¡R¥QÛ¶×hæÌ“´uëA¶@U০݂ðz‡ ²>"Àö¼ >8€ô|=˜ ‡V¸_ŸŸÕ«2%A‚÷N3󷔸îzô¸Ì@Üv¢£F «\©R%ùoM`ÉMÐÔPè‚Ð …æ MiÒðh¬°=Ò¨â¨PfõkסaÃÎSݺw)S¦—lYj–´¼Oóznjؽ<øœ ÷3~Ê.AX“îFX-ÄLù±#»à`C þü¼å@¿@ˆ¬pý‹÷ƒS¶ÿhF÷mÓ¦Ã;ò‘àa31‚G%ñãÇ÷­`t`.#ªÅúH*’Ý…¦ ƒO(^€ !`θlÙ1>q”.ýˆWú Mjï›:ñã¿gíu¸Ÿ,h®{®ÀO ÂéãŒúHà‚p n@,†óåËÇ À¯NVY€.\`t50@'`ذaT¡B¿ˆ†„0Œ£ö mÅ#ÑBÂ}§ªòÒé]ƒ @€ ?+À)|É’ã<§/Pà)ÅŽý!HÉQè(@H¨ ,XpÂ&¢“N ÂêÝÌ‹› ÍžÖa²Â¦  {÷î´zõj‹ kªÝ‘@Ô¹¬‰»wG pÊš6l`%E˜ù±"h€á‡Kè\Œ/zP’,¢½ÄúgÝU ) BÅϰbçÎWy- ³~´ëƒ^ò”x¼€€€%KŽY½.¨€ÓN„Ш¬¿Wo  ‰[ @êÕ«—E€1—‡ÇM6rf!Ã+êÛ÷mÝxim€˜õïßß¿A@ùñ ÐÈP»4Ô±ù¥H耈X& @‘U¨ø€a‡Ú»÷%^9Šý“C5Ññ\ØÀ¬7fKh ü´ÅŠê'ÉŽî€u<|^L·ð™ÇŠjÊ”ovª˜+×s>ü<íÜyÀ.ë³Ævt hE'à©ðY7Ȇ^̨ {åHwðEjC@…ŠŸìÚu€&NdaŸà¸ ë4è Xs³Sà§8B<öÃ%b;§]¬âôk€|îq²Îšõ%o¶8#€?ü¦L9mW!-¼0T0@yÒÌEМø\ ã«©B“î*4J¸ æšr¤é†€*T8+À.þÂ…Çe!½I ¼ =tÛ8>­Uë.Í›wÂßž?åH‚2Øp˜„0N·8åbîxÌÁQñÙ?þ¬YqÏ82uëÞanŽ5]±€€€ž={ò¨#?@Z ‚6fˆo®‚¸U‘™Oh0‚’¤ÚP¡Ân( ¡õ<˜ùÁ}ÃÃk€2Fjñ릧ÀO àFRÙ G‚¨á•-[–†ÊkqÞÞ>´hÑ ªWÎclÚKžü-µo5Èœ5Á‹À¦ÄßÿMyóæ%777ÿ@ÀK}€ÏÿRñØOh>Ѐr 6”|° Î Àú‡:Ôý,¹úW‚Pµê}š:õ´Ÿ²Á ü´á¢ƒÌ’9 @B8Ø…3f mß¾6l8ÄÄ×ôé_9Ýíÿ|ùžÑˆçlÚŽ±5á"¸lÙ2êØ±£<ä È‘#û7x&óˆÐƒðÙ9>Xû„™6  C¬„ ¨Pá„$¨¾}/òé?(vý3 Șñ%uëv…ý‘÷%‹ž8 „ž’$I"Aom 0§Ò–-{hܸ3¬yáhò«§ŒÃêÔ¹Ëönÿÿxð”ϳˆZ·nM2dà#¢_ï£Á €tð¡iÌ×A6ÚȬ-´5A¸ ¥¨B…sÌ?/>N¿ÿ~‹&|çtíOt$ B.€ož „C/¯\\\(yòäò³ÿ;Í™3O¾ì €mgƒ§ÿìÙ_P¯^—Y.;(‹ÿ7°=´`ÁjÚ´)¥I“†Ýýá—¹Qæ ¡m Úšào& ¾*T8 €êPÐåwF zÊíÏ;*r r…F,s˜b Š[úôé©M›¶4kÖr>ü_ ÎÐÀóljóí¶áÅXÓ,[AÀÌ™3©Aƒ”"E Kþ€pŽšuÐÕdL9 ¨Pœ`Ëü—5ë §jÿ›î?ƒ Ø¥‹ïcB(¦¿×3ruu¥\¹rÉÏX7š4i#µm{…Ew‚ûzÀ …°Ü^³æH·ÿÍsçÎòý˜$H-Jœ8±_æA¦ À¸N|…P¡Â n(¸±´hqƒÜÝßëêŸ_§ ¸´aDo¸*âA@E™—„ƒt µÀ‚ R÷îýhøð]T¯Þ-J’ä]J`[óÙǬrå4yò™ %úf‚䨱cÿ3òÛûôk Z“…&ÔÞl`ø(b  ŽÚîÿ ž±£Õèló#cÄж°©`I@€˜7•yÓ‘ V¬XTªT)êÝ{$ °_½{\€ƒS\w÷7Ô°áš?ÿ„]E€lq„yP™2eü“ 6dN/ýÚX¡ƒ€)2ÿÑA@½Óÿ€D ¨P ë3fœ¢J•ðÌÓY5УDùÌsY¨Zr T ÄE½E a™‡Âë &¤êÕkÒàÁÓ©OŸãT²ä#6à ÎîFƒµn}–/?êp`* µ@È*û£ÐvO¦§~m `ªI' ®2è @T¨p$@K­Å2eñν³W×ÏT¤È=ú˶**ÂP ÄÉñ¹pàf@ªT©¨qã?hÔ¨ÅÔ»÷Y°¬J}tß0n@ZêÄá€[&ì¹1¶³4 Ê„PÐÊ•+ÙZ| ð&¬¸f®ËÜ%4b'@ÀBý÷i ˜®*Ù`*&L8#O×Î+ªÝü´M€‘#ÏYôP ÄÚÂõ÷÷£@@¤H‘(sæÌòÄÝŽ¦M[Oœ§Ê•ïSŠo¸Û{T†î8I’¼¥È‘|ü]º%ÑàÁhãÆC'B(hÉ’%Ô¦MJ—.ëÈBãrl3¦œ€vú8À؈'”  ( B˜ÉTÚŠÙá >@Ô¨Q)_¾|Ô»w?Z´hs†‰¨9s>g^€=tPÔ±çýB…žðã7hp›2ez)AÈg‹@ðaÆ;KÛ¶t8Àzàüùó©Q£Fäîî΂Jþ¼Ÿoež‘¹ÙÌ×A€±± ÈÃ; Ž P@…Š žžØ]¬lYç-úDބϪ@è øË7“yÇQ]€8ã•/_^~îÆÑºu{˜„‡< 0ÖaЃ±€­|?ô- ðõMx t–.=Æ‚WM›Þ¤Ô©_[9x$KöV…;Ì߱ĉ êܽ{7¯V­Z•7ü!šjlÔ¯ÓAˆA6Þy…æ"ø‹ÌÈ2Ã+ BE‰À¬Ç L{gèN”)ó&O>mqJ€PІÑÌG‚x@ gæÌY´s§'ËfÏœy’ºw¿Ìn•˜Ëc…öÙª(Ð8©cs@KíÏ(æ ³¢{€â^´ècjÜø p‘]8·o?HÞÞûùó=sæ) îr¡·ÔipqùBiÒ¼&›¼ÅãhR ëØ ÀÖl–­ð ا_/kL:†lpK¡¹Â*:¹ú\P¡"ˆn8°mØð6ŸFœq ¯ 7W¨¡ùf ¬@¨)ôӣÔÑâ†î_ýÅù˜ƒ{yiZ —Eð<µoê׿CeË>¤ž²toæÌ/¹•Ÿ%ËK Å_±âjÒä;\B}sùòc¬niNèÛ±ão¼T¯~Ÿ&|oQ‹<~›6Úf®eßœ>ñ5¬üâÚ1Mü¾1Öa§Ü§O¶¶Â=‰Ž=& ¿KÈOÐÁîeЀÈ)Ó]Ð…P›*TØàÂÿ÷ß#Ô¶íuÖ?NÑ¿RdùóÏ›´råQ‹7+BM`.\@h^ôá ኗ3gN¶ÊÅg kq€Þ¹ó ­[w„z0NƒdõÀea¼Ä ›}û^bÒÞèÑg¹e6?,}qÒ÷­ðâïÑi:ôßMÓt5@ÎC‡‰ë ÅÜœº­=m£œD,ø„¢Eûd±;B±béŸÎ3°Às(O›vŠGÇŽW™\ˆ+TxȉÿÇßýñÇ-êÔéªüw] éÓOѪUGùõZë;RàܹsùúK™2¥žƵtAh›ÿê `©Ì92GËü[fc™¥dfßk¨ëG… {œDpr)_þ¡Å›Lp'V¢J”xì+P€P9 €£\w¡‰Í8L$|€êÕ«3n×®]X»Û/OØG¹ˆgËöÂ"@Gg*ž(èè: xZ´¸ÎFwà×__ó!vìÌóÁ*!ÿ¿K”èO޼ϨJ•ûÔ²åuk £aiFOõQÎz°Hæ4¡IC#ëÐÀz Ú P¡ÂÞ‰¶!Z€ùó?ã39æÈñ‚稘‰úvR TÈa%…&7,|€7þ0 šµ»Ì¨]û.cYé¸V"Fü̲Á¾© Z«Fˆ®x7è¼uèpfÏ>Éc¿@ÀÐúõë©gÏžòšÍ!_KDÿÞKüÎn‹o¤@s¡ ¬¶šD¡mÄŠ¨B…}æ}8a7ir›o0Îp"Á:Ôܹ~¯:)jÒ± „6SvˆiÎàŒ9’-scÍ{€ÆŽ=Ë£:K¤À Ø¾ÁÚbúô¯xÃ2Üààø5XµjµhÑ‚7'¬ ÂuuEævñýfÖ±ÐW¿Ž*ÉÌ.3©ÌB‘U¨°ÐözÒðáç¨X±'UȘwæÊõœ‰LèPøusT Ô ”ãzË|$H Œ'ÕªUKžÌg0Π׃Тw”pWøðšRa¹ry»ÄD¿6°&9gΪ[·.%NœØûà÷BS Übv™nxm3 ‹ÌÄB‘U¨°/²¸]»k”*Õ Næòäo©iÓ[¼Âäßj’¡:ðžbF¼Ø¿ë@ؙӠæÍ›3…/è yöËâz’ªW¿çPûnŒâÆýÀŽœ¦+‡–¤ÀQ£FQÙ²e)vìØÖaötPæñýf8Ø è&³¡Ìââ)ÑŠ¨B{m#@S¬Yó£þàÐÀsB”,æñãÏX%uª@¨¼·…ôß9*a”'Ož{Ã.7(I(¸X+DG ΘnnƒåºÄbøðó~ú€aðòæÍ˾ Vˆ=ŸÃ”hºÐA|#¦Š¨B…}.hŒ°U3 ~G‚Ì5á½^¼øc>i`¯Ùš½iTè'BˆÈ8l5E '\øŒ=ZÅmAF 8Ÿ:õ4Õ¨|à9l@?¤D¼&ÿœ[¶lIéÓ§·–‘ ƒ°Rh›S…F „\pUñ½\°"ªPÀÀhñA>èŽZñÿȧ›ž=5ÏskÅHP¡ˆÆŒÚ\ÙaRÁÉ’%cw<̾===í^ü1žƒ¸Oóæ×)Uª×Á:ž3|Ú´¹Æ#C¿HÐJ˜>}º­|CÀ¸¦ŒÍûP^|¯¨®- سƵk0@''‚zƈ“¯^—ùÆb‹Á‰*ôÀI0§þ»pØVF™3g¦:0 ÞÞ£tå Ÿ9ƒ;g‘"YöbA~élݺU~ß*S¦ »+ ÿ¥‚aä#¾ñŒÍ€9âJXÍ(¾ R] Aè`Þ×3 þ ¸ù`oÂ%p$Ä|ÀÃÚ“¿*,äjÊ<åÈQÄo .Lƒ â¢gOâÜ!†7®ó¬èâP± Å‹ûy½ >ô"Dˆ`Í(à®ÌÝâÀ Îß”ëÊ,,¾‰)ç@ Ø ã}`ŠíhŽ'HðŽÅ‚в¨Ø~ÖðCÏ—ï)µjuµË·nõ_zTVŒb MSþ¾£@˜îX ¬\¹2ËâÚk5×ä}¡ðKa߯­¯z’?ßc¿5Ý|ùžYµ¦ Ó eË–Éë¼%OžÜšQîg—…æ`ð@ Ħ‡¡ØVzù„ R¡€ý€@öÁ :8‰äÏÿ”½Ê¡)Ž¢$†Â¾~†öxƒwøæ;S°ýbKª€ _F8‚AþJ8"E þ,‚‡Â¸Ùÿ~Y8Ék÷&“ïœÉ®¯j͚ݴjU€|€jÕªñujÅV~oÇdn?*N’Ù_fs¡‰`® @€  L‰H˜E¢9dÈyúë¯T¡Â6)Ak2\8ËF%ÅÎ24Æsäx΂"7YÐ¥pK³Å‚TVÞï¢z+ù“p  [¶lÔ£G@KOœx†-£Fýì4ÅßHˆázž4ɺu]lI 0€ (`Íj øw²Ä0D‚zÊüCh|€ Bé¨P h€éL-?ÁE°s竾*“¡øCJ´FûÔºõuê×ï"ß,pªAá·…è§€ŠD ýú¸)D DQ‹=º,Œåhܸq’ ÆÞ?þ²f}áOû?xüìÙŸóuëÙÿn¢“$1 Àj |€Ï2oÈÜir|\g#ev–YG|¯ ø* `Úž„•ð‚'¨Q£Û<Ï7¿A@³¼råûòFx–É}¸Ià¤Ø¿*¬Œ°zkx¨ÌwÂ|€$I’ðgrþüùR Ä8lõê#Ô²å rww>—Nc{^€{\ßÖŒð0 €|òo¿ýF‰%²v5ð´> øWüèˆk ¦AUdæ’é.”_€ ‚|+¾G¨yó”8ñ»nñâ}à9ÿÒ¥Ç<ßW@E EëbÛ8ÝfÍš•]×®]kój Æþ?AuëÞ BÙ߯"â5a}fA0³f{ÇX 6l*a5Ð >Àk}°Þ|€‰BÓ€”áPhFQÆ(@ À)„ ón ¥Fº*ü‰ú ñ®pàj F(r(v¶ªzzàÍðl@œ šSü×@o€ Ùn(zzî·²{èÍ ¨K—.”%KkT‘ØèØav½|ètš3dQ™ieÆJ*X… ( BhŠq˜¿tä( ^¼xT³fMY̧ÑîÝ»m"ŽVˆÇlË´ à äD˜¬ˆ‘ µÿ>ŒE,XÀ×­»»»5£ð`û¼EüÈ€>À0™í…æP@f*¡­ƒªÕ@ ( @(0úÉ^ó“ vqq¡_ý•5ñ± oíjà®]h̘sT´è&Ó:ÛüßH€(x‚çcÍ&€¹kàØ±cÙK!f̘֌ÞÈ<"s£ø^|€)2Èl!³²Ð!“‰ï¥‚P¡€ „ÒÀi°¢ÌBSœsÈ(+oX}ëׯmڴɪQÀèÑçØ#¤¼P ìÞ½;åÈ‘ÃZ•@¸îÀT`žø~5×^&ñM*XT( €¡¼ ð‹Þ*¾éÈQ®ÁªU«ÒäÉ“­R D1EQ-^Üù@É’i„36óQ ƒð^ ëT·‹o¤À5âÇÕÀzB“ N­Ôj  P ”G8}€ßÓkGŽR§NMþù'­\¹Òß­ÌÓ'O>MeË:§‘ (–/ÿ­ŠA\ Èõ ­h&”*UJ>^4kÞOüÞNÍ5Ð’T0®¿62« m50©P«*P@ú( ”#GÈÈ‘#SžJ;v´J:TA †ÁWþ–#»±cǦJ•*Ñøñã}5 Âõ1þ ªW/(å€>ÿ‡ÿF|ð±×µŒ­€yóæQ£F(iÒ¤Ö¸]·0 0VÇÊì&³‘Ìâ2Ó뵨B[Nü@ùhçñ·kwjÖ¼Gùò=ãÓ¿‹‹e;`¬ %HðžÒ¥{Eùó?¥jÕîS›6×iĈs´xñ1¶lG@v¼q><¥L™’<<™L®IC%p²ÐT›Ë¬$3‡Ì$&£u]ªPÀRñG«+>há—+÷ÜÝß›Ûg.èiI`»ÍI“¾e±Ž¯ò8Ý€€€TØ!°"V_æ9G*T¨À£à,]ƒë×;¯!sæ—Ô£ÇeîPÁbI3gΤ:uê°Ÿ‚ïçg}Œ³S|ï¸\|S ì!³‰ÐV@3e¤Bß‹ÿÖ­‡Øäã?nQÖ¬/(zôvõ%×FŸ˜'аám;ö,mÞ|Èæ‘€*ì(‰õvñ GŽ’'OΟ_lX24°o®[˜0_ƒqöÿ•ùà%ÌðÝk¶Áƒ ¢‚ ZK|+´­K£\—#dv”YGfA¡mÄJ H…ßï Ùƒ´W¥Ê>©Gˆð%O_(aÂwÜa8ð¢|Gl") ÂN“ ¶À"ÿà(;ÜlÙ²Q×®]Ù+ÀòFÀ6ì<+ö„-xƒƒˆçŒã#KÿŽuÎ.ÌkFíÛ·g¾„„@ÒG,Œ •ÀÁ2[ɬ*3»> ˆ®®O è'¬ìýóÏy*_þøÑv' X‹öï‘Ö­;lu'@v hÆCBö¸p  zôè,…‹=xßFÆÅåÊõŒGhŽx.7·O”'Ï3êÖíJµþÍbIÀG!V¬Xֿ裀]f£lÀ r‚ÌÞ2ÿ'³¤Ð¶âë# ðêUj€Æô?È,ìï£ø;’yŒç‚xPÙ²¹û°eË!«n2 ¨°ó( ®^$; @ù xMš4¡E‹YB»}É’ã¼E“-Û .ÈŽ#~aB«V7XûÃ^kÖ$¶úôé#O.ruuµæý„Ýó £\Ÿ³eŽ’ÙEæoâû­W5 PjØþ üÕ­{‡ÛþÁA8ÀZa÷hÊ”ÓV1ŒPaç§Ÿ —ê¿{‡€°Ý1 èÑ£‡[X uëë”3çsnÉÛ“—ã—ë_Á‚Oy Ô­K£€¥K—ò5¾„ £€ýâ{ sà–2+ m+ ©¨µívÈúvî|…wöƒræïÿŽñ^ü믴j•ÿã ¨‚1 F2ç1bÄà­ŒvìØá«UðÂ…Ç©S§«T¤ÈŠ?èÇtè ëæ?ˆºŽæ2ÁV޾ê£KAÆ( —ÌßÅ·­cࢺ*Bرã·þ1ƒÚw™Ñ|ù´ÖP  Ù®Â^8ݺ»»SÓ¦MýB'<\ êÕï³.6j‚Ê4£ptàN8cÆ)‡ŽÌe‚³fÍjíV®Ûcâ›W€é(`ŽÌ‘BÛ 0( ¡à„ …?ˆü õï ’£x qã¾§&MnÓ¢EÇý¼á( "ˆ µþ{u˜WæÜ˜w÷îÝÛO¯\8OzŠZ¶¼NE‹>–àá-Eú)@Üü €7¸–:€èÌоý5»ªÿÙ"gàŽY»ÛO3gždUÎxñÞ[è|¥)ÞП~/Œn:;lÞ|˜Ö¯?ÂäÄuëŽðúà¶m‡˜Ïãåe_™`´ î´Ùäú5Œ×Gd“™V(Û`!`v¹ÝråñW×ÏT´è^=ÚµKÁØÇî¸Ãlƒ±óŽQ@«V­hÅŠ`ývàÀ ”?ÿSÖ°,ØõŒ‰…ÑAœhúôSL"ìÞý2µkwš7¿IÍšÝdx `l€-Œ,8.Âa#€€Ñîo)R¤°vðF¿ž× ß·:Ȭ%3Ÿ> ˆ)”m°Špšž8ñ •*õ˜ÝÅœ|¡B…žújAª€  @"™ód~vÀ ·@² ”'ëÍVonÖ¾ŸEÂ@L™òÍ’Ãàè`]Á7Y¸ :p,L›ö5ë $Jôž&Ô2I’w”*Õ&öB¸¨V­{¬]t@N´Ue£€ &°„2H“VŒ÷dî0ë˜z Ô¯a?³é£7¡d‚U„$Ž‹÷g#G* Âi'ÁÂBÛ/w@a‹/ž,šµhÚ´iÚ Hb[s~Œá0*´4 ÀVŠ=м¡$è›,1~?ƒî~.Uª×òú~B¿ÿ~‹† ¹@K–ãñµBX“ĺ$¸Vn`“Ž›|Œ“Ù]—™NT„8€á2eñœÝ™G0==úœ¨p¦9 „±Ž¾I—.³ßW®\éQ 1˜þ0 Ë”é¥E™`utB$6$ˆÑÀ¸ã¾ÿ=Ì<ke‚çÏŸÏvÊÉ’%³v€Ž·…Q\ gÊ*³Ì2óÊLn2 PÚ*~n¢æušór T²ä#îVXR@E0Žðû#󃣺ѢE£bÅŠñ<¶1 €Ê î0ŠûC­üÂÇ#‚ŠPïÞ—xü€û”5¯qûöí4räH– Ž=º-2ÁÛÍ@F eN’ÙOf3¡ÉAgÑG?QÄ7¯*~N€YTÀ°b‡Vœ3®"NªT¹ÏŒdK7Tc`G¼¬~’tÈVN·0ÃÞœ9s¬’ lâ^Ö<Úô‰½ Ò{< à<%Bh”ÀúØšNº!«W¯¦N:QÆŒ#ŒQÀr™seŽ‘ù·Ì2‹è×3¤¡]…"ªø™X·`îj.€ïœÎÀh-ÂïZ¸Yš * "˜»(„‚"FŒÈZø;wf›Ü ìà>±iÓ!úçŸóìàˆ•aÜ‹À%Èžý{˜®úGœ5kÕ­[—ùV¼—mðxð4ë@ h‰Ìé2ÿ‘ÙFf5™¹Ä2Á*Tü| yÝ=.ólÈÛÙæƒð:‡ Ö…”° ' ü^Ó<æ0 777*Q¢·½ƒŠˆâ½ÿ#ÎQ… XÀ‘…H‘>ó½©C‡k¼bh À(`ذa<&±’ˆëûŠÌ­â{B 裀¾2=ôn|!â å¨âg ÕpS¼øcžµ;€?ZC‡žgf°*œ4ðûÅV¼ç²m€D‰QýúõiÑ¢EABÄÖ ˆÂBë?8äÂaH”'Ï3êÙó2 ù·€n1 H“&µ„À—2ËÜhvMc0Gæh¡É@×—YPf*½ó£d‚Uü¼ˆzÑ¢ãÔ°ánµ; V¬¼+ ÅBßæ€ ¨p’@[¸¥ÌÛÂ[Y²d¡=zØ]À¸7`¿^!ærÀŽ²Ã£Fe­ÿDƒ ÇÀ5jð}ÁJ™`Œpöˆe‚룀!ú( ’ÌìBÉ«øÙrÓ¦ÃÔ«×eÊšõ…SÐjL—îuér•M‹|»èPá$S »Ð„d>: €ñ^±bEyRŸD»ví²@ë¿OŸK|ú¶¤èèQ t@BqÙR FðOÈ“'-ŽxÞ"¾'®ÐGp ì-³‰PÚ*B ø¶âsŸOÜδþW±âCö,·´þ§€ ' ÈÇqÀ(2¸-Z´ eË–É“»—Nÿû¸ë†ÖܸÎÑ„Àd‡AJô¯ €nÞ~‡o­6À~ñ#!Ð\&¸¦øQ@Tü|vÐîΚõårŸÁ…øS§~C:]幟_»*œ,¢è£€—Ž8áB&xРAv ÍÞ«×%ʒŲðOpuáZZ¿þš7Ϻ.È‘ãǧòåË[+ŒQÀ-ñ#!ÐÔ1°¿Ð´Ê M ¡ø&¬®q?0œ¿æÎ=ÁvpÄùŒ«_Ù¸J•4cÆI…@Pá„£€dBS“ûâ`* Ç@¬Ãœù¯íü×­{ÇéôAÐÈ›÷™:¤Ø"œ={v^Ÿ´òz!pƒÙõmÈ[Òˆ#”6€ŠŸ Á²‡áœ÷¢Eû,>žj``þ÷ïѪ‹\N: (¦·“ÿßÞu€WQmÝ£>Qz EJB‘^BD@ˆ4éHU©*Q¤„ŽT¥JQ@y€òžþ J•¦t,HEóŸufÆ\n&ÉÜäÎÍMXëûÖ§„¦Iæœ5{¯½v@‚Pâ.\¸°Ž ^³fM‚§ÐnÃ9P©Ò…$3þÅU€qÁ t²/AIK–,‘:tð%&ø¢0 vÏø\aÄ[ÙLÁâñœSÉK ÌŽ}Ý“*\Ô ‚)ðwÁlqYÏý~ðÁ.G+B)ˆ …5p&PU€téÒɪU«ÊqãÆ%8&øã¿Õ­·ððA™rKgÌž}Àñ®€­[·Ê)S¦ÈÚµkËL™29i`”óh,­+&x„ˆÎ(¡˜C0€H®ÀýAêVÏžÇe™2—tW ü0ý!ô£k×Úéë4œ€Ràë]òÓ¯ŠsæÌ)[´h¡Çà0çkðÏòå{dÛ¶§ƒÊl— 0vì!Ûí ±µpþ 8P–.]Úi+àš0ÌœžÙkÍç|¡âDÅþŠ­„‘0¨Ì‚ÙDrÖT.à=ŽËòå/é\7@PÖCËÄ.]NÊ… ÷Ùný£ ’!àÇÈØçæ÷QÀb‚„ƒ@_ªx£ž3g¿¬[÷WÝoF“r¡B×d¿~?êi§ÿoCØÐ¦M äpYÐ9qw6€µ,Ù÷+Œ Ë fÉ]X}@,á@)«xäF?ÂÂZüªŒˆPõNJÿD2À}æ×­€j„„„è­x¾ÆCx¿ýö÷ò‰'.øqèŽß«…¹rý¡}hú6Ýð\£F §ÙŠ»³¬V€eD6ÀPÅŽŠµ³ˆ” ŒJÀv¹rån9zôaP¸ðU™6í-õÆî·~£ß~]¯#FàZ¾\þD2jÌ2ËÊÙ˜'OÙ®];õ\­plܼy‡3æ°^“¸çÜÝ)¢G¹)_xáŒ>3|9/¬½{÷– pb„ó‚0ÌœžSx<ïû*6Ì RаJ‚(³Íšu@¾úêq]  »¡ú i à÷ ×íƒxË@ÌèôéµñÈ©¡‡€H†ÀŒ8úÄ»D€¦Ð ÀèÛðáíô¿’%¯$ÒûsÇU€1áfÍÎÉ%Köúü€V66lØPfΜ٩!ð'Å-Â>Ân´bWÅ:Š%³ˆ” ,c yk×îÔYܯ¼r\çrð‡`\è±½-àA¼0Ì„øµÅ‹_‘‘‘¿Ê—_>!ßzë{¹zõ.]iðõ!¦ ’!Ò #För ˜!CÙ A9{ölGÙØŠ âůúÁüë®ÀŽx•rv`*ÀŠ ~øá‡|.±åq¯ˆ6z¶ÞUŒR|MDgÀ˜E0€HîÀÛ ˆd>D#’³cÇSÚ‘›!Ã_¶fxðóh!térBŽqDï|øáÎ8ã})ˆ ô‡gŠñv&»víê(&Øw™%KÂ+ÖTÀêÕ«Õ™ÔE·ÆŸWüBÄŒ ^a>÷o)öTl,Œl€0 ð¿õ‰d/<[›6}£û(é{?¤hT®|AtLÿº5kvé߃jBbÞø)ˆd |`WÀ·jÀìV¹re9zôèxc‚¼å€tÝ€ÔRŒ,&ô,9 ”êׯïtc ¦Ž)nöÙhŒP|I1RDg¤3[¬)Cx‡†(p=ÆCš)“ç;þ>/àï¿›€H¦Ho>·YŒ‹ g€×T€5€©œ`Ø ×üC¾NxW6lØ ‡ "Ë—//Ó¤Iã´°]ÄŒ ^©¸HÙý[*VU,¨*˜ @ÜkA"(ýcq‡[—?‘ K ¿0Òä®B¤J•J-ZTöêÕ+Θ`#à€öøkŒÄÈÀ¹ãK@\1Á8?ÂÃÃÆ#`«M+`¹ˆÎÀØg}Ųй…‘ @C A@@zD »>X„* ˜Ç<æáíZ(§/[¶GØs ¢Ê}IŒ{Ê9yòd©ó´pÞôhX «…‘ 0Uqˆ0 Ÿ5‹ füv%<ª0ˆ½"Œ³€däÊ•K]î/ÈE‹é·_»çé£vʾ}Ôã¾Áh̘ñ–N*ÄhrBG‡½³˜Ø¿Y¢D ]-ñg`YЗ6­ë €!°bSax>ò™_ofñO@ÐÜ@µ PªT)}ÙaM®]>€ ~PoÙ—‚r`îܨóì”)ûòKÿx‹¬˜`ˆ£9r8Í@²ãf¯sÀ3`”y<«XZDY ((B÷…‘÷¹y©$ fÍšrâĉê²ÿ̦ ðµn´hqV††Þ *_ÆŠÇŒ9ähU¸¯­|N¡ìÐѶOñ¯V€g6À`Ŷf»‡ÙAÜL`tìd Z¹sç–mÛ¶•Ë—/·5nÚ´C¾öÚQö… ¯àxû—:W¤e˳rÑ¢}~)ÿÛm ìׯŸ,T¨“l´~±gÌS|S±—âóŠåó*f4[÷SĽ \pŠc*àf Z:&Ø.c»ßÅó‹e]ÁàÀÛ?¶“"¨hÆoüš%â9€V@“&MÔÿwV§†À#Šÿ1 K̯éHÅÎæyP\1»`6A@@&þe¾!~-Dà6"&xÖ¬Y¶ÙV,0¼Xܕԣèý·kwZŸ'së<Ù¼y³5j”¬T©’ÓVÀa;m°9*NR ØB±ŠbÁl‚€€ V6Ï‚€€`+@äÆb™Ë¨  zõêrüøñºà=€À<ëo¾ùƒz¦ÏɼyoèÒ¼ÛÓ9sþ!Û´9­7‰ºÙ÷[·n•QQQ²nݺê<Ë준)ŽÝ6­d`YÐÅAŠm«+VÌ*˜ @¤4€m€ þ,,Øçjߎ€HÀ÷ ¶ÉmŠïùÂ1Á­[·Ö1ÁÞ­C퀩SʶmOËR¥®èg[ÝiÓÞ–Õªý®Í[¶ì¸€Z¿~½ø`§œ1ã€4è˜>ž~ú7Y¦ÌeY°à5}©CÀ8ˆ>úèúÂÇ…^µêïÚOÐ­Û }¹7mzNæÉsCW쪉˜BÀKGR´¬}ûö•=ö˜þ<Åó¹Ä×ê”0¦9<[0.Sœ£øºb7Åú"Ú˜žg4jûóÏ·Ë+vëR>ÒˆˆËº,²“²~ ~-vy#Z´dÉ+Ú 8dÈQÝFÀA’PeO@¤p¤5ß]7Z­€råÊÉ‘#Gê7_'gÄÿ»]WW¬Ø£ÄÀA}VÀÔµë ]ýkßþ´ìÐá´ìÔé¤ìÙó'08qâ÷º•€ÙþM›¾ÑÕ„ÚµÕí»6ÄA‡§t1)ZVLp£FÔKO¨“VÀŸŠDô² «Cà{ŠS‡(¶Ʋ œYÍV«DÒ ¼™£o?yòw:‚³L™K2cÆ¿ü2„ª¥¿Ö­Ïh“Ê{ y°)ˆ{ø#`Ó§O/ëׯ/gÏžk+ .A°mÛ×Ú+€Vßúõ;õ%ççɧŸ~£ÿ_Üýûðq¼”/Q¿\صQ9èßÿÇ€Å\•ü™Mzâ‰'œÆ_PüJñcaŸ ð¶â¿…}6Àü¶'’Dà"ÆèÍÈ‘Gôì¯5öão—/Z( ¢¼‡‡;¾}õP÷î3ËÄÛD ˆ Η/ŸìÞ½»mL°;s÷_ëj`ÇŽ§t›À»­hE?óÌoºz‘SkÖ¬‘¯¼òŠn<øàƒNüÇ7‹˜1ÁKg›ÂË‚ê(–RÌi¶ä™A\àò_µj·<ø¨6ø=òˆ}ߟ‹>BCÿ’Uª\ÐeÃåË÷ø$(ˆ{( c·|@‚R§N­c‚ÇŽ«ÞÎ?µ ðÿÜývõ6Â[äÝ^ÄY—T$½[¨<@D ¶åÖ­;þ!~Œ3?ŸX!*"X Ô°aC½1bÉA+`M­€w…aò¨ø‚Ⓤ fI!PVƒ©—?L:péÚ™rÜØõ1ã-ùøãõxÑÊ•»·(ˆ{¨ €µÁˆ”½"0={vKðOüø¹ç~Ñy=z׿oîÜýZ4À૟†À¥K—ês­1ÁøZRÜ(bf,F6À0ÅŽæ¹Q\1»0  Ì Ü(“Íšu@?((ûré‡g%í€ÈÈ_ufN ‚¸« Ѽ(¢ 1cF=ÿþöÛoËÍ›7¤×Žþþĉßé~?*ƒvmCx‡P¥„([ö² ¿®'Œ`.N“æ¶®`êÿÄñqœkð”-{I§”b"&çÕ«wéóÏ—ŠÀ–-[t^B:ut~‚ƒ*À5aoD6€§!°…0BåÌ !0ç´¾®]ëq›¤XûéùpçÊuC©õÓrÙ²ø—€P÷ð}”Ç|kt½€™÷.9ÄôÑG1Bø/^¼GÏþ#wÄ}ø3á) »®+ ð à´Q„óÇaL0 =Zëý­ ºü™ß¿å“Oþ®gyQ¤ ˆ€K»N ˜7–ï¼óŽ^•ëNäîv9oÞ>= ˜?ÿuWƒÈìˆvC¡B×ô‹Ú£N<hŒ3FV©RÅi+àga,yòÎX®8׬ìôTl XNÆOÏlŠ"qåÿ#ŽèÞ—›=¶Ä˜‹½ªC?àÐ¥ [ DŒ³7Ýx»-X° ìÕ«—ŽÅõw+•>L$aDF`Œò%ÅÙƒÑÁÒ¥/˾}Ôc‚¨–Æ× X½zµìÚµ«Ž vØ øAñSÓˆl€Š#„1òYK†@´|Ò N‰(k! #0˜¡MÊÑ¿¸r0ïûÒK§t: ]`Ah‡8–Él D+‹pªW¯.Çïhc /¥l|ýõCzÆUI7ÜÿN=0"רq^¾ùæêÿs‡£˜à™3gêEJ8f|ms– °Ðu}E´!Ð3€U"áÀZ¾ô,ôÚ“êA‹ï!Ì”é/½:xÁ‚}¶ñÀ¡7Cäɘ È–-›Ž ž7ožßZØ"~“&çôè_ “Hc›€ÿRhÄ—I‚*ÀƵGâñÇwº1ð¬¸{Yg+àÅ×Et6F?=³x¦P$L`´#võëÿ¢G`‚íò·ˆ°$‚Á„Üo ‚°Þ5¿ÿ.¸]À&¼bÅŠù-&oÿX<`À1Y²ä• iI¢äÀ¡CêêDü-Œ/äŠ+dçÎeþüùÆïSÜ bÆ/F6ÀkŠíDt6À#Â¥! a—éÌ™Ákô\þ# ";í¢)âà­°‚ù=éªÀŠ ®Y³¦œ0a‚n$& o×óçïÓÙˆô –Š$þ;PhÑâ¬Þ@è$`ëÖ­rêÔ©²^½z244ÔI+à’â"¦!Ð:S°ª·0r*)†›ç ³(.¦M;¨£0Ó¥»ÄàoYµê½ëB)"N¤7[?¸Ý ðŒ ~ï½÷¼eËFZ¾|på‘U€ÛzSé¸q‡l«±e¼öÚkzlã“ñ|.o+þäÕ @`0²f*ŽRì¬XG±”bNAC AØ´0FvÙíV@êÔ©e™2eä AƒtLpBÀ‡î’;ŸÔ»H‚-ÿ=áá7d·n't›ÂiL0DQ‡dîܹT®+îöhxgLV¨ØZñIÅÇC³(Ó¨];ø[Õ«³@>SeEÖãbËœ9³¬[·®.{'Ĉ²:L¾†鯠<‡°‚¸qãszA™Ó­È˜8q¢|æ™gœñuB6À6aŸ ðŽy¶ôPl¤X^1·bž/> 8êḥ €H‘À›áóŠçÝ® ®Ïdøê@ÿa_•+_L²¹'/"Õªý®Î¡ñNxo ìׯŸ,T¨þ<Åó¹„okž?ö†ÀiŠÃ_4Ï—"ÂÈH#¸,ˆÀ€` ¨YDíû ŒA r |¾÷^ïŒð±1°\¹rrÔ¨Q>g å;?úŒy$Ön¬&Æn»Vd\SØؼys};4°PÙý…‘þC`>md+€ÀyкuFP¾|ÁtGï(èÔé¤\µj—mé€ b.ƒÂXìj+ÄT€ŒP§—$Æëüv#(_D¬—œ“ƒÕ ª¾E&ßxã Y­Z5™.]:'1Áˆuþ̦°LqŽâXÅîŠõ#„‘ ÀV€oQÀض5räaY¦Ì¥ .VìŠ4è(£€ "a@j\ ³´ìzL0bp{öì©cqf|ôÑ·²K—“:õ3X[‘`îÜÈ>}~Šõ,Š«€¬„îÝ»ËÂ… ë …x>—*F²£ç³Jñ=³0T±­âSŠ…Et6[Η¡·¬F@Ë8i—D"ªØ0Bñw·EáX1ÁØèÄ€J$ª|9rüÔ• ßS·ÉÙ³gËF9݈0§ÿ³©¬0Ï™ н„áó¨(Œl€ @PÄ¿‹7:u:¥ÕmRGoÆ\ü§lÓæŒ\²d×D·B¸Æ× —ý¸ØræÌ)[·n-,Xàh* ¹´#{õJ˜€úä“Oþ‰ v°1-dx­l€Ù¦¨ë¤X[k¡‘ N0€À‰°ÚcÆ–+^ ªx° œŸ|òMóÃáéÍVÀ~C`©R¥dÿþýåÚµkãm`¶>¹´úöõ½ài\¾|¹Ž Η/Ÿ“˜`´`üئð®â$ÅŠ­«*TÌ"˜ @àTÀYÿî»{ÿ™–‡-cÆ[²iÓs:{ (Â/­€7„Ë»àt ‘‘‘‘ròäÉ:7> Þ¬Sª Ð;&8**Jç& ?ÁÁTÀ¯Š[EÌeAh À26P,§˜G1£ˆ^D@»€³~Ó¦z¼¥J• AaÄCéÒW䈇ã}Ø(Â1ðVˆÙn·0óž7o^Ù®];¹dÉýö»K~‡:ôH²D°] ™/­$&:TÇ£ZâàüùÁ£°Î£€l€éÂÈè¨ø´bqÅ삆@ 'Àò¬\¹Kvï~\«p¸ï“òAC©­]»ÓrùòÝú¿€ üL`| S®î Àå¡c‚?þøãX ÑA@‚>hæLçA@qµ–.]ªÏeìRpP¸K+Ù ßVìg¶xžP,`ž;³@¯°&æÌÙ/4øEo¿J %ŽÌí,Ynʺu5ú—€  ô‰±\æºÛ­Ï˜àز¢£€ê(`’ù_+`Ê”)²N:N²°,È3À³ €løôŠb}Å2ÂØ‘Þ£@PÄÇVÀ믒5kž—¡¡7:€¿ ©PÙÇÑN['Aø |/b¯ü·«0ºÁðÖ©S§8³‚{ÐsÐqÇË€œn 8p ,R¤ˆCà adxNx£_Sl¯XÃ<{² ÃÈVÀºx¿)¿üò+¹víNí¼òÉßµD%<”?J€SÅ.Çe6 ‚H2ßáþÆÀŠ+ª—‹×c Æ:àÑ£ƒwp¥JÎ×;¥µ1°Y³f2{öìñe@¨a¯Ãÿyµ,Cà<Å7{+6Q¬ ˜WDg°@àl1Çš5»´¨Qã¼n¸é €À@Ù+qù#— .×?Aø xC­xÍíV@hh¨¾èæÏŸo› €sm”Ù³f žÝ$øïÈžýOÙ²åYuYïõKùß{cà¸qãÔùWÕI+gÑQaly´Ë˜i¶vp/D ÈÉtlPø L¿VëN€¾Ê_n¨òü[þêÔùU—ýñæïËåO@‰‚5°ÚíV¦ƒÛ·o_ýÌzq±¢¼>pà1Y²ä• ‰&ǹW¶ìe9lØ‘DÿÅÜ­[7½1ÐA+¾U«Cà"ÅÉŠ…±,È2† )|ùÆ„û<Ó§Ô½9eÎ|S]Úw¥Îñ{QQ@¿A?:œ’S¦|§þ„¸k)"QHm–Œ÷»-~÷ÔSOÉ·ÞzKnÞ¼9†€øGîG³fgõ[wR'“ZËÈ^xáŒ~ûO¬û?6¢"‚JX¤”-[6'1Á?+~.bf,Wœ«8^Ùhñ”F6—QøªN逕+w뜀-ÎÊrå.Éœ9ÿPóm-ðÄ%ðsø5¸ôñ{ð`GD\Ö!?PÕõƒÛ?¡¥5 ‚HðFˆE2#„Ñcvµ#GÙªU+Û˜`œxËÆY30ö“$U+/|I0D¿ùæò?ÿÙáÊåȺIŽ3FV©RÅiL0Æ8?ñj ’³ÄlØe¤4RøJTð,\¸WoÄ[{­Z¿éR\»0 âaM“æö]LŸþ¶ŸA™¿xñ+ÚSжíi}ñCé£Âßœ?A¤PÂüþÃM*U*un””ýúõ³ Æ[6^8°tÁ;I• €ó /*ÿþ÷Ú•ØsÊI6ÀŠ+d—.]dÁ‚l D+À{YÕ X(Œl€¾ŠÍ«(æWÌl¶˜H×ê×Z¡c6*ê;9dÈQõ {B6o~V÷ñqÁc{ˆÇÇš49§[ˆÐÄl?ûàâ÷µ×O@®ÉqM…1jö·›U€L™2é˜à‰'êV€]& ^ð¢Q Àu]i tß¿P¡«zAbÒýéü/`úôé²~ýúú¶eA¨(.^¼GöïLFFþ&óæ½®ßÖŒ‹f¿ððëzÿ|Jð7ªçW6–'a‰RÑ¢Ed`Y²6ˆ˜†@dLR ØZ±š0²,C [q ˜Èm7E6¢€¸Øb‚-2öƒL›vPgòc<mTR¥º£Eãkàçñë2„’‚¯ÉgžùMöèq\Θq@nØðüâ‹à8×àX¶l™™Ä褃˜`dü׫`e¼£ø†bÅFÂÈÈ-˜ @@@@±´c*Àµ€ kc`Ó¦Må¼yól[ž-TV­Ú%'MúN >÷Ü/:›bà‘GnjA€¼¼Ù£J€âÇø8bÍaðþÆ–½{ÿ(£¢j#3}|~Tk~ðhx+NW®ø’b-ax=²±@@@@Þ°‚°aîW·[؈קOÝ ˆmc çÒ2ôèW>gνDoñHì«_ÿY«Öy½Ì aBIÆññ6mÎÈ^½~ÒOçÎݯŸAPàÏ Æ³ÍŠ îÑ£‡þü C!žÏå%Å"æ² œM '*öSl¦XI1[a\ ¥ÍïmWýX„S³fMW+À»"€~ý¦MßèË|éÒ=úbG›£È'~/'Oþ^ÿGݺêÏ7rH‚í?¶˜à9sæÈçŸÞIL0&7N*n1—!`Žâëæùô¬ùµ}TDgðŒ¢   ˆ€1vìu³€‹-W®\²mÛ¶rÑ¢Eq¶âGÆÅn#£J`$ããøùäpé{W6nÜ(G%+W®¬…’ƒVÀ^¯*ΧUŠï)NU¤ØF±ºyF! [áÝ ÀØØp·[0FDDh÷ûúõëc ¸×hÅwíÚU(PÀÉTZŸÛœQ+Ì3 Ù06V¬ Œl€sŠ"€€€€ þi3¿×]kX1ÁuêÔ‘QQQÚGmœ1c†ÎM utXñS›VÀR C`¤0ö@ä4¦<5kV*…‹æš”Æ5kÖèHQ ‚pøFœ¬«S0æÍ›W¶oß^.^¼X·Râ¹å+Q@LððáÃe… œÆëÕ Xk¶Š˜Ù)† S–Àˆ $X2±téÒÇ©S§ê=Úxs  W2ñ¿¯—c‚Ë–-«§æÏŸŸ"Ï­„‚hܸqòÙgŸÕU€x>i¿˜­€Ø²pV½ªØP±¼bqw6E@riÒ¤‘%J” 6”-[¶LqlР,^¼¸>4(ÂUà{8¿y‰¸¾1" Q£F)òÜJ(ñùÀç%$$ÄÉçÒ;`½G†ÀiŠÃ;*>m¶y0úIC`Jp×B €2yJ#þ¿ðÿÏx Aø«£ø½›U€”~n%”xóÏ!ƒ|ðÁ|QÀfÇí"æT€• 0A±¯bsÅ*Šáæ™ÅV@J$AøYÌ7ÇË<‚žˆr>!Œl€DLCà,Å1Š]ë cYPNÁl€dìyþƒßä®ò/SAS„Ì‹b…pyWé^w׋èl€w§(6ïË‘—šU€à–:`å#âwÅÁ݇€ƒ÷°©±<»¤/cn”ŒI|nŽ›êy]´â6±{¬Y™¡ R2P"ÆTÀ"›óÇ:oö™­‚£æ™sNñ7ó¹ºÈó% Äçù¬â×fÀ:³¬* s…‘ ÐKûÊ Ã˜‘gWpã!S©å6¿`v †q'„‘êÕÏT}KÌo„O„13JÞͦj^k^ò± 7¬¨M”Ó: cá”t""…%bdÉçò8oòšg¶ W¬¬Xß|&F(Î4Ÿ¼n0Ÿ1ž5îža<*•žçÖóÅUöÔAD/ ¢!0à~óA…šƒÁFvŠC£„aYj*A2aÄço¡0b6‡™ÑSæ!H@Ük¸Ï¼4pyd5+aO*¾ ŒÙsÏ0†öÏž$å2ó%p¡)ÌP½ìæQàÊàô0¦7¿ ˆ~lªØ[‹!P @2Ì ³Éq¦)¦ðõQl!¢µLFMÜ‹/8w`*CÚRç0r6Pûé£Ìg‡gHÒž]xûǦÀQŠÝŸÑÛ3™•f €d.ó˜Ý|k ÃðÑS‹!†™å¹‘¤Ïa–Î`¢éeVW0&!ŒM[i)ˆ{ôÜyÀ¼Real-Time Appearance-Based Mapping Version @RTABMAP_VERSION@
Author: Mathieu Labbé
Copyright 2016-2020
IntRoLab - Université de Sherbrooke
http://introlab.github.io/rtabmap

rtabmap-0.22.1/app/android/jni/000077500000000000000000000000001503504370700162065ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/.gitignore000066400000000000000000000000251503504370700201730ustar00rootroot00000000000000CameraAvailability.h rtabmap-0.22.1/app/android/jni/CMakeLists.txt000066400000000000000000000111041503504370700207430ustar00rootroot00000000000000 SET(INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tango-gl/include ${CMAKE_CURRENT_SOURCE_DIR}/third-party/include ${PROJECT_BINARY_DIR}/corelib/include ${PROJECT_SOURCE_DIR}/corelib/include ${PROJECT_SOURCE_DIR}/utilite/include ${CMAKE_CURRENT_BINARY_DIR} ${OpenCV_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS} "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include" ) SET(LIBRARIES ${OpenCV_LIBRARIES} ${PCL_LIBRARIES} ) set(sources jni_interface.cpp CameraMobile.cpp RTABMapApp.cpp scene.cpp point_cloud_drawable.cpp graph_drawable.cpp background_renderer.cc text_drawable.cpp quad_color.cpp tango-gl/bounding_box.cpp tango-gl/axis.cpp tango-gl/camera.cpp tango-gl/circle.cpp tango-gl/conversions.cpp tango-gl/drawable_object.cpp tango-gl/frustum.cpp tango-gl/gesture_camera.cpp tango-gl/grid.cpp tango-gl/line.cpp tango-gl/mesh.cpp tango-gl/shaders.cpp tango-gl/trace.cpp tango-gl/transform.cpp tango-gl/util.cpp ) IF(OPENMP_FOUND) file(COPY ${OpenMP_CXX_LIBRARIES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) ENDIF(OPENMP_FOUND) IF(Tango_FOUND) SET(sources ${sources} CameraTango.cpp ) SET(INCLUDE_DIRS ${INCLUDE_DIRS} ${Tango_INCLUDE_DIRS} ) SET(LIBRARIES ${LIBRARIES} ${Tango_LIBRARIES} ) file(COPY ${Tango_support_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) ENDIF(Tango_FOUND) IF(ARCore_FOUND) SET(sources ${sources} CameraARCore.cpp ) SET(INCLUDE_DIRS ${INCLUDE_DIRS} ${ARCore_INCLUDE_DIRS} ) SET(LIBRARIES ${LIBRARIES} ${ARCore_LIBRARIES} ) file(COPY ${ARCore_c_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) file(COPY ${ARCore_jni_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) ENDIF(ARCore_FOUND) IF(AREngine_FOUND) SET(sources ${sources} CameraAREngine.cpp ) SET(INCLUDE_DIRS ${INCLUDE_DIRS} ${AREngine_INCLUDE_DIRS} ) SET(LIBRARIES ${LIBRARIES} ${AREngine_LIBRARIES} camera2ndk mediandk ) file(COPY ${AREngine_impl_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) file(COPY ${AREngine_jni_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) file(COPY ${AREngine_ndk_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}) ENDIF(AREngine_FOUND) add_definitions(${PCL_DEFINITIONS}) INCLUDE_DIRECTORIES(${INCLUDE_DIRS}) #################################### # Generate resources files #################################### SET(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources/text_atlas.png ) foreach(arg ${RESOURCES}) get_filename_component(filename ${arg} NAME) string(REPLACE "." "_" output ${filename}) set(RESOURCES_HEADERS "${RESOURCES_HEADERS}" "${CMAKE_CURRENT_BINARY_DIR}/${output}.h") endforeach(arg ${RESOURCES}) find_host_program(RTABMAP_RES_TOOL rtabmap-res_tool PATHS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) IF(NOT RTABMAP_RES_TOOL) MESSAGE( FATAL_ERROR "RTABMAP_RES_TOOL is not defined (it is the path to \"rtabmap-res_tool\" application created by a non-Android build)." ) ENDIF(NOT RTABMAP_RES_TOOL) ADD_CUSTOM_COMMAND( OUTPUT ${RESOURCES_HEADERS} COMMAND ${RTABMAP_RES_TOOL} -n rtabmap -p ${CMAKE_CURRENT_BINARY_DIR} ${RESOURCES} COMMENT "[Creating resources]" DEPENDS ${RESOURCES} ) #################################### # Generate resources files END #################################### add_library(NativeRTABMap SHARED ${sources} ${RESOURCES_HEADERS}) target_link_libraries(NativeRTABMap ${LIBRARIES} android log GLESv2 rtabmap_core rtabmap_utilite ) # see ant.properties.in set_target_properties(NativeRTABMap PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}" LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}" LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}") IF(ANDROID_NATIVE_API_LEVEL GREATER 22) add_custom_command(TARGET NativeRTABMap POST_BUILD COMMAND "${ANDROID_TOOLCHAIN_PREFIX}strip" -g -S -d --strip-debug --verbose "${CMAKE_CURRENT_BINARY_DIR}/../libs/${ANDROID_NDK_ABI_NAME}/libNativeRTABMap.so" COMMENT "Strip debug symbols done on final binary.") ENDIF(ANDROID_NATIVE_API_LEVEL GREATER 22) rtabmap-0.22.1/app/android/jni/CameraARCore.cpp000066400000000000000000000432001503504370700211350ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "CameraARCore.h" #include "util.h" #include "rtabmap/utilite/ULogger.h" #include "rtabmap/core/util3d_transforms.h" #include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/util2d.h" namespace rtabmap { ////////////////////////////// // CameraARCore ////////////////////////////// CameraARCore::CameraARCore(void* env, void* context, void* activity, bool depthFromMotion, float upstreamRelocalizationAccThr): CameraMobile(upstreamRelocalizationAccThr), env_(env), context_(context), activity_(activity), arInstallRequested_(false), depthFromMotion_(depthFromMotion) { } CameraARCore::~CameraARCore() { // Disconnect ARCore service close(); } struct CameraConfig { int32_t width = 0; int32_t height = 0; std::string config_label; ArCameraConfig* config = nullptr; }; void getCameraConfigLowestAndHighestResolutions( std::vector & camera_configs, CameraConfig** lowest_resolution_config, CameraConfig** highest_resolution_config) { if (camera_configs.empty()) { return; } int low_resolution_config_idx = 0; int high_resolution_config_idx = 0; int32_t smallest_height = camera_configs[0].height; int32_t largest_height = camera_configs[0].height; for (int i = 1; i < camera_configs.size(); ++i) { int32_t image_height = camera_configs[i].height; if (image_height < smallest_height) { smallest_height = image_height; low_resolution_config_idx = i; } else if (image_height > largest_height) { largest_height = image_height; high_resolution_config_idx = i; } } if (low_resolution_config_idx == high_resolution_config_idx) { *lowest_resolution_config = &camera_configs[low_resolution_config_idx]; } else { *lowest_resolution_config = &camera_configs[low_resolution_config_idx]; *highest_resolution_config = &camera_configs[high_resolution_config_idx]; } } void copyCameraConfig( const ArSession* ar_session, const ArCameraConfigList* all_configs, int index, int num_configs, CameraConfig* camera_config) { if (camera_config != nullptr && index >= 0 && index < num_configs) { ArCameraConfig_create(ar_session, &camera_config->config); ArCameraConfigList_getItem(ar_session, all_configs, index, camera_config->config); ArCameraConfig_getImageDimensions(ar_session, camera_config->config, &camera_config->width, &camera_config->height); camera_config->config_label = "(" + std::to_string(camera_config->width) + "x" + std::to_string(camera_config->height) + ")"; } } void destroyCameraConfigs(std::vector & camera_configs) { for (int i = 0; i < camera_configs.size(); ++i) { if (camera_configs[i].config != nullptr) { ArCameraConfig_destroy(camera_configs[i].config); } } } std::string CameraARCore::getSerial() const { return "ARCore"; } bool CameraARCore::init(const std::string & calibrationFolder, const std::string & cameraName) { close(); CameraMobile::init(calibrationFolder, cameraName); UScopeMutex lock(arSessionMutex_); ArInstallStatus install_status; // If install was not yet requested, that means that we are resuming the // activity first time because of explicit user interaction (such as // launching the application) bool user_requested_install = !arInstallRequested_; // === ATTENTION! ATTENTION! ATTENTION! === // This method can and will fail in user-facing situations. Your // application must handle these cases at least somewhat gracefully. See // HelloAR Java sample code for reasonable behavior. ArCoreApk_requestInstall(env_, activity_, user_requested_install, &install_status); switch (install_status) { case AR_INSTALL_STATUS_INSTALLED: break; case AR_INSTALL_STATUS_INSTALL_REQUESTED: arInstallRequested_ = true; return false; } // === ATTENTION! ATTENTION! ATTENTION! === // This method can and will fail in user-facing situations. Your // application must handle these cases at least somewhat gracefully. See // HelloAR Java sample code for reasonable behavior. UASSERT(ArSession_create(env_, context_, &arSession_) == AR_SUCCESS); UASSERT(arSession_); int32_t is_depth_supported = 0; ArSession_isDepthModeSupported(arSession_, AR_DEPTH_MODE_AUTOMATIC, &is_depth_supported); ArConfig_create(arSession_, &arConfig_); UASSERT(arConfig_); if (is_depth_supported!=0) { ArConfig_setDepthMode(arSession_, arConfig_, AR_DEPTH_MODE_AUTOMATIC); } else { ArConfig_setDepthMode(arSession_, arConfig_, AR_DEPTH_MODE_DISABLED); } ArConfig_setFocusMode(arSession_, arConfig_, AR_FOCUS_MODE_FIXED); UASSERT(ArSession_configure(arSession_, arConfig_) == AR_SUCCESS); ArFrame_create(arSession_, &arFrame_); UASSERT(arFrame_); ArCameraIntrinsics_create(arSession_, &arCameraIntrinsics_); UASSERT(arCameraIntrinsics_); ArPose_create(arSession_, nullptr, &arPose_); UASSERT(arPose_); ArCameraConfigList* all_camera_configs = nullptr; int32_t num_configs = 0; ArCameraConfigList_create(arSession_, &all_camera_configs); // Create filter first to get both 30 and 60 fps. ArCameraConfigFilter* camera_config_filter = nullptr; ArCameraConfigFilter_create(arSession_, &camera_config_filter); ArCameraConfigFilter_setTargetFps(arSession_, camera_config_filter, AR_CAMERA_CONFIG_TARGET_FPS_30 | AR_CAMERA_CONFIG_TARGET_FPS_60); ArSession_getSupportedCameraConfigsWithFilter(arSession_, camera_config_filter, all_camera_configs); ArCameraConfigList_getSize(arSession_, all_camera_configs, &num_configs); if (num_configs < 1) { UERROR("No camera config found"); close(); return false; } std::vector camera_configs; CameraConfig* cpu_low_resolution_camera_config_ptr = nullptr; CameraConfig* cpu_high_resolution_camera_config_ptr = nullptr; camera_configs.resize(num_configs); for (int i = 0; i < num_configs; ++i) { copyCameraConfig(arSession_, all_camera_configs, i, num_configs, &camera_configs[i]); } // Determine the highest and lowest CPU resolutions. cpu_low_resolution_camera_config_ptr = nullptr; cpu_high_resolution_camera_config_ptr = nullptr; getCameraConfigLowestAndHighestResolutions( camera_configs, &cpu_low_resolution_camera_config_ptr, &cpu_high_resolution_camera_config_ptr); // Cleanup the list obtained as it is safe to destroy the list as camera // config instances were explicitly created and copied. Refer to the // previous comment. ArCameraConfigList_destroy(all_camera_configs); ArSession_setCameraConfig(arSession_, cpu_low_resolution_camera_config_ptr->config); /// Sets the behavior of @ref ArSession_update(). See /// ::ArUpdateMode for available options. ArConfig_setUpdateMode(arSession_, arConfig_, AR_UPDATE_MODE_BLOCKING); deviceTColorCamera_ = opticalRotation; if (ArSession_resume(arSession_) != ArStatus::AR_SUCCESS) { UERROR("Cannot resume camera!"); // In a rare case (such as another camera app launching) the camera may be // given to a different app and so may not be available to this app. Handle // this properly and recreate the session at the next iteration. close(); return false; } return true; } void CameraARCore::close() { UScopeMutex lock(arSessionMutex_); if(arSession_!= nullptr) { ArSession_destroy(arSession_); } arSession_ = nullptr; if(arConfig_!= nullptr) { ArConfig_destroy(arConfig_); } arConfig_ = nullptr; if (arFrame_ != nullptr) { ArFrame_destroy(arFrame_); } arFrame_ = nullptr; if (arCameraIntrinsics_ != nullptr) { ArCameraIntrinsics_destroy(arCameraIntrinsics_); } arCameraIntrinsics_ = nullptr; if (arPose_ != nullptr) { ArPose_destroy(arPose_); } arPose_ = nullptr; CameraMobile::close(); } void CameraARCore::setScreenRotationAndSize(ScreenRotation colorCameraToDisplayRotation, int width, int height) { CameraMobile::setScreenRotationAndSize(colorCameraToDisplayRotation, width, height); if(arSession_) { int ret = static_cast(colorCameraToDisplayRotation) + 1; // remove 90deg camera rotation if (ret > 3) { ret -= 4; } ArSession_setDisplayGeometry(arSession_, ret, width, height); } } SensorData CameraARCore::updateDataOnRender(Transform & pose) { UScopeMutex lock(arSessionMutex_); //LOGI("Capturing image..."); pose.setNull(); SensorData data; if(!arSession_) { return data; } if(textureId_ == 0) { glGenTextures(1, &textureId_); glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId_); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } if(textureId_!=0) ArSession_setCameraTextureName(arSession_, textureId_); // Update session to get current frame and render camera background. if (ArSession_update(arSession_, arFrame_) != AR_SUCCESS) { LOGE("CameraARCore::captureImage() ArSession_update error"); return data; } // If display rotation changed (also includes view size change), we need to // re-query the uv coordinates for the on-screen portion of the camera image. int32_t geometry_changed = 0; ArFrame_getDisplayGeometryChanged(arSession_, arFrame_, &geometry_changed); if (geometry_changed != 0 || !uvs_initialized_) { ArFrame_transformCoordinates2d( arSession_, arFrame_, AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES, BackgroundRenderer::kNumVertices, BackgroundRenderer_kVerticesDevice, AR_COORDINATES_2D_TEXTURE_NORMALIZED, transformed_uvs_); UASSERT(transformed_uvs_); uvs_initialized_ = true; } ArCamera* ar_camera; ArFrame_acquireCamera(arSession_, arFrame_, &ar_camera); ArCamera_getViewMatrix(arSession_, ar_camera, glm::value_ptr(viewMatrix_)); ArCamera_getProjectionMatrix(arSession_, ar_camera, /*near=*/0.1f, /*far=*/100.f, glm::value_ptr(projectionMatrix_)); ArTrackingState camera_tracking_state; ArCamera_getTrackingState(arSession_, ar_camera, &camera_tracking_state); CameraModel model; if(camera_tracking_state == AR_TRACKING_STATE_TRACKING) { // pose in OpenGL coordinates float pose_raw[7]; ArCamera_getPose(arSession_, ar_camera, arPose_); ArPose_getPoseRaw(arSession_, arPose_, pose_raw); Transform poseArCore = Transform(pose_raw[4], pose_raw[5], pose_raw[6], pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]); pose = rtabmap::rtabmap_world_T_opengl_world * poseArCore * rtabmap::opengl_world_T_rtabmap_world; if(pose.isNull()) { LOGE("CameraARCore: Pose is null"); return data; } // Get calibration parameters float fx,fy, cx, cy; int32_t width, height; ArCamera_getImageIntrinsics(arSession_, ar_camera, arCameraIntrinsics_); ArCameraIntrinsics_getFocalLength(arSession_, arCameraIntrinsics_, &fx, &fy); ArCameraIntrinsics_getPrincipalPoint(arSession_, arCameraIntrinsics_, &cx, &cy); ArCameraIntrinsics_getImageDimensions(arSession_, arCameraIntrinsics_, &width, &height); #ifndef DISABLE_LOG LOGI("%f %f %f %f %d %d", fx, fy, cx, cy, width, height); #endif if(fx > 0 && fy > 0 && width > 0 && height > 0 && cx > 0 && cy > 0) { model = CameraModel(fx, fy, cx, cy, deviceTColorCamera_, 0, cv::Size(width, height)); ArPointCloud * pointCloud = nullptr; ArFrame_acquirePointCloud(arSession_, arFrame_, &pointCloud); int32_t is_depth_supported = 0; ArSession_isDepthModeSupported(arSession_, AR_DEPTH_MODE_AUTOMATIC, &is_depth_supported); ArImage * image = nullptr; ArStatus status = ArFrame_acquireCameraImage(arSession_, arFrame_, &image); if(status == AR_SUCCESS) { if(is_depth_supported) { LOGD("Acquire depth image!"); ArImage * depthImage = nullptr; ArFrame_acquireDepthImage(arSession_, arFrame_, &depthImage); ArImageFormat format; ArImage_getFormat(arSession_, depthImage, &format); if(format == AR_IMAGE_FORMAT_DEPTH16) { LOGD("Depth format detected!"); int planeCount; ArImage_getNumberOfPlanes(arSession_, depthImage, &planeCount); LOGD("planeCount=%d", planeCount); UASSERT_MSG(planeCount == 1, uFormat("Error: getNumberOfPlanes() planceCount = %d", planeCount).c_str()); const uint8_t *data = nullptr; int len = 0; int stride; int depth_width; int depth_height; ArImage_getWidth(arSession_, depthImage, &depth_width); ArImage_getHeight(arSession_, depthImage, &depth_height); ArImage_getPlaneRowStride(arSession_, depthImage, 0, &stride); ArImage_getPlaneData(arSession_, depthImage, 0, &data, &len); LOGD("width=%d, height=%d, bytes=%d stride=%d", depth_width, depth_height, len, stride); cv::Mat occlusionImage = cv::Mat(depth_height, depth_width, CV_16UC1, (void*)data).clone(); float scaleX = (float)depth_width / (float)width; float scaleY = (float)depth_height / (float)height; CameraModel occlusionModel(fx*scaleX, fy*scaleY, cx*scaleX, cy*scaleY, pose*deviceTColorCamera_, 0, cv::Size(depth_width, depth_height)); this->setOcclusionImage(occlusionImage, occlusionModel); } ArImage_release(depthImage); } int64_t timestamp_ns; ArImageFormat format; ArImage_getTimestamp(arSession_, image, ×tamp_ns); ArImage_getFormat(arSession_, image, &format); if(format == AR_IMAGE_FORMAT_YUV_420_888) { #ifndef DISABLE_LOG int32_t num_planes; ArImage_getNumberOfPlanes(arSession_, image, &num_planes); for(int i=0;i kpts; std::vector kpts3; LaserScan scan; if(pointCloud) { int32_t points = 0; ArPointCloud_getNumberOfPoints(arSession_, pointCloud, &points); const float * pointCloudData = 0; ArPointCloud_getData(arSession_, pointCloud, &pointCloudData); #ifndef DISABLE_LOG LOGI("pointCloudData=%d size=%d", pointCloudData?1:0, points); #endif if(pointCloudData && points>0) { cv::Mat pointCloudDataMat(1, points, CV_32FC4, (void *)pointCloudData); scan = scanFromPointCloudData(pointCloudDataMat, pose, model, rgb, &kpts, &kpts3); #ifndef DISABLE_LOG LOGI("valid scan points = %d", scan.size()); #endif } } else { LOGI("pointCloud empty"); } data = SensorData(scan, rgb, depthFromMotion_?getOcclusionImage():cv::Mat(), model, 0, stamp); data.setFeatures(kpts, kpts3, cv::Mat()); if(!pose.isNull()) { this->poseReceived(pose, stamp); } } } else { LOGE("CameraARCore: cannot convert image format %d", format); } } else { LOGE("CameraARCore: failed to get rgb image (status=%d)", (int)status); } ArImage_release(image); ArPointCloud_release(pointCloud); } } ArCamera_release(ar_camera); return data; } } /* namespace rtabmap */ rtabmap-0.22.1/app/android/jni/CameraARCore.h000066400000000000000000000063071503504370700206110ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CAMERAARCORE_H_ #define CAMERAARCORE_H_ #include "CameraMobile.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace rtabmap { class CameraARCore : public CameraMobile { public: CameraARCore(void* env, void* context, void* activity, bool depthFromMotion = false, float upstreamRelocalizationAccThr = 0.0f); virtual ~CameraARCore(); virtual void setScreenRotationAndSize(ScreenRotation colorCameraToDisplayRotation, int width, int height); virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = ""); virtual void close(); // close ARCore connection virtual std::string getSerial() const; protected: virtual SensorData updateDataOnRender(Transform & pose); // should be called in opengl thread private: rtabmap::Transform getPoseAtTimestamp(double timestamp); private: void * env_; void * context_; void * activity_; ArSession* arSession_ = nullptr; ArConfig* arConfig_ = nullptr; ArFrame* arFrame_ = nullptr; ArCameraIntrinsics *arCameraIntrinsics_ = nullptr; ArPose * arPose_ = nullptr; bool arInstallRequested_; UMutex arSessionMutex_; bool depthFromMotion_; }; } /* namespace rtabmap */ #endif /* CAMERAARCORE_H_ */ rtabmap-0.22.1/app/android/jni/CameraAREngine.cpp000066400000000000000000000272241503504370700214620ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "CameraAREngine.h" #include "util.h" #include "rtabmap/utilite/ULogger.h" #include "rtabmap/core/util3d_transforms.h" #include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/util2d.h" #include namespace rtabmap { ////////////////////////////// // CameraAREngine ////////////////////////////// CameraAREngine::CameraAREngine(void* env, void* context, void* activity, float upstreamRelocalizationAccThr): CameraMobile(upstreamRelocalizationAccThr), env_(env), context_(context), activity_(activity), arInstallRequested_(false) { glGenTextures(1, &textureId_); } CameraAREngine::~CameraAREngine() { // Disconnect ARCore service close(); glDeleteTextures(1, &textureId_); } std::string CameraAREngine::getSerial() const { return "AREngine"; } bool CameraAREngine::init(const std::string & calibrationFolder, const std::string & cameraName) { close(); CameraMobile::init(calibrationFolder, cameraName); UScopeMutex lock(arSessionMutex_); HwArInstallStatus install_status; // If install was not yet requested, that means that we are resuming the // activity first time because of explicit user interaction (such as // launching the application) bool user_requested_install = !arInstallRequested_; // === ATTENTION! ATTENTION! ATTENTION! === // This method can and will fail in user-facing situations. Your // application must handle these cases at least somewhat gracefully. See // HelloAR Java sample code for reasonable behavior. HwArEnginesApk_requestInstall(env_, activity_, user_requested_install, &install_status); switch (install_status) { case HWAR_INSTALL_STATUS_INSTALLED: break; case HWAR_INSTALL_STATUS_INSTALL_REQUESTED: arInstallRequested_ = true; return false; } // === ATTENTION! ATTENTION! ATTENTION! === // This method can and will fail in user-facing situations. Your // application must handle these cases at least somewhat gracefully. See // HelloAR Java sample code for reasonable behavior. UASSERT(HwArSession_create(env_, context_, &arSession_) == HWAR_SUCCESS); UASSERT(arSession_); HwArConfig_create(arSession_, &arConfig_); UASSERT(arConfig_); HwArConfig_setFocusMode(arSession_, arConfig_, HWAR_FOCUS_MODE_FIXED); UASSERT(HwArSession_configure(arSession_, arConfig_) == HWAR_SUCCESS); HwArFrame_create(arSession_, &arFrame_); UASSERT(arFrame_); HwArCameraIntrinsics_create(arSession_, &arCameraIntrinsics_); // May fail?! //UASSERT(arCameraIntrinsics_); HwArPose_create(arSession_, nullptr, &arPose_); UASSERT(arPose_); /// Sets the behavior of @ref ArSession_update(). See /// ::ArUpdateMode for available options. HwArConfig_setUpdateMode(arSession_, arConfig_, HWAR_UPDATE_MODE_BLOCKING); deviceTColorCamera_ = opticalRotation; if (HwArSession_resume(arSession_) != HWAR_SUCCESS) { UERROR("Cannot resume camera!"); // In a rare case (such as another camera app launching) the camera may be // given to a different app and so may not be available to this app. Handle // this properly and recreate the session at the next iteration. close(); return false; } return true; } void CameraAREngine::close() { UScopeMutex lock(arSessionMutex_); if (arCameraIntrinsics_ != nullptr) { HwArCameraIntrinsics_destroy(arSession_, arCameraIntrinsics_); } arCameraIntrinsics_ = nullptr; if(arSession_!= nullptr) { HwArSession_destroy(arSession_); } arSession_ = nullptr; if(arConfig_!= nullptr) { HwArConfig_destroy(arConfig_); } arConfig_ = nullptr; if (arFrame_ != nullptr) { HwArFrame_destroy(arFrame_); } arFrame_ = nullptr; if (arPose_ != nullptr) { HwArPose_destroy(arPose_); } arPose_ = nullptr; CameraMobile::close(); } void CameraAREngine::setScreenRotationAndSize(ScreenRotation colorCameraToDisplayRotation, int width, int height) { CameraMobile::setScreenRotationAndSize(colorCameraToDisplayRotation, width, height); if(arSession_) { int ret = static_cast(colorCameraToDisplayRotation) + 1; // remove 90deg camera rotation if (ret > 3) { ret -= 4; } HwArSession_setDisplayGeometry(arSession_, ret, width, height); } } SensorData CameraAREngine::updateDataOnRender(Transform & pose) { UScopeMutex lock(arSessionMutex_); //LOGI("Capturing image..."); pose.setNull(); SensorData data; if(!arSession_) { return data; } if(textureId_ == 0) { glGenTextures(1, &textureId_); glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId_); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } if(textureId_!=0) HwArSession_setCameraTextureName(arSession_, textureId_); // Update session to get current frame and render camera background. if (HwArSession_update(arSession_, arFrame_) != HWAR_SUCCESS) { LOGE("CameraAREngine::captureImage() ArSession_update error"); return data; } // If display rotation changed (also includes view size change), we need to // re-query the uv coordinates for the on-screen portion of the camera image. int32_t geometry_changed = 0; HwArFrame_getDisplayGeometryChanged(arSession_, arFrame_, &geometry_changed); if (geometry_changed != 0 || !uvs_initialized_) { HwArFrame_transformDisplayUvCoords( arSession_, arFrame_, BackgroundRenderer::kNumVertices*2, BackgroundRenderer_kVerticesView, transformed_uvs_); UERROR("uv: (%f,%f) (%f,%f) (%f,%f) (%f,%f)", transformed_uvs_[0], transformed_uvs_[1], transformed_uvs_[2], transformed_uvs_[3], transformed_uvs_[4], transformed_uvs_[5], transformed_uvs_[6], transformed_uvs_[7]); UASSERT(transformed_uvs_); uvs_initialized_ = true; } HwArCamera* ar_camera; HwArFrame_acquireCamera(arSession_, arFrame_, &ar_camera); HwArCamera_getViewMatrix(arSession_, ar_camera, glm::value_ptr(viewMatrix_)); HwArCamera_getProjectionMatrix(arSession_, ar_camera, /*near=*/0.1f, /*far=*/100.f, glm::value_ptr(projectionMatrix_)); HwArTrackingState camera_tracking_state; HwArCamera_getTrackingState(arSession_, ar_camera, &camera_tracking_state); if(camera_tracking_state == HWAR_TRACKING_STATE_TRACKING) { // Get calibration parameters // FIXME: Hard-coded as getting intrinsics with the api fails float fx=492.689667,fy=492.606201, cx=323.594849, cy=234.659744; int32_t camWidth=640, camHeight=480; //HwArCamera_getImageIntrinsics(arSession_, ar_camera, arCameraIntrinsics_); //HwArCameraIntrinsics_getFocalLength(arSession_, arCameraIntrinsics_, &fx, &fy); //HwArCameraIntrinsics_getPrincipalPoint(arSession_, arCameraIntrinsics_, &cx, &cy); //HwArCameraIntrinsics_getImageDimensions(arSession_, arCameraIntrinsics_, &camWidth, &camHeight); LOGI("%f %f %f %f %d %d", fx, fy, cx, cy, camWidth, camHeight); if(fx > 0 && fy > 0 && camWidth > 0 && camHeight > 0 && cx > 0 && cy > 0) { //ArPointCloud * point_cloud; //ArFrame_acquirePointCloud(ar_session_, ar_frame_, &point_cloud); HwArImage * image = nullptr; HwArImage * depthImage = nullptr; HwArStatus statusRgb = HwArFrame_acquireCameraImage(arSession_, arFrame_, &image); HwArStatus statusDepth = HwArFrame_acquireDepthImage(arSession_, arFrame_, &depthImage); if(statusRgb == HWAR_SUCCESS && statusDepth == HWAR_SUCCESS) { int64_t timestamp_ns; HwArFrame_getTimestamp(arSession_, arFrame_, ×tamp_ns); int planeCount; uint8_t *imageData = nullptr; int len = 0; int stride; int width; int height; const AImage* ndkImageRGB; HwArImage_getNdkImage(image, &ndkImageRGB); AImage_getNumberOfPlanes(ndkImageRGB, &planeCount); AImage_getWidth(ndkImageRGB, &width); AImage_getHeight(ndkImageRGB, &height); AImage_getPlaneRowStride(ndkImageRGB, 0, &stride); AImage_getPlaneData(ndkImageRGB, 0, &imageData, &len); LOGI("RGB: width=%d, height=%d, bytes=%d stride=%d planeCount=%d", width, height, len, stride, planeCount); cv::Mat outputRGB; if(imageData != nullptr && len>0) { cv::cvtColor(cv::Mat(height+height/2, width, CV_8UC1, (void*)imageData), outputRGB, cv::COLOR_YUV2BGR_NV21); } //Depth const AImage* ndkImageDepth; HwArImage_getNdkImage(depthImage, &ndkImageDepth); AImage_getNumberOfPlanes(ndkImageDepth, &planeCount); AImage_getWidth(ndkImageDepth, &width); AImage_getHeight(ndkImageDepth, &height); AImage_getPlaneRowStride(ndkImageDepth, 0, &stride); AImage_getPlaneData(ndkImageDepth, 0, &imageData, &len); LOGI("Depth: width=%d, height=%d, bytes=%d stride=%d planeCount=%d", width, height, len, stride, planeCount); cv::Mat outputDepth(height, width, CV_16UC1); uint16_t *dataShort = (uint16_t *)imageData; for (int y = 0; y < outputDepth.rows; ++y) { for (int x = 0; x < outputDepth.cols; ++x) { uint16_t depthSample = dataShort[y*outputDepth.cols + x]; uint16_t depthRange = (depthSample & 0x1FFF); // first 3 bits are confidence outputDepth.at(y,x) = depthRange; } } if(!outputRGB.empty() && !outputDepth.empty()) { double stamp = double(timestamp_ns)/10e8; CameraModel model = CameraModel(fx, fy, cx, cy, deviceTColorCamera_, 0, cv::Size(camWidth, camHeight)); data = SensorData(outputRGB, outputDepth, model, 0, stamp); // pose in OpenGL coordinates float pose_raw[7]; HwArCamera_getPose(arSession_, ar_camera, arPose_); HwArPose_getPoseRaw(arSession_, arPose_, pose_raw); pose = Transform(pose_raw[4], pose_raw[5], pose_raw[6], pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]); if(pose.isNull()) { LOGE("CameraAREngine: Pose is null"); } else { pose = rtabmap::rtabmap_world_T_opengl_world * pose * rtabmap::opengl_world_T_rtabmap_world; this->poseReceived(pose, stamp); } } } else { LOGE("CameraAREngine: failed to get rgb image (status=%d %d)", (int)statusRgb, (int)statusDepth); } HwArImage_release(image); HwArImage_release(depthImage); } else { LOGE("Invalid intrinsics!"); } } HwArCamera_release(ar_camera); return data; } } /* namespace rtabmap */ rtabmap-0.22.1/app/android/jni/CameraAREngine.h000066400000000000000000000060031503504370700211170ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CAMERAARENGINE_H_ #define CAMERAARENGINE_H_ #include "CameraMobile.h" #include #include #include #include #include #include #include #include #include #include #include namespace rtabmap { class CameraAREngine : public CameraMobile { public: CameraAREngine(void* env, void* context, void* activity, float upstreamRelocalizationAccThr = 0.0f); virtual ~CameraAREngine(); virtual void setScreenRotationAndSize(ScreenRotation colorCameraToDisplayRotation, int width, int height); virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = ""); virtual void close(); // close AREngine connection virtual std::string getSerial() const; protected: virtual SensorData updateDataOnRender(Transform & pose); private: rtabmap::Transform getPoseAtTimestamp(double timestamp); private: void * env_; void * context_; void * activity_; HwArSession* arSession_ = nullptr; HwArConfig* arConfig_ = nullptr; HwArFrame* arFrame_ = nullptr; HwArCameraIntrinsics *arCameraIntrinsics_ = nullptr; HwArPose * arPose_ = nullptr; bool arInstallRequested_; UMutex arSessionMutex_; }; } /* namespace rtabmap */ #endif /* CAMERAARENGINE_H_ */ rtabmap-0.22.1/app/android/jni/CameraMobile.cpp000066400000000000000000000537101503504370700212400ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "CameraMobile.h" #include "util.h" #include "rtabmap/utilite/ULogger.h" #include "rtabmap/core/util3d_transforms.h" #include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/util2d.h" #include namespace rtabmap { #define nullptr 0 ////////////////////////////// // CameraMobile ////////////////////////////// const rtabmap::Transform CameraMobile::opticalRotation = Transform( 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); const rtabmap::Transform CameraMobile::opticalRotationInv = Transform( 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); CameraMobile::CameraMobile(float upstreamRelocalizationAccThr) : Camera(10), deviceTColorCamera_(Transform::getIdentity()), textureId_(0), uvs_initialized_(false), stampEpochOffset_(0.0), colorCameraToDisplayRotation_(ROTATION_0), originUpdate_(true), upstreamRelocalizationAccThr_(upstreamRelocalizationAccThr), previousAnchorStamp_(0.0), dataGoodTracking_(true) { } CameraMobile::~CameraMobile() { // Disconnect camera service close(); } bool CameraMobile::init(const std::string &, const std::string &) { deviceTColorCamera_ = opticalRotation; // clear semaphore if(dataReady_.value() > 0) { dataReady_.acquire(dataReady_.value()); } return true; } void CameraMobile::close() { UScopeMutex lock(dataMutex_); firstFrame_ = true; lastKnownGPS_ = GPS(); lastEnvSensors_.clear(); originOffset_ = Transform(); originUpdate_ = true; dataPose_ = Transform(); data_ = SensorData(); dataGoodTracking_ = true; previousAnchorPose_.setNull(); previousAnchorLinearVelocity_.clear(); previousAnchorStamp_ = 0.0; if(textureId_ != 0) { glDeleteTextures(1, &textureId_); textureId_ = 0; } // in case someone is waiting on captureImage() dataReady_.release(); } void CameraMobile::resetOrigin(const rtabmap::Transform & offset) { manualOriginOffset_ = offset; originUpdate_ = true; } bool CameraMobile::getPose(double epochStamp, Transform & pose, cv::Mat & covariance, double maxWaitTime) { pose.setNull(); int maxWaitTimeMs = maxWaitTime * 1000; // Interpolate pose if(!poseBuffer_.empty()) { poseMutex_.lock(); int waitTry = 0; while(maxWaitTimeMs>0 && poseBuffer_.rbegin()->first < epochStamp && waitTry < maxWaitTimeMs) { poseMutex_.unlock(); ++waitTry; uSleep(1); poseMutex_.lock(); } if(poseBuffer_.rbegin()->first < epochStamp) { if(maxWaitTimeMs > 0) { UWARN("Could not find poses to interpolate at time %f after waiting %d ms (latest is %f)...", epochStamp, maxWaitTimeMs, poseBuffer_.rbegin()->first); } else { UWARN("Could not find poses to interpolate at time %f (latest is %f)...", epochStamp, poseBuffer_.rbegin()->first); } } else { std::map::const_iterator iterB = poseBuffer_.lower_bound(epochStamp); std::map::const_iterator iterA = iterB; if(iterA != poseBuffer_.begin()) { iterA = --iterA; } if(iterB == poseBuffer_.end()) { iterB = --iterB; } if(iterA == iterB && epochStamp == iterA->first) { pose = iterA->second; } else if(epochStamp >= iterA->first && epochStamp <= iterB->first) { pose = iterA->second.interpolate((epochStamp-iterA->first) / (iterB->first-iterA->first), iterB->second); } else // stamp < iterA->first { UWARN("Could not find pose data to interpolate at time %f (earliest is %f). Are sensors synchronized?", epochStamp, iterA->first); } } poseMutex_.unlock(); } return !pose.isNull(); } void CameraMobile::poseReceived(const Transform & pose, double deviceStamp) { // Pose reveived is the pose of the device in rtabmap coordinate if(!pose.isNull()) { Transform p = pose; if(stampEpochOffset_ == 0.0) { stampEpochOffset_ = UTimer::now() - deviceStamp; } if(originUpdate_) { firstFrame_ = true; lastKnownGPS_ = GPS(); lastEnvSensors_.clear(); dataGoodTracking_ = true; previousAnchorPose_.setNull(); previousAnchorLinearVelocity_.clear(); previousAnchorStamp_ = 0.0; originOffset_ = manualOriginOffset_.isNull() ? pose.translation().inverse() : manualOriginOffset_; originUpdate_ = false; } double epochStamp = stampEpochOffset_ + deviceStamp; if(!originOffset_.isNull()) { // Filter re-localizations from poses received rtabmap::Transform rawPose = originOffset_ * pose.translation(); // remove rotation to keep position in fixed frame // Remove upstream localization corrections by integrating pose from previous frame anchor bool showLog = false; if(upstreamRelocalizationAccThr_>0.0f && !previousAnchorPose_.isNull()) { float dt = epochStamp - previousAnchorStamp_; std::vector currentLinearVelocity(3); float dx = rawPose.x()-previousAnchorPose_.x(); float dy = rawPose.y()-previousAnchorPose_.y(); float dz = rawPose.z()-previousAnchorPose_.z(); currentLinearVelocity[0] = dx / dt; currentLinearVelocity[1] = dy / dt; currentLinearVelocity[2] = dz / dt; if(!previousAnchorLinearVelocity_.empty() && uNorm(dx, dy, dz)>0.02) { float ax = (currentLinearVelocity[0] - previousAnchorLinearVelocity_[0]) / dt; float ay = (currentLinearVelocity[1] - previousAnchorLinearVelocity_[1]) / dt; float az = (currentLinearVelocity[2] - previousAnchorLinearVelocity_[2]) / dt; float acceleration = sqrt(ax*ax + ay*ay + az*az); if(acceleration>=upstreamRelocalizationAccThr_) { // Only correct the translation to not lose rotation aligned // with gravity. // Use constant motion model to update current pose. rtabmap::Transform offset(previousAnchorLinearVelocity_[0] * dt, previousAnchorLinearVelocity_[1] * dt, previousAnchorLinearVelocity_[2] * dt, 0, 0, 0, 1); rtabmap::Transform newRawPose = offset * previousAnchorPose_; currentLinearVelocity = previousAnchorLinearVelocity_; originOffset_.x() += newRawPose.x() - rawPose.x(); originOffset_.y() += newRawPose.y() - rawPose.y(); originOffset_.z() += newRawPose.z() - rawPose.z(); UERROR("Upstream re-localization has been suppressed because of " "high acceleration detected (%f m/s^2) causing a jump!", acceleration); dataGoodTracking_ = false; post(new CameraInfoEvent(0, "UpstreamRelocationFiltered", uFormat("%.1f m/s^2", acceleration).c_str())); showLog = true; } } previousAnchorLinearVelocity_ = currentLinearVelocity; } p = originOffset_*pose; previousAnchorPose_ = p; previousAnchorStamp_ = epochStamp; if(upstreamRelocalizationAccThr_>0.0f) { relocalizationDebugBuffer_.insert(std::make_pair(epochStamp, std::make_pair(pose, p))); if(relocalizationDebugBuffer_.size() > 60) { relocalizationDebugBuffer_.erase(relocalizationDebugBuffer_.begin()); } if(showLog) { std::stringstream stream; for(auto iter=relocalizationDebugBuffer_.begin(); iter!=relocalizationDebugBuffer_.end(); ++iter) { stream << iter->first - relocalizationDebugBuffer_.begin()->first << " " << iter->second.first.x() << " " << iter->second.first.y() << " " << iter->second.first.z() << " " << iter->second.second.x() << " " << iter->second.second.y() << " " << iter->second.second.z() << std::endl; } UERROR("timestamp original_xyz corrected_xyz:\n%s", stream.str().c_str()); } } } { UScopeMutex lock(poseMutex_); poseBuffer_.insert(poseBuffer_.end(), std::make_pair(epochStamp, p)); if(poseBuffer_.size() > 1000) { poseBuffer_.erase(poseBuffer_.begin()); } } // send pose of the camera (with optical rotation) this->post(new PoseEvent(p * deviceTColorCamera_)); } } bool CameraMobile::isCalibrated() const { return model_.isValidForProjection(); } void CameraMobile::setGPS(const GPS & gps) { lastKnownGPS_ = gps; } void CameraMobile::addEnvSensor(int type, float value) { lastEnvSensors_.insert(std::make_pair((EnvSensor::Type)type, EnvSensor((EnvSensor::Type)type, value))); } void CameraMobile::update(const SensorData & data, const Transform & pose, const glm::mat4 & viewMatrix, const glm::mat4 & projectionMatrix, const float * texCoord) { UScopeMutex lock(dataMutex_); LOGD("CameraMobile::update pose=%s stamp=%f", pose.prettyPrint().c_str(), data.stamp()); bool notify = !data_.isValid(); data_ = data; dataPose_ = pose; viewMatrix_ = viewMatrix; projectionMatrix_ = projectionMatrix; if(textureId_ == 0) { glGenTextures(1, &textureId_); } if(texCoord) { memcpy(transformed_uvs_, texCoord, 8*sizeof(float)); uvs_initialized_ = true; } LOGD("CameraMobile::update textureId_=%d", (int)textureId_); if(textureId_ != 0 && texCoord != 0) { cv::Mat rgbImage; cv::cvtColor(data.imageRaw(), rgbImage, cv::COLOR_BGR2RGBA); glBindTexture(GL_TEXTURE_2D, textureId_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); //glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); //glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rgbImage.cols, rgbImage.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbImage.data); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGE("OpenGL: Could not allocate texture (0x%x)\n", error); textureId_ = 0; return; } } if(data_.isValid()) { postUpdate(); if(notify) { dataReady_.release(); } } } void CameraMobile::updateOnRender() { UScopeMutex lock(dataMutex_); bool notify = !data_.isValid(); data_ = updateDataOnRender(dataPose_); if(data_.isValid()) { postUpdate(); if(notify) { dataReady_.release(); } } } SensorData CameraMobile::updateDataOnRender(Transform & pose) { LOGE("To use CameraMobile::updateOnRender(), CameraMobile::updateDataOnRender() " "should be overridden by inherited classes. Returning empty data!\n"); return SensorData(); } void CameraMobile::postUpdate() { if(data_.isValid()) { // adjust origin if(!originOffset_.isNull()) { dataPose_ = originOffset_ * dataPose_; viewMatrix_ = glm::inverse(rtabmap::glmFromTransform(rtabmap::opengl_world_T_rtabmap_world * originOffset_ *rtabmap::rtabmap_world_T_opengl_world)*glm::inverse(viewMatrix_)); occlusionModel_.setLocalTransform(originOffset_ * occlusionModel_.localTransform()); } if(lastKnownGPS_.stamp() > 0.0 && data_.stamp()-lastKnownGPS_.stamp()<1.0) { data_.setGPS(lastKnownGPS_); } else if(lastKnownGPS_.stamp()>0.0) { LOGD("GPS too old (current time=%f, gps time = %f)", data_.stamp(), lastKnownGPS_.stamp()); } if(lastEnvSensors_.size()) { data_.setEnvSensors(lastEnvSensors_); lastEnvSensors_.clear(); } // Rotate image depending on the camera orientation if(colorCameraToDisplayRotation_ == ROTATION_90) { UDEBUG("ROTATION_90"); cv::Mat rgb, depth, confidence; cv::Mat rgbt; cv::flip(data_.imageRaw(),rgb,1); cv::transpose(rgb,rgbt); rgb = rgbt; cv::Mat deptht; cv::flip(data_.depthRaw(),depth,1); cv::transpose(depth,deptht); depth = deptht; if(!data_.depthConfidenceRaw().empty()) { cv::Mat conft; cv::flip(data_.depthConfidenceRaw(),confidence,1); cv::transpose(confidence,conft); confidence = conft; } CameraModel model = data_.cameraModels()[0]; cv::Size sizet(model.imageHeight(), model.imageWidth()); model = CameraModel( model.fy(), model.fx(), model.cy(), model.cx()>0?model.imageWidth()-model.cx():0, model.localTransform()*rtabmap::Transform(0,-1,0,0, 1,0,0,0, 0,0,1,0)); model.setImageSize(sizet); data_.setRGBDImage(rgb, depth, confidence, model); std::vector keypoints = data_.keypoints(); for(size_t i=0; i0?model.imageWidth()-model.cx():0, model.cy()>0?model.imageHeight()-model.cy():0, model.localTransform()*rtabmap::Transform(0,0,0,0,0,1,0)); model.setImageSize(sizet); data_.setRGBDImage(rgb, depth, confidence, model); std::vector keypoints = data_.keypoints(); for(size_t i=0; i0?model.imageHeight()-model.cy():0, model.cx(), model.localTransform()*rtabmap::Transform(0,1,0,0, -1,0,0,0, 0,0,1,0)); model.setImageSize(sizet); data_.setRGBDImage(rgb, depth, confidence, model); std::vector keypoints = data_.keypoints(); for(size_t i=0; iodomCovariance = cv::Mat::eye(6,6,CV_64FC1) * (firstFrame?9999.0:0.00001); if(!firstFrame) { // angular cov = 0.000001 // roll/pitch should be fairly accurate with VIO input info->odomCovariance.at(3,3) *= 0.01; // roll info->odomCovariance.at(4,4) *= 0.01; // pitch if(!dataGoodTracking) { UERROR("not good tracking!"); // add slightly more error on translation // 0.001 info->odomCovariance.at(0,0) *= 10; // x info->odomCovariance.at(1,1) *= 10; // y info->odomCovariance.at(2,2) *= 10; // z info->odomCovariance.at(5,5) *= 10; // yaw } } info->odomPose = dataPose; } } else { UWARN("CameraMobile::captureImage() invalid data!"); } return data; } LaserScan CameraMobile::scanFromPointCloudData( const cv::Mat & pointCloudData, const Transform & pose, const CameraModel & model, const cv::Mat & rgb, std::vector * kpts, std::vector * kpts3D, int kptsSize) { if(!pointCloudData.empty()) { cv::Mat scanData(1, pointCloudData.cols, CV_32FC4); float * ptr = scanData.ptr(); const float * inPtr = pointCloudData.ptr(); int ic = pointCloudData.channels(); UASSERT(pointCloudData.depth() == CV_32F && ic >= 3); int oi = 0; for(unsigned int i=0;i 0) { int u,v; model.reproject(pt.x, pt.y, pt.z, u, v); unsigned char r=255,g=255,b=255; if(model.inFrame(u, v)) { b=rgb.at(v,u).val[0]; g=rgb.at(v,u).val[1]; r=rgb.at(v,u).val[2]; if(kpts) kpts->push_back(cv::KeyPoint(u,v,kptsSize)); if(kpts3D) kpts3D->push_back(org); *(int*)&ptr[oi*4 + 3] = int(b) | (int(g) << 8) | (int(r) << 16); ++oi; } } //confidence //*(int*)&ptr[i*4 + 3] = (int(pointCloudData[i*4 + 3] * 255.0f) << 8) | (int(255) << 16); } return LaserScan::backwardCompatibility(scanData.colRange(0, oi), 0, 10, rtabmap::Transform::getIdentity()); } return LaserScan(); } } /* namespace rtabmap */ rtabmap-0.22.1/app/android/jni/CameraMobile.h000066400000000000000000000154261503504370700207070ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CAMERAMOBILE_H_ #define CAMERAMOBILE_H_ #include #include #include #include #include #include #include #include #include #include "util.h" namespace rtabmap { class CameraInfoEvent: public UEvent { public: CameraInfoEvent(int type, const std::string & key, const std::string & value) : type_(type), key_(key), value_(value) {} virtual std::string getClassName() const {return "CameraInfoEvent";} int type() const {return type_;} const std::string & key() const {return key_;} const std::string & value() const {return value_;} private: int type_; std::string key_; std::string value_; }; class PoseEvent: public UEvent { public: PoseEvent(const Transform & pose) : pose_(pose) {} virtual std::string getClassName() const {return "PoseEvent";} const Transform & pose() const {return pose_;} private: Transform pose_; }; class CameraMobile : public Camera, public UEventsSender { public: static const rtabmap::Transform opticalRotation; static const rtabmap::Transform opticalRotationInv; public: static LaserScan scanFromPointCloudData( const cv::Mat & pointCloudData, const Transform & pose, const CameraModel & model, const cv::Mat & rgb, std::vector * kpts = 0, std::vector * kpts3D = 0, int kptsSize = 3); public: CameraMobile(float upstreamRelocalizationAccThr = 0.0f); virtual ~CameraMobile(); // abstract functions virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = ""); virtual void close(); // inherited classes should call its parent at the end of their close(). virtual std::string getSerial() const {return "CameraMobile";} // original pose of device in rtabmap frame (without origin offset), stamp of the device (may be not epoch), viewMatrix in opengl frame (without origin offset) void update(const SensorData & data, const Transform & pose, const glm::mat4 & viewMatrix, const glm::mat4 & projectionMatrix, const float * texCoord); void updateOnRender(); void resetOrigin(const rtabmap::Transform & offset = rtabmap::Transform()); virtual bool isCalibrated() const; virtual bool odomProvided() const { return true; } virtual bool getPose(double epochStamp, Transform & pose, cv::Mat & covariance, double maxWaitTime = 0.06); // Return pose of device in rtabmap frame (with origin offset), stamp should be epoch time // original pose of device in rtabmap frame (without origin offset), stamp of the device (may be not epoch) void poseReceived(const Transform & pose, double deviceStamp); double getStampEpochOffset() const {return stampEpochOffset_;} const CameraModel & getCameraModel() const {return model_;} const Transform & getDeviceTColorCamera() const {return deviceTColorCamera_;} virtual void setScreenRotationAndSize(ScreenRotation colorCameraToDisplayRotation, int width, int height) {colorCameraToDisplayRotation_ = colorCameraToDisplayRotation;} void setGPS(const GPS & gps); void addEnvSensor(int type, float value); GLuint getTextureId() {return textureId_;} bool uvsInitialized() const {return uvs_initialized_;} const float* uvsTransformed() const {return transformed_uvs_;} void getVPMatrices(glm::mat4 & view, glm::mat4 & projection) const {view=viewMatrix_; projection=projectionMatrix_;} ScreenRotation getScreenRotation() const {return colorCameraToDisplayRotation_;} void setOcclusionImage(const cv::Mat & image, const CameraModel & model) {occlusionModel_ = model; occlusionImage_ = image;} const cv::Mat & getOcclusionImage(CameraModel * model=0) const {if(model)*model=occlusionModel_; return occlusionImage_; } protected: virtual SensorData updateDataOnRender(Transform & pose); private: virtual SensorData captureImage(SensorCaptureInfo * info = 0); void postUpdate(); // Should be called while being protected by dataMutex_ protected: CameraModel model_; // local transform is the device to camera optical rotation in rtabmap frame Transform deviceTColorCamera_; // device to camera optical rotation in rtabmap frame GLuint textureId_; glm::mat4 viewMatrix_; glm::mat4 projectionMatrix_; float transformed_uvs_[8]; bool uvs_initialized_ = false; private: bool firstFrame_; double stampEpochOffset_; ScreenRotation colorCameraToDisplayRotation_; GPS lastKnownGPS_; EnvSensors lastEnvSensors_; Transform originOffset_; bool originUpdate_; rtabmap::Transform manualOriginOffset_; float upstreamRelocalizationAccThr_; rtabmap::Transform previousAnchorPose_; std::vector previousAnchorLinearVelocity_; double previousAnchorStamp_; std::map > relocalizationDebugBuffer_; USemaphore dataReady_; UMutex dataMutex_; SensorData data_; Transform dataPose_; bool dataGoodTracking_; UMutex poseMutex_; std::map poseBuffer_; // cv::Mat occlusionImage_; CameraModel occlusionModel_; }; } /* namespace rtabmap */ #endif /* CAMERATANGO_H_ */ rtabmap-0.22.1/app/android/jni/CameraTango.cpp000066400000000000000000000722151503504370700211020ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "CameraTango.h" #include "util.h" #include "rtabmap/utilite/ULogger.h" #include "rtabmap/core/util3d_transforms.h" #include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/util2d.h" #include #include #include "tango-gl/camera.h" namespace rtabmap { #define nullptr 0 const int kVersionStringLength = 128; const int holeSize = 5; const float maxDepthError = 0.10; const int scanDownsampling = 1; //android phone //11 10 01 00 // portrait //01 11 00 10 // left //10 00 11 01 // right //00 01 10 11 // down const float kTextureCoords0[] = {1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0}; const float kTextureCoords90[] = {0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0}; const float kTextureCoords180[] = {0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0}; const float kTextureCoords270[] = {1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0}; // Callbacks void onPointCloudAvailableRouter(void* context, const TangoPointCloud* point_cloud) { CameraTango* app = static_cast(context); if(point_cloud->num_points>0) { app->cloudReceived(cv::Mat(1, point_cloud->num_points, CV_32FC4, point_cloud->points[0]), point_cloud->timestamp); } } void onFrameAvailableRouter(void* context, TangoCameraId id, const TangoImageBuffer* color) { CameraTango* app = static_cast(context); cv::Mat tangoImage; if(color->format == TANGO_HAL_PIXEL_FORMAT_RGBA_8888) { tangoImage = cv::Mat(color->height, color->width, CV_8UC4, color->data); } else if(color->format == TANGO_HAL_PIXEL_FORMAT_YV12) { tangoImage = cv::Mat(color->height+color->height/2, color->width, CV_8UC1, color->data); } else if(color->format == TANGO_HAL_PIXEL_FORMAT_YCrCb_420_SP) { tangoImage = cv::Mat(color->height+color->height/2, color->width, CV_8UC1, color->data); } else if(color->format == 35) { tangoImage = cv::Mat(color->height+color->height/2, color->width, CV_8UC1, color->data); } else { LOGE("Not supported color format : %d.", color->format); } if(!tangoImage.empty()) { app->rgbReceived(tangoImage, (unsigned int)color->format, color->timestamp); } } void onPoseAvailableRouter(void* context, const TangoPoseData* pose) { if(pose->status_code == TANGO_POSE_VALID) { CameraTango* app = static_cast(context); app->poseReceived(rtabmap_world_T_tango_world * app->tangoPoseToTransform(pose) * tango_device_T_rtabmap_world, pose->timestamp); } } void onTangoEventAvailableRouter(void* context, const TangoEvent* event) { CameraTango* app = static_cast(context); app->tangoEventReceived(event->type, event->event_key, event->event_value); } ////////////////////////////// // CameraTango ////////////////////////////// CameraTango::CameraTango(bool colorCamera, int decimation, bool publishRawScan) : tango_config_(0), colorCamera_(colorCamera), decimation_(decimation), rawScanPublished_(publishRawScan), tangoColorType_(0), tangoColorStamp_(0) { UASSERT(decimation >= 1); } CameraTango::~CameraTango() { // Disconnect Tango service close(); } // Compute fisheye distorted coordinates from undistorted coordinates. // The distortion model used by the Tango fisheye camera is called FOV and is // described in 'Straight lines have to be straight' by Frederic Devernay and // Olivier Faugeras. See https://hal.inria.fr/inria-00267247/document. // Tango ROS Streamer: https://github.com/Intermodalics/tango_ros/blob/master/tango_ros_common/tango_ros_native/src/tango_ros_node.cpp void applyFovModel( double xu, double yu, double w, double w_inverse, double two_tan_w_div_two, double* xd, double* yd) { double ru = sqrt(xu * xu + yu * yu); constexpr double epsilon = 1e-7; if (w < epsilon || ru < epsilon) { *xd = xu; *yd = yu ; } else { double rd_div_ru = std::atan(ru * two_tan_w_div_two) * w_inverse / ru; *xd = xu * rd_div_ru; *yd = yu * rd_div_ru; } } // Compute the warp maps to undistort the Tango fisheye image using the FOV // model. See OpenCV documentation for more information on warp maps: // http://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html // Tango ROS Streamer: https://github.com/Intermodalics/tango_ros/blob/master/tango_ros_common/tango_ros_native/src/tango_ros_node.cpp // @param fisheyeModel the fisheye camera intrinsics. // @param mapX the output map for the x direction. // @param mapY the output map for the y direction. void initFisheyeRectificationMap( const CameraModel& fisheyeModel, cv::Mat & mapX, cv::Mat & mapY) { const double & fx = fisheyeModel.K().at(0,0); const double & fy = fisheyeModel.K().at(1,1); const double & cx = fisheyeModel.K().at(0,2); const double & cy = fisheyeModel.K().at(1,2); const double & w = fisheyeModel.D().at(0,0); mapX.create(fisheyeModel.imageSize(), CV_32FC1); mapY.create(fisheyeModel.imageSize(), CV_32FC1); LOGD("initFisheyeRectificationMap: fx=%f fy=%f, cx=%f, cy=%f, w=%f", fx, fy, cx, cy, w); // Pre-computed variables for more efficiency. const double fy_inverse = 1.0 / fy; const double fx_inverse = 1.0 / fx; const double w_inverse = 1 / w; const double two_tan_w_div_two = 2.0 * std::tan(w * 0.5); // Compute warp maps in x and y directions. // OpenCV expects maps from dest to src, i.e. from undistorted to distorted // pixel coordinates. for(int iu = 0; iu < fisheyeModel.imageHeight(); ++iu) { for (int ju = 0; ju < fisheyeModel.imageWidth(); ++ju) { double xu = (ju - cx) * fx_inverse; double yu = (iu - cy) * fy_inverse; double xd, yd; applyFovModel(xu, yu, w, w_inverse, two_tan_w_div_two, &xd, &yd); double jd = cx + xd * fx; double id = cy + yd * fy; mapX.at(iu, ju) = jd; mapY.at(iu, ju) = id; } } } bool CameraTango::init(const std::string & calibrationFolder, const std::string & cameraName) { close(); CameraMobile::init(calibrationFolder, cameraName); TangoSupport_initialize(TangoService_getPoseAtTime, TangoService_getCameraIntrinsics); // Connect to Tango LOGI("NativeRTABMap: Setup tango config"); tango_config_ = TangoService_getConfig(TANGO_CONFIG_DEFAULT); if (tango_config_ == nullptr) { LOGE("NativeRTABMap: Failed to get default config form"); return false; } // Set auto-recovery for motion tracking as requested by the user. bool is_atuo_recovery = true; int ret = TangoConfig_setBool(tango_config_, "config_enable_auto_recovery", is_atuo_recovery); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: config_enable_auto_recovery() failed with error code: %d", ret); return false; } if(colorCamera_) { // Enable color. ret = TangoConfig_setBool(tango_config_, "config_enable_color_camera", true); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: config_enable_color_camera() failed with error code: %d", ret); return false; } } // Enable depth. ret = TangoConfig_setBool(tango_config_, "config_enable_depth", true); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: config_enable_depth() failed with error code: %d", ret); return false; } // Need to specify the depth_mode as XYZC. ret = TangoConfig_setInt32(tango_config_, "config_depth_mode", TANGO_POINTCLOUD_XYZC); if (ret != TANGO_SUCCESS) { LOGE("Failed to set 'depth_mode' configuration flag with error code: %d", ret); return false; } // Note that it's super important for AR applications that we enable low // latency imu integration so that we have pose information available as // quickly as possible. Without setting this flag, you'll often receive // invalid poses when calling GetPoseAtTime for an image. ret = TangoConfig_setBool(tango_config_, "config_enable_low_latency_imu_integration", true); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to enable low latency imu integration."); return false; } // Drift correction allows motion tracking to recover after it loses tracking. // // The drift corrected pose is is available through the frame pair with // base frame AREA_DESCRIPTION and target frame DEVICE. /*ret = TangoConfig_setBool(tango_config_, "config_enable_drift_correction", true); if (ret != TANGO_SUCCESS) { LOGE( "NativeRTABMap: enabling config_enable_drift_correction " "failed with error code: %d", ret); return false; }*/ // Get TangoCore version string from service. char tango_core_version[kVersionStringLength]; ret = TangoConfig_getString(tango_config_, "tango_service_library_version", tango_core_version, kVersionStringLength); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: get tango core version failed with error code: %d", ret); return false; } LOGI("NativeRTABMap: Tango version : %s", tango_core_version); // Callbacks LOGI("NativeRTABMap: Setup callbacks"); // Attach the OnXYZijAvailable callback. // The callback will be called after the service is connected. ret = TangoService_connectOnPointCloudAvailable(onPointCloudAvailableRouter); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to connect to point cloud callback with error code: %d", ret); return false; } ret = TangoService_connectOnFrameAvailable(colorCamera_?TANGO_CAMERA_COLOR:TANGO_CAMERA_FISHEYE, this, onFrameAvailableRouter); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to connect to color callback with error code: %d", ret); return false; } // Attach the onPoseAvailable callback. // The callback will be called after the service is connected. TangoCoordinateFramePair pair; //pair.base = TANGO_COORDINATE_FRAME_AREA_DESCRIPTION; // drift correction is enabled pair.base = TANGO_COORDINATE_FRAME_START_OF_SERVICE; pair.target = TANGO_COORDINATE_FRAME_DEVICE; ret = TangoService_connectOnPoseAvailable(1, &pair, onPoseAvailableRouter); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to connect to pose callback with error code: %d", ret); return false; } // Attach the onEventAvailable callback. // The callback will be called after the service is connected. ret = TangoService_connectOnTangoEvent(onTangoEventAvailableRouter); if (ret != TANGO_SUCCESS) { LOGE("PointCloudApp: Failed to connect to event callback with error code: %d", ret); return false; } // Now connect service so the callbacks above will be called LOGI("NativeRTABMap: Connect to tango service"); ret = TangoService_connect(this, tango_config_); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to connect to the Tango service with error code: %d", ret); return false; } // update extrinsics LOGI("NativeRTABMap: Update extrinsics"); TangoPoseData pose_data; TangoCoordinateFramePair frame_pair; // TangoService_getPoseAtTime function is used for query device extrinsics // as well. We use timestamp 0.0 and the target frame pair to get the // extrinsics from the sensors. // // Get color camera with respect to device transformation matrix. frame_pair.base = TANGO_COORDINATE_FRAME_DEVICE; frame_pair.target = colorCamera_?TANGO_COORDINATE_FRAME_CAMERA_COLOR:TANGO_COORDINATE_FRAME_CAMERA_FISHEYE; ret = TangoService_getPoseAtTime(0.0, frame_pair, &pose_data); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to get transform between the color camera frame and device frames"); return false; } deviceTColorCamera_ = rtabmap::Transform( pose_data.translation[0], pose_data.translation[1], pose_data.translation[2], pose_data.orientation[0], pose_data.orientation[1], pose_data.orientation[2], pose_data.orientation[3]); deviceTColorCamera_ = rtabmap_world_T_opengl_world * deviceTColorCamera_; // camera intrinsic TangoCameraIntrinsics color_camera_intrinsics; ret = TangoService_getCameraIntrinsics(colorCamera_?TANGO_CAMERA_COLOR:TANGO_CAMERA_FISHEYE, &color_camera_intrinsics); if (ret != TANGO_SUCCESS) { LOGE("NativeRTABMap: Failed to get the intrinsics for the color camera with error code: %d.", ret); return false; } LOGD("Calibration: fx=%f fy=%f cx=%f cy=%f width=%d height=%d", color_camera_intrinsics.fx, color_camera_intrinsics.fy, color_camera_intrinsics.cx, color_camera_intrinsics.cy, color_camera_intrinsics.width, color_camera_intrinsics.height); cv::Mat K = cv::Mat::eye(3, 3, CV_64FC1); K.at(0,0) = color_camera_intrinsics.fx; K.at(1,1) = color_camera_intrinsics.fy; K.at(0,2) = color_camera_intrinsics.cx; K.at(1,2) = color_camera_intrinsics.cy; cv::Mat D = cv::Mat::zeros(1, 5, CV_64FC1); LOGD("Calibration type = %d", color_camera_intrinsics.calibration_type); if(color_camera_intrinsics.calibration_type == TANGO_CALIBRATION_POLYNOMIAL_5_PARAMETERS || color_camera_intrinsics.calibration_type == TANGO_CALIBRATION_EQUIDISTANT) { D.at(0,0) = color_camera_intrinsics.distortion[0]; D.at(0,1) = color_camera_intrinsics.distortion[1]; D.at(0,2) = color_camera_intrinsics.distortion[2]; D.at(0,3) = color_camera_intrinsics.distortion[3]; D.at(0,4) = color_camera_intrinsics.distortion[4]; } else if(color_camera_intrinsics.calibration_type == TANGO_CALIBRATION_POLYNOMIAL_3_PARAMETERS) { D.at(0,0) = color_camera_intrinsics.distortion[0]; D.at(0,1) = color_camera_intrinsics.distortion[1]; D.at(0,2) = 0.; D.at(0,3) = 0.; D.at(0,4) = color_camera_intrinsics.distortion[2]; } else if(color_camera_intrinsics.calibration_type == TANGO_CALIBRATION_POLYNOMIAL_2_PARAMETERS) { D.at(0,0) = color_camera_intrinsics.distortion[0]; D.at(0,1) = color_camera_intrinsics.distortion[1]; D.at(0,2) = 0.; D.at(0,3) = 0.; D.at(0,4) = 0.; } cv::Mat R = cv::Mat::eye(3, 3, CV_64FC1); cv::Mat P; LOGD("Distortion params: %f, %f, %f, %f, %f", D.at(0,0), D.at(0,1), D.at(0,2), D.at(0,3), D.at(0,4)); model_ = CameraModel(colorCamera_?"color":"fisheye", cv::Size(color_camera_intrinsics.width, color_camera_intrinsics.height), K, D, R, P, deviceTColorCamera_); if(!colorCamera_) { initFisheyeRectificationMap(model_, fisheyeRectifyMapX_, fisheyeRectifyMapY_); } LOGI("deviceTColorCameraRtabmap =%s", deviceTColorCamera_.prettyPrint().c_str()); return true; } void CameraTango::close() { if(tango_config_) { TangoConfig_free(tango_config_); tango_config_ = nullptr; LOGI("TangoService_disconnect()"); TangoService_disconnect(); LOGI("TangoService_disconnect() done."); } fisheyeRectifyMapX_ = cv::Mat(); fisheyeRectifyMapY_ = cv::Mat(); CameraMobile::close(); } void CameraTango::cloudReceived(const cv::Mat & cloud, double timestamp) { if(!cloud.empty()) { //LOGD("Depth received! %fs (%d points)", timestamp, cloud.cols); UASSERT(cloud.type() == CV_32FC4); boost::mutex::scoped_lock lock(tangoDataMutex_); // From post: http://stackoverflow.com/questions/29236110/timing-issues-with-tango-image-frames // "In the current version of Project Tango Tablet RGB IR camera // is used for both depth and color images and it can only do one // or the other for each frame. So in the stream we get 4 RGB frames // followed by 1 Depth frame resulting in the pattern you observed. This // is more of a hardware limitation." // // So, synchronize with the last RGB frame before the Depth is acquired if(!tangoColor_.empty()) { UTimer timer; double dt = fabs(timestamp - tangoColorStamp_); //LOGD("Depth: %f vs %f = %f", tangoColorStamp_, timestamp, dt); if(dt >= 0.0 && dt < 0.5) { bool notify = !tangoData_.isValid(); cv::Mat tangoImage = tangoColor_; cv::Mat rgb; double cloudStamp = timestamp; double rgbStamp = tangoColorStamp_; int tangoColorType = tangoColorType_; tangoColor_ = cv::Mat(); tangoColorStamp_ = 0.0; tangoColorType_ = 0; LOGD("tangoColorType=%d", tangoColorType); if(tangoColorType == TANGO_HAL_PIXEL_FORMAT_RGBA_8888) { cv::cvtColor(tangoImage, rgb, cv::COLOR_RGBA2BGR); } else if(tangoColorType == TANGO_HAL_PIXEL_FORMAT_YV12) { cv::cvtColor(tangoImage, rgb, cv::COLOR_YUV2BGR_YV12); } else if(tangoColorType == TANGO_HAL_PIXEL_FORMAT_YCrCb_420_SP) { cv::cvtColor(tangoImage, rgb, cv::COLOR_YUV2BGR_NV21); } else if(tangoColorType == 35) { cv::cvtColor(tangoImage, rgb, cv::COLOR_YUV420sp2GRAY); } else { LOGE("Not supported color format : %d.", tangoColorType); tangoData_ = SensorData(); return; } //for(int i=0; i(i)[0], (int)rgb.at(i)[1], (int)rgb.at(i)[2]); //} CameraModel model = model_; if(colorCamera_) { if(decimation_ > 1) { rgb = util2d::decimate(rgb, decimation_); model = model.scaled(1.0/double(decimation_)); } } else { //UTimer t; cv::Mat rgbRect; cv::remap(rgb, rgbRect, fisheyeRectifyMapX_, fisheyeRectifyMapY_, cv::INTER_LINEAR, cv::BORDER_CONSTANT, 0); rgb = rgbRect; //LOGD("Rectification time=%fs", t.ticks()); } // Querying the depth image's frame transformation based on the depth image's // timestamp. cv::Mat depth; // Calculate the relative pose from color camera frame at timestamp // color_timestamp t1 and depth // camera frame at depth_timestamp t0. Transform colorToDepth; TangoPoseData pose_color_image_t1_T_depth_image_t0; if (TangoSupport_calculateRelativePose( rgbStamp, colorCamera_?TANGO_COORDINATE_FRAME_CAMERA_COLOR:TANGO_COORDINATE_FRAME_CAMERA_FISHEYE, cloudStamp, TANGO_COORDINATE_FRAME_CAMERA_DEPTH, &pose_color_image_t1_T_depth_image_t0) == TANGO_SUCCESS) { colorToDepth = tangoPoseToTransform(&pose_color_image_t1_T_depth_image_t0); } else { LOGE( "SynchronizationApplication: Could not find a valid relative pose at " "time for color and " " depth cameras."); } if(colorToDepth.getNormSquared() > 100000) { LOGE("Very large color to depth error detected (%s)! Ignoring this frame!", colorToDepth.prettyPrint().c_str()); colorToDepth.setNull(); } cv::Mat scan; if(!colorToDepth.isNull()) { // The Color Camera frame at timestamp t0 with respect to Depth // Camera frame at timestamp t1. //LOGD("colorToDepth=%s", colorToDepth.prettyPrint().c_str()); LOGD("rgb=%dx%d cloud size=%d", rgb.cols, rgb.rows, (int)cloud.total()); int pixelsSet = 0; int depthSizeDec = colorCamera_?8:1; depth = cv::Mat::zeros(model_.imageHeight()/depthSizeDec, model_.imageWidth()/depthSizeDec, CV_16UC1); // mm CameraModel depthModel = model_.scaled(1.0f/float(depthSizeDec)); std::vector scanData(rawScanPublished_?cloud.total():0); int oi=0; int closePoints = 0; float closeROI[4]; closeROI[0] = depth.cols/4; closeROI[1] = 3*(depth.cols/4); closeROI[2] = depth.rows/4; closeROI[3] = 3*(depth.rows/4); unsigned short minDepthValue=10000; for(unsigned int i=0; i(0,i); cv::Point3f pt = util3d::transformPoint(cv::Point3f(p[0], p[1], p[2]), colorToDepth); if(pt.z > 0.0f && i%scanDownsampling == 0 && rawScanPublished_) { scanData.at(oi++) = pt; } int pixel_x_l, pixel_y_l, pixel_x_h, pixel_y_h; // get the coordinate on image plane. pixel_x_l = static_cast((depthModel.fx()) * (pt.x / pt.z) + depthModel.cx()); pixel_y_l = static_cast((depthModel.fy()) * (pt.y / pt.z) + depthModel.cy()); pixel_x_h = static_cast((depthModel.fx()) * (pt.x / pt.z) + depthModel.cx() + 0.5f); pixel_y_h = static_cast((depthModel.fy()) * (pt.y / pt.z) + depthModel.cy() + 0.5f); unsigned short depth_value(pt.z * 1000.0f); if(pixel_x_l>=closeROI[0] && pixel_x_lcloseROI[2] && pixel_y_l=0 && pixel_x_l0 && pixel_y_l(pixel_y_l, pixel_x_l); if(depthPixel == 0 || depthPixel > depth_value) { depthPixel = depth_value; pixelSet = true; } } if(pixel_x_h>=0 && pixel_x_h0 && pixel_y_h(pixel_y_h, pixel_x_h); if(depthPixel == 0 || depthPixel > depth_value) { depthPixel = depth_value; pixelSet = true; } } if(pixelSet) { pixelsSet += 1; } } if(closePoints > 100) { this->post(new CameraInfoEvent(0, "TooClose", "")); } if(oi) { scan = cv::Mat(1, oi, CV_32FC3, scanData.data()).clone(); } //LOGD("pixels depth set= %d", pixelsSet); } else { LOGE("color to depth pose is null?!? (rgb stamp=%f) (depth stamp=%f)", rgbStamp, cloudStamp); } if(!rgb.empty() && !depth.empty()) { depth = rtabmap::util2d::fillDepthHoles(depth, holeSize, maxDepthError); Transform odom = getPoseAtTimestamp(rgbStamp); //LOGD("Local = %s", model.localTransform().prettyPrint().c_str()); //LOGD("tango = %s", poseDevice.prettyPrint().c_str()); //LOGD("opengl(t)= %s", (opengl_world_T_tango_world * poseDevice).prettyPrint().c_str()); // occlusion depth if(!depth.empty()) { rtabmap::CameraModel depthModel = model.scaled(float(depth.cols) / float(model.imageWidth())); depthModel.setLocalTransform(odom*model.localTransform()); this->setOcclusionImage(depth, depthModel); } //LOGD("rtabmap = %s", odom.prettyPrint().c_str()); //LOGD("opengl(r)= %s", (opengl_world_T_rtabmap_world * odom * rtabmap_device_T_opengl_device).prettyPrint().c_str()); Transform scanLocalTransform = model.localTransform(); if(rawScanPublished_) { tangoData_ = SensorData(LaserScan::backwardCompatibility(scan, cloud.total()/scanDownsampling, 0, scanLocalTransform), rgb, depth, model, this->getNextSeqID(), rgbStamp); } else { tangoData_ = SensorData(rgb, depth, model, this->getNextSeqID(), rgbStamp); } tangoData_.setGroundTruth(odom); } else { LOGE("Could not get depth and rgb images!?!"); tangoData_ = SensorData(); return; } if(notify) { tangoDataReady_.release(); } LOGD("process cloud received %fs", timer.ticks()); } } } } void CameraTango::rgbReceived(const cv::Mat & tangoImage, int type, double timestamp) { if(!tangoImage.empty()) { //LOGD("RGB received! %fs", timestamp); boost::mutex::scoped_lock lock(tangoDataMutex_); tangoColor_ = tangoImage.clone(); tangoColorStamp_ = timestamp; tangoColorType_ = type; } } void CameraTango::tangoEventReceived(int type, const char * key, const char * value) { this->post(new CameraInfoEvent(type, key, value)); } std::string CameraTango::getSerial() const { return "Tango"; } rtabmap::Transform CameraTango::tangoPoseToTransform(const TangoPoseData * tangoPose) const { UASSERT(tangoPose); rtabmap::Transform pose; pose = rtabmap::Transform( tangoPose->translation[0], tangoPose->translation[1], tangoPose->translation[2], tangoPose->orientation[0], tangoPose->orientation[1], tangoPose->orientation[2], tangoPose->orientation[3]); return pose; } rtabmap::Transform CameraTango::getPoseAtTimestamp(double timestamp) { rtabmap::Transform pose; TangoPoseData pose_start_service_T_device; TangoCoordinateFramePair frame_pair; frame_pair.base = TANGO_COORDINATE_FRAME_START_OF_SERVICE; frame_pair.target = TANGO_COORDINATE_FRAME_DEVICE; TangoErrorType status = TangoService_getPoseAtTime(timestamp, frame_pair, &pose_start_service_T_device); if (status != TANGO_SUCCESS) { LOGE( "PoseData: Failed to get transform between the Start of service and " "device frames at timestamp %lf", timestamp); } if (pose_start_service_T_device.status_code != TANGO_POSE_VALID) { LOGW( "PoseData: Failed to get transform between the Start of service and " "device frames at timestamp %lf", timestamp); } else { pose = rtabmap_world_T_tango_world * tangoPoseToTransform(&pose_start_service_T_device) * tango_device_T_rtabmap_world; } return pose; } SensorData CameraTango::updateDataOnRender(Transform & pose) { //LOGI("Capturing image..."); pose.setNull(); if(textureId_ == 0) { glGenTextures(1, &textureId_); glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId_); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } // Update Texture (optional, just for first-view rendering) if(colorCamera_ && textureId_) { double video_overlay_timestamp; TangoErrorType status = TangoService_updateTextureExternalOes(TANGO_CAMERA_COLOR, textureId_, &video_overlay_timestamp); if (status == TANGO_SUCCESS) { pose = getPoseAtTimestamp(video_overlay_timestamp); int rotation = static_cast(getScreenRotation()) + 1; // remove 90deg camera rotation if (rotation > 3) { rotation -= 4; } TangoDoubleMatrixTransformData matrix_transform; status = TangoSupport_getDoubleMatrixTransformAtTime( video_overlay_timestamp, TANGO_COORDINATE_FRAME_CAMERA_COLOR, TANGO_COORDINATE_FRAME_START_OF_SERVICE, TANGO_SUPPORT_ENGINE_OPENGL, TANGO_SUPPORT_ENGINE_OPENGL, static_cast(rotation), &matrix_transform); if (matrix_transform.status_code == TANGO_POSE_VALID) { // Get projection matrix TangoCameraIntrinsics color_camera_intrinsics; int ret = TangoSupport_getCameraIntrinsicsBasedOnDisplayRotation( TANGO_CAMERA_COLOR, static_cast(rotation), &color_camera_intrinsics); if (ret == TANGO_SUCCESS) { float image_width = static_cast(color_camera_intrinsics.width); float image_height = static_cast(color_camera_intrinsics.height); float fx = static_cast(color_camera_intrinsics.fx); float fy = static_cast(color_camera_intrinsics.fy); float cx = static_cast(color_camera_intrinsics.cx); float cy = static_cast(color_camera_intrinsics.cy); viewMatrix_ = glm::make_mat4(matrix_transform.matrix); projectionMatrix_ = tango_gl::Camera::ProjectionMatrixForCameraIntrinsics( image_width, image_height, fx, fy, cx, cy, 0.3, 50); switch(rotation) { case ROTATION_90: memcpy(transformed_uvs_, kTextureCoords90, 8*sizeof(float)); break; case ROTATION_180: memcpy(transformed_uvs_, kTextureCoords180, 8*sizeof(float)); break; case ROTATION_270: memcpy(transformed_uvs_, kTextureCoords270, 8*sizeof(float)); break; case ROTATION_0: default: memcpy(transformed_uvs_, kTextureCoords0, 8*sizeof(float)); } uvs_initialized_ = true; } else { UERROR("TangoSupport_getCameraIntrinsicsBasedOnDisplayRotation failed!"); } } else { UERROR("TangoSupport_getDoubleMatrixTransformAtTime failed!"); } } else { UERROR("TangoService_updateTextureExternalOes failed!"); } } SensorData data; if(tangoDataReady_.acquireTry(1)) { boost::mutex::scoped_lock lock(tangoDataMutex_); data = tangoData_; tangoData_ = SensorData(); pose = data.groundTruth(); data.setGroundTruth(Transform()); } return data; } } /* namespace rtabmap */ rtabmap-0.22.1/app/android/jni/CameraTango.h000066400000000000000000000064021503504370700205420ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CAMERATANGO_H_ #define CAMERATANGO_H_ #include "CameraMobile.h" #include #include #include #include #include #include #include #include #include #include #include namespace rtabmap { class CameraTango : public CameraMobile { public: CameraTango(bool colorCamera, int decimation, bool publishRawScan); virtual ~CameraTango(); virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = ""); virtual void close(); // close Tango connection virtual std::string getSerial() const; rtabmap::Transform tangoPoseToTransform(const TangoPoseData * tangoPose) const; void setDecimation(int value) {decimation_ = value;} void setRawScanPublished(bool enabled) {rawScanPublished_ = enabled;} void cloudReceived(const cv::Mat & cloud, double timestamp); void rgbReceived(const cv::Mat & tangoImage, int type, double timestamp); void tangoEventReceived(int type, const char * key, const char * value); protected: virtual SensorData updateDataOnRender(Transform & pose); private: rtabmap::Transform getPoseAtTimestamp(double timestamp); private: void * tango_config_; bool colorCamera_; int decimation_; bool rawScanPublished_; SensorData tangoData_; cv::Mat tangoColor_; int tangoColorType_; double tangoColorStamp_; boost::mutex tangoDataMutex_; USemaphore tangoDataReady_; cv::Mat fisheyeRectifyMapX_; cv::Mat fisheyeRectifyMapY_; }; } /* namespace rtabmap */ #endif /* CAMERATANGO_H_ */ rtabmap-0.22.1/app/android/jni/Measure.h000066400000000000000000000043231503504370700177620ustar00rootroot00000000000000/* Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MEASURE_H_ #define MEASURE_H_ #include class Measure { public: Measure( const cv::Point3f & pt1, const cv::Point3f & pt2, const cv::Vec3f & n1, const cv::Vec3f & n2) : pt1_(pt1), pt2_(pt2), n1_(n1), n2_(n2) { length_ = cv::norm(pt2_ - pt1_); } virtual ~Measure() {} float length() const {return length_;} const cv::Point3f & pt1() const {return pt1_;} const cv::Point3f & pt2() const {return pt2_;} const cv::Vec3f & n1() const {return n1_;} const cv::Vec3f & n2() const {return n2_;} private: cv::Point3f pt1_; cv::Point3f pt2_; cv::Vec3f n1_; cv::Vec3f n2_; float length_; }; #endif /* MEASURE_H_ */ rtabmap-0.22.1/app/android/jni/ProgressionStatus.h000066400000000000000000000055201503504370700220770ustar00rootroot00000000000000/* * ProgressionStatus.h * * Created on: Feb 28, 2017 * Author: mathieu */ #ifndef APP_ANDROID_JNI_PROGRESSIONSTATUS_H_ #define APP_ANDROID_JNI_PROGRESSIONSTATUS_H_ #include #include #include #ifdef __ANDROID__ #include #endif namespace rtabmap { class ProgressEvent : public UEvent { public: ProgressEvent(int count = 1) : count_(count){} virtual std::string getClassName() const {return "ProgressEvent";} int count_; }; class ProgressionStatus: public ProgressState, public UEventsHandler { public: ProgressionStatus() : count_(0), max_(100) #ifdef __ANDROID__ , jvm_(0), rtabmap_(0) #else , swiftClassPtr_(0) #endif { registerToEventsManager(); } #ifdef __ANDROID__ void setJavaObjects(JavaVM * jvm, jobject rtabmap) { jvm_ = jvm; rtabmap_ = rtabmap; } #else void setSwiftCallback(void * classPtr, void(*callback)(void *, int, int)) { swiftClassPtr_ = classPtr; swiftCallback = callback; } #endif void reset(int max) { count_=-1; max_ = max; setCanceled(false); increment(); } void setMax(int max) { max_ = max; } int getMax() const {return max_;} void increment(int count = 1) const { UEventsManager::post(new ProgressEvent(count)); } void finish() { } virtual bool callback(const std::string & msg) const { if(!isCanceled()) { increment(); } return ProgressState::callback(msg); } virtual ~ProgressionStatus(){} protected: virtual bool handleEvent(UEvent * event) { if(event->getClassName().compare("ProgressEvent") == 0) { count_ += ((ProgressEvent*)event)->count_; // Call JAVA callback bool success = false; #ifdef __ANDROID__ if(jvm_ && rtabmap_) { JNIEnv *env = 0; jint rs = jvm_->AttachCurrentThread(&env, NULL); if(rs == JNI_OK && env) { jclass clazz = env->GetObjectClass(rtabmap_); if(clazz) { jmethodID methodID = env->GetMethodID(clazz, "updateProgressionCallback", "(II)V" ); if(methodID) { env->CallVoidMethod(rtabmap_, methodID, count_, max_); success = true; } } } jvm_->DetachCurrentThread(); } #else // APPLE if(swiftClassPtr_) { std::function actualCallback = [&](){ swiftCallback(swiftClassPtr_, count_, max_); }; actualCallback(); success = true; } #endif if(!success) { UERROR("Failed to call rtabmap::updateProgressionCallback"); } } return false; } private: int count_; int max_; #ifdef __ANDROID__ JavaVM *jvm_; jobject rtabmap_; #else void * swiftClassPtr_; void(*swiftCallback)(void *, int, int); #endif }; } #endif /* APP_ANDROID_JNI_PROGRESSIONSTATUS_H_ */ rtabmap-0.22.1/app/android/jni/RTABMapApp.cpp000066400000000000000000005643131503504370700205550ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "RTABMapApp.h" #ifdef __ANDROID__ #include "CameraAvailability.h" #endif #ifdef RTABMAP_TANGO #include "CameraTango.h" #endif #ifdef RTABMAP_ARCORE #include "CameraARCore.h" #include #endif #ifdef RTABMAP_ARENGINE #include "CameraAREngine.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RTABMAP_PDAL #include #elif defined(RTABMAP_LIBLAS) #include #endif #define LOW_RES_PIX 2 #define DEBUG_RENDERING_PERFORMANCE const int g_optMeshId = -100; const float g_bilateralFilteringSigmaS = 2.0f; const float g_bilateralFilteringSigmaR = 0.075f; #ifdef __ANDROID__ static JavaVM *jvm; static jobject RTABMapActivity = 0; #endif #ifdef __ANDROID__ #ifndef DISABLE_LOG //ref: https://codelab.wordpress.com/2014/11/03/how-to-use-standard-output-streams-for-logging-in-android-apps/ static int pfd[2]; static pthread_t thr; static void *thread_func(void*) { ssize_t rdsz; char buf[128]; while((rdsz = read(pfd[0], buf, sizeof buf - 1)) > 0) { if(buf[rdsz - 1] == '\n') --rdsz; buf[rdsz] = 0; /* add null-terminator */ __android_log_write(ANDROID_LOG_DEBUG, LOG_TAG, buf); } return 0; } int start_logger() { /* make stdout line-buffered and stderr unbuffered */ setvbuf(stdout, 0, _IOLBF, 0); setvbuf(stderr, 0, _IONBF, 0); /* create the pipe and redirect stdout and stderr */ pipe(pfd); dup2(pfd[1], 1); dup2(pfd[1], 2); /* spawn the logging thread */ if(pthread_create(&thr, 0, thread_func, 0) == -1) return -1; pthread_detach(thr); return 0; } #endif #endif rtabmap::ParametersMap RTABMapApp::getRtabmapParameters() { rtabmap::ParametersMap parameters; parameters.insert(mappingParameters_.begin(), mappingParameters_.end()); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kKpDetectorStrategy(), "5")); // GFTT/FREAK parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kKpMaxFeatures(), std::string("200"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kGFTTQualityLevel(), std::string("0.0001"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemImagePreDecimation(), std::string(cameraColor_&&fullResolution_?"2":"1"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kBRIEFBytes(), std::string("64"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapTimeThr(), std::string("800"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapPublishLikelihood(), std::string("false"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapPublishPdf(), std::string("false"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapStartNewMapOnLoopClosure(), uBool2Str(!localizationMode_ && appendMode_ && !dataRecorderMode_))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDAggressiveLoopThr(), "0.0")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemBinDataKept(), uBool2Str(!trajectoryMode_))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerIterations(), "10")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemIncrementalMemory(), uBool2Str(!localizationMode_))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMaxRetrieved(), "1")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDMaxLocalRetrieved(), "0")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemCompressionParallelized(), std::string("false"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kKpParallelized(), std::string("false"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDOptimizeFromGraphEnd(), std::string("true"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kVisMinInliers(), std::string("25"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kVisPnPVarianceMedianRatio(), std::string("2"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDProximityPathMaxNeighbors(), std::string("0"))); // disable scan matching to merged nodes parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDProximityBySpace(), std::string("false"))); // just keep loop closure detection parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDLinearUpdate(), std::string("0.05"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDAngularUpdate(), std::string("0.05"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMarkerLength(), std::string("0.0"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemUseOdomGravity(), "true")); if(parameters.find(rtabmap::Parameters::kOptimizerStrategy()) != parameters.end()) { if(parameters.at(rtabmap::Parameters::kOptimizerStrategy()).compare("2") == 0) // GTSAM { parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerEpsilon(), "0.00001")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerIterations(), "10")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerGravitySigma(), "0.2")); } else if(parameters.at(rtabmap::Parameters::kOptimizerStrategy()).compare("1") == 0) // g2o { parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerEpsilon(), "0.0")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerIterations(), "10")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerGravitySigma(), "0.2")); } else // TORO { parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerEpsilon(), "0.00001")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerIterations(), "100")); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerGravitySigma(), "0")); } } parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpPointToPlane(), std::string("true"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemLaserScanNormalK(), std::string("0"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpIterations(), std::string("10"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpEpsilon(), std::string("0.001"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxRotation(), std::string("0.17"))); // 10 degrees parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxTranslation(), std::string("0.05"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpCorrespondenceRatio(), std::string("0.49"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxCorrespondenceDistance(), std::string("0.05"))); parameters.insert(*rtabmap::Parameters::getDefaultParameters().find(rtabmap::Parameters::kKpMaxFeatures())); parameters.insert(*rtabmap::Parameters::getDefaultParameters().find(rtabmap::Parameters::kMemRehearsalSimilarity())); parameters.insert(*rtabmap::Parameters::getDefaultParameters().find(rtabmap::Parameters::kMemMapLabelsAdded())); if(dataRecorderMode_) { // Example taken from https://github.com/introlab/rtabmap_ros/blob/master/rtabmap_launch/launch/data_recorder.launch uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kMemRehearsalSimilarity(), "1.0")); // deactivate rehearsal uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kKpMaxFeatures(), "-1")); // deactivate keypoints extraction uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMaxRetrieved(), "0")); // deactivate global retrieval uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDMaxLocalRetrieved(), "0")); // deactivate local retrieval uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kMemMapLabelsAdded(), "false")); // don't create map labels uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMemoryThr(), "2")); // keep the WM empty uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kMemSTMSize(), "1")); // STM=1 --> uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDProximityBySpace(), "false")); uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDLinearUpdate(), "0")); uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDAngularUpdate(), "0")); uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kMemNotLinkedNodesKept(), std::string("true"))); } return parameters; } #ifdef __ANDROID__ RTABMapApp::RTABMapApp(JNIEnv* env, jobject caller_activity) : #else //__APPLE__ RTABMapApp::RTABMapApp() : #endif cameraDriver_(0), camera_(0), sensorCaptureThread_(0), rtabmapThread_(0), rtabmap_(0), logHandler_(0), odomCloudShown_(true), graphOptimization_(true), nodesFiltering_(false), localizationMode_(false), trajectoryMode_(false), rawScanSaved_(false), smoothing_(true), depthBleedingError_(0.0f), depthFromMotion_(false), cameraColor_(true), fullResolution_(false), appendMode_(true), useExternalLidar_(false), maxCloudDepth_(2.5), minCloudDepth_(0.0), cloudDensityLevel_(1), meshTrianglePix_(2), meshAngleToleranceDeg_(20.0), meshDecimationFactor_(0), clusterRatio_(0.1), maxGainRadius_(0.02f), renderingTextureDecimation_(4), backgroundColor_(0.2f), #ifndef RTABMAP_ARCORE depthConfidence_(100), // iOS #else depthConfidence_(0), #endif upstreamRelocalizationMaxAcc_(0.0f), exportPointCloudFormat_("ply"), dataRecorderMode_(false), clearSceneOnNextRender_(false), openingDatabase_(false), exporting_(false), postProcessing_(false), filterPolygonsOnNextRender_(false), gainCompensationOnNextRender_(0), bilateralFilteringOnNextRender_(false), takeScreenshotOnNextRender_(false), cameraJustInitialized_(false), totalPoints_(0), totalPolygons_(0), lastDrawnCloudsCount_(0), renderingTime_(0.0f), lastPostRenderEventTime_(0.0), lastPoseEventTime_(0.0), visualizingMesh_(false), exportedMeshUpdated_(false), optTextureMesh_(new pcl::TextureMesh), optRefId_(0), optRefPose_(0), measuresUpdated_(false), metricSystem_(true), measuringTextSize_(0.05f), snapAxisThr_(0.95), measuringMode_(0), addMeasureClicked_(false), teleportClicked_(false), removeMeasureClicked_(false), targetPoint_(new pcl::PointCloud), quadSample_(new pcl::PointCloud), quadSamplePolygons_(2), mapToOdom_(rtabmap::Transform::getIdentity()) { pcl::PointXYZRGB ptWhite; ptWhite.r = ptWhite.g = ptWhite.b = 255; targetPoint_->push_back(ptWhite); snapAxes_.push_back(cv::Vec3f(1,0,0)); snapAxes_.push_back(cv::Vec3f(0,1,0)); snapAxes_.push_back(cv::Vec3f(0,0,1)); float quadSize = 0.05f; quadSample_->push_back(pcl::PointXYZ(-quadSize, -quadSize, 0.0f)); quadSample_->push_back(pcl::PointXYZ(quadSize, -quadSize, 0.0f)); quadSample_->push_back(pcl::PointXYZ(quadSize, quadSize, 0.0f)); quadSample_->push_back(pcl::PointXYZ(-quadSize, quadSize, 0.0f)); quadSamplePolygons_[0].vertices.resize(3); quadSamplePolygons_[0].vertices[0] = 0; quadSamplePolygons_[0].vertices[1] = 1; quadSamplePolygons_[0].vertices[2] = 2; quadSamplePolygons_[1].vertices.resize(3); quadSamplePolygons_[1].vertices[0] = 0; quadSamplePolygons_[1].vertices[1] = 2; quadSamplePolygons_[1].vertices[2] = 3; #ifdef __ANDROID__ env->GetJavaVM(&jvm); RTABMapActivity = env->NewGlobalRef(caller_activity); #endif LOGI("RTABMapApp::RTABMapApp()"); #ifdef __ANDROID__ progressionStatus_.setJavaObjects(jvm, RTABMapActivity); #endif main_scene_.setBackgroundColor(backgroundColor_, backgroundColor_, backgroundColor_); logHandler_ = new rtabmap::LogHandler(); this->registerToEventsManager(); LOGI("RTABMapApp::RTABMapApp() end"); #ifdef __ANDROID__ #ifndef DISABLE_LOG start_logger(); #endif #endif } #ifndef __ANDROID__ // __APPLE__ void RTABMapApp::setupSwiftCallbacks(void * classPtr, void(*progressCallback)(void *, int, int), void(*initCallback)(void *, int, const char*), void(*statsUpdatedCallback)(void *, int, int, int, int, float, int, int, int, int, int ,int, float, int, float, int, float, float, float, float, int, int, float, float, float, float, float, float), void(*cameraInfoEventCallback)(void *, int, const char*, const char*)) { swiftClassPtr_ = classPtr; progressionStatus_.setSwiftCallback(classPtr, progressCallback); swiftInitCallback = initCallback; swiftStatsUpdatedCallback = statsUpdatedCallback; swiftCameraInfoEventCallback = cameraInfoEventCallback; } #endif RTABMapApp::~RTABMapApp() { LOGI("~RTABMapApp() begin"); stopCamera(); if(rtabmapThread_) { rtabmapThread_->close(false); } delete rtabmapThread_; delete logHandler_; delete optRefPose_; { boost::mutex::scoped_lock lock(rtabmapMutex_); if(rtabmapEvents_.size()) { for(std::list::iterator iter=rtabmapEvents_.begin(); iter!=rtabmapEvents_.end(); ++iter) { delete *iter; } } rtabmapEvents_.clear(); } LOGI("~RTABMapApp() end"); } void RTABMapApp::setScreenRotation(int displayRotation, int cameraRotation) { rtabmap::ScreenRotation rotation = rtabmap::GetAndroidRotationFromColorCameraToDisplay(displayRotation, cameraRotation); //LOGI("Set orientation: display=%d camera=%d -> %d", displayRotation, cameraRotation, (int)rotation); main_scene_.setScreenRotation(rotation); boost::mutex::scoped_lock lock(cameraMutex_); if(camera_) { camera_->setScreenRotationAndSize(main_scene_.getScreenRotation(), main_scene_.getViewPortWidth(), main_scene_.getViewPortHeight()); } } int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMemory, bool optimize, bool clearDatabase) { LOGW("Opening database %s (inMemory=%d, optimize=%d, clearDatabase=%d)", databasePath.c_str(), databaseInMemory?1:0, optimize?1:0, clearDatabase?1:0); this->unregisterFromEventsManager(); // to ignore published init events when closing rtabmap status_.first = rtabmap::RtabmapEventInit::kInitializing; rtabmapMutex_.lock(); if(rtabmapEvents_.size()) { for(std::list::iterator iter=rtabmapEvents_.begin(); iter!=rtabmapEvents_.end(); ++iter) { delete *iter; } } rtabmapEvents_.clear(); openingDatabase_ = true; bool restartThread = false; if(rtabmapThread_) { restartThread = rtabmapThread_->isRunning(); rtabmapThread_->close(false); delete rtabmapThread_; rtabmapThread_ = 0; rtabmap_ = 0; } totalPoints_ = 0; totalPolygons_ = 0; lastDrawnCloudsCount_ = 0; renderingTime_ = 0.0f; lastPostRenderEventTime_ = 0.0; lastPoseEventTime_ = 0.0; bufferedStatsData_.clear(); graphOptimization_ = true; measuresUpdated_ = !measures_.empty(); measures_.clear(); this->registerToEventsManager(); int status = 0; // Open visualization while we load (if there is an optimized mesh saved in database) optTextureMesh_.reset(new pcl::TextureMesh); optMesh_ = rtabmap::Mesh(); optTexture_ = cv::Mat(); optRefId_ = 0; if(optRefPose_) { delete optRefPose_; optRefPose_ = 0; } visualizingMesh_ = false; cv::Mat cloudMat; std::vector > > polygons; #if PCL_VERSION_COMPARE(>=, 1, 8, 0) std::vector > > texCoords; #else std::vector > texCoords; #endif cv::Mat textures; if(!databasePath.empty() && UFile::exists(databasePath) && !clearDatabase) { UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized cloud/mesh...")); rtabmap::DBDriver * driver = rtabmap::DBDriver::create(); if(driver->openConnection(databasePath)) { cloudMat = driver->loadOptimizedMesh(&polygons, &texCoords, &textures); if(!cloudMat.empty()) { LOGI("Open: Found optimized mesh! Visualizing it."); optTextureMesh_ = rtabmap::util3d::assembleTextureMesh(cloudMat, polygons, texCoords, textures, true); optMesh_ = rtabmap::Mesh(); if(textures.rows > 4096) // Limitation iOS, just for rendering on device { cv::resize(textures, optTexture_, cv::Size(4096, 4096), 0.0f, 0.0f, cv::INTER_AREA); } else { optTexture_ = textures; } if(!optTexture_.empty()) { LOGI("Open: Texture mesh: %dx%d.", optTexture_.cols, optTexture_.rows); status=3; } else if(optTextureMesh_->tex_polygons.size()) { LOGI("Open: Polygon mesh"); status=2; } else if(!optTextureMesh_->cloud.data.empty()) { LOGI("Open: Point cloud"); status=1; } } else { LOGI("Open: No optimized mesh found."); } delete driver; } } if(status > 0) { if(status==1) { UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized cloud...done!")); } else if(status==2) { UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized mesh...done!")); } else { UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized texture mesh...done!")); } boost::mutex::scoped_lock lockRender(renderingMutex_); visualizingMesh_ = true; exportedMeshUpdated_ = true; } UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading database...")); if(clearDatabase) { LOGI("Erasing database \"%s\"...", databasePath.c_str()); UFile::erase(databasePath); } //Rtabmap mapToOdom_.setIdentity(); rtabmap_ = new rtabmap::Rtabmap(); rtabmap::ParametersMap parameters = getRtabmapParameters(); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kDbSqlite3InMemory(), uBool2Str(databaseInMemory && !dataRecorderMode_))); LOGI("Initializing database..."); rtabmap_->init(parameters, databasePath); rtabmapThread_ = new rtabmap::RtabmapThread(rtabmap_); if(parameters.find(rtabmap::Parameters::kRtabmapDetectionRate()) != parameters.end()) { rtabmapThread_->setDetectorRate(uStr2Float(parameters.at(rtabmap::Parameters::kRtabmapDetectionRate()))); } // Generate all meshes std::map signatures; std::map poses; std::multimap links; LOGI("Loading full map from database..."); UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading data from database...")); rtabmap_->getGraph( poses, links, true, false, // Make sure poses are the same than optimized mesh (in case we switched RGBD/OptimizedFromGraphEnd) &signatures, true, true, true, true); if(signatures.size() && poses.empty()) { LOGE("Failed to optimize the graph!"); status = -1; } { LOGI("Creating the meshes (%d)....", (int)poses.size()); boost::mutex::scoped_lock lock(meshesMutex_); createdMeshes_.clear(); int i=0; UTimer addTime; rawPoses_.clear(); for(std::map::iterator iter=poses.begin(); iter!=poses.end() && status>=0; ++iter) { try { int id = iter->first; if(!iter->second.isNull()) { if(uContains(signatures, id)) { UTimer timer; rtabmap::SensorData data = signatures.at(id).sensorData(); rawPoses_.insert(std::make_pair(id, signatures.at(id).getPose())); cv::Mat tmpA, tmpB, tmpC; data.uncompressData(&tmpA, &tmpB, 0, 0, 0, 0, 0, depthConfidence_>0?&tmpC:0); if(!(!data.imageRaw().empty() && !data.depthRaw().empty()) && !data.laserScanCompressed().isEmpty()) { rtabmap::LaserScan scan; data.uncompressData(0, 0, &scan); } if((!data.imageRaw().empty() && !data.depthRaw().empty()) || !data.laserScanRaw().isEmpty()) { // Voxelize and filter depending on the previous cloud? pcl::PointCloud::Ptr cloud; pcl::IndicesPtr indices(new std::vector); if(!data.imageRaw().empty() && !data.depthRaw().empty() && (!useExternalLidar_ || data.laserScanRaw().isEmpty())) { int meshDecimation = updateMeshDecimation(data.depthRaw().cols, data.depthRaw().rows); if(smoothing_ || depthBleedingError_>0.0f) { cv::Mat depth = data.depthRaw(); if(depthBleedingError_ > 0.0f) { rtabmap::util2d::depthBleedingFiltering(depth, depthBleedingError_); } if(smoothing_) { depth = rtabmap::util2d::fastBilateralFiltering(depth, g_bilateralFilteringSigmaS, g_bilateralFilteringSigmaR); } data.setRGBDImage(data.imageRaw(), depth, data.depthConfidenceRaw(), data.cameraModels()); } cloud = rtabmap::util3d::cloudRGBFromSensorData( data, meshDecimation, maxCloudDepth_, minCloudDepth_, indices.get(), rtabmap::ParametersMap(), std::vector(), depthConfidence_); } else { //scan cloud = rtabmap::util3d::laserScanToPointCloudRGB(rtabmap::util3d::commonFiltering(data.laserScanRaw(), 1, minCloudDepth_, maxCloudDepth_), data.laserScanRaw().localTransform(), 255, 255, 255); indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } if(cloud->size() && indices->size()) { std::vector polygons; std::vector polygonsLowRes; #if PCL_VERSION_COMPARE(>=, 1, 8, 0) std::vector > texCoords; #else std::vector texCoords; #endif if(cloud->isOrganized() && main_scene_.isMeshRendering() && main_scene_.isMapRendering()) { polygons = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); #ifndef DISABLE_VTK if(meshDecimationFactor_ > 0.0f && !polygons.empty()) { pcl::PolygonMesh::Ptr tmpMesh(new pcl::PolygonMesh); pcl::toPCLPointCloud2(*cloud, tmpMesh->cloud); tmpMesh->polygons = polygons; rtabmap::util3d::denseMeshPostProcessing(tmpMesh, meshDecimationFactor_, 0, cloud, 0); if(!tmpMesh->polygons.empty()) { if(main_scene_.isMeshTexturing() && main_scene_.isMapRendering()) { std::map cameraPoses; std::map cameraModels; cameraPoses.insert(std::make_pair(0, rtabmap::Transform::getIdentity())); cameraModels.insert(std::make_pair(0, data.cameraModels()[0])); pcl::TextureMesh::Ptr textureMesh = rtabmap::util3d::createTextureMesh( tmpMesh, cameraPoses, cameraModels, std::map()); pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud); polygons = textureMesh->tex_polygons[0]; texCoords = textureMesh->tex_coordinates[0]; } else { pcl::fromPCLPointCloud2(tmpMesh->cloud, *cloud); polygons = tmpMesh->polygons; } indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } else { LOGE("Mesh decimation factor is too high (%f), returning full mesh (id=%d).", meshDecimationFactor_, data.id()); polygonsLowRes = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_+LOW_RES_PIX); } #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Mesh simplication, %d polygons, %d points (%fs)", (int)polygons.size(), (int)cloud->size(), timer.ticks()); #endif } else #endif { polygonsLowRes = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_+LOW_RES_PIX); } } std::pair::iterator, bool> inserted = createdMeshes_.insert(std::make_pair(id, rtabmap::Mesh())); UASSERT(inserted.second); inserted.first->second.cloud = cloud; inserted.first->second.indices = indices; inserted.first->second.polygons = polygons; inserted.first->second.polygonsLowRes = polygonsLowRes; inserted.first->second.visible = true; inserted.first->second.cameraModel = data.cameraModels()[0]; inserted.first->second.gains[0] = 1.0; inserted.first->second.gains[1] = 1.0; inserted.first->second.gains[2] = 1.0; if((cloud->isOrganized() || !texCoords.empty()) && main_scene_.isMeshTexturing() && main_scene_.isMapRendering()) { inserted.first->second.texCoords = texCoords; if(renderingTextureDecimation_>1) { cv::Size reducedSize(data.imageRaw().cols/renderingTextureDecimation_, data.imageRaw().rows/renderingTextureDecimation_); cv::resize(data.imageRaw(), inserted.first->second.texture, reducedSize, 0, 0, cv::INTER_LINEAR); } else { inserted.first->second.texture = data.imageRaw(); } } LOGI("Created cloud %d (%fs, %d points)", id, timer.ticks(), (int)cloud->size()); } else { UWARN("Cloud %d is empty", id); } } else if(!data.depthOrRightCompressed().empty() || !data.laserScanCompressed().isEmpty()) { UERROR("Failed to uncompress data! (rgb=%d, depth=%d, scan=%d)", data.imageCompressed().cols, data.depthOrRightCompressed().cols, data.laserScanCompressed().size()); status=-2; } } else { UWARN("Data for node %d not found", id); } } else { UWARN("Pose %d is null !?", id); } ++i; if(addTime.elapsed() >= 4.0f) { UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, uFormat("Created clouds %d/%d", i, (int)poses.size()))); addTime.restart(); } } catch(const UException & e) { UERROR("Exception! msg=\"%s\"", e.what()); status = -2; } catch (const cv::Exception & e) { UERROR("Exception! msg=\"%s\"", e.what()); status = -2; } catch (const std::exception & e) { UERROR("Exception! msg=\"%s\"", e.what()); status = -2; } } if(status < 0) { createdMeshes_.clear(); rawPoses_.clear(); } else { LOGI("Created %d meshes...", (int)createdMeshes_.size()); } } if(optimize && status>=0) { UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Visual optimization...")); gainCompensation(); LOGI("Polygon filtering..."); boost::mutex::scoped_lock lock(meshesMutex_); UTimer time; for(std::map::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { if(iter->second.polygons.size()) { // filter polygons iter->second.polygons = filterOrganizedPolygons(iter->second.polygons, iter->second.cloud->size()); } } } UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Updating scene...")); LOGI("Open: add rtabmap event to update the scene"); rtabmap::Statistics stats; stats.addStatistic(rtabmap::Statistics::kMemoryWorking_memory_size(), (float)rtabmap_->getWMSize()); stats.addStatistic(rtabmap::Statistics::kKeypointDictionary_size(), (float)rtabmap_->getMemory()->getVWDictionary()->getVisualWords().size()); stats.addStatistic(rtabmap::Statistics::kMemoryDatabase_memory_used(), (float)rtabmap_->getMemory()->getDatabaseMemoryUsed()); stats.setPoses(poses); stats.setConstraints(links); rtabmapEvents_.push_back(new rtabmap::RtabmapEvent(stats)); rtabmap_->setOptimizedPoses(poses, links); // for optimized mesh if(poses.size()) { // just take the last as reference optRefId_ = poses.rbegin()->first; optRefPose_ = new rtabmap::Transform(poses.rbegin()->second); } { boost::mutex::scoped_lock lock(cameraMutex_); if(camera_) { camera_->resetOrigin(); if(dataRecorderMode_) { // Don't update faster than we record, so that we see is what is recorded camera_->setFrameRate(rtabmapThread_->getDetectorRate()); rtabmapThread_->setDetectorRate(0); } else { // set default 10 camera_->setFrameRate(10); } } } UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInitialized, "")); if(restartThread) { rtabmapThread_->registerToEventsManager(); rtabmapThread_->start(); } rtabmapMutex_.unlock(); boost::mutex::scoped_lock lockRender(renderingMutex_); if(poses.empty() || status>0) { openingDatabase_ = false; } clearSceneOnNextRender_ = status<=0; return status; } int RTABMapApp::updateMeshDecimation(int width, int height) { int meshDecimation = 1; if(cloudDensityLevel_ == 3) // very low { if((height >= 480 || width >= 480) && width % 20 == 0 && height % 20 == 0) { meshDecimation = 20; } else if(width % 15 == 0 && height % 15 == 0) { meshDecimation = 15; } else if(width % 10 == 0 && height % 10 == 0) { meshDecimation = 10; } else if(width % 8 == 0 && height % 8 == 0) { meshDecimation = 8; } else { UERROR("Could not set decimation to high (size=%dx%d)", width, height); } } else if(cloudDensityLevel_ == 2) // low { if((height >= 480 || width >= 480) && width % 10 == 0 && height % 10 == 0) { meshDecimation = 10; } else if(width % 5 == 0 && height % 5 == 0) { meshDecimation = 5; } else if(width % 4 == 0 && height % 4 == 0) { meshDecimation = 4; } else { UERROR("Could not set decimation to medium (size=%dx%d)", width, height); } } else if(cloudDensityLevel_ == 1) // high { if((height >= 480 || width >= 480) && width % 5 == 0 && height % 5 == 0) { meshDecimation = 5; } else if(width % 3 == 0 && height % 3 == 0) { meshDecimation = 3; } else if(width % 2 == 0 && height % 2 == 0) { meshDecimation = 2; } else { UERROR("Could not set decimation to low (size=%dx%d)", width, height); } } // else maximum LOGI("Set decimation to %d (image=%dx%d, density level=%d)", meshDecimation, width, height, cloudDensityLevel_); return meshDecimation; } bool RTABMapApp::isBuiltWith(int cameraDriver) const { if(cameraDriver == 0) { #ifdef RTABMAP_TANGO return true; #else return false; #endif } if(cameraDriver == 1) { #ifdef RTABMAP_ARCORE return true; #else return false; #endif } if(cameraDriver == 2) { #ifdef RTABMAP_ARENGINE return true; #else return false; #endif } return false; } #ifdef __ANDROID__ bool RTABMapApp::startCamera(JNIEnv* env, jobject iBinder, jobject context, jobject activity, int driver) #else // __APPLE__ bool RTABMapApp::startCamera() #endif { stopCamera(); //ccapp = new computer_vision::ComputerVisionApplication(); //ccapp->OnResume(env, context, activity); //return true; #ifdef __ANDROID__ cameraDriver_ = driver; #else // __APPLE__ cameraDriver_ = 3; #endif LOGW("startCamera() camera driver=%d", cameraDriver_); boost::mutex::scoped_lock lock(cameraMutex_); if(cameraDriver_ == 0) // Tango { #ifdef RTABMAP_TANGO camera_ = new rtabmap::CameraTango(cameraColor_, !cameraColor_ || fullResolution_?1:2, rawScanSaved_); if (TangoService_setBinder(env, iBinder) != TANGO_SUCCESS) { UERROR("TangoHandler::ConnectTango, TangoService_setBinder error"); delete camera_; camera_ = 0; return false; } #else UERROR("RTAB-Map is not built with Tango support!"); #endif } else if(cameraDriver_ == 1) { #ifdef RTABMAP_ARCORE camera_ = new rtabmap::CameraARCore(env, context, activity, depthFromMotion_, upstreamRelocalizationMaxAcc_); #else UERROR("RTAB-Map is not built with ARCore support!"); #endif } else if(cameraDriver_ == 2) { #ifdef RTABMAP_ARENGINE camera_ = new rtabmap::CameraAREngine(env, context, activity, upstreamRelocalizationMaxAcc_); #else UERROR("RTAB-Map is not built with AREngine support!"); #endif } else if(cameraDriver_ == 3) { camera_ = new rtabmap::CameraMobile(upstreamRelocalizationMaxAcc_); } if(camera_ == 0) { UERROR("Unknown or not supported camera driver! %d", cameraDriver_); return false; } if(rtabmapThread_ && dataRecorderMode_) { // Don't update faster than we record, so that we see is what is recorded camera_->setFrameRate(rtabmapThread_->getDetectorRate()); rtabmapThread_->setDetectorRate(0); } if(camera_->init()) { camera_->setScreenRotationAndSize(main_scene_.getScreenRotation(), main_scene_.getViewPortWidth(), main_scene_.getViewPortHeight()); //update mesh decimation based on camera calibration LOGI("Cloud density level %d", cloudDensityLevel_); LOGI("Start camera thread"); cameraJustInitialized_ = true; if(useExternalLidar_) { #if BOOST_VERSION >= 108700 rtabmap::LidarVLP16 * lidar = new rtabmap::LidarVLP16(boost::asio::ip::make_address("192.168.1.201"), 2368, true); #else rtabmap::LidarVLP16 * lidar = new rtabmap::LidarVLP16(boost::asio::ip::address_v4::from_string("192.168.1.201"), 2368, true); #endif lidar->init(); camera_->setImageRate(0); // if lidar, to get close camera synchronization sensorCaptureThread_ = new rtabmap::SensorCaptureThread(lidar, camera_, camera_, rtabmap::Transform::getIdentity()); sensorCaptureThread_->setScanParameters(false, 1, 0.0f, 0.0f, 0.0f, 0, 0.0f, 0.0f, true); } else { sensorCaptureThread_ = new rtabmap::SensorCaptureThread(camera_); } sensorCaptureThread_->start(); return true; } UERROR("Failed camera initialization!"); return false; } void RTABMapApp::stopCamera() { LOGI("stopCamera()"); { boost::mutex::scoped_lock lock(cameraMutex_); if(sensorCaptureThread_!=0) { camera_->close(); sensorCaptureThread_->join(true); delete sensorCaptureThread_; // camera_ is closed and deleted inside sensorCaptureThread_ = 0; camera_ = 0; } } { boost::mutex::scoped_lock lock(renderingMutex_); delete main_scene_.background_renderer_; main_scene_.background_renderer_ = 0; } } std::vector RTABMapApp::filterOrganizedPolygons( const std::vector & polygons, int cloudSize) const { std::vector vertexToCluster(cloudSize, 0); std::map > clusters; int lastClusterID = 0; for(unsigned int i=0; i0) { clusterID = vertexToCluster[polygons[i].vertices[j]]; break; } } if(clusterID>0) { clusters.at(clusterID).push_back(i); } else { clusterID = ++lastClusterID; std::list polygons; polygons.push_back(i); clusters.insert(std::make_pair(clusterID, polygons)); } for(unsigned int j=0;j >::iterator iter=clusters.begin(); iter!=clusters.end(); ++iter) { //LOGD("cluster %d = %d", iter->first, (int)iter->second.size()); if(iter->second.size() > biggestClusterSize) { biggestClusterSize = iter->second.size(); } } unsigned int minClusterSize = (unsigned int)(float(biggestClusterSize)*clusterRatio_); //LOGI("Biggest cluster %d -> minClusterSize(ratio=%f)=%d", // biggestClusterSize, clusterRatio_, (int)minClusterSize); std::vector filteredPolygons(polygons.size()); int oi = 0; for(std::map >::iterator iter=clusters.begin(); iter!=clusters.end(); ++iter) { if(iter->second.size() >= minClusterSize) { for(std::list::iterator jter=iter->second.begin(); jter!=iter->second.end(); ++jter) { filteredPolygons[oi++] = polygons[*jter]; } } } filteredPolygons.resize(oi); return filteredPolygons; } std::vector RTABMapApp::filterPolygons( const std::vector & polygons, int cloudSize) const { // filter polygons std::vector > neighbors; std::vector > vertexToPolygons; rtabmap::util3d::createPolygonIndexes( polygons, cloudSize, neighbors, vertexToPolygons); std::list > clusters = rtabmap::util3d::clusterPolygons(neighbors); unsigned int biggestClusterSize = 0; for(std::list >::iterator iter=clusters.begin(); iter!=clusters.end(); ++iter) { if(iter->size() > biggestClusterSize) { biggestClusterSize = iter->size(); } } unsigned int minClusterSize = (unsigned int)(float(biggestClusterSize)*clusterRatio_); LOGI("Biggest cluster = %d -> minClusterSize(ratio=%f)=%d", biggestClusterSize, clusterRatio_, (int)minClusterSize); std::vector filteredPolygons(polygons.size()); int oi=0; for(std::list >::iterator jter=clusters.begin(); jter!=clusters.end(); ++jter) { if(jter->size() >= minClusterSize) { for(std::list::iterator kter=jter->begin(); kter!=jter->end(); ++kter) { filteredPolygons[oi++] = polygons.at(*kter); } } } filteredPolygons.resize(oi); return filteredPolygons; } // OpenGL thread void RTABMapApp::InitializeGLContent() { UINFO(""); main_scene_.InitGLContent(); float v = backgroundColor_ == 0.5f?0.4f:1.0f-backgroundColor_; main_scene_.setGridColor(v, v, v); } // OpenGL thread void RTABMapApp::SetViewPort(int width, int height) { main_scene_.SetupViewPort(width, height); boost::mutex::scoped_lock lock(cameraMutex_); if(camera_) { camera_->setScreenRotationAndSize(main_scene_.getScreenRotation(), main_scene_.getViewPortWidth(), main_scene_.getViewPortHeight()); } } class PostRenderEvent : public UEvent { public: PostRenderEvent(rtabmap::RtabmapEvent * event = 0) : rtabmapEvent_(event) { } ~PostRenderEvent() { if(rtabmapEvent_!=0) { delete rtabmapEvent_; } } virtual std::string getClassName() const {return "PostRenderEvent";} const rtabmap::RtabmapEvent * getRtabmapEvent() const {return rtabmapEvent_;} private: rtabmap::RtabmapEvent * rtabmapEvent_; }; // OpenGL thread bool RTABMapApp::smoothMesh(int id, rtabmap::Mesh & mesh) { UTimer t; // reconstruct depth image UASSERT(mesh.indices.get() && mesh.indices->size()); cv::Mat depth = cv::Mat::zeros(mesh.cloud->height, mesh.cloud->width, CV_32FC1); rtabmap::Transform localTransformInv = mesh.cameraModel.localTransform().inverse(); for(unsigned int i=0; isize(); ++i) { int index = mesh.indices->at(i); // FastBilateralFilter works in camera frame if(mesh.cloud->at(index).x > 0) { pcl::PointXYZRGB pt = rtabmap::util3d::transformPoint(mesh.cloud->at(index), localTransformInv); depth.at(index) = pt.z; } } depth = rtabmap::util2d::fastBilateralFiltering(depth, 2.0f, 0.075f); LOGI("smoothMesh() Bilateral filtering of %d, time=%fs", id, t.ticks()); if(!depth.empty() && mesh.indices->size()) { pcl::IndicesPtr newIndices(new std::vector(mesh.indices->size())); int oi = 0; for(unsigned int i=0; isize(); ++i) { int index = mesh.indices->at(i); pcl::PointXYZRGB & pt = mesh.cloud->at(index); pcl::PointXYZRGB newPt = rtabmap::util3d::transformPoint(mesh.cloud->at(index), localTransformInv); if(depth.at(index) > 0) { newPt.z = depth.at(index); newPt = rtabmap::util3d::transformPoint(newPt, mesh.cameraModel.localTransform()); newIndices->at(oi++) = index; } else { newPt.x = newPt.y = newPt.z = std::numeric_limits::quiet_NaN(); } pt.x = newPt.x; pt.y = newPt.y; pt.z = newPt.z; } newIndices->resize(oi); mesh.indices = newIndices; //reconstruct the mesh with smoothed surfaces std::vector polygons; if(main_scene_.isMeshRendering()) { polygons = rtabmap::util3d::organizedFastMesh(mesh.cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); } LOGI("smoothMesh() Reconstructing the mesh of %d, time=%fs", id, t.ticks()); mesh.polygons = polygons; } else { UERROR("smoothMesh() Failed to smooth surface %d", id); return false; } return true; } void RTABMapApp::gainCompensation(bool full) { UTimer tGainCompensation; LOGI("Gain compensation..."); boost::mutex::scoped_lock lock(meshesMutex_); std::map::Ptr > clouds; std::map indices; for(std::map::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { clouds.insert(std::make_pair(iter->first, iter->second.cloud)); indices.insert(std::make_pair(iter->first, iter->second.indices)); } std::map poses; std::multimap links; rtabmap_->getGraph(poses, links, true, true); if(full) { // full compensation links.clear(); for(std::map::Ptr>::const_iterator iter=clouds.begin(); iter!=clouds.end(); ++iter) { int from = iter->first; std::map::Ptr>::const_iterator jter = iter; ++jter; for(;jter!=clouds.end(); ++jter) { int to = jter->first; links.insert(std::make_pair(from, rtabmap::Link(from, to, rtabmap::Link::kUserClosure, poses.at(from).inverse()*poses.at(to)))); } } } UASSERT(maxGainRadius_>0.0f); rtabmap::GainCompensator compensator(maxGainRadius_, 0.0f, 0.01f, 1.0f); if(clouds.size() > 1 && links.size()) { compensator.feed(clouds, indices, links); LOGI("Gain compensation... compute gain: links=%d, time=%fs", (int)links.size(), tGainCompensation.ticks()); } for(std::map::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { if(!iter->second.cloud->empty()) { if(clouds.size() > 1 && links.size()) { compensator.getGain(iter->first, &iter->second.gains[0], &iter->second.gains[1], &iter->second.gains[2]); LOGI("%d mesh has gain %f,%f,%f", iter->first, iter->second.gains[0], iter->second.gains[1], iter->second.gains[2]); } } } LOGI("Gain compensation... applying gain: meshes=%d, time=%fs", (int)createdMeshes_.size(), tGainCompensation.ticks()); } // OpenGL thread int RTABMapApp::Render() { std::list rtabmapEvents; try { if(sensorCaptureThread_ == 0) { // We are not doing continous drawing, just measure single draw fpsTime_.restart(); } #ifdef DEBUG_RENDERING_PERFORMANCE UTimer time; #endif boost::mutex::scoped_lock lock(renderingMutex_); bool notifyDataLoaded = false; bool notifyCameraStarted = false; if(clearSceneOnNextRender_) { visualizingMesh_ = false; } // ARCore and AREngine capture should be done in opengl thread! const float* uvsTransformed = 0; glm::mat4 arProjectionMatrix(0); glm::mat4 arViewMatrix(0); rtabmap::Mesh occlusionMesh; { boost::mutex::scoped_lock lock(cameraMutex_); if(camera_!=0) { if(cameraDriver_ <= 2) { camera_->updateOnRender(); } #ifdef DEBUG_RENDERING_PERFORMANCE LOGD("Camera updateOnRender %fs", time.ticks()); #endif // We check if we are in measuring mode: not visualizing mesh or rtabmap is not started (localization mode) if(main_scene_.background_renderer_ == 0 && camera_->getTextureId() != 0 && (!visualizingMesh_ || !(rtabmapThread_ == 0 || !rtabmapThread_->isRunning()))) { main_scene_.background_renderer_ = new BackgroundRenderer(); main_scene_.background_renderer_->InitializeGlContent(((rtabmap::CameraMobile*)camera_)->getTextureId(), cameraDriver_ <= 2); } if(camera_->uvsInitialized()) { uvsTransformed = ((rtabmap::CameraMobile*)camera_)->uvsTransformed(); ((rtabmap::CameraMobile*)camera_)->getVPMatrices(arViewMatrix, arProjectionMatrix); if(graphOptimization_ && !mapToOdom_.isIdentity()) { rtabmap::Transform mapCorrection = rtabmap::opengl_world_T_rtabmap_world * mapToOdom_ *rtabmap::rtabmap_world_T_opengl_world; arViewMatrix = glm::inverse(rtabmap::glmFromTransform(mapCorrection)*glm::inverse(arViewMatrix)); } } if(!visualizingMesh_ && !dataRecorderMode_ && main_scene_.GetCameraType() == tango_gl::GestureCamera::kFirstPerson) { rtabmap::CameraModel occlusionModel; cv::Mat occlusionImage = ((rtabmap::CameraMobile*)camera_)->getOcclusionImage(&occlusionModel); if(occlusionModel.isValidForProjection()) { pcl::IndicesPtr indices(new std::vector); int meshDecimation = updateMeshDecimation(occlusionImage.cols, occlusionImage.rows); pcl::PointCloud::Ptr cloud = rtabmap::util3d::cloudFromDepth(occlusionImage, occlusionModel, meshDecimation, 0, 0, indices.get()); cloud = rtabmap::util3d::transformPointCloud(cloud, rtabmap::opengl_world_T_rtabmap_world*mapToOdom_*occlusionModel.localTransform()); occlusionMesh.cloud.reset(new pcl::PointCloud()); pcl::copyPointCloud(*cloud, *occlusionMesh.cloud); occlusionMesh.indices = indices; occlusionMesh.polygons = rtabmap::util3d::organizedFastMesh(cloud, 1.0*M_PI/180.0, false, meshTrianglePix_); } else if(!occlusionImage.empty()) { UERROR("invalid occlusionModel: %f %f %f %f %dx%d", occlusionModel.fx(), occlusionModel.fy(), occlusionModel.cx(), occlusionModel.cy(), occlusionModel.imageWidth(), occlusionModel.imageHeight()); } } #ifdef DEBUG_RENDERING_PERFORMANCE LOGD("Update background and occlusion mesh %fs", time.ticks()); #endif } } // process only pose events in visualization mode rtabmap::Transform pose; { boost::mutex::scoped_lock lock(poseMutex_); if(poseEvents_.size()) { pose = poseEvents_.back(); poseEvents_.clear(); } } rtabmap::SensorEvent sensorEvent; { boost::mutex::scoped_lock lock(sensorMutex_); if(sensorEvents_.size()) { LOGI("Process sensor events"); sensorEvent = sensorEvents_.back(); sensorEvents_.clear(); if(cameraJustInitialized_) { notifyCameraStarted = true; cameraJustInitialized_ = false; } } } if(!pose.isNull()) { // update camera pose? if(graphOptimization_ && !mapToOdom_.isIdentity()) { main_scene_.SetCameraPose(rtabmap::opengl_world_T_rtabmap_world*mapToOdom_*pose*rtabmap::optical_T_opengl); } else { main_scene_.SetCameraPose(rtabmap::opengl_world_T_rtabmap_world*pose*rtabmap::optical_T_opengl); } if(sensorCaptureThread_!=0 && cameraJustInitialized_) { notifyCameraStarted = true; cameraJustInitialized_ = false; } lastPoseEventTime_ = UTimer::now(); } if(visualizingMesh_) { if(exportedMeshUpdated_) { main_scene_.clear(); exportedMeshUpdated_ = false; measuresUpdated_ = measures_.size()>0; } if(!main_scene_.hasCloud(g_optMeshId)) { LOGI("Adding optimized mesh to opengl (%d points, %d polygons, %d tex_coords, materials=%d texture=%dx%d)...", optTextureMesh_->cloud.point_step==0?0:(int)optTextureMesh_->cloud.data.size()/optTextureMesh_->cloud.point_step, optTextureMesh_->tex_polygons.size()!=1?0:(int)optTextureMesh_->tex_polygons[0].size(), optTextureMesh_->tex_coordinates.size()!=1?0:(int)optTextureMesh_->tex_coordinates[0].size(), (int)optTextureMesh_->tex_materials.size(), optTexture_.cols, optTexture_.rows); if(optTextureMesh_->tex_polygons.size() && optTextureMesh_->tex_polygons[0].size()) { optMesh_ = rtabmap::Mesh(); optMesh_.gains[0] = optMesh_.gains[1] = optMesh_.gains[2] = 1.0; optMesh_.cloud.reset(new pcl::PointCloud); optMesh_.normals.reset(new pcl::PointCloud); pcl::fromPCLPointCloud2(optTextureMesh_->cloud, *optMesh_.cloud); pcl::fromPCLPointCloud2(optTextureMesh_->cloud, *optMesh_.normals); bool hasColors = false; for(unsigned int i=0; icloud.fields.size(); ++i) { if(optTextureMesh_->cloud.fields[i].name.compare("rgb") == 0) { hasColors = true; break; } } if(!hasColors) { std::uint8_t r = 255, g = 255, b = 255; // White std::uint32_t rgb = ((std::uint32_t)r << 16 | (std::uint32_t)g << 8 | (std::uint32_t)b); for(size_t i=0; isize(); ++i) { optMesh_.cloud->at(i).rgb = *reinterpret_cast(&rgb); } } optMesh_.polygons = optTextureMesh_->tex_polygons[0]; if(optTextureMesh_->tex_coordinates.size()) { optMesh_.texCoords = optTextureMesh_->tex_coordinates[0]; optMesh_.texture = optTexture_; } main_scene_.addMesh(g_optMeshId, optMesh_, rtabmap::opengl_world_T_rtabmap_world, true); } else { pcl::IndicesPtr indices(new std::vector); // null pcl::PointCloud::Ptr cloud(new pcl::PointCloud); pcl::fromPCLPointCloud2(optTextureMesh_->cloud, *cloud); main_scene_.addCloud(g_optMeshId, cloud, indices, rtabmap::opengl_world_T_rtabmap_world); } if(!measures_.empty()) { measuresUpdated_ = true; } } if(camera_ != 0 && (rtabmapThread_ == 0 || !rtabmapThread_->isRunning())) { updateMeasuringState(); } else { main_scene_.removeLine(55555); main_scene_.removeQuad(55555); main_scene_.removeQuad(55556); main_scene_.removeCircle(55555); main_scene_.removeCircle(55556); main_scene_.removeText(55555); main_scene_.removeCloudOrMesh(-99999); measuringTmpPts_.clear(); measuringTmpNormals_.clear(); } if(measuresUpdated_) { std::list measures = measures_; measuresUpdated_ = false; main_scene_.clearLines(); main_scene_.clearTexts(); main_scene_.clearQuads(); main_scene_.clearCircles(); int lineId = 0; int textId = 0; int quadId = 0; int circleId = 0; float quadSize=0.05f; float quadAlpha = 0.3f; tango_gl::Color color(1.0f, 0.0f, 1.0f); tango_gl::Color xColor(1.0f, 0.0f, 0.0f); tango_gl::Color yColor(0.0f, 1.0f, 0.0f); tango_gl::Color zColor(0.0f, 0.0f, 1.0f); float restrictiveSnapThr = 0.9999; for(std::list::iterator iter=measures.begin(); iter!=measures.end(); ++iter) { // Determinate color based on current snap axes tango_gl::Color quadColor = color; bool sameNormal = false; if(iter->n1().dot(iter->n2()) > 0.99) // Same normal, plane to plane { sameNormal = true; Eigen::Vector3f n(iter->n1()[0], iter->n1()[1], iter->n1()[2]); float n1ProdX = n.dot(Eigen::Vector3f(snapAxes_[0][0], snapAxes_[0][1], snapAxes_[0][2])); float n1ProdY = n.dot(Eigen::Vector3f(snapAxes_[1][0], snapAxes_[1][1], snapAxes_[1][2])); float n1ProdZ = n.dot(Eigen::Vector3f(snapAxes_[2][0], snapAxes_[2][1], snapAxes_[2][2])); if(fabs(n1ProdX) > restrictiveSnapThr) { quadColor = xColor; } else if(fabs(n1ProdY) > restrictiveSnapThr) { quadColor = yColor; } else if(fabs(n1ProdZ) > restrictiveSnapThr) { quadColor = zColor; } } const Measure & m = *iter; LOGI("dist=%f, %f,%f,%f -> %f,%f,%f", m.length(), m.pt1().x, m.pt1().y, m.pt1().z, m.pt2().x, m.pt2().y, m.pt2().z); cv::Point3f pt1 = rtabmap::util3d::transformPoint(m.pt1(), rtabmap::opengl_world_T_rtabmap_world); cv::Point3f pt2 = rtabmap::util3d::transformPoint(m.pt2(), rtabmap::opengl_world_T_rtabmap_world); main_scene_.addLine(++lineId, pt1, pt2, quadColor); if (fabs(iter->n1()[2]) < 0.00001 && sameNormal) { // Add a line so that in orthogonal view, we can see better where the lines are starting/finishing cv::Point3f n = cv::Vec3f(0,0,1).cross(iter->n1()); n = rtabmap::util3d::transformPoint(n, rtabmap::opengl_world_T_rtabmap_world) * (quadSize/2); cv::Point3f pa = pt1 + n; cv::Point3f pb = pt1 - n; main_scene_.addLine(++lineId, pa, pb, quadColor); cv::Point3f pc = pt2 + n; cv::Point3f pd = pt2 - n; main_scene_.addLine(++lineId, pc, pd, quadColor); } float diff = m.length(); std::string text = uFormat("%0.2f m", diff); if(!metricSystem_) { static const double METERS_PER_FOOT = 0.3048; static double INCHES_PER_FOOT = 12.0; double lengthInFeet = diff / METERS_PER_FOOT; int feet = (int)lengthInFeet; float inches = (lengthInFeet - feet) * INCHES_PER_FOOT; if(feet > 0) { text = uFormat("%d' %0.1f\"", feet, inches); } else { text = uFormat("%0.1f\"", inches); } } main_scene_.addText(++textId, text, rtabmap::Transform((pt1.x+pt2.x)/2.0f, (pt1.y+pt2.y)/2.0f, (pt1.z+pt2.z)/2.0f, 0, 0,0), measuringTextSize_, quadColor); cv::Vec3f n1 = rtabmap::util3d::transformPoint(m.n1(), rtabmap::opengl_world_T_rtabmap_world); cv::Vec3f n2 = rtabmap::util3d::transformPoint(m.n2(), rtabmap::opengl_world_T_rtabmap_world); Eigen::Quaternionf q1, q2; q1.setFromTwoVectors(Eigen::Vector3f(0,0,1), Eigen::Vector3f(n1[0],n1[1],n1[2])); q2.setFromTwoVectors(Eigen::Vector3f(0,0,1), Eigen::Vector3f(n2[0],n2[1],n2[2])); if(sameNormal) { main_scene_.addQuad(++quadId, quadSize, rtabmap::Transform(pt1.x, pt1.y, pt1.z, q1.x(), q1.y(), q1.z(), q1.w()), quadColor, quadAlpha); main_scene_.addQuad(++quadId, quadSize, rtabmap::Transform(pt2.x, pt2.y, pt2.z, q2.x(), q2.y(), q2.z(), q2.w()), quadColor, quadAlpha); } else { main_scene_.addCircle(++circleId, quadSize/2, rtabmap::Transform(pt1.x, pt1.y, pt1.z, q1.x(), q1.y(), q1.z(), q1.w()), quadColor, quadAlpha); main_scene_.addCircle(++circleId, quadSize/2, rtabmap::Transform(pt2.x, pt2.y, pt2.z, q2.x(), q2.y(), q2.z(), q2.w()), quadColor, quadAlpha); } } } if(!openingDatabase_) { rtabmapMutex_.lock(); rtabmapEvents = rtabmapEvents_; rtabmapEvents_.clear(); rtabmapMutex_.unlock(); if(rtabmapEvents.size()) { const rtabmap::Statistics & stats = rtabmapEvents.back()->getStats(); if(!stats.mapCorrection().isNull()) { mapToOdom_ = stats.mapCorrection(); } std::map::const_iterator iter = stats.poses().find(optRefId_); if(iter != stats.poses().end() && !iter->second.isNull() && optRefPose_) { // adjust opt mesh pose main_scene_.setCloudPose(g_optMeshId, rtabmap::opengl_world_T_rtabmap_world * iter->second * (*optRefPose_).inverse()); } int fastMovement = (int)uValue(stats.data(), rtabmap::Statistics::kMemoryFast_movement(), 0.0f); int loopClosure = (int)uValue(stats.data(), rtabmap::Statistics::kLoopAccepted_hypothesis_id(), 0.0f); int proximityClosureId = int(uValue(stats.data(), rtabmap::Statistics::kProximitySpace_last_detection_id(), 0.0f)); int rejected = (int)uValue(stats.data(), rtabmap::Statistics::kLoopRejectedHypothesis(), 0.0f); int landmark = (int)uValue(stats.data(), rtabmap::Statistics::kLoopLandmark_detected(), 0.0f); if(rtabmapThread_ && rtabmapThread_->isRunning() && loopClosure>0) { main_scene_.setBackgroundColor(0, 0.5f, 0); // green } else if(rtabmapThread_ && rtabmapThread_->isRunning() && proximityClosureId>0) { main_scene_.setBackgroundColor(0.5f, 0.5f, 0); // yellow } else if(rtabmapThread_ && rtabmapThread_->isRunning() && landmark!=0) { if(rejected) { main_scene_.setBackgroundColor(0.5, 0.325f, 0); // dark orange } else { main_scene_.setBackgroundColor(1, 0.65f, 0); // orange } } else if(rtabmapThread_ && rtabmapThread_->isRunning() && rejected>0) { main_scene_.setBackgroundColor(0, 0.2f, 0); // dark green } else if(rtabmapThread_ && rtabmapThread_->isRunning() && fastMovement) { main_scene_.setBackgroundColor(0.2f, 0, 0.2f); // dark magenta } else { main_scene_.setBackgroundColor(backgroundColor_, backgroundColor_, backgroundColor_); } // Update markers for(std::map::const_iterator iter=stats.poses().begin(); iter!=stats.poses().end() && iter->first<0; ++iter) { int id = iter->first; if(main_scene_.hasMarker(id)) { //just update pose main_scene_.setMarkerPose(id, rtabmap::opengl_world_T_rtabmap_world*iter->second); } else { main_scene_.addMarker(id, rtabmap::opengl_world_T_rtabmap_world*iter->second); } } } } //backup state bool isMeshRendering = main_scene_.isMeshRendering(); bool isTextureRendering = main_scene_.isMeshTexturing(); main_scene_.setMeshRendering(main_scene_.hasMesh(g_optMeshId), main_scene_.hasTexture(g_optMeshId)); main_scene_.setFrustumVisible(camera_!=0); lastDrawnCloudsCount_ = main_scene_.Render(uvsTransformed, arViewMatrix, arProjectionMatrix); double fpsTime = fpsTime_.ticks(); if(renderingTime_ < fpsTime) { renderingTime_ = fpsTime; } // revert state main_scene_.setMeshRendering(isMeshRendering, isTextureRendering); if(rtabmapEvents.size()) { // send statistics to GUI if(rtabmapEvents.back()->getStats().refImageId()>0 || !rtabmapEvents.back()->getStats().data().empty()) { UEventsManager::post(new PostRenderEvent(rtabmapEvents.back())); rtabmapEvents.pop_back(); } for(std::list::iterator iter=rtabmapEvents.begin(); iter!=rtabmapEvents.end(); ++iter) { delete *iter; } rtabmapEvents.clear(); lastPostRenderEventTime_ = UTimer::now(); } } else { if(main_scene_.hasCloud(g_optMeshId)) { main_scene_.clear(); optTextureMesh_.reset(new pcl::TextureMesh); optMesh_ = rtabmap::Mesh(); optTexture_ = cv::Mat(); } // should be before clearSceneOnNextRender_ in case database is reset if(!openingDatabase_) { rtabmapMutex_.lock(); rtabmapEvents = rtabmapEvents_; rtabmapEvents_.clear(); rtabmapMutex_.unlock(); if(!clearSceneOnNextRender_ && rtabmapEvents.size()) { boost::mutex::scoped_lock lockMesh(meshesMutex_); if(createdMeshes_.size()) { if(rtabmapEvents.front()->getStats().refImageId()>0 && rtabmapEvents.front()->getStats().refImageId() < createdMeshes_.rbegin()->first) { LOGI("Detected new database! new=%d old=%d", rtabmapEvents.front()->getStats().refImageId(), createdMeshes_.rbegin()->first); clearSceneOnNextRender_ = true; } } } #ifdef DEBUG_RENDERING_PERFORMANCE if(rtabmapEvents.size()) { LOGW("begin and getting rtabmap events %fs", time.ticks()); } #endif } if(clearSceneOnNextRender_) { LOGI("Clearing all rendering data..."); sensorMutex_.lock(); sensorEvents_.clear(); sensorMutex_.unlock(); poseMutex_.lock(); poseEvents_.clear(); poseMutex_.unlock(); main_scene_.clear(); clearSceneOnNextRender_ = false; if(!openingDatabase_) { boost::mutex::scoped_lock lock(meshesMutex_); LOGI("Clearing meshes..."); createdMeshes_.clear(); rawPoses_.clear(); } else { notifyDataLoaded = true; } totalPoints_ = 0; totalPolygons_ = 0; lastDrawnCloudsCount_ = 0; renderingTime_ = 0.0f; lastPostRenderEventTime_ = 0.0; lastPoseEventTime_ = 0.0; bufferedStatsData_.clear(); measuresUpdated_ = !measures_.empty(); measures_.clear(); } // Did we lose OpenGL context? If so, recreate the context; std::set added = main_scene_.getAddedClouds(); added.erase(-1); if(!openingDatabase_) { boost::mutex::scoped_lock lock(meshesMutex_); unsigned int meshes = (unsigned int)createdMeshes_.size(); if(added.size() != meshes) { LOGI("added (%d) != meshes (%d)", (int)added.size(), meshes); boost::mutex::scoped_lock lockRtabmap(rtabmapMutex_); UASSERT(rtabmap_!=0); for(std::map::iterator iter=createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { if(!main_scene_.hasCloud(iter->first) && !iter->second.pose.isNull()) { LOGI("Re-add mesh %d to OpenGL context", iter->first); if(iter->second.cloud->isOrganized() && main_scene_.isMeshRendering() && iter->second.polygons.size() == 0) { iter->second.polygons = rtabmap::util3d::organizedFastMesh(iter->second.cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); iter->second.polygonsLowRes = rtabmap::util3d::organizedFastMesh(iter->second.cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_+LOW_RES_PIX); } if(iter->second.cloud->isOrganized() && main_scene_.isMeshTexturing()) { cv::Mat textureRaw; textureRaw = rtabmap::uncompressImage(rtabmap_->getMemory()->getImageCompressed(iter->first)); if(!textureRaw.empty()) { if(renderingTextureDecimation_ > 1) { cv::Size reducedSize(textureRaw.cols/renderingTextureDecimation_, textureRaw.rows/renderingTextureDecimation_); LOGD("resize image from %dx%d to %dx%d", textureRaw.cols, textureRaw.rows, reducedSize.width, reducedSize.height); cv::resize(textureRaw, iter->second.texture, reducedSize, 0, 0, cv::INTER_LINEAR); } else { iter->second.texture = textureRaw; } } } main_scene_.addMesh(iter->first, iter->second, rtabmap::opengl_world_T_rtabmap_world*iter->second.pose, true); main_scene_.setCloudVisible(iter->first, iter->second.visible); iter->second.texture = cv::Mat(); // don't keep textures in memory } } } } else if(notifyDataLoaded) { rtabmapMutex_.lock(); rtabmapEvents = rtabmapEvents_; rtabmapEvents_.clear(); rtabmapMutex_.unlock(); openingDatabase_ = false; } if(rtabmapEvents.size()) { #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Process rtabmap events %fs", time.ticks()); #else LOGI("Process rtabmap events"); #endif // update buffered signatures std::map bufferedSensorData; if(!dataRecorderMode_) { for(std::list::iterator iter=rtabmapEvents.begin(); iter!=rtabmapEvents.end(); ++iter) { const rtabmap::Statistics & stats = (*iter)->getStats(); // Don't create mesh for the last node added if rehearsal happened or if discarded (small movement) int smallMovement = (int)uValue(stats.data(), rtabmap::Statistics::kMemorySmall_movement(), 0.0f); int fastMovement = (int)uValue(stats.data(), rtabmap::Statistics::kMemoryFast_movement(), 0.0f); int rehearsalMerged = (int)uValue(stats.data(), rtabmap::Statistics::kMemoryRehearsal_merged(), 0.0f); if(!localizationMode_ && stats.getLastSignatureData().id() > 0 && smallMovement == 0 && rehearsalMerged == 0 && fastMovement == 0) { int id = stats.getLastSignatureData().id(); const rtabmap::Signature & s = stats.getLastSignatureData(); if(!trajectoryMode_ && ((!s.sensorData().imageRaw().empty() && !s.sensorData().depthRaw().empty()) || !s.sensorData().laserScanRaw().isEmpty())) { uInsert(bufferedSensorData, std::make_pair(id, s.sensorData())); } uInsert(rawPoses_, std::make_pair(id, s.getPose())); } int loopClosure = (int)uValue(stats.data(), rtabmap::Statistics::kLoopAccepted_hypothesis_id(), 0.0f); int proximityClosureId = int(uValue(stats.data(), rtabmap::Statistics::kProximitySpace_last_detection_id(), 0.0f)); int rejected = (int)uValue(stats.data(), rtabmap::Statistics::kLoopRejectedHypothesis(), 0.0f); int landmark = (int)uValue(stats.data(), rtabmap::Statistics::kLoopLandmark_detected(), 0.0f); if(rtabmapThread_ && rtabmapThread_->isRunning() && loopClosure>0) { main_scene_.setBackgroundColor(0, 0.5f, 0); // green } else if(rtabmapThread_ && rtabmapThread_->isRunning() && proximityClosureId>0) { main_scene_.setBackgroundColor(0.5f, 0.5f, 0); // yellow } else if(rtabmapThread_ && rtabmapThread_->isRunning() && landmark!=0) { main_scene_.setBackgroundColor(1, 0.65f, 0); // orange } else if(rtabmapThread_ && rtabmapThread_->isRunning() && rejected>0) { main_scene_.setBackgroundColor(0, 0.2f, 0); // dark green } else if(rtabmapThread_ && rtabmapThread_->isRunning() && rehearsalMerged>0) { main_scene_.setBackgroundColor(0, 0, 0.2f); // blue } else if(rtabmapThread_ && rtabmapThread_->isRunning() && fastMovement) { main_scene_.setBackgroundColor(0.2f, 0, 0.2f); // dark magenta } else { main_scene_.setBackgroundColor(backgroundColor_, backgroundColor_, backgroundColor_); } } } #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Looking for data to load (%d) %fs", (int)bufferedSensorData.size(), time.ticks()); #endif std::map posesWithMarkers = rtabmapEvents.back()->getStats().poses(); if(!rtabmapEvents.back()->getStats().mapCorrection().isNull()) { mapToOdom_ = rtabmapEvents.back()->getStats().mapCorrection(); } // Transform pose in OpenGL world for(std::map::iterator iter=posesWithMarkers.begin(); iter!=posesWithMarkers.end(); ++iter) { if(!graphOptimization_ && !dataRecorderMode_) { std::map::iterator jter = rawPoses_.find(iter->first); if(jter != rawPoses_.end()) { iter->second = rtabmap::opengl_world_T_rtabmap_world*jter->second; } } else { iter->second = rtabmap::opengl_world_T_rtabmap_world*iter->second; } } std::map poses(posesWithMarkers.lower_bound(0), posesWithMarkers.end()); const std::multimap & links = rtabmapEvents.back()->getStats().constraints(); if(poses.size()) { //update graph main_scene_.updateGraph(poses, links); #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Update graph: %fs", time.ticks()); #endif // update clouds boost::mutex::scoped_lock lock(meshesMutex_); std::set strIds; for(std::map::iterator iter=poses.begin(); iter!=poses.end(); ++iter) { int id = iter->first; if(!iter->second.isNull()) { if(main_scene_.hasCloud(id)) { //just update pose main_scene_.setCloudPose(id, iter->second); main_scene_.setCloudVisible(id, true); std::map::iterator meshIter = createdMeshes_.find(id); UASSERT(meshIter!=createdMeshes_.end()); meshIter->second.pose = rtabmap::opengl_world_T_rtabmap_world.inverse()*iter->second; meshIter->second.visible = true; } else { if(createdMeshes_.find(id) == createdMeshes_.end() && bufferedSensorData.find(id) != bufferedSensorData.end()) { rtabmap::SensorData data = bufferedSensorData.at(id); cv::Mat tmpA, tmpB, tmpC; data.uncompressData(&tmpA, &tmpB, 0, 0, 0, 0, 0, depthConfidence_>0?&tmpC:0); if(!(!data.imageRaw().empty() && !data.depthRaw().empty()) && !data.laserScanCompressed().isEmpty()) { rtabmap::LaserScan scan; data.uncompressData(0, 0, &scan); } #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Decompressing data: %fs", time.ticks()); #endif if((!data.imageRaw().empty() && !data.depthRaw().empty()) || !data.laserScanRaw().isEmpty()) { // Voxelize and filter depending on the previous cloud? pcl::PointCloud::Ptr cloud; pcl::IndicesPtr indices(new std::vector); if(!data.imageRaw().empty() && !data.depthRaw().empty() && (!useExternalLidar_ || data.laserScanRaw().isEmpty())) { int meshDecimation = updateMeshDecimation(data.depthRaw().cols, data.depthRaw().rows); if(smoothing_ || depthBleedingError_>0.0f) { cv::Mat depth = data.depthRaw(); if(depthBleedingError_ > 0.0f) { rtabmap::util2d::depthBleedingFiltering(depth, depthBleedingError_); } if(smoothing_) { depth = rtabmap::util2d::fastBilateralFiltering(depth, g_bilateralFilteringSigmaS, g_bilateralFilteringSigmaR); } data.setRGBDImage(data.imageRaw(), depth, data.depthConfidenceRaw(), data.cameraModels()); } cloud = rtabmap::util3d::cloudRGBFromSensorData(data, meshDecimation, maxCloudDepth_, minCloudDepth_, indices.get(), rtabmap::ParametersMap(), std::vector(), depthConfidence_); } else { //scan cloud = rtabmap::util3d::laserScanToPointCloudRGB(rtabmap::util3d::commonFiltering(data.laserScanRaw(), 1, minCloudDepth_, maxCloudDepth_), data.laserScanRaw().localTransform(), 255, 255, 255); indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Creating node cloud %d (depth=%dx%d rgb=%dx%d, %fs)", id, data.depthRaw().cols, data.depthRaw().rows, data.imageRaw().cols, data.imageRaw().rows, time.ticks()); #endif if(cloud->size() && indices->size()) { std::vector polygons; std::vector polygonsLowRes; #if PCL_VERSION_COMPARE(>=, 1, 8, 0) std::vector > texCoords; #else std::vector texCoords; #endif pcl::PointCloud::Ptr normals(new pcl::PointCloud); if(cloud->isOrganized() && main_scene_.isMeshRendering() && main_scene_.isMapRendering()) { polygons = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Creating mesh, %d polygons (%fs)", (int)polygons.size(), time.ticks()); #endif #ifndef DISABLE_VTK if(meshDecimationFactor_ > 0.0f && !polygons.empty()) { pcl::PolygonMesh::Ptr tmpMesh(new pcl::PolygonMesh); pcl::toPCLPointCloud2(*cloud, tmpMesh->cloud); tmpMesh->polygons = polygons; rtabmap::util3d::denseMeshPostProcessing(tmpMesh, meshDecimationFactor_, 0, cloud, 0); if(!tmpMesh->polygons.empty()) { if(main_scene_.isMeshTexturing() && main_scene_.isMapRendering()) { std::map cameraPoses; std::map cameraModels; cameraPoses.insert(std::make_pair(0, rtabmap::Transform::getIdentity())); cameraModels.insert(std::make_pair(0, data.cameraModels()[0])); pcl::TextureMesh::Ptr textureMesh = rtabmap::util3d::createTextureMesh( tmpMesh, cameraPoses, cameraModels, std::map()); pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud); polygons = textureMesh->tex_polygons[0]; texCoords = textureMesh->tex_coordinates[0]; } else { pcl::fromPCLPointCloud2(tmpMesh->cloud, *cloud); polygons = tmpMesh->polygons; } indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } else { LOGE("Mesh decimation factor is too high (%f), returning full mesh (id=%d).", meshDecimationFactor_, data.id()); polygonsLowRes = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_+LOW_RES_PIX); #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Creating mesh, %d polygons (%fs)", (int)polygons.size(), time.ticks()); #endif } #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Mesh simplication, %d polygons, %d points (%fs)", (int)polygons.size(), (int)cloud->size(), time.ticks()); #endif } else #endif { polygonsLowRes = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_+LOW_RES_PIX); #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Creating mesh, %d polygons (%fs)", (int)polygons.size(), time.ticks()); #endif } } std::pair::iterator, bool> inserted = createdMeshes_.insert(std::make_pair(id, rtabmap::Mesh())); UASSERT(inserted.second); inserted.first->second.cloud = cloud; inserted.first->second.indices = indices; inserted.first->second.polygons = polygons; inserted.first->second.polygonsLowRes = polygonsLowRes; inserted.first->second.visible = true; inserted.first->second.cameraModel = data.cameraModels()[0]; inserted.first->second.gains[0] = 1.0; inserted.first->second.gains[1] = 1.0; inserted.first->second.gains[2] = 1.0; if((cloud->isOrganized() || !texCoords.empty()) && main_scene_.isMeshTexturing() && main_scene_.isMapRendering()) { inserted.first->second.texCoords = texCoords; if(renderingTextureDecimation_ > 1) { cv::Size reducedSize(data.imageRaw().cols/renderingTextureDecimation_, data.imageRaw().rows/renderingTextureDecimation_); cv::resize(data.imageRaw(), inserted.first->second.texture, reducedSize, 0, 0, cv::INTER_LINEAR); #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("resize image from %dx%d to %dx%d (%fs)", data.imageRaw().cols, data.imageRaw().rows, reducedSize.width, reducedSize.height, time.ticks()); #endif } else { inserted.first->second.texture = data.imageRaw(); } } } } } if(createdMeshes_.find(id) != createdMeshes_.end()) { rtabmap::Mesh & mesh = createdMeshes_.at(id); totalPoints_+=mesh.indices->size(); totalPolygons_ += mesh.polygons.size(); mesh.pose = rtabmap::opengl_world_T_rtabmap_world.inverse()*iter->second; main_scene_.addMesh(id, mesh, iter->second, true); #ifdef DEBUG_RENDERING_PERFORMANCE LOGW("Adding mesh to scene: %fs", time.ticks()); #endif mesh.texture = cv::Mat(); // don't keep textures in memory } } } } } //filter poses? if(poses.size() > 2) { if(nodesFiltering_) { for(std::multimap::const_iterator iter=links.begin(); iter!=links.end(); ++iter) { if(iter->second.type() != rtabmap::Link::kNeighbor) { int oldId = iter->second.to()>iter->second.from()?iter->second.from():iter->second.to(); poses.erase(oldId); } } } } if(!poses.empty()) { //update cloud visibility boost::mutex::scoped_lock lock(meshesMutex_); std::set addedClouds = main_scene_.getAddedClouds(); for(std::set::const_iterator iter=addedClouds.begin(); iter!=addedClouds.end(); ++iter) { if(*iter > 0 && poses.find(*iter) == poses.end()) { main_scene_.setCloudVisible(*iter, false); std::map::iterator meshIter = createdMeshes_.find(*iter); UASSERT(meshIter!=createdMeshes_.end()); meshIter->second.visible = false; } } } // Update markers std::set addedMarkers = main_scene_.getAddedMarkers(); for(std::set::const_iterator iter=addedMarkers.begin(); iter!=addedMarkers.end(); ++iter) { if(posesWithMarkers.find(*iter) == posesWithMarkers.end()) { main_scene_.removeMarker(*iter); } } for(std::map::const_iterator iter=posesWithMarkers.begin(); iter!=posesWithMarkers.end() && iter->first<0; ++iter) { int id = iter->first; if(main_scene_.hasMarker(id)) { //just update pose main_scene_.setMarkerPose(id, iter->second); } else { main_scene_.addMarker(id, iter->second); } } } if(dataRecorderMode_ || !rtabmapEvents.size()) { main_scene_.setCloudVisible(-1, odomCloudShown_ && !trajectoryMode_ && sensorCaptureThread_!=0); //just process the last one if(!sensorEvent.info().odomPose.isNull()) { if(odomCloudShown_ && !trajectoryMode_) { if((!sensorEvent.data().imageRaw().empty() && !sensorEvent.data().depthRaw().empty()) || !sensorEvent.data().laserScanRaw().isEmpty()) { pcl::PointCloud::Ptr cloud; pcl::IndicesPtr indices(new std::vector); if(!sensorEvent.data().imageRaw().empty() && !sensorEvent.data().depthRaw().empty() && (!useExternalLidar_ || sensorEvent.data().laserScanRaw().isEmpty())) { int meshDecimation = updateMeshDecimation(sensorEvent.data().depthRaw().cols, sensorEvent.data().depthRaw().rows); cloud = rtabmap::util3d::cloudRGBFromSensorData(sensorEvent.data(), meshDecimation, maxCloudDepth_, minCloudDepth_, indices.get(), rtabmap::ParametersMap(), std::vector(), depthConfidence_); } else { //scan cloud = rtabmap::util3d::laserScanToPointCloudRGB(rtabmap::util3d::commonFiltering(sensorEvent.data().laserScanRaw(), 1, minCloudDepth_, maxCloudDepth_), sensorEvent.data().laserScanRaw().localTransform(), 255, 255, 255); indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } if(cloud->size() && indices->size()) { LOGI("Created odom cloud (rgb=%dx%d depth=%dx%d cloud=%dx%d)", sensorEvent.data().imageRaw().cols, sensorEvent.data().imageRaw().rows, sensorEvent.data().depthRaw().cols, sensorEvent.data().depthRaw().rows, (int)cloud->width, (int)cloud->height); main_scene_.addCloud(-1, cloud, indices, rtabmap::opengl_world_T_rtabmap_world*mapToOdom_*sensorEvent.info().odomPose); main_scene_.setCloudVisible(-1, true); } else { UERROR("Generated cloud is empty!"); } } else { UWARN("Odom data images/scans are empty!"); } } } } if(gainCompensationOnNextRender_>0) { gainCompensation(gainCompensationOnNextRender_==2); for(std::map::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { main_scene_.updateGains(iter->first, iter->second.gains[0], iter->second.gains[1], iter->second.gains[2]); } gainCompensationOnNextRender_ = 0; notifyDataLoaded = true; } if(bilateralFilteringOnNextRender_) { LOGI("Bilateral filtering..."); bilateralFilteringOnNextRender_ = false; boost::mutex::scoped_lock lock(meshesMutex_); for(std::map::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { if(iter->second.cloud->size() && iter->second.indices->size()) { if(smoothMesh(iter->first, iter->second)) { main_scene_.updateMesh(iter->first, iter->second); } } } notifyDataLoaded = true; } if(filterPolygonsOnNextRender_ && clusterRatio_>0.0f) { LOGI("Polygon filtering..."); filterPolygonsOnNextRender_ = false; boost::mutex::scoped_lock lock(meshesMutex_); UTimer time; for(std::map::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter) { if(iter->second.polygons.size()) { // filter polygons iter->second.polygons = filterOrganizedPolygons(iter->second.polygons, iter->second.cloud->size()); main_scene_.updateCloudPolygons(iter->first, iter->second.polygons); } } notifyDataLoaded = true; } main_scene_.setFrustumVisible(camera_!=0); lastDrawnCloudsCount_ = main_scene_.Render(uvsTransformed, arViewMatrix, arProjectionMatrix, occlusionMesh, true); double fpsTime = fpsTime_.ticks(); if(renderingTime_ < fpsTime) { renderingTime_ = fpsTime; } if(rtabmapEvents.size()) { // send statistics to GUI LOGI("New data added to map, rendering time: %fs", renderingTime_); if(rtabmapEvents.back()->getStats().refImageId()>0 || !rtabmapEvents.back()->getStats().data().empty()) { UEventsManager::post(new PostRenderEvent(rtabmapEvents.back())); rtabmapEvents.pop_back(); } for(std::list::iterator iter=rtabmapEvents.begin(); iter!=rtabmapEvents.end(); ++iter) { delete *iter; } rtabmapEvents.clear(); lastPostRenderEventTime_ = UTimer::now(); if(sensorCaptureThread_!=0 && lastPoseEventTime_>0.0 && UTimer::now()-lastPoseEventTime_ > 1.0) { UERROR("TangoPoseEventNotReceived"); UEventsManager::post(new rtabmap::CameraInfoEvent(10, "TangoPoseEventNotReceived", uNumber2Str(UTimer::now()-lastPoseEventTime_, 6))); } } } if(takeScreenshotOnNextRender_) { takeScreenshotOnNextRender_ = false; int w = main_scene_.getViewPortWidth(); int h = main_scene_.getViewPortHeight(); cv::Mat image(h, w, CV_8UC4); glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data); cv::flip(image, image, 0); cv::cvtColor(image, image, cv::COLOR_RGBA2BGRA); cv::Mat roi; if(w>h) { int offset = (w-h)/2; roi = image(cv::Range::all(), cv::Range(offset,offset+h)); } else { int offset = (h-w)/2; roi = image(cv::Range(offset,offset+w), cv::Range::all()); } rtabmapMutex_.lock(); LOGI("Saving screenshot %dx%d...", roi.cols, roi.rows); rtabmap_->getMemory()->savePreviewImage(roi); rtabmapMutex_.unlock(); screenshotReady_.release(); } if((rtabmapThread_==0 || !rtabmapThread_->isRunning()) && lastPostRenderEventTime_ > 0.0) { double interval = UTimer::now() - lastPostRenderEventTime_; double updateInterval = 1.0; if(!openingDatabase_ && rtabmapThread_) { boost::mutex::scoped_lock lock(rtabmapMutex_); if(rtabmapThread_ && rtabmapThread_->getDetectorRate()>0.0f) { updateInterval = 1.0f/rtabmapThread_->getDetectorRate(); } } if(interval >= updateInterval) { if(!openingDatabase_) { // don't send event when we are opening the database (init events already sent) UEventsManager::post(new PostRenderEvent()); } lastPostRenderEventTime_ = UTimer::now(); } } return notifyDataLoaded||notifyCameraStarted?1:0; } catch(const UException & e) { for(std::list::iterator iter=rtabmapEvents.begin(); iter!=rtabmapEvents.end(); ++iter) { delete *iter; } rtabmapEvents.clear(); UERROR("Exception! msg=\"%s\"", e.what()); return -2; } catch(const cv::Exception & e) { for(std::list::iterator iter=rtabmapEvents.begin(); iter!=rtabmapEvents.end(); ++iter) { delete *iter; } rtabmapEvents.clear(); UERROR("Exception! msg=\"%s\"", e.what()); return -1; } catch(const std::exception & e) { for(std::list::iterator iter=rtabmapEvents.begin(); iter!=rtabmapEvents.end(); ++iter) { delete *iter; } rtabmapEvents.clear(); UERROR("Exception! msg=\"%s\"", e.what()); return -2; } } void RTABMapApp::updateMeasuringState() { rtabmap::Transform openglCam = main_scene_.GetOpenGLCameraPose(); rtabmap::Transform rtabmapCam = rtabmap::rtabmap_world_T_opengl_world * openglCam * rtabmap::opengl_world_T_rtabmap_world; Eigen::Vector3f origin(openglCam.x(), openglCam.y(), openglCam.z()); Eigen::Vector3f rtabmapOrigin(rtabmapCam.x(), rtabmapCam.y(), rtabmapCam.z()); rtabmap::Transform v = openglCam.rotation() * rtabmap::Transform(0,0,-1,0,0,0); Eigen::Vector3f dir(v.x(), v.y(), v.z()); v = rtabmapCam.rotation() * rtabmap::Transform(1,0,0,0,0,0); Eigen::Vector3f rtabmapDir(v.x(), v.y(), v.z()); tango_gl::Color color(1.0f, 0.0f, 1.0f); tango_gl::Color xColor(1.0f, 0.0f, 0.0f); // in rtabmap world tango_gl::Color yColor(0.0f, 1.0f, 0.0f); // in rtabmap world tango_gl::Color zColor(0.0f, 0.0f, 1.0f); // in rtabmap world float circleRadius = 0.025; float quadSize=0.05f; float quadAlpha = 0.3f; main_scene_.removeQuad(55555); main_scene_.removeQuad(55556); main_scene_.removeCircle(55555); main_scene_.removeCircle(55556); main_scene_.removeLine(55555); main_scene_.removeText(55555); if(removeMeasureClicked_) { if(!measuringTmpPts_.empty()) { measuringTmpPts_.clear(); measuringTmpNormals_.clear(); } else { bool removed = false; for(std::list::iterator iter=measures_.begin(); iter!=measures_.end() && !removed; ++iter) { const Measure & m = *iter; for(int i=0; i<2;++i) { // intersecting the quad? cv::Point3f pt = i==0?m.pt1():m.pt2(); cv::Point3f n = i==0?m.n1():m.n2(); pt = rtabmap::util3d::transformPoint(pt, rtabmap::opengl_world_T_rtabmap_world); n = rtabmap::util3d::transformPoint(n, rtabmap::opengl_world_T_rtabmap_world); Eigen::Quaternionf q; q.setFromTwoVectors(Eigen::Vector3f(0,0,-1), Eigen::Vector3f(n.x,n.y,n.z)); float dTmp; Eigen::Vector3f nTmp; int indexTmp; if(rtabmap::util3d::intersectRayMesh( origin, dir, *rtabmap::util3d::transformPointCloud(quadSample_, rtabmap::Transform(pt.x, pt.y, pt.z, q.x(), q.y(), q.z(), q.w())), quadSamplePolygons_, false, dTmp, nTmp, indexTmp)) { measures_.erase(iter); removed = true; break; } } } measuresUpdated_ |= removed; } } float distance =0.0f; Eigen::Vector3f n; int index; if(rtabmap::util3d::intersectRayMesh( rtabmapOrigin, rtabmapDir, *optMesh_.cloud, optMesh_.polygons, true, distance, n, index)) { Eigen::Vector3f intersectionPt = origin + dir*distance; cv::Point3f pt(intersectionPt[0], intersectionPt[1], intersectionPt[2]); cv::Point3f normal(n[0], n[1], n[2]); // rtabmap world cv::Point3f normalGl = rtabmap::util3d::transformPoint(normal, rtabmap::opengl_world_T_rtabmap_world); // opengl world tango_gl::Color quadColor = color; float normalProdX = n.dot(Eigen::Vector3f(snapAxes_[0][0], snapAxes_[0][1], snapAxes_[0][2])); float normalProdY = n.dot(Eigen::Vector3f(snapAxes_[1][0], snapAxes_[1][1], snapAxes_[1][2])); float normalProdZ = n.dot(Eigen::Vector3f(snapAxes_[2][0], snapAxes_[2][1], snapAxes_[2][2])); if(fabs(normal.x) > fabs(normal.z) && fabs(normal.y) > fabs(normal.z)) { if(fabs(normalProdX) > snapAxisThr_) { normal.x = snapAxes_[0][0] * (normalProdX>0?1:-1); normal.y = snapAxes_[0][1] * (normalProdX>0?1:-1); normal.z = snapAxes_[0][2] * (normalProdX>0?1:-1); quadColor = xColor; } else if(fabs(normalProdY) > snapAxisThr_) { normal.x = snapAxes_[1][0] * (normalProdY>0?1:-1); normal.y = snapAxes_[1][1] * (normalProdY>0?1:-1); normal.z = snapAxes_[1][2] * (normalProdY>0?1:-1); quadColor = yColor; } else if(measuringMode_ == 0) { // We force to be aligned with xy plane normal.z = 0; float n = cv::norm(normal); normal.x/=n; normal.y/=n; normal.z/=n; } } else if(fabs(normalProdZ) > snapAxisThr_) { normal.x = snapAxes_[2][0] * (normalProdZ>0?1:-1); normal.y = snapAxes_[2][1] * (normalProdZ>0?1:-1); normal.z = snapAxes_[2][2] * (normalProdZ>0?1:-1); quadColor = zColor; } normalGl = rtabmap::util3d::transformPoint(normal, rtabmap::opengl_world_T_rtabmap_world); if(teleportClicked_) { camera_->resetOrigin(rtabmap::Transform(-pt.z, -pt.x, pt.y-Scene::kHeightOffset.y,0,0,0)); } else if(addMeasureClicked_) // Add measure { if((measuringMode_ == 1 || measuringTmpPts_.size()==1)) { if(measuringMode_ == 1) // Height single click { measuringTmpPts_.clear(); measuringTmpNormals_.clear(); normal.x=0; normal.y=0; normal.z=1; normalGl = rtabmap::util3d::transformPoint(normal, rtabmap::opengl_world_T_rtabmap_world); measuringTmpPts_.push_back(pt); measuringTmpNormals_.push_back(normalGl); pt.y = 0; measuringTmpPts_.push_back(pt); measuringTmpNormals_.push_back(normalGl); } else if(measuringMode_ == 0 || measuringMode_ == 2) // Plane to Plane or point to point { if(measuringMode_ == 0) { normalGl = measuringTmpNormals_.front(); // project point on line float n = (pt-measuringTmpPts_.front()).dot(measuringTmpNormals_.front()); pt = measuringTmpPts_.front() + (measuringTmpNormals_.front() * n); } measuringTmpPts_.push_back(pt); measuringTmpNormals_.push_back(normalGl); } // Add measure! const cv::Point3f & pt1 = measuringTmpPts_.at(0); const cv::Point3f & pt2 = measuringTmpPts_.at(1); const cv::Vec3f & n1 = measuringTmpNormals_.at(0); const cv::Vec3f & n2 = measuringTmpNormals_.at(1); Measure measure( rtabmap::util3d::transformPoint(pt1, rtabmap::rtabmap_world_T_opengl_world), rtabmap::util3d::transformPoint(pt2, rtabmap::rtabmap_world_T_opengl_world), rtabmap::util3d::transformPoint(n1, rtabmap::rtabmap_world_T_opengl_world), rtabmap::util3d::transformPoint(n2, rtabmap::rtabmap_world_T_opengl_world)); if(measure.length()>=0.01f) { measures_.push_back(measure); measuringTmpPts_.clear(); measuringTmpNormals_.clear(); measuresUpdated_ = true; } } else { measuringTmpPts_.clear(); measuringTmpNormals_.clear(); // init the first point measuringTmpPts_.push_back(pt); measuringTmpNormals_.push_back(normalGl); } } else if(measuringMode_ >= 0) { // move action if(measuringMode_ == 0 && measuringTmpNormals_.size()==1) { normalGl = measuringTmpNormals_.front(); normal = rtabmap::util3d::transformPoint(normalGl, rtabmap::rtabmap_world_T_opengl_world); n = Eigen::Vector3f(normal.x, normal.y, normal.z); float normalProdX = n.dot(Eigen::Vector3f(snapAxes_[0][0], snapAxes_[0][1], snapAxes_[0][2])); float normalProdY = n.dot(Eigen::Vector3f(snapAxes_[1][0], snapAxes_[1][1], snapAxes_[1][2])); float normalProdZ = n.dot(Eigen::Vector3f(snapAxes_[2][0], snapAxes_[2][1], snapAxes_[2][2])); if(fabs(normalProdX) > snapAxisThr_) { quadColor = xColor; } else if(fabs(normalProdY) > snapAxisThr_) { quadColor = yColor; } else if(fabs(normalProdZ) > snapAxisThr_) { quadColor = zColor; } else { quadColor = color; } } float quadWidthLeft = 0.05; float quadWidthRight = quadWidthLeft; float quadHeightBottom = quadWidthLeft; float quadHeightTop = quadWidthLeft; cv::Point3f pt2 = pt; float lineLength = 0.0f; // project point on line if(measuringTmpPts_.size() == 1) { cv::Point3f v = pt2-measuringTmpPts_.front(); if(measuringMode_ == 0) { float n = v.dot(measuringTmpNormals_.front()); lineLength = fabs(n); pt2 = measuringTmpPts_.front() + (measuringTmpNormals_.front() * n); v = rtabmap::util3d::transformPoint(v, rtabmap::rtabmap_world_T_opengl_world); if(!(fabs(normal.z) > fabs(normal.x) && fabs(normal.z) > fabs(normal.y))) { quadHeightTop = v.z>quadSize?v.z:quadSize; quadHeightBottom = -v.z>quadSize?-v.z:quadSize; if(fabs(normal.x) > fabs(normal.y)) { cv::Point3f y = cv::Point3f(0,0,1).cross(normal); float n = v.dot(y); quadWidthRight = n>quadSize?n:quadSize; quadWidthLeft = n<-quadSize?fabs(n):quadSize; if(normal.x>0) { quadWidthLeft = n>quadSize?n:quadSize; quadWidthRight = n<-quadSize?fabs(n):quadSize; float tmp =quadHeightTop; quadHeightTop= quadHeightBottom; quadHeightBottom = tmp; } } else { cv::Point3f x = normal.cross(cv::Point3f(0,0,1)); float n = v.dot(x); quadWidthRight = n<-quadSize?fabs(n):quadSize; quadWidthLeft = n>quadSize?n:quadSize; } } else { if(normal.z>0) { quadHeightBottom = v.x<-quadSize?fabs(v.x):quadSize; quadHeightTop = v.x>quadSize?v.x:quadSize; quadWidthRight = v.y<-quadSize?fabs(v.y):quadSize; quadWidthLeft = v.y>quadSize?v.y:quadSize; } else { quadHeightTop = v.x<-quadSize?fabs(v.x):quadSize; quadHeightBottom = v.x>quadSize?v.x:quadSize; quadWidthRight = v.y<-quadSize?fabs(v.y):quadSize; quadWidthLeft = v.y>quadSize?v.y:quadSize; } } } else { lineLength = cv::norm(v); } std::string text = uFormat("%0.2f m", lineLength); if(!metricSystem_) { static const double METERS_PER_FOOT = 0.3048; static double INCHES_PER_FOOT = 12.0; double lengthInFeet = lineLength / METERS_PER_FOOT; int feet = (int)lengthInFeet; float inches = (lengthInFeet - feet) * INCHES_PER_FOOT; if(feet > 0) { text = uFormat("%d' %0.1f\"", feet, inches); } else { text = uFormat("%0.1f\"", inches); } } main_scene_.addText(55555, text, rtabmap::Transform(pt.x, pt.y, pt.z, 0,0,0), 0.05f, measuringMode_ == 0?quadColor:color); } Eigen::Quaternionf q; q.setFromTwoVectors(Eigen::Vector3f(0,0,1), Eigen::Vector3f(normalGl.x,normalGl.y,normalGl.z)); if(measuringTmpPts_.size() == 1 && measuringMode_ != 1) { const cv::Point3f & pt1 = measuringTmpPts_.at(0); main_scene_.addLine(55555, pt1, pt2, measuringMode_ == 0?quadColor:color); if(measuringMode_ == 2) { // Use respective orientation for each circle Eigen::Quaternionf q1; cv::Vec3f normalGL1 = measuringTmpNormals_.front(); q1.setFromTwoVectors(Eigen::Vector3f(0,0,1), Eigen::Vector3f(normalGL1[0],normalGL1[0],normalGL1[0])); main_scene_.addCircle(55555, circleRadius, rtabmap::Transform( measuringTmpPts_.front().x, measuringTmpPts_.front().y, measuringTmpPts_.front().z, q1.x(),q1.y(), q1.z(), q1.w()), color, quadAlpha); main_scene_.addCircle(55556, circleRadius, rtabmap::Transform(pt2.x, pt2.y, pt2.z, q.x(),q.y(), q.z(), q.w()), color, quadAlpha); } else { // Use same orientation for both quads main_scene_.addQuad(55555, quadSize, rtabmap::Transform( measuringTmpPts_.front().x, measuringTmpPts_.front().y, measuringTmpPts_.front().z, q.x(),q.y(), q.z(), q.w()), quadColor, quadAlpha); main_scene_.addQuad(55556, quadWidthLeft, quadWidthRight, quadHeightBottom, quadHeightTop, rtabmap::Transform(pt2.x, pt2.y, pt2.z, q.x(),q.y(), q.z(), q.w()), quadColor, quadAlpha); } } else if(measuringMode_ == 2) { main_scene_.addCircle(55555, circleRadius, rtabmap::Transform(pt2.x, pt2.y, pt2.z, q.x(),q.y(), q.z(), q.w()), color, quadAlpha); } else { main_scene_.addQuad(55555, quadSize, rtabmap::Transform(pt2.x, pt2.y, pt2.z, q.x(),q.y(), q.z(), q.w()), quadColor, quadAlpha); } } } Eigen::Vector3f target = origin+dir*(distance<100.0f?distance:0.5f); rtabmap::Transform pose(target[0], target[1], target[2], 0,0,0); if(main_scene_.hasCloud(-99999)) { main_scene_.setCloudPose(-99999, pose); } else { main_scene_.addCloud(-99999, targetPoint_, pcl::IndicesPtr(), pose); } // reset states removeMeasureClicked_ = false; addMeasureClicked_ = false; teleportClicked_ = false; } void RTABMapApp::SetCameraType( tango_gl::GestureCamera::CameraType camera_type) { main_scene_.SetCameraType(camera_type); } void RTABMapApp::OnTouchEvent(int touch_count, tango_gl::GestureCamera::TouchEvent event, float x0, float y0, float x1, float y1) { main_scene_.OnTouchEvent(touch_count, event, x0, y0, x1, y1); } void RTABMapApp::setPausedMapping(bool paused) { { boost::mutex::scoped_lock lock(renderingMutex_); main_scene_.setBackgroundColor(backgroundColor_, backgroundColor_, backgroundColor_); } if(rtabmapThread_) { if(rtabmapThread_->isRunning() && paused) { LOGW("Pause!"); rtabmapThread_->unregisterFromEventsManager(); rtabmapThread_->join(true); } else if(!rtabmapThread_->isRunning() && !paused) { LOGW("Resume!"); rtabmap_->triggerNewMap(); rtabmap_->parseParameters(getRtabmapParameters()); rtabmapThread_->registerToEventsManager(); rtabmapThread_->start(); } } } void RTABMapApp::setOnlineBlending(bool enabled) { main_scene_.setBlending(enabled); } void RTABMapApp::setMapCloudShown(bool shown) { main_scene_.setMapRendering(shown); } void RTABMapApp::setOdomCloudShown(bool shown) { odomCloudShown_ = shown; main_scene_.setTraceVisible(shown); } void RTABMapApp::setMeshRendering(bool enabled, bool withTexture) { main_scene_.setMeshRendering(enabled, withTexture); } void RTABMapApp::setPointSize(float value) { main_scene_.setPointSize(value); } void RTABMapApp::setFOV(float angle) { main_scene_.setFOV(angle); } void RTABMapApp::setOrthoCropFactor(float value) { main_scene_.setOrthoCropFactor(value); } void RTABMapApp::setGridRotation(float value) { // Update measuring snap axes rtabmap::Transform rotation(0,0, value * DEGREE_2_RADIANS); renderingMutex_.lock(); snapAxes_[0] = rtabmap::util3d::transformPoint(cv::Vec3f(1,0,0), rotation); snapAxes_[1] = rtabmap::util3d::transformPoint(cv::Vec3f(0,1,0), rotation); measuresUpdated_ = true; renderingMutex_.unlock(); // Update grid main_scene_.setGridRotation(value); } void RTABMapApp::setLighting(bool enabled) { main_scene_.setLighting(enabled); } void RTABMapApp::setBackfaceCulling(bool enabled) { main_scene_.setBackfaceCulling(enabled); } void RTABMapApp::setWireframe(bool enabled) { main_scene_.setWireframe(enabled); } void RTABMapApp::setTextureColorSeamsHidden(bool hidden) { main_scene_.setTextureColorSeamsHidden(hidden); } void RTABMapApp::setLocalizationMode(bool enabled) { localizationMode_ = enabled; rtabmap::ParametersMap parameters; parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapStartNewMapOnLoopClosure(), uBool2Str(!localizationMode_ && appendMode_))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemIncrementalMemory(), uBool2Str(!localizationMode_))); this->post(new rtabmap::ParamEvent(parameters)); } void RTABMapApp::setTrajectoryMode(bool enabled) { trajectoryMode_ = enabled; this->post(new rtabmap::ParamEvent(rtabmap::Parameters::kMemBinDataKept(), uBool2Str(!trajectoryMode_))); } void RTABMapApp::setGraphOptimization(bool enabled) { graphOptimization_ = enabled; if((sensorCaptureThread_ == 0) && rtabmap_ && rtabmap_->getMemory()->getLastWorkingSignature()!=0) { std::map poses; std::multimap links; rtabmap_->getGraph(poses, links, true, true); if(poses.size()) { boost::mutex::scoped_lock lock(rtabmapMutex_); rtabmap::Statistics stats = rtabmap_->getStatistics(); stats.setPoses(poses); stats.setConstraints(links); LOGI("Send rtabmap event to update graph..."); rtabmapEvents_.push_back(new rtabmap::RtabmapEvent(stats)); rtabmap_->setOptimizedPoses(poses, links); } } } void RTABMapApp::setNodesFiltering(bool enabled) { nodesFiltering_ = enabled; setGraphOptimization(graphOptimization_); // this will resend the graph if paused } void RTABMapApp::setGraphVisible(bool visible) { main_scene_.setGraphVisible(visible); main_scene_.setTraceVisible(visible); setGraphOptimization(graphOptimization_); // this will republish the graph } void RTABMapApp::setGridVisible(bool visible) { main_scene_.setGridVisible(visible); } void RTABMapApp::setRawScanSaved(bool enabled) { if(rawScanSaved_ != enabled) { rawScanSaved_ = enabled; } } void RTABMapApp::setCameraColor(bool enabled) { if(cameraColor_ != enabled) { cameraColor_ = enabled; } } void RTABMapApp::setFullResolution(bool enabled) { if(fullResolution_ != enabled) { fullResolution_ = enabled; } } void RTABMapApp::setSmoothing(bool enabled) { if(smoothing_ != enabled) { smoothing_ = enabled; } } void RTABMapApp::setDepthBleedingError(float value) { depthBleedingError_ = value; } void RTABMapApp::setDepthFromMotion(bool enabled) { if(depthFromMotion_ != enabled) { depthFromMotion_ = enabled; } } void RTABMapApp::setAppendMode(bool enabled) { if(appendMode_ != enabled) { appendMode_ = enabled; rtabmap::ParametersMap parameters; parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapStartNewMapOnLoopClosure(), uBool2Str(!localizationMode_ && appendMode_))); this->post(new rtabmap::ParamEvent(parameters)); } } void RTABMapApp::setUpstreamRelocalizationAccThr(float value) { upstreamRelocalizationMaxAcc_ = value; } void RTABMapApp::setDataRecorderMode(bool enabled) { if(dataRecorderMode_ != enabled) { dataRecorderMode_ = enabled; // parameters will be set when resuming (we assume we are paused) if(localizationMode_ && enabled) { localizationMode_ = false; } } } void RTABMapApp::setMaxCloudDepth(float value) { maxCloudDepth_ = value; } void RTABMapApp::setMinCloudDepth(float value) { minCloudDepth_ = value; } void RTABMapApp::setCloudDensityLevel(int value) { cloudDensityLevel_ = value; } void RTABMapApp::setMeshAngleTolerance(float value) { meshAngleToleranceDeg_ = value; } void RTABMapApp::setMeshDecimationFactor(float value) { meshDecimationFactor_ = value; } void RTABMapApp::setMeshTriangleSize(int value) { meshTrianglePix_ = value; } void RTABMapApp::setClusterRatio(float value) { clusterRatio_ = value; } void RTABMapApp::setMaxGainRadius(float value) { maxGainRadius_ = value; } void RTABMapApp::setRenderingTextureDecimation(int value) { UASSERT(value>=1); renderingTextureDecimation_ = value; } void RTABMapApp::setBackgroundColor(float gray) { backgroundColor_ = gray; float v = backgroundColor_ == 0.5f?0.4f:1.0f-backgroundColor_; main_scene_.setGridColor(v, v, v); main_scene_.setBackgroundColor(backgroundColor_, backgroundColor_, backgroundColor_); } void RTABMapApp::setDepthConfidence(int value) { depthConfidence_ = value*50; // [0,2] -> [0,100] if(depthConfidence_>100) { depthConfidence_ = 100; } } void RTABMapApp::setExportPointCloudFormat(const std::string & format) { #if defined(RTABMAP_PDAL) || defined(RTABMAP_LIBLAS) if(format == "las" || format == "laz") { exportPointCloudFormat_ = format; } else #endif if(format != "ply") { UERROR("Not supported point cloud format %s", format.c_str()); } else { exportPointCloudFormat_ = format; } } int RTABMapApp::setMappingParameter(const std::string & key, const std::string & value) { std::string compatibleKey = key; // Backward compatibility std::map >::const_iterator iter=rtabmap::Parameters::getRemovedParameters().find(key); if(iter != rtabmap::Parameters::getRemovedParameters().end()) { if(iter->second.first) { // can be migrated compatibleKey = iter->second.second; LOGW("Parameter name changed: \"%s\" -> \"%s\". Please update the code accordingly. Value \"%s\" is still set to the new parameter name.", iter->first.c_str(), iter->second.second.c_str(), value.c_str()); } else { if(iter->second.second.empty()) { UERROR("Parameter \"%s\" doesn't exist anymore!", iter->first.c_str()); } else { UERROR("Parameter \"%s\" doesn't exist anymore! You may look at this similar parameter: \"%s\"", iter->first.c_str(), iter->second.second.c_str()); } } } if(rtabmap::Parameters::getDefaultParameters().find(compatibleKey) != rtabmap::Parameters::getDefaultParameters().end()) { LOGI("%s", uFormat("Setting param \"%s\" to \"%s\"", compatibleKey.c_str(), value.c_str()).c_str()); uInsert(mappingParameters_, rtabmap::ParametersPair(compatibleKey, value)); UEventsManager::post(new rtabmap::ParamEvent(this->getRtabmapParameters())); return 0; } else { UERROR(uFormat("Key \"%s\" doesn't exist!", compatibleKey.c_str()).c_str()); return -1; } } void RTABMapApp::setGPS(const rtabmap::GPS & gps) { boost::mutex::scoped_lock lock(cameraMutex_); if(camera_!=0) { camera_->setGPS(gps); } } void RTABMapApp::addEnvSensor(int type, float value) { boost::mutex::scoped_lock lock(cameraMutex_); if(camera_!=0) { camera_->addEnvSensor(type, value); } } void RTABMapApp::save(const std::string & databasePath) { LOGI("Saving database to %s", databasePath.c_str()); rtabmapThread_->unregisterFromEventsManager(); rtabmapThread_->join(true); LOGI("Taking screenshot..."); takeScreenshotOnNextRender_ = true; if(!screenshotReady_.acquire(1, 2000)) { UERROR("Failed to take a screenshot after 2 sec!"); } // save mapping parameters in the database bool appendModeBackup = appendMode_; if(appendMode_) { appendMode_ = false; } bool dataRecorderModeBackup = dataRecorderMode_; if(dataRecorderMode_) { dataRecorderMode_ = false; } bool localizationModeBackup = localizationMode_; if(localizationMode_) { localizationMode_ = false; } if(appendModeBackup || dataRecorderModeBackup || localizationModeBackup) { rtabmap::ParametersMap parameters = getRtabmapParameters(); rtabmap_->parseParameters(parameters); appendMode_ = appendModeBackup; dataRecorderMode_ = dataRecorderModeBackup; localizationMode_ = localizationModeBackup; } std::map poses = rtabmap_->getLocalOptimizedPoses(); std::multimap links = rtabmap_->getLocalConstraints(); rtabmap_->close(true, databasePath); rtabmap_->init(getRtabmapParameters(), dataRecorderMode_?"":databasePath); if(dataRecorderMode_) { clearSceneOnNextRender_ = true; } else { rtabmap_->setOptimizedPoses(poses, links); } } bool RTABMapApp::recover(const std::string & from, const std::string & to) { std::string errorMsg; if(!databaseRecovery(from, false, &errorMsg, &progressionStatus_)) { LOGE("Recovery Error: %s", errorMsg.c_str()); return false; } else { LOGI("Renaming %s to %s", from.c_str(), to.c_str()); if(UFile::rename(from, to) != 0) { LOGE("Failed renaming %s to %s", from.c_str(), to.c_str()); return false; } return true; } } void RTABMapApp::cancelProcessing() { UWARN("Processing canceled!"); progressionStatus_.setCanceled(true); } bool RTABMapApp::exportMesh( float cloudVoxelSize, bool regenerateCloud, bool meshing, int textureSize, int textureCount, int normalK, bool optimized, float optimizedVoxelSize, int optimizedDepth, int optimizedMaxPolygons, float optimizedColorRadius, bool optimizedCleanWhitePolygons, int optimizedMinClusterSize, float optimizedMaxTextureDistance, int optimizedMinTextureClusterSize, int textureVertexColorPolicy, bool blockRendering) { // make sure createdMeshes_ is not modified while exporting! We don't // lock the meshesMutex_ because we want to continue rendering. std::map poses = rtabmap_->getLocalOptimizedPoses(); if(poses.empty()) { // look if we just triggered new map without localizing afterward (pause/resume in append Mode) std::multimap links; rtabmap_->getGraph( poses, links, true, false); if(poses.empty()) { UERROR("Empty optimized poses!"); return false; } rtabmap_->setOptimizedPoses(poses, links); } if(blockRendering) { renderingMutex_.lock(); main_scene_.clear(); } exporting_ = true; bool success = false; try { int totalSteps = 0; totalSteps+=poses.size(); // assemble if(meshing) { if(optimized) { totalSteps += poses.size(); // meshing if(textureSize > 0) { totalSteps += 1; // gain totalSteps += 1; // blending if(optimizedMaxPolygons > 0) { totalSteps += 1; // decimation } } totalSteps += 1; // texture/coloring if(textureSize > 0) { totalSteps+=poses.size()+1; // texture cameras + apply polygons } } if(textureSize>0) { totalSteps += poses.size()+1; // uncompress and merge textures } } totalSteps += 1; // save file progressionStatus_.reset(totalSteps); //Assemble the meshes if(meshing) // Mesh or Texture Mesh { pcl::PolygonMesh::Ptr polygonMesh(new pcl::PolygonMesh); pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh); std::vector > vertexToPixels; cv::Mat globalTextures; int totalPolygons = 0; { if(optimized) { std::map cameraPoses; std::map cameraModels; std::map cameraDepths; UTimer timer; LOGI("Assemble clouds (%d)...", (int)poses.size()); #ifndef DISABLE_LOG int cloudCount=0; #endif pcl::PointCloud::Ptr mergedClouds(new pcl::PointCloud); for(std::map::iterator iter=poses.begin(); iter!= poses.end(); ++iter) { std::map::iterator jter = createdMeshes_.find(iter->first); pcl::PointCloud::Ptr cloud(new pcl::PointCloud); pcl::IndicesPtr indices(new std::vector); rtabmap::CameraModel model; cv::Mat depth; float gains[3]; gains[0] = gains[1] = gains[2] = 1.0f; if(jter != createdMeshes_.end() && (jter->second.polygons.empty() || meshDecimationFactor_ == 0.0f)) { cloud = jter->second.cloud; indices = jter->second.indices; model = jter->second.cameraModel; gains[0] = jter->second.gains[0]; gains[1] = jter->second.gains[1]; gains[2] = jter->second.gains[2]; rtabmap::SensorData data = rtabmap_->getMemory()->getNodeData(iter->first, true, false, false, false); data.uncompressData(0, &depth); } else { rtabmap::SensorData data = rtabmap_->getMemory()->getNodeData(iter->first, true, false, false, false); data.uncompressData(); if(!data.imageRaw().empty() && !data.depthRaw().empty() && data.cameraModels().size() == 1) { int meshDecimation = updateMeshDecimation(data.depthRaw().cols, data.depthRaw().rows); if(smoothing_ || depthBleedingError_>0.0f) { cv::Mat depth = data.depthRaw(); if(depthBleedingError_ > 0.0f) { rtabmap::util2d::depthBleedingFiltering(depth, depthBleedingError_); } if(smoothing_) { depth = rtabmap::util2d::fastBilateralFiltering(depth, g_bilateralFilteringSigmaS, g_bilateralFilteringSigmaR); } data.setRGBDImage(data.imageRaw(), depth, data.depthConfidenceRaw(), data.cameraModels()); } cloud = rtabmap::util3d::cloudRGBFromSensorData(data, meshDecimation, maxCloudDepth_, minCloudDepth_, indices.get()); model = data.cameraModels()[0]; depth = data.depthRaw(); } } if(cloud->size() && indices->size() && model.isValidForProjection()) { pcl::PointCloud::Ptr transformedCloud(new pcl::PointCloud); if(optimizedVoxelSize > 0.0f) { transformedCloud = rtabmap::util3d::voxelize(cloud, indices, optimizedVoxelSize); transformedCloud = rtabmap::util3d::transformPointCloud(transformedCloud, iter->second); } else { // it looks like that using only transformPointCloud with indices // flushes the colors, so we should extract points before... maybe a too old PCL version pcl::copyPointCloud(*cloud, *indices, *transformedCloud); transformedCloud = rtabmap::util3d::transformPointCloud(transformedCloud, iter->second); } Eigen::Vector3f viewpoint( iter->second.x(), iter->second.y(), iter->second.z()); pcl::PointCloud::Ptr normals = rtabmap::util3d::computeNormals(transformedCloud, normalK, 0.0f, viewpoint); pcl::PointCloud::Ptr cloudWithNormals(new pcl::PointCloud); pcl::concatenateFields(*transformedCloud, *normals, *cloudWithNormals); if(textureSize == 0 && (gains[0] != 1.0 || gains[1] != 1.0 || gains[2] != 1.0)) { for(unsigned int i=0; isize(); ++i) { pcl::PointXYZRGBNormal & pt = cloudWithNormals->at(i); pt.r = uchar(std::max(0.0, std::min(255.0, double(pt.r) * gains[0]))); pt.g = uchar(std::max(0.0, std::min(255.0, double(pt.g) * gains[1]))); pt.b = uchar(std::max(0.0, std::min(255.0, double(pt.b) * gains[2]))); } } if(mergedClouds->size() == 0) { *mergedClouds = *cloudWithNormals; } else { *mergedClouds += *cloudWithNormals; } cameraPoses.insert(std::make_pair(iter->first, iter->second)); cameraModels.insert(std::make_pair(iter->first, model)); if(!depth.empty()) { cameraDepths.insert(std::make_pair(iter->first, depth)); } LOGI("Assembled %d points (%d/%d total=%d)", (int)cloudWithNormals->size(), ++cloudCount, (int)poses.size(), (int)mergedClouds->size()); } else { UERROR("Cloud %d not found or empty", iter->first); } if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } progressionStatus_.increment(); } LOGI("Assembled clouds (%d)... done! %fs (total points=%d)", (int)cameraPoses.size(), timer.ticks(), (int)mergedClouds->size()); if(mergedClouds->size()>=3) { if(optimizedDepth == 0) { Eigen::Vector4f min,max; pcl::getMinMax3D(*mergedClouds, min, max); float mapLength = uMax3(max[0]-min[0], max[1]-min[1], max[2]-min[2]); optimizedDepth = 12; for(int i=6; i<12; ++i) { if(mapLength/float(1< poisson; poisson.setDepth(optimizedDepth); poisson.setInputCloud(mergedClouds); poisson.reconstruct(*mesh); LOGI("Mesh reconstruction... done! %fs (%d polygons)", timer.ticks(), (int)mesh->polygons.size()); if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } progressionStatus_.increment(poses.size()); if(mesh->polygons.size()) { totalPolygons=(int)mesh->polygons.size(); if(optimizedMaxPolygons > 0 && optimizedMaxPolygons < (int)mesh->polygons.size()) { #ifndef DISABLE_VTK unsigned int count = mesh->polygons.size(); float factor = 1.0f-float(optimizedMaxPolygons)/float(count); LOGI("Mesh decimation (max polygons %d/%d -> factor=%f)...", optimizedMaxPolygons, (int)count, factor); progressionStatus_.setMax(progressionStatus_.getMax() + optimizedMaxPolygons/10000); pcl::PolygonMesh::Ptr output(new pcl::PolygonMesh); pcl::MeshQuadricDecimationVTK mqd; mqd.setTargetReductionFactor(factor); mqd.setInputMesh(mesh); mqd.process (*output); mesh = output; //mesh = rtabmap::util3d::meshDecimation(mesh, decimationFactor); // use direct instantiation above to this fix some linker errors on android like: // pcl::MeshQuadricDecimationVTK::performProcessing(pcl::PolygonMesh&): error: undefined reference to 'vtkQuadricDecimation::New()' // pcl::VTKUtils::mesh2vtk(pcl::PolygonMesh const&, vtkSmartPointer&): error: undefined reference to 'vtkFloatArray::New()' LOGI("Mesh decimated (factor=%f) from %d to %d polygons (%fs)", factor, count, (int)mesh->polygons.size(), timer.ticks()); if(count < mesh->polygons.size()) { UWARN("Decimated mesh has more polygons than before!"); } #else UWARN("RTAB-Map is not built with PCL-VTK module so mesh decimation cannot be used!"); #endif } if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } progressionStatus_.increment(); rtabmap::util3d::denseMeshPostProcessing( mesh, 0.0f, 0, mergedClouds, optimizedColorRadius, !(textureSize > 0 && textureVertexColorPolicy == 0), optimizedCleanWhitePolygons, optimizedMinClusterSize); if(textureSize>0) { LOGI("Texturing... cameraPoses=%d, cameraDepths=%d", (int)cameraPoses.size(), (int)cameraDepths.size()); textureMesh = rtabmap::util3d::createTextureMesh( mesh, cameraPoses, cameraModels, cameraDepths, optimizedMaxTextureDistance, 0.0f, 0.0f, optimizedMinTextureClusterSize, std::vector(), &progressionStatus_, &vertexToPixels); LOGI("Texturing... done! %fs", timer.ticks()); if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } // Remove occluded polygons (polygons with no texture) if(textureMesh->tex_coordinates.size() && optimizedCleanWhitePolygons) { LOGI("Cleanup mesh..."); rtabmap::util3d::cleanTextureMesh(*textureMesh, 0); LOGI("Cleanup mesh... done! %fs", timer.ticks()); } totalPolygons = 0; for(unsigned int t=0; ttex_polygons.size(); ++t) { totalPolygons+=textureMesh->tex_polygons[t].size(); } } else { totalPolygons = (int)mesh->polygons.size(); polygonMesh = mesh; } } } else { UERROR("Merged cloud too small (%d points) to create polygons!", (int)mergedClouds->size()); } } else // organized meshes { pcl::PointCloud::Ptr mergedClouds(new pcl::PointCloud); if(textureSize > 0) { textureMesh->tex_materials.resize(poses.size()); textureMesh->tex_polygons.resize(poses.size()); textureMesh->tex_coordinates.resize(poses.size()); } int polygonsStep = 0; int oi = 0; for(std::map::iterator iter=poses.begin(); iter!= poses.end(); ++iter) { LOGI("Assembling cloud %d (total=%d)...", iter->first, (int)poses.size()); std::map::iterator jter = createdMeshes_.find(iter->first); pcl::PointCloud::Ptr cloud(new pcl::PointCloud); std::vector polygons; float gains[3] = {1.0f}; if(jter != createdMeshes_.end()) { cloud = jter->second.cloud; polygons= jter->second.polygons; if(cloud->size() && polygons.size() == 0) { polygons = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); } gains[0] = jter->second.gains[0]; gains[1] = jter->second.gains[1]; gains[2] = jter->second.gains[2]; } else { rtabmap::SensorData data = rtabmap_->getMemory()->getNodeData(iter->first, true, false, false, false); data.uncompressData(); if(!data.imageRaw().empty() && !data.depthRaw().empty() && data.cameraModels().size() == 1) { int meshDecimation = updateMeshDecimation(data.depthRaw().cols, data.depthRaw().rows); if(smoothing_ || depthBleedingError_>0.0f) { cv::Mat depth = data.depthRaw(); if(depthBleedingError_ > 0.0f) { rtabmap::util2d::depthBleedingFiltering(depth, depthBleedingError_); } if(smoothing_) { depth = rtabmap::util2d::fastBilateralFiltering(depth, g_bilateralFilteringSigmaS, g_bilateralFilteringSigmaR); } data.setRGBDImage(data.imageRaw(), depth, data.depthConfidenceRaw(), data.cameraModels()); } cloud = rtabmap::util3d::cloudRGBFromSensorData(data, meshDecimation, maxCloudDepth_, minCloudDepth_, 0, rtabmap::ParametersMap(), std::vector(), depthConfidence_); polygons = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); } } if(cloud->size() && polygons.size()) { // Convert organized to dense cloud pcl::PointCloud::Ptr outputCloud(new pcl::PointCloud); std::vector outputPolygons; std::vector denseToOrganizedIndices = rtabmap::util3d::filterNaNPointsFromMesh(*cloud, polygons, *outputCloud, outputPolygons); pcl::PointCloud::Ptr normals = rtabmap::util3d::computeNormals(outputCloud, normalK); pcl::PointCloud::Ptr cloudWithNormals(new pcl::PointCloud); pcl::concatenateFields(*outputCloud, *normals, *cloudWithNormals); UASSERT(outputPolygons.size()); totalPolygons+=outputPolygons.size(); if(textureSize == 0) { // colored mesh cloudWithNormals = rtabmap::util3d::transformPointCloud(cloudWithNormals, iter->second); if(gains[0] != 1.0f || gains[1] != 1.0f || gains[2] != 1.0f) { for(unsigned int i=0; isize(); ++i) { pcl::PointXYZRGBNormal & pt = cloudWithNormals->at(i); pt.r = uchar(std::max(0.0, std::min(255.0, double(pt.r) * gains[0]))); pt.g = uchar(std::max(0.0, std::min(255.0, double(pt.g) * gains[1]))); pt.b = uchar(std::max(0.0, std::min(255.0, double(pt.b) * gains[2]))); } } if(mergedClouds->size() == 0) { *mergedClouds = *cloudWithNormals; polygonMesh->polygons = outputPolygons; } else { rtabmap::util3d::appendMesh(*mergedClouds, polygonMesh->polygons, *cloudWithNormals, outputPolygons); } } else { // texture mesh size_t polygonSize = outputPolygons.front().vertices.size(); textureMesh->tex_polygons[oi].resize(outputPolygons.size()); textureMesh->tex_coordinates[oi].resize(outputPolygons.size() * polygonSize); for(unsigned int j=0; jtex_coordinates[oi][j*vertices.vertices.size()+k] = Eigen::Vector2f( float(originalVertex % cloud->width) / float(cloud->width), // u float(cloud->height - originalVertex / cloud->width) / float(cloud->height)); // v vertices.vertices[k] += polygonsStep; } textureMesh->tex_polygons[oi][j] = vertices; } polygonsStep += outputCloud->size(); pcl::PointCloud::Ptr transformedCloud = rtabmap::util3d::transformPointCloud(cloudWithNormals, iter->second); if(mergedClouds->size() == 0) { *mergedClouds = *transformedCloud; } else { *mergedClouds += *transformedCloud; } textureMesh->tex_materials[oi].tex_illum = 1; textureMesh->tex_materials[oi].tex_name = uFormat("material_%d", iter->first); textureMesh->tex_materials[oi].tex_file = uNumber2Str(iter->first); ++oi; } } else { UERROR("Mesh not found for mesh %d", iter->first); } if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } progressionStatus_.increment(); } if(textureSize == 0) { if(mergedClouds->size()) { pcl::toPCLPointCloud2(*mergedClouds, polygonMesh->cloud); } else { polygonMesh->polygons.clear(); } } else { textureMesh->tex_materials.resize(oi); textureMesh->tex_polygons.resize(oi); if(mergedClouds->size()) { pcl::toPCLPointCloud2(*mergedClouds, textureMesh->cloud); } } } // end optimized or organized if(textureSize>0 && totalPolygons && textureMesh->tex_materials.size()) { LOGI("Merging %d textures...", (int)textureMesh->tex_materials.size()); globalTextures = rtabmap::util3d::mergeTextures( *textureMesh, std::map(), std::map >(), rtabmap_->getMemory(), 0, textureSize, textureCount, vertexToPixels, true, 10.0f, true ,true, 0, 0, 0, false, &progressionStatus_, 255, textureVertexColorPolicy == 1); LOGI("Merging %d textures... globalTextures=%dx%d", (int)textureMesh->tex_materials.size(), globalTextures.cols, globalTextures.rows); } if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } progressionStatus_.increment(); } if(totalPolygons) { if(textureSize == 0) { UASSERT((int)polygonMesh->polygons.size() == totalPolygons); if(polygonMesh->polygons.size()) { // save in database pcl::PointCloud::Ptr cloud(new pcl::PointCloud); pcl::fromPCLPointCloud2(polygonMesh->cloud, *cloud); cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud, rtabmap::Transform(), false).data()); // for database std::vector > > polygons(1); polygons[0].resize(polygonMesh->polygons.size()); for(unsigned int p=0; ppolygons.size(); ++p) { polygons[0][p] = polygonMesh->polygons[p].vertices; } boost::mutex::scoped_lock lock(rtabmapMutex_); rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, polygons); success = true; } } else if(textureMesh->tex_materials.size()) { bool hasColors = false; for(unsigned int i=0; icloud.fields.size(); ++i) { if(textureMesh->cloud.fields[i].name.compare("rgb") == 0) { hasColors = true; break; } } cv::Mat cloudMat; if(hasColors) { pcl::PointCloud::Ptr cloud(new pcl::PointCloud); pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud); cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud, rtabmap::Transform(), false).data()); // for database } else { pcl::PointCloud::Ptr cloud(new pcl::PointCloud); pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud); cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud, rtabmap::Transform(), false).data()); // for database } // save in database std::vector > > polygons(textureMesh->tex_polygons.size()); for(unsigned int t=0; ttex_polygons.size(); ++t) { polygons[t].resize(textureMesh->tex_polygons[t].size()); for(unsigned int p=0; ptex_polygons[t].size(); ++p) { polygons[t][p] = textureMesh->tex_polygons[t][p].vertices; } } boost::mutex::scoped_lock lock(rtabmapMutex_); rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, polygons, textureMesh->tex_coordinates, globalTextures); success = true; } else { UERROR("Failed exporting texture mesh! There are no textures!"); } } else { UERROR("Failed exporting mesh! There are no polygons!"); } } else // Point cloud { pcl::PointCloud::Ptr mergedClouds(new pcl::PointCloud); for(std::map::iterator iter=poses.begin(); iter!= poses.end(); ++iter) { std::map::iterator jter=createdMeshes_.find(iter->first); pcl::PointCloud::Ptr cloud(new pcl::PointCloud); pcl::IndicesPtr indices(new std::vector); float gains[3]; gains[0] = gains[1] = gains[2] = 1.0f; if(regenerateCloud) { if(jter != createdMeshes_.end()) { gains[0] = jter->second.gains[0]; gains[1] = jter->second.gains[1]; gains[2] = jter->second.gains[2]; } rtabmap::SensorData data = rtabmap_->getMemory()->getNodeData(iter->first, true, true, false, false); data.uncompressData(); if(!data.imageRaw().empty() && !data.depthRaw().empty()) { // full resolution if(smoothing_ || depthBleedingError_>0.0f) { cv::Mat depth = data.depthRaw(); if(depthBleedingError_ > 0.0f) { rtabmap::util2d::depthBleedingFiltering(depth, depthBleedingError_); } if(smoothing_) { depth = rtabmap::util2d::fastBilateralFiltering(depth, g_bilateralFilteringSigmaS, g_bilateralFilteringSigmaR); } data.setRGBDImage(data.imageRaw(), depth, data.depthConfidenceRaw(), data.cameraModels()); } cloud = rtabmap::util3d::cloudRGBFromSensorData(data, 1, maxCloudDepth_, minCloudDepth_, indices.get(), rtabmap::ParametersMap(), std::vector(), depthConfidence_); } else if(!data.laserScanRaw().empty()) { //scan cloud = rtabmap::util3d::laserScanToPointCloudRGB(rtabmap::util3d::commonFiltering(data.laserScanRaw(), 1, minCloudDepth_, maxCloudDepth_), data.laserScanRaw().localTransform(), 255, 255, 255); indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } } else { if(jter != createdMeshes_.end()) { cloud = jter->second.cloud; indices = jter->second.indices; gains[0] = jter->second.gains[0]; gains[1] = jter->second.gains[1]; gains[2] = jter->second.gains[2]; } else { rtabmap::SensorData data = rtabmap_->getMemory()->getNodeData(iter->first, true, true, false, false); data.uncompressData(); if(!data.imageRaw().empty() && !data.depthRaw().empty()) { int meshDecimation = updateMeshDecimation(data.depthRaw().cols, data.depthRaw().rows); if(smoothing_ || depthBleedingError_>0.0f) { cv::Mat depth = data.depthRaw(); if(depthBleedingError_ > 0.0f) { rtabmap::util2d::depthBleedingFiltering(depth, depthBleedingError_); } if(smoothing_) { depth = rtabmap::util2d::fastBilateralFiltering(depth, g_bilateralFilteringSigmaS, g_bilateralFilteringSigmaR); } data.setRGBDImage(data.imageRaw(), depth, data.depthConfidenceRaw(), data.cameraModels()); } cloud = rtabmap::util3d::cloudRGBFromSensorData(data, meshDecimation, maxCloudDepth_, minCloudDepth_, indices.get(), rtabmap::ParametersMap(), std::vector(), depthConfidence_); } else if(!data.laserScanRaw().empty()) { //scan cloud = rtabmap::util3d::laserScanToPointCloudRGB(rtabmap::util3d::commonFiltering(data.laserScanRaw(), 1, minCloudDepth_, maxCloudDepth_), data.laserScanRaw().localTransform(), 255, 255, 255); indices->resize(cloud->size()); for(unsigned int i=0; isize(); ++i) { indices->at(i) = i; } } } } if(cloud->size() && indices->size()) { // Convert organized to dense cloud pcl::PointCloud::Ptr transformedCloud(new pcl::PointCloud); if(cloudVoxelSize > 0.0f) { transformedCloud = rtabmap::util3d::voxelize(cloud, indices, cloudVoxelSize); transformedCloud = rtabmap::util3d::transformPointCloud(transformedCloud, iter->second); } else { // it looks like that using only transformPointCloud with indices // flushes the colors, so we should extract points before... maybe a too old PCL version pcl::copyPointCloud(*cloud, *indices, *transformedCloud); transformedCloud = rtabmap::util3d::transformPointCloud(transformedCloud, iter->second); } if(gains[0] != 1.0f || gains[1] != 1.0f || gains[2] != 1.0f) { //LOGD("cloud %d, gain=%f", iter->first, gain); for(unsigned int i=0; isize(); ++i) { pcl::PointXYZRGB & pt = transformedCloud->at(i); //LOGI("color %d = %d %d %d", i, (int)pt.r, (int)pt.g, (int)pt.b); pt.r = uchar(std::max(0.0, std::min(255.0, double(pt.r) * gains[0]))); pt.g = uchar(std::max(0.0, std::min(255.0, double(pt.g) * gains[1]))); pt.b = uchar(std::max(0.0, std::min(255.0, double(pt.b) * gains[2]))); } } if(mergedClouds->size() == 0) { *mergedClouds = *transformedCloud; } else { *mergedClouds += *transformedCloud; } } if(progressionStatus_.isCanceled()) { if(blockRendering) { renderingMutex_.unlock(); } exporting_ = false; return false; } progressionStatus_.increment(); } if(mergedClouds->size()) { if(cloudVoxelSize > 0.0f) { mergedClouds = rtabmap::util3d::voxelize(mergedClouds, cloudVoxelSize); } // save in database { cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*mergedClouds).data()); // for database boost::mutex::scoped_lock lock(rtabmapMutex_); rtabmap_->getMemory()->saveOptimizedMesh(cloudMat); success = true; } } else { UERROR("Merged cloud is empty!"); } } progressionStatus_.finish(); if(blockRendering) { renderingMutex_.unlock(); } } catch (std::exception & e) { UERROR("Out of memory! %s", e.what()); if(blockRendering) { renderingMutex_.unlock(); } success = false; } exporting_ = false; optRefId_ = 0; if(optRefPose_) { delete optRefPose_; optRefPose_ = 0; } if(success && poses.size()) { // for optimized mesh // just take the last as reference optRefId_ = poses.rbegin()->first; optRefPose_ = new rtabmap::Transform(poses.rbegin()->second); } return success; } bool RTABMapApp::postExportation(bool visualize) { LOGI("postExportation(visualize=%d)", visualize?1:0); optTextureMesh_.reset(new pcl::TextureMesh); optMesh_= rtabmap::Mesh(); optTexture_ = cv::Mat(); exportedMeshUpdated_ = false; if(visualize) { visualizingMesh_ = false; cv::Mat cloudMat; std::vector > > polygons; #if PCL_VERSION_COMPARE(>=, 1, 8, 0) std::vector > > texCoords; #else std::vector > texCoords; #endif cv::Mat textures; if(rtabmap_ && rtabmap_->getMemory()) { cloudMat = rtabmap_->getMemory()->loadOptimizedMesh(&polygons, &texCoords, &textures); if(!cloudMat.empty()) { LOGI("postExportation: Found optimized mesh! Visualizing it."); optTextureMesh_ = rtabmap::util3d::assembleTextureMesh(cloudMat, polygons, texCoords, textures, true); optMesh_ = rtabmap::Mesh(); if(textures.rows > 4096) // Limitation iOS, just for rendering on device { cv::resize(textures, optTexture_, cv::Size(4096, 4096), 0.0f, 0.0f, cv::INTER_AREA); } else { optTexture_ = textures; } boost::mutex::scoped_lock lock(renderingMutex_); visualizingMesh_ = true; exportedMeshUpdated_ = true; } else { LOGI("postExportation: No optimized mesh found."); } } } else if(visualizingMesh_) { rtabmapMutex_.lock(); std::map poses = rtabmap_->getLocalOptimizedPoses(); std::multimap links = rtabmap_->getLocalConstraints(); if(poses.empty()) { rtabmap_->getGraph( poses, links, true, true, 0, false, false, false, false, false, false); } if(!poses.empty()) { rtabmap::Statistics stats; for(std::map::iterator iter=bufferedStatsData_.begin(); iter!=bufferedStatsData_.end(); ++iter) { stats.addStatistic(iter->first, iter->second); } stats.setPoses(poses); stats.setConstraints(links); rtabmapEvents_.push_back(new rtabmap::RtabmapEvent(stats)); } rtabmapMutex_.unlock(); visualizingMesh_ = false; } return visualizingMesh_; } bool RTABMapApp::writeExportedMesh(const std::string & directory, const std::string & name) { LOGI("writeExportedMesh: dir=%s name=%s", directory.c_str(), name.c_str()); exporting_ = true; bool success = false; pcl::PolygonMesh::Ptr polygonMesh(new pcl::PolygonMesh); pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh); cv::Mat cloudMat; std::vector > > polygons; #if PCL_VERSION_COMPARE(>=, 1, 8, 0) std::vector > > texCoords; #else std::vector > texCoords; #endif cv::Mat textures; if(rtabmap_ && rtabmap_->getMemory()) { cloudMat = rtabmap_->getMemory()->loadOptimizedMesh(&polygons, &texCoords, &textures); if(!cloudMat.empty()) { LOGI("writeExportedMesh: Found optimized mesh!"); if(textures.empty()) { polygonMesh = rtabmap::util3d::assemblePolygonMesh(cloudMat, polygons.size() == 1?polygons[0]:std::vector >()); } else { textureMesh = rtabmap::util3d::assembleTextureMesh(cloudMat, polygons, texCoords, textures, false); } } else { LOGI("writeExportedMesh: No optimized mesh found."); } } if(polygonMesh->cloud.data.size()) { #if defined(RTABMAP_PDAL) || defined(RTABMAP_LIBLAS) if(polygonMesh->polygons.empty() && (exportPointCloudFormat_ == "las" || exportPointCloudFormat_ == "laz")) { // Point cloud LAS std::string filePath = directory + UDirectory::separator() + name + (exportPointCloudFormat_ == "las"? ".las" : ".laz"); LOGI("Saving las (%d vertices) to %s.", (int)polygonMesh->cloud.data.size()/polygonMesh->cloud.point_step, filePath.c_str()); pcl::PointCloud output; pcl::fromPCLPointCloud2(polygonMesh->cloud, output); #ifdef RTABMAP_PDAL success = rtabmap::savePDALFile(filePath, output) == 0; #else success = rtabmap::saveLASFile(filePath, output) == 0; #endif if(success) { LOGI("Saved las to %s!", filePath.c_str()); } else { UERROR("Failed saving las to %s!", filePath.c_str()); } } else #endif { // Point cloud PLY std::string filePath = directory + UDirectory::separator() + name + ".ply"; LOGI("Saving ply (%d vertices, %d polygons) to %s.", (int)polygonMesh->cloud.data.size()/polygonMesh->cloud.point_step, (int)polygonMesh->polygons.size(), filePath.c_str()); success = pcl::io::savePLYFileBinary(filePath, *polygonMesh) == 0; if(success) { LOGI("Saved ply to %s!", filePath.c_str()); } else { UERROR("Failed saving ply to %s!", filePath.c_str()); } } } else if(textureMesh->cloud.data.size()) { // TextureMesh OBJ LOGD("Saving texture(s) (%d)", textures.empty()?0:textures.cols/textures.rows); UASSERT(textures.empty() || textures.cols % textures.rows == 0); UASSERT((int)textureMesh->tex_materials.size() == textures.cols/textures.rows); for(unsigned int i=0; itex_materials.size(); ++i) { std::string baseNameNum = name; if(textureMesh->tex_materials.size()>1) { baseNameNum+=uNumber2Str(i); } std::string fullPath = directory+UDirectory::separator()+baseNameNum+".jpg"; textureMesh->tex_materials[i].tex_file = baseNameNum+".jpg"; LOGI("Saving texture to %s.", fullPath.c_str()); success = cv::imwrite(fullPath, textures(cv::Range::all(), cv::Range(i*textures.rows, (i+1)*textures.rows))); if(!success) { LOGI("Failed saving %s!", fullPath.c_str()); } else { LOGI("Saved %s.", fullPath.c_str()); } } if(success) { // With Sketchfab, the OBJ models are rotated 90 degrees on x axis, so rotate -90 to have model in right position //pcl::PointCloud::Ptr cloud(new pcl::PointCloud); //pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud); //cloud = rtabmap::util3d::transformPointCloud(cloud, rtabmap::Transform(1,0,0,0, 0,0,1,0, 0,-1,0,0)); //pcl::toPCLPointCloud2(*cloud, textureMesh->cloud); std::string filePath = directory + UDirectory::separator() + name + ".obj"; int totalPolygons = 0; for(unsigned int i=0;itex_polygons.size(); ++i) { totalPolygons += textureMesh->tex_polygons[i].size(); } LOGI("Saving obj (%d vertices, %d polygons) to %s.", (int)textureMesh->cloud.data.size()/textureMesh->cloud.point_step, totalPolygons, filePath.c_str()); success = rtabmap::util3d::saveOBJFile(filePath, *textureMesh) == 0; if(success) { LOGI("Saved obj to %s!", filePath.c_str()); } else { UERROR("Failed saving obj to %s!", filePath.c_str()); } } } exporting_ = false; return success; } int RTABMapApp::postProcessing(int approach) { postProcessing_ = true; LOGI("postProcessing begin(%d)", approach); int returnedValue = 0; if(rtabmap_) { std::map poses; std::multimap links; // detect more loop closures if(approach == -1 || approach == 2) { if(approach == -1) { progressionStatus_.reset(6); } returnedValue = rtabmap_->detectMoreLoopClosures(1.0f, M_PI/6.0f, approach == -1?5:1, true, true, approach==-1?&progressionStatus_:0); if(approach == -1 && progressionStatus_.isCanceled()) { postProcessing_ = false; return -1; } } // graph optimization if(returnedValue >=0) { if (approach == 1) { if(rtabmap::Optimizer::isAvailable(rtabmap::Optimizer::kTypeG2O)) { std::map signatures; rtabmap_->getGraph(poses, links, true, true, &signatures); rtabmap::ParametersMap param; param.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerIterations(), "30")); param.insert(rtabmap::ParametersPair(rtabmap::Parameters::kOptimizerEpsilon(), "0")); rtabmap::Optimizer * sba = rtabmap::Optimizer::create(rtabmap::Optimizer::kTypeG2O, param); poses = sba->optimizeBA(poses.rbegin()->first, poses, links, signatures); delete sba; } else { UERROR("g2o not available!"); } } else if(approach!=4 && approach!=5 && approach != 7) { // simple graph optmimization rtabmap_->getGraph(poses, links, true, true); } } if(poses.size()) { boost::mutex::scoped_lock lock(rtabmapMutex_); rtabmap::Statistics stats = rtabmap_->getStatistics(); stats.setPoses(poses); stats.setConstraints(links); LOGI("PostProcessing, sending rtabmap event to update graph..."); rtabmapEvents_.push_back(new rtabmap::RtabmapEvent(stats)); rtabmap_->setOptimizedPoses(poses, links); } else if(approach!=4 && approach!=5 && approach != 7) { returnedValue = -1; } if(returnedValue >=0) { boost::mutex::scoped_lock lock(renderingMutex_); // filter polygons if(approach == -1 || approach == 4) { filterPolygonsOnNextRender_ = true; } // gain compensation if(approach == -1 || approach == 5 || approach == 6) { gainCompensationOnNextRender_ = approach == 6 ? 2 : 1; // 2 = full, 1 = fast } // bilateral filtering if(approach == 7) { bilateralFilteringOnNextRender_ = true; } } } postProcessing_ = false; LOGI("postProcessing end(%d) -> %d", approach, returnedValue); return returnedValue; } void RTABMapApp::clearMeasures() { boost::mutex::scoped_lock lockRender(renderingMutex_); measures_.clear(); measuresUpdated_ = true; } void RTABMapApp::setMeasuringMode(int mode) { if(visualizingMesh_) { measuringMode_ = mode; } } void RTABMapApp::addMeasureButtonClicked() { if(visualizingMesh_) { boost::mutex::scoped_lock lock(renderingMutex_); addMeasureClicked_ = true; } } void RTABMapApp::teleportButtonClicked() { if(visualizingMesh_) { boost::mutex::scoped_lock lock(renderingMutex_); teleportClicked_ = true; } } void RTABMapApp::removeMeasure() { if(visualizingMesh_) { boost::mutex::scoped_lock lock(renderingMutex_); removeMeasureClicked_ = true; } } void RTABMapApp::setMetricSystem(bool enabled) { metricSystem_ = enabled; if(measures_.size()) { measuresUpdated_ = true; } } void RTABMapApp::setMeasuringTextSize(float size) { measuringTextSize_ = size; if(measures_.size()) { measuresUpdated_ = true; } } void RTABMapApp::postOdometryEvent( rtabmap::Transform pose, float rgb_fx, float rgb_fy, float rgb_cx, float rgb_cy, float depth_fx, float depth_fy, float depth_cx, float depth_cy, const rtabmap::Transform & rgbFrame, const rtabmap::Transform & depthFrame, double stamp, double depthStamp, const void * yPlane, const void * uPlane, const void * vPlane, int yPlaneLen, int rgbWidth, int rgbHeight, int rgbFormat, const void * depth, int depthLen, int depthWidth, int depthHeight, int depthFormat, const void * conf, int confLen, int confWidth, int confHeight, int confFormat, const float * points, int pointsLen, int pointsChannels, rtabmap::Transform viewMatrix, float p00, float p11, float p02, float p12, float p22, float p32, float p23, float t0, float t1, float t2, float t3, float t4, float t5, float t6, float t7) { #if defined(RTABMAP_ARCORE) || defined(__APPLE__) boost::mutex::scoped_lock lock(cameraMutex_); if(cameraDriver_ == 3 && camera_) { if(pose.isNull()) { // We are lost, trigger a new map on next update camera_->resetOrigin(); return; } if(rgb_fx > 0.0f && rgb_fy > 0.0f && rgb_cx > 0.0f && rgb_cy > 0.0f && stamp > 0.0f && yPlane && vPlane && yPlaneLen == rgbWidth*rgbHeight) { #ifndef DISABLE_LOG //LOGD("rgb format = %d depth format =%d ", rgbFormat, depthFormat); #endif #if defined(RTABMAP_ARCORE) if(rgbFormat == AR_IMAGE_FORMAT_YUV_420_888 && (depth==0 || depthFormat == AIMAGE_FORMAT_DEPTH16)) #else //__APPLE__ if(rgbFormat == 875704422 && (depth==0 || depthFormat == 1717855600)) #endif { cv::Mat outputRGB; #ifndef DISABLE_LOG //LOGD("y=%p u=%p v=%p yLen=%d y->v=%ld", yPlane, uPlane, vPlane, yPlaneLen, (long)vPlane-(long)yPlane); #endif if((long)vPlane-(long)yPlane != yPlaneLen) { // The uv-plane is not concatenated to y plane in memory, so concatenate them cv::Mat yuv(rgbHeight+rgbHeight/2, rgbWidth, CV_8UC1); memcpy(yuv.data, yPlane, yPlaneLen); memcpy(yuv.data+yPlaneLen, vPlane, rgbHeight/2*rgbWidth); cv::cvtColor(yuv, outputRGB, cv::COLOR_YUV2BGR_NV21); } else { #ifdef __ANDROID__ cv::cvtColor(cv::Mat(rgbHeight+rgbHeight/2, rgbWidth, CV_8UC1, (void*)yPlane), outputRGB, cv::COLOR_YUV2BGR_NV21); #else // __APPLE__ cv::cvtColor(cv::Mat(rgbHeight+rgbHeight/2, rgbWidth, CV_8UC1, (void*)yPlane), outputRGB, cv::COLOR_YUV2RGB_NV21); #endif } cv::Mat outputDepth; cv::Mat outputDepthConfidence; if(depth && depthHeight>0 && depthWidth>0) { #ifndef DISABLE_LOG //LOGD("depth %dx%d len=%d", depthWidth, depthHeight, depthLen); #endif if(depthLen == 4*depthWidth*depthHeight) { // IOS outputDepth = cv::Mat(depthHeight, depthWidth, CV_32FC1, (void*)depth).clone(); if(conf && confWidth == depthWidth && confHeight == depthHeight && confFormat == 1278226488) { // https://developer.apple.com/documentation/arkit/arconfidencelevel // 0 = low // 1 = medium // 2 = high // Re-scale confidence from [0,2] to [0,100] cv::Mat(depthHeight, depthWidth, CV_8UC1, (void*)conf).convertTo(outputDepthConfidence, CV_8UC1, 50, 0); } } else if(depthLen == 2*depthWidth*depthHeight) { // ANDROID outputDepth = cv::Mat(depthHeight, depthWidth, CV_16UC1); outputDepthConfidence = cv::Mat(depthHeight, depthWidth, CV_8UC1); uint16_t *dataShort = (uint16_t *)depth; for (int y = 0; y < outputDepth.rows; ++y) { for (int x = 0; x < outputDepth.cols; ++x) { uint16_t depthSample = dataShort[y*outputDepth.cols + x]; uint16_t depthRange = (depthSample & 0x1FFF); // first 3 bits are confidence outputDepth.at(y,x) = depthRange; // https://developer.android.com/reference/android/graphics/ImageFormat#DEPTH16 // The confidence value is an estimate of correctness for this sample. It // is encoded in the 3 most significant bits of the sample, with a value of // 0 representing 100% confidence, a value of 1 representing 0% confidence, a // value of 2 representing 1/7, a value of 3 representing 2/7, and so on. uint8_t depthConfidence = uint8_t((depthSample >> 13) & 0x7); outputDepthConfidence.at(y,x) = depthConfidence == 0 ? 100 : (depthConfidence - 1)*100 / 7; } } } } if(!outputRGB.empty()) { // Convert in our coordinate frame pose = rtabmap::rtabmap_world_T_opengl_world * pose * rtabmap::opengl_world_T_rtabmap_world; // We should update the pose before querying poses for depth below (if not same stamp than rgb) camera_->poseReceived(pose, stamp); // Registration depth to rgb if(!outputDepth.empty() && !depthFrame.isNull() && depth_fx!=0 && (rgbFrame != depthFrame || depthStamp!=stamp)) { UTimer time; rtabmap::Transform motion = rtabmap::Transform::getIdentity(); if(depthStamp != stamp) { // Interpolate pose rtabmap::Transform poseRgb; rtabmap::Transform poseDepth; cv::Mat cov; if(!camera_->getPose(camera_->getStampEpochOffset()+stamp, poseRgb, cov, 0.0)) { UERROR("Could not find pose at rgb stamp %f (epoch %f)!", stamp, camera_->getStampEpochOffset()+stamp); } else if(!camera_->getPose(camera_->getStampEpochOffset()+depthStamp, poseDepth, cov, 0.0)) { UERROR("Could not find pose at depth stamp %f (epoch %f) last rgb is %f!", depthStamp, camera_->getStampEpochOffset()+depthStamp, stamp); } else { #ifndef DISABLE_LOG UDEBUG("poseRGB =%s (stamp=%f)", poseRgb.prettyPrint().c_str(), stamp); UDEBUG("poseDepth=%s (stamp=%f)", poseDepth.prettyPrint().c_str(), depthStamp); #endif motion = poseRgb.inverse()*poseDepth; // transform in camera frame #ifndef DISABLE_LOG UDEBUG("motion=%s", motion.prettyPrint().c_str()); #endif motion = rtabmap::CameraModel::opticalRotation().inverse() * motion * rtabmap::CameraModel::opticalRotation(); #ifndef DISABLE_LOG UDEBUG("motion=%s", motion.prettyPrint().c_str()); #endif } } rtabmap::Transform rgbToDepth = motion*rgbFrame.inverse()*depthFrame; float scale = (float)outputDepth.cols/(float)outputRGB.cols; cv::Mat colorK = (cv::Mat_(3,3) << rgb_fx*scale, 0, rgb_cx*scale, 0, rgb_fy*scale, rgb_cy*scale, 0, 0, 1); cv::Mat depthK = (cv::Mat_(3,3) << depth_fx, 0, depth_cx, 0, depth_fy, depth_cy, 0, 0, 1); cv::Mat regConfidence; outputDepth = rtabmap::util2d::registerDepth(outputDepth, outputDepthConfidence, depthK, outputDepth.size(), colorK, rgbToDepth, regConfidence); outputDepthConfidence = regConfidence; #ifndef DISABLE_LOG UDEBUG("Depth registration time: %fs", time.elapsed()); #endif } rtabmap::CameraModel model = rtabmap::CameraModel(rgb_fx, rgb_fy, rgb_cx, rgb_cy, camera_->getDeviceTColorCamera(), 0, cv::Size(rgbWidth, rgbHeight)); #ifndef DISABLE_LOG //LOGI("pointCloudData size=%d", pointsLen); #endif if(!fullResolution_) { outputRGB = rtabmap::util2d::decimate(outputRGB, 2); model = model.scaled(1.0/double(2)); } std::vector kpts; std::vector kpts3; rtabmap::LaserScan scan; if(points && pointsLen>0) { cv::Mat pointsMat(1, pointsLen, CV_32FC(pointsChannels), (void*)points); if(outputDepth.empty()) { int kptsSize = fullResolution_ ? 12 : 6; scan = rtabmap::CameraMobile::scanFromPointCloudData(pointsMat, pose, model, outputRGB, &kpts, &kpts3, kptsSize); } else { // We will recompute features if depth is available scan = rtabmap::CameraMobile::scanFromPointCloudData(pointsMat, pose, model, outputRGB); } } if(!outputDepth.empty()) { rtabmap::CameraModel depthModel = model.scaled(float(outputDepth.cols) / float(model.imageWidth())); depthModel.setLocalTransform(pose*model.localTransform()); camera_->setOcclusionImage(outputDepth, depthModel); } rtabmap::SensorData data(scan, outputRGB, outputDepth, outputDepthConfidence, model, 0, stamp); data.setFeatures(kpts, kpts3, cv::Mat()); glm::mat4 projectionMatrix(0); projectionMatrix[0][0] = p00; projectionMatrix[1][1] = p11; projectionMatrix[2][0] = p02; projectionMatrix[2][1] = p12; projectionMatrix[2][2] = p22; projectionMatrix[2][3] = p32; projectionMatrix[3][2] = p23; glm::mat4 viewMatrixMat = rtabmap::glmFromTransform(viewMatrix); float texCoords[8]; texCoords[0] = t0; texCoords[1] = t1; texCoords[2] = t2; texCoords[3] = t3; texCoords[4] = t4; texCoords[5] = t5; texCoords[6] = t6; texCoords[7] = t7; camera_->update(data, pose, viewMatrixMat, projectionMatrix, main_scene_.GetCameraType() == tango_gl::GestureCamera::kFirstPerson?texCoords:0); } } } else { UERROR("Missing image information! fx=%f fy=%f cx=%f cy=%f stamp=%f yPlane=%d vPlane=%d yPlaneLen=%d rgbWidth=%d rgbHeight=%d", rgb_fx, rgb_fy, rgb_cx, rgb_cy, stamp, yPlane?1:0, vPlane?1:0, yPlaneLen, rgbWidth, rgbHeight); } } #else UERROR("Not built with ARCore or iOS!"); #endif } bool RTABMapApp::handleEvent(UEvent * event) { if(sensorCaptureThread_!=0) { // called from events manager thread, so protect the data if(event->getClassName().compare("SensorEvent") == 0) { LOGI("Received SensorEvent!"); if(sensorMutex_.try_lock()) { sensorEvents_.clear(); sensorEvents_.push_back(*((rtabmap::SensorEvent*)(event))); sensorMutex_.unlock(); } } if(event->getClassName().compare("RtabmapEvent") == 0) { LOGI("Received RtabmapEvent event! status=%d", status_.first); if(status_.first == rtabmap::RtabmapEventInit::kInitialized) { boost::mutex::scoped_lock lock(rtabmapMutex_); rtabmapEvents_.push_back((rtabmap::RtabmapEvent*)event); return true; } else { LOGW("Received RtabmapEvent event but ignoring it while we are initializing...status=%d", status_.first); } } } if(event->getClassName().compare("PoseEvent") == 0) { if(poseMutex_.try_lock()) { poseEvents_.clear(); poseEvents_.push_back(((rtabmap::PoseEvent*)event)->pose()); poseMutex_.unlock(); } } if(event->getClassName().compare("CameraInfoEvent") == 0) { rtabmap::CameraInfoEvent * tangoEvent = (rtabmap::CameraInfoEvent*)event; // Call JAVA callback with tango event msg bool success = false; #ifdef __ANDROID__ if(jvm && RTABMapActivity) { JNIEnv *env = 0; jint rs = jvm->AttachCurrentThread(&env, NULL); if(rs == JNI_OK && env) { jclass clazz = env->GetObjectClass(RTABMapActivity); if(clazz) { jmethodID methodID = env->GetMethodID(clazz, "cameraEventCallback", "(ILjava/lang/String;Ljava/lang/String;)V" ); if(methodID) { env->CallVoidMethod(RTABMapActivity, methodID, tangoEvent->type(), env->NewStringUTF(tangoEvent->key().c_str()), env->NewStringUTF(tangoEvent->value().c_str())); success = true; } } } jvm->DetachCurrentThread(); } #else if(swiftClassPtr_) { std::function actualCallback = [&](){ swiftCameraInfoEventCallback(swiftClassPtr_, tangoEvent->type(), tangoEvent->key().c_str(), tangoEvent->value().c_str()); }; actualCallback(); success = true; } #endif if(!success) { UERROR("Failed to call RTABMapActivity::tangoEventCallback"); } } if(event->getClassName().compare("RtabmapEventInit") == 0) { status_.first = ((rtabmap::RtabmapEventInit*)event)->getStatus(); status_.second = ((rtabmap::RtabmapEventInit*)event)->getInfo(); LOGI("Received RtabmapEventInit! Status=%d info=%s", (int)status_.first, status_.second.c_str()); // Call JAVA callback with init msg bool success = false; #ifdef __ANDROID__ if(jvm && RTABMapActivity) { JNIEnv *env = 0; jint rs = jvm->AttachCurrentThread(&env, NULL); if(rs == JNI_OK && env) { jclass clazz = env->GetObjectClass(RTABMapActivity); if(clazz) { jmethodID methodID = env->GetMethodID(clazz, "rtabmapInitEventCallback", "(ILjava/lang/String;)V" ); if(methodID) { env->CallVoidMethod(RTABMapActivity, methodID, status_.first, env->NewStringUTF(status_.second.c_str())); success = true; } } } jvm->DetachCurrentThread(); } #else if(swiftClassPtr_) { std::function actualCallback = [&](){ swiftInitCallback(swiftClassPtr_, status_.first, status_.second.c_str()); }; actualCallback(); success = true; } #endif if(!success) { UERROR("Failed to call RTABMapActivity::rtabmapInitEventsCallback"); } } if(event->getClassName().compare("PostRenderEvent") == 0) { LOGI("Received PostRenderEvent!"); int loopClosureId = 0; int featuresExtracted = 0; if(((PostRenderEvent*)event)->getRtabmapEvent()) { LOGI("Received PostRenderEvent! has getRtabmapEvent"); const rtabmap::Statistics & stats = ((PostRenderEvent*)event)->getRtabmapEvent()->getStats(); loopClosureId = stats.loopClosureId()>0?stats.loopClosureId():stats.proximityDetectionId()>0?stats.proximityDetectionId():0; featuresExtracted = stats.getLastSignatureData().getWords().size(); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kMemoryWorking_memory_size(), uValue(stats.data(), rtabmap::Statistics::kMemoryWorking_memory_size(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kMemoryShort_time_memory_size(), uValue(stats.data(), rtabmap::Statistics::kMemoryShort_time_memory_size(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kKeypointDictionary_size(), uValue(stats.data(), rtabmap::Statistics::kKeypointDictionary_size(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kTimingTotal(), uValue(stats.data(), rtabmap::Statistics::kTimingTotal(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopHighest_hypothesis_id(), uValue(stats.data(), rtabmap::Statistics::kLoopHighest_hypothesis_id(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kMemoryDatabase_memory_used(), uValue(stats.data(), rtabmap::Statistics::kMemoryDatabase_memory_used(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopVisual_inliers(), uValue(stats.data(), rtabmap::Statistics::kLoopVisual_inliers(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopVisual_matches(), uValue(stats.data(), rtabmap::Statistics::kLoopVisual_matches(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopRejectedHypothesis(), uValue(stats.data(), rtabmap::Statistics::kLoopRejectedHypothesis(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopOptimization_max_error(), uValue(stats.data(), rtabmap::Statistics::kLoopOptimization_max_error(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopOptimization_max_error_ratio(), uValue(stats.data(), rtabmap::Statistics::kLoopOptimization_max_error_ratio(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kMemoryRehearsal_sim(), uValue(stats.data(), rtabmap::Statistics::kMemoryRehearsal_sim(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopHighest_hypothesis_value(), uValue(stats.data(), rtabmap::Statistics::kLoopHighest_hypothesis_value(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kMemoryDistance_travelled(), uValue(stats.data(), rtabmap::Statistics::kMemoryDistance_travelled(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kMemoryFast_movement(), uValue(stats.data(), rtabmap::Statistics::kMemoryFast_movement(), 0.0f))); uInsert(bufferedStatsData_, std::make_pair(rtabmap::Statistics::kLoopLandmark_detected(), uValue(stats.data(), rtabmap::Statistics::kLoopLandmark_detected(), 0.0f))); } // else use last data int nodes = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kMemoryWorking_memory_size(), 0.0f) + uValue(bufferedStatsData_, rtabmap::Statistics::kMemoryShort_time_memory_size(), 0.0f); int words = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kKeypointDictionary_size(), 0.0f); float updateTime = uValue(bufferedStatsData_, rtabmap::Statistics::kTimingTotal(), 0.0f); int highestHypId = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kLoopHighest_hypothesis_id(), 0.0f); int databaseMemoryUsed = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kMemoryDatabase_memory_used(), 0.0f); int inliers = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kLoopVisual_inliers(), 0.0f); int matches = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kLoopVisual_matches(), 0.0f); int rejected = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kLoopRejectedHypothesis(), 0.0f); float optimizationMaxError = uValue(bufferedStatsData_, rtabmap::Statistics::kLoopOptimization_max_error(), 0.0f); float optimizationMaxErrorRatio = uValue(bufferedStatsData_, rtabmap::Statistics::kLoopOptimization_max_error_ratio(), 0.0f); float rehearsalValue = uValue(bufferedStatsData_, rtabmap::Statistics::kMemoryRehearsal_sim(), 0.0f); float hypothesis = uValue(bufferedStatsData_, rtabmap::Statistics::kLoopHighest_hypothesis_value(), 0.0f); float distanceTravelled = uValue(bufferedStatsData_, rtabmap::Statistics::kMemoryDistance_travelled(), 0.0f); int fastMovement = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kMemoryFast_movement(), 0.0f); int landmarkDetected = (int)uValue(bufferedStatsData_, rtabmap::Statistics::kLoopLandmark_detected(), 0.0f); rtabmap::Transform currentPose = main_scene_.GetCameraPose(); float x=0.0f,y=0.0f,z=0.0f,roll=0.0f,pitch=0.0f,yaw=0.0f; if(!currentPose.isNull()) { currentPose.getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw); } // Call JAVA callback with some stats UINFO("Send statistics to GUI"); bool success = false; #ifdef __ANDROID__ if(jvm && RTABMapActivity) { JNIEnv *env = 0; jint rs = jvm->AttachCurrentThread(&env, NULL); if(rs == JNI_OK && env) { jclass clazz = env->GetObjectClass(RTABMapActivity); if(clazz) { jmethodID methodID = env->GetMethodID(clazz, "updateStatsCallback", "(IIIIFIIIIIIFIFIFFFFIIFFFFFF)V" ); if(methodID) { env->CallVoidMethod(RTABMapActivity, methodID, nodes, words, totalPoints_, totalPolygons_, updateTime, loopClosureId, highestHypId, databaseMemoryUsed, inliers, matches, featuresExtracted, hypothesis, lastDrawnCloudsCount_, renderingTime_>0.0f?1.0f/renderingTime_:0.0f, rejected, rehearsalValue, optimizationMaxError, optimizationMaxErrorRatio, distanceTravelled, fastMovement, landmarkDetected, x, y, z, roll, pitch, yaw); success = true; } } } jvm->DetachCurrentThread(); } #else // __APPLE__ if(swiftClassPtr_) { std::function actualCallback = [&](){ swiftStatsUpdatedCallback(swiftClassPtr_, nodes, words, totalPoints_, totalPolygons_, updateTime, loopClosureId, highestHypId, databaseMemoryUsed, inliers, matches, featuresExtracted, hypothesis, lastDrawnCloudsCount_, renderingTime_>0.0f?1.0f/renderingTime_:0.0f, rejected, rehearsalValue, optimizationMaxError, optimizationMaxErrorRatio, distanceTravelled, fastMovement, landmarkDetected, x, y, z, roll, pitch, yaw); }; actualCallback(); success = true; } #endif if(!success) { UERROR("Failed to call RTABMapActivity::updateStatsCallback"); } renderingTime_ = 0.0f; } return false; } rtabmap-0.22.1/app/android/jni/RTABMapApp.h000066400000000000000000000313661503504370700202170ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef RTABMAP_APP_H_ #define RTABMAP_APP_H_ #ifdef __ANDROID__ #include #endif #include #include #include "scene.h" #include "CameraMobile.h" #include "util.h" #include "ProgressionStatus.h" #include #include #include #include #include #include #include #include "Measure.h" // RTABMapApp handles the application lifecycle and resources. class RTABMapApp : public UEventsHandler { public: // Constructor and deconstructor. #ifdef __ANDROID__ RTABMapApp(JNIEnv* env, jobject caller_activity); #else // __APPLE__ RTABMapApp(); void setupSwiftCallbacks(void * classPtr, void(*progressCallback)(void *, int, int), void(*initCallback)(void *, int, const char*), void(*statsUpdatedCallback)(void *, int, int, int, int, float, int, int, int, int, int ,int, float, int, float, int, float, float, float, float, int, int, float, float, float, float, float, float), void(*cameraInfoCallback)(void *, int, const char*, const char*)); #endif ~RTABMapApp(); void setScreenRotation(int displayRotation, int cameraRotation); int openDatabase(const std::string & databasePath, bool databaseInMemory, bool optimize, bool clearDatabase); bool isBuiltWith(int cameraDriver) const; #ifdef __ANDROID__ bool startCamera(JNIEnv* env, jobject iBinder, jobject context, jobject activity, int driver); #else // __APPLE__ bool startCamera(); #endif // Allocate OpenGL resources for rendering, mainly for initializing the Scene. void InitializeGLContent(); // Setup the view port width and height. void SetViewPort(int width, int height); // Main render loop. int Render(); void stopCamera(); // Set render camera's viewing angle, first person, third person or top down. // // @param: camera_type, camera type includes first person, third person and // top down void SetCameraType(tango_gl::GestureCamera::CameraType camera_type); // Touch event passed from android activity. This function only supports two // touches. // // @param: touch_count, total count for touches. // @param: event, touch event of current touch. // @param: x0, normalized touch location for touch 0 on x axis. // @param: y0, normalized touch location for touch 0 on y axis. // @param: x1, normalized touch location for touch 1 on x axis. // @param: y1, normalized touch location for touch 1 on y axis. void OnTouchEvent(int touch_count, tango_gl::GestureCamera::TouchEvent event, float x0, float y0, float x1, float y1); void setPausedMapping(bool paused); void setOnlineBlending(bool enabled); void setMapCloudShown(bool shown); void setOdomCloudShown(bool shown); void setMeshRendering(bool enabled, bool withTexture); void setPointSize(float value); void setFOV(float angle); void setOrthoCropFactor(float value); void setGridRotation(float value); void setLighting(bool enabled); void setBackfaceCulling(bool enabled); void setWireframe(bool enabled); void setTextureColorSeamsHidden(bool hidden); void setLocalizationMode(bool enabled); void setTrajectoryMode(bool enabled); void setGraphOptimization(bool enabled); void setNodesFiltering(bool enabled); void setGraphVisible(bool visible); void setGridVisible(bool visible); void setRawScanSaved(bool enabled); void setCameraColor(bool enabled); void setFullResolution(bool enabled); void setSmoothing(bool enabled); void setDepthBleedingError(float value); void setDepthFromMotion(bool enabled); void setAppendMode(bool enabled); void setUpstreamRelocalizationAccThr(float value); void setDataRecorderMode(bool enabled); void setMaxCloudDepth(float value); void setMinCloudDepth(float value); void setCloudDensityLevel(int value); void setMeshAngleTolerance(float value); void setMeshDecimationFactor(float value); void setMeshTriangleSize(int value); void setClusterRatio(float value); void setMaxGainRadius(float value); void setRenderingTextureDecimation(int value); void setBackgroundColor(float gray); void setDepthConfidence(int value); void setExportPointCloudFormat(const std::string & format); int setMappingParameter(const std::string & key, const std::string & value); void setGPS(const rtabmap::GPS & gps); void addEnvSensor(int type, float value); void save(const std::string & databasePath); bool recover(const std::string & from, const std::string & to); void cancelProcessing(); bool exportMesh( float cloudVoxelSize, bool regenerateCloud, bool meshing, int textureSize, int textureCount, int normalK, bool optimized, float optimizedVoxelSize, int optimizedDepth, int optimizedMaxPolygons, float optimizedColorRadius, bool optimizedCleanWhitePolygons, int optimizedMinClusterSize, float optimizedMaxTextureDistance, int optimizedMinTextureClusterSize, int textureVertexColorPolicy, bool blockRendering); bool postExportation(bool visualize); bool writeExportedMesh(const std::string & directory, const std::string & name); int postProcessing(int approach); void clearMeasures(); void showMeasures(bool x, bool y, bool z, bool custom); void setMeasuringMode(int mode); void addMeasureButtonClicked(); void teleportButtonClicked(); void removeMeasure(); void setMetricSystem(bool enabled); void setMeasuringTextSize(float size); void postOdometryEvent( rtabmap::Transform pose, float rgb_fx, float rgb_fy, float rgb_cx, float rgb_cy, float depth_fx, float depth_fy, float depth_cx, float depth_cy, const rtabmap::Transform & rgbFrame, const rtabmap::Transform & depthFrame, double stamp, double depthStamp, const void * yPlane, const void * uPlane, const void * vPlane, int yPlaneLen, int rgbWidth, int rgbHeight, int rgbFormat, const void * depth, int depthLen, int depthWidth, int depthHeight, int depthFormat, const void * conf, int confLen, int confWidth, int confHeight, int confFormat, const float * points, int pointsLen, int pointsChannels, rtabmap::Transform viewMatrix, //view matrix float p00, float p11, float p02, float p12, float p22, float p32, float p23, // projection matrix float t0, float t1, float t2, float t3, float t4, float t5, float t6, float t7); // tex coord protected: virtual bool handleEvent(UEvent * event); private: int updateMeshDecimation(int width, int height); rtabmap::ParametersMap getRtabmapParameters(); void updateMeasuringState(); bool smoothMesh(int id, rtabmap::Mesh & mesh); void gainCompensation(bool full = false); std::vector filterOrganizedPolygons(const std::vector & polygons, int cloudSize) const; std::vector filterPolygons(const std::vector & polygons, int cloudSize) const; private: int cameraDriver_; rtabmap::CameraMobile * camera_; rtabmap::SensorCaptureThread * sensorCaptureThread_; rtabmap::RtabmapThread * rtabmapThread_; rtabmap::Rtabmap * rtabmap_; rtabmap::LogHandler * logHandler_; bool odomCloudShown_; bool graphOptimization_; bool nodesFiltering_; bool localizationMode_; bool trajectoryMode_; bool rawScanSaved_; bool smoothing_; float depthBleedingError_; bool depthFromMotion_; bool cameraColor_; bool fullResolution_; bool appendMode_; bool useExternalLidar_; float maxCloudDepth_; float minCloudDepth_; int cloudDensityLevel_; int meshTrianglePix_; float meshAngleToleranceDeg_; float meshDecimationFactor_; float clusterRatio_; float maxGainRadius_; int renderingTextureDecimation_; float backgroundColor_; unsigned char depthConfidence_; float upstreamRelocalizationMaxAcc_; std::string exportPointCloudFormat_; rtabmap::ParametersMap mappingParameters_; bool dataRecorderMode_; bool clearSceneOnNextRender_; bool openingDatabase_; bool exporting_; bool postProcessing_; bool filterPolygonsOnNextRender_; int gainCompensationOnNextRender_; bool bilateralFilteringOnNextRender_; bool takeScreenshotOnNextRender_; bool cameraJustInitialized_; int totalPoints_; int totalPolygons_; int lastDrawnCloudsCount_; float renderingTime_; double lastPostRenderEventTime_; double lastPoseEventTime_; std::map bufferedStatsData_; bool visualizingMesh_; bool exportedMeshUpdated_; pcl::TextureMesh::Ptr optTextureMesh_; cv::Mat optTexture_; rtabmap::Mesh optMesh_; int optRefId_; rtabmap::Transform * optRefPose_; // App crashes when loading native library if not dynamic std::list measures_; // In opengl frame bool measuresUpdated_; bool metricSystem_; float measuringTextSize_; float snapAxisThr_; std::vector snapAxes_; int measuringMode_; bool addMeasureClicked_; bool teleportClicked_; bool removeMeasureClicked_; std::vector measuringTmpPts_; // In opengl frame std::vector measuringTmpNormals_; // In opengl frame pcl::PointCloud::Ptr targetPoint_; pcl::PointCloud::Ptr quadSample_; std::vector quadSamplePolygons_; // main_scene_ includes all drawable object for visualizing Tango device's // movement and point cloud. Scene main_scene_; UTimer fpsTime_; std::list rtabmapEvents_; std::list sensorEvents_; std::list poseEvents_; rtabmap::Transform mapToOdom_; boost::mutex cameraMutex_; boost::mutex rtabmapMutex_; boost::mutex meshesMutex_; boost::mutex sensorMutex_; boost::mutex poseMutex_; boost::mutex renderingMutex_; USemaphore screenshotReady_; std::map createdMeshes_; std::map rawPoses_; std::pair status_; rtabmap::ProgressionStatus progressionStatus_; #ifndef __ANDROID__ void * swiftClassPtr_; void(*swiftInitCallback)(void *, int, const char *); void(*swiftStatsUpdatedCallback)(void *, int, int, int, int, float, int, int, int, int, int ,int, float, int, float, int, float, float, float, float, int, int, float, float, float, float, float, float); void(*swiftCameraInfoEventCallback)(void *, int, const char *, const char *); #endif }; #endif // TANGO_POINT_CLOUD_POINT_CLOUD_APP_H_ rtabmap-0.22.1/app/android/jni/background_renderer.cc000066400000000000000000000163151503504370700225300ustar00rootroot00000000000000/* * Copyright 2018 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // This modules handles drawing the passthrough camera image into the OpenGL // scene. #include "background_renderer.h" #include namespace { const std::string kVertexShader = "attribute vec4 a_Position;\n" "attribute vec2 a_TexCoord;\n" "varying vec2 v_TexCoord;\n" "void main() {\n" " gl_Position = a_Position;\n" " v_TexCoord = a_TexCoord;\n" "}\n"; const std::string kFragmentShaderOES = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "varying vec2 v_TexCoord;\n" "uniform samplerExternalOES sTexture;\n" "uniform bool uRedUnknown;\n" "void main() {\n" " vec4 sample = texture2D(sTexture, v_TexCoord);\n" " float grey = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;\n" " gl_FragColor = vec4(grey, uRedUnknown?0.0:grey, uRedUnknown?0.0:grey, 0.5);\n" "}\n"; const std::string kFragmentShaderBlendingOES = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "varying vec2 v_TexCoord;\n" "uniform samplerExternalOES sTexture;\n" "uniform sampler2D uDepthTexture;\n" "uniform vec2 uScreenScale;\n" "uniform bool uRedUnknown;\n" "void main() {\n" " vec4 sample = texture2D(sTexture, v_TexCoord);\n" " vec2 coord = uScreenScale * gl_FragCoord.xy;\n;" " vec4 depthPacked = texture2D(uDepthTexture, coord);\n" " float depth = dot(depthPacked, 1./vec4(1.,255.,65025.,16581375.));\n" " if(depth > 0.0)\n" " gl_FragColor = vec4(sample.r, sample.g, sample.b, 0.5);\n" " else {\n" " float grey = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;\n" " gl_FragColor = vec4(grey, uRedUnknown?0.0:grey, uRedUnknown?0.0:grey, 0.5);\n" " }\n" "}\n"; const std::string kFragmentShader = "precision mediump float;\n" "varying vec2 v_TexCoord;\n" "uniform sampler2D sTexture;\n" "uniform bool uRedUnknown;\n" "void main() {\n" " vec4 sample = texture2D(sTexture, v_TexCoord);\n" " float grey = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;\n" " gl_FragColor = vec4(grey, uRedUnknown?0.0:grey, uRedUnknown?0.0:grey, 0.5);\n" "}\n"; const std::string kFragmentShaderBlending = "precision mediump float;\n" "varying vec2 v_TexCoord;\n" "uniform sampler2D sTexture;\n" "uniform sampler2D uDepthTexture;\n" "uniform vec2 uScreenScale;\n" "uniform bool uRedUnknown;\n" "void main() {\n" " vec4 sample = texture2D(sTexture, v_TexCoord);\n" " vec2 coord = uScreenScale * gl_FragCoord.xy;\n;" " vec4 depthPacked = texture2D(uDepthTexture, coord);\n" " float depth = dot(depthPacked, 1./vec4(1.,255.,65025.,16581375.));\n" " if(depth > 0.0)\n" " gl_FragColor = vec4(sample.r, sample.g, sample.b, 0.5);\n" " else {\n" " float grey = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;\n" " gl_FragColor = vec4(grey, uRedUnknown?0.0:grey, uRedUnknown?0.0:grey, 0.5);\n" " }\n" "}\n"; /* To debug depth texture const std::string kFragmentShader = "precision mediump float;\n" "varying vec2 v_TexCoord;\n" "uniform sampler2D sTexture;\n" "void main() {\n" " float uNearZ = 0.2;\n" " float uFarZ = 1000.0;\n" " float depth = texture2D(sTexture, v_TexCoord).r;\n" " float num = (2.0 * uNearZ * uFarZ);\n" " float diff = (uFarZ - uNearZ);\n" " float add = (uFarZ + uNearZ);\n" " float ndcDepth = depth * 2.0 - 1.0;\n" // Back to NDC " float linearDepth = num / (add - ndcDepth * diff);\n" // inverse projection matrix " float grey = linearDepth/3.0;\n" " gl_FragColor = vec4(grey, grey, grey, 0.5);\n" "}\n"; */ } // namespace std::vector BackgroundRenderer::shaderPrograms_; BackgroundRenderer::~BackgroundRenderer() { for(unsigned int i=0; i::value == kNumVertices * 2, "Incorrect kVertices length"); GLuint program = shaderPrograms_[depthTexture>0?1:0]; glUseProgram(program); glDepthMask(GL_FALSE); glEnable (GL_BLEND); glActiveTexture(GL_TEXTURE0); #ifdef __ANDROID__ if(oes_) glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_); else #endif glBindTexture(GL_TEXTURE_2D, texture_id_); if(depthTexture>0) { // Texture activate unit 1 glActiveTexture(GL_TEXTURE1); // Bind the texture to this unit. glBindTexture(GL_TEXTURE_2D, depthTexture); // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 1. GLuint depth_texture_handle = glGetUniformLocation(program, "uDepthTexture"); glUniform1i(depth_texture_handle, 1); GLuint screenScale_handle = glGetUniformLocation(program, "uScreenScale"); glUniform2f(screenScale_handle, 1.0f/(float)screenWidth, 1.0f/(float)screenHeight); } GLuint screenScale_handle = glGetUniformLocation(program, "uRedUnknown"); glUniform1i(screenScale_handle, redUnknown); GLuint attributeVertices = glGetAttribLocation(program, "a_Position"); GLuint attributeUvs = glGetAttribLocation(program, "a_TexCoord"); glVertexAttribPointer(attributeVertices, 2, GL_FLOAT, GL_FALSE, 0, BackgroundRenderer_kVerticesDevice); glVertexAttribPointer(attributeUvs, 2, GL_FLOAT, GL_FALSE, 0, transformed_uvs?transformed_uvs:BackgroundRenderer_kTexCoord); glEnableVertexAttribArray(attributeVertices); glEnableVertexAttribArray(attributeUvs); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(attributeVertices); glDisableVertexAttribArray(attributeUvs); glUseProgram(0); glDepthMask(GL_TRUE); glDisable (GL_BLEND); tango_gl::util::CheckGlError("BackgroundRenderer::Draw() error"); } rtabmap-0.22.1/app/android/jni/background_renderer.h000066400000000000000000000045161503504370700223720ustar00rootroot00000000000000/* * Copyright 2018 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef C_ARCORE_AUGMENTED_IMAGE_BACKGROUND_RENDERER_H_ #define C_ARCORE_AUGMENTED_IMAGE_BACKGROUND_RENDERER_H_ #ifdef __ANDROID__ #include #include #else // __APPLE__ #include #include #endif #include #include "util.h" static const GLfloat BackgroundRenderer_kVerticesDevice[] = { -1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f, }; //static const GLfloat BackgroundRenderer_kVerticesView[] = { // 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, //}; static const GLfloat BackgroundRenderer_kVerticesView[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; static const GLfloat BackgroundRenderer_kTexCoord[] = { 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, }; //android phone //11 10 01 00 // portrait //01 11 00 10 // left //10 00 11 01 // right //00 01 10 11 // down // This class renders the passthrough camera image into the OpenGL frame. class BackgroundRenderer { public: // Positions of the quad vertices in clip space (X, Y). static constexpr int kNumVertices = 4; public: BackgroundRenderer() = default; ~BackgroundRenderer(); // Sets up OpenGL state. Must be called on the OpenGL thread and before any // other methods below. void InitializeGlContent(GLuint textureId, bool oes); // Draws the background image. This methods must be called for every ArFrame // returned by ArSession_update() to catch display geometry change events. void Draw(const float * transformed_uvs, const GLuint & depthTexture, int screenWidth, int screenHeight, bool redUnknown); private: static std::vector shaderPrograms_; GLuint texture_id_; bool oes_ = false; }; #endif // C_ARCORE_AUGMENTED_IMAGE_BACKGROUND_RENDERER_H_ rtabmap-0.22.1/app/android/jni/bounding_box_drawable.h000066400000000000000000000105351503504370700227010ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef BOUNDING_BOX_DRAWABLE_H_ #define BOUNDING_BOX_DRAWABLE_H_ #include "tango-gl/line.h" class BoundingBoxDrawable : public tango_gl::Line { public: BoundingBoxDrawable() : Line(3.0f, GL_LINES) { vec_vertices_.resize(24); } void updateVertices(const pcl::PointXYZ & min, const pcl::PointXYZ & max) { int index = 0; vec_vertices_[index].x = min.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = min.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = max.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = min.y; vec_vertices_[index++].z = max.z; vec_vertices_[index].x = min.x; vec_vertices_[index].y = max.y; vec_vertices_[index++].z = max.z; } }; #endif // TANGO_GL_LINE_H_ rtabmap-0.22.1/app/android/jni/graph_drawable.cpp000066400000000000000000000131571503504370700216630ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "graph_drawable.h" #include "rtabmap/utilite/ULogger.h" #include "util.h" #ifdef __ANDROID__ #include #else //__APPLE__ #include #endif GraphDrawable::GraphDrawable( GLuint shaderProgram, const std::map & poses, const std::multimap & links) : vertex_buffers_(0), pose_(1.0f), visible_(true), lineWidth_(3.0f), shader_program_(shaderProgram) { UASSERT(!poses.empty()); glGenBuffers(1, &vertex_buffers_); if(vertex_buffers_) { LOGI("Creating vertex buffer %d", vertex_buffers_); std::vector vertices = std::vector(poses.size()*3); int i=0; std::map idsToIndices; for(std::map::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter) { vertices[i*3] = iter->second.x(); vertices[i*3+1] = iter->second.y(); vertices[i*3+2] = iter->second.z(); idsToIndices.insert(std::make_pair(iter->first, i)); ++i; } glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (int)vertices.size(), (const void *)vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGI("OpenGL: Could not allocate point cloud (0x%x)\n", error); vertex_buffers_ = 0; } else if(links.size()) { neighborIndices_.resize(links.size() * 2); loopClosureIndices_.resize(links.size() * 2); int oiNeighbors = 0; int oiLoopClosures = 0; for(std::multimap::const_iterator iter=links.begin(); iter!=links.end(); ++iter) { std::map::const_iterator jterFrom = idsToIndices.find(iter->second.from()); std::map::const_iterator jterTo = idsToIndices.find(iter->second.to()); if(jterFrom != idsToIndices.end() && jterTo != idsToIndices.end()) { if(iter->second.type() == rtabmap::Link::kNeighbor) { neighborIndices_[oiNeighbors++] = (unsigned short)jterFrom->second; neighborIndices_[oiNeighbors++] = (unsigned short)jterTo->second; } else { loopClosureIndices_[oiLoopClosures++] = (unsigned short)jterFrom->second; loopClosureIndices_[oiLoopClosures++] = (unsigned short)jterTo->second; } } } neighborIndices_.resize(oiNeighbors); loopClosureIndices_.resize(oiLoopClosures); } } } GraphDrawable::~GraphDrawable() { LOGI("Freeing cloud buffer %d", vertex_buffers_); if (vertex_buffers_) { glDeleteBuffers(1, &vertex_buffers_); tango_gl::util::CheckGlError("GraphDrawable::~GraphDrawable()"); vertex_buffers_ = 0; } } void GraphDrawable::setPose(const rtabmap::Transform & pose) { UASSERT(!pose.isNull()); pose_ = glmFromTransform(pose); } void GraphDrawable::Render(const glm::mat4 & projectionMatrix, const glm::mat4 & viewMatrix) { if(vertex_buffers_ && (neighborIndices_.size() || loopClosureIndices_.size()) && visible_) { glUseProgram(shader_program_); glLineWidth(lineWidth_); GLuint mvp_handle_ = glGetUniformLocation(shader_program_, "mvp"); glm::mat4 mvp_mat = projectionMatrix * viewMatrix * pose_; glUniformMatrix4fv(mvp_handle_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); GLuint color_handle = glGetUniformLocation(shader_program_, "color"); GLint attribute_vertex = glGetAttribLocation(shader_program_, "vertex"); glEnableVertexAttribArray(attribute_vertex); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_); glVertexAttribPointer(attribute_vertex, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), 0); if(neighborIndices_.size()) { glUniform3f(color_handle, 1.0f, 0.0f, 0.0f); // blue for neighbors glDrawElements(GL_LINES, neighborIndices_.size(), GL_UNSIGNED_SHORT, neighborIndices_.data()); } if(loopClosureIndices_.size()) { glUniform3f(color_handle, 0.0f, 0.0f, 1.0f); // red for loop closures glDrawElements(GL_LINES, loopClosureIndices_.size(), GL_UNSIGNED_SHORT, loopClosureIndices_.data()); } glDisableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(0); tango_gl::util::CheckGlError("GraphDrawable::Render()"); } } rtabmap-0.22.1/app/android/jni/graph_drawable.h000066400000000000000000000050161503504370700213230ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GRAPH_DRAWABLE_H_ #define GRAPH_DRAWABLE_H_ #ifdef __ANDROID__ #include #endif #include #include #include #include #include #include #include #include "util.h" class GraphDrawable { public: GraphDrawable( GLuint shaderProgram, const std::map & poses, const std::multimap & links); virtual ~GraphDrawable(); void setPose(const rtabmap::Transform & mapToOdom); void setVisible(bool visible) {visible_=visible;} void Render(const glm::mat4 & projectionMatrix, const glm::mat4 & viewMatrix); private: // Vertex buffer of the point cloud geometry. GLuint vertex_buffers_; std::vector neighborIndices_; std::vector loopClosureIndices_; glm::mat4 pose_; bool visible_; float lineWidth_; GLuint shader_program_; }; #endif // GRAPH_DRAWABLE_H_ rtabmap-0.22.1/app/android/jni/jni_interface.cpp000066400000000000000000000603011503504370700215120ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define GLM_FORCE_RADIANS #include #include #include #ifdef __cplusplus extern "C" { #endif void GetJStringContent(JNIEnv *AEnv, jstring AStr, std::string &ARes) { if (!AStr) { ARes.clear(); return; } const char *s = AEnv->GetStringUTFChars(AStr,NULL); ARes=s; AEnv->ReleaseStringUTFChars(AStr,s); } inline jlong jptr(RTABMapApp *native_computer_vision_application) { return reinterpret_cast(native_computer_vision_application); } inline RTABMapApp *native(jlong ptr) { return reinterpret_cast(ptr); } JNIEXPORT jlong JNICALL Java_com_introlab_rtabmap_RTABMapLib_createNativeApplication( JNIEnv* env, jclass, jobject activity) { return jptr(new RTABMapApp(env, activity)); } JNIEXPORT void Java_com_introlab_rtabmap_RTABMapLib_destroyNativeApplication( JNIEnv *, jclass, jlong native_application) { if(native_application) { delete native(native_application); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setScreenRotation( JNIEnv* env, jclass, jlong native_application, int displayRotation, int cameraRotation) { if(native_application) { return native(native_application)->setScreenRotation(displayRotation, cameraRotation); } else { UERROR("native_application is null!"); } } JNIEXPORT int JNICALL Java_com_introlab_rtabmap_RTABMapLib_openDatabase( JNIEnv* env, jclass, jlong native_application, jstring databasePath, bool databaseInMemory, bool optimize, bool clearDatabase) { std::string databasePathC; GetJStringContent(env,databasePath,databasePathC); if(native_application) { return native(native_application)->openDatabase(databasePathC, databaseInMemory, optimize, clearDatabase); } else { UERROR("native_application is null!"); return -1; } } JNIEXPORT bool JNICALL Java_com_introlab_rtabmap_RTABMapLib_recover( JNIEnv* env, jclass, jlong native_application, jstring from, jstring to) { if(native_application) { std::string toC; GetJStringContent(env,to,toC); std::string fromC; GetJStringContent(env,from,fromC); return native(native_application)->recover(fromC, toC); } else { UERROR("native_application is null!"); return -1; } } JNIEXPORT bool JNICALL Java_com_introlab_rtabmap_RTABMapLib_isBuiltWith( JNIEnv* env, jclass, jlong native_application, int cameraDriver) { if(native_application) { return native(native_application)->isBuiltWith(cameraDriver); } else { UERROR("native_application is null!"); return false; } } JNIEXPORT bool JNICALL Java_com_introlab_rtabmap_RTABMapLib_startCamera( JNIEnv* env, jclass, jlong native_application, jobject iBinder, jobject context, jobject activity, int driver) { if(native_application) { return native(native_application)->startCamera(env, iBinder, context, activity, driver); } else { UERROR("native_application is null!"); return false; } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_initGlContent( JNIEnv*, jclass, jlong native_application) { if(native_application) { native(native_application)->InitializeGLContent(); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setupGraphic( JNIEnv*, jclass, jlong native_application, jint width, jint height) { if(native_application) { native(native_application)->SetViewPort(width, height); } else { UERROR("native_application is null!"); } } JNIEXPORT int JNICALL Java_com_introlab_rtabmap_RTABMapLib_render( JNIEnv*, jclass, jlong native_application) { if(native_application) { return native(native_application)->Render(); } else { UERROR("native_application is null!"); return -1; } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_stopCamera( JNIEnv*, jclass, jlong native_application) { if(native_application) { native(native_application)->stopCamera(); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setCamera( JNIEnv*, jclass, jlong native_application, int camera_index) { if(native_application) { using namespace tango_gl; GestureCamera::CameraType cam_type = static_cast(camera_index); native(native_application)->SetCameraType(cam_type); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_onTouchEvent( JNIEnv*, jclass, jlong native_application, int touch_count, int event, float x0, float y0, float x1, float y1) { if(native_application) { using namespace tango_gl; GestureCamera::TouchEvent touch_event = static_cast(event); native(native_application)->OnTouchEvent(touch_count, touch_event, x0, y0, x1, y1); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setPausedMapping( JNIEnv*, jclass, jlong native_application, bool paused) { if(native_application) { return native(native_application)->setPausedMapping(paused); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setOnlineBlending( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setOnlineBlending(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMapCloudShown( JNIEnv*, jclass, jlong native_application, bool shown) { if(native_application) { return native(native_application)->setMapCloudShown(shown); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setOdomCloudShown( JNIEnv*, jclass, jlong native_application, bool shown) { if(native_application) { return native(native_application)->setOdomCloudShown(shown); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMeshRendering( JNIEnv*, jclass, jlong native_application, bool enabled, bool withTexture) { if(native_application) { return native(native_application)->setMeshRendering(enabled, withTexture); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setPointSize( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setPointSize(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setFOV( JNIEnv*, jclass, jlong native_application, float fov) { if(native_application) { return native(native_application)->setFOV(fov); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setOrthoCropFactor( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setOrthoCropFactor(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setGridRotation( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setGridRotation(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setLighting( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setLighting(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setBackfaceCulling( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setBackfaceCulling(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setWireframe( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setWireframe(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setLocalizationMode( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setLocalizationMode(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setTrajectoryMode( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setTrajectoryMode(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setGraphOptimization( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setGraphOptimization(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setNodesFiltering( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setNodesFiltering(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setGraphVisible( JNIEnv*, jclass, jlong native_application, bool visible) { if(native_application) { return native(native_application)->setGraphVisible(visible); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setGridVisible( JNIEnv*, jclass, jlong native_application, bool visible) { if(native_application) { return native(native_application)->setGridVisible(visible); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setRawScanSaved( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setRawScanSaved(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setFullResolution( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setFullResolution(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setSmoothing( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setSmoothing(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setDepthBleedingError( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setDepthBleedingError(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setDepthFromMotion( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setDepthFromMotion(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setCameraColor( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setCameraColor(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setAppendMode( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setAppendMode(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setUpstreamRelocalizationAccThr( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setUpstreamRelocalizationAccThr(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setDataRecorderMode( JNIEnv*, jclass, jlong native_application, bool enabled) { if(native_application) { return native(native_application)->setDataRecorderMode(enabled); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMaxCloudDepth( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setMaxCloudDepth(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMinCloudDepth( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setMinCloudDepth(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setCloudDensityLevel( JNIEnv*, jclass, jlong native_application, int value) { if(native_application) { return native(native_application)->setCloudDensityLevel(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMeshAngleTolerance( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setMeshAngleTolerance(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMeshTriangleSize( JNIEnv*, jclass, jlong native_application, int value) { if(native_application) { return native(native_application)->setMeshTriangleSize(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setClusterRatio( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setClusterRatio(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMaxGainRadius( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setMaxGainRadius(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setRenderingTextureDecimation( JNIEnv*, jclass, jlong native_application, int value) { if(native_application) { return native(native_application)->setRenderingTextureDecimation(value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setBackgroundColor( JNIEnv*, jclass, jlong native_application, float value) { if(native_application) { return native(native_application)->setBackgroundColor(value); } else { UERROR("native_application is null!"); } } JNIEXPORT jint JNICALL Java_com_introlab_rtabmap_RTABMapLib_setMappingParameter( JNIEnv* env, jclass, jlong native_application, jstring key, jstring value) { if(native_application) { std::string keyC, valueC; GetJStringContent(env,key,keyC); GetJStringContent(env,value,valueC); return native(native_application)->setMappingParameter(keyC, valueC); } else { UERROR("native_application is null!"); return -1; } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setGPS( JNIEnv*, jclass, jlong native_application, double stamp, double longitude, double latitude, double altitude, double accuracy, double bearing) { if(native_application) { return native(native_application)->setGPS(rtabmap::GPS(stamp, longitude, latitude, altitude, accuracy, bearing)); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_addEnvSensor( JNIEnv*, jclass, jlong native_application, int type, float value) { if(native_application) { return native(native_application)->addEnvSensor(type, value); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_save( JNIEnv* env, jclass, jlong native_application, jstring databasePath) { if(native_application) { std::string databasePathC; GetJStringContent(env,databasePath,databasePathC); return native(native_application)->save(databasePathC); } else { UERROR("native_application is null!"); } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_cancelProcessing( JNIEnv* env, jclass, jlong native_application) { if(native_application) { return native(native_application)->cancelProcessing(); } else { UERROR("native_application is null!"); } } JNIEXPORT bool JNICALL Java_com_introlab_rtabmap_RTABMapLib_exportMesh( JNIEnv* env, jclass, jlong native_application, float cloudVoxelSize, bool regenerateCloud, bool meshing, int textureSize, int textureCount, int normalK, bool optimized, float optimizedVoxelSize, int optimizedDepth, int optimizedMaxPolygons, float optimizedColorRadius, bool optimizedCleanWhitePolygons, int optimizedMinClusterSize, float optimizedMaxTextureDistance, int optimizedMinTextureClusterSize, bool blockRendering) { if(native_application) { return native(native_application)->exportMesh( cloudVoxelSize, regenerateCloud, meshing, textureSize, textureCount, normalK, optimized, optimizedVoxelSize, optimizedDepth, optimizedMaxPolygons, optimizedColorRadius, optimizedCleanWhitePolygons, optimizedMinClusterSize, optimizedMaxTextureDistance, optimizedMinTextureClusterSize, 0, blockRendering); } else { UERROR("native_application is null!"); return false; } } JNIEXPORT bool JNICALL Java_com_introlab_rtabmap_RTABMapLib_postExportation( JNIEnv* env, jclass, jlong native_application, bool visualize) { if(native_application) { return native(native_application)->postExportation(visualize); } else { UERROR("native_application is null!"); return false; } } JNIEXPORT bool JNICALL Java_com_introlab_rtabmap_RTABMapLib_writeExportedMesh( JNIEnv* env, jclass, jlong native_application, jstring directory, jstring name) { if(native_application) { std::string directoryC; GetJStringContent(env,directory,directoryC); std::string nameC; GetJStringContent(env,name,nameC); return native(native_application)->writeExportedMesh(directoryC, nameC); } else { UERROR("native_application is null!"); return false; } } JNIEXPORT int JNICALL Java_com_introlab_rtabmap_RTABMapLib_postProcessing( JNIEnv* env, jclass, jlong native_application, int approach) { if(native_application) { return native(native_application)->postProcessing(approach); } else { UERROR("native_application is null!"); return -1; } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_postOdometryEvent( JNIEnv* env, jclass, jlong native_application, float x, float y, float z, float qx, float qy, float qz, float qw, float rgb_fx, float rgb_fy, float rgb_cx, float rgb_cy, float rgbFrameX, float rgbFrameY, float rgbFrameZ, float rgbFrameQX, float rgbFrameQY, float rgbFrameQZ, float rgbFrameQW, double stamp, jobject yPlane, jobject uPlane, jobject vPlane, int yPlaneLen, int rgbWidth, int rgbHeight, int rgbFormat, jobject points, int pointsLen, float vx, float vy, float vz, float vqx, float vqy, float vqz, float vqw, //view matrix float p00, float p11, float p02, float p12, float p22, float p32, float p23, // projection matrix float t0, float t1, float t2, float t3, float t4, float t5, float t6, float t7) // tex coord { if(native_application) { void *yPtr = env->GetDirectBufferAddress(yPlane); void *uPtr = env->GetDirectBufferAddress(uPlane); void *vPtr = env->GetDirectBufferAddress(vPlane); float *pointsPtr = (float *)env->GetDirectBufferAddress(points); native(native_application)->postOdometryEvent( rtabmap::Transform(x,y,z,qx,qy,qz,qw), rgb_fx,rgb_fy,rgb_cx,rgb_cy, 0,0,0,0, rtabmap::Transform(rgbFrameX, rgbFrameY, rgbFrameZ, rgbFrameQX, rgbFrameQY, rgbFrameQZ, rgbFrameQW), rtabmap::Transform(), stamp, 0, yPtr, uPtr, vPtr, yPlaneLen, rgbWidth, rgbHeight, rgbFormat, 0,0,0,0,0, //depth 0,0,0,0,0, //conf pointsPtr, pointsLen, 4, rtabmap::Transform(vx, vy, vz, vqx, vqy, vqz, vqw), p00, p11, p02, p12, p22, p32, p23, t0, t1, t2, t3, t4, t5, t6, t7); } else { UERROR("native_application is null!"); return; } } JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_postOdometryEventDepth( JNIEnv* env, jclass, jlong native_application, float x, float y, float z, float qx, float qy, float qz, float qw, float rgb_fx, float rgb_fy, float rgb_cx, float rgb_cy, float depth_fx, float depth_fy, float depth_cx, float depth_cy, float rgbFrameX, float rgbFrameY, float rgbFrameZ, float rgbFrameQX, float rgbFrameQY, float rgbFrameQZ, float rgbFrameQW, float depthFrameX, float depthFrameY, float depthFrameZ, float depthFrameQX, float depthFrameQY, float depthFrameQZ, float depthFrameQW, double rgbStamp, double depthStamp, jobject yPlane, jobject uPlane, jobject vPlane, int yPlaneLen, int rgbWidth, int rgbHeight, int rgbFormat, jobject depth, int depthLen, int depthWidth, int depthHeight, int depthFormat, jobject points, int pointsLen, float vx, float vy, float vz, float vqx, float vqy, float vqz, float vqw, //view matrix float p00, float p11, float p02, float p12, float p22, float p32, float p23, // projection matrix float t0, float t1, float t2, float t3, float t4, float t5, float t6, float t7) // tex coord) { void *yPtr = env->GetDirectBufferAddress(yPlane); void *uPtr = env->GetDirectBufferAddress(uPlane); void *vPtr = env->GetDirectBufferAddress(vPlane); void *depthPtr = env->GetDirectBufferAddress(depth); float *pointsPtr = (float *)env->GetDirectBufferAddress(points); native(native_application)->postOdometryEvent( rtabmap::Transform(x,y,z,qx,qy,qz,qw), rgb_fx,rgb_fy,rgb_cx,rgb_cy, depth_fx,depth_fy,depth_cx,depth_cy, rtabmap::Transform(rgbFrameX, rgbFrameY, rgbFrameZ, rgbFrameQX, rgbFrameQY, rgbFrameQZ, rgbFrameQW), rtabmap::Transform(depthFrameX, depthFrameY, depthFrameZ, depthFrameQX, depthFrameQY, depthFrameQZ, depthFrameQW), rgbStamp, depthStamp, yPtr, uPtr, vPtr, yPlaneLen, rgbWidth, rgbHeight, rgbFormat, depthPtr, depthLen, depthWidth, depthHeight, depthFormat, 0,0,0,0,0, // conf pointsPtr, pointsLen, 4, rtabmap::Transform(vx, vy, vz, vqx, vqy, vqz, vqw), p00, p11, p02, p12, p22, p32, p23, t0, t1, t2, t3, t4, t5, t6, t7); } #ifdef __cplusplus } #endif rtabmap-0.22.1/app/android/jni/point_cloud_drawable.cpp000066400000000000000000001216371503504370700231040ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "point_cloud_drawable.h" #include "rtabmap/utilite/ULogger.h" #include "rtabmap/utilite/UTimer.h" #include "rtabmap/utilite/UConversion.h" #include #include "util.h" #include "pcl/common/transforms.h" #ifdef __ANDROID__ #include #else // __APPLE__ #include #endif #define LOW_DEC 2 #define LOWLOW_DEC 4 enum PointCloudShaders { kPointCloud = 0, kPointCloudBlending = 1, kPointCloudLighting = 2, kPointCloudLightingBlending = 3, kTexture = 4, kTextureBlending = 5, kTextureLighting = 6, kTextureLightingBlending = 7, kDepthPacking = 8 }; // PointCloud shaders const std::string kPointCloudVertexShader = "precision highp float;\n" "precision mediump int;\n" "attribute vec3 aVertex;\n" "attribute vec3 aColor;\n" "uniform mat4 uMVP;\n" "uniform float uPointSize;\n" "varying vec3 vColor;\n" "varying float vLightWeighting;\n" "void main() {\n" " gl_Position = uMVP*vec4(aVertex.x, aVertex.y, aVertex.z, 1.0);\n" " gl_PointSize = uPointSize;\n" " vLightWeighting = 1.0;\n" " vColor = aColor;\n" "}\n"; const std::string kPointCloudLightingVertexShader = "precision highp float;\n" "precision mediump int;\n" "attribute vec3 aVertex;\n" "attribute vec3 aNormal;\n" "attribute vec3 aColor;\n" "uniform mat4 uMVP;\n" "uniform mat3 uN;\n" "uniform vec3 uLightingDirection;\n" "uniform float uPointSize;\n" "varying vec3 vColor;\n" "varying float vLightWeighting;\n" "void main() {\n" " gl_Position = uMVP*vec4(aVertex.x, aVertex.y, aVertex.z, 1.0);\n" " gl_PointSize = uPointSize;\n" " vec3 transformedNormal = uN * aNormal;\n" " vLightWeighting = max(dot(transformedNormal, uLightingDirection)*0.5+0.5, 0.0);\n" " if(vLightWeighting<0.5)" " vLightWeighting=0.5;\n" " vColor = aColor;\n" "}\n"; const std::string kPointCloudFragmentShader = "precision highp float;\n" "precision mediump int;\n" "uniform float uGainR;\n" "uniform float uGainG;\n" "uniform float uGainB;\n" "varying vec3 vColor;\n" "varying float vLightWeighting;\n" "void main() {\n" " vec4 textureColor = vec4(vColor.z, vColor.y, vColor.x, 1.0);\n" " gl_FragColor = vec4(textureColor.r * uGainR * vLightWeighting, textureColor.g * uGainG * vLightWeighting, textureColor.b * uGainB * vLightWeighting, textureColor.a);\n" "}\n"; const std::string kPointCloudBlendingFragmentShader = "precision highp float;\n" "precision mediump int;\n" "uniform float uGainR;\n" "uniform float uGainG;\n" "uniform float uGainB;\n" "uniform float uNearZ;\n" "uniform float uFarZ;\n" "uniform sampler2D uDepthTexture;\n" "uniform vec2 uScreenScale;\n" "varying vec3 vColor;\n" "varying float vLightWeighting;\n" "void main() {\n" " vec4 textureColor = vec4(vColor.z, vColor.y, vColor.x, 1.0);\n" " float alpha = 1.0;\n" " vec2 coord = uScreenScale * gl_FragCoord.xy;\n;" " vec4 depthPacked = texture2D(uDepthTexture, coord);\n" " float depth = dot(depthPacked, 1./vec4(1.,255.,65025.,16581375.));\n" " float num = (2.0 * uNearZ * uFarZ);\n" " float diff = (uFarZ - uNearZ);\n" " float add = (uFarZ + uNearZ);\n" " float ndcDepth = depth * 2.0 - 1.0;\n" // Back to NDC " float linearDepth = num / (add - ndcDepth * diff);\n" // inverse projection matrix " float ndcFragz = gl_FragCoord.z * 2.0 - 1.0;\n" // Back to NDC " float linearFragz = num / (add - ndcFragz * diff);\n" // inverse projection matrix " if(linearFragz > linearDepth + 0.05)\n" " alpha=0.0;\n" " gl_FragColor = vec4(textureColor.r * uGainR * vLightWeighting, textureColor.g * uGainG * vLightWeighting, textureColor.b * uGainB * vLightWeighting, alpha);\n" "}\n"; const std::string kPointCloudDepthPackingVertexShader = "precision highp float;\n" "precision mediump int;\n" "attribute vec3 aVertex;\n" "uniform mat4 uMVP;\n" "uniform float uPointSize;\n" "void main() {\n" " gl_Position = uMVP*vec4(aVertex.x, aVertex.y, aVertex.z, 1.0);\n" " gl_PointSize = uPointSize;\n" "}\n"; const std::string kPointCloudDepthPackingFragmentShader = "precision highp float;\n" "precision mediump int;\n" "void main() {\n" " vec4 enc = vec4(1.,255.,65025.,16581375.) * gl_FragCoord.z;\n" " enc = fract(enc);\n" " enc -= enc.yzww * vec2(1./255., 0.).xxxy;\n" " gl_FragColor = enc;\n" "}\n"; // Texture shaders const std::string kTextureMeshVertexShader = "precision highp float;\n" "precision mediump int;\n" "attribute vec3 aVertex;\n" "attribute vec3 aColor;\n" "attribute vec2 aTexCoord;\n" "uniform mat4 uMVP;\n" "varying vec3 vColor;\n" "varying vec2 vTexCoord;\n" "varying float vLightWeighting;\n" "void main() {\n" " gl_Position = uMVP*vec4(aVertex.x, aVertex.y, aVertex.z, 1.0);\n" " if(aTexCoord.x < 0.0) {\n" " vTexCoord.x = 1.0;\n" " vTexCoord.y = 1.0;\n" // bottom right corner " } else {\n" " vTexCoord = aTexCoord;\n" " }\n" " vColor = aColor;\n" " vLightWeighting = 1.0;\n" "}\n"; const std::string kTextureMeshLightingVertexShader = "precision highp float;\n" "precision mediump int;\n" "attribute vec3 aVertex;\n" "attribute vec3 aColor;\n" "attribute vec3 aNormal;\n" "attribute vec2 aTexCoord;\n" "uniform mat4 uMVP;\n" "uniform mat3 uN;\n" "uniform vec3 uLightingDirection;\n" "varying vec3 vColor;\n" "varying vec2 vTexCoord;\n" "varying float vLightWeighting;\n" "void main() {\n" " gl_Position = uMVP*vec4(aVertex.x, aVertex.y, aVertex.z, 1.0);\n" " if(aTexCoord.x < 0.0) {\n" " vTexCoord.x = 1.0;\n" " vTexCoord.y = 1.0;\n" // bottom right corner " } else {\n" " vTexCoord = aTexCoord;\n" " }\n" " vColor = aColor;\n" " vec3 transformedNormal = uN * aNormal;\n" " vLightWeighting = max(dot(transformedNormal, uLightingDirection)*0.5+0.5, 0.0);\n" " if(vLightWeighting<0.5) \n" " vLightWeighting=0.5;\n" "}\n"; const std::string kTextureMeshFragmentShader = "precision highp float;\n" "precision mediump int;\n" "uniform sampler2D uTexture;\n" "uniform float uGainR;\n" "uniform float uGainG;\n" "uniform float uGainB;\n" "uniform int uHideSeams;\n" "varying vec3 vColor;\n" "varying vec2 vTexCoord;\n" "varying float vLightWeighting;\n" "" "void main() {\n" " vec4 textureColor;\n" " if(uHideSeams==1) {\n" " if(vTexCoord.x>0.99 && vTexCoord.y>0.99) {\n" " textureColor = vec4(vColor.z, vColor.y, vColor.x, 1.0);\n" " } else {\n" " textureColor = texture2D(uTexture, vTexCoord);\n" " }\n" " } else {\n" " textureColor = texture2D(uTexture, vTexCoord) * vec4(vColor.z, vColor.y, vColor.x, 1.0);\n" " }\n" " gl_FragColor = vec4(textureColor.r * uGainR * vLightWeighting, textureColor.g * uGainG * vLightWeighting, textureColor.b * uGainB * vLightWeighting, textureColor.a);\n" "}\n"; const std::string kTextureMeshBlendingFragmentShader = "precision highp float;\n" "precision mediump int;\n" "uniform sampler2D uTexture;\n" "uniform sampler2D uDepthTexture;\n" "uniform float uGainR;\n" "uniform float uGainG;\n" "uniform float uGainB;\n" "uniform vec2 uScreenScale;\n" "uniform float uNearZ;\n" "uniform float uFarZ;\n" "varying vec3 vColor;\n" "varying vec2 vTexCoord;\n" "varying float vLightWeighting;\n" "" "void main() {\n" " vec4 textureColor = texture2D(uTexture, vTexCoord);\n" " float alpha = 1.0;\n" " vec2 coord = uScreenScale * gl_FragCoord.xy;\n;" " vec4 depthPacked = texture2D(uDepthTexture, coord);\n" " float depth = dot(depthPacked, 1./vec4(1.,255.,65025.,16581375.));\n" " float num = (2.0 * uNearZ * uFarZ);\n" " float diff = (uFarZ - uNearZ);\n" " float add = (uFarZ + uNearZ);\n" " float ndcDepth = depth * 2.0 - 1.0;\n" // Back to NDC " float linearDepth = num / (add - ndcDepth * diff);\n" // inverse projection matrix " float ndcFragz = gl_FragCoord.z * 2.0 - 1.0;\n" // Back to NDC " float linearFragz = num / (add - ndcFragz * diff);\n" // inverse projection matrix " if(linearFragz > linearDepth + 0.05)\n" " alpha=0.0;\n" " gl_FragColor = vec4(textureColor.r * uGainR * vLightWeighting, textureColor.g * uGainG * vLightWeighting, textureColor.b * uGainB * vLightWeighting, alpha);\n" "}\n"; std::vector PointCloudDrawable::shaderPrograms_; void PointCloudDrawable::createShaderPrograms() { if(shaderPrograms_.empty()) { shaderPrograms_.resize(9); shaderPrograms_[kPointCloud] = tango_gl::util::CreateProgram(kPointCloudVertexShader.c_str(), kPointCloudFragmentShader.c_str()); UASSERT(shaderPrograms_[kPointCloud] != 0); shaderPrograms_[kPointCloudBlending] = tango_gl::util::CreateProgram(kPointCloudVertexShader.c_str(), kPointCloudBlendingFragmentShader.c_str()); UASSERT(shaderPrograms_[kPointCloudBlending] != 0); shaderPrograms_[kPointCloudLighting] = tango_gl::util::CreateProgram(kPointCloudLightingVertexShader.c_str(), kPointCloudFragmentShader.c_str()); UASSERT(shaderPrograms_[kPointCloudLighting] != 0); shaderPrograms_[kPointCloudLightingBlending] = tango_gl::util::CreateProgram(kPointCloudLightingVertexShader.c_str(), kPointCloudBlendingFragmentShader.c_str()); UASSERT(shaderPrograms_[kPointCloudLightingBlending] != 0); shaderPrograms_[kTexture] = tango_gl::util::CreateProgram(kTextureMeshVertexShader.c_str(), kTextureMeshFragmentShader.c_str()); UASSERT(shaderPrograms_[kTexture] != 0); shaderPrograms_[kTextureBlending] = tango_gl::util::CreateProgram(kTextureMeshVertexShader.c_str(), kTextureMeshBlendingFragmentShader.c_str()); UASSERT(shaderPrograms_[kTextureBlending] != 0); shaderPrograms_[kTextureLighting] = tango_gl::util::CreateProgram(kTextureMeshLightingVertexShader.c_str(), kTextureMeshFragmentShader.c_str()); UASSERT(shaderPrograms_[kTextureLighting] != 0); shaderPrograms_[kTextureLightingBlending] = tango_gl::util::CreateProgram(kTextureMeshLightingVertexShader.c_str(), kTextureMeshBlendingFragmentShader.c_str()); UASSERT(shaderPrograms_[kTextureLightingBlending] != 0); shaderPrograms_[kDepthPacking] = tango_gl::util::CreateProgram(kPointCloudDepthPackingVertexShader.c_str(), kPointCloudDepthPackingFragmentShader.c_str()); UASSERT(shaderPrograms_[kDepthPacking] != 0); } } void PointCloudDrawable::releaseShaderPrograms() { for(unsigned int i=0; i::Ptr & cloud, const pcl::IndicesPtr & indices, float gainR, float gainG, float gainB) : vertex_buffer_(0), texture_(0), nPoints_(0), pose_(rtabmap::Transform::getIdentity()), poseGl_(1.0f), visible_(true), hasNormals_(false), gainR_(gainR), gainG_(gainG), gainB_(gainB) { index_buffers_.resize(6, 0); index_buffers_count_.resize(6, 0); updateCloud(cloud, indices); } PointCloudDrawable::PointCloudDrawable( const rtabmap::Mesh & mesh, bool createWireframe) : vertex_buffer_(0), texture_(0), nPoints_(0), pose_(rtabmap::Transform::getIdentity()), poseGl_(1.0f), visible_(true), hasNormals_(false), gainR_(1.0f), gainG_(1.0f), gainB_(1.0f) { index_buffers_.resize(6, 0); index_buffers_count_.resize(6, 0); updateMesh(mesh, createWireframe); } PointCloudDrawable::~PointCloudDrawable() { LOGI("Freeing cloud buffer %d", vertex_buffer_); if (vertex_buffer_) { glDeleteBuffers(1, &vertex_buffer_); tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()"); vertex_buffer_ = 0; } if (texture_) { glDeleteTextures(1, &texture_); tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()"); texture_ = 0; } for(size_t i=0; i & polygons, const std::vector & polygonsLowRes, bool createWireframe) { for(int i=0; i<4; ++i) { if(index_buffers_[i]) { glDeleteBuffers(1, &index_buffers_[i]); index_buffers_[i] = 0; tango_gl::util::CheckGlError("PointCloudDrawable::updatePolygons() clearing polygon buffers"); } } //LOGD("Update polygons"); if(polygons.size() && organizedToDenseIndices_.size()) { size_t polygonSize = polygons[0].vertices.size(); UASSERT(polygonSize == 3); std::vector > indexes(4); indexes[0].resize(polygons.size() * polygonSize); if(createWireframe) indexes[2].resize(indexes[0].size()*2); int oi = 0; int li = 0; for(size_t i=0; i::Ptr & cloud, const pcl::IndicesPtr & indices) { UASSERT(cloud.get() && !cloud->empty()); nPoints_ = 0; aabbMinModel_ = aabbMinWorld_ = pcl::PointXYZ(1000,1000,1000); aabbMaxModel_ = aabbMaxWorld_ = pcl::PointXYZ(-1000,-1000,-1000); if (vertex_buffer_) { glDeleteBuffers(1, &vertex_buffer_); tango_gl::util::CheckGlError("PointCloudDrawable::updateCloud() clear vertex buffer"); vertex_buffer_ = 0; } if (texture_) { glDeleteTextures(1, &texture_); tango_gl::util::CheckGlError("PointCloudDrawable::updateCloud() clear texture buffer"); texture_ = 0; } for(size_t i=0; i vertices; size_t totalPoints = 0; std::vector verticesLowRes; std::vector verticesLowLowRes; if(indices.get() && indices->size()) { totalPoints = indices->size(); vertices.resize(indices->size()*4); verticesLowRes.resize(cloud->isOrganized()?totalPoints:0); verticesLowLowRes.resize(cloud->isOrganized()?totalPoints:0); int oi_low = 0; int oi_lowlow = 0; for(unsigned int i=0; isize(); ++i) { const pcl::PointXYZRGB & pt = cloud->at(indices->at(i)); vertices[i*4] = pt.x; vertices[i*4+1] = pt.y; vertices[i*4+2] = pt.z; vertices[i*4+3] = pt.rgb; updateAABBMinMax(pt, aabbMinModel_, aabbMaxModel_); if(cloud->isOrganized()) { if(indices->at(i)%LOW_DEC == 0 && (indices->at(i)/cloud->width) % LOW_DEC == 0) { verticesLowRes[oi_low++] = i; } if(indices->at(i)%LOWLOW_DEC == 0 && (indices->at(i)/cloud->width) % LOWLOW_DEC == 0) { verticesLowLowRes[oi_lowlow++] = i; } } } verticesLowRes.resize(oi_low); verticesLowLowRes.resize(oi_lowlow); } else { totalPoints = cloud->size(); vertices.resize(cloud->size()*4); verticesLowRes.resize(cloud->isOrganized()?totalPoints:0); verticesLowLowRes.resize(cloud->isOrganized()?totalPoints:0); int oi_low = 0; int oi_lowlow = 0; for(unsigned int i=0; isize(); ++i) { const pcl::PointXYZRGB & pt = cloud->at(i); vertices[i*4] = pt.x; vertices[i*4+1] = pt.y; vertices[i*4+2] = pt.z; vertices[i*4+3] = pt.rgb; updateAABBMinMax(pt, aabbMinModel_, aabbMaxModel_); if(cloud->isOrganized()) { if(i%LOW_DEC == 0 && (i/cloud->width) % LOW_DEC == 0) { verticesLowRes[oi_low++] = i; } if(i%LOWLOW_DEC == 0 && (i/cloud->width) % LOWLOW_DEC == 0) { verticesLowLowRes[oi_lowlow++] = i; } } } verticesLowRes.resize(oi_low); verticesLowLowRes.resize(oi_lowlow); } glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (int)vertices.size(), (const void *)vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGE("OpenGL: Could not allocate point cloud (0x%x)\n", error); vertex_buffer_ = 0; return; } // vertex index buffers for(size_t i=4; i<5; ++i) { if((i==4 && !verticesLowRes.empty()) || (i==5 && !verticesLowLowRes.empty())) { glGenBuffers(1, &index_buffers_[i]); if(!index_buffers_[i]) { LOGE("OpenGL: could not generate index buffer %ld\n", i); return; } index_buffers_count_[i] = i==4?(int)verticesLowRes.size():(int)verticesLowLowRes.size(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffers_[i]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * index_buffers_count_[i], i==4?verticesLowRes.data():verticesLowLowRes.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGE("OpenGL: Could not allocate indexes (0x%x)\n", error); index_buffers_[i] = 0; return; } } } nPoints_ = (int)totalPoints; } void PointCloudDrawable::updateMesh(const rtabmap::Mesh & mesh, bool createWireframe) { UASSERT(mesh.cloud.get() && !mesh.cloud->empty()); nPoints_ = 0; aabbMinModel_ = aabbMinWorld_ = pcl::PointXYZ(1000,1000,1000); aabbMaxModel_ = aabbMaxWorld_ = pcl::PointXYZ(-1000,-1000,-1000); if (vertex_buffer_) { glDeleteBuffers(1, &vertex_buffer_); tango_gl::util::CheckGlError("PointCloudDrawable::updateMesh() clear vertex buffer"); vertex_buffer_ = 0; } for(size_t i=0; i vertices; int totalPoints = 0; std::vector polygons = mesh.polygons; std::vector polygonsLowRes; hasNormals_ = mesh.normals.get() && mesh.normals->size() == mesh.cloud->size(); UASSERT(!hasNormals_ || mesh.cloud->size() == mesh.normals->size()); if(mesh.cloud->isOrganized()) // assume organized mesh { polygonsLowRes = mesh.polygonsLowRes; // only in organized we keep the low res organizedToDenseIndices_ = std::vector(mesh.cloud->width*mesh.cloud->height, -1); totalPoints = (int)mesh.indices->size(); std::vector verticesLowRes; std::vector verticesLowLowRes; verticesLowRes.resize(totalPoints); verticesLowLowRes.resize(totalPoints); int oi_low = 0; int oi_lowlow = 0; if(texture_ && polygons.size()) { int items = hasNormals_?9:6; vertices = std::vector(mesh.indices->size()*items); for(unsigned int i=0; isize(); ++i) { const pcl::PointXYZRGB & pt = mesh.cloud->at(mesh.indices->at(i)); vertices[i*items] = pt.x; vertices[i*items+1] = pt.y; vertices[i*items+2] = pt.z; // rgb vertices[i*items+3] = pt.rgb; updateAABBMinMax(pt, aabbMinModel_, aabbMaxModel_); // texture uv int index = mesh.indices->at(i); vertices[i*items+4] = float(index % mesh.cloud->width)/float(mesh.cloud->width); //u vertices[i*items+5] = float(index / mesh.cloud->width)/float(mesh.cloud->height); //v if(hasNormals_) { // normal vertices[i*items+6] = mesh.normals->at(mesh.indices->at(i)).normal_x; vertices[i*items+7] = mesh.normals->at(mesh.indices->at(i)).normal_y; vertices[i*items+8] = mesh.normals->at(mesh.indices->at(i)).normal_z; } organizedToDenseIndices_[mesh.indices->at(i)] = i; if(mesh.indices->at(i)%LOW_DEC == 0 && (mesh.indices->at(i)/mesh.cloud->width) % LOW_DEC == 0) { verticesLowRes[oi_low++] = i; } if(mesh.indices->at(i)%LOWLOW_DEC == 0 && (mesh.indices->at(i)/mesh.cloud->width) % LOWLOW_DEC == 0) { verticesLowLowRes[oi_lowlow++] = i; } } } else { //LOGD("Organized mesh"); int items = hasNormals_?7:4; vertices = std::vector(mesh.indices->size()*items); for(unsigned int i=0; isize(); ++i) { const pcl::PointXYZRGB & pt = mesh.cloud->at(mesh.indices->at(i)); vertices[i*items] = pt.x; vertices[i*items+1] = pt.y; vertices[i*items+2] = pt.z; vertices[i*items+3] = pt.rgb; updateAABBMinMax(pt, aabbMinModel_, aabbMaxModel_); if(hasNormals_) { // normal vertices[i*items+4] = mesh.normals->at(mesh.indices->at(i)).normal_x; vertices[i*items+5] = mesh.normals->at(mesh.indices->at(i)).normal_y; vertices[i*items+6] = mesh.normals->at(mesh.indices->at(i)).normal_z; } organizedToDenseIndices_[mesh.indices->at(i)] = i; if(mesh.indices->at(i)%LOW_DEC == 0 && (mesh.indices->at(i)/mesh.cloud->width) % LOW_DEC == 0) { verticesLowRes[oi_low++] = i; } if(mesh.indices->at(i)%LOWLOW_DEC == 0 && (mesh.indices->at(i)/mesh.cloud->width) % LOWLOW_DEC == 0) { verticesLowLowRes[oi_lowlow++] = i; } } } verticesLowRes.resize(oi_low); verticesLowLowRes.resize(oi_lowlow); // vertex index buffers for(size_t i=4; i<5; ++i) { if((i==4 && !verticesLowRes.empty()) || (i==5 && !verticesLowLowRes.empty())) { glGenBuffers(1, &index_buffers_[i]); if(!index_buffers_[i]) { LOGE("OpenGL: could not generate index buffer %ld\n", i); return; } index_buffers_count_[i] = i==4?(int)verticesLowRes.size():(int)verticesLowLowRes.size(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffers_[i]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * index_buffers_count_[i], i==4?verticesLowRes.data():verticesLowLowRes.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGE("OpenGL: Could not allocate indexes (0x%x)\n", error); index_buffers_[i] = 0; return; } } } } else // assume dense mesh with texCoords set to polygons { if(texture_ && polygons.size()) { //LOGD("Dense mesh with texture (%d texCoords %d points %d polygons %dx%d)", // (int)mesh.texCoords.size(), (int)mesh.cloud->size(), (int)mesh.polygons.size(), texture.cols, texture.rows); // Texturing issue: // tex_coordinates should be linked to points, not // polygon vertices. Points linked to multiple different texCoords (different textures) should // be duplicated. totalPoints = (int)mesh.texCoords.size(); int items = hasNormals_?9:6; vertices = std::vector(mesh.texCoords.size()*items); organizedToDenseIndices_ = std::vector(totalPoints, -1); UASSERT_MSG(mesh.texCoords.size() == polygons[0].vertices.size()*polygons.size(), uFormat("%d vs %d x %d", (int)mesh.texCoords.size(), (int)polygons[0].vertices.size(), (int)polygons.size()).c_str()); unsigned int oi=0; for(unsigned int i=0; isize()); const pcl::PointXYZRGB & pt = mesh.cloud->at(v.vertices[j]); vertices[oi*items] = pt.x; vertices[oi*items+1] = pt.y; vertices[oi*items+2] = pt.z; // rgb vertices[oi*items+3] = pt.rgb; updateAABBMinMax(pt, aabbMinModel_, aabbMaxModel_); // texture uv if(mesh.texCoords[oi][0]>=0.0f) { vertices[oi*items+4] = mesh.texCoords[oi][0]; //u vertices[oi*items+5] = 1.0f-mesh.texCoords[oi][1]; //v } else { vertices[oi*items+4] = vertices[oi*items+5] = -1.0f; } if(hasNormals_) { // normal vertices[oi*items+6] = mesh.normals->at(v.vertices[j]).normal_x; vertices[oi*items+7] = mesh.normals->at(v.vertices[j]).normal_y; vertices[oi*items+8] = mesh.normals->at(v.vertices[j]).normal_z; } v.vertices[j] = (int)oi; // new vertex index UASSERT(oi < organizedToDenseIndices_.size()); organizedToDenseIndices_[oi] = oi; ++oi; } } } else { totalPoints = (int)mesh.cloud->size(); //LOGD("Dense mesh"); int items = hasNormals_?7:4; organizedToDenseIndices_ = std::vector(totalPoints, -1); vertices = std::vector(mesh.cloud->size()*items); for(unsigned int i=0; isize(); ++i) { const pcl::PointXYZRGB & pt = mesh.cloud->at(i); vertices[i*items] =pt.x; vertices[i*items+1] = pt.y; vertices[i*items+2] = pt.z; vertices[i*items+3] = pt.rgb; updateAABBMinMax(pt, aabbMinModel_, aabbMaxModel_); if(hasNormals_) { vertices[i*items+4] = mesh.normals->at(i).normal_x; vertices[i*items+5] = mesh.normals->at(i).normal_y; vertices[i*items+6] = mesh.normals->at(i).normal_z; } organizedToDenseIndices_[i] = i; } } } glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (int)vertices.size(), (const void *)vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGE("OpenGL: Could not allocate point cloud (0x%x)\n", error); vertex_buffer_ = 0; return; } if(texture_ && textureUpdate) { //GLint maxTextureSize = 0; //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); //LOGI("maxTextureSize=%d", maxTextureSize); //GLint maxTextureUnits = 0; //glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); //LOGW("maxTextureUnits=%d", maxTextureUnits); // gen texture from image glBindTexture(GL_TEXTURE_2D, texture_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); cv::Mat rgbImage; cv::cvtColor(mesh.texture, rgbImage, cv::COLOR_BGR2RGBA); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); //glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); //glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rgbImage.cols, rgbImage.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbImage.data); GLint error = glGetError(); if(error != GL_NO_ERROR) { LOGE("OpenGL: Could not allocate texture (0x%x)\n", error); texture_ = 0; glDeleteBuffers(1, &vertex_buffer_); vertex_buffer_ = 0; return; } } nPoints_ = totalPoints; updatePolygons(polygons, polygonsLowRes, createWireframe); if(!pose_.isNull()) { updateAABBWorld(pose_); } } void PointCloudDrawable::setPose(const rtabmap::Transform & pose) { UASSERT(!pose.isNull()); if(pose_ != pose) { updateAABBWorld(pose); } pose_ = pose; poseGl_ = glmFromTransform(pose); } void PointCloudDrawable::updateAABBWorld(const rtabmap::Transform & pose) { pcl::PointCloud corners; corners.resize(8); corners.at(0) = pcl::PointXYZ(aabbMinModel_.x, aabbMinModel_.y, aabbMinModel_.z); corners.at(1) = pcl::PointXYZ(aabbMinModel_.x, aabbMinModel_.y, aabbMaxModel_.z); corners.at(2) = pcl::PointXYZ(aabbMinModel_.x, aabbMaxModel_.y, aabbMinModel_.z); corners.at(3) = pcl::PointXYZ(aabbMaxModel_.x, aabbMinModel_.y, aabbMinModel_.z); corners.at(4) = pcl::PointXYZ(aabbMaxModel_.x, aabbMaxModel_.y, aabbMaxModel_.z); corners.at(5) = pcl::PointXYZ(aabbMaxModel_.x, aabbMaxModel_.y, aabbMinModel_.z); corners.at(6) = pcl::PointXYZ(aabbMaxModel_.x, aabbMinModel_.y, aabbMaxModel_.z); corners.at(7) = pcl::PointXYZ(aabbMinModel_.x, aabbMaxModel_.y, aabbMaxModel_.z); pcl::PointCloud cornersTransformed; pcl::transformPointCloud(corners, cornersTransformed, pose.toEigen3f()); aabbMinWorld_ = pcl::PointXYZ(1000,1000,1000); aabbMaxWorld_ = pcl::PointXYZ(-1000,-1000,-1000); for(unsigned int i=0; i0?kTextureLightingBlending:kTextureLighting]; } else { program = shaderPrograms_[depthTexture>0?kTextureBlending:kTexture]; } } else { if(lighting) { program = shaderPrograms_[depthTexture>0?kPointCloudLightingBlending:kPointCloudLighting]; } else { program = shaderPrograms_[depthTexture>0?kPointCloudBlending:kPointCloud]; } } glUseProgram(program); tango_gl::util::CheckGlError("Pointcloud::Render() set program"); GLuint mvp_handle = glGetUniformLocation(program, "uMVP"); glm::mat4 mv_mat = viewMatrix * poseGl_; glm::mat4 mvp_mat = projectionMatrix * mv_mat; glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, glm::value_ptr(mvp_mat)); GLint attribute_vertex = glGetAttribLocation(program, "aVertex"); glEnableVertexAttribArray(attribute_vertex); GLint attribute_color = 0; GLint attribute_texture = 0; GLint attribute_normal = 0; if(packDepthToColorChannel || !textureRendering) { GLuint point_size_handle_ = glGetUniformLocation(program, "uPointSize"); glUniform1f(point_size_handle_, pointSize); } tango_gl::util::CheckGlError("Pointcloud::Render() vertex"); if(!packDepthToColorChannel) { GLuint gainR_handle = glGetUniformLocation(program, "uGainR"); GLuint gainG_handle = glGetUniformLocation(program, "uGainG"); GLuint gainB_handle = glGetUniformLocation(program, "uGainB"); glUniform1f(gainR_handle, gainR_); glUniform1f(gainG_handle, gainG_); glUniform1f(gainB_handle, gainB_); // blending if(depthTexture > 0) { // Texture activate unit 1 glActiveTexture(GL_TEXTURE1); // Bind the texture to this unit. glBindTexture(GL_TEXTURE_2D, depthTexture); // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 1. GLuint depth_texture_handle = glGetUniformLocation(program, "uDepthTexture"); glUniform1i(depth_texture_handle, 1); GLuint zNear_handle = glGetUniformLocation(program, "uNearZ"); GLuint zFar_handle = glGetUniformLocation(program, "uFarZ"); glUniform1f(zNear_handle, nearClipPlane); glUniform1f(zFar_handle, farClipPlane); GLuint screenScale_handle = glGetUniformLocation(program, "uScreenScale"); glUniform2f(screenScale_handle, 1.0f/(float)screenWidth, 1.0f/(float)screenHeight); } if(lighting) { GLuint n_handle = glGetUniformLocation(program, "uN"); glm::mat3 normalMatrix(mv_mat); normalMatrix = glm::inverse(normalMatrix); normalMatrix = glm::transpose(normalMatrix); glUniformMatrix3fv(n_handle, 1, GL_FALSE, glm::value_ptr(normalMatrix)); GLuint lightingDirection_handle = glGetUniformLocation(program, "uLightingDirection"); glUniform3f(lightingDirection_handle, 0.0, 0.0, 1.0); // from the camera attribute_normal = glGetAttribLocation(program, "aNormal"); glEnableVertexAttribArray(attribute_normal); } if(textureRendering) { // Texture activate unit 0 glActiveTexture(GL_TEXTURE0); // Bind the texture to this unit. glBindTexture(GL_TEXTURE_2D, texture_); // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. GLuint texture_handle = glGetUniformLocation(program, "uTexture"); glUniform1i(texture_handle, 0); attribute_texture = glGetAttribLocation(program, "aTexCoord"); glEnableVertexAttribArray(attribute_texture); if(depthTexture == 0) { GLuint hideSeams_handle = glGetUniformLocation(program, "uHideSeams"); glUniform1i(hideSeams_handle, hideSeams?1:0); } } attribute_color = glGetAttribLocation(program, "aColor"); glEnableVertexAttribArray(attribute_color); } tango_gl::util::CheckGlError("Pointcloud::Render() common"); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); if(texture_) { glVertexAttribPointer(attribute_vertex, 3, GL_FLOAT, GL_FALSE, (hasNormals_?9:6)*sizeof(GLfloat), 0); if(textureRendering) { glVertexAttribPointer(attribute_texture, 2, GL_FLOAT, GL_FALSE, (hasNormals_?9:6)*sizeof(GLfloat), (GLvoid*) (4 * sizeof(GLfloat))); } if(!packDepthToColorChannel) { glVertexAttribPointer(attribute_color, 3, GL_UNSIGNED_BYTE, GL_TRUE, (hasNormals_?9:6)*sizeof(GLfloat), (GLvoid*) (3 * sizeof(GLfloat))); } if(lighting && hasNormals_) { glVertexAttribPointer(attribute_normal, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), (GLvoid*) (6 * sizeof(GLfloat))); } } else { glVertexAttribPointer(attribute_vertex, 3, GL_FLOAT, GL_FALSE, (hasNormals_?7:4)*sizeof(GLfloat), 0); if(!packDepthToColorChannel) { glVertexAttribPointer(attribute_color, 3, GL_UNSIGNED_BYTE, GL_TRUE, (hasNormals_?7:4)*sizeof(GLfloat), (GLvoid*) (3 * sizeof(GLfloat))); } if(lighting && hasNormals_) { glVertexAttribPointer(attribute_normal, 3, GL_FLOAT, GL_FALSE, 7*sizeof(GLfloat), (GLvoid*) (4 * sizeof(GLfloat))); } } tango_gl::util::CheckGlError("Pointcloud::Render() set attribute pointer"); UTimer drawTime; if((textureRendering || meshRendering) && index_buffers_[0]) { float dist = meshRendering?50.0f:16.0f; if(distanceToCameraSqr600.0f && index_buffers_[5]) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffers_[5]); glDrawElements(GL_POINTS, index_buffers_count_[5], GL_UNSIGNED_INT, 0); } else if(distanceToCameraSqr>150.0f) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffers_[4]); glDrawElements(GL_POINTS, index_buffers_count_[4], GL_UNSIGNED_INT, 0); } else { glDrawArrays(GL_POINTS, 0, nPoints_); } } else { glDrawArrays(GL_POINTS, 0, nPoints_); } //UERROR("drawTime=%fs", drawTime.ticks()); tango_gl::util::CheckGlError("Pointcloud::Render() draw"); glDisableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glUseProgram(0); tango_gl::util::CheckGlError("Pointcloud::Render() cleaning"); } } rtabmap-0.22.1/app/android/jni/point_cloud_drawable.h000066400000000000000000000126361503504370700225470ustar00rootroot00000000000000/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TANGO_POINT_CLOUD_POINT_CLOUD_DRAWABLE_H_ #define TANGO_POINT_CLOUD_POINT_CLOUD_DRAWABLE_H_ #ifdef __ANDROID__ #include #endif #include #include #include #include #include #include #include "util.h" // PointCloudDrawable is responsible for the point cloud rendering. class PointCloudDrawable { public: static void createShaderPrograms(); static void releaseShaderPrograms(); private: static std::vector shaderPrograms_; public: PointCloudDrawable( const pcl::PointCloud::Ptr & cloud, const pcl::IndicesPtr & indices, float gainR = 1.0f, float gainG = 1.0f, float gainB = 1.0f); PointCloudDrawable( const rtabmap::Mesh & mesh, bool createWireframe = false); virtual ~PointCloudDrawable(); void updatePolygons(const std::vector & polygons, const std::vector & polygonsLowRes = std::vector(), bool createWireframe = false); void updateCloud(const pcl::PointCloud::Ptr & cloud, const pcl::IndicesPtr & indices); void updateMesh(const rtabmap::Mesh & mesh, bool createWireframe = false); void setPose(const rtabmap::Transform & pose); void setVisible(bool visible) {visible_=visible;} void setGains(float gainR, float gainG, float gainB) {gainR_ = gainR; gainG_ = gainG; gainB_ = gainB;} rtabmap::Transform getPose() const {return pose_;} const glm::mat4 & getPoseGl() const {return poseGl_;} bool isVisible() const {return visible_;} bool hasMesh() const {return index_buffers_[0] != 0;} bool hasTexture() const {return texture_ != 0;} float getMinHeight() const {return minHeight_;} const pcl::PointXYZ & aabbMinModel() const {return aabbMinModel_;} const pcl::PointXYZ & aabbMaxModel() const {return aabbMaxModel_;} const pcl::PointXYZ & aabbMinWorld() const {return aabbMinWorld_;} const pcl::PointXYZ & aabbMaxWorld() const {return aabbMaxWorld_;} // Update current point cloud data. // // @param projection_mat: projection matrix from current render camera. // @param view_mat: view matrix from current render camera. // @param model_mat: model matrix for this point cloud frame. // @param vertices: all vertices in this point cloud frame. void Render( const glm::mat4 & projectionMatrix, const glm::mat4 & viewMatrix, bool meshRendering = true, float pointSize = 3.0f, bool textureRendering = false, bool lighting = true, float distanceToCamSqr = 0.0f, const GLuint & depthTexture = 0, int screenWidth = 0, // nonnull if depthTexture>0 int screenHeight = 0, // nonnull if depthTexture>0 float nearClipPlane = 0, // nonnull if depthTexture>0 float farClipPlane = 0, // nonnull if depthTexture>0 bool packDepthToColorChannel = false, bool wireFrame = false, bool hideSeams = false) const; private: template void updateAABBMinMax(const PointT & pt, pcl::PointXYZ & min, pcl::PointXYZ & max) { if(pt.xmax.x) max.x = pt.x; if(pt.y>max.y) max.y = pt.y; if(pt.z>max.z) max.z = pt.z; } void updateAABBWorld(const rtabmap::Transform & pose); private: // Vertex buffer of the point cloud geometry. GLuint vertex_buffer_; GLuint texture_; std::vector index_buffers_; std::vector index_buffers_count_; int nPoints_; rtabmap::Transform pose_; glm::mat4 poseGl_; bool visible_; bool hasNormals_; std::vector organizedToDenseIndices_; float minHeight_; // odom frame float gainR_; float gainG_; float gainB_; pcl::PointXYZ aabbMinModel_; pcl::PointXYZ aabbMaxModel_; pcl::PointXYZ aabbMinWorld_; pcl::PointXYZ aabbMaxWorld_; }; #endif // TANGO_POINT_CLOUD_POINT_CLOUD_DRAWABLE_H_ rtabmap-0.22.1/app/android/jni/quad_color.cpp000066400000000000000000000040521503504370700210430ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "quad_color.h" #include "tango-gl/util.h" static const float vertices[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; QuadColor::QuadColor(float size) { SetShader(); vertices_.resize(8); for(int i=0; i<8; ++i) { vertices_[i] = vertices[i]*size; } } QuadColor::QuadColor( float widthLeft, float widthRight, float heightBottom, float heightTop) { SetShader(); vertices_.resize(8); vertices_[0] = vertices[0]*widthLeft; vertices_[1] = vertices[1]*heightBottom; vertices_[2] = vertices[2]*widthRight; vertices_[3] = vertices[3]*heightBottom; vertices_[4] = vertices[4]*widthLeft; vertices_[5] = vertices[5]*heightTop; vertices_[6] = vertices[6]*widthRight; vertices_[7] = vertices[7]*heightTop; } void QuadColor::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glUseProgram(shader_program_); // Calculate MVP matrix and pass it to shader. glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mvp_mat = projection_mat * view_mat * model_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); glUniform4f(uniform_color_, red_, green_, blue_, alpha_); // Vertice binding glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 2, GL_FLOAT, GL_FALSE, 0, &vertices_[0]); glBindBuffer(GL_ARRAY_BUFFER, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glUseProgram(0); } rtabmap-0.22.1/app/android/jni/quad_color.h000066400000000000000000000021551503504370700205120ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_QUADCOLOR_H_ #define TANGO_GL_QUADCOLOR_H_ #include "tango-gl/drawable_object.h" class QuadColor : public tango_gl::DrawableObject { public: QuadColor(float size); QuadColor(float widthLeft, float widthRight, float heightBottom, float heightTop); QuadColor(const QuadColor& other) = delete; QuadColor& operator=(const QuadColor&) = delete; virtual ~QuadColor() {} void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; }; #endif // TANGO_GL_QUADCOLOR_H_ rtabmap-0.22.1/app/android/jni/resources/000077500000000000000000000000001503504370700202205ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/resources/text_atlas.png000066400000000000000000000621041503504370700231010ustar00rootroot00000000000000‰PNG  IHDRôxÔúsBIT|dˆ IDATxœìyümSýÿ_o¹Ht’HDüˆ(|%C†¢H2•D‘!)$EÆ ™gÉœ  ™ç)ó5Þkº×uqïu‡çïµÏ:ë¬3ïá3¬çãqŸÏY{íý~ï}ÎÙ{­÷´¤D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$C«ZÄàØHÒ ’f˜Ù!Uë“H$‰D¢€Sq¼[µ.‰j–*øøKyüD"g–ª s×^^Ûœa["1’Ⱦÿ§KºX¸@Q·Ë(#‘H$¡žu³¶ñ^Û–UëXUX²Á×¢À¨²d¶Ðe4°HÁ2|v.RV§ó·yzý² 9kz2Æk!'‘H42h,ÀÍÀ}Þ몪uªàÏÞõXÛk¿Ðk_¢¹k/HÚ6k¼üX'o™™Ü€‡$M’ôŒ¤·;€o!¯…Ëg/JzCÒ³Àäl–ºU™ºT0‡¤‹$­’5M•4¶ q“%½”ý¿€¤ó ’ÕÙà³öûº¥J]#`ŽìžSû.îRµN…¼Ì„ž*Q¶Ï*Y›oظ,]½ËäÏÄ3Á³öIÈ<˜Nk¦‡ç,÷€ì<›ÉÛ.Oy-ôØ$øìCfGä,Óç‡y»G}N ηа:ð¦'ó!*t½ózº¼\•‰‘ °mð;\©j 7»ôy¤DÙ>ófmþCà³eéâé40%“?Ök_ÁÓëþœe>íÚpPNr—ÆÍ°kügåxÞk Ìì7p°xNz,A뇙À÷ó™ÉõÙ4¯ãö¨Ë—q®ç—$wŸà:W†ÜúÔ®Á3Uꑹ{¿‡«Ö§P€1Á à¾eטäµÕ3©`6‚3Ã׸ÙkßÁkÿ{ŽòVÞ<ìîʾˆG¶M&‡n`=èE¨ oì÷—Úç|#=ü™¯þÿ q`Ûà+8îy·G]|¿ÿŠ þ eßëÉžBIAÀ ‘¶72=n¼¤ÀàDÁàâ Þð~¹Z\k šIaYigã#mošYî¦öð­OxÿÚûÿ±åm-) º;ÌÌV2³MÌìÓ’N ¶Ï!iÇdû™ÛÌìI2³ç$ùÚÅjÿkJú^ööI×å ÇêÁû3²óÿª¤u%½åm[–À"‘/µïR À&ðûKÒ)föB‰*ø7¹Ù%í[´@\ éµÀãÀÁÞ€çìïÛY¿yqVŠû$V‚^¿žó^·-31¨ØRÒèì$S¡.Å<Ì„n*Qv{¼¶ÚŒïé²ôt:ÉÓko¯ýJ¯ý;9Ê»:¸þ“¢ðq&÷p&üßdŸâï¤`Û5Þ¶_xíwem3ÍûÕ!;fxnëÛo ¶/“Ü3Âk^&Áwk °h:<ééð6[ €-‚ÏôààåìýýÀÙÔÏÆ^/úsŽôz®Hy‰Áp¹÷Ùß[”œY‹:p„3þ©èðz‡m¹ƒóýÎå5ù‘ÐsÛdÿ/ãµÏïµ?bfwõ¡ÂÁû7Ílšß`fp>Ñy¼æö!³†oa oø yÿ¿!IÀ~¸>—˜Ù9è 5~ç«ýÌ#É5xÙÌò›^ó²Àž¬å5Ýbfc*På2I{dÿÏ)éû’r‰5i‚’Æià{ü~I~Ðïg²—Ïû%­©|¬N‰DÀhI_ôš ˈÌ€2]5_ú­^Ûå’> é’t8BÒ¢M¶ýªIûÑÞÿGJêg0!xÿQàófšPýCùí>dÖ¸IÒ®ÙÿëûKºYÒ740à™.é\Úãϲ¶ñ’öÌA~ÇäRÑj|OÒ9ÙÃÿù,Ô~¯Âty*Ðeýö{å"wàYOîsÞÿ”¡ƒ§KŒ@²ûÜ[Þç^¤ûkð\|áO«Z§ª~í]‡ã¼v?XnŸä~›úÙ—ÏLÜ,½Ðª|ÀFÀa¸*ˆ¿e 4ó< Ìħ«$µ×àJ*|0 õ5Þ¨X—[‚ë¾kû½ú–9 ¸)ø¬×Ä €Éþöj‘» õPk¯ƒk0¥I¿ýòÖ)QÀO¼Ï|:°d‘òs @A€ƒõ5¿ Ò§¼ÿs-$Ifv>®òÙñª6”ÜÒÑŸ—ô7œItW3{¼.“  ù­¤OdÿŸdf·FúäÁr)h!ÏJúŽ™M¬5˪>fàföbAzßðfeZ8Bÿzƒ®ã4ú†¤ÝÍìqàT¹T×Ù$•ÞtpU }þ\‚Ì¿1à^ÚËkŸ‡·À$²nò’»R›ï\;þ’§>UlßÁyžZµžEƒ»ŸùÖÇý‹–9˜ M ÞO®D‹ Ènï½$}2Û4EÒŒ¬}u¹”%ɾYÜÛgÞ<õ1³ifö[¹´·$ýW.Úg>IgQN¥¸#åf`HÚGÒ,ÀÙ’ž—t‰¤3å2^¢÷Õôþ.q^Ia ˆ ƒn8T#Ã?¼¯ÚúÞ Þí•#f¶™¤JºÔÌ~ïµOô 9‹Û:fvFѺ$F,[ià»>]%Dÿ&@xÓ©ú&T&w6iŸ£É¶íÛJÊýƔ够 gúüФc508‘Ü `OÕ§må ®øOmŒKÍìà:I± ”t,0ÃÌ:ž?•ôå ùPI’KKó]/?ÄÕB8[õ®$=Ô©ÌAÆÌàý`šHú‚™ýUÒ_#íg«¸›ñóHkõYWÒ†Þû×従!¥•LOŽoþ¿ÃÌ _o0 ÂQÿHT ®žÿü^ÓL3»ÙïcfWâ%ºEõ3²Õ Ôk I5sìI?Å™Ùkÿ™’.ô¤¤ä ¶˜¤_JêÆ4úÍàýsf¶_¦Ãæ’®–ô‘l›Iú\<„ŸžöBVºx(ýÍíUaÚ_xo6dŤRx©~ðŽ™åº å âB9+c+* L-`AI_ðš®lÖ71ÌÀ•­½þçù€îñÚ}üÍÁ>k÷)ÿÔÀß6“&¥N;‚¾cú‘ÝF¯Ë<9{gm~¬ÈE^ß…¨_Rv­.䄱7Û¿LcnxÈé¹xPŸzTé@†úƒ«†úBTU,~åë…úQ•úTi0¢þí}Öÿ)Kî ñõá"‘÷À-ñÚ.%d8³„÷¿ŸâSó½¿mfy—i¥ÔíC°Ú]ö[.è—ûêu¸ZØ¿•3·O•ôÓNwõþ·.D>¼ÿAîyæþS“ýÿff7v!o0â¯ú7,ä±`ð~¨ÆV$†¸lÃq•Fæo¿Wn²‘s)Ö¸¢,Ùƒ)à\y¾X`e3û^‹þÃ\ÉÓE³·¯™Ù“YûrX²ˆ÷?傟ðÚ¾(霙}¬\F@,G¾ˆQ~§úœÿ[¼m~½ü÷*òáR?îmëf`r¹ê‘\}øµ%Ý-·HÓgä³b|¨ Yƒ•GU¿òç$)[‰ìsô˜zÓÌJ[t¤‚[}õ½ß¿™4ôy’üŠ“+Ê­ Q[i îfªFBí`n\n»OÕÅHJWy¬†oööód/.Hö¶‘Ï Og³õ¼uùÎ$ý^ο·m;Oþ àL`oê×’G˵â¬O÷vyî!'æ}ÊWKÁç÷í÷*DozŒÈ÷€Ýpñ6µW¬8Vžòüe˜Kɺ,d¿ÿ©Á÷n&9§W·ïW¾¼¡ýÃŒì˜|ãªÖ«l€Ý½ó?Ëk?Òkÿ]òDýºç­x† Âæ‰Öܧ¾dkŒN]þ1—îðܧáj„ìÛÿ™WCv½'zçrsû½ ÑãøàšÆRä9Cý`FÕú” nÒàSJÖ°ðn?÷®an5ºÚ…˜ ìVµNeœè}~éµÿÃkߪÕ1rÐa)àtàµè£ÏU#<– WÃÀe?„Ayãquz=îܸ૰{‰ÀEÀZÙuz>Øþ.°ežçZ&ÀÞ¹LÃ¥‡–­ƒŸ‘1+^& >öªõ)`G²ˆÞ¦¤çð+ÿºWy_­œì¦º)°xÕºŒtpV™µßMIKw nô¼5ðcàî‚>½2Îݰ+.`mºp+ E²s¬e^œP²ü‚Õv« %€×½ë^HfÏ`[ ¬Ó&Ó_õš²ä&Á@z؈ôÅŽ4€k¼Ò[ÀRí÷ÊEî(êk`L¤ÚL„°|0ð+$Î(QO6™„³xOv©Z§D¢Ž4Yd–A’«J’{h0û?° ¹ —öæ³yÕ:%‰A°3.>cÈú¶ÝüºÌ‡1°n½û)í¯$pñ4ãÓµO$‰„¤†%y§…,ø¬C}æÉÉô_À& dŒV®Z§D"‘HTÎ'ïŠ|WÏ\ @é™#`Qà$`Ãö½‰D"1"À¥E–Žü8—˯&‰D"‘hÁšCíø‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰Ä˜s$×èΖË\¨"Ù£²…:æ­B~"Ì ,R¡üÒ÷?1ɾûs)ÚÞHÒŽ’V–4_ÖïmIKºLÒf6¥HÅ":}GÒæ’1³ý ”3ZÒ^’¾,iIïÏ6M—ô”¤›%kf÷$9I?‘ôEIŸ”4k¶é-IKºZÒñföbò›è4‡Üçþ¯ù-3[=g9JúF—»lf橇°ž¤¯IZUÒG%}HÒX3[*§ã/ ÷™öÃÚf6!'}FIÚYÒf’–•ôÁlSíû½¤£Íì±<ä5ÑaGI[Júœ¤¹²æ‰’ît²™_”ìD¢*²e°w•´ž¤OIš=Û4QÒ£’.–tLaÏ^`àl`&­yX¡%êõY8ëÉ>µ@yë/´9w€·€] ¿?ðvò_¶É[~ ½þÑabr.ïàÜCvÌ[L—µ€›È|2G9 ÷pÎ!¹Xè€eû:÷ð£Õ…ܼ¿ÿ£€[»Ÿ¬‰aðuZßoBî'ï0°]DÐàßÀ?€—#Û/ÍQþ†¸ÀŒ6'Ÿûwóy<"ëEà2ܬäÈö'r’¿ îò\ví¯ÆG¶?“‡ü6ºÝÒäs(b0¦å''×°'0½‰¬IÀäû½_xµ‹×´@§[sÒãÈÈù¾ ü¸’øï?ÏÐá‘ã¿\\G|úƒ¼ä'UA|â3 ¸ ¸›ø3q—¼•oô“€ ¼íóG}È˺ä$c1Ø="ç¼ œeâ¹H¿urÿ§ÈqOÃùÞk}Šôûz¿ò[èõ³ŸC€ð&¿QÞ2ÚÈ_=¢À?oP±ÙX<¢ß9ûÑà¸Ó€oxÛ‰\›<¾ÿóÒ8À¬âõY7òy¸_Ù‰D•KÐø€ÏÌìùÝå6 ©ÍBÂYÏI‘~kGÙ?'ÂÀ4Üè'¤ˆÀÕŒÉÀ2‘~ûEôÙ3ù×Ç|“ÈÃØ:"ÿ—ýÊo¢ÓbÀëy5rdòBr\v¡Ã=üwŸ”©C+h(>ûžôxìp`q}¤ÏΑÏh÷dï9îa‘~{Gúm;f:̃³B\EÉ™OUËï\fØÂUë‘Ùµ_ "Ù›F¾ÓEú…“Ï»ò_‹0ÿ¢¤÷Ûþv6³€qrÑÐ5>“‡"“%Ý.éZIç›ÙS9¿‹ï3³G"ý呦±§¢­%i²™M‹ô{(Ò6W¤-Ž’ôaïý+ªÏÈ›ða?µÈHó`SIŸ š5³cËÒ¡¸T¸0æãì&ß“^/Dú<i{¤­[VŽ´ý%Òv²¤ßªþ^õEõŸE!IçIZß{¿¢¤5r8îP‘ß`i dˆ$éøj5Êÿ'éà I2³›J”ý°¤‚¶#ý&ïÃçuOÔ±™V3óÚ‹ª,š‡"r?€ãÍìõœŽ× '«þAÞÌ·ù¡HÛ«ý 7³û:ìºl¤í©~å‡[Jò] wH+iã¼ey|"xÿJ²b„¦ô'ÍìIÂe¼,&— û ™-Y7IÚC’ì÷–¤<'/Kòë]|,Òç“‘¶§s=_ðþ 3kˆo1³×W$-è5÷m%ʬ(kÍ«ó–q?ªZ~;€åÒÂו4[A2~¬Þ&S§™ÙË9¨0—ÜûÛÀ’ÎtJÑéîÙ$§åDXB¿½gsS‚xP´ø.(Ðç¹)—RX`ºœè2ƒˆ«  Ùó÷òÇáêä)gœi¹Æ`5à’@vÞ.€ßÇg ½˜€ Â|&Ó£ÛZÈÝGâ\>a`âTà`Õ¼uh¡ÛÀógæ,ãÔàøïë{ÛGÓè" Ì™ƒìË‚ã¾Ù¢o˜©Ð`©ìQ‡qÁqßÈã¸CE~DŸÑÀ>ÄãŽvÎYÞ‹Mä´c¥d¯ÚäØcc(ÙÑï_ݶÌSÀ ÑÑþêçsS$./¤’°~ÒÂLE¸hôƒ€C€Ói¬O07jÎ[c³ö¢'G>ëfÌÎà ’ìSöšO´Ñá-à+yÈï@¿0Hu^€\N2–¢ñ!ôÎ'}6ðtä3èÛÿŸÉ>-r}£kàÉ ßsÒaG\ì ¸Z»åqÜ¡"ßÓcà$ZÇÿÀÈÔ˜ŠË„Ù$síB¯9€K#ú\”· “"B:Z‘.¢Wé`n ¤LÛ! 3–qPã `d®‹›ùÕxªö= øÀ?Zœo3NÏIöŽ=ÈwÓ*¼L-ðp ÷†‚ä¬K½õ§S‰*õ!wˆŒÿ2°4µ:rddzvURz¸JùÀÀµ4¦™†< üŽœËCSí`nÜ„ëßÄÓ±}ÁYËøÝ‡–1pÙz}[ÝjÌÚ¾KBÒé’–ÚŽ6³kªPFÒ’NËó€8?ä‘CÒ>fŸE8›Ÿ.éIOd:­¢Æ€Ó­3Ììº>e¸IûIWÈÅ?,)WØÿÍ|LÒÏ%íݧü¦àŠí„³á‹ef×gHÚOMÊ„gÜ!é¯9Šþ»¤CTPø=à>3;IrƒIRcÐë;y)af%Ý’×ñ†‚|\`ߥr¥g›ñ¢¤+%Y`€Ü!ê- o tv;BÒ¸âv[ËÅ@}NÁvKK:XÒ^ÀfvT¿òcGª>Krò™Ynßùš°dh.?æ)'¹ ã\Œ>wR”ÕF¿/t>ˆ¥ å©[Hià÷ùÿ£„Õq¦Àu pyÒçRož7Hè;WäÈ7­¾JPæ™è97¦Â¾ÀÀa‘Ï9š_Ž«Îå33¼Vy[›"¼ñ²nÍâŠlMã:còøþeò¤Ñ¿#¼'ä!¤B|ð2°+¯D7T¾EãïŠü83 — ;5sæäÛBf ÞÇöò¿’ËÏ÷yIÒ·ÊX‰ÏÌBSà_pE‡þàµ}\Ònr9¹ýp´êM«¯H:Œú ¡ù}ê±_of…߈Ílp¯êSÔòŒM´kÒ÷&Iþ*ˆ&g¢“ƒ!»«Þ?AÎ ž+83pU|‘™}Ó{p§\zTM§Od:þº_Ìl,®ªå)’>é2YÒ1™¼Ù½öXmŽD|Dîžp.ËâB3ëÛÒÖ àD5wŵâçfVD*ôš’¶’ôåsé†Ðå÷3Ë­ìv4ši6ân úušÃÞ«n!…[pQ×ayÆ×€Ê sÐXµ¯8Ü pýRÈH¸‰¾§Fä÷ì9f4Íx5¼ïô#¿‰œi È*Ä÷üÚ¤_§ô]Ȩ_²ñºAóùf6&Ò=ôƒMÍ!(&ö=_BRl¡Øõ.¢X˪·ÂM“›A,¢»Y¬Á+rצƂMúõŒ™]"é’°ås[¡3£€™M´¤}²{ß÷$}AA •´¤íp‹@ýÄÌ®*S×¢—t–\EÊVöÆh `]a®o3û®¤ÜVÚl °dd”«Å½>¸Ã Ö-¤0 ðmÜ:>“(xQë ü§I¿Q4Zú^ Žö©/íèËl9f4Í‘ÆÂ<}¯ˆˆ‹-?ƒhŠ!.UÊç]r®#,J£¿ýÊ y Î/‹BAN«¡u cÑ…€b)hO~{L‰œÿêíÞ‘üuh̲x W¥î—¸jŒ±¥‚·ÎC¾§Ç<¸xŸû󔑹ñÙÞ#¸’È⊒ÄÖ%Ï¥ §ËÀÀU¸Œ›p•R€‰ÀŠ9Ë W-sA˜ZvÉsµW™²;·é Þç‘ë J¨<€[u2×ë]êsqð]¼¬ ¡£€»"?¶fLÖ*A¯"GtqÞ1ÖìSþhàÁäž–Ó%èDÇ¢ËÓ¾ühÈÉ9ëÐí÷ ß’œN‡#rr}È6‘{p—ç{>°M™ã/ç,sn°™”X‘úÈ™eÉí\IôÓȳ}ÅŸÅœ–RÚ»4N4¦Ó²ßí/[|¥oÛ®FÆ ÓaaàŽåÍ ç‡_ú•Qh kÎ7ãŸ`þBó¢3¡ü¼bE£}\çÙDþ1ÄgÜ1î¡€ú´ÜDA‹0Qýb@þ`F™²ƒ \Æ…Oy+£âf£‡Êxç έXú„ Ë€§Ëî¸Uÿb5¹ßÁ™F¿–—¼.ô*¥ß¿0Ø܃ù \ÚZa#b\0èĆ?+HnÌ_H©Ñ:|9ûŽÅÜ3¨…^Èõ§qðÎ%PhE:ª_ Èøæ[ê51¤È¾‹5—Ü[°è[Võ¾…ó¯..W§}œ\A‚ E)“·ØÆçäÒoÞ•ô‚Üõ/t}êÁ.g9¹"Eã%=df•(q¹Ï`>¹TЇͬPü`guXSÒÂrk1Œ—t‡™õuÑFîb’V”Kw}ÚÌÆ)/=ZÒ²’þgfE¤v¶’ýº áŒ1³ÅZõO opA¨Ÿ–»çTžªH$‰ÀÅ¿øn§‹«Ö)12Ëú&‰D¢\¾§zkì¹U)’H$‰D¢p)˜~ r©é‡‰D"‘H$*ØW×€l PZ€u"‘H$‰ Á•~> ذj]‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$ú¼Y IDAT‰a°00oÕz$ª˜X¨j=‰Dùs£ó>®õ¨Ì<’j7£§ÍìüTju¨$•¬™’¾ifÏä,çNI£ZtyWÒÛ’^’ô°¤ Íìñ4O–´m/÷¥^>ÿ H:6§c+)fN~KÒã’ž’û B6öËI‡nøª¤«€9*=ì“´ydÓd9“ÿ]’bŽ…$T j‰Anw‚¤Ù‚Mw˹ ‡#sIÚºƒ~ÛhÀM–³Iú}ÎÇlJvß9²i¦œÛo¬¤‘íßþ§"¼Æ[ÀQÀb-ö™ØxÂÛïmàÛ=ê° ðN‹QOè{ì G€÷â\µ×òÀ–À5MôY£Où_ÇYO|¦¿Æ›e£q&âiAßñô$Öæü—¾œLŽœ{èË[~»×œÊ_°_Ù™üUhœÙÍN ˜ß^|ßêC~²´ -8×à­‘k4/R¨ü¢‰È÷¹±ƒýÿÝbÿ^-5vo²On`%âÏ»¿ãYÓ¥€3q÷†ºsÖëEvLÿáÿ(ç´Úê͘ã€EzÐc¿¸`n3OºóÁÝѧgÿ[vÌØÀ"ÐRëz¤ÿ/úß© r]Ü`Ãg:}ºAº¹þE0äŸù<ÿÚ¢ÿö‘þõ!? Ú@µ€"×g ðõ2äMD¾ÏT`Ñû.J똩~/™\‘ïàœnŽü9ÒÿÊnåÖ¹pÔÓ%}(kzDÒzfvW§4³)fömIÿÌšst˦Áûå" ķÎLCEpu¤­g_8n€´vÐü¤™ý®Ån±@È/õªC§˜Ùu’ šß'iï¢eW€QrîŸI’š*Íìd9—OÇõÄÐØAqÿ¯ûÍ~ÄŒóþŸMÎÄߌmUï׬c|DR«{q_à,¼Íã%íÑb·½Õè\X¼Ùa À’–ÉþCÒföB¸näÿK\ÔÿþÀ†a¹Ø7²ÿ¿JÑòÀÊ’>4Ÿ%çëò‰ùKË –®òRÇ[_Ò¬AÛ¿Zí¥ú<'iš÷úx:tŒ™¥ÆÙË=LYCÈ›[¥ e!箽†ëÃ`Ä’Ý ¯Æ”íóÌìÐ T*‹ƒ÷á²Õ¶pß<Ø‚af³y¯Oõ¡C·Ü¼ŸØ DùÉU"m´ÛÉÌþlf;y¯XQbˆ’ÝkNVãàðIÛ—¯Q©<¢úIÕgqu'êVPýdñ%Iæ ÿ1¹Ú'5f•txÇñùHÛìwK¤­e½ƒÖ¹³Lÿc$ÕEâ Ó\$©YÐÓ²’.V7³[³¶3䊙˜\AƒN ýZÿ6³w2ŸÈ¯<½g—û!ü²‹cwÊ€-½÷”´¨¤Mä 3øœlfcúKsy¶ã•Á‘¶%wôÂB¸b'­xÊ̊ʃXºMŸÉ}~î5æ‹´5XÞJ¦“ë?¨|ôÃc$…éÔ/KÚr0H*ˆ9å&|?ÈÞÏ"çØ3è·ê'‹ÿÈöí—·å,l{m+û™YÞuh>iëdó`¤mnûߌr_é WùïL <ü'ËÍÈ”«R¶KÖ>‹¤÷Ì–fvð¬Üƒs1`Îv_\\ÖÀÂAó¥ÙñžÄ§ø3¦MTÌ`)Iírk§I:ÝÌ~Ò§¬FÚr 0*ˆ‰‘¶Øƒ¬WPû;å,PEð¹YH+î‘´b²bŸìú–I'×?Q‹©Ñ *¹ ¤•­L|@î™ó¯í«j|%x¦rr ›Ù!Àf’|ËÞÀ99W¢íõþÿj‡ÇjÊ,’”ÔÊJ"ço÷9DR­ÐÈTIß6³í3_°oúž.)¼0c²¿³Kê¤J`X~r¢$¿ä?ƒíËa]YܯF·D/„y½’û3S"myçáŽf´Í €—pi˜­^i½€áÁ<Š—j߈ †ï7³ÿ¨~¾°VíMvß÷S 1³›$½?G=öV}ðù<Ê¿6@,›-v ‰Mº:÷Z ÀÚr‘Ü’4Æ÷ý Hú®·ÏÙAä©ïx#R#ÞTlYÑ)‹CX7h¾1°œ­úDrQ U°’¤¿ìó8±â=­ÓP"aТäÖKHtO§Ÿÿ¼û÷&Ñs)¿Âcƒ™š? lݲÉÿ’tY°oߘÙ5ªŸ€JÒ&ô_äÎ'¶®J'ë’Ä]­ÑR»‰û‹J„ Il®z³BXÙ÷_LP#þúÝèä{¨:XãÿMæ¸GÒÿyÍ_æ0³NFMò€+Î/í¾«ÌÝ&i7à:3ë5pR¤m°ûW?ikµÞ Kúq›>o¶ÙÞÕÞ÷¿œdÅΣêÏÿ Õû?c,¨Ó£ï1C4IZ Øw˜gÔž§Ê™ýkÏ*?ãÌÿzÖ×ß7/ö‘‹ÔŸ?{?‹¤C¯Ú× ±û'iå1ìXM©]ÔymÏ}|óú‹fv{°}~ïÿ˜ßŸݴÕ„ÑõHúB€èÎræ—´•¤¦…Sz`º™…€G%ýx@ÒÅž&—AÑë fÊÉ¥Â\ÄòMó \›š™«â)3Ëe‡ˆýnb¦üSUïfY_ÎW\“̬e 9­‘hÉ…’žVcŸ—›ÙCèTsJ’™=ܦ÷ñÂÀ&r÷\ÿ7r›· b®3{·*í‘^óÒªOè‡ØÄéS’®k³ßÿ‹´u;V|¸ÅüEFžŽÃüŠˆ¿}l3E²0…ÁÔyëÍ•ï )fv)0Võ§O÷qÈØL2öáÖ¯úQàx3ûaztCXtfª¤kJ’=܈-éÜP7ÃÌê €ëTÜ Q=×Ê™¹ß'ék¨Ñ"¹â±j, 6\ðâ—¨>~ls5N/öþÏÍPÃÌþ˜¨ûè=•¹p_¤-–+üÕ6}اà_Ìпî?äb–üük‘íþþ­¢W·W‹Å‰:`MZ”‹,€0›¡ŸUñnˆ´µ ˜ÌªG}OÒ7½W)•à€­Ôøà¹ÛÌb. D{®‘ˬñY“AV w8|¸#xmפ{øyäér #é›f6-smî¡Æ{óZ@«jqCpšÜ‚h5Ö—äåyK®‚m¢'û™êãœÞ׬c—\­Æ ßõ³ ¡­ˆ໾Áµ®Rap—¡ òòûêYú`-Jóù6é+ýÖ´ž]Ò÷ûå·:ÿ§i¾4óK¸uÊû¢ƒëßêÖŽÈC~©«¡e:œÑä?—}ö'àVü7ñe™Â"Zʱ«â~S!pK±ž<Ù> 7ÈK‡NWãÜ‚øï¿]ÆL·ò;ýýå2#nwþ¸ûã¸Èy%0•Óxíe5ÀV«ÁîÑ£FO«fÇ­ ð p)p1+RÖè=þ ØÍ;Ð#Á¶pYÞ x¸û¼ ü ظÇkoúpæ&Ç™@k¼Dt8µÝ~MŽÕj9Èvü­™V"þ%oÇcôxãïóüŸ!§Hðå×è;'"¿ŠÀÜÀû¸WÓÞoØLöH|÷@ï†ãsÖ¡›Ðß#ú¼J1Pùrc¯2ÛÈo8àÔˆüS"ý dýohr=úŒníæâg‰D"‘H$9ÀþÄÓb^ÅEõÿ}xðš·ýèªuO$‰D"ÑÀz¸ÈÜnÒ‘:JßK$‰D"Q-áŠJ ëÈÕéÿ‚¤OJú ÊàN“[öIÿ4³ Ò3‘H$‰DÕd}UžK$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"Ñ@ÛB@‰D"‘(`1I?o²ù3;¦L}Ê8Pn ùéf¶KÉê$‰D"QÀª-Ê«_Sµ~E<Öä¼ß­Z·‘À,U+H$‰D¢|†Ì Œ®Z*ÈÎ}ªõH$‰ÄðaÖÚ?À’Fµè‹¤)’&JzLÒufvYQŠ‹HÚEÒº’–4wÖ>IÒ“’®—t¼™ÉI^»óŸ)émIã%=,é23»-Ù]V‘´½¤UäÎ}ά}š¤ç%Ý#é¯fvu²~ wó`G3»½Ö”ú9/5³Zì³§¤­ƒæÍì’.eŸ'éÿͧ™ÙQ컿¤MƒæûÌl».äÿPÒƒæCÌìo‘¾ Hú§êîß6³Ç"}¿"é·AóIfv|Ðïë’~ô›)éûfv_ ½/”ûnúüÛÌvk¶OÇ9ÊÌNk³ßa’6š¯2³}º‘Ÿë*ÅýÐݰ·™åi®?ÔÌ~‘ãñ¢d.†xM™Ù·‹–[ÃÌ–Êô8GÒwÊ’Û àï’ÏÞ>gfU©O)ïv±ìo€¯ Ë~ÀøäOöËIf·ç?¸ ø|ò3æN¦u¨Ã¥ÀB}ÊܯËónź=êðõȱNm³Ï‘}¶ïAö‘‘ãÜÔá¾Eö=¨KùFŽqt“¾[Gúþ¤IßC#}7iÒ÷îHßK[è¼U¤ÿT`ÕnÎ=;ÖO#Ǻ·Í> ƒ}¦õ"?;Þsºeó^dgòc1áà­€Ó¹3€Ï–!;Ðãœ@Jb€µp÷vŸõ«Ð¥ úu,'éïÀ®y(#IÀI’–4OÝ?$é`à”¼äwIZ]Òµ@8 ì`NI×HÚFže¦ Iº’äè‡3å¬[>+ó¶Ú XFÒ2AótIgt)ÿI“ƒ¶¥›ô=àþ¯IßP·©rV³ûJš´} X¯IÿØÌô,3»µIÿV+鉠mš V2~*·,¹Ïå=ÊÏ‹ð;4Tï³HúAŠ vTcvÜ«P¤ òˆ˜MÒá8“c_¿QüË7SÒ8Ic³ÿC¾Ò¯ü™KÒ ô¿<òá’bÖ„×%Ý'é.I/G¶FRtƘh™Ý+é yI›µÙõ›j¼QÜnfOv)ФG‚楚tÿ\¤­Ùl-<Æãf6±‰×Hú{Ð<‹¤ ÎõP^‘ôË&z´Ä̦Iй[¢“Šl º~ÞU££Lž”¼9Ìì?’îš7®B—ªæ—ôÕȦ éÓÒ:Xi5¸OÒÇƒ× r?Ì1AßÙÕçXMÒ^‘MçKZÎÌ4³ÉYÎôÛX£Âó_Bî‡ñW¹ŽÏ|’öèUPvSÛ2l–t˜¤EÌì³f¶²™}TÒ>r3MŸoÍfí8FŸsí³¬lÕ¢ÿzÔ¡jþikgö‹¹;.ïQ~x^8´ê£$}:²ï’¡µ"û>-ô{°ûHz#h[ xï{ Ì­ø÷üf6¶Íñ›bfRã hÍ&¿ç]%…¯‹²\¯¬¢æßéðþ÷J°ï4I»4\ . Þ/lU‰&Õò#e±frñnïdÿÏ)i§J4* }àw·è»0)â·Z¹ùFŽ×Ô œéqò»9ÿ#²ïìCöê‘ãÝТìÜsþ‘ÓÊ4Û«œÊb²c-EcÜEx£÷ûϼôŸ ,Ø£ü"ç²YÐçË‘>5¾ôÝ Ò§íøud¿‡½í¿‹lïçÁëËþ~äØEú=ô™ŒsÇpYDǾ­oTÉ_x;eYò3*õÏcU#tS zrdQÇ¡ÉP’Öéåx¸ÙNhzyEÎ×׌=Õh/ËTsˆý¶ýDÇâžiÑÿ¹ªÿê7ŠyÄ’}Ÿï šçÇEÈÇØLÒûƒ¶›ú˜ÿK>äд¿f‹ý¿¼_)Ò§é€Òã`¹ Ÿepz «Ñ:Sñx€®1³Sä¬n>_ñ•0cà3 ­¹ì,)ü>< æü† Ù÷ö_Aó:¸L¬°±ê³Î–t–÷~ ‚Aùp Ÿ€‡#m‰´uÂr~WŸ«ÌlB³²mWͳKj¸”™ßòÕ y¶>{د+“ž™½?xåˆ9B¹"ÒÖ,Ã%æh5ߎ,nà… 94÷¯¼÷ al@¸ï+­Rú<=¦IÚ_ƒ‘=̽ØÌþÙî¸]pXð~v¹~Ð û¶JðýgƒPÎdI?Îb8†gïg—´CŠT„o=|ÑÌÎÎRqŸõÚ¿W²N…ÓÏ V”'œwÊ*‘¶NòÉcQ¿±cå 0‡ýM+í0³‡Ô8óZHÒ-À^8Ÿn¢XΔ‹”÷ùb“¾¡oz’o Ýúèà ¾å½ÿ_R}äü²¸a]ƒ‡:UÂÌ.PãÀzaIÛmoÊŠ䆙§Æßý·2õFjÝmÐeœ ÆÁÏÌì–d—Bö°4÷Ý4È&Zþ þÒ&ÿ¯ ,YŽVåÐÏ ,Â!¹hØ^ˆ¥±ubÖ‹Y!ÊH‰ûÜÙ'Œ$ï–ÃÕ8óZ0k6ó‘ˆd˜ÙsjP. øÞZÐÒuCA`÷ïÃÝ ——½@Ð×ïÿAIkyïC3yÛÙÀ¾j?˜?¾ ‡ï¡ÁûʹC ×D9W\¡à«Y=h¾ÕÌö/Zv„E´–açÀ{üX÷ó™rÞ5þªÙÙä‡ ]€…ÓÕèg|K½›AÜ^Iz­ƒý^´åU.x¶,8§öZØW%*ŒM˜)éä~„e>ÐÔá/¹,ƒïÈÕ\x7büs%V¶4¹t?ŸXU°X÷ÐÒ%¹RÉEú¾+éJ œÛ‡ƒíTðì?sóýE÷“ƒ²‡Â°ÃÌþ«ú€Ó ÑC<ͱ)™uí XpÏ‹t;^õ.Þ$õœõ5(¡·RÀ5nèÇO \9fÛ¼v`ñÈ~•q«—óŸ‚«Iž;ÀÀ-¸Òœ­¸”‚F¤Œ4Àà¸WF>ãù³mËÓX&ôÜ~ez²ïŒxÜk{Ùëÿ´×þbÖvbpŒ˜›¬]‹\ßPð,¸¶…üË”É?6"·ïA~Y•¦ºü(¢Ëî%È­< p$Òo%Àér>’Ììv[›É¨ƒýæ‹´õ£G7L“´®™’df'™Ùjr3º$53wn$7BMäCèÆš]U7Ucõ¿‘c%}ÎÌv0³Ø¼b¾üN¢-?i×§.5&ÈÕN¯½BwÈ(µ¯×7fv—™ídfŸô]I±Â+ÛRR1”À9r~^ŸÚçVÿ{ÑÌz.>!4+~J.ØÖÿÞáýF̯§ÆßDXß “+qÝŠÝi’¦šfv£âåuO6³Øý"pUQ½›Iû˜ÙSEÉ,d¹°2æÿQÁA‰âi5¸×Ìfó^³›ÙÜfö13[ÕÌvÍÑÎ|¤x1“0-Hê,{ ž1³uk/¹Ez™ÑfјNÖÁUz«½¢%Íì9‹@èæ˜MRî¦ù Óï$é}mö‰mïÛ/ fÃEsVë ¿“aº\¿\§úL¹Ô„èô _RcÌ¿»Qçâ 뼦FËȇ$ý®›c÷À³‘¶¢ÓþŽUcå¿›Y_A¾CŒ´@Ð!Å€òà5¦À}¹ƒýÂ>(ÿ›²$)[i,Ï/)åˆÏ’[ç½öjêWΊµÄnF‹æ GU¼ikWT*–î™—õ'ŒêŸOñÔÏÜüÿÒ{žš}«Ã3»Þë¿ê …Š×̬c @æ×?DnŽãä*Þ½´ˆ¥I€­%m4?/i— Ô©Œ´@ÐÈaP ÌìQ5¦5-A‹úö¸ÚæaÁ“»²›bQ"çñù>ý§ä…Uà>Ĭ5´4t—#•\¤Ðì¾2MéÌT«ÍSÕ-†¨±Òã6Áû'Í캜äù„V5¿Âdì»í»„Âj”ÊØO1ÏH:,+—f¾OyûC`QI¿Wýok†¤ŸšYlÎáNZ h0(Š´íOdà‡jLB.}¨0ÌìN5抖ô«>}c(Jn‰á†L`Åg$­ÖÔdVpøaIg’é‘=üO–[Íç^3 Úýè¦Î…³ÿf…fv•\ M7~Ûé’Ž.ªAÙ˜Ùîr‘屬€Ó%kf¹?2š”¶Vzæ~ˆe²™¥$Ž€Õäò²kçÿl–¦”H p‹…ëŽjfEW[l ®ÀØýª·’œkf¹çÈ-xVcš™…Y-Ã`†z›ˆokfEOD'ÀœÀyM|q?¬Z¿D"‘è”&1'ãâ‚:)ˆV´~×âk¯—ècí—àØkdç®Á1"bh¿ÖK3´ä‘°ùÓ¦fËD"‘Œ4Ôˆ•C659ï4(a0¤ƒ_ÌìàIGKú­™ý¥j‰D"‘è3Õ› àé¼²PðÒ¤‰D"QÉ0r-U3¤-5Z¤G%‰Ä`çi5_o Ù2àÃ…_Kš'Ò>£lE‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$rƪV ‘¬kKú”¤Ù$½(é?fözµZ%†ÙbfsÍSÒ'‰ÂFw+1½¬\µnelYjûªõlŒ”ë Œ‹œëñUëVÀ~‘s]·d–Nþì\¦ìLþJÀ)ÀùÀv%ËÞ¦Êß0wDþ©eÉOTǬf6ØTÒu’–ÌÚß/éI«V¦Y"QÀ¥’6j²yf™ºŒ€%]+é£YÓ·€ùÌì×%É_BÒ5X•ns`.3;® ù‰DUÌ"Ifö‚¤oIëmû<°w%Z%ì¡æ)-QZëhàá_ãË%Êÿ²—¤ýj‰ò‰J˜µö™=l)éI£³æ}€‹ÌìÉJ´K$Êå;‘¶;%]/iª¤ûÊUgÄ0FªIz©dù!/–(?‘¨„Yü7fv£¤HzZîG1QÒn¥k•HTçƒ÷HZÍÌö1³Ììâ*”î˜Ù-’•ô¦œ•å.I¿*Qþr.Ï·$M—t³¤ƒÊ’ŸHTŬaƒ™](é tI$*˜[ÒœAó])»Ìl?IûsšÙ;ÈßMÒnUÉO$ª a0XFKZ^Ò‡%M’ôˆ™m½W!zŒ’ô’æ•ô¢™Ý]¢Üe%-$é]IO˜Ù3eÈÎä//iIã%ÝQŃXRÒbr–ªçÍì¡Å…iX’»î•,&i)¹ÀÇͬ2“4°Œ¤ÅåfÈ·ù€¬úá[µüD#Þoa†¤‡bÏì~ù¹ûåI·åuÏ— Ÿ)é>3{9ãv wiIŸs}ÿ?îÞ ÒR¦{ 3–Þöà0àÕ },pÎóÒe%àR`| w&ðð«ìË^ÀþÀ˜@ö«À¡M®S®éJÀ<ÀÀÓY/âRÅÊQÞ‰Ùµ¯7ÀdoÛÃyÉl£Ï6ÀýÙç]ã]à`Uà,O§ñÀ‰9ÈŒ¦fŸ÷ÿ‚ö·q©r‹äq¾™ü9ƒs›×ñ‡€ü¶i€ÀbÀ£=ÿƒüJÓïFÎk4°%p_ð[˜\‹{8Öt?x9Ðð':¸O»²ŸÍÚ·&þ[¼X¡ k±0pð|ä3‡ûý/ÙþHC `^àúÈIǸ ˜7GÙ±Ûmt¸™œÀNÀ”Îÿv\êT®—µ‘ûx¤-·°"ñÈ À*9É<§y…ÏÀ?R³ ™¹>çä 76¸°ÍõxŠœbTÿªZ~Ë@¦ß‘>×’Ãd`œìþûWZÿž–îmÑà†äÿ<Øg&nÒŠWes¾_¦qÂãM`‹Ôƒüã³{ŽÏ¿"ýòˆÅúV¤í ÈÌþru*|BëPW €™="gê–ôÞhou¹Ô£Ê­Æ–êTÖ^ e_Íìà!IŸõšÏAVLòh“T“SåÌR5ÞÈA¶ÔèÛ<2“Š+…û¹àýk’ÆEúz¼,÷ð¿·ŸUüÁ4”øÁûçiÑ¡ñ‘¶¹K×bd°«]‚¿0³ó+Òg00)Ò69ÒöV𾓸ªNˆÝo?ÃqÃ8†'Íìæ°“™=Ü¥zW`è–èŠA1¨¬%ég’¾(çwŒŒ Þ÷2º ?^‰u2³kä-É›ðÊ®·Zræ“7 lCnÙ òáàýó•hÑiIñb£û'k %ÑE~Gó8vÌÜ*ãè9Õúºÿ €€}åVûªïÃ_j Ëã°€³åO(Y~?Ÿ÷ûsÓ¢:ÂsîÁ]‰î™SÎUš~„éä1K[‰ÁûYž]áƒÂ|].¢û}^ó;rÚÿ“3½|Xƒ£>wø° ýD½0Eõfª\ÛºàÝ@~!EŽZúµºa8Ì@§ïGG{%F:«û›Ù`¸&ò#¼ÿ·š…®ðýT&I;«þá?YÒ×Ìì½"ÀJ€p´•Çl}‚êoú䂿ÉDÕûr?R²ü ’õÞUcÊO3ò€UÍÕ_óÜ‹<%†$WÈ™{ýûÁÞÀ•fvWE:%òçuÕßÿ[ÍèÃe³û²Ö@¸ Ûþ𥥄iyÔçýÝËâ§ å‚«€U{å Ö1XW;¤¨ÙöÁû%m`f“b/¹ò”ìýÐÎu<¼_X³Mƒ…Ë$mªÆIÏ”\Ã÷Ÿ– ; ËÕÄñék}€Á2MÞ1¿î—ÊPÄcë0TÒj4ÏÞ‘ƒ¬«ƒ÷sÊYEÞ—¾…\ÚUí•WJXq:JÒÏùsKÚ.'y!±Õ'lò#X^.V¤´<å»&©aÍ`O žßl¢X.3³ifv¬ŠÑÔX™ê$ Wïß'—ò²¯3®êGð`q¼ z3×ÊÀÉ’.‘ñn,ióÈ~ï‹´åÅ%Ý\)g"_NÒ&AŸé’ÎÊAÖ_å¸~%¼ƒ²‡ÝCrñ›ªÞL.IÿÈA¶$"iOÕ¼v̬·Ë¥b~E•6³+€ÿHòg½—t+p™Ü ù}r)/_’ûN|xÅÌÚ•ì œ,÷ƒ÷£¿$é.à*¹ïÙÊ*œì*W•Ò÷ ï \‘ÕI aÌì¯ÀÏTïöÜø¸\æÇt¹Z-aµÜ—Ô§5h° þ¦Æ\Èïg¯V,µ´Mþ>šÙýý 1³w€_ÈÍjkföQr3þfÜ/éýÊÎä?ƒ«z·k°iýìU;É•½ô‚ôûl ¹†,f6 ø­?ÏϨÏ<ßÄÐÇÌŽt€×ü~IÇ«7ØHg7I—ª~†¿ZöŠ1CÒÏú½÷ sâï%ý³MŸ×ÔXà¡Ý‚=½2]fñGÔøÀì3;]Òê̬ÿ¤oæüÃÿ¹M!7ª b@fö¨œ…¥eÙN$m:Ôþ5Ìì(9KP+^“+“adU ï šWÔàŒNô‰™]-é‡rÙoí˜&iO3;·_¹ƒb=È6–t¸s gÊ•€ÜPnaŸUúÉlÁk’6’3‡•¦&K:OÒ—"¥‚û"KïÙXÎÜ{¸“+¼Š™=•³ì)r5ŽSc®é;’N’»&¯å)7Ðá¹UÏŽ–s ÅxR®$ó*yX_f¶ƒ\!¬ðÜ‘nÙ­ŸÊÄÐb75¦Œî4›%&†fv†œô25Nv%÷ì¹V®TþѹÈÌã y’Þ­-éržÛÍ,Œ’.SŸù3}>,W¡ï¦¼üMä.$ç÷]@î&ÿxY5À³€¿µåb&Hº¾ŒsŽè±¢Üz’ôª\‰ÌX‰âa°¶\™éé’î2³‡€uÔX î3Û­t‡Ù÷=Lç=ÕÌÚ¹ ‰BÈ2ξ(wžEnòw³™•]¤-‘H”ðy`¿ÛÏŽ¬¶C™:G€å#×µ—åc‰D"‘èÜ’¬á–½,›í×¶-öÿÛ»ó [ªòàßAP D$hp%¨‰+F4nˆ ‰±$A‰[–€Š‘¨‘H5\‚kp+J@ŒX‚(jDqA#*""Š‚‚("…²ˆ²Èòåî¾çöÌ{3Ó÷Þyo~¿ª©÷z™þNß¹÷v÷éï|™GfæMÕAꪬÌâdó˜é÷œô ÀaeæQ=¡«3óòžùs^?ëv¯«2ó}™yef^ÛóºÞ”Íí'X¯­”a€°Ú]MÂk71w¡šà.¥¸J]ºó#þT)å[Ól «Xf>33Ï^àŠ?³)ýâY·u]—™GÌóú~='3²Vœ7 V»lž°s4£6fHÐÅñÕˆ8Qá—åËÌ¢)m}‡hž†ù³h®üÕÙ€õ_fn”™fæ™ùÂY·g’2ó‹™ù‘ÌÜaÖm€™ÉÌGdæ¹}aÖmš”Ìü‹Ì¼¹ÝÏ2óÝ™¹Ñ¬ÛS•™÷ÍÌKª'þx qŸÚ>‰òìÌ|Ù¤ãuâîÛø»Þ7­ø@Ddæ–™ù€êgÓY· V“Ì<¡:~%3·˜BÜçub>éxUì]2óºNü›2ó/§ÙXÕ2óàžg£?qÖí‚Õ"3ïÞs |ð”bÏì tõÝóái·a5Ú`Ö ""v‰ˆÛu¦\J9gV™²ÏTÓžI+V'+ßVÓϤ³ñƒjúî3iÅ*³ášVÈ&#ó‘ñGñ‹RÊ·'Þª&îC"b눸""Î,¥\7¸=íØ""%"¾RJ¹fJqï÷ŠˆÛFÄeñíY½ÀTÔ97WϤ³qI5½afnYJùÍLZ³šdæ³2óòÎÏe™¹if¾¹ý×Å™yXfn;õ,Þ""^Žˆm#âöeCœˆôíÿ›#âÙÕ¼›ˆÕ+3·ˆGÄØÁ§µIDìOÈÌ'•Rž;Pè;Æø¾ÏÙ¼óÿIÞ.Ú¤§ }cûþN·]fìÓ"bjÞîQdwŠˆmªyg,3vDÄ™1ºO[dæ¶¥”ïuæíwêLÿuÏvQmg­zêJ)'gæ7côs·{fîZJùTgÞTÛÿ}D¶61Öàô¿ß»gD¼±š÷÷qÏjÞ'ˆ±YÌÿ˜Þ8Öy }©÷ü»vŒæ 9”7EÿÁ¿ë 1z🤽{æMä =ù9.æ?øw•ˆØ/3_3‰¶¬BŒˆk«yOèY¯~ofD|h€øŸæ`ÚU÷~üU5ý ž^ŠVÓ__D^£¯A‰æ€™yPÏöß[J9w1æóÁh^Ë®¾Ñ'Ó3o–™â7L`›õIï$b¬T×Åøû`“Y4d5YÓUÝ…qtD¼;"ÎêYþœÌ¬W­íò뻢=3"ÞGFÄZui¨ôÌ»qB±ö‰¦g£ëȈ¸_4=.ûGÄUÕòÎanÇFħz–½·]vhŒ¤Ö ¥”Ë¢éèzTϪõ ñ¹¥”oÿšˆøN5û¡Õt}…¼AD)º°”ròmˆæ»å¬~nîù±Û0¸K5}éb¬H¥”«cü;îþ³h˪“™ûôÜùrfnV­w|Ïz¯ þ!=Û=¾ZgÓÌü|Ïzû ¿oÿ33¿”™/ΦJÖ³³IÌ\޽ g·ô´¯¾2\NfV`mcçè}Ê9{ ÿ_zm3ó®™ù‡jù`½_=ÛÓ;ËžÔÓ¶ÌÌOwÖÙ«ZvÙÚ°e6ù=]çgæ{zbï7Ô¾·±_Ûã™åîYþî!Û°@ÛÞºÐk?`œ­³É¹éZV~Ë"ãÏ4 mëý?vm803ÏÊ&/æ—™yJfî6ϺÏÌ×w~&^°i"²ÿ8v`ÍÌm3óÆj½/ÿ Õ6oÈÌû.µKˆß·Ý©u/fæªØ¿ÍªH6%BŸYýl=`Vó À–™ùûj»oê,~µìæÌ¬¯Ò—×jûWv–½­gŸ3›D´Úu¯–}v‰í8 'ÎÍÕtÝ[²lÙ¼·ër°è,?¬§]º=ízZO».ÈÌ»ã~Ù$‚^PÅùÞš{8¹2NöÌÑãËÍ™yRf>oJñOìyŸe6™ÆN83ó5Õz›õmw%[TbW›˜T¿1׿¾õšüY5ýƒRÊØîr|nŠ±ê„­Í"âË™yL6=[•R~TJùPõsÑÛ¸Þj‡ý_5»{€Ù±ZvÎÀÃa?¿íLož™Û·ÿ\g~·Kx‹ˆØµý]-n1÷ÿoQJyGDÔ·5º·Ânˆ&_`Píg½ŽÛÝï«eç•R?éʦ·ï1:Túºˆxn)¥²¶ŠˆWÄh‚é…ÑŸƒ´^+¥Í­¥ëçfEÄ“#âß&;3Ž&Ѵϱf~fîä/›[cÏè¬si{c²”ÌîúÍ¿yïZ‹s‡júçls]òÞˆ¨‡4m{EÄQñóÌüVf¾)3ȘŒ:£|»¼µKoûjY]µlYJ)7DÄÙÕìÚ¿õŸwæ½'FOvnÿ­ï•.çäõà˜?Ùõ¸RÊW–±í…|¢šÞ&3ç^ƒíªeƒwÁweÓ³rLDÔÃÐ-¥LúÂàÆˆxe›±½9Æß ÓP÷2\ãÇ¡"â»ÙÜ:3F/\‡4uK9ø]5=Ô0¼®©ÛY)ÚD°]¢IÄëK8Ú("FÄ÷sÀûÏÜâbô½½q4ÃáMAª9Ceÿ×N¯¦·‹æŠd.3<#â#1zuÿØln•u»¤/-¥,¹>A{€;žÅÇÏ3ÇÆøÐºÝ£IvìõÌhFLÒÛc<ô¥”×O Výyß0"ŽÉÌL ÖŠÖžxÍ0БEŽ»CŒ1=-"¶)¥lMrz79ñNÑœxw¿nŒˆwM²“²”€úŠˆñ°×WÓÓê·b”R.*¥ìÍ›ë£1ðæñÒôÈÌA•R®ŠñÛOŒˆ:è;¥”:k}õ3ß·Ñáç¶ÃîºÙý÷Îh€Vßhµ–™DÄXþMëß—³í…”R.ŽñÑ;Æx̓³K)uoÉ`²É)©G$ý$"öD¼RÊ£bÙÍ9ºMDüWfn5‰˜+Ø+côvÏå±_4•h'éAÕôæºóK)GDs«íg üþᥔ%åÝÌÚRNê7啽k-N=ücµ½ñoQJ9¥”òÔRÊ]¢érzcŒ“ŠˆØËí€ÁTMo£÷¢#îþŸSJ9%š²×sî£C¿Ôþ{BÜ:µÄøÁêkKmC{Ëc¡ƒü™9‘a«~ý£¹ýÃU‘MùñÿŽÑïÅk"bŸv¸èD”RÎ+¥<#"ºU 7‹Õ—ðÔjú­¥”#K)Wô®=œ £¹ÕúhzGrhÚ|“íÛåWU¿÷ÒRÊnßÄ,ê ›1ÿuA!Æç×ÛØ6{ʱ®²bvHõóÀˆ[N*¥lM=„®ÛÅš‹5­+ê ˆæÙÓv\ŒÞc¿wŒŽÁ¿9š/ˆIé^½o£'ŸŒˆ(¥\£ùÛTÛXΕÈ!ÑÔèªë?¼r‚ÙÎÇÆhýû c´BßÑ| .›ºGÅxEÀW—Rêž¡I©{oæë‰Y_Õ% ×º–År´ß³Ï)¥ì]JùǾŽRÊ%¥”½£É ypDÜ»”r¯RÊ$êALÍB'OiïÉtã"·ÞƆQe~¶m©»;׈&³úUŸ¾çüoϼ¾r¹KÕWä¨..3)}WW#]¿Ù=šèß¿íöûRwVŒV#;»*Ñ;´:`ίª.ÆSçYï—K-NÔ^ýÖCj¿M¨®?‰ˆW/%Æš´·aæÛ·ˆˆ3J)“*ö®/ÀtB)å-Š×ç×Õô,N‚g"›gÔ•ÿ~:‹¶,¤”rC)å{¥”±Z-뢅ž¸sDœ‘™'Gsð˜¸5ëxÎ5ÑT \®£¢9¹è¾ž›™÷Œ¦Kóömì‡ kE)¥ü&3¿£•Î^”™WDÄ¥”«2óѼ>]7F“‰:”úË'"âÌ|X4'ŠGO03ù«Ñ$út‡œ='3ÿ8"Ήæªl§.: 'E3ô¨ÏP•çæóùh²ðkuæý‰Ñ?4j9Ÿ7Äèçïæh®~?“™ÿ£÷IŸŸ™ï/¥œ·ŒxóùhÌÿúO$;¼g^w·gDÜ)3OYàW_UJùÆ$ÚÔZMk_Mûº²äü•ðÖd°‚9^ÌdmMªв·»ˆøÏæiµ›2óêyöû„ÛpŸ/zÒ5ß١⟾ˆ¿yײ UíØ43¯è‰sc6'b•ãOßÌì/ÊõÓžõ–4F?›J—uÁŸ“:Ë÷èY>‘'ÕeóÔ¿¾×àÚ°O³ïµ\½â–ÑOåi€¶ÐYךêPŸ Ø–ƒb´ûµOßUê:¯½Çø‚¿ßºAôˆ8-šç Ù†ó£¹²œ•—ÇøÓÚ|]äƒi‡döuCŸ5¡+ÞZ}M4‰µ/÷Ì[tE6·Ö‰ÑÞ—k£ÉÈŽˆˆRʉ1^[`·ÌÜ5Ö>]´ïÞïiàéZ¨'tšêú ·™I+fãv=óøM²[=*àúh’¯&rïq%(¥OЦÈIýšÏùa4EB7¡¬ä}£IĪ“ò2&üd²RÊ©Ñüý¿ãã~¯Œæá+u–ð¤|¬gÞ¤»ÿçÔUüÎhï×êb8?_âð¸ƒ"¢îÙ8¦”rN5ï1ú¾Ø "^»„xkã#=óêë£:ùìŽ3iÅlÔùF׬‹•õÖI¹@xÛ5óô>BAØ×IDATlê¡ÿ]fQùoMíÙ,3ŸÜÆ|FÞZ~q¦]õÓ’M7ô.™¹ofîß¾þË~êâ"âß½}Ý_Í3¦»ÿÀÌÜ»ýûï–Ã<õuDfî^}ƯÎuõA+‹Ð³ß¿šbìYßxQµï“¨µAe]_í•æTŸ»ÝžùÍ¢äŠÐvCO´Üéâ_͸YÅ?7"†xÖ<ë¦ú¤þÔ)Œ_ >Í­×¹áŸ[eæ³J)“zºRìQMOóY,«–ÌK`EÈ&ðàhÊbw šðºRµÏ„8¢šý–̬Kã®72s‹lªšvGA]MA&&Ì 0S™ù¸Ì¼,š\ׯhòÛ…¥”‰ÿY¡‰Ñ²Ð[EóTÂõÕÞÑ”ûs}D¼¤”ò‹µgUqÌÚm£yÈJ žÑÔ'X5Ú^€£I†ž{(ZýœˆI8%"žÖþ5…xsŽ‹[“žÏ‹ˆ=K)ÇL1þª¶R†¿teD¼­”²êzÕžÐ&ã=;ú‡|󂈘zu»RÊ¥™yXDœß>‰)š;øaDY-ûá”Û²6Ö•vkï’hj€Ü-š±ßFÄûK)«aè߼ڲǯ\ãŠë¸RÊkfÝÖ)ÿJ Ñ*^TŸIEND®B`‚rtabmap-0.22.1/app/android/jni/scene.cpp000066400000000000000000001015371503504370700200160ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "scene.h" #include "util.h" // We want to represent the device properly with respect to the ground so we'll // add an offset in z to our origin. We'll set this offset to 1.3 meters based // on the average height of a human standing with a Tango device. This allows us // to place a grid roughly on the ground for most users. const glm::vec3 Scene::kHeightOffset = glm::vec3(0.0f, -1.3f, 0.0f); // Color of the motion tracking trajectory. const tango_gl::Color kTraceColor(0.66f, 0.66f, 0.66f); // Color of the ground grid. const tango_gl::Color kGridColor(0.85f, 0.85f, 0.85f); // Frustum scale. const glm::vec3 kFrustumScale = glm::vec3(0.4f, 0.3f, 0.5f); const std::string kGraphVertexShader = "precision mediump float;\n" "precision mediump int;\n" "attribute vec3 vertex;\n" "uniform vec3 color;\n" "uniform mat4 mvp;\n" "varying vec3 v_color;\n" "void main() {\n" " gl_Position = mvp*vec4(vertex.x, vertex.y, vertex.z, 1.0);\n" " v_color = color;\n" "}\n"; const std::string kGraphFragmentShader = "precision mediump float;\n" "precision mediump int;\n" "varying vec3 v_color;\n" "void main() {\n" " gl_FragColor = vec4(v_color.z, v_color.y, v_color.x, 1.0);\n" "}\n"; Scene::Scene() : background_renderer_(0), gesture_camera_(0), axis_(0), frustum_(0), grid_(0), box_(0), trace_(0), graph_(0), graphVisible_(true), gridVisible_(true), traceVisible_(true), frustumVisible_(true), color_camera_to_display_rotation_(rtabmap::ROTATION_0), currentPose_(0), graph_shader_program_(0), blending_(true), mapRendering_(true), meshRendering_(true), meshRenderingTexture_(true), pointSize_(10.0f), boundingBoxRendering_(false), lighting_(false), backfaceCulling_(true), wireFrame_(false), textureColorSeamsHidden_(true), r_(0.0f), g_(0.0f), b_(0.0f), fboId_(0), rboId_(0), screenWidth_(0), screenHeight_(0), doubleTapOn_(false) { depthTexture_ = 0; gesture_camera_ = new tango_gl::GestureCamera(); gesture_camera_->SetCameraType( tango_gl::GestureCamera::kThirdPersonFollow); } Scene::~Scene() { DeleteResources(); delete gesture_camera_; delete currentPose_; } //Should only be called in OpenGL thread! void Scene::InitGLContent() { if(axis_ != 0) { DeleteResources(); } UASSERT(axis_ == 0); TextDrawable::createShaderProgram(); axis_ = new tango_gl::Axis(); frustum_ = new tango_gl::Frustum(); trace_ = new tango_gl::Trace(); grid_ = new tango_gl::Grid(); box_ = new BoundingBoxDrawable(); axis_->SetScale(glm::vec3(0.5f,0.5f,0.5f)); frustum_->SetColor(kTraceColor); trace_->ClearVertexArray(); trace_->SetColor(kTraceColor); grid_->SetColor(kGridColor); grid_->SetPosition(kHeightOffset); box_->SetShader(); box_->SetColor(1,0,0); PointCloudDrawable::createShaderPrograms(); if(graph_shader_program_ == 0) { graph_shader_program_ = tango_gl::util::CreateProgram(kGraphVertexShader.c_str(), kGraphFragmentShader.c_str()); UASSERT(graph_shader_program_ != 0); } } //Should only be called in OpenGL thread! void Scene::DeleteResources() { LOGI("Scene::DeleteResources()"); if(axis_) { delete axis_; axis_ = 0; delete frustum_; delete trace_; delete grid_; delete box_; delete background_renderer_; background_renderer_ = 0; } TextDrawable::releaseShaderProgram(); PointCloudDrawable::releaseShaderPrograms(); if (graph_shader_program_) { glDeleteShader(graph_shader_program_); graph_shader_program_ = 0; } if(fboId_>0) { glDeleteFramebuffers(1, &fboId_); fboId_ = 0; glDeleteRenderbuffers(1, &rboId_); rboId_ = 0; glDeleteTextures(1, &depthTexture_); depthTexture_ = 0; } clear(); } //Should only be called in OpenGL thread! void Scene::clear() { LOGI("Scene::clear()"); for(std::map::iterator iter=pointClouds_.begin(); iter!=pointClouds_.end(); ++iter) { delete iter->second; } for(std::map::iterator iter=markers_.begin(); iter!=markers_.end(); ++iter) { delete iter->second; } clearLines(); clearQuads(); clearTexts(); clearCircles(); if(trace_) { trace_->ClearVertexArray(); } if(graph_) { delete graph_; graph_ = 0; } pointClouds_.clear(); markers_.clear(); if(grid_) { grid_->SetPosition(kHeightOffset); } } void Scene::clearLines() { for(std::map::iterator iter=lines_.begin(); iter!=lines_.end(); ++iter) { delete iter->second; } lines_.clear(); } void Scene::clearTexts() { for(std::map::iterator iter=texts_.begin(); iter!=texts_.end(); ++iter) { delete iter->second; } texts_.clear(); } void Scene::clearQuads() { for(std::map::iterator iter=quads_.begin(); iter!=quads_.end(); ++iter) { delete iter->second; } quads_.clear(); } void Scene::clearCircles() { for(std::map::iterator iter=circles_.begin(); iter!=circles_.end(); ++iter) { delete iter->second; } circles_.clear(); } //Should only be called in OpenGL thread! void Scene::SetupViewPort(int w, int h) { if (h == 0) { LOGE("Setup graphic height not valid"); } UASSERT(gesture_camera_ != 0); gesture_camera_->SetWindowSize(static_cast(w), static_cast(h)); glViewport(0, 0, w, h); if(screenWidth_ != w || screenHeight_ != h || fboId_ == 0) { UINFO("Setup viewport OpenGL: %dx%d", w, h); if(fboId_>0) { glDeleteFramebuffers(1, &fboId_); fboId_ = 0; glDeleteRenderbuffers(1, &rboId_); rboId_ = 0; glDeleteTextures(1, &depthTexture_); depthTexture_ = 0; } GLint originid = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originid); // regenerate fbo texture // create a framebuffer object, you need to delete them when program exits. glGenFramebuffers(1, &fboId_); glBindFramebuffer(GL_FRAMEBUFFER, fboId_); // Create depth texture glGenTextures(1, &depthTexture_); glBindTexture(GL_TEXTURE_2D, depthTexture_); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); glGenRenderbuffers(1, &rboId_); glBindRenderbuffer(GL_RENDERBUFFER, rboId_); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, w, h); glBindRenderbuffer(GL_RENDERBUFFER, 0); // Set the texture to be at the color attachment point of the FBO (we pack depth 32 bits in color) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, depthTexture_, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId_); GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER); UASSERT ( status == GL_FRAMEBUFFER_COMPLETE); glBindFramebuffer(GL_FRAMEBUFFER, originid); } screenWidth_ = w; screenHeight_ = h; } std::vector computeFrustumPlanes(const glm::mat4 & mat, bool normalize = true) { // http://www.txutxi.com/?p=444 std::vector planes(6); // Left Plane // col4 + col1 planes[0].x = mat[0][3] + mat[0][0]; planes[0].y = mat[1][3] + mat[1][0]; planes[0].z = mat[2][3] + mat[2][0]; planes[0].w = mat[3][3] + mat[3][0]; // Right Plane // col4 - col1 planes[1].x = mat[0][3] - mat[0][0]; planes[1].y = mat[1][3] - mat[1][0]; planes[1].z = mat[2][3] - mat[2][0]; planes[1].w = mat[3][3] - mat[3][0]; // Bottom Plane // col4 + col2 planes[2].x = mat[0][3] + mat[0][1]; planes[2].y = mat[1][3] + mat[1][1]; planes[2].z = mat[2][3] + mat[2][1]; planes[2].w = mat[3][3] + mat[3][1]; // Top Plane // col4 - col2 planes[3].x = mat[0][3] - mat[0][1]; planes[3].y = mat[1][3] - mat[1][1]; planes[3].z = mat[2][3] - mat[2][1]; planes[3].w = mat[3][3] - mat[3][1]; // Near Plane // col4 + col3 planes[4].x = mat[0][3] + mat[0][2]; planes[4].y = mat[1][3] + mat[1][2]; planes[4].z = mat[2][3] + mat[2][2]; planes[4].w = mat[3][3] + mat[3][2]; // Far Plane // col4 - col3 planes[5].x = mat[0][3] - mat[0][2]; planes[5].y = mat[1][3] - mat[1][2]; planes[5].z = mat[2][3] - mat[2][2]; planes[5].w = mat[3][3] - mat[3][2]; //if(normalize) { for(unsigned int i=0;i &planes, const pcl::PointXYZ &boxMin, const pcl::PointXYZ &boxMax) { // Indexed for the 'index trick' later const pcl::PointXYZ * box[] = {&boxMin, &boxMax}; // We only need to do 6 point-plane tests for (unsigned int i = 0; i < planes.size(); ++i) { // This is the current plane const glm::vec4 &p = planes[i]; // p-vertex selection (with the index trick) // According to the plane normal we can know the // indices of the positive vertex const int px = p.x > 0.0f?1:0; const int py = p.y > 0.0f?1:0; const int pz = p.z > 0.0f?1:0; // Dot product // project p-vertex on plane normal // (How far is p-vertex from the origin) const float dp = (p.x*box[px]->x) + (p.y*box[py]->y) + (p.z*box[pz]->z) + p.w; // Doesn't intersect if it is behind the plane if (dp < 0) {return false; } } return true; } //Should only be called in OpenGL thread! int Scene::Render(const float * uvsTransformed, glm::mat4 arViewMatrix, glm::mat4 arProjectionMatrix, const rtabmap::Mesh & occlusionMesh, bool mapping) { UASSERT(gesture_camera_ != 0); if(currentPose_ == 0) { currentPose_ = new rtabmap::Transform(0,0,0,0,0,-M_PI/2.0f); } glm::vec3 position(currentPose_->x(), currentPose_->y(), currentPose_->z()); Eigen::Quaternionf quat = currentPose_->getQuaternionf(); glm::quat rotation(quat.w(), quat.x(), quat.y(), quat.z()); glm::mat4 rotateM; if(!currentPose_->isNull()) { rotateM = glm::rotate(float(color_camera_to_display_rotation_)*-1.57079632679489661923132169163975144, glm::vec3(0.0f, 0.0f, 1.0f)); if (gesture_camera_->GetCameraType() == tango_gl::GestureCamera::kFirstPerson) { // In first person mode, we directly control camera's motion. gesture_camera_->SetPosition(position); gesture_camera_->SetRotation(rotation*glm::quat(rotateM)); } else { // In third person or top down mode, we follow the camera movement. gesture_camera_->SetAnchorPosition(position, rotation*glm::quat(rotateM)); } } glm::mat4 projectionMatrix = gesture_camera_->GetProjectionMatrix(); glm::mat4 viewMatrix = gesture_camera_->GetViewMatrix(); bool renderBackgroundCamera = background_renderer_ && gesture_camera_->GetCameraType() == tango_gl::GestureCamera::kFirstPerson && !rtabmap::glmToTransform(arProjectionMatrix).isNull() && uvsTransformed; if(renderBackgroundCamera) { if(projectionMatrix[0][0] > arProjectionMatrix[0][0]-0.3) { projectionMatrix = arProjectionMatrix; viewMatrix = arViewMatrix; } else { renderBackgroundCamera = false; } } rtabmap::Transform openglCamera = GetOpenGLCameraPose();//*rtabmap::Transform(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f); // transform in same coordinate as frustum filtering openglCamera *= rtabmap::Transform( 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f); //Culling std::vector planes = computeFrustumPlanes(projectionMatrix*viewMatrix, true); std::vector cloudsToDraw(pointClouds_.size()); int oi=0; int positiveCloudIds = 0; for(std::map::const_iterator iter=pointClouds_.begin(); iter!=pointClouds_.end(); ++iter) { if(iter->first > 0) { positiveCloudIds++; } if(!mapRendering_ && iter->first > 0) { break; } if(iter->second->isVisible()) { if(intersectFrustumAABB(planes, iter->second->aabbMinWorld(), iter->second->aabbMaxWorld())) { cloudsToDraw[oi++] = iter->second; } } } cloudsToDraw.resize(oi); // First rendering to get depth texture glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if(backfaceCulling_) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } bool onlineBlending = (!meshRendering_ && occlusionMesh.cloud.get() && occlusionMesh.cloud->size()) || (blending_ && gesture_camera_->GetCameraType()!=tango_gl::GestureCamera::kTopOrtho && mapRendering_ && meshRendering_ && (positiveCloudIds > 1 || (renderBackgroundCamera && wireFrame_))); if(onlineBlending && fboId_) { GLint originid = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originid); // set the rendering destination to FBO glBindFramebuffer(GL_FRAMEBUFFER, fboId_); glClearColor(0, 0, 0, 0); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // Draw scene for(std::vector::const_iterator iter=cloudsToDraw.begin(); iter!=cloudsToDraw.end(); ++iter) { Eigen::Vector3f cloudToCamera( (*iter)->getPose().x() - openglCamera.x(), (*iter)->getPose().y() - openglCamera.y(), (*iter)->getPose().z() - openglCamera.z()); float distanceToCameraSqr = cloudToCamera[0]*cloudToCamera[0] + cloudToCamera[1]*cloudToCamera[1] + cloudToCamera[2]*cloudToCamera[2]; (*iter)->Render(projectionMatrix, viewMatrix, meshRendering_, pointSize_, false, false, distanceToCameraSqr, 0, 0, 0, 0, 0, true); } if(!meshRendering_ && occlusionMesh.cloud.get() && occlusionMesh.cloud->size()) { PointCloudDrawable drawable(occlusionMesh); drawable.Render(projectionMatrix, viewMatrix, true, pointSize_, false, false, 0, 0, 0, 0, 0, 0, true); } // back to normal window-system-provided framebuffer glBindFramebuffer(GL_FRAMEBUFFER, originid); // unbind } if(doubleTapOn_ && gesture_camera_->GetCameraType() != tango_gl::GestureCamera::kFirstPerson) { glClearColor(0, 0, 0, 0); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // FIXME: we could use the depthTexture if already computed! for(std::vector::const_iterator iter=cloudsToDraw.begin(); iter!=cloudsToDraw.end(); ++iter) { Eigen::Vector3f cloudToCamera( (*iter)->getPose().x() - openglCamera.x(), (*iter)->getPose().y() - openglCamera.y(), (*iter)->getPose().z() - openglCamera.z()); float distanceToCameraSqr = cloudToCamera[0]*cloudToCamera[0] + cloudToCamera[1]*cloudToCamera[1] + cloudToCamera[2]*cloudToCamera[2]; (*iter)->Render(projectionMatrix, viewMatrix, meshRendering_, pointSize_*10.0f, false, false, distanceToCameraSqr, 0, 0, 0, 0, 0, true); } GLubyte zValue[4]; glReadPixels(doubleTapPos_.x*screenWidth_, screenHeight_-doubleTapPos_.y*screenHeight_, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, zValue); float zValueF = float(zValue[0]/255.0f) + float(zValue[1]/255.0f)/255.0f + float(zValue[2]/255.0f)/65025.0f + float(zValue[3]/255.0f)/160581375.0f; if(zValueF != 0.0f) { zValueF = zValueF*2.0-1.0;//NDC glm::vec4 point = glm::inverse(projectionMatrix*viewMatrix)*glm::vec4(doubleTapPos_.x*2.0f-1.0f, (1.0f-doubleTapPos_.y)*2.0f-1.0f, zValueF, 1.0f); point /= point.w; gesture_camera_->SetAnchorOffset(glm::vec3(point.x, point.y, point.z) - position); } } doubleTapOn_ = false; glClearColor(r_, g_, b_, 1.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); if(renderBackgroundCamera && (!onlineBlending || !meshRendering_)) { background_renderer_->Draw(uvsTransformed, 0, screenWidth_, screenHeight_, false); //To debug occlusion image: //PointCloudDrawable drawable(occlusionMesh); //drawable.Render(projectionMatrix, viewMatrix, true, pointSize_, false, false, 999.0f); } if(!currentPose_->isNull()) { if (frustumVisible_ && gesture_camera_->GetCameraType() != tango_gl::GestureCamera::kFirstPerson) { frustum_->SetPosition(position); frustum_->SetRotation(rotation); // Set the frustum scale to 4:3, this doesn't necessarily match the physical // camera's aspect ratio, this is just for visualization purposes. frustum_->SetScale(kFrustumScale); frustum_->Render(projectionMatrix, viewMatrix); rtabmap::Transform cameraFrame = *currentPose_*rtabmap::optical_T_opengl*rtabmap::CameraMobile::opticalRotationInv; glm::vec3 positionCamera(cameraFrame.x(), cameraFrame.y(), cameraFrame.z()); Eigen::Quaternionf quatCamera = cameraFrame.getQuaternionf(); glm::quat rotationCamera(quatCamera.w(), quatCamera.x(), quatCamera.y(), quatCamera.z()); axis_->SetPosition(positionCamera); axis_->SetRotation(rotationCamera); axis_->Render(projectionMatrix, viewMatrix); } trace_->UpdateVertexArray(position); if(traceVisible_) { trace_->Render(projectionMatrix, viewMatrix); } else { trace_->ClearVertexArray(); } } if(gridVisible_ && !renderBackgroundCamera) { grid_->Render(projectionMatrix, viewMatrix); } if(graphVisible_ && graph_) { graph_->Render(projectionMatrix, viewMatrix); } if(onlineBlending) { glEnable (GL_BLEND); glDepthMask(GL_FALSE); } for(std::vector::const_iterator iter=cloudsToDraw.begin(); iter!=cloudsToDraw.end(); ++iter) { PointCloudDrawable * cloud = *iter; if(boundingBoxRendering_) { box_->updateVertices(cloud->aabbMinWorld(), cloud->aabbMaxWorld()); box_->Render(projectionMatrix, viewMatrix); } Eigen::Vector3f cloudToCamera( cloud->getPose().x() - openglCamera.x(), cloud->getPose().y() - openglCamera.y(), cloud->getPose().z() - openglCamera.z()); float distanceToCameraSqr = cloudToCamera[0]*cloudToCamera[0] + cloudToCamera[1]*cloudToCamera[1] + cloudToCamera[2]*cloudToCamera[2]; cloud->Render(projectionMatrix, viewMatrix, meshRendering_, pointSize_, meshRenderingTexture_, lighting_, distanceToCameraSqr, onlineBlending?depthTexture_:0, screenWidth_, screenHeight_, gesture_camera_->getNearClipPlane(), gesture_camera_->getFarClipPlane(), false, wireFrame_, textureColorSeamsHidden_); } if(quads_.find(55556)!=quads_.end()) { glEnable(GL_BLEND); glDisable(GL_CULL_FACE); const QuadColor * quad = quads_.at(55556); quad->Render(projectionMatrix, viewMatrix); glEnable(GL_CULL_FACE); } if(onlineBlending) { if(renderBackgroundCamera && meshRendering_) { background_renderer_->Draw(uvsTransformed, depthTexture_, screenWidth_, screenHeight_, mapping); } glDisable (GL_BLEND); glDepthMask(GL_TRUE); } /////// glDisable (GL_DEPTH_TEST); if(lines_.size()) { for(std::map::const_iterator iter=lines_.begin(); iter!=lines_.end(); ++iter) { const tango_gl::Line * line = iter->second; line->Render(projectionMatrix, viewMatrix); } } if(quads_.size()) { glEnable(GL_BLEND); glDisable(GL_CULL_FACE); for(std::map::const_iterator iter=quads_.begin(); iter!=quads_.end(); ++iter) { if(iter->first!=55556) { const QuadColor * quad = iter->second; quad->Render(projectionMatrix, viewMatrix); } } } if(circles_.size()) { glEnable(GL_BLEND); glDisable(GL_CULL_FACE); for(std::map::const_iterator iter=circles_.begin(); iter!=circles_.end(); ++iter) { const tango_gl::Circle * circle = iter->second; circle->Render(projectionMatrix, viewMatrix); } } if(texts_.size()) { glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glm::mat4 viewMatrixRotInv = viewMatrix; viewMatrixRotInv[3][0] = 0; viewMatrixRotInv[3][1] = 0; viewMatrixRotInv[3][2] = 0; viewMatrixRotInv = glm::inverse(viewMatrixRotInv); for(std::map::const_iterator iter=texts_.begin(); iter!=texts_.end(); ++iter) { const TextDrawable * text = iter->second; text->Render(projectionMatrix, viewMatrix, viewMatrixRotInv); } } //draw markers on foreground for(std::map::const_iterator iter=markers_.begin(); iter!=markers_.end(); ++iter) { iter->second->Render(projectionMatrix, viewMatrix); } return (int)cloudsToDraw.size(); } void Scene::SetCameraType(tango_gl::GestureCamera::CameraType camera_type) { gesture_camera_->SetCameraType(camera_type); } void Scene::SetCameraPose(const rtabmap::Transform & pose) { UASSERT(!pose.isNull()); if(currentPose_ ==0) { currentPose_ = new rtabmap::Transform(0,0,0,0,0,-M_PI/2.0f); } *currentPose_ = pose; } void Scene::setFOV(float angle) { gesture_camera_->SetFieldOfView(angle); } void Scene::setOrthoCropFactor(float value) { gesture_camera_->SetOrthoCropFactor(value); } void Scene::setGridRotation(float angleDeg) { float angleRad = angleDeg * DEGREE_2_RADIANS; if(grid_) { glm::quat rot = glm::rotate(glm::quat(1,0,0,0), angleRad, glm::vec3(0, 1, 0)); grid_->SetRotation(rot); } } rtabmap::Transform Scene::GetOpenGLCameraPose(float * fov) const { if(fov) { *fov = gesture_camera_->getFOV(); } return rtabmap::glmToTransform(gesture_camera_->GetTransformationMatrix()); } void Scene::OnTouchEvent(int touch_count, tango_gl::GestureCamera::TouchEvent event, float x0, float y0, float x1, float y1) { UASSERT(gesture_camera_ != 0); if(touch_count == 3) { //doubletap if(!doubleTapOn_) { doubleTapPos_.x = x0; doubleTapPos_.y = y0; doubleTapOn_ = true; } } else { // rotate/translate/zoom gesture_camera_->OnTouchEvent(touch_count, event, x0, y0, x1, y1); } } void Scene::updateGraph( const std::map & poses, const std::multimap & links) { LOGI("updateGraph"); //create UASSERT(graph_shader_program_ != 0); delete graph_; graph_ = new GraphDrawable(graph_shader_program_, poses, links); } void Scene::setGraphVisible(bool visible) { graphVisible_ = visible; } void Scene::setGridVisible(bool visible) { gridVisible_ = visible; } void Scene::setTraceVisible(bool visible) { traceVisible_ = visible; } void Scene::setFrustumVisible(bool visible) { frustumVisible_ = visible; } //Should only be called in OpenGL thread! void Scene::addMarker( int id, const rtabmap::Transform & pose) { LOGI("add marker %d", id); std::map::iterator iter=markers_.find(id); if(iter == markers_.end()) { //create tango_gl::Axis * drawable = new tango_gl::Axis(); drawable->SetScale(glm::vec3(0.05f,0.05f,0.05f)); drawable->SetLineWidth(5); markers_.insert(std::make_pair(id, drawable)); } setMarkerPose(id, pose); } void Scene::setMarkerPose(int id, const rtabmap::Transform & pose) { UASSERT(!pose.isNull()); std::map::iterator iter=markers_.find(id); if(iter != markers_.end()) { glm::vec3 position(pose.x(), pose.y(), pose.z()); Eigen::Quaternionf quat = pose.getQuaternionf(); glm::quat rotation(quat.w(), quat.x(), quat.y(), quat.z()); iter->second->SetPosition(position); iter->second->SetRotation(rotation); } } bool Scene::hasMarker(int id) const { return markers_.find(id) != markers_.end(); } void Scene::removeMarker(int id) { std::map::iterator iter=markers_.find(id); if(iter != markers_.end()) { delete iter->second; markers_.erase(iter); } } std::set Scene::getAddedMarkers() const { return uKeysSet(markers_); } void Scene::addCloud( int id, const pcl::PointCloud::Ptr & cloud, const pcl::IndicesPtr & indices, const rtabmap::Transform & pose) { LOGI("add cloud %d (%d points %d indices)", id, (int)cloud->size(), indices.get()?(int)indices->size():0); removeCloudOrMesh(id); //create PointCloudDrawable * drawable = new PointCloudDrawable(cloud, indices); drawable->setPose(pose); pointClouds_.insert(std::make_pair(id, drawable)); } void Scene::removeCloudOrMesh(int id) { std::map::iterator iter=pointClouds_.find(id); if(iter != pointClouds_.end()) { delete iter->second; pointClouds_.erase(iter); } } void Scene::addMesh( int id, const rtabmap::Mesh & mesh, const rtabmap::Transform & pose, bool createWireframe) { LOGI("add mesh %d", id); removeCloudOrMesh(id); //create PointCloudDrawable * drawable = new PointCloudDrawable(mesh, createWireframe); drawable->setPose(pose); pointClouds_.insert(std::make_pair(id, drawable)); if(!mesh.pose.isNull() && mesh.cloud->size() && (!mesh.cloud->isOrganized() || mesh.indices->size())) { UTimer time; float height = 0.0f; Eigen::Affine3f affinePose = mesh.pose.toEigen3f(); if(mesh.polygons.size()) { for(unsigned int i=0; iat(mesh.polygons[i].vertices[j]), affinePose); if(pt.z < height) { height = pt.z; } } } } else { if(mesh.cloud->isOrganized()) { for(unsigned int i=0; isize(); ++i) { pcl::PointXYZRGB pt = pcl::transformPoint(mesh.cloud->at(mesh.indices->at(i)), affinePose); if(pt.z < height) { height = pt.z; } } } else { for(unsigned int i=0; isize(); ++i) { pcl::PointXYZRGB pt = pcl::transformPoint(mesh.cloud->at(i), affinePose); if(pt.z < height) { height = pt.z; } } } } if(grid_->GetPosition().y == kHeightOffset.y || grid_->GetPosition().y > height) { grid_->SetPosition(glm::vec3(0,height,0)); } LOGD("compute min height %f s", time.ticks()); } } void Scene::addLine( int id, const cv::Point3f & pt1, const cv::Point3f & pt2, const tango_gl::Color & color) { LOGI("add line %d", id); removeLine(id); //create tango_gl::Line * line = new tango_gl::Line(2.0f, GL_LINES); line->SetShader(); std::vector vertices(2); vertices[0].x = pt1.x; vertices[0].y = pt1.y; vertices[0].z = pt1.z; vertices[1].x = pt2.x; vertices[1].y = pt2.y; vertices[1].z = pt2.z; line->UpdateLineVertices(vertices); line->SetColor(color); lines_.insert(std::make_pair(id, line)); } void Scene::removeLine(int id) { std::map::iterator iter=lines_.find(id); if(iter != lines_.end()) { delete iter->second; lines_.erase(iter); } } void Scene::addText( int id, const std::string & text, const rtabmap::Transform & pose, float size, const tango_gl::Color & color) { LOGI("add text %d", id); removeText(id); //create TextDrawable * textD = new TextDrawable(text, pose, size, color); texts_.insert(std::make_pair(id, textD)); } void Scene::removeText(int id) { std::map::iterator iter=texts_.find(id); if(iter != texts_.end()) { delete iter->second; texts_.erase(iter); } } void Scene::addQuad( int id, float size, const rtabmap::Transform & pose, const tango_gl::Color & color, float alpha) { //LOGI("add quad %d", id); std::map::iterator iter=quads_.find(id); if(iter != quads_.end()) { delete iter->second; quads_.erase(iter); } //create QuadColor * quad = new QuadColor(size); quad->SetTransformationMatrix(glmFromTransform(pose)); quad->SetColor(color); quad->SetAlpha(alpha); quads_.insert(std::make_pair(id, quad)); } void Scene::addQuad( int id, float widthLeft, float widthRight, float heightBottom, float heightTop, const rtabmap::Transform & pose, const tango_gl::Color & color, float alpha) { //LOGI("add quad %d", id); removeQuad(id); //create QuadColor * quad = new QuadColor(widthLeft, widthRight, heightBottom, heightTop); quad->SetTransformationMatrix(glmFromTransform(pose)); quad->SetColor(color); quad->SetAlpha(alpha); quads_.insert(std::make_pair(id, quad)); } void Scene::removeQuad(int id) { std::map::iterator iter=quads_.find(id); if(iter != quads_.end()) { delete iter->second; quads_.erase(iter); } } bool Scene::hasQuad(int id) const { return quads_.find(id) != quads_.end(); } void Scene::addCircle( int id, float radius, const rtabmap::Transform & pose, const tango_gl::Color & color, float alpha) { //LOGI("add quad %d", id); std::map::iterator iter=circles_.find(id); if(iter != circles_.end()) { delete iter->second; circles_.erase(iter); } //create tango_gl::Circle * circle = new tango_gl::Circle(radius, 12); circle->SetTransformationMatrix(glmFromTransform(pose)); circle->SetColor(color); circle->SetAlpha(alpha); circles_.insert(std::make_pair(id, circle)); } void Scene::removeCircle(int id) { std::map::iterator iter=circles_.find(id); if(iter != circles_.end()) { delete iter->second; circles_.erase(iter); } } bool Scene::hasCircle(int id) const { return circles_.find(id) != circles_.end(); } void Scene::setCloudPose(int id, const rtabmap::Transform & pose) { UASSERT(!pose.isNull()); std::map::iterator iter=pointClouds_.find(id); if(iter != pointClouds_.end()) { iter->second->setPose(pose); } } void Scene::setCloudVisible(int id, bool visible) { std::map::iterator iter=pointClouds_.find(id); if(iter != pointClouds_.end()) { iter->second->setVisible(visible); } } bool Scene::hasCloud(int id) const { return pointClouds_.find(id) != pointClouds_.end(); } bool Scene::hasMesh(int id) const { return pointClouds_.find(id) != pointClouds_.end() && pointClouds_.at(id)->hasMesh(); } bool Scene::hasTexture(int id) const { return pointClouds_.find(id) != pointClouds_.end() && pointClouds_.at(id)->hasTexture(); } std::set Scene::getAddedClouds() const { return uKeysSet(pointClouds_); } void Scene::updateCloudPolygons(int id, const std::vector & polygons) { std::map::iterator iter=pointClouds_.find(id); if(iter != pointClouds_.end()) { iter->second->updatePolygons(polygons); } } void Scene::updateMesh(int id, const rtabmap::Mesh & mesh) { std::map::iterator iter=pointClouds_.find(id); if(iter != pointClouds_.end()) { iter->second->updateMesh(mesh); } } void Scene::updateGains(int id, float gainR, float gainG, float gainB) { std::map::iterator iter=pointClouds_.find(id); if(iter != pointClouds_.end()) { iter->second->setGains(gainR, gainG, gainB); } } void Scene::setGridColor(float r, float g, float b) { if(grid_) { grid_->SetColor(r, g, b); } } rtabmap-0.22.1/app/android/jni/scene.h000066400000000000000000000216301503504370700174560ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_POINT_CLOUD_SCENE_H_ #define TANGO_POINT_CLOUD_SCENE_H_ #ifdef __ANDROID__ #include #endif #include #include #include "CameraMobile.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "point_cloud_drawable.h" #include "graph_drawable.h" #include "bounding_box_drawable.h" #include "background_renderer.h" #include "text_drawable.h" #include "quad_color.h" #include #include // Scene provides OpenGL drawable objects and renders them for visualization. class Scene { public: static const glm::vec3 kHeightOffset; public: // Constructor and destructor. Scene(); ~Scene(); // Allocate OpenGL resources for rendering. void InitGLContent(); // Release non-OpenGL allocated resources. void DeleteResources(); // Setup GL view port. void SetupViewPort(int w, int h); int getViewPortWidth() const {return screenWidth_;} int getViewPortHeight() const {return screenHeight_;} rtabmap::ScreenRotation getScreenRotation() const {return color_camera_to_display_rotation_;} void setScreenRotation(rtabmap::ScreenRotation colorCameraToDisplayRotation) {color_camera_to_display_rotation_ = colorCameraToDisplayRotation;} void clear(); // removed all point clouds void clearLines(); void clearTexts(); void clearQuads(); void clearCircles(); // Render loop. // @param: cur_pose_transformation, latest pose's transformation. // @param: point_cloud_transformation, pose transformation at point cloud // frame's timestamp. // @param: point_cloud_vertices, point cloud's vertices of the current point // frame. int Render(const float * uvsTransformed = 0, glm::mat4 arViewMatrix = glm::mat4(0), glm::mat4 arProjectionMatrix=glm::mat4(0), const rtabmap::Mesh & occlusionMesh=rtabmap::Mesh(), bool mapping=false); // Set render camera's viewing angle, first person, third person or top down. // // @param: camera_type, camera type includes first person, third person and // top down void SetCameraType(tango_gl::GestureCamera::CameraType camera_type); tango_gl::GestureCamera::CameraType GetCameraType() const {return gesture_camera_->GetCameraType();} void SetCameraPose(const rtabmap::Transform & pose); // opengl camera rtabmap::Transform GetCameraPose() const {return currentPose_!=0?*currentPose_:rtabmap::Transform();} rtabmap::Transform GetOpenGLCameraPose(float * fov = 0) const; // Touch event passed from android activity. This function only support two // touches. // // @param: touch_count, total count for touches. // @param: event, touch event of current touch. // @param: x0, normalized touch location for touch 0 on x axis. // @param: y0, normalized touch location for touch 0 on y axis. // @param: x1, normalized touch location for touch 1 on x axis. // @param: y1, normalized touch location for touch 1 on y axis. void OnTouchEvent(int touch_count, tango_gl::GestureCamera::TouchEvent event, float x0, float y0, float x1, float y1); void updateGraph( const std::map & poses, const std::multimap & links); void setGraphVisible(bool visible); void setGridVisible(bool visible); void setTraceVisible(bool visible); void setFrustumVisible(bool visible); void addMarker(int id, const rtabmap::Transform & pose); void setMarkerPose(int id, const rtabmap::Transform & pose); bool hasMarker(int id) const; void removeMarker(int id); std::set getAddedMarkers() const; void addCloud( int id, const pcl::PointCloud::Ptr & cloud, const pcl::IndicesPtr & indices, const rtabmap::Transform & pose); void removeCloudOrMesh(int id); void addMesh( int id, const rtabmap::Mesh & mesh, const rtabmap::Transform & pose, bool createWireframe = false); void addLine( int id, const cv::Point3f & pt1, const cv::Point3f & pt2, const tango_gl::Color & color = tango_gl::Color(1.0f, 1.0f, 1.0f)); void removeLine(int id); void addText( int id, const std::string & text, const rtabmap::Transform & pose, float size, const tango_gl::Color & color); void removeText(int id); void addQuad( int id, float size, const rtabmap::Transform & pose, const tango_gl::Color & color, float alpha = 1.0f); void addQuad( int id, float widthLeft, float widthRight, float heightBottom, float heightTop, const rtabmap::Transform & pose, const tango_gl::Color & color, float alpha =1.0f); void removeQuad(int id); bool hasQuad(int id) const; void addCircle( int id, float radius, const rtabmap::Transform & pose, const tango_gl::Color & color, float alpha = 1.0f); void removeCircle(int id); bool hasCircle(int id) const; void setCloudPose(int id, const rtabmap::Transform & pose); void setCloudVisible(int id, bool visible); bool hasCloud(int id) const; bool hasMesh(int id) const; bool hasTexture(int id) const; std::set getAddedClouds() const; void updateCloudPolygons(int id, const std::vector & polygons); void updateMesh(int id, const rtabmap::Mesh & mesh); void updateGains(int id, float gainR, float gainG, float gainB); void setBlending(bool enabled) {blending_ = enabled;} void setMapRendering(bool enabled) {mapRendering_ = enabled;} void setMeshRendering(bool enabled, bool withTexture) {meshRendering_ = enabled; meshRenderingTexture_ = withTexture;} void setPointSize(float size) {pointSize_ = size;} void setFOV(float angle); void setOrthoCropFactor(float value); void setGridRotation(float angleDeg); void setLighting(bool enabled) {lighting_ = enabled;} void setBackfaceCulling(bool enabled) {backfaceCulling_ = enabled;} void setWireframe(bool enabled) {wireFrame_ = enabled;} void setTextureColorSeamsHidden(bool hidden) {textureColorSeamsHidden_ = hidden;} void setBackgroundColor(float r, float g, float b) {r_=r; g_=g; b_=b;} // 0.0f <> 1.0f void setGridColor(float r, float g, float b); bool isBlending() const {return blending_;} bool isMapRendering() const {return mapRendering_;} bool isMeshRendering() const {return meshRendering_;} bool isMeshTexturing() const {return meshRendering_ && meshRenderingTexture_;} float getPointSize() const {return pointSize_;} bool isLighting() const {return lighting_;} bool isBackfaceCulling() const {return backfaceCulling_;} bool isWireframe() const {return wireFrame_;} BackgroundRenderer * background_renderer_; private: // Camera object that allows user to use touch input to interact with. tango_gl::GestureCamera* gesture_camera_; // Device axis (in device frame of reference). tango_gl::Axis* axis_; // Device frustum. tango_gl::Frustum* frustum_; // Ground grid. tango_gl::Grid* grid_; // Bounding box BoundingBoxDrawable * box_; // Trace of pose data. tango_gl::Trace* trace_; GraphDrawable * graph_; bool graphVisible_; bool gridVisible_; bool traceVisible_; bool frustumVisible_; std::map markers_; rtabmap::ScreenRotation color_camera_to_display_rotation_; std::map pointClouds_; std::map lines_; std::map texts_; std::map quads_; std::map circles_; rtabmap::Transform * currentPose_; // Shader to display point cloud. GLuint graph_shader_program_; bool blending_; bool mapRendering_; bool meshRendering_; bool meshRenderingTexture_; float pointSize_; bool boundingBoxRendering_; bool lighting_; bool backfaceCulling_; bool wireFrame_; bool textureColorSeamsHidden_; float r_; float g_; float b_; GLuint fboId_; GLuint rboId_; GLuint depthTexture_; // 0=objects+occlusion GLsizei screenWidth_; GLsizei screenHeight_; bool doubleTapOn_; cv::Point2f doubleTapPos_; }; #endif // TANGO_POINT_CLOUD_SCENE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/000077500000000000000000000000001503504370700177165ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/tango-gl/axis.cpp000066400000000000000000000056711503504370700213770ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/axis.h" #include "tango-gl/shaders.h" namespace tango_gl { static const float float_vertices[] = {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; static const float float_colors[] = { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f}; Axis::Axis() : Line(3.0f, GL_LINES) { // Implement SetShader here, not using the dedault one. shader_program_ = util::CreateProgram(shaders::GetColorVertexShader().c_str(), shaders::GetBasicFragmentShader().c_str()); if (!shader_program_) { LOGE("Could not create program."); } uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp"); attrib_colors_ = glGetAttribLocation(shader_program_, "color"); attrib_vertices_ = glGetAttribLocation(shader_program_, "vertex"); size_t size = sizeof(float_vertices) / (sizeof(float) * 3); for (size_t i = 0; i < size; i++) { vec_vertices_.push_back(glm::vec3(float_vertices[i * 3], float_vertices[i * 3 + 1], float_vertices[i * 3 + 2])); vec_colors_.push_back( glm::vec4(float_colors[i * 4], float_colors[i * 4 + 1], float_colors[i * 4 + 2], float_colors[i * 4 + 3])); } } void Axis::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glUseProgram(shader_program_); glLineWidth(line_width_); glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mvp_mat = projection_mat * view_mat * model_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), &vec_vertices_[0]); glEnableVertexAttribArray(attrib_colors_); glVertexAttribPointer(attrib_colors_, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), &vec_colors_[0]); glDrawArrays(render_mode_, 0, vec_vertices_.size()); glDisableVertexAttribArray(attrib_vertices_); glDisableVertexAttribArray(attrib_colors_); glUseProgram(0); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/band.cpp000066400000000000000000000122421503504370700213270ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/band.h" #include "tango-gl/util.h" namespace tango_gl { // Set band resolution to 0.01m(1cm) when using UpdateVertexArray() static const float kMinDistanceSquared = 0.0001f; Band::Band(const unsigned int max_length) : band_width_(0.2), max_length_(max_length) { SetShader(); vertices_v_.reserve(max_length); pivot_left = glm::vec3(0, 0, 0); pivot_right = glm::vec3(0, 0, 0); } void Band::SetWidth(const float width) { band_width_ = width; } void Band::UpdateVertexArray(const glm::mat4 m, BandMode mode) { // First 2 vertices of a band + 3 arrow head vertices. bool need_to_initialize = (vertices_v_.size() < 5); bool sufficient_delta = false; if (!need_to_initialize) { // Band head is the first two vertices after arrow head. glm::vec3 band_front = 0.5f * (vertices_v_[vertices_v_.size() - 4] + vertices_v_[vertices_v_.size() - 5]); sufficient_delta = kMinDistanceSquared < util::DistanceSquared(band_front, util::GetTranslationFromMatrix(m)); } if (need_to_initialize || sufficient_delta) { glm::vec3 left = glm::vec3(-band_width_ * 0.5f, 0, 0); glm::vec3 right = glm::vec3(band_width_ * 0.5f, 0, 0); glm::vec3 arrow_left = glm::vec3(-band_width_ * 0.75f, 0, 0); glm::vec3 arrow_right = glm::vec3(band_width_ * 0.75f, 0, 0); glm::vec3 arrow_front = glm::vec3(0, 0, -band_width_ * 0.75f); // If keep right pivot point, or normal mode, // then only update left pivot point. if (mode == BandMode::kNormal || mode == BandMode::kKeepRight) { pivot_left = util::ApplyTransform(m, left); } // If keep left pivot point, or normal mode, // then only update right pivot point. if (mode == BandMode::kNormal || mode == BandMode::kKeepLeft) { pivot_right = util::ApplyTransform(m, right); } glm::mat4 head_m = m; if (mode != BandMode::kNormal) { glm::vec3 up = glm::vec3(0, 1.0f, 0); glm::vec3 position = 0.5f * (pivot_left + pivot_right); glm::vec3 heading = glm::cross(up, pivot_right-pivot_left); head_m = glm::inverse(glm::lookAt(glm::vec3(0, 0, 0), heading, up)); head_m[3][0] = position.x; head_m[3][1] = position.y; head_m[3][2] = position.z; } if (need_to_initialize) { vertices_v_.resize(5); } else { vertices_v_.resize(vertices_v_.size() + 2); } size_t insertion_start = vertices_v_.size() - 5; vertices_v_[insertion_start + 0] = pivot_left; vertices_v_[insertion_start + 1] = pivot_right; vertices_v_[insertion_start + 2] = util::ApplyTransform(head_m, arrow_left); vertices_v_[insertion_start + 3] = util::ApplyTransform(head_m, arrow_right); vertices_v_[insertion_start + 4] = util::ApplyTransform(head_m, arrow_front); if (vertices_v_.size() > max_length_) { vertices_v_.erase(vertices_v_.begin(), vertices_v_.begin() + 2); } } } void Band::UpdateVertexArray(const glm::mat4 m) { // Defualt to call update with normal mode. UpdateVertexArray(m, BandMode::kNormal); } void Band::SetVertexArray(const std::vector& v, const glm::vec3& up) { vertices_v_.clear(); vertices_v_.reserve(2 * v.size()); if (v.size() < 2) return; for (size_t i = 0; i < v.size() - 1; ++i) { glm::vec3 gl_p_world_a = v[i]; glm::vec3 gl_p_world_b = v[i + 1]; glm::vec3 dir = glm::normalize(gl_p_world_b - gl_p_world_a); glm::vec3 left = glm::cross(up, dir); glm::normalize(left); vertices_v_.push_back(gl_p_world_a + (band_width_ / 2.0f * left)); vertices_v_.push_back(gl_p_world_a - (band_width_ / 2.0f * left)); // Cap the end of the path. if (i == v.size() - 2) { vertices_v_.push_back(gl_p_world_b + (band_width_ / 2.0f * left)); vertices_v_.push_back(gl_p_world_b - (band_width_ / 2.0f * left)); } } } void Band::ClearVertexArray() { vertices_v_.clear(); } void Band::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glUseProgram(shader_program_); glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mvp_mat = projection_mat * view_mat * model_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); glUniform4f(uniform_color_, red_, green_, blue_, alpha_); glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), &vertices_v_[0]); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices_v_.size()); glDisableVertexAttribArray(attrib_vertices_); glUseProgram(0); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/bounding_box.cpp000066400000000000000000000051131503504370700230770ustar00rootroot00000000000000#include "tango-gl/bounding_box.h" namespace tango_gl { BoundingBox::BoundingBox(const std::vector& vertices) { // Set min and max to the first vertice. bounding_min_ = glm::vec3(vertices[0], vertices[1], vertices[2]); bounding_max_ = bounding_min_; size_t vertices_count = vertices.size() / 3; for (size_t i = 1; i < vertices_count; i += 3) { bounding_min_.x = std::min(vertices[i * 3], bounding_min_.x); bounding_min_.y = std::min(vertices[i * 3 + 1], bounding_min_.y); bounding_min_.z = std::min(vertices[i * 3 + 2], bounding_min_.z); bounding_max_.x = std::max(vertices[i * 3], bounding_max_.x); bounding_max_.y = std::max(vertices[i * 3 + 1], bounding_max_.y); bounding_max_.z = std::max(vertices[i * 3 + 2], bounding_max_.z); } } bool BoundingBox::IsIntersecting(const Segment& segment, const glm::quat& rotation, const glm::mat4& transformation) { // The current bounding box. glm::vec3 min, max; // If the mesh has been rotated, we need to derive a new bounding box // based on the original one, if it just been translated or scaled, // we can still use the original one with current model matrix applied. if (rotation == glm::quat(1.0f, 0.0f, 0.0f, 0.0f)) { min = util::ApplyTransform(transformation, bounding_min_); max = util::ApplyTransform(transformation, bounding_max_); } else { std::vector box; // Derive 8 vertices of the new bounding box from original min and max. box.push_back(bounding_min_); box.push_back(bounding_max_); box.push_back(glm::vec3(bounding_min_.x, bounding_max_.y, bounding_max_.z)); box.push_back(glm::vec3(bounding_max_.x, bounding_min_.y, bounding_min_.z)); box.push_back(glm::vec3(bounding_min_.x, bounding_min_.y, bounding_max_.z)); box.push_back(glm::vec3(bounding_max_.x, bounding_max_.y, bounding_min_.z)); box.push_back(glm::vec3(bounding_max_.x, bounding_min_.y, bounding_max_.z)); box.push_back(glm::vec3(bounding_min_.x, bounding_max_.y, bounding_min_.z)); min = util::ApplyTransform(transformation, bounding_min_); max = min; for (size_t i = 1; i < box.size(); i++) { glm::vec3 temp = util::ApplyTransform(transformation, box[i]); min.x = std::min(temp.x, min.x); min.y = std::min(temp.y, min.y); min.z = std::min(temp.z, min.z); max.x = std::max(temp.x, max.x); max.y = std::max(temp.y, max.y); max.z = std::max(temp.z, max.z); } } return util::SegmentAABBIntersect(min, max, segment.start, segment.end); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/camera.cpp000066400000000000000000000050261503504370700216550ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/camera.h" #include "tango-gl/util.h" namespace tango_gl { Camera::Camera() { field_of_view_ = 45.0f * DEGREE_2_RADIANS; aspect_ratio_ = 4.0f / 3.0f; width_ = 800.0f; height_ = 600.0f; near_clip_plane_ = 0.5f; far_clip_plane_ = 50.0f; ortho_ = false; orthoScale_ = 2.0f; orthoCropFactor_ = -1.0f; } glm::mat4 Camera::GetViewMatrix() { return glm::inverse(GetTransformationMatrix()); } glm::mat4 Camera::GetProjectionMatrix() { if(ortho_) { return glm::ortho(-orthoScale_*aspect_ratio_, orthoScale_*aspect_ratio_, -orthoScale_, orthoScale_, orthoScale_ + orthoCropFactor_, far_clip_plane_); } return glm::perspective(field_of_view_, aspect_ratio_, near_clip_plane_, far_clip_plane_); } void Camera::SetWindowSize(float width, float height) { width_ = width; height_ = height; aspect_ratio_ = width/height; } void Camera::SetFieldOfView(float fov) { field_of_view_ = fov * DEGREE_2_RADIANS; } void Camera::SetNearFarClipPlanes(const float near, const float far) { near_clip_plane_ = near; far_clip_plane_ = far; } Camera::~Camera() { } glm::mat4 Camera::ProjectionMatrixForCameraIntrinsics(float width, float height, float fx, float fy, float cx, float cy, float near, float far) { const float xscale = near / fx; const float yscale = near / fy; const float xoffset = (cx - (width / 2.0)) * xscale; // Color camera's coordinates has y pointing downwards so we negate this term. const float yoffset = -(cy - (height / 2.0)) * yscale; return glm::frustum(xscale * -width / 2.0f - xoffset, xscale * width / 2.0f - xoffset, yscale * -height / 2.0f - yoffset, yscale * height / 2.0f - yoffset, near, far); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/circle.cpp000066400000000000000000000023321503504370700216630ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/circle.h" namespace tango_gl { Circle::Circle(float radius, int resolution) : Mesh(GL_TRIANGLE_FAN){ SetShader(); std::vector vertices; vertices.reserve(3 * (resolution + 2)); vertices.push_back(0); vertices.push_back(0); vertices.push_back(0); float delta_theta = M_PI * 2.0f / static_cast(resolution); for (int i = resolution; i >= 0; i--) { float theta = delta_theta * static_cast(i); vertices.push_back(cos(theta) * radius); vertices.push_back(sin(theta) * radius); vertices.push_back(0); } SetVertices(vertices); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/conversions.cpp000066400000000000000000000033601503504370700227740ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/conversions.h" namespace tango_gl { namespace conversions { glm::mat4 opengl_world_T_tango_world() { // Note glm is column-wise. return glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } glm::mat4 color_camera_T_opengl_camera() { // Note glm is column-wise. return glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } glm::mat4 depth_camera_T_opengl_camera() { // Note glm is column-wise. return glm::mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } glm::quat QuatTangoToGl(const glm::quat& tango_q_frame) { const float kSqrt2Over2 = std::sqrt(2.0) / 2.0f; // Tango frame is a -90 degree rotation about +X from the GL frame. glm::quat gl_q_tango = glm::quat(kSqrt2Over2, -kSqrt2Over2, 0.0f, 0.0f); return gl_q_tango * tango_q_frame; } } // namespace gl_tango_conversions } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/cube.cpp000066400000000000000000000051451503504370700213450ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/cube.h" namespace tango_gl { static const GLfloat const_vertices[] = { -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f}; static const GLfloat const_normals[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f}; Cube::Cube() { SetShader(true); std::vector vertices( const_vertices, const_vertices + sizeof(const_vertices) / sizeof(GLfloat)); std::vector normals( const_normals, const_normals + sizeof(const_normals) / sizeof(GLfloat)); SetVertices(vertices, normals); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/drawable_object.cpp000066400000000000000000000040371503504370700235350ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/drawable_object.h" #include "tango-gl/shaders.h" namespace tango_gl { void DrawableObject::SetShader() { shader_program_ = util::CreateProgram(shaders::GetBasicVertexShader().c_str(), shaders::GetBasicFragmentShader().c_str()); if (!shader_program_) { LOGE("Could not create program."); } uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp"); attrib_vertices_ = glGetAttribLocation(shader_program_, "vertex"); uniform_color_ = glGetUniformLocation(shader_program_, "color"); } void DrawableObject::DeleteGlResources() { if (shader_program_) { glDeleteShader(shader_program_); } } void DrawableObject::SetColor(float red, float green, float blue) { red_ = red; green_ = green; blue_ = blue; } void DrawableObject::SetColor(const Color& color) { SetColor(color.r, color.g, color.b); } void DrawableObject::SetAlpha(const float alpha) { alpha_ = alpha; } void DrawableObject::SetVertices(const std::vector& vertices) { vertices_ = vertices; } void DrawableObject::SetVertices(const std::vector& vertices, const std::vector& indices) { vertices_ = vertices; indices_ = indices; } void DrawableObject::SetVertices(const std::vector& vertices, const std::vector& normals) { vertices_ = vertices; normals_ = normals; } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/frustum.cpp000066400000000000000000000025571503504370700221400ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/frustum.h" namespace tango_gl { static const float float_vertices[] = { 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f}; Frustum::Frustum() : Line(3.0f, GL_LINES) { SetShader(); size_t size = sizeof(float_vertices) / sizeof(float); for (size_t i = 0; i < size; i += 3) { vec_vertices_.push_back(glm::vec3(float_vertices[i], float_vertices[i + 1], float_vertices[i + 2])); } } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/gesture_camera.cpp000066400000000000000000000175721503504370700234240ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/gesture_camera.h" #include "tango-gl/util.h" #include "glm/gtx/quaternion.hpp" namespace { // Render camera observation distance in third person camera mode. const float kThirdPersonCameraDist = 7.0f; // Render camera observation distance in third person camera mode. const float kThirdPersonFollowCameraDist = 2.0f; // Render camera observation distance in top down camera mode. const float kTopDownCameraDist = 5.0f; // Zoom in speed. const float kZoomSpeed = 10.0f; // Move speed const float kMoveSpeed = 10.0f; // Rotation speed const float kRotationSpeed = 2.0f; // Min/max clamp value of camera observation distance. const float kCamViewMinDist = .1f; const float kCamViewMaxDist = 100.f; // FOV set up values. // Third and top down camera's FOV is 65 degrees. // First person camera's FOV is 85 degrees. const float kHighestFov = 120.0f; const float kHighFov = 85.0f; const float kLowFov = 65.0f; const float kLowestFov = 40.0f; } namespace tango_gl { GestureCamera::GestureCamera() : cam_cur_target_rot_(1,0,0,0), start_touch_dist_(0.0f), cur_touch_dist_(0.0f) { cam_parent_transform_ = new Transform(); SetParent(cam_parent_transform_); } GestureCamera::~GestureCamera() { delete cam_parent_transform_; } void GestureCamera::OnTouchEvent(int touch_count, TouchEvent event, float x0, float y0, float x1, float y1) { if (camera_type_!=kFirstPerson && touch_count == 1) { switch (event) { case kTouch0Down: { cam_start_angle_ = cam_cur_angle_; touch0_start_position_.x = x0; touch0_start_position_.y = y0; break; } case kTouchMove: { glm::vec2 offset; float rotation_x = (touch0_start_position_.y - y0) * kRotationSpeed; float rotation_y = (touch0_start_position_.x - x0) * kRotationSpeed; if(camera_type_!=kTopOrtho) cam_cur_angle_.x = cam_start_angle_.x + rotation_x; cam_cur_angle_.y = cam_start_angle_.y + rotation_y; StartCameraToCurrentTransform(); break; } default: { break; } } } if (touch_count == 2) { switch (event) { case kTouch1Down: { float abs_x = x0 - x1; float abs_y = y0 - y1; start_touch_dist_ = std::sqrt(abs_x * abs_x + abs_y * abs_y); cam_start_dist_ = GetPosition().z; cam_start_fov_ = this->getFOV(); // center touch touch0_start_position_.x = (x0+x1)/2.0f; touch0_start_position_.y = (y0+y1)/2.0f; break; } case kTouchMove: { float abs_x = x0 - x1; float abs_y = y0 - y1; float dist = start_touch_dist_ - std::sqrt(abs_x * abs_x + abs_y * abs_y); if(camera_type_ == kFirstPerson) { this->SetFieldOfView(tango_gl::util::Clamp(cam_start_fov_ + dist * kZoomSpeed*10.0f, kLowestFov, kHighestFov)); } else { cam_cur_dist_ = tango_gl::util::Clamp(cam_start_dist_ + dist * kZoomSpeed, kCamViewMinDist, kCamViewMaxDist); this->SetOrthoMode(camera_type_ == kTopOrtho); if(camera_type_ == kTopOrtho) { this->SetOrthoScale(cam_cur_dist_); } glm::vec2 touch_center_position((x0+x1)/2.0f, (y0+y1)/2.0f); glm::vec2 offset; offset.x = (touch_center_position.x - touch0_start_position_.x) * kMoveSpeed; offset.y = (touch_center_position.y - touch0_start_position_.y) * kMoveSpeed; touch0_start_position_ = touch_center_position; StartCameraToCurrentTransform(); anchor_offset_ += glm::rotate(cam_parent_transform_->GetRotation(), glm::vec3(-offset.x, offset.y, 0)); } break; } default: { break; } } } } Segment GestureCamera::GetSegmentFromTouch(float normalized_x, float normalized_y, float touch_range) { float screen_height = touch_range * (2.0f * glm::tan(field_of_view_ * 0.5f)); float screen_width = screen_height * aspect_ratio_; // normalized_x and normalized_x are from OnTouchEvent, top-left corner of the // screen // is [0, 0], transform it to opengl frame. normalized_x = normalized_x - 0.5f; normalized_y = 0.5f - normalized_y; glm::vec3 start = util::ApplyTransform(GetTransformationMatrix(), glm::vec3(0, 0, 0)); glm::vec3 end = util::ApplyTransform(GetTransformationMatrix(), glm::vec3(normalized_x * screen_width, normalized_y * screen_height, -touch_range)); Segment segment(start, end); return segment; } void GestureCamera::SetAnchorPosition(const glm::vec3& pos, const glm::quat & rotation) { // Anchor position cam_parent_transform_->SetPosition(pos+anchor_offset_); // Anchor rotation if(camera_type_ == kThirdPersonFollow) { cam_cur_target_rot_ = rotation; cam_cur_target_rot_.x = 0; cam_cur_target_rot_.z = 0; cam_cur_target_rot_ = glm::normalize(cam_cur_target_rot_); StartCameraToCurrentTransform(); } } void GestureCamera::SetCameraType(CameraType camera_index) { camera_type_ = camera_index; switch (camera_index) { case kFirstPerson: SetOrthoMode(false); SetFieldOfView(kLowestFov); SetNearFarClipPlanes(0.25, 25); SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); cam_cur_dist_ = 0.0f; anchor_offset_ = glm::vec3(0.0f,0.0f,0.0f); cam_cur_angle_.x = 0.0f; cam_cur_angle_.y = 0.0f; cam_cur_target_rot_ = glm::quat(1,0,0,0); cam_parent_transform_->SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); StartCameraToCurrentTransform(); break; case kThirdPerson: case kThirdPersonFollow: SetOrthoMode(false); SetFieldOfView(kLowFov); SetNearFarClipPlanes(1, 50); SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); cam_cur_dist_ = camera_index==kThirdPersonFollow?kThirdPersonFollowCameraDist:kThirdPersonCameraDist; anchor_offset_ = glm::vec3(0.0f,0.0f,0.0f); cam_cur_angle_.x = -M_PI / 12.0f; cam_cur_angle_.y = kThirdPersonFollow?0:M_PI / 2.0f; cam_cur_target_rot_ = glm::quat(1,0,0,0); StartCameraToCurrentTransform(); break; case kTopDown: SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); SetOrthoMode(false); SetFieldOfView(kLowFov); SetNearFarClipPlanes(1, 50); cam_cur_dist_ = kTopDownCameraDist; anchor_offset_ = glm::vec3(0.0f,0.0f,0.0f); cam_cur_angle_.x = -M_PI / 2.0f; cam_cur_angle_.y = 0.0f; cam_cur_target_rot_ = glm::quat(1,0,0,0); StartCameraToCurrentTransform(); break; case kTopOrtho: SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); SetOrthoMode(true); SetOrthoScale(kTopDownCameraDist); SetOrthoCropFactor(-1.0f); SetFieldOfView(kLowFov); SetNearFarClipPlanes(1, 50); cam_cur_dist_ = kTopDownCameraDist; anchor_offset_ = glm::vec3(0.0f,0.0f,0.0f); cam_cur_angle_.x = -M_PI / 2.0f; cam_cur_angle_.y = 0.0f; cam_cur_target_rot_ = glm::quat(1,0,0,0); StartCameraToCurrentTransform(); break; default: break; } } void GestureCamera::StartCameraToCurrentTransform() { //Anchor rotation glm::quat parent_cam_rot = glm::rotate(cam_cur_target_rot_, cam_cur_angle_.y, glm::vec3(0, 1, 0)); parent_cam_rot = glm::rotate(parent_cam_rot, cam_cur_angle_.x, glm::vec3(1, 0, 0)); cam_parent_transform_->SetRotation(parent_cam_rot); //Camera position SetPosition(glm::vec3(0, 0, cam_cur_dist_)); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/goal_marker.cpp000066400000000000000000000060211503504370700227040ustar00rootroot00000000000000 /* * Copyright 2014 Google Inc. All Rights Reserved. * Distributed under the Project Tango Preview Development Kit (PDK) Agreement. * CONFIDENTIAL. AUTHORIZED USE ONLY. DO NOT REDISTRIBUTE. */ #include "tango-gl/goal_marker.h" namespace tango_gl { static const GLfloat const_vertices[] = { -4.5298f, -0.2676f, 0.0, -4.3209f, -1.3857f, 0.0, -3.7242f, -11.1445f, 0.0, -5.6799f, -8.9811f, 0.0, -4.4540f, 0.8673f, 0.0, -7.8376f, -6.4508f, 0.0, -9.5223f, -3.0538f, 0.0, -9.9826f, -0.5898f, 0.0, -9.8157f, 1.9113f, 0.0, -9.0320f, 4.2923f, 0.0, -7.6808f, 6.4036f, 0.0, -5.8469f, 8.1125f, 0.0, -3.6457f, 9.3117f, 0.0, -4.0984f, 1.9477f, 0.0, -1.2155f, 9.9259f, 0.0, -3.4853f, 2.9057f, 0.0, 1.2912f, 9.9163f, 0.0, -2.6532f, 3.6812f, 0.0, 3.7167f, 9.2837f, 0.0, -1.6543f, 4.2254f, 0.0, 5.9087f, 8.0677f, 0.0, -0.5515f, 4.5040f, 0.0, 7.7294f, 6.3448f, 0.0, 0.5859f, 4.4997f, 0.0, 9.0645f, 4.2232f, 0.0, 1.6865f, 4.2126f, 0.0, 9.8300f, 1.8363f, 0.0, 2.6812f, 3.6609f, 0.0, 9.9778f, -0.6660f, 0.0, 3.5074f, 2.8791f, 0.0, 9.4987f, -3.1264f, 0.0, 4.1132f, 1.9164f, 0.0, 7.7964f, -6.5204f, 0.0, 4.4605f, 0.8333f, 0.0, 5.6245f, -9.0444f, 0.0, 4.5276f, -0.3022f, 0.0, 3.6572f, -11.1925f, 0.0, 4.3102f, -1.4187f, 0.0, 1.5165f, -13.5960f, 0.0, 3.8220f, -2.4460f, 0.0, -0.0382f, -16.4245f, 0.0, 3.0936f, -3.3197f, 0.0, -1.5902f, -13.5657f, 0.0, 2.1709f, -3.9847f, 0.0, -3.8405f, -2.4168f, 0.0, -3.1189f, -3.2959f, 0.0, -2.2012f, -3.9680f, 0.0, -1.1452f, -4.3908f, 0.0, -0.0173f, -4.5377f, 0.0, 1.1117f, -4.3994f, 0.0}; static const GLushort const_indices[] = { 1, 2, 3, 1, 3, 4, 5, 1, 4, 4, 6, 7, 5, 4, 7, 7, 8, 9, 9, 10, 11, 7, 9, 11, 5, 7, 11, 5, 11, 12, 5, 12, 13, 14, 5, 13, 14, 13, 15, 16, 14, 15, 16, 15, 17, 18, 16, 17, 18, 17, 19, 20, 18, 19, 20, 19, 21, 22, 20, 21, 22, 21, 23, 24, 22, 23, 24, 23, 25, 26, 24, 25, 26, 25, 27, 28, 26, 27, 28, 27, 29, 30, 28, 29, 30, 29, 31, 32, 30, 31, 32, 31, 33, 34, 32, 33, 34, 33, 35, 36, 34, 35, 36, 35, 37, 38, 36, 37, 38, 37, 39, 40, 38, 39, 40, 39, 41, 42, 40, 41, 42, 41, 43, 44, 42, 43, 44, 43, 3, 3, 2, 45, 3, 45, 46, 3, 46, 47, 3, 47, 48, 3, 48, 49, 3, 49, 50, 44, 3, 50}; GoalMarker::GoalMarker() { SetShader(); std::vector vertices( const_vertices, const_vertices + sizeof(const_vertices) / sizeof(GLfloat)); std::vector indices( const_indices, const_indices + sizeof(const_indices) / sizeof(GLushort)); // Change indices so they are zero-indexed. for (size_t i = 0; i < indices.size(); ++i) { indices[i] = indices[i] - 1; } SetVertices(vertices, indices); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/grid.cpp000066400000000000000000000030741503504370700213530ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/grid.h" namespace tango_gl { // Initialize Grid with x and y grid count, // qx, quantity in x // qy, quantity in y. Grid::Grid(float density, int qx, int qy) : Line(1.0f, GL_LINES) { SetShader(); // 3 float in 1 vertex, 2 vertices form a line. // Horizontal line and vertical line forms the grid. float width = density * qx / 2; float height = density * qy / 2; // Horizontal line. for (int i = 0; i < (qy + 1); i++) { for (int j = 0; j < (qx + 1); j++) { vec_vertices_.push_back(glm::vec3(-width + j*density, 0.0f, -height + i * density)); vec_vertices_.push_back(glm::vec3(-width+ + (j+1)*density, 0.0f, -height + i * density)); } } for (int i = 0; i < (qx + 1); i++) { for (int j = 0; j < (qy + 1); j++) { vec_vertices_.push_back(glm::vec3(-width + i * density, 0.0f, -height + j*density)); vec_vertices_.push_back(glm::vec3(-width + i * density, 0.0f, -height + (j+1)*density)); } } } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/include/000077500000000000000000000000001503504370700213415ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/000077500000000000000000000000001503504370700230515ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/axis.h000066400000000000000000000017161503504370700241730ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_AXIS_H_ #define TANGO_GL_AXIS_H_ #include "tango-gl/line.h" namespace tango_gl { class Axis : public Line { public: Axis(); void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; private: GLuint attrib_colors_; std::vector vec_colors_; }; } // namespace tango_gl #endif // TANGO_GL_AXIS_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/band.h000066400000000000000000000033071503504370700241310ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_BAND_H_ #define TANGO_GL_BAND_H_ #include #include #include "tango-gl/drawable_object.h" namespace tango_gl { class Band : public DrawableObject { public: enum BandMode { kNormal = 0, kKeepLeft = 1, kKeepRight = 2 }; Band(const unsigned int max_legnth); void SetWidth(const float width); // Render a Band with arrow head, pass in the mode for rendering, // kKeepLeft is left turn, kKeepRight is right turn, // when making a turn, vertices only get updated in one side to avoid overlapping. void UpdateVertexArray(const glm::mat4 m, BandMode mode); void UpdateVertexArray(const glm::mat4 m); void SetVertexArray(const std::vector& v, const glm::vec3& up); void ClearVertexArray(); void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; private: float band_width_; unsigned int max_length_; std::vector vertices_v_; // Current band head's left and right position in world frame. glm::vec3 pivot_left; glm::vec3 pivot_right; }; } // namespace tango_gl #endif // TANGO_GL_BAND_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/bounding_box.h000066400000000000000000000025471503504370700257070ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_BOUNDING_BOX_H_ #define TANGO_GL_BOUNDING_BOX_H_ #include #include "tango-gl/segment.h" #include "tango-gl/util.h" namespace tango_gl { class BoundingBox { public: BoundingBox() : bounding_min_(glm::vec3(0, 0, 0)), bounding_max_(glm::vec3(0, 0, 0)) {} BoundingBox(const std::vector& vertices); BoundingBox(const glm::vec3& min, const glm::vec3& max) : bounding_min_(min), bounding_max_(max) {} bool IsIntersecting(const Segment& segment, const glm::quat& rotation, const glm::mat4& transformation); private: // Axis-aligned bounding box minimum and maximum point. glm::vec3 bounding_min_; glm::vec3 bounding_max_; }; } // namespace tango_gl #endif // TANGO_GL_BOUNDING_BOX_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/camera.h000066400000000000000000000050011503504370700244460ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_CAMERA_H_ #define TANGO_GL_CAMERA_H_ #include "tango-gl/transform.h" namespace tango_gl { class Camera : public Transform { public: Camera(); Camera(const Camera& other) = delete; Camera& operator=(const Camera&) = delete; ~Camera(); void SetWindowSize(const float width, const float height); void SetFieldOfView(const float fov); void SetOrthoMode(bool enabled) {ortho_ = enabled;} void SetOrthoScale(float scale) {orthoScale_ = scale;} void SetOrthoCropFactor(float value) {orthoCropFactor_ = value;} void SetNearFarClipPlanes(const float near, const float far); glm::mat4 GetViewMatrix(); glm::mat4 GetProjectionMatrix(); float getNearClipPlane() const {return near_clip_plane_;} float getFarClipPlane() const {return far_clip_plane_;} /** * Create an OpenGL perspective matrix from window size, camera intrinsics, and clip settings. * * @param width - The width of the camera image. * @param height - The height of the camera image. * @param fx - The x-axis focal length of the camera. * @param fy - The y-axis focal length of the camera. * @param cx - The x-coordinate principal point in pixels. * @param cy - The y-coordinate principal point in pixels. * @param near - The desired near z-clipping plane. * @param far - The desired far z-clipping plane. */ static glm::mat4 ProjectionMatrixForCameraIntrinsics(float width, float height, float fx, float fy, float cx, float cy, float near, float far); protected: float field_of_view_; float aspect_ratio_; float width_; float height_; float near_clip_plane_, far_clip_plane_; bool ortho_; float orthoScale_; float orthoCropFactor_; }; } // namespace tango_gl #endif // TANGO_GL_CAMERA_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/circle.h000066400000000000000000000015321503504370700244640ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_CIRCLE_H_ #define TANGO_GL_CIRCLE_H_ #include "tango-gl/mesh.h" namespace tango_gl { class Circle : public Mesh { public: Circle(float radius, int resolution); }; } // namespace tango_gl #endif // TANGO_GL_CIRCLE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/color.h000066400000000000000000000017351503504370700243460ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_COLOR_H_ #define TANGO_GL_COLOR_H_ namespace tango_gl { class Color { public: Color() : r(0), g(0), b(0) {} Color(float red, float green, float blue) : r(red), g(green), b(blue) {} Color(const Color&) = default; Color& operator=(const Color&) = default; float r; float g; float b; }; } // namespace tango_gl #endif // TANGO_GL_COLOR_H_rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/conversions.h000066400000000000000000000122361503504370700255760ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_GL_TANGO_CONVERSIONS_H_ #define TANGO_GL_GL_TANGO_CONVERSIONS_H_ #define GLM_FORCE_RADIANS #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/quaternion.hpp" namespace tango_gl { namespace conversions { /** * @brief Creates a glm::vec3 from double[3] = {x, y, z}. This is designed to * work with the TangoPoseData.translation field. */ inline glm::vec3 Vec3FromArray(const double* array) { return glm::vec3(array[0], array[1], array[2]); } /** * @brief Creates a glm::quat from double[4] = {x, y, z, w}. This is designed to * work with the TangoPoseData.orientation field. */ inline glm::quat QuatFromArray(const double* array) { // Note GLM expects arguments in order {w, x, y, z}. return glm::quat(array[3], array[0], array[1], array[2]); } /** * @brief Creates a glm::mat4 rigid-frame transformation matrix from two arrays. * This is designed for the TangoPoseData translation and orientation fields. * @param A_p_B Position [x, y, z] of B_origin from A_origin, expressed in A. * @param A_q_B The quaternion representation [x, y, z, w] of the rotation * matrix A_R_B. * @return The transformation matrix A_T_B. */ inline glm::mat4 TransformFromArrays(const double* A_p_B, const double* A_q_B) { glm::vec3 glm_A_p_B = Vec3FromArray(A_p_B); glm::quat glm_A_q_B = QuatFromArray(A_q_B); return glm::translate(glm::mat4(1.0f), glm_A_p_B) * glm::mat4_cast(glm_A_q_B); } /** * @brief Creates a glm::mat4 rigid-frame transformation matrix from glm::vec3 and glm::quat. * This is designed for the TangoPoseData translation and orientation fields. * @param A_p_B A position vector of B_origin from A_origin, expressed in A. * @param A_q_B A quaternion representation of the rotation. * matrix A_R_B. * @return The transformation matrix A_T_B. */ inline glm::mat4 TransformFromVecAndQuat(const glm::vec3& A_p_B, const glm::quat& A_q_B) { return glm::translate(glm::mat4(1.0f), A_p_B) * glm::mat4_cast(A_q_B); } /** * @brief Convert (re-express, or rotate) a vector from the Tango ADF (or start- * of-service) frame convention [right, forward, up] to the typical OpenGl world * frame convention [right, up, backward]. Note this assumes the two frames are * coincident, and it doesn't know about any additional offsets between a * particular OpenGl scene and the Tango service frames. * @param tango_vec A vector expressed in the Tango ADF frame convention. * @return The same vector expressed using the Opengl frame convention. */ inline glm::vec3 Vec3TangoToGl(const glm::vec3& tango_vec) { return glm::vec3(tango_vec.x, tango_vec.z, -tango_vec.y); } /** * @brief Convert (re-express, or rotate) a vector from the typical OpenGl world * frame convention [right, up, backward] to the Tango ADF (or start-of-service) * frame convention [right, forward, up]. Note this assumes the two frames are * coincident, and it doesn't know about any additional offsets between a * particular OpenGl scene and the Tango service frames. * @param gl_vec A vector expressed in the Opengl world frame convention. * @return The same vector expressed using the Tango ADF frame convention. */ inline glm::vec3 Vec3GlToTango(const glm::vec3& gl_vec) { return glm::vec3(gl_vec.x, -gl_vec.z, gl_vec.y); } /** * @brief Given a quaternion representing the rotation matrix tango_R_any, * returns the quaternion representing gl_R_any, where "any" is an arbitrary * frame. Note the gl base frame is rotated by 90-degrees about +X from the * Tango ADF/start-of-service frame, so this is equivalent to applying such a * rotation to the quaternion. * @param tango_q_any A quaternion representing rotation matrix tango_R_any. * @return The quaternion representing gl_R_any. */ glm::quat QuatTangoToGl(const glm::quat& tango_q_any); /** * Get the fixed transformation matrix relating the opengl frame convention * (with Y-up, X-right) and the tango frame convention for the start-of-service * and ADF frames (with Z-up, X-right), termed "world" here. */ glm::mat4 opengl_world_T_tango_world(); /** * Get the fixed transformation matrix relating the frame convention of the * device's color camera frame (with Z-forward, X-right) and the opengl camera * frame (with Z-backward, X-right). */ glm::mat4 color_camera_T_opengl_camera(); /** * Get the fixed transformation matrix relating the frame convention of the * device's depth camera frame (with Z-forward, X-right) and the opengl camera * frame (with Z-backward, X-right). */ glm::mat4 depth_camera_T_opengl_camera(); } // namespace conversions } // namespace tango_gl #endif // TANGO_GL_GL_TANGO_CONVERSIONS_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/cube.h000066400000000000000000000014641503504370700241450ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_CUBE_H_ #define TANGO_GL_CUBE_H_ #include "tango-gl/mesh.h" namespace tango_gl { class Cube : public Mesh { public: Cube(); }; } // namespace tango_gl #endif // TANGO_GL_CUBE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/drawable_object.h000066400000000000000000000040051503504370700263300ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_DRAWABLE_OBJECT_H_ #define TANGO_GL_DRAWABLE_OBJECT_H_ #include #include "tango-gl/color.h" #include "tango-gl/transform.h" #include "tango-gl/util.h" namespace tango_gl { class DrawableObject : public Transform { public: DrawableObject() : red_(0), green_(0), blue_(0), alpha_(1.0f) {}; DrawableObject(const DrawableObject& other) = delete; const DrawableObject& operator=(const DrawableObject&) = delete; void DeleteGlResources(); void SetShader(); void SetColor(const Color& color); void SetColor(const float red, const float green, const float blue); void SetAlpha(const float alpha); void SetVertices(const std::vector& vertices); void SetVertices(const std::vector& vertices, const std::vector& indices); void SetVertices(const std::vector& vertices, const std::vector& normals); virtual void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const = 0; protected: float red_; float green_; float blue_; float alpha_; std::vector indices_; std::vector vertices_; std::vector normals_; GLenum render_mode_; GLuint shader_program_; GLuint uniform_color_; GLuint uniform_mvp_mat_; GLuint attrib_vertices_; GLuint attrib_normals_; }; } // namespace tango_gl #endif // TANGO_GL_DRAWABLE_OBJECT_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/frustum.h000066400000000000000000000015031503504370700247260ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_FRUSTUM_H_ #define TANGO_GL_FRUSTUM_H_ #include "tango-gl/line.h" namespace tango_gl { class Frustum : public Line { public: Frustum(); }; } // namespace tango_gl #endif // TANGO_GL_FRUSTUM_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/gesture_camera.h000066400000000000000000000053451503504370700262170ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_GESTURE_CAMERA_H_ #define TANGO_GL_GESTURE_CAMERA_H_ #include "tango-gl/camera.h" #include "tango-gl/segment.h" #include "tango-gl/transform.h" #include "tango-gl/util.h" namespace tango_gl { class GestureCamera : public Camera { public: enum CameraType { kFirstPerson = 0, kThirdPersonFollow = 1, kTopDown = 2, kTopOrtho = 3, kThirdPerson = 4 }; enum TouchEvent { kTouch0Down = 0, kTouch0Up = 1, kTouchMove = 2, kTouch1Down = 5, kTouch1Up = 6, kTouchNone = -1 }; GestureCamera(); ~GestureCamera(); void OnTouchEvent(int touch_count, TouchEvent event, float x0, float y0, float x1, float y1); // Get the ray in opengl world frame given the 2d touch position on screen, // normalized touch_x and normalized touch_y should be the same value get from // OnTouchEvent, x0 and y0, touch_range is the depth of the touch in // camera frame. Segment GetSegmentFromTouch(float normalized_x, float normalized_y, float touch_range); void SetAnchorPosition(const glm::vec3& pos, const glm::quat & rotation); void SetAnchorOffset(const glm::vec3& pos) {anchor_offset_ = pos;} const glm::vec3& GetAnchorOffset() const {return anchor_offset_;} void SetCameraDistance(float cameraDistance) {cam_cur_dist_ = cameraDistance;} float GetCameraDistance() const {return cam_cur_dist_;} // Set camera type, set render camera's parent position and rotation. void SetCameraType(CameraType camera_index); CameraType GetCameraType() const { return camera_type_; } float getFOV() const {return field_of_view_ * RADIAN_2_DEGREE;} private: void StartCameraToCurrentTransform(); // Render camera's parent transformation. Transform* cam_parent_transform_; CameraType camera_type_; glm::vec2 cam_start_angle_; glm::vec2 cam_cur_angle_; glm::quat cam_cur_target_rot_; float cam_start_dist_; float cam_start_fov_; float cam_cur_dist_; glm::vec3 anchor_offset_; float start_touch_dist_; float cur_touch_dist_; glm::vec2 touch0_start_position_; }; } // namespace tango_gl #endif // TANGO_GL_GESTURE_CAMERA_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/goal_marker.h000066400000000000000000000006611503504370700255100ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * Distributed under the Project Tango Preview Development Kit (PDK) Agreement. * CONFIDENTIAL. AUTHORIZED USE ONLY. DO NOT REDISTRIBUTE. */ #ifndef TANGO_GL_GOAL_MARKER_H_ #define TANGO_GL_GOAL_MARKER_H_ #include namespace tango_gl { class GoalMarker : public Mesh { public: GoalMarker(); }; } // namespace tango_gl #endif // TANGO_GL_GOAL_MARKER_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/grid.h000066400000000000000000000015421503504370700241510ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_GRID_H_ #define TANGO_GL_GRID_H_ #include "tango-gl/line.h" namespace tango_gl { class Grid : public Line { public: Grid(float density = 1.0f, int qx = 50, int qy = 50); }; } // namespace tango_gl #endif // TANGO_GL_GRID_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/line.h000066400000000000000000000022361503504370700241540ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_LINE_H_ #define TANGO_GL_LINE_H_ #include "tango-gl/drawable_object.h" namespace tango_gl { class Line : public DrawableObject { public: Line(float line_width, GLenum render_mode); void SetLineWidth(const float pixels); void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; void UpdateLineVertices(const std::vector& vec_vertices) { vec_vertices_ = vec_vertices; } protected: float line_width_; std::vector vec_vertices_; }; } // namespace tango_gl #endif // TANGO_GL_LINE_H_rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/mesh.h000066400000000000000000000025521503504370700241620ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_MESH_H_ #define TANGO_GL_MESH_H_ #include "tango-gl/bounding_box.h" #include "tango-gl/drawable_object.h" #include "tango-gl/segment.h" namespace tango_gl { class Mesh : public DrawableObject { public: Mesh(); Mesh(GLenum render_mode); void SetShader(); void SetShader(bool is_lighting_on); void SetBoundingBox(); void SetLightDirection(const glm::vec3& light_direction); void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; bool IsIntersecting(const Segment& segment); protected: BoundingBox* bounding_box_; bool is_lighting_on_; bool is_bounding_box_on_; glm::vec3 light_direction_; GLuint uniform_mv_mat_; GLuint uniform_light_vec_; }; } // namespace tango_gl #endif // TANGO_GL_MESH_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/obj_loader.h000066400000000000000000000035751503504370700253340ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_OBJ_LOADER_H #define TANGO_GL_OBJ_LOADER_H #include #include "tango-gl/util.h" namespace tango_gl { namespace obj_loader { // Load standard .obj file into vertices, indices or normals vectors, // OBJ file can be exported from 3D tools like 3ds Max or Blender. // A readable file with only vertices should look like // "v 1.00 2.00 3.00 // ... // f 1 2 3 // f 1 2 3 4 // ..." // // If exported with normals, file should look like // "v 1.00 2.00 3.00 // ... // f 1//1 2//3 3//4 // f 1//1 2//3 3//4 4//6 // ... // vn 1.00 2.00 3.00 // ..." // this can be used with Mesh: // // std::vector vertices; // std::vector indices; // std::vector normals; // tango_gl::obj_loader::LoadOBJData("/sdcard/model.obj", vertices, indices); // mesh->SetVertices(vertices, indices); // or // tango_gl::obj_loader::LoadOBJData("/sdcard/model.obj", vertices, normals); // mesh->SetVertices(vertices, normals); bool LoadOBJData(const char* path, std::vector& vertices, std::vector& indices); bool LoadOBJData(const char* path, std::vector& vertices, std::vector& normals); } // namespace obj_loader } // namespace tango_gl #endif // TANGO_GL_OBJ_LOADER rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/quad.h000066400000000000000000000023311503504370700241530ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_QUAD_H_ #define TANGO_GL_QUAD_H_ #include "tango-gl/drawable_object.h" namespace tango_gl { class Quad : public DrawableObject { public: Quad(); Quad(const Quad& other) = delete; Quad& operator=(const Quad&) = delete; ~Quad(); void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; void SetTextureId(GLuint texture_id); private: GLuint vertex_buffer_; GLuint shader_program_; GLuint attrib_vertices_; GLuint texture_coords_; GLuint texture_handle; GLuint uniform_mvp_mat_; GLuint texture_id_; }; } // namespace tango_gl #endif // TANGO_GL_QUAD_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/segment.h000066400000000000000000000021341503504370700246640ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_SEGMENT_H_ #define TANGO_GL_SEGMENT_H_ #include "glm/glm.hpp" namespace tango_gl { class Segment { public: Segment() : start(glm::vec3(0, 0, 0)), end(glm::vec3(0, 0, 0)) {} Segment(const glm::vec3& segment_start, const glm::vec3& segment_end) : start(segment_start), end(segment_end) {} Segment(const Segment&) = default; Segment& operator=(const Segment&) = default; glm::vec3 start; glm::vec3 end; }; } // namespace tango_gl #endif // TANGO_GL_SEGMENT_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/segment_drawable.h000066400000000000000000000016721503504370700265330ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_SEGMENT_DRAWABLE_H_ #define TANGO_GL_SEGMENT_DRAWABLE_H_ #include "tango-gl/line.h" #include "tango-gl/segment.h" namespace tango_gl { class SegmentDrawable : public Line { public: SegmentDrawable(); void UpdateSegment(const Segment& segment); }; } // namespace tango_gl #endif // TANGO_GL_SEGMENT_DRAWABLE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/shaders.h000066400000000000000000000020321503504370700246500ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_SHADERS_H_ #define TANGO_GL_SHADERS_H_ #include namespace tango_gl { namespace shaders { std::string GetBasicVertexShader(); std::string GetBasicFragmentShader(); std::string GetColorVertexShader(); std::string GetVideoOverlayVertexShader(); std::string GetVideoOverlayFragmentShader(); std::string GetShadedVertexShader(); } // namespace shaders } // namespace tango_gl #endif // TANGO_GL_SHADERS_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/texture.h000066400000000000000000000023431503504370700247240ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_TEXTURE_H_ #define TANGO_GL_TEXTURE_H_ #include #ifdef __ANDROID__ #include #endif #include "tango-gl/util.h" namespace tango_gl { class Texture { public: Texture(const char* file_path); Texture(const Texture& other) = delete; Texture& operator=(const Texture&) = delete; ~Texture(); #ifdef __ANDROID__ bool LoadFromPNG(const char* file_path); #endif GLuint GetTextureID() const; private: #ifdef __ANDROID__ png_uint_32 width_, height_; #endif int bit_depth_, color_type_; char* byte_data_; GLuint texture_id_; }; } // namespace tango_gl #endif // TANGO_GL_TEXTURE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/trace.h000066400000000000000000000016021503504370700243170ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_TRACE_H_ #define TANGO_GL_TRACE_H_ #include "tango-gl/line.h" namespace tango_gl { class Trace : public Line { public: Trace(); void UpdateVertexArray(const glm::vec3& v); void ClearVertexArray(); }; } // namespace tango_gl #endif // TANGO_GL_TRACE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/transform.h000066400000000000000000000030721503504370700252370ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_TRANSFORM_H_ #define TANGO_GL_TRANSFORM_H_ #include "glm/glm.hpp" #include "glm/gtc/quaternion.hpp" namespace tango_gl { class Transform { public: Transform(); virtual ~Transform(); Transform(const Transform& other) = delete; const Transform& operator=(const Transform& rhs) = delete; void SetPosition(const glm::vec3& position); glm::vec3 GetPosition() const; void SetRotation(const glm::quat& rotation); glm::quat GetRotation() const; void SetScale(const glm::vec3& scale); glm::vec3 GetScale() const; void Translate(const glm::vec3& translation); void SetTransformationMatrix(const glm::mat4& transform_mat); glm::mat4 GetTransformationMatrix() const; void SetParent(Transform* transform); const Transform* GetParent() const ; Transform* GetParent() ; private: Transform* parent_; glm::vec3 position_; glm::quat rotation_; glm::vec3 scale_; }; } // namespace tango_gl #endif // TANGO_GL_TRANSFORM_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/triangle.h000066400000000000000000000015101503504370700250240ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_TRIANGLE_H_ #define TANGO_GL_TRIANGLE_H_ #include "tango-gl/mesh.h" namespace tango_gl { class Triangle : public Mesh { public: Triangle(); }; } // namespace tango_gl #endif // TANGO_GL_TRIANGLE_H_ rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/util.h000066400000000000000000000067461503504370700242140ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_GL_UTIL_H_ #define TANGO_GL_GL_UTIL_H_ #define GLM_FORCE_RADIANS #define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #include #ifdef __ANDROID__ #include #include #include #include #else // __APPLE__ #include #include #include #endif #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/quaternion.hpp" #include "glm/gtc/type_ptr.hpp" #include "glm/gtx/matrix_decompose.hpp" #define LOG_TAG "rtabmap" #if defined(DISABLE_LOG) #define LOGD(...) ; #define LOGI(...) ; #define LOGW(...) ; #else #ifdef __APPLE__ #define LOGD(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n") #define LOGI(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n") #define LOGW(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n") #else #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) #endif #endif #ifdef __APPLE__ #define LOGE(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n") #define LOGF(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n") #else #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__) #endif #ifndef M_PI #define M_PI 3.1415926f #endif #define RADIAN_2_DEGREE 57.2957795f #define DEGREE_2_RADIANS 0.0174532925f namespace tango_gl { namespace util { void CheckGlError(const char* operation); GLuint CreateProgram(const char* vertex_source, const char* fragment_source); void DecomposeMatrix(const glm::mat4& transform_mat, glm::vec3& translation, glm::quat& rotation, glm::vec3& scale); // Get a 3x1 column from the upper 3x4 of a transformation matrix. Columns // 0, 1, 2 are the rotation/scale portion, and column 3 is the translation. glm::vec3 GetColumnFromMatrix(const glm::mat4& mat, const int col); // Get the translation component of a transformation matrix. glm::vec3 GetTranslationFromMatrix(const glm::mat4& mat); float Clamp(float value, float min, float max); void PrintMatrix(const glm::mat4& matrix); void PrintVector(const glm::vec3& vector); void PrintQuaternion(const glm::quat& quat); glm::vec3 LerpVector(const glm::vec3& x, const glm::vec3& y, float a); float DistanceSquared(const glm::vec3& v1, const glm::vec3& v2); bool SegmentAABBIntersect(const glm::vec3& aabb_min, const glm::vec3& aabb_max, const glm::vec3& start, const glm::vec3& end); glm::vec3 ApplyTransform(const glm::mat4& mat, const glm::vec3& vec); } // namespace util } // namespace tango_gl #endif // TANGO_GL_RENDERER_GL_UTIL rtabmap-0.22.1/app/android/jni/tango-gl/include/tango-gl/video_overlay.h000066400000000000000000000024451503504370700260760ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TANGO_GL_RENDERER_VIDEO_OVERLAY_H_ #define TANGO_GL_RENDERER_VIDEO_OVERLAY_H_ #include "tango-gl/drawable_object.h" namespace tango_gl { class VideoOverlay : public DrawableObject { public: VideoOverlay(); void Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const; GLuint GetTextureId() const { return texture_id_; } void SetTextureId(GLuint texture_id) { texture_id_ = texture_id; } private: // This id is populated on construction, and is passed to the tango service. GLuint texture_id_; GLuint attrib_texture_coords_; GLuint uniform_texture_; GLuint vertex_buffers_[3]; }; } // namespace tango_gl #endif // TANGO_GL_RENDERER_VIDEO_OVERLAY_H_ rtabmap-0.22.1/app/android/jni/tango-gl/line.cpp000066400000000000000000000030671503504370700213570ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/line.h" namespace tango_gl { Line::Line(float line_width, GLenum render_mode) { line_width_ = line_width; render_mode_ = render_mode; } void Line::SetLineWidth(const float pixels) { line_width_ = pixels; } void Line::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glUseProgram(shader_program_); glLineWidth(line_width_); glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mvp_mat = projection_mat * view_mat * model_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); glUniform4f(uniform_color_, red_, green_, blue_, alpha_); glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), &vec_vertices_[0]); glDrawArrays(render_mode_, 0, vec_vertices_.size()); glDisableVertexAttribArray(attrib_vertices_); glUseProgram(0); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/mesh.cpp000066400000000000000000000104041503504370700213550ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/mesh.h" #include "tango-gl/shaders.h" namespace tango_gl { Mesh::Mesh() { render_mode_ = GL_TRIANGLES; } Mesh::Mesh(GLenum render_mode) { render_mode_ = render_mode; } void Mesh::SetShader() { DrawableObject::SetShader(); // Default mode set to no lighting. is_lighting_on_ = false; // Default mode set to without bounding box detection. is_bounding_box_on_ = false; } void Mesh::SetShader(bool is_lighting_on) { if (is_lighting_on) { shader_program_ = util::CreateProgram(shaders::GetShadedVertexShader().c_str(), shaders::GetBasicFragmentShader().c_str()); if (!shader_program_) { LOGE("Could not create program."); } uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp"); uniform_mv_mat_ = glGetUniformLocation(shader_program_, "mv"); uniform_light_vec_ = glGetUniformLocation(shader_program_, "lightVec"); uniform_color_ = glGetUniformLocation(shader_program_, "color"); attrib_vertices_ = glGetAttribLocation(shader_program_, "vertex"); attrib_normals_ = glGetAttribLocation(shader_program_, "normal"); is_lighting_on_ = true; // Set a defualt direction for directional light. light_direction_ = glm::vec3(-1.0f, -3.0f, -1.0f); light_direction_ = glm::normalize(light_direction_); } else { SetShader(); } } void Mesh::SetBoundingBox(){ // Traverse all the vertices to define an axis-aligned // bounding box for this mesh, needs to be called after SetVertices(). if(vertices_.size()==0){ LOGE("Please set up vertices first!"); return; } is_bounding_box_on_ = true; bounding_box_ = new BoundingBox(vertices_); } void Mesh::SetLightDirection(const glm::vec3& light_direction) { light_direction_ = light_direction; } bool Mesh::IsIntersecting(const Segment& segment) { // If there is no bounding box defined based on all vertices, // we can not calculate intersection. if (!is_bounding_box_on_) { LOGE("Mesh::IsIntersecting, bounding box is not available."); return false; } return bounding_box_->IsIntersecting(segment, GetRotation(), GetTransformationMatrix()); } void Mesh::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glUseProgram(shader_program_); glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mv_mat = view_mat * model_mat; glm::mat4 mvp_mat = projection_mat * mv_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); glUniform4f(uniform_color_, red_, green_, blue_, alpha_); if (is_lighting_on_) { glUniformMatrix4fv(uniform_mv_mat_, 1, GL_FALSE, glm::value_ptr(mv_mat)); glEnableVertexAttribArray(attrib_normals_); glVertexAttribPointer(attrib_normals_, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), &normals_[0]); glm::vec3 light_direction = glm::mat3(view_mat) * light_direction_; glUniform3fv(uniform_light_vec_, 1, glm::value_ptr(light_direction)); } glEnableVertexAttribArray(attrib_vertices_); if (!indices_.empty()) { glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), vertices_.data()); glDrawElements(render_mode_, indices_.size(), GL_UNSIGNED_SHORT, indices_.data()); } else { glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), &vertices_[0]); glDrawArrays(render_mode_, 0, vertices_.size() / 3); } glDisableVertexAttribArray(attrib_vertices_); if (is_lighting_on_) { glDisableVertexAttribArray(attrib_normals_); } glUseProgram(0); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/obj_loader.cpp000066400000000000000000000131311503504370700225210ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "tango-gl/obj_loader.h" namespace tango_gl { bool obj_loader::LoadOBJData(const char* path, std::vector& vertices, std::vector& indices) { FILE *file = fopen(path, "r"); if (file == NULL) { LOGE("Failed to open file: %s", path); return false; } while (1) { char lineHeader[128]; int res = fscanf(file, "%s", lineHeader); if (res == EOF) break; if (strcmp(lineHeader, "v") == 0) { GLfloat vertex[3]; int matches = fscanf(file, "%f %f %f\n", &vertex[0], &vertex[1], &vertex[2]); if (matches != 3) { LOGE("Format of 'v float float float' required for each vertice line"); return false; } vertices.push_back(vertex[0]); vertices.push_back(vertex[1]); vertices.push_back(vertex[2]); } else if (strcmp(lineHeader, "f") == 0) { GLushort vertexIndex[3]; int matches = fscanf(file, "%hu %hu %hu\n", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]); if (matches != 3) { LOGE("Format of 'f int int int' required for each face line"); return false; } indices.push_back(vertexIndex[0] - 1); indices.push_back(vertexIndex[1] - 1); indices.push_back(vertexIndex[2] - 1); } else { char comments_buffer[1000]; fgets(comments_buffer, 1000, file); } } fclose(file); return true; } bool obj_loader::LoadOBJData(const char* path, std::vector& vertices, std::vector& normals) { std::vector vertexIndices, normalIndices; std::vector temp_vertices, temp_normals; FILE* file = fopen(path, "r"); if (file == NULL) { LOGE("Failed to open file: %s", path); return false; } while (1) { char lineHeader[128]; int res = fscanf(file, "%s", lineHeader); if (res == EOF) break; if (strcmp(lineHeader, "v") == 0) { GLfloat vertex[3]; int matches = fscanf(file, "%f %f %f\n", &vertex[0], &vertex[1], &vertex[2]); if (matches != 3) { LOGE("Format of 'v float float float' required for each vertice line"); return false; } temp_vertices.push_back(vertex[0]); temp_vertices.push_back(vertex[1]); temp_vertices.push_back(vertex[2]); } else if (strcmp(lineHeader, "vn") == 0) { GLfloat normal[3]; int matches = fscanf(file, "%f %f %f\n", &normal[0], &normal[1], &normal[2]); if (matches != 3) { LOGE("Format of 'vn float float float' required for each normal line"); return false; } temp_normals.push_back(normal[0]); temp_normals.push_back(normal[1]); temp_normals.push_back(normal[2]); } else if (strcmp(lineHeader, "f") == 0) { GLushort vertexIndex[4]; GLushort normalIndex[4]; int matches = fscanf(file, "%hu//%hu %hu//%hu %hu//%hu %hu//%hu\n", &vertexIndex[0], &normalIndex[0], &vertexIndex[1], &normalIndex[1], &vertexIndex[2], &normalIndex[2], &vertexIndex[3], &normalIndex[3]); // .obj file is 1-indexed, so subtract 1 from all indices. if (matches == 6) { // If triangles provided. vertexIndices.push_back(vertexIndex[0] - 1); vertexIndices.push_back(vertexIndex[1] - 1); vertexIndices.push_back(vertexIndex[2] - 1); normalIndices.push_back(normalIndex[0] - 1); normalIndices.push_back(normalIndex[1] - 1); normalIndices.push_back(normalIndex[2] - 1); } else if(matches == 8) { // If quads provided. vertexIndices.push_back(vertexIndex[0] - 1); vertexIndices.push_back(vertexIndex[1] - 1); vertexIndices.push_back(vertexIndex[2] - 1); vertexIndices.push_back(vertexIndex[0] - 1); vertexIndices.push_back(vertexIndex[2] - 1); vertexIndices.push_back(vertexIndex[3] - 1); normalIndices.push_back(normalIndex[0] - 1); normalIndices.push_back(normalIndex[1] - 1); normalIndices.push_back(normalIndex[2] - 1); normalIndices.push_back(normalIndex[0] - 1); normalIndices.push_back(normalIndex[2] - 1); normalIndices.push_back(normalIndex[3] - 1); } else { LOGE("Format of 'f int//int int//int int//int' required for each face"); return false; } } else { char comments_buffer[1000]; fgets(comments_buffer, 1000, file); } } for (unsigned int i = 0; i < vertexIndices.size(); i++) { unsigned int vertexIndex = vertexIndices[i]; unsigned int normalIndex = normalIndices[i]; vertices.push_back(temp_vertices[vertexIndex * 3]); vertices.push_back(temp_vertices[vertexIndex * 3 + 1]); vertices.push_back(temp_vertices[vertexIndex * 3 + 2]); normals.push_back(temp_normals[normalIndex * 3]); normals.push_back(temp_normals[normalIndex * 3 + 1]); normals.push_back(temp_normals[normalIndex * 3 + 2]); } fclose(file); return true; } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/quad.cpp000066400000000000000000000060611503504370700213570ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/quad.h" #include "tango-gl/util.h" namespace tango_gl { static const char kVertexShader[] = "attribute vec4 vertex;\n" "attribute vec2 inputTextureCoordinate;\n" "varying vec2 textureCoordinate;\n" "uniform mat4 mvp;\n" "void main() {\n" " gl_Position = mvp*vertex;\n" " textureCoordinate = inputTextureCoordinate.xy;\n" "}\n"; static const char kFragmentShader[] = "varying vec2 textureCoordinate;\n" "uniform sampler2D inputTexture;\n" "void main() {\n" " gl_FragColor = texture2D(inputTexture, textureCoordinate);\n" "}\n"; static const float vertices[] = {-0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f}; static const GLfloat texture_coords[] = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; Quad::Quad() { shader_program_ = util::CreateProgram(kVertexShader, kFragmentShader); if (!shader_program_) { LOGE("Could not create program."); } uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp"); attrib_vertices_ = glGetAttribLocation(shader_program_, "vertex"); texture_coords_ = glGetAttribLocation(shader_program_, "inputTextureCoordinate"); texture_handle = glGetUniformLocation(shader_program_, "inputTexture"); glGenBuffers(1, &vertex_buffer_); } Quad::~Quad() { glDeleteShader(shader_program_); } void Quad::SetTextureId(GLuint texture_id) { texture_id_ = texture_id; } void Quad::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(shader_program_); glBindTexture(GL_TEXTURE_2D, texture_id_); glUniform1i(texture_handle, 0); // Calculate MVP matrix and pass it to shader. glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mvp_mat = projection_mat * view_mat * model_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); // Vertice binding glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 2, GL_FLOAT, GL_FALSE, 0, vertices); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(texture_coords_); glVertexAttribPointer(texture_coords_, 2, GL_FLOAT, GL_FALSE, 0, texture_coords); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glUseProgram(0); glBindTexture(GL_TEXTURE_2D, 0); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/segment_drawable.cpp000066400000000000000000000020011503504370700237160ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/segment_drawable.h" namespace tango_gl { SegmentDrawable::SegmentDrawable() : tango_gl::Line(5.0f, GL_LINES) { SetShader(); vec_vertices_.push_back(glm::vec3(0, 0, 0)); vec_vertices_.push_back(glm::vec3(1.f, 1.f, 1.f)); } void SegmentDrawable::UpdateSegment(const Segment& segment) { vec_vertices_[0] = segment.start; vec_vertices_[1] = segment.end; } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/shaders.cpp000066400000000000000000000057451503504370700220660ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/shaders.h" namespace tango_gl { namespace shaders { std::string GetBasicVertexShader() { return "precision mediump float;\n" "precision mediump int;\n" "attribute vec4 vertex;\n" "uniform mat4 mvp;\n" "uniform vec4 color;\n" "varying vec4 v_color;\n" "void main() {\n" " gl_Position = mvp*vertex;\n" " v_color = color;\n" "}\n"; } std::string GetBasicFragmentShader() { return "precision mediump float;\n" "varying vec4 v_color;\n" "void main() {\n" " gl_FragColor = v_color;\n" "}\n"; } std::string GetColorVertexShader() { return "precision mediump float;\n" "precision mediump int;\n" "attribute vec4 vertex;\n" "attribute vec4 color;\n" "uniform mat4 mvp;\n" "varying vec4 v_color;\n" "void main() {\n" " gl_Position = mvp*vertex;\n" " v_color = color;\n" "}\n"; } std::string GetVideoOverlayVertexShader() { return "precision highp float;\n" "precision highp int;\n" "attribute vec4 vertex;\n" "attribute vec2 textureCoords;\n" "varying vec2 f_textureCoords;\n" "uniform mat4 mvp;\n" "void main() {\n" " f_textureCoords = textureCoords;\n" " gl_Position = mvp * vertex;\n" "}\n"; } std::string GetVideoOverlayFragmentShader() { return "#extension GL_OES_EGL_image_external : require\n" "precision highp float;\n" "precision highp int;\n" "uniform samplerExternalOES texture;\n" "varying vec2 f_textureCoords;\n" "void main() {\n" " gl_FragColor = texture2D(texture, f_textureCoords);\n" "}\n"; } std::string GetShadedVertexShader() { return "attribute vec4 vertex;\n" "attribute vec3 normal;\n" "uniform mat4 mvp;\n" "uniform mat4 mv;\n" "uniform vec4 color;\n" "uniform vec3 lightVec;\n" "varying vec4 v_color;\n" "void main() {\n" " vec3 mvNormal = vec3(mv * vec4(normal, 0.0));\n" " float diffuse = max(-dot(mvNormal, lightVec), 0.0);\n" " v_color.a = color.a;\n" " v_color.xyz = color.xyz * diffuse + color.xyz * 0.3;\n" " gl_Position = mvp*vertex;\n" "}\n"; } } // namespace shaders } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/texture.cpp000066400000000000000000000061401503504370700221230ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/texture.h" #include "tango-gl/util.h" namespace tango_gl { static const int kMaxExponentiation = 12; static int RoundUpPowerOfTwo(int w) { int start = 2; for (int i = 0; i <= kMaxExponentiation; ++i) { if (w < start) { w = start; break; } else { start = start << 1; } } return w; } Texture::Texture(const char* file_path) { #ifdef __ANDROID__ if (!LoadFromPNG(file_path)) { LOGE("Texture initialing error"); } #endif } #ifdef __ANDROID__ bool Texture::LoadFromPNG(const char* file_path) { FILE* file = fopen(file_path, "rb"); if (file == NULL) { LOGE("fp not loaded: %s", strerror(errno)); return false; } fseek(file, 8, SEEK_CUR); png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, file); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width_, &height_, &bit_depth_, &color_type_, NULL, NULL, NULL); width_ = RoundUpPowerOfTwo(width_); height_ = RoundUpPowerOfTwo(height_); int row = width_ * (color_type_ == PNG_COLOR_TYPE_RGBA ? 4 : 3); byte_data_ = new char[row * height_]; png_bytep* row_pointers = new png_bytep[height_]; for (uint i = 0; i < height_; ++i) { row_pointers[i] = (png_bytep)(byte_data_ + i * row); } png_read_image(png_ptr, row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, 0); glGenTextures(1, &texture_id_); glBindTexture(GL_TEXTURE_2D, texture_id_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); util::CheckGlError("glBindTexture"); if (color_type_ == PNG_COLOR_TYPE_RGBA) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, byte_data_); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width_, height_, 0, GL_RGB, GL_UNSIGNED_BYTE, byte_data_); } util::CheckGlError("glTexImage2D"); glBindTexture(GL_TEXTURE_2D, 0); fclose(file); delete[] row_pointers; delete[] byte_data_; return true; } #endif GLuint Texture::GetTextureID() const { return texture_id_; } Texture::~Texture() { if (byte_data_ != NULL) { delete[] byte_data_; } byte_data_ = NULL; } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/trace.cpp000066400000000000000000000022411503504370700215170ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/trace.h" namespace tango_gl { static const int kMaxTraceLength = 5000; static const float kDistanceCheck = 0.05f; Trace::Trace() : Line(3.0f, GL_LINE_STRIP) { SetShader(); } void Trace::UpdateVertexArray(const glm::vec3& v) { if (vec_vertices_.size() == 0) { vec_vertices_.push_back(v); } else { float dist = glm::distance(vec_vertices_[vec_vertices_.size() - 1], v); if (dist >= kDistanceCheck) { vec_vertices_.push_back(v); } } } void Trace::ClearVertexArray() { vec_vertices_.clear(); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/transform.cpp000066400000000000000000000043341503504370700224410ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/transform.h" #include "tango-gl/util.h" #define nullptr 0 namespace tango_gl { Transform::Transform() : parent_(nullptr), position_(0.0f, 0.0f, 0.0f), rotation_(1.0f, 0.0f, 0.0f, 0.0f), scale_(1.0f, 1.0f, 1.0f) { } Transform::~Transform() { // Objects are not responsible for deleting their parents. } void Transform::SetPosition(const glm::vec3& position) { position_ = position; } glm::vec3 Transform::GetPosition() const { return position_; } void Transform::SetRotation(const glm::quat& rotation) { rotation_ = rotation; } glm::quat Transform::GetRotation() const { return rotation_; } void Transform::SetScale(const glm::vec3& scale) { scale_ = scale; } glm::vec3 Transform::GetScale() const { return scale_; } void Transform::Translate(const glm::vec3& translation) { position_ += translation; } void Transform::SetTransformationMatrix(const glm::mat4& transform_mat) { util::DecomposeMatrix(transform_mat, position_, rotation_, scale_); } glm::mat4 Transform::GetTransformationMatrix() const { glm::mat4 trans_mat = glm::scale(glm::mat4_cast(rotation_), scale_); trans_mat[3][0] = position_.x; trans_mat[3][1] = position_.y; trans_mat[3][2] = position_.z; glm::mat4 parent_mat = glm::mat4(1.0f); if (parent_ != NULL) { parent_mat = parent_->GetTransformationMatrix(); trans_mat = parent_mat * trans_mat; } return trans_mat; } void Transform::SetParent(Transform* transform) { parent_ = transform; } const Transform* Transform::GetParent() const { return parent_; } Transform* Transform::GetParent() { return parent_; } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/triangle.cpp000066400000000000000000000022101503504370700222220ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/triangle.h" namespace tango_gl { static const GLfloat const_vertices[] = {-0.15f, 0.0f, 0.0f, 0.15f, 0.0f, 0.0f, 0.0f, 0.0f, -0.2f}; static const GLushort const_indices[] = {0, 1, 2}; Triangle::Triangle() { SetShader(); std::vector vertices( const_vertices, const_vertices + sizeof(const_vertices) / sizeof(GLfloat)); std::vector indices( const_indices, const_indices + sizeof(const_indices) / sizeof(GLushort)); SetVertices(vertices, indices); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/util.cpp000066400000000000000000000162341503504370700214050ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/util.h" #include namespace tango_gl { namespace { int NormalizedColorCameraRotation(int camera_rotation) { int camera_n = 0; switch (camera_rotation) { case 90: camera_n = 1; break; case 180: camera_n = 2; break; case 270: camera_n = 3; break; default: camera_n = 0; break; } return camera_n; } } // annonymous namespace void util::CheckGlError(const char* operation) { for (GLint error = glGetError(); error; error = glGetError()) { LOGE("after %s() glError (0x%x)\n", operation, error); } } // Convenience function used in CreateProgram below. static GLuint LoadShader(GLenum shader_type, const char* shader_source) { GLuint shader = glCreateShader(shader_type); if (shader) { glShaderSource(shader, 1, &shader_source, NULL); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint info_len = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); if (info_len) { char* buf = (char*) malloc(info_len); if (buf) { glGetShaderInfoLog(shader, info_len, NULL, buf); LOGE("Could not compile shader %d:\n%s\n", shader_type, buf); free(buf); } glDeleteShader(shader); shader = 0; } } } return shader; } GLuint util::CreateProgram(const char* vertex_source, const char* fragment_source) { GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertex_source); if (!vertexShader) { return 0; } GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER, fragment_source); if (!fragment_shader) { return 0; } GLuint program = glCreateProgram(); if (program) { glAttachShader(program, vertexShader); CheckGlError("glAttachShader"); glAttachShader(program, fragment_shader); CheckGlError("glAttachShader"); glLinkProgram(program); GLint link_status = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { GLint buf_length = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &buf_length); if (buf_length) { char* buf = (char*) malloc(buf_length); if (buf) { glGetProgramInfoLog(program, buf_length, NULL, buf); LOGE("Could not link program:\n%s\n", buf); free(buf); } } glDeleteProgram(program); program = 0; } } CheckGlError("CreateProgram"); return program; } void util::DecomposeMatrix (const glm::mat4& transform_mat, glm::vec3& translation, glm::quat& rotation, glm::vec3& scale) { float scale_x = glm::length( glm::vec3( transform_mat[0][0], transform_mat[1][0], transform_mat[2][0] ) ); float scale_y = glm::length( glm::vec3( transform_mat[0][1], transform_mat[1][1], transform_mat[2][1] ) ); float scale_z = glm::length( glm::vec3( transform_mat[0][2], transform_mat[1][2], transform_mat[2][2] ) ); float determinant = glm::determinant( transform_mat ); if( determinant < 0.0 ) scale_x = -scale_x; translation.x = transform_mat[3][0]; translation.y = transform_mat[3][1]; translation.z = transform_mat[3][2]; float inverse_scale_x = 1.0 / scale_x; float inverse_scale_y = 1.0 / scale_y; float inverse_scale_z = 1.0 / scale_z; glm::mat4 transform_unscaled = transform_mat; transform_unscaled[0][0] *= inverse_scale_x; transform_unscaled[1][0] *= inverse_scale_x; transform_unscaled[2][0] *= inverse_scale_x; transform_unscaled[0][1] *= inverse_scale_y; transform_unscaled[1][1] *= inverse_scale_y; transform_unscaled[2][1] *= inverse_scale_y; transform_unscaled[0][2] *= inverse_scale_z; transform_unscaled[1][2] *= inverse_scale_z; transform_unscaled[2][2] *= inverse_scale_z; rotation = glm::quat_cast( transform_mat ); scale.x = scale_x; scale.y = scale_y; scale.z = scale_z; } glm::vec3 util::GetColumnFromMatrix(const glm::mat4& mat, const int col) { return glm::vec3(mat[col][0], mat[col][1], mat[col][2]); } glm::vec3 util::GetTranslationFromMatrix(const glm::mat4& mat) { return glm::vec3(mat[3][0], mat[3][1], mat[3][2]); } float util::Clamp(float value, float min, float max) { return value < min ? min : (value > max ? max : value); } // Print out a column major matrix. void util::PrintMatrix(const glm::mat4& matrix) { int i; for (i = 0; i < 4; i++) { LOGI("[ %f, %f, %f, %f ]", matrix[0][i], matrix[1][i], matrix[2][i], matrix[3][i]); } LOGI(" "); } void util::PrintVector(const glm::vec3& vector) { LOGI("[ %f, %f, %f ]", vector[0], vector[1], vector[2]); LOGI(" "); } void util::PrintQuaternion(const glm::quat& quat) { LOGI("[ %f, %f, %f, %f ]", quat[0], quat[1], quat[2], quat[3]); LOGI(" "); } glm::vec3 util::LerpVector(const glm::vec3& x, const glm::vec3& y, float a) { return x * (1.0f - a) + y * a; } float util::DistanceSquared(const glm::vec3& v1, const glm::vec3& v2) { glm::vec3 delta = v2 - v1; return glm::dot(delta, delta); } bool util::SegmentAABBIntersect(const glm::vec3& aabb_min, const glm::vec3& aabb_max, const glm::vec3& start, const glm::vec3& end) { float tmin, tmax, tymin, tymax, tzmin, tzmax; glm::vec3 direction = end - start; if (direction.x >= 0) { tmin = (aabb_min.x - start.x) / direction.x; tmax = (aabb_max.x - start.x) / direction.x; } else { tmin = (aabb_max.x - start.x) / direction.x; tmax = (aabb_min.x - start.x) / direction.x; } if (direction.y >= 0) { tymin = (aabb_min.y - start.y) / direction.y; tymax = (aabb_max.y - start.y) / direction.y; } else { tymin = (aabb_max.y - start.y) / direction.y; tymax = (aabb_min.y - start.y) / direction.y; } if ((tmin > tymax) || (tymin > tmax)) return false; if (tymin > tmin) tmin = tymin; if (tymax < tmax) tmax = tymax; if (direction.z >= 0) { tzmin = (aabb_min.z - start.z) / direction.z; tzmax = (aabb_max.z - start.z) / direction.z; } else { tzmin = (aabb_max.z - start.z) / direction.z; tzmax = (aabb_min.z - start.z) / direction.z; } if ((tmin > tzmax) || (tzmin > tmax)) return false; if (tzmin > tmin) tmin = tzmin; if (tzmax < tmax) tmax = tzmax; // Use the full length of the segment. return ((tmin < 1.0f) && (tmax > 0)); } glm::vec3 util::ApplyTransform(const glm::mat4& mat, const glm::vec3& vec) { return glm::vec3(mat * glm::vec4(vec, 1.0f)); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/tango-gl/video_overlay.cpp000066400000000000000000000105351503504370700232750ustar00rootroot00000000000000/* * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tango-gl/video_overlay.h" #include "tango-gl/shaders.h" namespace tango_gl { static const GLfloat kVertices[] = {-1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 1.0, -1.0, 0.0}; static const GLushort kIndices[] = {0, 1, 2, 2, 1, 3}; static const GLfloat kTextureCoords[] = {0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0}; VideoOverlay::VideoOverlay() { glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); shader_program_ = util::CreateProgram(shaders::GetVideoOverlayVertexShader().c_str(), shaders::GetVideoOverlayFragmentShader().c_str()); if (!shader_program_) { LOGE("Could not create program."); } glGenTextures(1, &texture_id_); glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); uniform_texture_ = glGetUniformLocation(shader_program_, "texture"); glGenBuffers(3, vertex_buffers_); // Allocate vertices buffer. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 4, kVertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // Allocate triangle indices buffer. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertex_buffers_[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 6, kIndices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Allocate texture coordinates buufer. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * 4, kTextureCoords, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // Assign the vertices attribute data. attrib_vertices_ = glGetAttribLocation(shader_program_, "vertex"); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_[0]); glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); // Assign the texture coordinates attribute data. attrib_texture_coords_ = glGetAttribLocation(shader_program_, "textureCoords"); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_[2]); glEnableVertexAttribArray(attrib_texture_coords_); glVertexAttribPointer(attrib_texture_coords_, 2, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp"); } void VideoOverlay::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const { glUseProgram(shader_program_); glUniform1i(uniform_texture_, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_); glm::mat4 model_mat = GetTransformationMatrix(); glm::mat4 mvp_mat = projection_mat * view_mat * model_mat; glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_mat)); // Bind vertices buffer. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_[0]); glEnableVertexAttribArray(attrib_vertices_); glVertexAttribPointer(attrib_vertices_, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); // Bind texture coordinates buffer. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers_[2]); glEnableVertexAttribArray(attrib_texture_coords_); glVertexAttribPointer(attrib_texture_coords_, 2, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); // Bind element array buffer. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertex_buffers_[1]); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); util::CheckGlError("glDrawElements"); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glUseProgram(0); util::CheckGlError("glUseProgram()"); } } // namespace tango_gl rtabmap-0.22.1/app/android/jni/text_drawable.cpp000066400000000000000000000274511503504370700215500ustar00rootroot00000000000000/* Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef __ANDROID__ #include #endif #include #include #include #include "util.h" #include "text_drawable.h" #include "text_atlas_png.h" #include const std::string kTextVertexShader = "uniform mat4 uMVPMatrix;" "attribute vec4 vPosition;" "attribute vec2 a_texCoord;" "varying vec2 v_texCoord;" "void main() {" " gl_Position = uMVPMatrix * vPosition;" " v_texCoord = a_texCoord;" "}"; const std::string kTextFragmentShader = "precision mediump float;" "uniform vec3 uColor;" "varying vec2 v_texCoord;" "uniform sampler2D s_texture;" "void main() {" " gl_FragColor = texture2D( s_texture, v_texCoord );" " gl_FragColor.rgb = uColor;" "}"; const int RI_TEXT_TEXTURE_SIZE = 512; // 512 const float RI_TEXT_HEIGHT_BASE = 32.0f; const char RI_TEXT_START = ' '; const char RI_TEXT_STOP = '~'+1; GLuint TextDrawable::textProgram_ = 0; GLuint TextDrawable::textTextureId_ = 0; float TextDrawable::textUVWidth_ = 0; float TextDrawable::textUVHeight_ = 0; float TextDrawable::textHeight_ = 0; std::vector TextDrawable::textCharacterWidths_; void TextDrawable::createShaderProgram() { releaseShaderProgram(); // hard-coded with text_atlas.png resource textCharacterWidths_.resize(95); textCharacterWidths_[0]=8; textCharacterWidths_[1]=9.000000; textCharacterWidths_[2]=10.000000; textCharacterWidths_[3]=19.000000; textCharacterWidths_[4]=18.000000; textCharacterWidths_[5]=24.000000; textCharacterWidths_[6]=21.000000; textCharacterWidths_[7]=5.000000; textCharacterWidths_[8]=11.000000; textCharacterWidths_[9]=11.000000; textCharacterWidths_[10]=15.000000; textCharacterWidths_[11]=17.000000; textCharacterWidths_[12]=8.000000; textCharacterWidths_[13]=12.000000; textCharacterWidths_[14]=9.000000; textCharacterWidths_[15]=12.000000; textCharacterWidths_[16]=18.000000; textCharacterWidths_[17]=18.000000; textCharacterWidths_[18]=18.000000; textCharacterWidths_[19]=18.000000; textCharacterWidths_[20]=18.000000; textCharacterWidths_[21]=18.000000; textCharacterWidths_[22]=18.000000; textCharacterWidths_[23]=18.000000; textCharacterWidths_[24]=18.000000; textCharacterWidths_[25]=18.000000; textCharacterWidths_[26]=9.000000; textCharacterWidths_[27]=8.000000; textCharacterWidths_[28]=16.000000; textCharacterWidths_[29]=18.000000; textCharacterWidths_[30]=17.000000; textCharacterWidths_[31]=16.000000; textCharacterWidths_[32]=29.000000; textCharacterWidths_[33]=22.000000; textCharacterWidths_[34]=20.000000; textCharacterWidths_[35]=21.000000; textCharacterWidths_[36]=21.000000; textCharacterWidths_[37]=18.000000; textCharacterWidths_[38]=18.000000; textCharacterWidths_[39]=22.000000; textCharacterWidths_[40]=23.000000; textCharacterWidths_[41]=9.000000; textCharacterWidths_[42]=18.000000; textCharacterWidths_[43]=20.000000; textCharacterWidths_[44]=17.000000; textCharacterWidths_[45]=28.000000; textCharacterWidths_[46]=23.000000; textCharacterWidths_[47]=22.000000; textCharacterWidths_[48]=21.000000; textCharacterWidths_[49]=22.000000; textCharacterWidths_[50]=20.000000; textCharacterWidths_[51]=20.000000; textCharacterWidths_[52]=20.000000; textCharacterWidths_[53]=21.000000; textCharacterWidths_[54]=21.000000; textCharacterWidths_[55]=28.000000; textCharacterWidths_[56]=20.000000; textCharacterWidths_[57]=20.000000; textCharacterWidths_[58]=19.000000; textCharacterWidths_[59]=9.000000; textCharacterWidths_[60]=14.000000; textCharacterWidths_[61]=9.000000; textCharacterWidths_[62]=14.000000; textCharacterWidths_[63]=14.000000; textCharacterWidths_[64]=11.000000; textCharacterWidths_[65]=17.000000; textCharacterWidths_[66]=18.000000; textCharacterWidths_[67]=17.000000; textCharacterWidths_[68]=18.000000; textCharacterWidths_[69]=17.000000; textCharacterWidths_[70]=11.000000; textCharacterWidths_[71]=18.000000; textCharacterWidths_[72]=18.000000; textCharacterWidths_[73]=8.000000; textCharacterWidths_[74]=8.000000; textCharacterWidths_[75]=17.000000; textCharacterWidths_[76]=8.000000; textCharacterWidths_[77]=28.000000; textCharacterWidths_[78]=18.000000; textCharacterWidths_[79]=18.000000; textCharacterWidths_[80]=18.000000; textCharacterWidths_[81]=18.000000; textCharacterWidths_[82]=12.000000; textCharacterWidths_[83]=16.000000; textCharacterWidths_[84]=11.000000; textCharacterWidths_[85]=18.000000; textCharacterWidths_[86]=16.000000; textCharacterWidths_[87]=24.000000; textCharacterWidths_[88]=16.000000; textCharacterWidths_[89]=16.000000; textCharacterWidths_[90]=16.000000; textCharacterWidths_[91]=11.000000; textCharacterWidths_[92]=8.000000; textCharacterWidths_[93]=11.000000; textCharacterWidths_[94]=21.000000; textUVWidth_=0.062500; textUVHeight_=0.082031; textHeight_=42.000000; textProgram_ = tango_gl::util::CreateProgram(kTextVertexShader.c_str(), kTextFragmentShader.c_str()); UASSERT(textProgram_ != 0); glGenTextures(1, &textTextureId_); UASSERT(textTextureId_); // gen texture from image glBindTexture(GL_TEXTURE_2D, textTextureId_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); std::vector data = uHex2Bytes(rtabmap::TEXT_ATLAS_PNG); cv::Mat rgbImage = cv::imdecode(data, cv::IMREAD_UNCHANGED); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rgbImage.cols, rgbImage.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbImage.data); GLint error = glGetError(); UASSERT(error == GL_NO_ERROR); } void TextDrawable::releaseShaderProgram() { if(textProgram_) { glDeleteShader(textProgram_); textProgram_ = 0; } } TextDrawable::TextDrawable( const std::string & text, const rtabmap::Transform & pose, float textSize, // meters const tango_gl::Color & color) : poseGl_(glmFromTransform(pose)), color_(color) { if(textProgram_ > 0) { vertexBuffer_ = std::vector(text.size() * 12); textureBuffer_ = std::vector(text.size() * 8); drawListBuffer_ = std::vector(text.size() * 6); int index_vecs = 0; int index_indices = 0; int index_uvs = 0; float x=0.0f; float y=0.0f; float uniformscale = textSize/textHeight_; for(unsigned int i=0; i=textCharacterWidths_.size()) { // unknown character, we will add a space for it to be save. indx = 0; } int colCount = RI_TEXT_TEXTURE_SIZE/(int)RI_TEXT_HEIGHT_BASE; // Calculate the uv parts int row = indx / colCount; int col = indx % colCount; float v = row * textUVHeight_; float v2 = v + textUVHeight_; float u = col * textUVWidth_; float u2 = u + textCharacterWidths_[indx]/(float)RI_TEXT_TEXTURE_SIZE; // Creating the triangle information std::vector vec(12); std::vector uv(8); vec[0] = x; vec[1] = y + (textHeight_ * uniformscale); vec[2] = 0; vec[3] = x; vec[4] = y; vec[5] = 0; vec[6] = x + (textCharacterWidths_[indx] * uniformscale); vec[7] = y; vec[8] = 0; vec[9] = x + (textCharacterWidths_[indx] * uniformscale); vec[10] = y + (textHeight_ * uniformscale); vec[11] = 0; // 0.001f = texture bleeding hack/fix uv[0] = u+0.001f; uv[1] = v+0.001f; uv[2] = u+0.001f; uv[3] = v2-0.001f; uv[4] = u2-0.001f; uv[5] = v2-0.001f; uv[6] = u2-0.001f; uv[7] = v+0.001f; unsigned short inds[6] = {0, 1, 2, 0, 2, 3}; // We need a base value because the object has indices related to // that object and not to this collection so basicly we need to // translate the indices to align with the vertexlocation in ou // vecs array of vectors. short base = (short) (index_vecs / 3); // We should add the vec, translating the indices to our saved vector for(int i=0;iGetParent()->GetRotation()); glUniformMatrix4fv(mtrxhandle, 1, GL_FALSE, glm::value_ptr(mvp_mat)); // get handle to color value int colorhandle = glGetUniformLocation(textProgram_, "uColor"); glUniform3f(colorhandle, color_.r, color_.g, color_.b); int mSamplerLoc = glGetUniformLocation (textProgram_, "s_texture" ); // Texture activate unit 0 glActiveTexture(GL_TEXTURE0); // Bind the texture to this unit. glBindTexture(GL_TEXTURE_2D, textTextureId_); // Set the sampler texture unit to our selected id glUniform1i ( mSamplerLoc, 0); // Draw the triangle glDrawElements(GL_TRIANGLES, drawListBuffer_.size(), GL_UNSIGNED_SHORT, &drawListBuffer_[0]); // Disable vertex array glDisableVertexAttribArray(mPositionHandle); glDisableVertexAttribArray(mTexCoordLoc); } rtabmap-0.22.1/app/android/jni/text_drawable.h000066400000000000000000000051671503504370700212150ustar00rootroot00000000000000/* Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TANGO_TEXT_DRAWABLE_H_ #define TANGO_TEXT_DRAWABLE_H_ #include #include #include // PointCloudDrawable is responsible for the point cloud rendering. class TextDrawable { private: static GLuint textProgram_; static GLuint textTextureId_; static float textUVWidth_; static float textUVHeight_; static float textHeight_; static std::vector textCharacterWidths_; public: static void createShaderProgram(); static void releaseShaderProgram(); public: TextDrawable( const std::string & text, const rtabmap::Transform & pose, float textSize = 0.1f, // meters const tango_gl::Color & color = tango_gl::Color(1,0,0)); virtual ~TextDrawable() {} void Render( const glm::mat4 & projectionMatrix, const glm::mat4 & viewMatrix, const glm::mat4 & viewMatrixRotInv) const; private: std::vector vertexBuffer_; std::vector textureBuffer_; std::vector drawListBuffer_; glm::mat4 poseGl_; tango_gl::Color color_; }; #endif // TANGO_POINT_CLOUD_POINT_CLOUD_DRAWABLE_H_ rtabmap-0.22.1/app/android/jni/third-party/000077500000000000000000000000001503504370700204555ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/third-party/include/000077500000000000000000000000001503504370700221005ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/third-party/include/glm/000077500000000000000000000000001503504370700226575ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/third-party/include/glm/common.hpp000066400000000000000000000027621503504370700246670ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/common.hpp /// @date 2013-12-24 / 2013-12-24 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once #include "detail/func_common.hpp" rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/000077500000000000000000000000001503504370700241215ustar00rootroot00000000000000rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_features.hpp000066400000000000000000000312171503504370700266130ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/_features.hpp /// @date 2013-02-20 / 2013-02-20 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once // #define GLM_CXX98_EXCEPTIONS // #define GLM_CXX98_RTTI // #define GLM_CXX11_RVALUE_REFERENCES // Rvalue references - GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html // GLM_CXX11_TRAILING_RETURN // Rvalue references for *this - GCC not supported // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm // GLM_CXX11_NONSTATIC_MEMBER_INIT // Initialization of class objects by rvalues - GCC any // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1610.html // GLM_CXX11_NONSTATIC_MEMBER_INIT // Non-static data member initializers - GCC 4.7 // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2756.htm // #define GLM_CXX11_VARIADIC_TEMPLATE // Variadic templates - GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf // // Extending variadic template template parameters - GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2555.pdf // #define GLM_CXX11_GENERALIZED_INITIALIZERS // Initializer lists - GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm // #define GLM_CXX11_STATIC_ASSERT // Static assertions - GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.html // #define GLM_CXX11_AUTO_TYPE // auto-typed variables - GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1984.pdf // #define GLM_CXX11_AUTO_TYPE // Multi-declarator auto - GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1737.pdf // #define GLM_CXX11_AUTO_TYPE // Removal of auto as a storage-class specifier - GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2546.htm // #define GLM_CXX11_AUTO_TYPE // New function declarator syntax - GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2541.htm // #define GLM_CXX11_LAMBDAS // New wording for C++0x lambdas - GCC 4.5 // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2927.pdf // #define GLM_CXX11_DECLTYPE // Declared type of an expression - GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2343.pdf // // Right angle brackets - GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html // // Default template arguments for function templates DR226 GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#226 // // Solving the SFINAE problem for expressions DR339 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html // #define GLM_CXX11_ALIAS_TEMPLATE // Template aliases N2258 GCC 4.7 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf // // Extern templates N1987 Yes // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm // #define GLM_CXX11_NULLPTR // Null pointer constant N2431 GCC 4.6 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf // #define GLM_CXX11_STRONG_ENUMS // Strongly-typed enums N2347 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf // // Forward declarations for enums N2764 GCC 4.6 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf // // Generalized attributes N2761 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf // // Generalized constant expressions N2235 GCC 4.6 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf // // Alignment support N2341 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf // #define GLM_CXX11_DELEGATING_CONSTRUCTORS // Delegating constructors N1986 GCC 4.7 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf // // Inheriting constructors N2540 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2540.htm // #define GLM_CXX11_EXPLICIT_CONVERSIONS // Explicit conversion operators N2437 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf // // New character types N2249 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2249.html // // Unicode string literals N2442 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm // // Raw string literals N2442 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm // // Universal character name literals N2170 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2170.html // #define GLM_CXX11_USER_LITERALS // User-defined literals N2765 GCC 4.7 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2765.pdf // // Standard Layout Types N2342 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm // #define GLM_CXX11_DEFAULTED_FUNCTIONS // #define GLM_CXX11_DELETED_FUNCTIONS // Defaulted and deleted functions N2346 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm // // Extended friend declarations N1791 GCC 4.7 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf // // Extending sizeof N2253 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html // #define GLM_CXX11_INLINE_NAMESPACES // Inline namespaces N2535 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2535.htm // #define GLM_CXX11_UNRESTRICTED_UNIONS // Unrestricted unions N2544 GCC 4.6 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf // #define GLM_CXX11_LOCAL_TYPE_TEMPLATE_ARGS // Local and unnamed types as template arguments N2657 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm // #define GLM_CXX11_RANGE_FOR // Range-based for N2930 GCC 4.6 // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2930.html // #define GLM_CXX11_OVERRIDE_CONTROL // Explicit virtual overrides N2928 N3206 N3272 GCC 4.7 // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm // // Minimal support for garbage collection and reachability-based leak detection N2670 No // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2670.htm // #define GLM_CXX11_NOEXCEPT // Allowing move constructors to throw [noexcept] N3050 GCC 4.6 (core language only) // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html // // Defining move special member functions N3053 GCC 4.6 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3053.html // // Sequence points N2239 Yes // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2239.html // // Atomic operations N2427 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2239.html // // Strong Compare and Exchange N2748 GCC 4.5 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html // // Bidirectional Fences N2752 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2752.htm // // Memory model N2429 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm // // Data-dependency ordering: atomics and memory model N2664 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2664.htm // // Propagating exceptions N2179 GCC 4.4 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html // // Abandoning a process and at_quick_exit N2440 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2440.htm // // Allow atomics use in signal handlers N2547 Yes // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2547.htm // // Thread-local storage N2659 GCC 4.8 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2659.htm // // Dynamic initialization and destruction with concurrency N2660 GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm // // __func__ predefined identifier N2340 GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2340.htm // // C99 preprocessor N1653 GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm // // long long N1811 GCC 4.3 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1811.pdf // // Extended integral types N1988 Yes // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1988.pdf #if(GLM_COMPILER & GLM_COMPILER_GCC) # if(GLM_COMPILER >= GLM_COMPILER_GCC43) # define GLM_CXX11_STATIC_ASSERT # endif #elif(GLM_COMPILER & GLM_COMPILER_CLANG) # if(__has_feature(cxx_exceptions)) # define GLM_CXX98_EXCEPTIONS # endif # if(__has_feature(cxx_rtti)) # define GLM_CXX98_RTTI # endif # if(__has_feature(cxx_access_control_sfinae)) # define GLM_CXX11_ACCESS_CONTROL_SFINAE # endif # if(__has_feature(cxx_alias_templates)) # define GLM_CXX11_ALIAS_TEMPLATE # endif # if(__has_feature(cxx_alignas)) # define GLM_CXX11_ALIGNAS # endif # if(__has_feature(cxx_attributes)) # define GLM_CXX11_ATTRIBUTES # endif # if(__has_feature(cxx_constexpr)) # define GLM_CXX11_CONSTEXPR # endif # if(__has_feature(cxx_decltype)) # define GLM_CXX11_DECLTYPE # endif # if(__has_feature(cxx_default_function_template_args)) # define GLM_CXX11_DEFAULT_FUNCTION_TEMPLATE_ARGS # endif # if(__has_feature(cxx_defaulted_functions)) # define GLM_CXX11_DEFAULTED_FUNCTIONS # endif # if(__has_feature(cxx_delegating_constructors)) # define GLM_CXX11_DELEGATING_CONSTRUCTORS # endif # if(__has_feature(cxx_deleted_functions)) # define GLM_CXX11_DELETED_FUNCTIONS # endif # if(__has_feature(cxx_explicit_conversions)) # define GLM_CXX11_EXPLICIT_CONVERSIONS # endif # if(__has_feature(cxx_generalized_initializers)) # define GLM_CXX11_GENERALIZED_INITIALIZERS # endif # if(__has_feature(cxx_implicit_moves)) # define GLM_CXX11_IMPLICIT_MOVES # endif # if(__has_feature(cxx_inheriting_constructors)) # define GLM_CXX11_INHERITING_CONSTRUCTORS # endif # if(__has_feature(cxx_inline_namespaces)) # define GLM_CXX11_INLINE_NAMESPACES # endif # if(__has_feature(cxx_lambdas)) # define GLM_CXX11_LAMBDAS # endif # if(__has_feature(cxx_local_type_template_args)) # define GLM_CXX11_LOCAL_TYPE_TEMPLATE_ARGS # endif # if(__has_feature(cxx_noexcept)) # define GLM_CXX11_NOEXCEPT # endif # if(__has_feature(cxx_nonstatic_member_init)) # define GLM_CXX11_NONSTATIC_MEMBER_INIT # endif # if(__has_feature(cxx_nullptr)) # define GLM_CXX11_NULLPTR # endif # if(__has_feature(cxx_override_control)) # define GLM_CXX11_OVERRIDE_CONTROL # endif # if(__has_feature(cxx_reference_qualified_functions)) # define GLM_CXX11_REFERENCE_QUALIFIED_FUNCTIONS # endif # if(__has_feature(cxx_range_for)) # define GLM_CXX11_RANGE_FOR # endif # if(__has_feature(cxx_raw_string_literals)) # define GLM_CXX11_RAW_STRING_LITERALS # endif # if(__has_feature(cxx_rvalue_references)) # define GLM_CXX11_RVALUE_REFERENCES # endif # if(__has_feature(cxx_static_assert)) # define GLM_CXX11_STATIC_ASSERT # endif # if(__has_feature(cxx_auto_type)) # define GLM_CXX11_AUTO_TYPE # endif # if(__has_feature(cxx_strong_enums)) # define GLM_CXX11_STRONG_ENUMS # endif # if(__has_feature(cxx_trailing_return)) # define GLM_CXX11_TRAILING_RETURN # endif # if(__has_feature(cxx_unicode_literals)) # define GLM_CXX11_UNICODE_LITERALS # endif # if(__has_feature(cxx_unrestricted_unions)) # define GLM_CXX11_UNRESTRICTED_UNIONS # endif # if(__has_feature(cxx_user_literals)) # define GLM_CXX11_USER_LITERALS # endif # if(__has_feature(cxx_variadic_templates)) # define GLM_CXX11_VARIADIC_TEMPLATES # endif #endif//(GLM_COMPILER & GLM_COMPILER_CLANG) rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_fixes.hpp000066400000000000000000000035031503504370700261100ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/_fixes.hpp /// @date 2011-02-21 / 2011-11-22 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #include //! Workaround for compatibility with other libraries #ifdef max #undef max #endif //! Workaround for compatibility with other libraries #ifdef min #undef min #endif //! Workaround for Android #ifdef isnan #undef isnan #endif //! Workaround for Android #ifdef isinf #undef isinf #endif //! Workaround for Chrone Native Client #ifdef log2 #undef log2 #endif rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_literals.hpp000066400000000000000000000034511503504370700266130ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/_literals.hpp /// @date 2013-05-06 / 2013-05-06 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once namespace glm { #define GLM_CXX11_USER_LITERALS #ifdef GLM_CXX11_USER_LITERALS /* GLM_FUNC_QUALIFIER detail::half operator "" _h(long double const s) { return detail::half(s); } GLM_FUNC_QUALIFIER float operator "" _f(long double const s) { return static_cast(s); } */ #endif//GLM_CXX11_USER_LITERALS }//namespace glm rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_noise.hpp000066400000000000000000000102031503504370700261020ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/detail/_noise.hpp /// @date 2013-12-24 / 2013-12-24 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once namespace glm{ namespace detail { template GLM_FUNC_QUALIFIER T mod289(T const & x) { return x - floor(x * static_cast(1.0) / static_cast(289.0)) * static_cast(289.0); } template GLM_FUNC_QUALIFIER T permute(T const & x) { return mod289(((x * static_cast(34)) + static_cast(1)) * x); } template GLM_FUNC_QUALIFIER tvec2 permute(tvec2 const & x) { return mod289(((x * static_cast(34)) + static_cast(1)) * x); } template GLM_FUNC_QUALIFIER tvec3 permute(tvec3 const & x) { return mod289(((x * static_cast(34)) + static_cast(1)) * x); } template GLM_FUNC_QUALIFIER tvec4 permute(tvec4 const & x) { return mod289(((x * static_cast(34)) + static_cast(1)) * x); } /* template class vecType> GLM_FUNC_QUALIFIER vecType permute(vecType const & x) { return mod289(((x * T(34)) + T(1)) * x); } */ template GLM_FUNC_QUALIFIER T taylorInvSqrt(T const & r) { return T(1.79284291400159) - T(0.85373472095314) * r; } template GLM_FUNC_QUALIFIER detail::tvec2 taylorInvSqrt(detail::tvec2 const & r) { return T(1.79284291400159) - T(0.85373472095314) * r; } template GLM_FUNC_QUALIFIER detail::tvec3 taylorInvSqrt(detail::tvec3 const & r) { return T(1.79284291400159) - T(0.85373472095314) * r; } template GLM_FUNC_QUALIFIER detail::tvec4 taylorInvSqrt(detail::tvec4 const & r) { return T(1.79284291400159) - T(0.85373472095314) * r; } /* template class vecType> GLM_FUNC_QUALIFIER vecType taylorInvSqrt(vecType const & r) { return T(1.79284291400159) - T(0.85373472095314) * r; } */ template GLM_FUNC_QUALIFIER detail::tvec2 fade(detail::tvec2 const & t) { return (t * t * t) * (t * (t * T(6) - T(15)) + T(10)); } template GLM_FUNC_QUALIFIER detail::tvec3 fade(detail::tvec3 const & t) { return (t * t * t) * (t * (t * T(6) - T(15)) + T(10)); } template GLM_FUNC_QUALIFIER detail::tvec4 fade(detail::tvec4 const & t) { return (t * t * t) * (t * (t * T(6) - T(15)) + T(10)); } /* template class vecType> GLM_FUNC_QUALIFIER vecType fade(vecType const & t) { return (t * t * t) * (t * (t * T(6) - T(15)) + T(10)); } */ }//namespace detail }//namespace glm rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_swizzle.hpp000066400000000000000000001437641503504370700265170ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/_swizzle.hpp /// @date 2006-04-20 / 2011-02-16 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once namespace glm{ namespace detail { // Internal class for implementing swizzle operators template struct _swizzle_base0 { typedef T value_type; protected: GLM_FUNC_QUALIFIER value_type& elem (size_t i) { return (reinterpret_cast(_buffer))[i]; } GLM_FUNC_QUALIFIER const value_type& elem (size_t i) const { return (reinterpret_cast(_buffer))[i]; } // Use an opaque buffer to *ensure* the compiler doesn't call a constructor. // The size 1 buffer is assumed to aligned to the actual members so that the // elem() char _buffer[1]; }; template struct _swizzle_base1 : public _swizzle_base0 { }; template struct _swizzle_base1 : public _swizzle_base0 { GLM_FUNC_QUALIFIER V operator ()() const { return V(this->elem(E0), this->elem(E1)); } }; template struct _swizzle_base1 : public _swizzle_base0 { GLM_FUNC_QUALIFIER V operator ()() const { return V(this->elem(E0), this->elem(E1), this->elem(E2)); } }; template struct _swizzle_base1 : public _swizzle_base0 { GLM_FUNC_QUALIFIER V operator ()() const { return V(this->elem(E0), this->elem(E1), this->elem(E2), this->elem(E3)); } }; // Internal class for implementing swizzle operators /* Template parameters: ValueType = type of scalar values (e.g. float, double) VecType = class the swizzle is applies to (e.g. tvec3) N = number of components in the vector (e.g. 3) E0...3 = what index the n-th element of this swizzle refers to in the unswizzled vec DUPLICATE_ELEMENTS = 1 if there is a repeated element, 0 otherwise (used to specialize swizzles containing duplicate elements so that they cannot be used as r-values). */ template struct _swizzle_base2 : public _swizzle_base1 { typedef VecType vec_type; typedef ValueType value_type; GLM_FUNC_QUALIFIER _swizzle_base2& operator= (const ValueType& t) { for (int i = 0; i < N; ++i) (*this)[i] = t; return *this; } GLM_FUNC_QUALIFIER _swizzle_base2& operator= (const VecType& that) { struct op { GLM_FUNC_QUALIFIER void operator() (value_type& e, value_type& t) { e = t; } }; _apply_op(that, op()); return *this; } GLM_FUNC_QUALIFIER void operator -= (const VecType& that) { struct op { GLM_FUNC_QUALIFIER void operator() (value_type& e, value_type& t) { e -= t; } }; _apply_op(that, op()); } GLM_FUNC_QUALIFIER void operator += (const VecType& that) { struct op { GLM_FUNC_QUALIFIER void operator() (value_type& e, value_type& t) { e += t; } }; _apply_op(that, op()); } GLM_FUNC_QUALIFIER void operator *= (const VecType& that) { struct op { GLM_FUNC_QUALIFIER void operator() (value_type& e, value_type& t) { e *= t; } }; _apply_op(that, op()); } GLM_FUNC_QUALIFIER void operator /= (const VecType& that) { struct op { GLM_FUNC_QUALIFIER void operator() (value_type& e, value_type& t) { e /= t; } }; _apply_op(that, op()); } GLM_FUNC_QUALIFIER value_type& operator[] (size_t i) { #ifndef __CUDA_ARCH__ static #endif const int offset_dst[4] = { E0, E1, E2, E3 }; return this->elem(offset_dst[i]); } GLM_FUNC_QUALIFIER value_type operator[] (size_t i) const { #ifndef __CUDA_ARCH__ static #endif const int offset_dst[4] = { E0, E1, E2, E3 }; return this->elem(offset_dst[i]); } protected: template GLM_FUNC_QUALIFIER void _apply_op(const VecType& that, T op) { // Make a copy of the data in this == &that. // The copier should optimize out the copy in cases where the function is // properly inlined and the copy is not necessary. ValueType t[N]; for (int i = 0; i < N; ++i) t[i] = that[i]; for (int i = 0; i < N; ++i) op( (*this)[i], t[i] ); } }; // Specialization for swizzles containing duplicate elements. These cannot be modified. template struct _swizzle_base2 : public _swizzle_base1 { typedef VecType vec_type; typedef ValueType value_type; struct Stub {}; GLM_FUNC_QUALIFIER _swizzle_base2& operator= (Stub const &) { return *this; } GLM_FUNC_QUALIFIER value_type operator[] (size_t i) const { #ifndef __CUDA_ARCH__ static #endif const int offset_dst[4] = { E0, E1, E2, E3 }; return this->elem(offset_dst[i]); } }; template struct _swizzle : public _swizzle_base2 { typedef _swizzle_base2 base_type; using base_type::operator=; GLM_FUNC_QUALIFIER operator VecType () const { return (*this)(); } }; // // To prevent the C++ syntax from getting entirely overwhelming, define some alias macros // #define _GLM_SWIZZLE_TEMPLATE1 template #define _GLM_SWIZZLE_TEMPLATE2 template #define _GLM_SWIZZLE_TYPE1 _swizzle #define _GLM_SWIZZLE_TYPE2 _swizzle // // Wrapper for a binary operator (e.g. u.yy + v.zy) // #define _GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(OPERAND) \ _GLM_SWIZZLE_TEMPLATE2 \ GLM_FUNC_QUALIFIER V operator OPERAND ( const _GLM_SWIZZLE_TYPE1& a, const _GLM_SWIZZLE_TYPE2& b) \ { \ return a() OPERAND b(); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER V operator OPERAND ( const _GLM_SWIZZLE_TYPE1& a, const V& b) \ { \ return a() OPERAND b; \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER V operator OPERAND ( const V& a, const _GLM_SWIZZLE_TYPE1& b) \ { \ return a OPERAND b(); \ } // // Wrapper for a operand between a swizzle and a binary (e.g. 1.0f - u.xyz) // #define _GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(OPERAND) \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER V operator OPERAND ( const _GLM_SWIZZLE_TYPE1& a, const T& b) \ { \ return a() OPERAND b; \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER V operator OPERAND ( const T& a, const _GLM_SWIZZLE_TYPE1& b) \ { \ return a OPERAND b(); \ } // // Macro for wrapping a function taking one argument (e.g. abs()) // #define _GLM_SWIZZLE_FUNCTION_1_ARGS(RETURN_TYPE,FUNCTION) \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a) \ { \ return FUNCTION(a()); \ } // // Macro for wrapping a function taking two vector arguments (e.g. dot()). // #define _GLM_SWIZZLE_FUNCTION_2_ARGS(RETURN_TYPE,FUNCTION) \ _GLM_SWIZZLE_TEMPLATE2 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a, const _GLM_SWIZZLE_TYPE2& b) \ { \ return FUNCTION(a(), b()); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a, const _GLM_SWIZZLE_TYPE1& b) \ { \ return FUNCTION(a(), b()); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a, const typename V& b) \ { \ return FUNCTION(a(), b); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const V& a, const _GLM_SWIZZLE_TYPE1& b) \ { \ return FUNCTION(a, b()); \ } // // Macro for wrapping a function take 2 vec arguments followed by a scalar (e.g. mix()). // #define _GLM_SWIZZLE_FUNCTION_2_ARGS_SCALAR(RETURN_TYPE,FUNCTION) \ _GLM_SWIZZLE_TEMPLATE2 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a, const _GLM_SWIZZLE_TYPE2& b, const T& c) \ { \ return FUNCTION(a(), b(), c); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a, const _GLM_SWIZZLE_TYPE1& b, const T& c) \ { \ return FUNCTION(a(), b(), c); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const _GLM_SWIZZLE_TYPE1& a, const typename S0::vec_type& b, const T& c)\ { \ return FUNCTION(a(), b, c); \ } \ _GLM_SWIZZLE_TEMPLATE1 \ GLM_FUNC_QUALIFIER typename _GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const typename V& a, const _GLM_SWIZZLE_TYPE1& b, const T& c) \ { \ return FUNCTION(a, b(), c); \ } }//namespace detail }//namespace glm namespace glm { namespace detail { _GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(-) _GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(*) _GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(+) _GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(-) _GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(*) _GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(/) } // // Swizzles are distinct types from the unswizzled type. The below macros will // provide template specializations for the swizzle types for the given functions // so that the compiler does not have any ambiguity to choosing how to handle // the function. // // The alternative is to use the operator()() when calling the function in order // to explicitly convert the swizzled type to the unswizzled type. // //_GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, abs); //_GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, acos); //_GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, acosh); //_GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, all); //_GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, any); //_GLM_SWIZZLE_FUNCTION_2_ARGS(value_type, dot); //_GLM_SWIZZLE_FUNCTION_2_ARGS(vec_type, cross); //_GLM_SWIZZLE_FUNCTION_2_ARGS(vec_type, step); //_GLM_SWIZZLE_FUNCTION_2_ARGS_SCALAR(vec_type, mix); } #define _GLM_SWIZZLE2_2_MEMBERS(T, P, V, E0,E1) \ struct { _swizzle<2, T, P, V, 0,0,-1,-2> E0 ## E0; }; \ struct { _swizzle<2, T, P, V, 0,1,-1,-2> E0 ## E1; }; \ struct { _swizzle<2, T, P, V, 1,0,-1,-2> E1 ## E0; }; \ struct { _swizzle<2, T, P, V, 1,1,-1,-2> E1 ## E1; }; #define _GLM_SWIZZLE2_3_MEMBERS(T, P, V, E0,E1) \ struct { _swizzle<3,T, P, V, 0,0,0,-1> E0 ## E0 ## E0; }; \ struct { _swizzle<3,T, P, V, 0,0,1,-1> E0 ## E0 ## E1; }; \ struct { _swizzle<3,T, P, V, 0,1,0,-1> E0 ## E1 ## E0; }; \ struct { _swizzle<3,T, P, V, 0,1,1,-1> E0 ## E1 ## E1; }; \ struct { _swizzle<3,T, P, V, 1,0,0,-1> E1 ## E0 ## E0; }; \ struct { _swizzle<3,T, P, V, 1,0,1,-1> E1 ## E0 ## E1; }; \ struct { _swizzle<3,T, P, V, 1,1,0,-1> E1 ## E1 ## E0; }; \ struct { _swizzle<3,T, P, V, 1,1,1,-1> E1 ## E1 ## E1; }; #define _GLM_SWIZZLE2_4_MEMBERS(T, P, V, E0,E1) \ struct { _swizzle<4,T, P, V, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; #define _GLM_SWIZZLE3_2_MEMBERS(T, P, V, E0,E1,E2) \ struct { _swizzle<2,T, P, V, 0,0,-1,-2> E0 ## E0; }; \ struct { _swizzle<2,T, P, V, 0,1,-1,-2> E0 ## E1; }; \ struct { _swizzle<2,T, P, V, 0,2,-1,-2> E0 ## E2; }; \ struct { _swizzle<2,T, P, V, 1,0,-1,-2> E1 ## E0; }; \ struct { _swizzle<2,T, P, V, 1,1,-1,-2> E1 ## E1; }; \ struct { _swizzle<2,T, P, V, 1,2,-1,-2> E1 ## E2; }; \ struct { _swizzle<2,T, P, V, 2,0,-1,-2> E2 ## E0; }; \ struct { _swizzle<2,T, P, V, 2,1,-1,-2> E2 ## E1; }; \ struct { _swizzle<2,T, P, V, 2,2,-1,-2> E2 ## E2; }; #define _GLM_SWIZZLE3_3_MEMBERS(T, P, V ,E0,E1,E2) \ struct { _swizzle<3,T,P, V, 0,0,0,-1> E0 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,0,1,-1> E0 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,0,2,-1> E0 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 0,1,0,-1> E0 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,1,1,-1> E0 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,1,2,-1> E0 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 0,2,0,-1> E0 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,2,1,-1> E0 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,2,2,-1> E0 ## E2 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,0,0,-1> E1 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,0,1,-1> E1 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,0,2,-1> E1 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,1,0,-1> E1 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,1,1,-1> E1 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,1,2,-1> E1 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,2,0,-1> E1 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,2,1,-1> E1 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,2,2,-1> E1 ## E2 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,0,0,-1> E2 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,0,1,-1> E2 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,0,2,-1> E2 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,1,0,-1> E2 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,1,1,-1> E2 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,1,2,-1> E2 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,2,0,-1> E2 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,2,1,-1> E2 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,2,2,-1> E2 ## E2 ## E2; }; #define _GLM_SWIZZLE3_4_MEMBERS(T, P, V, E0,E1,E2) \ struct { _swizzle<4,T, P, V, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,0,0,2> E0 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,0,1,2> E0 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,0,2,0> E0 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,0,2,1> E0 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,0,2,2> E0 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,1,0,2> E0 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,1,1,2> E0 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,1,2,0> E0 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,1,2,1> E0 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,1,2,2> E0 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,2,0,0> E0 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,2,0,1> E0 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,2,0,2> E0 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,2,1,0> E0 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,2,1,1> E0 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,2,1,2> E0 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 0,2,2,0> E0 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 0,2,2,1> E0 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 0,2,2,2> E0 ## E2 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,0,0,2> E1 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,0,1,2> E1 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,0,2,0> E1 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,0,2,1> E1 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,0,2,2> E1 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,1,0,2> E1 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,1,1,2> E1 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,1,2,0> E1 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,1,2,1> E1 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,1,2,2> E1 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,2,0,0> E1 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,2,0,1> E1 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,2,0,2> E1 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,2,1,0> E1 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,2,1,1> E1 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,2,1,2> E1 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 1,2,2,0> E1 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 1,2,2,1> E1 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 1,2,2,2> E1 ## E2 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,0,0,0> E2 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,0,0,1> E2 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,0,0,2> E2 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,0,1,0> E2 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,0,1,1> E2 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,0,1,2> E2 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,0,2,0> E2 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,0,2,1> E2 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,0,2,2> E2 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,1,0,0> E2 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,1,0,1> E2 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,1,0,2> E2 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,1,1,0> E2 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,1,1,1> E2 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,1,1,2> E2 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,1,2,0> E2 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,1,2,1> E2 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,1,2,2> E2 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,2,0,0> E2 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,2,0,1> E2 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,2,0,2> E2 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,2,1,0> E2 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,2,1,1> E2 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,2,1,2> E2 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4,T, P, V, 2,2,2,0> E2 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4,T, P, V, 2,2,2,1> E2 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4,T, P, V, 2,2,2,2> E2 ## E2 ## E2 ## E2; }; #define _GLM_SWIZZLE4_2_MEMBERS(T, P, V, E0,E1,E2,E3) \ struct { _swizzle<2,T, P, V, 0,0,-1,-2> E0 ## E0; }; \ struct { _swizzle<2,T, P, V, 0,1,-1,-2> E0 ## E1; }; \ struct { _swizzle<2,T, P, V, 0,2,-1,-2> E0 ## E2; }; \ struct { _swizzle<2,T, P, V, 0,3,-1,-2> E0 ## E3; }; \ struct { _swizzle<2,T, P, V, 1,0,-1,-2> E1 ## E0; }; \ struct { _swizzle<2,T, P, V, 1,1,-1,-2> E1 ## E1; }; \ struct { _swizzle<2,T, P, V, 1,2,-1,-2> E1 ## E2; }; \ struct { _swizzle<2,T, P, V, 1,3,-1,-2> E1 ## E3; }; \ struct { _swizzle<2,T, P, V, 2,0,-1,-2> E2 ## E0; }; \ struct { _swizzle<2,T, P, V, 2,1,-1,-2> E2 ## E1; }; \ struct { _swizzle<2,T, P, V, 2,2,-1,-2> E2 ## E2; }; \ struct { _swizzle<2,T, P, V, 2,3,-1,-2> E2 ## E3; }; \ struct { _swizzle<2,T, P, V, 3,0,-1,-2> E3 ## E0; }; \ struct { _swizzle<2,T, P, V, 3,1,-1,-2> E3 ## E1; }; \ struct { _swizzle<2,T, P, V, 3,2,-1,-2> E3 ## E2; }; \ struct { _swizzle<2,T, P, V, 3,3,-1,-2> E3 ## E3; }; #define _GLM_SWIZZLE4_3_MEMBERS(T,P, V, E0,E1,E2,E3) \ struct { _swizzle<3,T,P, V, 0,0,0,-1> E0 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,0,1,-1> E0 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,0,2,-1> E0 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 0,0,3,-1> E0 ## E0 ## E3; }; \ struct { _swizzle<3,T,P, V, 0,1,0,-1> E0 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,1,1,-1> E0 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,1,2,-1> E0 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 0,1,3,-1> E0 ## E1 ## E3; }; \ struct { _swizzle<3,T,P, V, 0,2,0,-1> E0 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,2,1,-1> E0 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,2,2,-1> E0 ## E2 ## E2; }; \ struct { _swizzle<3,T,P, V, 0,2,3,-1> E0 ## E2 ## E3; }; \ struct { _swizzle<3,T,P, V, 0,3,0,-1> E0 ## E3 ## E0; }; \ struct { _swizzle<3,T,P, V, 0,3,1,-1> E0 ## E3 ## E1; }; \ struct { _swizzle<3,T,P, V, 0,3,2,-1> E0 ## E3 ## E2; }; \ struct { _swizzle<3,T,P, V, 0,3,3,-1> E0 ## E3 ## E3; }; \ struct { _swizzle<3,T,P, V, 1,0,0,-1> E1 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,0,1,-1> E1 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,0,2,-1> E1 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,0,3,-1> E1 ## E0 ## E3; }; \ struct { _swizzle<3,T,P, V, 1,1,0,-1> E1 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,1,1,-1> E1 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,1,2,-1> E1 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,1,3,-1> E1 ## E1 ## E3; }; \ struct { _swizzle<3,T,P, V, 1,2,0,-1> E1 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,2,1,-1> E1 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,2,2,-1> E1 ## E2 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,2,3,-1> E1 ## E2 ## E3; }; \ struct { _swizzle<3,T,P, V, 1,3,0,-1> E1 ## E3 ## E0; }; \ struct { _swizzle<3,T,P, V, 1,3,1,-1> E1 ## E3 ## E1; }; \ struct { _swizzle<3,T,P, V, 1,3,2,-1> E1 ## E3 ## E2; }; \ struct { _swizzle<3,T,P, V, 1,3,3,-1> E1 ## E3 ## E3; }; \ struct { _swizzle<3,T,P, V, 2,0,0,-1> E2 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,0,1,-1> E2 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,0,2,-1> E2 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,0,3,-1> E2 ## E0 ## E3; }; \ struct { _swizzle<3,T,P, V, 2,1,0,-1> E2 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,1,1,-1> E2 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,1,2,-1> E2 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,1,3,-1> E2 ## E1 ## E3; }; \ struct { _swizzle<3,T,P, V, 2,2,0,-1> E2 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,2,1,-1> E2 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,2,2,-1> E2 ## E2 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,2,3,-1> E2 ## E2 ## E3; }; \ struct { _swizzle<3,T,P, V, 2,3,0,-1> E2 ## E3 ## E0; }; \ struct { _swizzle<3,T,P, V, 2,3,1,-1> E2 ## E3 ## E1; }; \ struct { _swizzle<3,T,P, V, 2,3,2,-1> E2 ## E3 ## E2; }; \ struct { _swizzle<3,T,P, V, 2,3,3,-1> E2 ## E3 ## E3; }; \ struct { _swizzle<3,T,P, V, 3,0,0,-1> E3 ## E0 ## E0; }; \ struct { _swizzle<3,T,P, V, 3,0,1,-1> E3 ## E0 ## E1; }; \ struct { _swizzle<3,T,P, V, 3,0,2,-1> E3 ## E0 ## E2; }; \ struct { _swizzle<3,T,P, V, 3,0,3,-1> E3 ## E0 ## E3; }; \ struct { _swizzle<3,T,P, V, 3,1,0,-1> E3 ## E1 ## E0; }; \ struct { _swizzle<3,T,P, V, 3,1,1,-1> E3 ## E1 ## E1; }; \ struct { _swizzle<3,T,P, V, 3,1,2,-1> E3 ## E1 ## E2; }; \ struct { _swizzle<3,T,P, V, 3,1,3,-1> E3 ## E1 ## E3; }; \ struct { _swizzle<3,T,P, V, 3,2,0,-1> E3 ## E2 ## E0; }; \ struct { _swizzle<3,T,P, V, 3,2,1,-1> E3 ## E2 ## E1; }; \ struct { _swizzle<3,T,P, V, 3,2,2,-1> E3 ## E2 ## E2; }; \ struct { _swizzle<3,T,P, V, 3,2,3,-1> E3 ## E2 ## E3; }; \ struct { _swizzle<3,T,P, V, 3,3,0,-1> E3 ## E3 ## E0; }; \ struct { _swizzle<3,T,P, V, 3,3,1,-1> E3 ## E3 ## E1; }; \ struct { _swizzle<3,T,P, V, 3,3,2,-1> E3 ## E3 ## E2; }; \ struct { _swizzle<3,T,P, V, 3,3,3,-1> E3 ## E3 ## E3; }; #define _GLM_SWIZZLE4_4_MEMBERS(T, P, V, E0,E1,E2,E3) \ struct { _swizzle<4, T, P, V, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,0,0,2> E0 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,0,0,3> E0 ## E0 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,0,1,2> E0 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,0,1,3> E0 ## E0 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,0,2,0> E0 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,0,2,1> E0 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,0,2,2> E0 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,0,2,3> E0 ## E0 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,0,3,0> E0 ## E0 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,0,3,1> E0 ## E0 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,0,3,2> E0 ## E0 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,0,3,3> E0 ## E0 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,1,0,2> E0 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,1,0,3> E0 ## E1 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,1,1,2> E0 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,1,1,3> E0 ## E1 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,1,2,0> E0 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,1,2,1> E0 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,1,2,2> E0 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,1,2,3> E0 ## E1 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,1,3,0> E0 ## E1 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,1,3,1> E0 ## E1 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,1,3,2> E0 ## E1 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,1,3,3> E0 ## E1 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,2,0,0> E0 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,2,0,1> E0 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,2,0,2> E0 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,2,0,3> E0 ## E2 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,2,1,0> E0 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,2,1,1> E0 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,2,1,2> E0 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,2,1,3> E0 ## E2 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,2,2,0> E0 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,2,2,1> E0 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,2,2,2> E0 ## E2 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,2,2,3> E0 ## E2 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,2,3,0> E0 ## E2 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,2,3,1> E0 ## E2 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,2,3,2> E0 ## E2 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,2,3,3> E0 ## E2 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,3,0,0> E0 ## E3 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,3,0,1> E0 ## E3 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,3,0,2> E0 ## E3 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,3,0,3> E0 ## E3 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,3,1,0> E0 ## E3 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,3,1,1> E0 ## E3 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,3,1,2> E0 ## E3 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,3,1,3> E0 ## E3 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,3,2,0> E0 ## E3 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,3,2,1> E0 ## E3 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,3,2,2> E0 ## E3 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,3,2,3> E0 ## E3 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 0,3,3,0> E0 ## E3 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 0,3,3,1> E0 ## E3 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 0,3,3,2> E0 ## E3 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 0,3,3,3> E0 ## E3 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,0,0,2> E1 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,0,0,3> E1 ## E0 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,0,1,2> E1 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,0,1,3> E1 ## E0 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,0,2,0> E1 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,0,2,1> E1 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,0,2,2> E1 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,0,2,3> E1 ## E0 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,0,3,0> E1 ## E0 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,0,3,1> E1 ## E0 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,0,3,2> E1 ## E0 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,0,3,3> E1 ## E0 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,1,0,2> E1 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,1,0,3> E1 ## E1 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,1,1,2> E1 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,1,1,3> E1 ## E1 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,1,2,0> E1 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,1,2,1> E1 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,1,2,2> E1 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,1,2,3> E1 ## E1 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,1,3,0> E1 ## E1 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,1,3,1> E1 ## E1 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,1,3,2> E1 ## E1 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,1,3,3> E1 ## E1 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,2,0,0> E1 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,2,0,1> E1 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,2,0,2> E1 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,2,0,3> E1 ## E2 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,2,1,0> E1 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,2,1,1> E1 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,2,1,2> E1 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,2,1,3> E1 ## E2 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,2,2,0> E1 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,2,2,1> E1 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,2,2,2> E1 ## E2 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,2,2,3> E1 ## E2 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,2,3,0> E1 ## E2 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,2,3,1> E1 ## E2 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,2,3,2> E1 ## E2 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,2,3,3> E1 ## E2 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,3,0,0> E1 ## E3 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,3,0,1> E1 ## E3 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,3,0,2> E1 ## E3 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,3,0,3> E1 ## E3 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,3,1,0> E1 ## E3 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,3,1,1> E1 ## E3 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,3,1,2> E1 ## E3 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,3,1,3> E1 ## E3 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,3,2,0> E1 ## E3 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,3,2,1> E1 ## E3 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,3,2,2> E1 ## E3 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,3,2,3> E1 ## E3 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 1,3,3,0> E1 ## E3 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 1,3,3,1> E1 ## E3 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 1,3,3,2> E1 ## E3 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 1,3,3,3> E1 ## E3 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,0,0,0> E2 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,0,0,1> E2 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,0,0,2> E2 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,0,0,3> E2 ## E0 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,0,1,0> E2 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,0,1,1> E2 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,0,1,2> E2 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,0,1,3> E2 ## E0 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,0,2,0> E2 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,0,2,1> E2 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,0,2,2> E2 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,0,2,3> E2 ## E0 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,0,3,0> E2 ## E0 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,0,3,1> E2 ## E0 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,0,3,2> E2 ## E0 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,0,3,3> E2 ## E0 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,1,0,0> E2 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,1,0,1> E2 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,1,0,2> E2 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,1,0,3> E2 ## E1 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,1,1,0> E2 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,1,1,1> E2 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,1,1,2> E2 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,1,1,3> E2 ## E1 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,1,2,0> E2 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,1,2,1> E2 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,1,2,2> E2 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,1,2,3> E2 ## E1 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,1,3,0> E2 ## E1 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,1,3,1> E2 ## E1 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,1,3,2> E2 ## E1 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,1,3,3> E2 ## E1 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,2,0,0> E2 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,2,0,1> E2 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,2,0,2> E2 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,2,0,3> E2 ## E2 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,2,1,0> E2 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,2,1,1> E2 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,2,1,2> E2 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,2,1,3> E2 ## E2 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,2,2,0> E2 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,2,2,1> E2 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,2,2,2> E2 ## E2 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,2,2,3> E2 ## E2 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,2,3,0> E2 ## E2 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,2,3,1> E2 ## E2 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,2,3,2> E2 ## E2 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,2,3,3> E2 ## E2 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,3,0,0> E2 ## E3 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,3,0,1> E2 ## E3 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,3,0,2> E2 ## E3 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,3,0,3> E2 ## E3 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,3,1,0> E2 ## E3 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,3,1,1> E2 ## E3 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,3,1,2> E2 ## E3 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,3,1,3> E2 ## E3 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,3,2,0> E2 ## E3 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,3,2,1> E2 ## E3 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,3,2,2> E2 ## E3 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,3,2,3> E2 ## E3 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 2,3,3,0> E2 ## E3 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 2,3,3,1> E2 ## E3 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 2,3,3,2> E2 ## E3 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 2,3,3,3> E2 ## E3 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,0,0,0> E3 ## E0 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,0,0,1> E3 ## E0 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,0,0,2> E3 ## E0 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,0,0,3> E3 ## E0 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,0,1,0> E3 ## E0 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,0,1,1> E3 ## E0 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,0,1,2> E3 ## E0 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,0,1,3> E3 ## E0 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,0,2,0> E3 ## E0 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,0,2,1> E3 ## E0 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,0,2,2> E3 ## E0 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,0,2,3> E3 ## E0 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,0,3,0> E3 ## E0 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,0,3,1> E3 ## E0 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,0,3,2> E3 ## E0 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,0,3,3> E3 ## E0 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,1,0,0> E3 ## E1 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,1,0,1> E3 ## E1 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,1,0,2> E3 ## E1 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,1,0,3> E3 ## E1 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,1,1,0> E3 ## E1 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,1,1,1> E3 ## E1 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,1,1,2> E3 ## E1 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,1,1,3> E3 ## E1 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,1,2,0> E3 ## E1 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,1,2,1> E3 ## E1 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,1,2,2> E3 ## E1 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,1,2,3> E3 ## E1 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,1,3,0> E3 ## E1 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,1,3,1> E3 ## E1 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,1,3,2> E3 ## E1 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,1,3,3> E3 ## E1 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,2,0,0> E3 ## E2 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,2,0,1> E3 ## E2 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,2,0,2> E3 ## E2 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,2,0,3> E3 ## E2 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,2,1,0> E3 ## E2 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,2,1,1> E3 ## E2 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,2,1,2> E3 ## E2 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,2,1,3> E3 ## E2 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,2,2,0> E3 ## E2 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,2,2,1> E3 ## E2 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,2,2,2> E3 ## E2 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,2,2,3> E3 ## E2 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,2,3,0> E3 ## E2 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,2,3,1> E3 ## E2 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,2,3,2> E3 ## E2 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,2,3,3> E3 ## E2 ## E3 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,3,0,0> E3 ## E3 ## E0 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,3,0,1> E3 ## E3 ## E0 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,3,0,2> E3 ## E3 ## E0 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,3,0,3> E3 ## E3 ## E0 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,3,1,0> E3 ## E3 ## E1 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,3,1,1> E3 ## E3 ## E1 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,3,1,2> E3 ## E3 ## E1 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,3,1,3> E3 ## E3 ## E1 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,3,2,0> E3 ## E3 ## E2 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,3,2,1> E3 ## E3 ## E2 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,3,2,2> E3 ## E3 ## E2 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,3,2,3> E3 ## E3 ## E2 ## E3; }; \ struct { _swizzle<4, T, P, V, 3,3,3,0> E3 ## E3 ## E3 ## E0; }; \ struct { _swizzle<4, T, P, V, 3,3,3,1> E3 ## E3 ## E3 ## E1; }; \ struct { _swizzle<4, T, P, V, 3,3,3,2> E3 ## E3 ## E3 ## E2; }; \ struct { _swizzle<4, T, P, V, 3,3,3,3> E3 ## E3 ## E3 ## E3; }; rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_swizzle_func.hpp000066400000000000000000001760211503504370700275220ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/_swizzle_func.hpp /// @date 2011-10-16 / 2011-10-16 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once #define GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, CONST, A, B) \ SWIZZLED_TYPE A ## B() CONST \ { \ return SWIZZLED_TYPE(this->A, this->B); \ } #define GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, CONST, A, B, C) \ SWIZZLED_TYPE A ## B ## C() CONST \ { \ return SWIZZLED_TYPE(this->A, this->B, this->C); \ } #define GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, CONST, A, B, C, D) \ SWIZZLED_TYPE A ## B ## C ## D() CONST \ { \ return SWIZZLED_TYPE(this->A, this->B, this->C, this->D); \ } #define GLM_SWIZZLE_GEN_VEC2_ENTRY_DEF(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, CONST, A, B) \ template \ SWIZZLED_TYPE CLASS_TYPE::A ## B() CONST \ { \ return SWIZZLED_TYPE(this->A, this->B); \ } #define GLM_SWIZZLE_GEN_VEC3_ENTRY_DEF(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, CONST, A, B, C) \ template \ SWIZZLED_TYPE CLASS_TYPE::A ## B ## C() CONST \ { \ return SWIZZLED_TYPE(this->A, this->B, this->C); \ } #define GLM_SWIZZLE_GEN_VEC4_ENTRY_DEF(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, CONST, A, B, C, D) \ template \ SWIZZLED_TYPE CLASS_TYPE::A ## B ## C ## D() CONST \ { \ return SWIZZLED_TYPE(this->A, this->B, this->C, this->D); \ } #define GLM_MUTABLE #define GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, A) #define GLM_SWIZZLE_GEN_REF_FROM_VEC2(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE) \ GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, x, y) \ GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, r, g) \ GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, s, t) //GLM_SWIZZLE_GEN_REF_FROM_VEC2(valType, detail::vec2, detail::ref2) #define GLM_SWIZZLE_GEN_REF2_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, B) #define GLM_SWIZZLE_GEN_REF3_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, B, A) #define GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_REF3_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC3_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_REF2_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, A, B, C) #define GLM_SWIZZLE_GEN_REF_FROM_VEC3(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE) \ GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, x, y, z) \ GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, r, g, b) \ GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, s, t, p) //GLM_SWIZZLE_GEN_REF_FROM_VEC3(valType, detail::vec3, detail::ref2, detail::ref3) #define GLM_SWIZZLE_GEN_REF2_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, A, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, B, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, C, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, D, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, D, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, GLM_MUTABLE, D, C) #define GLM_SWIZZLE_GEN_REF3_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, B, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, D, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, D, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, A, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, D, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, D, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, A, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, B, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, D, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, D, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, C, B) #define GLM_SWIZZLE_GEN_REF4_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, C, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, C, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, D, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, D, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, B, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , A, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, C, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, C, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, D, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, D, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, A, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , B, A, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, B, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, B, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, D, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, D, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, A, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , C, A, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, , D, B, C, A) #define GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_REF2_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_REF3_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC3_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_REF4_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC4_TYPE, A, B, C, D) #define GLM_SWIZZLE_GEN_REF_FROM_VEC4(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE) \ GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, x, y, z, w) \ GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, r, g, b, a) \ GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, s, t, p, q) //GLM_SWIZZLE_GEN_REF_FROM_VEC4(valType, detail::vec4, detail::ref2, detail::ref3, detail::ref4) #define GLM_SWIZZLE_GEN_VEC2_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B) #define GLM_SWIZZLE_GEN_VEC3_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B) #define GLM_SWIZZLE_GEN_VEC4_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, B) #define GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC2_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC3_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC3_TYPE, A, B) \ GLM_SWIZZLE_GEN_VEC4_FROM_VEC2_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC4_TYPE, A, B) #define GLM_SWIZZLE_GEN_VEC_FROM_VEC2(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, x, y) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, r, g) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, s, t) //GLM_SWIZZLE_GEN_VEC_FROM_VEC2(valType, detail::vec2, detail::vec2, detail::vec3, detail::vec4) #define GLM_SWIZZLE_GEN_VEC2_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C) #define GLM_SWIZZLE_GEN_VEC3_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C) #define GLM_SWIZZLE_GEN_VEC4_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, C) #define GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC2_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC3_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC3_TYPE, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_FROM_VEC3_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC4_TYPE, A, B, C) #define GLM_SWIZZLE_GEN_VEC_FROM_VEC3(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, x, y, z) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, r, g, b) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, s, t, p) //GLM_SWIZZLE_GEN_VEC_FROM_VEC3(valType, detail::vec3, detail::vec2, detail::vec3, detail::vec4) #define GLM_SWIZZLE_GEN_VEC2_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C) \ GLM_SWIZZLE_GEN_VEC2_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D) #define GLM_SWIZZLE_GEN_VEC3_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, D) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, A) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, B) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, C) \ GLM_SWIZZLE_GEN_VEC3_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, D) #define GLM_SWIZZLE_GEN_VEC4_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, A, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, B, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, C, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, A, D, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, A, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, B, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, C, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, B, D, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, A, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, B, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, C, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, C, D, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, A, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, B, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, C, D, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, A, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, A, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, A, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, A, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, B, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, B, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, B, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, B, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, C, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, C, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, C, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, C, D) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, D, A) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, D, B) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, D, C) \ GLM_SWIZZLE_GEN_VEC4_ENTRY(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_TYPE, const, D, D, D, D) #define GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC2_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC3_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC3_TYPE, A, B, C, D) \ GLM_SWIZZLE_GEN_VEC4_FROM_VEC4_SWIZZLE(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC4_TYPE, A, B, C, D) #define GLM_SWIZZLE_GEN_VEC_FROM_VEC4(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, x, y, z, w) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, r, g, b, a) \ GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(TMPL_TYPE, PRECISION, CLASS_TYPE, SWIZZLED_VEC2_TYPE, SWIZZLED_VEC3_TYPE, SWIZZLED_VEC4_TYPE, s, t, p, q) //GLM_SWIZZLE_GEN_VEC_FROM_VEC4(valType, detail::vec4, detail::vec2, detail::vec3, detail::vec4) rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/_vectorize.hpp000066400000000000000000000145621503504370700270130ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/_vectorize.hpp /// @date 2011-10-14 / 2011-10-14 /// @author Christophe Riccio /////////////////////////////////////////////////////////////////////////////////// #pragma once #include "type_vec1.hpp" #include "type_vec2.hpp" #include "type_vec3.hpp" #include "type_vec4.hpp" #define VECTORIZE1_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec1 func( \ detail::tvec1 const & v) \ { \ return detail::tvec1( \ func(v.x)); \ } #define VECTORIZE2_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec2 func( \ detail::tvec2 const & v) \ { \ return detail::tvec2( \ func(v.x), \ func(v.y)); \ } #define VECTORIZE3_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec3 func( \ detail::tvec3 const & v) \ { \ return detail::tvec3( \ func(v.x), \ func(v.y), \ func(v.z)); \ } #define VECTORIZE4_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec4 func( \ detail::tvec4 const & v) \ { \ return detail::tvec4( \ func(v.x), \ func(v.y), \ func(v.z), \ func(v.w)); \ } #define VECTORIZE_VEC(func) \ VECTORIZE1_VEC(func) \ VECTORIZE2_VEC(func) \ VECTORIZE3_VEC(func) \ VECTORIZE4_VEC(func) #define VECTORIZE1_VEC_SCA(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec1 func \ ( \ detail::tvec1 const & x, \ typename detail::tvec1::value_type const & y \ ) \ { \ return detail::tvec1( \ func(x.x, y)); \ } #define VECTORIZE2_VEC_SCA(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec2 func \ ( \ detail::tvec2 const & x, \ typename detail::tvec2::value_type const & y \ ) \ { \ return detail::tvec2( \ func(x.x, y), \ func(x.y, y)); \ } #define VECTORIZE3_VEC_SCA(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec3 func \ ( \ detail::tvec3 const & x, \ typename detail::tvec3::value_type const & y \ ) \ { \ return detail::tvec3( \ func(x.x, y), \ func(x.y, y), \ func(x.z, y)); \ } #define VECTORIZE4_VEC_SCA(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec4 func \ ( \ detail::tvec4 const & x, \ typename detail::tvec4::value_type const & y \ ) \ { \ return detail::tvec4( \ func(x.x, y), \ func(x.y, y), \ func(x.z, y), \ func(x.w, y)); \ } #define VECTORIZE_VEC_SCA(func) \ VECTORIZE1_VEC_SCA(func) \ VECTORIZE2_VEC_SCA(func) \ VECTORIZE3_VEC_SCA(func) \ VECTORIZE4_VEC_SCA(func) #define VECTORIZE1_VEC_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec1 func \ ( \ detail::tvec1 const & x, \ detail::tvec1 const & y \ ) \ { \ return detail::tvec1( \ func(x.x, y.x)); \ } #define VECTORIZE2_VEC_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec2 func \ ( \ detail::tvec2 const & x, \ detail::tvec2 const & y \ ) \ { \ return detail::tvec2( \ func(x.x, y.x), \ func(x.y, y.y)); \ } #define VECTORIZE3_VEC_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec3 func \ ( \ detail::tvec3 const & x, \ detail::tvec3 const & y \ ) \ { \ return detail::tvec3( \ func(x.x, y.x), \ func(x.y, y.y), \ func(x.z, y.z)); \ } #define VECTORIZE4_VEC_VEC(func) \ template \ GLM_FUNC_QUALIFIER detail::tvec4 func \ ( \ detail::tvec4 const & x, \ detail::tvec4 const & y \ ) \ { \ return detail::tvec4( \ func(x.x, y.x), \ func(x.y, y.y), \ func(x.z, y.z), \ func(x.w, y.w)); \ } #define VECTORIZE_VEC_VEC(func) \ VECTORIZE1_VEC_VEC(func) \ VECTORIZE2_VEC_VEC(func) \ VECTORIZE3_VEC_VEC(func) \ VECTORIZE4_VEC_VEC(func) namespace glm{ namespace detail { template struct If { template static GLM_FUNC_QUALIFIER T apply(F functor, const T& val) { return functor(val); } }; template<> struct If { template static GLM_FUNC_QUALIFIER T apply(F, const T& val) { return val; } }; }//namespace detail }//namespace glm rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/dummy.cpp000066400000000000000000000141701503504370700257630ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/dummy.cpp /// @date 2011-01-19 / 2011-06-15 /// @author Christophe Riccio /// /// GLM is a header only library. There is nothing to compile. /// dummy.cpp exist only a wordaround for CMake file. /////////////////////////////////////////////////////////////////////////////////// #define GLM_MESSAGES #include "../glm.hpp" #include struct material { glm::vec4 emission; // Ecm glm::vec4 ambient; // Acm glm::vec4 diffuse; // Dcm glm::vec4 specular; // Scm float shininess; // Srm }; struct light { glm::vec4 ambient; // Acli glm::vec4 diffuse; // Dcli glm::vec4 specular; // Scli glm::vec4 position; // Ppli glm::vec4 halfVector; // Derived: Hi glm::vec3 spotDirection; // Sdli float spotExponent; // Srli float spotCutoff; // Crli // (range: [0.0,90.0], 180.0) float spotCosCutoff; // Derived: cos(Crli) // (range: [1.0,0.0],-1.0) float constantAttenuation; // K0 float linearAttenuation; // K1 float quadraticAttenuation;// K2 }; // Sample 1 #include // glm::vec3 #include // glm::cross, glm::normalize glm::vec3 computeNormal ( glm::vec3 const & a, glm::vec3 const & b, glm::vec3 const & c ) { return glm::normalize(glm::cross(c - a, b - a)); } typedef unsigned int GLuint; #define GL_FALSE 0 void glUniformMatrix4fv(GLuint, int, int, float*){} // Sample 2 #include // glm::vec3 #include // glm::vec4, glm::ivec4 #include // glm::mat4 #include // glm::translate, glm::rotate, glm::scale, glm::perspective #include // glm::value_ptr void func(GLuint LocationMVP, float Translate, glm::vec2 const & Rotate) { glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.f); glm::mat4 ViewTranslate = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -Translate)); glm::mat4 ViewRotateX = glm::rotate(ViewTranslate, Rotate.y, glm::vec3(-1.0f, 0.0f, 0.0f)); glm::mat4 View = glm::rotate(ViewRotateX, Rotate.x, glm::vec3(0.0f, 1.0f, 0.0f)); glm::mat4 Model = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f)); glm::mat4 MVP = Projection * View * Model; glUniformMatrix4fv(LocationMVP, 1, GL_FALSE, glm::value_ptr(MVP)); } // Sample 3 #include // glm::vec2 #include // glm::packUnorm2x16 #include // glm::uint #include // glm::i8vec2, glm::i32vec2 std::size_t const VertexCount = 4; // Float quad geometry std::size_t const PositionSizeF32 = VertexCount * sizeof(glm::vec2); glm::vec2 const PositionDataF32[VertexCount] = { glm::vec2(-1.0f,-1.0f), glm::vec2( 1.0f,-1.0f), glm::vec2( 1.0f, 1.0f), glm::vec2(-1.0f, 1.0f) }; // Half-float quad geometry std::size_t const PositionSizeF16 = VertexCount * sizeof(glm::uint); glm::uint const PositionDataF16[VertexCount] = { glm::uint(glm::packUnorm2x16(glm::vec2(-1.0f, -1.0f))), glm::uint(glm::packUnorm2x16(glm::vec2( 1.0f, -1.0f))), glm::uint(glm::packUnorm2x16(glm::vec2( 1.0f, 1.0f))), glm::uint(glm::packUnorm2x16(glm::vec2(-1.0f, 1.0f))) }; // 8 bits signed integer quad geometry std::size_t const PositionSizeI8 = VertexCount * sizeof(glm::i8vec2); glm::i8vec2 const PositionDataI8[VertexCount] = { glm::i8vec2(-1,-1), glm::i8vec2( 1,-1), glm::i8vec2( 1, 1), glm::i8vec2(-1, 1) }; // 32 bits signed integer quad geometry std::size_t const PositionSizeI32 = VertexCount * sizeof(glm::i32vec2); glm::i32vec2 const PositionDataI32[VertexCount] = { glm::i32vec2 (-1,-1), glm::i32vec2 ( 1,-1), glm::i32vec2 ( 1, 1), glm::i32vec2 (-1, 1) }; struct intersection { glm::vec4 position; glm::vec3 normal; }; /* // Sample 4 #include // glm::vec3 #include // glm::normalize, glm::dot, glm::reflect #include // glm::pow #include // glm::vecRand3 glm::vec3 lighting ( intersection const & Intersection, material const & Material, light const & Light, glm::vec3 const & View ) { glm::vec3 Color(0.0f); glm::vec3 LightVertor(glm::normalize( Light.position - Intersection.position + glm::vecRand3(0.0f, Light.inaccuracy)); if(!shadow(Intersection.position, Light.position, LightVertor)) { float Diffuse = glm::dot(Intersection.normal, LightVector); if(Diffuse <= 0.0f) return Color; if(Material.isDiffuse()) Color += Light.color() * Material.diffuse * Diffuse; if(Material.isSpecular()) { glm::vec3 Reflect(glm::reflect( glm::normalize(-LightVector), glm::normalize(Intersection.normal))); float Dot = glm::dot(Reflect, View); float Base = Dot > 0.0f ? Dot : 0.0f; float Specular = glm::pow(Base, Material.exponent); Color += Material.specular * Specular; } } return Color; } */ int main() { return 0; } rtabmap-0.22.1/app/android/jni/third-party/include/glm/detail/func_common.hpp000066400000000000000000000535161503504370700271470ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////////////// /// OpenGL Mathematics (glm.g-truc.net) /// /// Copyright (c) 2005 - 2014 G-Truc Creation (www.g-truc.net) /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal /// in the Software without restriction, including without limitation the rights /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell /// copies of the Software, and to permit persons to whom the Software is /// furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN /// THE SOFTWARE. /// /// @ref core /// @file glm/core/func_common.hpp /// @date 2008-03-08 / 2010-01-26 /// @author Christophe Riccio /// /// @see GLSL 4.20.8 specification, section 8.3 Common Functions /// /// @defgroup core_func_common Common functions /// @ingroup core /// /// These all operate component-wise. The description is per component. /////////////////////////////////////////////////////////////////////////////////// #pragma once #include "setup.hpp" #include "precision.hpp" #include "type_int.hpp" #include "_fixes.hpp" namespace glm { /// @addtogroup core_func_common /// @{ /// Returns x if x >= 0; otherwise, it returns -x. /// /// @tparam genType floating-point or signed integer; scalar or vector types. /// /// @see GLSL abs man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType abs(genType const & x); /// Returns 1.0 if x > 0, 0.0 if x == 0, or -1.0 if x < 0. /// /// @tparam genType Floating-point or signed integer; scalar or vector types. /// /// @see GLSL sign man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType sign(genType const & x); /// Returns a value equal to the nearest integer that is less then or equal to x. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL floor man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType floor(genType const & x); /// Returns a value equal to the nearest integer to x /// whose absolute value is not larger than the absolute value of x. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL trunc man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType trunc(genType const & x); /// Returns a value equal to the nearest integer to x. /// The fraction 0.5 will round in a direction chosen by the /// implementation, presumably the direction that is fastest. /// This includes the possibility that round(x) returns the /// same value as roundEven(x) for all values of x. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL round man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType round(genType const & x); /// Returns a value equal to the nearest integer to x. /// A fractional part of 0.5 will round toward the nearest even /// integer. (Both 3.5 and 4.5 for x will return 4.0.) /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL roundEven man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions /// @see New round to even technique template GLM_FUNC_DECL genType roundEven(genType const & x); /// Returns a value equal to the nearest integer /// that is greater than or equal to x. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL ceil man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType ceil(genType const & x); /// Return x - floor(x). /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL fract man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType fract(genType const & x); /// Modulus. Returns x - y * floor(x / y) /// for each component in x using the floating point value y. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL mod man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType mod( genType const & x, genType const & y); /// Modulus. Returns x - y * floor(x / y) /// for each component in x using the floating point value y. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL mod man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType mod( genType const & x, typename genType::value_type const & y); /// Returns the fractional part of x and sets i to the integer /// part (as a whole number floating point value). Both the /// return value and the output parameter will have the same /// sign as x. /// /// @tparam genType Floating-point scalar or vector types. /// /// @see GLSL modf man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType modf( genType const & x, genType & i); /// Returns y if y < x; otherwise, it returns x. /// /// @tparam genType Floating-point or integer; scalar or vector types. /// /// @see GLSL min man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType min( genType const & x, genType const & y); template GLM_FUNC_DECL genType min( genType const & x, typename genType::value_type const & y); /// Returns y if x < y; otherwise, it returns x. /// /// @tparam genType Floating-point or integer; scalar or vector types. /// /// @see GLSL max man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType max( genType const & x, genType const & y); template GLM_FUNC_DECL genType max( genType const & x, typename genType::value_type const & y); /// Returns min(max(x, minVal), maxVal) for each component in x /// using the floating-point values minVal and maxVal. /// /// @tparam genType Floating-point or integer; scalar or vector types. /// /// @see GLSL clamp man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType clamp( genType const & x, genType const & minVal, genType const & maxVal); template GLM_FUNC_DECL genType clamp( genType const & x, typename genType::value_type const & minVal, typename genType::value_type const & maxVal); /// If genTypeU is a floating scalar or vector: /// Returns x * (1.0 - a) + y * a, i.e., the linear blend of /// x and y using the floating-point value a. /// The value for a is not restricted to the range [0, 1]. /// /// If genTypeU is a boolean scalar or vector: /// Selects which vector each returned component comes /// from. For a component of that is false, the /// corresponding component of x is returned. For a /// component of a that is true, the corresponding /// component of y is returned. Components of x and y that /// are not selected are allowed to be invalid floating point /// values and will have no effect on the results. Thus, this /// provides different functionality than /// genType mix(genType x, genType y, genType(a)) /// where a is a Boolean vector. /// /// @see GLSL mix man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions /// /// @param[in] x Value to interpolate. /// @param[in] y Value to interpolate. /// @param[in] a Interpolant. /// /// @tparam genTypeT Floating point scalar or vector. /// @tparam genTypeU Floating point or boolean scalar or vector. It can't be a vector if it is the length of genTypeT. /// /// @code /// #include /// ... /// float a; /// bool b; /// glm::dvec3 e; /// glm::dvec3 f; /// glm::vec4 g; /// glm::vec4 h; /// ... /// glm::vec4 r = glm::mix(g, h, a); // Interpolate with a floating-point scalar two vectors. /// glm::vec4 s = glm::mix(g, h, b); // Teturns g or h; /// glm::dvec3 t = glm::mix(e, f, a); // Types of the third parameter is not required to match with the first and the second. /// glm::vec4 u = glm::mix(g, h, r); // Interpolations can be perform per component with a vector for the last parameter. /// @endcode template class vecType> GLM_FUNC_DECL vecType mix( vecType const & x, vecType const & y, vecType const & a); template class vecType> GLM_FUNC_DECL vecType mix( vecType const & x, vecType const & y, U const & a); template GLM_FUNC_DECL genTypeT mix( genTypeT const & x, genTypeT const & y, genTypeU const & a); /// Returns 0.0 if x < edge, otherwise it returns 1.0 for each component of a genType. /// /// @see GLSL step man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template GLM_FUNC_DECL genType step( genType const & edge, genType const & x); /// Returns 0.0 if x < edge, otherwise it returns 1.0. /// /// @see GLSL step man page /// @see GLSL 4.20.8 specification, section 8.3 Common Functions template