diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..8cdd84d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,57 @@
+name: Bug Report
+description: File a bug report
+title: "[Bug]: "
+labels: ["bug"]
+assignees:
+ - TheWillard
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Loaded Mods
+ description: Please confirm that all required mods are actually loaded.
+ options:
+ - label: I checked that `@grad_meh` is loaded
+ required: true
+ - label: I checked that `@cba_a3` is loaded
+ required: true
+ - label: I checked that `@intcept` is loaded
+ required: true
+ - type: checkboxes
+ id: battleye
+ attributes:
+ label: Loaded Mods
+ description: BattlEye can cause problems with grad_meh. Make sure it is disabled!
+ options:
+ - label: I disabled BattlEye
+ required: true
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: What happened?
+ description: Also tell us what did you expected to happen, and include the workshop links to relevant maps.
+ placeholder: Tell us what you see!
+ value: "A bug happened!"
+ validations:
+ required: true
+ - type: textarea
+ id: rpt
+ attributes:
+ label: RPT
+ description: Please copy and paste the relevant RPT. You can find it in the following directory `%localappdata%\Arma 3`
+ render: shell
+ validations:
+ required: true
+ - type: textarea
+ id: logs
+ attributes:
+ label: grad_meh Logs
+ description: Please copy and paste the relevant grad_meh log. You can find it in the following directory `%localappdata%\Arma 3`
+ render: shell
+ validations:
+ required: true
+
diff --git a/.github/actions/update-versionfile/Dockerfile b/.github/actions/update-versionfile/Dockerfile
deleted file mode 100644
index c68cc8d..0000000
--- a/.github/actions/update-versionfile/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM alpine:3.10
-
-COPY update-versionfile.sh /update-versionfile.sh
-
-RUN apk update && apk upgrade && \
- apk add --no-cache bash git openssh
-
-RUN apk add --update bash && rm -rf /var/cache/apk/*
-
-ENTRYPOINT ["/update-versionfile.sh"]
\ No newline at end of file
diff --git a/.github/actions/update-versionfile/action.yml b/.github/actions/update-versionfile/action.yml
deleted file mode 100644
index 46560f8..0000000
--- a/.github/actions/update-versionfile/action.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-runs:
- using: 'docker'
- image: 'Dockerfile'
diff --git a/.github/actions/update-versionfile/update-versionfile.sh b/.github/actions/update-versionfile/update-versionfile.sh
deleted file mode 100644
index 91a7f9d..0000000
--- a/.github/actions/update-versionfile/update-versionfile.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash -l
-
-echo "versionfile updater. looking for tagā¦"
-version=$(git describe --always --tag)
-
-versionfilePath="./addons/main/script_version.hpp"
-
-IFS='.-' read -ra versionbits <<< ${version}
-major=${versionbits[0]}
-minor=${versionbits[1]}
-patch=${versionbits[2]}
-build=${versionbits[3]}
-commit=$(echo ${versionbits[4]} | tr -d g)
-
-re='^[0-9]+$'
-if ! [[ ${major} =~ $re && ${minor} =~ $re && ${patch} =~ $re ]] ; then
- echo "not a release tag: '$version', not writing versionfile."
- exit
-fi
-
-echo "we're on version $major.$minor.$patch, build $build (commit $commit ). updating versionfileā¦"
-
-printf "#define MAJOR $major\n#define MINOR $minor\n#define PATCHLVL $patch\n#define BUILD 0" > $versionfilePath
-
-echo "Version file is now:"
-echo $(cat $versionfilePath)
\ No newline at end of file
diff --git a/.github/workflows/add-to-release.yml b/.github/workflows/add-to-release.yml
deleted file mode 100644
index f4758bc..0000000
--- a/.github/workflows/add-to-release.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: 'Add to Release'
-
-on: [release]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@master
- name: 'Checkout the source code from GitHub'
-
- - uses: ./.github/actions/update-versionfile
- name: 'Update script_version.hpp'
-
- - uses: gruppe-adler/action-release-with-hemtt@1.1.2
- name: 'Build Mod with HEMTT'
- id: build
-
- - uses: Shopify/upload-to-release@1.0.0
- name: 'Add to release'
- with:
- name: '${{ steps.build.outputs.zip_name }}.zip'
- path: ${{ steps.build.outputs.zip_path }}
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- content-type: application/zip
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..8649de6
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,75 @@
+name: C/C++ CI
+
+on:
+ push:
+ pull_request:
+
+env:
+ VCPKG_COMMIT: cc97b4536ae749ec0e4f643488b600b217540fb3
+ VCPKG_DIR: C:/deps/vcpkg
+ CMAKE_PRESET: ninja-msvc-vcpkg
+
+jobs:
+ job_build_cpp:
+ name: win
+ runs-on: windows-latest
+
+ steps:
+ # Checkout
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ # Setup MSVC
+ - uses: ilammy/msvc-dev-cmd@v1
+ with:
+ vsversion: 2022
+ toolset: 14
+
+ # Install latest CMake
+ - uses: lukka/get-cmake@latest
+
+ - name: Create folder
+ run: |
+ mkdir ${{ env.VCPKG_DIR }}
+
+ # Setup vcpkg and build deps
+ - name: Restore artifacts, or Run vcpkg, build and cache artifacts
+ uses: lukka/run-vcpkg@v11
+ id: runvcpkg
+ with:
+ vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
+ vcpkgDirectory: ${{ env.VCPKG_DIR }}
+
+ - name: Run CMake consuming CMakePresets.json and vcpkg.json by mean of vcpkg.
+ uses: lukka/run-cmake@v10
+ with:
+ configurePreset: ninja-release-vcpkg-win
+ buildPreset: ninja-msvc-vcpkg-release
+
+ # Build the mod
+ - uses: gruppe-adler/action-release-with-hemtt@v3
+ name: 'Build Mod with HEMTT'
+ id: build
+
+ # Upload the mod
+ - uses: actions/upload-artifact@v1
+ with:
+ name: ${{ steps.build.outputs.mod_name }}
+ path: ${{ steps.build.outputs.release_path }}
+
+ # zip it
+ - uses: papeloto/action-zip@v1
+ with:
+ files: \@${{ steps.build.outputs.mod_name }}/
+ dest: \@${{ steps.build.outputs.mod_name }}.zip
+
+ # release it
+ - uses: "marvinpinto/action-automatic-releases@latest"
+ if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
+ with:
+ repo_token: "${{ secrets.GITHUB_TOKEN }}"
+ prerelease: true
+ files: |
+ \@${{needs.job_build_cpp.outputs.modName}}.zip
diff --git a/.github/workflows/buildMod.yml b/.github/workflows/buildMod.yml
deleted file mode 100644
index 9dbd0cd..0000000
--- a/.github/workflows/buildMod.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: '[CI] Build Mod'
-
-on: [push]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - name: 'Checkout source code'
- uses: actions/checkout@master
- - name: 'Build Mod with HEMTT'
- uses: gruppe-adler/action-release-with-hemtt@1.1.2
- id: build
- - name: 'Upload artifact'
- uses: actions/upload-artifact@master
- with:
- name: 'packed-mod'
- path: ${{ steps.build.outputs.zip_path }}
diff --git a/.gitignore b/.gitignore
index f486cde..f9829f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,7 @@
# Others
.vs
+.vscode
.hemtt
build
*.pbo
@@ -49,6 +50,7 @@ cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
+builds/
# CodeBlocks
*.cbp
@@ -60,4 +62,4 @@ CTestTestfile.cmake
releases/*
keys/*
*.pbo
-*.biprivatekey
\ No newline at end of file
+*.biprivatekey
diff --git a/.gitmodules b/.gitmodules
index c22f4c5..0e2fa03 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "intercept"]
path = intercept
- url = git@github.com:intercept/intercept.git
+ url = https://github.com/intercept/intercept.git
+[submodule "src/rvff-cxx"]
+ path = src/rvff-cxx
+ url = https://github.com/arma-tools/rvff-cxx.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 21862ec..c58aee5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,15 +23,20 @@ message("COMPILER USED: '${CMAKE_CXX_COMPILER_ID}'")
set(CMAKE_CL_64 ${USE_64BIT_BUILD})
if(USE_64BIT_BUILD)
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/win64/")
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/lib64/")
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/lib64/")
else()
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/win32/")
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/lib32/")
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/lib32/")
endif()
+
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
+set(Boost_NO_WARN_NEW_VERSIONS 1)
+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 0000000..8fcf657
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,65 @@
+{
+ "version": 5,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 23,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "ninja-debug-vcpkg-win",
+ "displayName": "Ninja/Windows/Debug/VCPKG Config",
+ "description": "Ninja/Windows/Debug/VCPKG Config",
+ "binaryDir": "${sourceDir}/builds/${presetName}",
+ "generator": "Ninja",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "CMAKE_TOOLCHAIN_FILE": {
+ "type": "FILEPATH",
+ "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
+ },
+ "CMAKE_INSTALL_PREFIX": {
+ "type": "FILEPATH",
+ "value": "${sourceDir}/install"
+ },
+ "USE_64BIT_BUILD": true,
+ "VCPKG_TARGET_TRIPLET": "x64-windows-static",
+ "CMAKE_C_COMPILER": "cl",
+ "CMAKE_CXX_COMPILER": "cl"
+ },
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ }
+ },
+ {
+ "name": "ninja-release-vcpkg-win",
+ "displayName": "Ninja/Windows/Release/VCPKG Config",
+ "inherits": "ninja-debug-vcpkg-win",
+ "description": "Ninja/Windows/Release/VCPKG Config",
+ "generator": "Ninja",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "ninja-msvc-vcpkg-debug",
+ "configurePreset": "ninja-debug-vcpkg-win",
+ "displayName": "Build ninja-msvc-vcpkg (Debug)",
+ "description": "Build ninja-msvc-vcpkg (Debug) Configurations",
+ "targets": "install",
+ "configuration": "Debug"
+ },
+ {
+ "name": "ninja-msvc-vcpkg-release",
+ "configurePreset": "ninja-release-vcpkg-win",
+ "displayName": "Build ninja-msvc-vcpkg (Release)",
+ "description": "Build ninja-msvc-vcpkg Configurations",
+ "targets": "install",
+ "configuration": "Release"
+ }
+ ]
+}
diff --git a/README.md b/README.md
index 3112ee8..93389fc 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,12 @@ Download the latest version of the mod from our [releases page](https://github.c
## Usage
Although we would say that using `grad_meh` is intuitive and mostly self-explanatory, we made this short video on how to use it.
-**HINT**: If you have any problems with the export not starting, try disabling BattlEye.
+
+## Troubleshooting
+- Make sure that `@grad_meh`, `@intercept` and `@CBA_A3` are loaded.
+- Make sure that BattlEye is disabled.
+- If you're still having problems check the rpt and grad_meh log files located in `C:\Users\\AppData\Local\Arma 3` for any errors.
+- If all else fails, open a new issue and make sure you include the rpt and grad_meh log files.
## Limitations
- To reduce complexity we only allow one export process at a time.
diff --git a/addons/main/script_version.hpp b/addons/main/script_version.hpp
index 49ca1d0..9ba59aa 100644
--- a/addons/main/script_version.hpp
+++ b/addons/main/script_version.hpp
@@ -1,4 +1,4 @@
-#define MAJOR 1
-#define MINOR 0
-#define PATCHLVL 0
-#define BUILD 0
\ No newline at end of file
+#define MAJOR 0
+#define MINOR 7
+#define PATCHLVL 1
+#define BUILD 8
diff --git a/cmake/version.cmake b/cmake/version.cmake
index 0b2b6f3..384f5a0 100644
--- a/cmake/version.cmake
+++ b/cmake/version.cmake
@@ -1,6 +1,30 @@
# From https://www.mattkeeter.com/blog/2018-01-06-versioning/
-execute_process(COMMAND git log --pretty=format:'%h' -n 1
+message("Update script_version.hpp")
+execute_process(COMMAND ${BASH} ./scripts/update-versionfile.sh)
+
+message("Writing version.cpp")
+
+find_program(GIT "git")
+
+if(GIT)
+ message("Found git at:")
+ message(${GIT})
+else()
+ message("Did not find git!")
+endif()
+
+find_program(BASH "bash")
+if(BASH)
+ message("Found bash at:")
+ message(${BASH})
+else()
+ message("Did not find bash!")
+endif()
+
+# Write version.cpp
+
+execute_process(COMMAND ${GIT} log --pretty=format:'%h' -n 1
OUTPUT_VARIABLE GIT_REV
ERROR_QUIET)
@@ -14,17 +38,17 @@ if ("${GIT_REV}" STREQUAL "")
set(GIT_BRANCH "N/A")
else()
execute_process(
- COMMAND bash -c "git diff --quiet --exit-code || echo +"
+ COMMAND ${BASH} -c "git diff --quiet --exit-code || echo +"
OUTPUT_VARIABLE GIT_DIFF)
execute_process(
- COMMAND git describe --exact-match --tags
+ COMMAND ${GIT} describe --exact-match --tags
OUTPUT_VARIABLE GIT_TAG ERROR_QUIET)
execute_process(
- COMMAND git rev-parse --abbrev-ref HEAD
+ COMMAND ${GIT} rev-parse --abbrev-ref HEAD
OUTPUT_VARIABLE GIT_BRANCH)
execute_process(
- COMMAND git describe --tags --abbrev=0 --match v*
+ COMMAND ${GIT} describe --tags --abbrev=0 --match v*
OUTPUT_VARIABLE GIT_LAST_VERSION
)
@@ -54,3 +78,53 @@ endif()
if (NOT "${VERSION}" STREQUAL "${VERSION_}")
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/version.cpp "${VERSION}")
endif()
+
+# Write script_version.hpp
+if ("${GIT_REV}" STREQUAL "")
+ set(VERSION_MAJOR "0")
+ set(VERSION_MINOR "0")
+ set(VERSION_PATCH "0")
+ set(VERSION_BUILD "0")
+else()
+ execute_process(
+ COMMAND ${GIT} describe --tag --always
+ OUTPUT_VARIABLE GIT_TAG ERROR_QUIET)
+ string(REGEX REPLACE "^v" "" GIT_TAG "${GIT_TAG}")
+ message("Using git tag ${GIT_TAG}")
+
+ string(REPLACE "-" ";" GIT_TAG_LIST ${GIT_TAG})
+ list(LENGTH GIT_TAG_LIST GIT_TAG_LIST_LENGTH)
+
+ list(GET GIT_TAG_LIST 0 VERSION_TAG)
+ if(GIT_TAG_LIST_LENGTH GREATER 1)
+ list(GET GIT_TAG_LIST 1 VERSION_BUILD)
+ else()
+ set(VERSION_BUILD "0")
+ endif()
+ message("Tag: ${VERSION_TAG}")
+ message("Build: ${VERSION_BUILD}")
+ message("")
+ string(REPLACE "." ";" GIT_VERSION_LIST ${VERSION_TAG})
+ list(GET GIT_VERSION_LIST 0 VERSION_MAJOR)
+ list(GET GIT_VERSION_LIST 1 VERSION_MINOR)
+ list(GET GIT_VERSION_LIST 2 VERSION_PATCH)
+endif()
+
+set(VERSION "#define MAJOR ${VERSION_MAJOR}
+#define MINOR ${VERSION_MINOR}
+#define PATCHLVL ${VERSION_PATCH}
+#define BUILD ${VERSION_BUILD}
+")
+
+if(EXISTS ${SCRIPTVERSION_PATH})
+ file(READ ${SCRIPTVERSION_PATH} VERSION_)
+else()
+ set(VERSION_ "")
+endif()
+
+message("Version: " ${VERSION})
+message("script_version.hpp Path: " ${SCRIPTVERSION_PATH})
+
+if (NOT "${VERSION}" STREQUAL "${VERSION_}")
+ file(WRITE ${SCRIPTVERSION_PATH} "${VERSION}")
+endif()
diff --git a/docs/geojson_spec.md b/docs/geojson_spec.md
index 2cbd2f5..d1ca944 100644
--- a/docs/geojson_spec.md
+++ b/docs/geojson_spec.md
@@ -81,3 +81,14 @@ Bridges can be split in the same categories as [roads](#11-roads). Like [roads](
- File: `roads/-bridge.geojson.gz`
- Geometry-Type: [Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6)
- Properties: none
+
+## 13. Rivers
+- File: `river.geojson.gz`
+- Geometry-Type: [Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6)
+- Properties: none
+
+## 14. Mounts
+- File: `mounts.geojson.gz`
+- Geometry-Type: [Point](https://tools.ietf.org/html/rfc7946#section-3.1.2)
+- Properties:
+ - `elevation`: The height of the location
\ No newline at end of file
diff --git a/hemtt.toml b/hemtt.toml
index 2997e3e..6557f91 100644
--- a/hemtt.toml
+++ b/hemtt.toml
@@ -6,9 +6,18 @@ files = [
"mod.cpp",
"Adler.paa",
"LICENSE",
- "README.md",
- "*.dll",
- "*.pdb"
+ "README.md"
]
key_name = "{{prefix}}-{{version}}"
-sig_name = "{{prefix}}-{{version}}"
\ No newline at end of file
+authority = "{{version}}"
+
+releasebuild = [
+ "!copyInterceptPlugin"
+]
+
+[scripts.copyInterceptPlugin]
+steps_windows = [
+ 'xcopy build\lib64\*.dll releases\\{{version}}\@{{prefix}}\intercept\ /Y /F'
+]
+
+show_output = true
diff --git a/intercept b/intercept
index 12a8c12..7c8b365 160000
--- a/intercept
+++ b/intercept
@@ -1 +1 @@
-Subproject commit 12a8c12992e8dbd5e4e8f1847bb09d079d836921
+Subproject commit 7c8b365d247a3fafad97ac29939ac5139bbfa2d5
diff --git a/rust-cxx.natvis b/rust-cxx.natvis
new file mode 100644
index 0000000..a5b4c27
--- /dev/null
+++ b/rust-cxx.natvis
@@ -0,0 +1,18 @@
+
+
+
+ { (char*)repr[1],s8 }
+ (char*)repr[1],s8
+
+
+ {{size = {repr[2]}}}
+
+ - repr[2]
+ - repr[0]
+
+ repr[2]
+ ($T1*)repr[1]
+
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 96cae3a..eb744eb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.12)
+cmake_minimum_required (VERSION 3.15)
file(GLOB_RECURSE INTERCEPT_PLUGIN_SOURCES *.h *.hpp *.c *.cpp)
@@ -10,28 +10,18 @@ SOURCE_GROUP("src" FILES ${INTERCEPT_PLUGIN_SOURCES})
#Here is a example of a "utilities" subdirectory.
#----Don't change anything below this line
-if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
- set(DEBUG_SUFFIX "d")
-else()
- set(DEBUG_SUFFIX "")
-endif()
-# DXT
-find_path(SQUISH_INCLUDE_DIR squish.h)
-find_library(SQUISH_LIBRARY squish${DEBUG_SUFFIX})
+# fmt
+find_package(fmt CONFIG REQUIRED)
# nlohmann json
find_package(nlohmann_json CONFIG REQUIRED)
-find_package(OpenSSL REQUIRED)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME ON)
-find_package(Boost 1.71.0 COMPONENTS system filesystem thread iostreams)
-
-# LZO
-find_package(lzokay CONFIG REQUIRED)
+find_package(Boost 1.82.0 REQUIRED COMPONENTS system filesystem thread iostreams)
# OIIO
if("${INTERCEPT_LINK_TYPE}" STREQUAL "static")
@@ -40,35 +30,24 @@ if("${INTERCEPT_LINK_TYPE}" STREQUAL "static")
add_definitions(-DGRAD_AFF_USE_OIIO)
endif()
-# Let OIIO find external libsquish
-set (LIBSQUISH_INCLUDES ${SQUISH_INCLUDE_DIR})
-set (LIBSQUISH_LIBRARIES ${SQUISH_LIBRARY})
-
-if (NOT TARGET Libsquish::Libsquish)
- add_library(Libsquish::Libsquish UNKNOWN IMPORTED)
- set_target_properties(Libsquish::Libsquish PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${LIBSQUISH_INCLUDES}")
-
- set_property(TARGET Libsquish::Libsquish APPEND PROPERTY
- IMPORTED_LOCATION "${LIBSQUISH_LIBRARIES}")
-endif ()
-
find_package(OpenEXR CONFIG)
find_package(PNG MODULE)
+
+add_definitions(-DOIIO_STATIC_DEFINE)
find_package(OpenImageIO CONFIG REQUIRED)
+# plog
+find_package(plog CONFIG REQUIRED)
+
# GADL
-find_package(GDAL REQUIRED)
+find_package(GDAL CONFIG REQUIRED)
#PCL
-find_package(PCL 1.3 REQUIRED)
+find_package(PCL CONFIG REQUIRED)
# Clipper
-FIND_PATH(CLIPPER_INCLUDE_DIR polyclipping/clipper.hpp)
-find_library(CLIPPER_LIBRARY NAMES polyclipping)
+find_package(polyclipping CONFIG REQUIRED)
-FIND_PATH(GRAD_AFF_INCLUDE_DIR grad_aff/grad_aff.h HINTS ${GRAD_AFF_PATH} PATH_SUFFIXES include)
-FIND_LIBRARY(GRAD_AFF_LIB_DIR NAMES grad_aff HINTS ${GRAD_AFF_PATH} PATH_SUFFIXES lib)
#include the Intercept headers from the submodule
set(INTERCEPT_CLIENT_PATH "${CMAKE_SOURCE_DIR}/intercept/src/client")
@@ -90,35 +69,60 @@ endif()
file(GLOB INTERCEPT_HOST_SOURCES "${INTERCEPT_CLIENT_PATH}/intercept/client/*.cpp" "${INTERCEPT_CLIENT_PATH}/intercept/client/sqf/*.cpp" "${INTERCEPT_CLIENT_PATH}/intercept/shared/*.cpp")
SOURCE_GROUP("intercept" FILES ${INTERCEPT_HOST_SOURCES})
-include_directories(${Boost_INCLUDE_DIRS})
-include_directories(${GRAD_AFF_INCLUDE_DIR})
-include_directories(${GDAL_INCLUDE_DIR})
-include_directories(${PCL_INCLUDE_DIRS})
-include_directories(${CLIPPER_INCLUDE_DIR})
-
-link_directories(${PCL_LIBRARY_DIRS})
-
# Version file
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
${CMAKE_CURRENT_BINARY_DIR}/_version.cpp
- COMMAND ${CMAKE_COMMAND} -P
- ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/version.cmake)
+ ${CMAKE_CURRENT_BINARY_DIR}/addons/main/script_version.hpp
+ ${CMAKE_CURRENT_BINARY_DIR}/addons/main/_script_version.hpp
+ COMMAND ${CMAKE_COMMAND} -DSCRIPTVERSION_PATH=${CMAKE_CURRENT_SOURCE_DIR}/../addons/main/script_version.hpp -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/version.cmake)
+
+include(FetchContent)
+
+FetchContent_Declare(
+ Corrosion
+ GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
+ GIT_TAG master # Optionally specify a commit hash, version tag or branch here
+)
+# Set any global configuration variables such as `Rust_TOOLCHAIN` before this line!
+FetchContent_MakeAvailable(Corrosion)
+
+# Import targets defined in a package or workspace manifest `Cargo.toml` file
+
+corrosion_import_crate(MANIFEST_PATH rvff-cxx/Cargo.toml)
+corrosion_add_cxxbridge(rust-lib CRATE rvff-cxx MANIFEST_PATH rvff-cxx FILES lib.rs)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_library(${INTERCEPT_PLUGIN_NAME} SHARED ${INTERCEPT_PLUGIN_SOURCES} ${INTERCEPT_HOST_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
target_include_directories(${INTERCEPT_PLUGIN_NAME} PUBLIC ${OPENIMAGEIO_INCLUDE_DIR})
-target_link_libraries(${INTERCEPT_PLUGIN_NAME} ${GDAL_LIBRARIES})
+
+target_link_libraries(${INTERCEPT_PLUGIN_NAME} GDAL::GDAL)
+
target_link_libraries(${INTERCEPT_PLUGIN_NAME} OpenImageIO::OpenImageIO OpenImageIO::OpenImageIO_Util)
+
target_link_libraries(${INTERCEPT_PLUGIN_NAME} ${Boost_LIBRARIES})
target_link_libraries(${INTERCEPT_PLUGIN_NAME} nlohmann_json nlohmann_json::nlohmann_json)
-target_link_libraries(${INTERCEPT_PLUGIN_NAME} lzokay::lzokay)
-target_link_libraries(${INTERCEPT_PLUGIN_NAME} ${SQUISH_LIBRARY})
-target_link_libraries(${INTERCEPT_PLUGIN_NAME} ${GRAD_AFF_LIB_DIR})
-target_link_libraries(${INTERCEPT_PLUGIN_NAME} OpenSSL::SSL OpenSSL::Crypto)
+target_link_libraries(${INTERCEPT_PLUGIN_NAME} fmt::fmt-header-only)
target_link_libraries(${INTERCEPT_PLUGIN_NAME} ${PCL_LIBRARIES})
-target_link_libraries(${INTERCEPT_PLUGIN_NAME} ${CLIPPER_LIBRARY})
+target_link_libraries(${INTERCEPT_PLUGIN_NAME} polyclipping::polyclipping)
+
+target_link_libraries(${INTERCEPT_PLUGIN_NAME} plog::plog)
+
+target_link_libraries(${INTERCEPT_PLUGIN_NAME} rust-lib)
+
+if(MSVC)
+ target_link_libraries(${INTERCEPT_PLUGIN_NAME} ntdll.lib)
+endif()
+
+if(MSVC)
+ # Note: This is required because we use `cxx` which uses `cc` to compile and link C++ code.
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ corrosion_set_env_vars(rvff-cxx "CFLAGS=-MTd" "CXXFLAGS=-MTd")
+ else()
+ corrosion_set_env_vars(rvff-cxx "CFLAGS=-MT" "CXXFLAGS=-MT")
+ endif()
+endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${INTERCEPT_INCLUDE_PATH})
@@ -134,10 +138,18 @@ else()
set(CMAKE_CXX_FLAGS_RELEASE "/MT /Zi /O2 /Ob1 /EHsc /MP") #with debug info
# /FORCE:MULTIPLE requrired to ignore second definition of tiff stuff in gdal (conflicting with oiio?)
target_link_options(${INTERCEPT_PLUGIN_NAME} PUBLIC "/FORCE:MULTIPLE")
- set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "/OPT:REF /DEBUG:FULL")
+ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/DEBUG:FULL /NODEFAULT:LIBCMTD")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "/OPT:REF /DEBUG:FULL /NODEFAULT:LIBCMTD")
endif()
+cmake_host_system_information(RESULT ARMA_PATH QUERY WINDOWS_REGISTRY "HKLM\\SOFTWARE\\WOW6432Node\\Bohemia Interactive\\arma 3" VALUE "main")
+message("Arma Path from Registry: " ${ARMA_PATH})
+
+if("${PLUGIN_FOLDER}" STREQUAL "")
+ SET(PLUGIN_FOLDER "${ARMA_PATH}\\@grad_meh\\intercept")
+endif()
+message("grad_meh plugin folder: " ${PLUGIN_FOLDER})
if(NOT "${PLUGIN_FOLDER}" STREQUAL "")
add_custom_command(TARGET ${INTERCEPT_PLUGIN_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGIN_FOLDER}/${INTERCEPT_PLUGIN_NAME}.dll
@@ -147,3 +159,6 @@ if(NOT "${PLUGIN_FOLDER}" STREQUAL "")
COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGIN_FOLDER}/${INTERCEPT_PLUGIN_NAME}.pdb
)
endif()
+
+install(TARGETS ${INTERCEPT_PLUGIN_NAME} RUNTIME DESTINATION bin)
+
diff --git a/src/SimplePoint.h b/src/SimplePoint.h
index 4c4fbdb..7f893d4 100644
--- a/src/SimplePoint.h
+++ b/src/SimplePoint.h
@@ -3,8 +3,8 @@
#include
struct SimplePoint {
- float_t x = 0, y = 0;
+ double x = 0, y = 0;
SimplePoint() {};
- SimplePoint(float_t x, float_t y) : x(x), y(y) {};
+ SimplePoint(double x, double y) : x(x), y(y) {};
};
\ No newline at end of file
diff --git a/src/area.cpp b/src/area.cpp
index 590dad6..18a2050 100644
--- a/src/area.cpp
+++ b/src/area.cpp
@@ -32,21 +32,27 @@
#include
-
+#include
namespace nl = nlohmann;
-
-void writeArea(grad_aff::Wrp& wrp, fs::path& basePathGeojson, const std::vector>& objectPairs,
+void writeArea(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson, const std::vector>& objectPairs,
uint32_t epsilon, uint32_t minClusterSize, uint32_t buffer, uint32_t simplify, const std::string& name) {
size_t nPoints = 0;
pcl::PointCloud::Ptr cloudPtr(new pcl::PointCloud());
for (auto& objectPair : objectPairs) {
pcl::PointXYZ point;
- point.x = objectPair.first.transformMatrix[3][0];
- point.y = objectPair.first.transformMatrix[3][2];
+ point.x = objectPair.first.transform_matrx._3.x;
+ point.y = objectPair.first.transform_matrx._3.z;
point.z = 0;
+
+ if (/*point.x < 0 || */std::isnan(point.x) || std::isinf(point.x) ||
+ /*point.y < 0 || */std::isnan(point.y) || std::isinf(point.y)) {
+ PLOG_WARNING << fmt::format("[writeArea {}] Skipping point X: {} Y: {}", name, point.x, point.y);
+ continue;
+ }
+
cloudPtr->push_back(point);
nPoints++;
}
@@ -129,7 +135,6 @@ void writeArea(grad_aff::Wrp& wrp, fs::path& basePathGeojson, const std::vector<
return;
}
- //auto multiPolygonPtr = multiPolygon.Simplify(simplify);
auto unionPtr = multiPolygonPtr->UnionCascaded();
if (unionPtr != nullptr) {
multiPolygonPtr = unionPtr;
diff --git a/src/area.h b/src/area.h
index fe80776..1dd18e5 100644
--- a/src/area.h
+++ b/src/area.h
@@ -6,10 +6,9 @@
#include
#include
-#include
-#include
+#include
namespace fs = std::filesystem;
-void writeArea(grad_aff::Wrp& wrp, fs::path& basePathGeojson, const std::vector>& objectPairs,
+void writeArea(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson, const std::vector>& objectPairs,
uint32_t epsilon, uint32_t minClusterSize, uint32_t buffer, uint32_t simplify, const std::string& name);
diff --git a/src/geojsons.cpp b/src/geojsons.cpp
index bca89a2..5bc6a1b 100644
--- a/src/geojsons.cpp
+++ b/src/geojsons.cpp
@@ -89,413 +89,389 @@ void normalizePolygon(nl::json& arr)
}
}
-void writeHouses(grad_aff::Wrp& wrp, std::filesystem::path& basePathGeojson, std::vector modelInfos)
+void writeHouses(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson, std::vector modelInfos)
{
nl::json house = nl::json::array();
- for (auto& mapInfo : wrp.mapInfo) {
- if (mapInfo->mapType == 4) {
- auto mapInfo4Ptr = std::static_pointer_cast(mapInfo);
- nl::json mapFeature;
- mapFeature["type"] = "Feature";
+ std::map objectMap = {};
+ for (auto& object : wrp.objects) {
+ objectMap.insert({ object.object_id, object });
+ }
- auto coordArr = nl::json::array();
- coordArr.push_back(std::vector { mapInfo4Ptr->bounds[0], mapInfo4Ptr->bounds[1] });
- coordArr.push_back(std::vector { mapInfo4Ptr->bounds[2], mapInfo4Ptr->bounds[3] });
- coordArr.push_back(std::vector { mapInfo4Ptr->bounds[6], mapInfo4Ptr->bounds[7] });
- coordArr.push_back(std::vector { mapInfo4Ptr->bounds[4], mapInfo4Ptr->bounds[5] });
- coordArr.push_back(std::vector { mapInfo4Ptr->bounds[0], mapInfo4Ptr->bounds[1] });
-
- normalizePolygon(coordArr);
-
- auto outerArr = nl::json::array();
- outerArr.push_back(coordArr);
-
- mapFeature["geometry"] = { { "type" , "Polygon" }, { "coordinates" , outerArr } };
-
- /**
- * Arma takes the color from the config and modifies it to make sure none of the rgb-values is bigger
- * than 128. This ensures that the houses have a big enough contrast against the backgorund.
- *
- * So we'll check if any of r/g/b is bigger than 128 and if that's the case we'll calculate the
- * factor we need to apply to reduce it to 128. After that we'll just apply that factor to all
- * values, round the result and then we're done.
- *
- * Oh yeah and the opacity / alpha value is just discarded completely. All houses have a opacity of 100%
- *
- * Don't ask me why they don't just multiply/overlay the config colors with a base color... Just BI things I guess
- * - DZ
- */
-
- // r, g, b
- auto color = std::vector{ mapInfo4Ptr->color[2], mapInfo4Ptr->color[1], mapInfo4Ptr->color[0] };
-
- auto maxColorValue = std::max_element(std::begin(color), std::end(color));
-
- // make sure every color-value is below or equal to 128
- if (*maxColorValue > 128) {
- float_t factor = 128.0f / *maxColorValue;
-
- std::transform(color.begin(), color.end(), color.begin(), [factor](uint8_t value) { return (uint8_t)std::round(factor * value); });
- }
+ for (auto& mapInfo : wrp.map_infos_4) {
+ nl::json mapFeature;
+ mapFeature["type"] = "Feature";
- float_t houseHeight = 0.0;
- XYZTriplet housePos = { 0, 0, 0 };
- if (wrp.objectIdMap.find(mapInfo4Ptr->objectId) != wrp.objectIdMap.end()) {
- auto &object = wrp.objectIdMap.at(mapInfo4Ptr->objectId);
+ auto coordArr = nl::json::array();
+ coordArr.push_back(std::vector { mapInfo.bounds.a.x, mapInfo.bounds.a.y });
+ coordArr.push_back(std::vector { mapInfo.bounds.b.x, mapInfo.bounds.b.y });
+ coordArr.push_back(std::vector { mapInfo.bounds.d.x, mapInfo.bounds.d.y });
+ coordArr.push_back(std::vector { mapInfo.bounds.c.x, mapInfo.bounds.c.y });
+ coordArr.push_back(std::vector { mapInfo.bounds.a.x, mapInfo.bounds.a.y });
+
+ normalizePolygon(coordArr);
+
+ auto outerArr = nl::json::array();
+ outerArr.push_back(coordArr);
+
+ mapFeature["geometry"] = { { "type" , "Polygon" }, { "coordinates" , outerArr } };
+
+ /**
+ * Arma takes the color from the config and modifies it to make sure none of the rgb-values is bigger
+ * than 128. This ensures that the houses have a big enough contrast against the backgorund.
+ *
+ * So we'll check if any of r/g/b is bigger than 128 and if that's the case we'll calculate the
+ * factor we need to apply to reduce it to 128. After that we'll just apply that factor to all
+ * values, round the result and then we're done.
+ *
+ * Oh yeah and the opacity / alpha value is just discarded completely. All houses have a opacity of 100%
+ *
+ * Don't ask me why they don't just multiply/overlay the config colors with a base color... Just BI things I guess
+ * - DZ
+ */
+
+ // r, g, b
+ auto color = std::vector{ mapInfo.color[2], mapInfo.color[1], mapInfo.color[0] };
+
+ auto maxColorValue = std::max_element(std::begin(color), std::end(color));
+
+ // make sure every color-value is below or equal to 128
+ if (*maxColorValue > 128) {
+ float_t factor = 128.0f / *maxColorValue;
+
+ std::transform(color.begin(), color.end(), color.begin(), [factor](uint8_t value) { return (uint8_t)std::round(factor * value); });
+ }
- housePos[0] = object.transformMatrix[3][0];
- housePos[1] = object.transformMatrix[3][2];
- housePos[2] = object.transformMatrix[3][1];
+ float_t houseHeight = 0.0;
+ std::array housePos = { 0, 0, 0 };
- if (object.modelIndex < wrp.models.size()) {
- auto &objectModel = modelInfos[object.modelIndex];
+ if (auto objectPair = objectMap.find(mapInfo.object_id); objectPair != objectMap.end()) {
+ auto object = objectPair->second;
- houseHeight = objectModel.bMax[1] - objectModel.bMin[1];
- }
- }
+ housePos[0] = object.transform_matrx._3.x;
+ housePos[1] = object.transform_matrx._3.z;
+ housePos[2] = object.transform_matrx._3.y;
- mapFeature["properties"] = { { "color", color }, { "height", houseHeight }, { "position", housePos } };
- house.push_back(mapFeature);
+ if (object.model_index < wrp.models.size()) {
+ auto& objectModel = modelInfos[object.model_index];
+ houseHeight = objectModel.b_max.y - objectModel.b_min.y;
+ }
}
+
+ mapFeature["properties"] = { { "color", color }, { "height", houseHeight }, { "position", housePos } };
+ house.push_back(mapFeature);
}
writeGZJson("house.geojson.gz", basePathGeojson, house);
}
-void writeObjects(grad_aff::Wrp& wrp, std::filesystem::path& basePathGeojson)
+void writeObjects(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson)
{
- nl::json house = nl::json::array();
- for (auto& mapInfo : wrp.mapInfo) {
- if (mapInfo->mapType == 4) {
- auto mapInfo4Ptr = std::static_pointer_cast(mapInfo);
+ //nl::json house = nl::json::array();
+ //for (auto& mapInfo : wrp.map_infos_4) {
+ // //if (mapInfo->mapType == 4) {
+ // //auto mapInfo4Ptr = std::static_pointer_cast(mapInfo);
- nl::json mapFeature;
- mapFeature["type"] = "Feature";
- auto posArray = nl::json::array();
- posArray.push_back(mapInfo4Ptr->bounds[0]);
- posArray.push_back(mapInfo4Ptr->bounds[1]);
+ // nl::json mapFeature;
+ // mapFeature["type"] = "Feature";
- mapFeature["geometry"] = { { "type" , "Point" }, { "coordinates" , posArray } };
- mapFeature["properties"] = { { "text", std::to_string(mapInfo4Ptr->infoType)} };
+ // auto posArray = nl::json::array();
+ // posArray.push_back(mapInfo.bounds[0]);
+ // posArray.push_back(mapInfo->bounds[1]);
- house.push_back(mapFeature);
- }
- }
- writeGZJson("debug.geojson.gz", basePathGeojson, house);
-}
+ // mapFeature["geometry"] = { { "type" , "Point" }, { "coordinates" , posArray } };
+ // mapFeature["properties"] = { { "text", std::to_string(mapInfo->infoType)} };
-void writeRoads(grad_aff::Wrp& wrp, const std::string& worldName, std::filesystem::path& basePathGeojson, const std::map>>& mapObjects) {
+ // house.push_back(mapFeature);
+ // //}
+ //}
+ //writeGZJson("debug.geojson.gz", basePathGeojson, house);
+}
- client::invoker_lock threadLock;
- auto roadsPath = sqf::get_text(sqf::config_entry(sqf::config_file()) >> "CfgWorlds" >> worldName >> "newRoadsShape");
- threadLock.unlock();
+static std::vector esri_shape_magic = { 0, 0, 0x27, 0x0A };
- // some maps have no roads (e.g. desert)
- if (roadsPath.empty()) {
- return;
- }
+void writeRoads(
+ rvff::cxx::OprwCxx& wrp,
+ const std::string& worldName,
+ std::filesystem::path& basePathGeojson,
+ const std::map>>& mapObjects) {
auto basePathGeojsonTemp = basePathGeojson / "temp";
- if (!fs::exists(basePathGeojsonTemp)) {
- fs::create_directories(basePathGeojsonTemp);
- }
- auto basePathGeojsonRoads = basePathGeojson / "roads";
- if (!fs::exists(basePathGeojsonRoads)) {
- fs::create_directories(basePathGeojsonRoads);
- }
+ try {
+ client::invoker_lock threadLock;
+ auto roadsPath = sqf::get_text(sqf::config_entry(sqf::config_file()) >> "CfgWorlds" >> worldName >> "newRoadsShape");
+ threadLock.unlock();
- auto roadsPbo = grad_aff::Pbo::Pbo(findPboPath(roadsPath).string());
- roadsPbo.readPbo(false);
+ // some maps have no roads (e.g. desert)
+ if (roadsPath.empty()) {
+ return;
+ }
- auto roadsPathDir = ((fs::path)roadsPath).remove_filename().string();
- if (boost::starts_with(roadsPathDir, "\\")) {
- roadsPathDir = roadsPathDir.substr(1);
- }
+ if (!fs::exists(basePathGeojsonTemp)) {
+ fs::create_directories(basePathGeojsonTemp);
+ }
- for (auto& [key, val] : roadsPbo.entries) {
- if (boost::istarts_with((((fs::path)roadsPbo.productEntries["prefix"]) / key).string(), roadsPathDir)) {
- roadsPbo.extractSingleFile(key, basePathGeojsonTemp, false);
+ auto basePathGeojsonRoads = basePathGeojson / "roads";
+ if (!fs::exists(basePathGeojsonRoads)) {
+ fs::create_directories(basePathGeojsonRoads);
}
- }
-#ifdef _WIN32
- char filePath[MAX_PATH + 1];
- GetModuleFileName(HINST_THISCOMPONENT, filePath, MAX_PATH + 1);
-#endif
+ auto roads_pbo = rvff::cxx::create_pbo_reader_path(findPboPath(roadsPath).string());
- auto gdalPath = fs::path(filePath);
- gdalPath = gdalPath.remove_filename();
+ auto roadsPathDir = ((fs::path)roadsPath).remove_filename().string();
+ if (boost::starts_with(roadsPathDir, "\\")) {
+ roadsPathDir = roadsPathDir.substr(1);
+ }
-#ifdef _WIN32
- // remove win garbage
- gdalPath = gdalPath.string().substr(4);
-
- // get appdata path
- fs::path gdalLogPath;
- PWSTR localAppdataPath = NULL;
- if (SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppdataPath) != S_OK) {
- gdalLogPath = "grad_meh_gdal.log";
- }
- else {
- gdalLogPath = (fs::path)localAppdataPath / "Arma 3" / "grad_meh_gdal.log";
- };
- CoTaskMemFree(localAppdataPath);
+ auto prefix = roads_pbo->get_prefix();
-#endif
+ for (auto& entry : roads_pbo->get_pbo().entries) {
+ if (boost::istarts_with((((fs::path)static_cast(prefix)) / static_cast(entry.filename)).string(), roadsPathDir)) {
+ roads_pbo->extract_single_file(static_cast(entry.filename), basePathGeojsonTemp.string(), false);
+ }
+ }
- CPLSetConfigOption("GDAL_DATA", gdalPath.string().c_str());
- CPLSetConfigOption("CPL_LOG", gdalLogPath.string().c_str());
- CPLSetConfigOption("CPL_LOG_ERRORS", "ON");
-
- const char* pszFormat = "GeoJSON";
- GDALDriver* poDriver;
- char** papszMetadata;
- GDALAllRegister();
- poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat);
- if (poDriver == NULL) {
- threadLock.lock();
- sqf::diag_log("Couldn't get the GeoJSON Driver");
- threadLock.unlock();
- return;
- }
- papszMetadata = poDriver->GetMetadata();
+ auto gdalPath = getDllPath();
+#ifdef _WIN32
+ // get appdata path
+ fs::path gdalLogPath;
+ PWSTR localAppdataPath = NULL;
+ if (SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppdataPath) != S_OK) {
+ gdalLogPath = "grad_meh_gdal.log";
+ }
+ else {
+ gdalLogPath = (fs::path)localAppdataPath / "Arma 3" / "grad_meh_gdal.log";
+ };
+ CoTaskMemFree(localAppdataPath);
+#endif
- GDALDatasetH poDatasetH = GDALOpenEx((basePathGeojsonTemp / "roads.shp").string().c_str(), GDAL_OF_VECTOR, NULL, NULL, NULL);
+ CPLSetConfigOption("GDAL_DATA", gdalPath.string().c_str());
+ CPLSetConfigOption("CPL_LOG", gdalLogPath.string().c_str());
+ CPLSetConfigOption("CPL_LOG_ERRORS", "ON");
+
+ const char* pszFormat = "GeoJSON";
+ GDALDriver* poDriver;
+ char** papszMetadata;
+ GDALAllRegister();
+ poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat);
+ if (poDriver == NULL) {
+ PLOG_ERROR << "Couldn't get the GeoJSON Driver";
+ return;
+ }
+ papszMetadata = poDriver->GetMetadata();
- GDALDataset* poDataset = (GDALDataset*)poDatasetH;
- GDALDataset* poDstDS;
- char** papszOptions = NULL;
+ auto shape_path = (basePathGeojsonTemp / "roads.shp").string();
+ auto index_path = (basePathGeojsonTemp / "roads.shx").string();
+ auto dbase_path = (basePathGeojsonTemp / "roads.dbf").string();
- fs::remove(basePathGeojsonTemp / "roads.geojson");
- poDstDS = poDriver->Create((basePathGeojsonTemp / "roads.geojson").string().c_str(),
- poDataset->GetRasterXSize(), poDataset->GetRasterYSize(), poDataset->GetBands().size(), GDT_Byte, papszOptions);
+ try {
+ if (rvff::cxx::check_for_magic_and_decompress_lzss_file(shape_path, esri_shape_magic)) {
+ PLOG_INFO << fmt::format("Decompressed LZSS compressed shape file ({})", shape_path);
+ }
+ if (rvff::cxx::check_for_magic_and_decompress_lzss_file(index_path, esri_shape_magic)) {
+ PLOG_INFO << fmt::format("Decompressed LZSS compressed index file ({})", index_path);
+ }
+ // Magic contains last update date (https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm)
+ if (rvff::cxx::check_for_magic_and_decompress_lzss_file(dbase_path, { 0x03, 0x5F })) {
+ PLOG_INFO << fmt::format("Decompressed LZSS compressed dbase file ({})", dbase_path);
+ }
+ }
+ catch (const rust::Error& ex) {
+ PLOG_WARNING << fmt::format("Exception during magic/lzss check: {})", ex.what());
+ }
+ GDALDatasetH poDatasetH = GDALOpenEx(shape_path.c_str(), GDAL_OF_VECTOR, NULL, NULL, NULL);
- char* options[] = { "-f", "GeoJSON", nullptr };
+ // Invalid shp/shx file (or lzss compressed?)
+ if (poDatasetH == nullptr)
+ {
+ PLOG_ERROR << fmt::format("Couldn't open roads.shp at {}", shape_path);
+ throw std::runtime_error("Couldn't open roads.shp");
+ }
- auto gdalOptions = GDALVectorTranslateOptionsNew(options, NULL);
+ GDALDataset* poDataset = (GDALDataset*)poDatasetH;
+ GDALDataset* poDstDS;
+ char** papszOptions = NULL;
- int error = 0;
- auto dataSet = GDALVectorTranslate((basePathGeojsonTemp / "roads.geojson").string().c_str(), poDstDS, 1, &poDatasetH, gdalOptions, &error);
+ fs::remove(basePathGeojsonTemp / "roads.geojson");
+ poDstDS = poDriver->Create((basePathGeojsonTemp / "roads.geojson").string().c_str(),
+ poDataset->GetRasterXSize(), poDataset->GetRasterYSize(), static_cast(poDataset->GetBands().size()), GDT_Byte, papszOptions);
- if (error != 0) {
- threadLock.lock();
- sqf::diag_log("GDALVectorTranslate failed!");
- threadLock.unlock();
- return;
- }
+ char* options[] = { PSTR("-f"), PSTR("GeoJSON"), nullptr };
- GDALClose(poDatasetH);
- GDALClose(poDstDS);
- GDALVectorTranslateOptionsFree(gdalOptions);
+ auto gdalOptions = GDALVectorTranslateOptionsNew(options, NULL);
- grad_aff::Rap roadConfig;
- roadConfig.parseConfig(basePathGeojsonTemp / "roadslib.cfg");
+ int error = 0;
+ auto dataSet = GDALVectorTranslate((basePathGeojsonTemp / "roads.geojson").string().c_str(), poDstDS, 1, &poDatasetH, gdalOptions, &error);
- // Parse RoadsLib.cfg and construct with:roadType Map
- std::map> roadWidthMap = {};
- for (auto& rootEntry : roadConfig.classEntries) {
- if (rootEntry->type == 0 && boost::iequals(rootEntry->name, "RoadTypesLibrary")) {
- for (auto& roadEntry : std::static_pointer_cast(rootEntry)->classEntries) {
- if (roadEntry->type == 0 && boost::istarts_with(roadEntry->name, "Road")) {
- std::pair roadPair = {};
- for (auto roadValues : std::static_pointer_cast(roadEntry)->classEntries) {
- if (roadValues->type == 1 && boost::iequals(roadValues->name, "width")) {
- if (auto val = std::get_if(&std::static_pointer_cast(roadValues)->value)) {
- roadPair.first = (float_t)*val;
- }
- else {
- roadPair.first = std::get(std::static_pointer_cast(roadValues)->value);
- }
- continue;
- }
- if (roadValues->type == 1 && boost::iequals(roadValues->name, "map")) {
- roadPair.second = boost::replace_all_copy(std::get(std::static_pointer_cast(roadValues)->value), " ", "_");
- continue;
- }
- }
- roadWidthMap.insert({ std::stoi(roadEntry->name.substr(4)), roadPair });
- }
- }
+ if (error != 0) {
+ PLOG_ERROR << "GDALVectorTranslate failed!";
+ return;
}
- }
- // calc additional roads
- std::vector roadTypes = { "road", "main road", "track" };
- auto additionalRoads = std::map>>{};
- for (auto& roadType : roadTypes) {
- if (mapObjects.find(roadType) != mapObjects.end()) {
- for (auto& road : mapObjects.at(roadType)) {
- auto correctedRoadType = roadType;
- if (roadType == "mainroad" || roadType == "main road") {
- // Blame Zade when this blows up
- correctedRoadType = "main_road";
- }
+ GDALClose(poDatasetH);
+ GDALClose(poDstDS);
+ GDALVectorTranslateOptionsFree(gdalOptions);
+
+ std::map> roadWidthMap = {};
- auto r11 = road.first.transformMatrix[0][0];
- auto r13 = road.first.transformMatrix[0][2];
+ try {
+ auto road_config = rvff::cxx::create_cfg_path((basePathGeojsonTemp / "roadslib.cfg").string());
- auto r34 = 0;
- auto r31 = road.first.transformMatrix[2][0];
- auto r32 = road.first.transformMatrix[2][1];
- auto r33 = road.first.transformMatrix[2][2];
+ auto entries = road_config->get_entry_as_entries(std::vector < std::string> { "RoadTypesLibrary" });
+ for (auto& entry : entries)
+ {
+ try {
+ auto _class = entry.get_entry_as_class();
+ auto class_name = static_cast(_class->get_class_name());
+ auto map = static_cast(_class->get_entry_as_string(std::vector < std::string> { "map" }));
+ auto width = _class->get_entry_as_number(std::vector < std::string> { "width" });
- // calculate rotation in RADIANS
- auto theta = std::atan2(-1 * r31, std::sqrt(std::pow(r32, 2) + std::pow(r33, 2)));
+ auto map_fixed = boost::replace_all_copy(map, " ", "_");
+ auto class_name_fixed = std::stoi(class_name.substr(4));
- if (r11 == 1.0f || r11 == -1.0f) {
- theta = std::atan2(r13, r34);
+ std::pair roadPair = {};
+ roadWidthMap.insert({ class_name_fixed, std::make_pair(width, map_fixed) });
}
- else {
- theta = std::atan2(-1 * r31, r11);
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << ex.what();
}
-
- // Corrected center pos
- auto centerCorX = road.first.transformMatrix[3][0] - road.second.bCeneter[0];
- auto centerCorY = road.first.transformMatrix[3][2] - road.second.bCeneter[2];
-
- // calc rotated pos of corner
- // https://gamedev.stackexchange.com/a/86780
-
- /*
- (x_1,y_1) --- (x_2,y_2)
- | |
- | |
- (x_0,y_0) --- (x_3,y_3)
- */
-
- XYZTriplet lb;
- XYZTriplet le;
- XYZTriplet pb;
- XYZTriplet pe;
- std::optional map3Triplet;
- for (auto& namedSelection : road.second.namedSelections) {
- if (namedSelection.selectedName == "lb") {
- lb = road.second.lodPoints[namedSelection.vertexTableIndexes[0]];
- }
- if (namedSelection.selectedName == "le") {
- le = road.second.lodPoints[namedSelection.vertexTableIndexes[0]];
- }
- if (namedSelection.selectedName == "pb") {
- pb = road.second.lodPoints[namedSelection.vertexTableIndexes[0]];
- }
- if (namedSelection.selectedName == "pe") {
- pe = road.second.lodPoints[namedSelection.vertexTableIndexes[0]];
+ }
+ }
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << fmt::format("Exception in roadslib.cfg parsing! Exception {}", ex.what());
+ throw;
+ }
+ // calc additional roads
+ std::vector roadTypes = { "road", "main road", "track" };
+ auto additionalRoads = std::map>>{};
+ for (auto& roadType : roadTypes) {
+ if (mapObjects.find(roadType) != mapObjects.end()) {
+ for (auto& road : mapObjects.at(roadType)) {
+ auto correctedRoadType = roadType;
+ if (roadType == "mainroad" || roadType == "main road") {
+ // Blame Zade when this blows up
+ correctedRoadType = "main_road";
}
- }
- auto x0 = centerCorX + (lb[0] * std::cos(theta)) - (lb[2] * std::sin(theta));
- auto y0 = centerCorY + (lb[0] * std::sin(theta)) + (lb[2] * std::cos(theta));
+ auto r11 = road.first.transform_matrx._0.x;
+ auto r13 = road.first.transform_matrx._0.z;
- auto x1 = centerCorX + (le[0] * std::cos(theta)) - (le[2] * std::sin(theta));
- auto y1 = centerCorY + (le[0] * std::sin(theta)) + (le[2] * std::cos(theta));
+ auto r34 = 0;
+ auto r31 = road.first.transform_matrx._2.x;
+ auto r32 = road.first.transform_matrx._2.y;
+ auto r33 = road.first.transform_matrx._2.z;
- auto x2 = centerCorX + (pe[0] * std::cos(theta)) - (pe[2] * std::sin(theta));
- auto y2 = centerCorY + (pe[0] * std::sin(theta)) + (pe[2] * std::cos(theta));
+ // calculate rotation in RADIANS
+ auto theta = std::atan2(-1 * r31, std::sqrt(std::pow(r32, 2) + std::pow(r33, 2)));
+
+ if (r11 == 1.0f || r11 == -1.0f) {
+ theta = std::atan2(r13, r34);
+ }
+ else {
+ theta = std::atan2(-1 * r31, r11);
+ }
- auto x3 = centerCorX + (pb[0] * std::cos(theta)) - (pb[2] * std::sin(theta));
- auto y3 = centerCorY + (pb[0] * std::sin(theta)) + (pb[2] * std::cos(theta));
+ // Corrected center pos
+ auto centerCorX = road.first.transform_matrx._3.x - road.second.b_center.x;
+ auto centerCorY = road.first.transform_matrx._3.z - road.second.b_center.z;
+
+ // calc rotated pos of corner
+ // https://gamedev.stackexchange.com/a/86780
+
+ /*
+ (x_1,y_1) --- (x_2,y_2)
+ | |
+ | |
+ (x_0,y_0) --- (x_3,y_3)
+ */
+
+ rvff::cxx::XYZTripletCxx lb = {};
+ rvff::cxx::XYZTripletCxx le = {};
+ rvff::cxx::XYZTripletCxx pb = {};
+ rvff::cxx::XYZTripletCxx pe = {};
+ std::optional map3Triplet = {};
+ for (auto& namedSelection : road.second.named_selection) {
+ if (static_cast(namedSelection.name) == "lb") {
+ lb = road.second.vertices[namedSelection.selected_vertices.edges[0]];
+ }
+ if (static_cast(namedSelection.name) == "le") {
+ le = road.second.vertices[namedSelection.selected_vertices.edges[0]];
+ }
+ if (static_cast(namedSelection.name) == "pb") {
+ pb = road.second.vertices[namedSelection.selected_vertices.edges[0]];
+ }
+ if (static_cast(namedSelection.name) == "pe") {
+ pe = road.second.vertices[namedSelection.selected_vertices.edges[0]];
+ }
+ }
- /*
- auto x0 = centerCorX + (road.second.bMin[0] * std::cos(theta)) - (road.second.bMin[2] * std::sin(theta));
- auto y0 = centerCorY + (road.second.bMin[0] * std::sin(theta)) + (road.second.bMin[2] * std::cos(theta));
+ auto x0 = centerCorX + (lb.x * std::cos(theta)) - (lb.z * std::sin(theta));
+ auto y0 = centerCorY + (lb.x * std::sin(theta)) + (lb.z * std::cos(theta));
- auto x1 = centerCorX + (road.second.bMin[0] * std::cos(theta)) - (road.second.bMax[2] * std::sin(theta));
- auto y1 = centerCorY + (road.second.bMin[0] * std::sin(theta)) + (road.second.bMax[2] * std::cos(theta));
+ auto x1 = centerCorX + (le.x * std::cos(theta)) - (le.z * std::sin(theta));
+ auto y1 = centerCorY + (le.x * std::sin(theta)) + (le.z * std::cos(theta));
- auto x2 = centerCorX + (road.second.bMax[0] * std::cos(theta)) - (road.second.bMax[2] * std::sin(theta));
- auto y2 = centerCorY + (road.second.bMax[0] * std::sin(theta)) + (road.second.bMax[2] * std::cos(theta));
+ auto x2 = centerCorX + (pe.x * std::cos(theta)) - (pe.z * std::sin(theta));
+ auto y2 = centerCorY + (pe.x * std::sin(theta)) + (pe.z * std::cos(theta));
- auto x3 = centerCorX + (road.second.bMax[0] * std::cos(theta)) - (road.second.bMin[2] * std::sin(theta));
- auto y3 = centerCorY + (road.second.bMax[0] * std::sin(theta)) + (road.second.bMin[2] * std::cos(theta));
- */
+ auto x3 = centerCorX + (pb.x * std::cos(theta)) - (pb.z * std::sin(theta));
+ auto y3 = centerCorY + (pb.x * std::sin(theta)) + (pb.z * std::cos(theta));
- std::array rectangle = { SimplePoint(x0,y0), SimplePoint(x1,y1), SimplePoint(x2,y2),SimplePoint(x3,y3) };
+ std::array rectangle = { SimplePoint(x0,y0), SimplePoint(x1,y1), SimplePoint(x2,y2),SimplePoint(x3,y3) };
- auto addRoadsResult = additionalRoads.find(correctedRoadType);
- if (addRoadsResult == additionalRoads.end()) {
- additionalRoads.insert({ correctedRoadType, {rectangle} });
- }
- else {
- addRoadsResult->second.push_back(rectangle);
+ auto addRoadsResult = additionalRoads.find(correctedRoadType);
+ if (addRoadsResult == additionalRoads.end()) {
+ additionalRoads.insert({ correctedRoadType, {rectangle} });
+ }
+ else {
+ addRoadsResult->second.push_back(rectangle);
+ }
}
}
}
- }
- std::ifstream inGeoJson(basePathGeojsonTemp / "roads.geojson");
- nl::json j;
- inGeoJson >> j;
- inGeoJson.close();
-
- std::map roadMap = {};
- for (auto& feature : j["features"]) {
- auto coordsUpdate = nl::json::array();
- for (auto& coords : feature["geometry"]["coordinates"]) {
- coords[0] = (float_t)coords[0] - 200000.0f;
- coordsUpdate.push_back(coords);
- }
- feature["geometry"]["coordinates"] = coordsUpdate;
-
- int32_t jId = feature["properties"]["ID"];
- feature["properties"].clear();
- feature["properties"]["width"] = roadWidthMap[jId].first;
-
- //auto ogrGeometry = OGRGeometryFactory::createFromGeoJson(feature["geometry"].dump().c_str());
- //ogrGeometry = ogrGeometry->Buffer(roadWidthMap[jId].first * GRAD_MEH_ROAD_WITH_FACTOR);
-
- //auto ret = ogrGeometry->exportToJson();
- //feature["geometry"] = nl::json::parse(ret);
- //CPLFree(ret);
- //OGRGeometryFactory::destroyGeometry(ogrGeometry);
-
- auto kvp = roadMap.find(roadWidthMap[jId].second);
- if (kvp == roadMap.end()) {
- kvp = roadMap.insert({ roadWidthMap[jId].second, nl::json() }).first;
- }
- kvp->second.push_back(feature);
- }
-
- // separate roads into main_road, track, etc.
- for (auto& [key, value] : roadMap) {
- writeGZJson((key + std::string(".geojson.gz")), basePathGeojsonRoads, value);
- }
-
- for (auto& [key, value] : additionalRoads) {
- nl::json bridge;
-
- for (auto& rectangles : value) {
- nl::json addtionalRoadJson;
-
- nl::json geometry;
- geometry["type"] = "Polygon";
- geometry["coordinates"].push_back({ nl::json::array() });
- for (auto& point : rectangles) {
- nl::json points = nl::json::array();
- points.push_back(point.x);
- points.push_back(point.y);
- geometry["coordinates"][0].push_back(points);
+ std::ifstream inGeoJson(basePathGeojsonTemp / "roads.geojson");
+ nl::json j;
+ inGeoJson >> j;
+ inGeoJson.close();
+
+ std::map roadMap = {};
+ for (auto& feature : j["features"]) {
+ auto coordsUpdate = nl::json::array();
+ for (auto& coords : feature["geometry"]["coordinates"]) {
+ coords[0] = (float_t)coords[0] - 200000.0f;
+ coordsUpdate.push_back(coords);
+ }
+ feature["geometry"]["coordinates"] = coordsUpdate;
+
+ // empty property happens when dbase file is invalid/compressed
+ auto propId = feature["properties"]["ID"];
+ if (!propId.is_number_integer() && !propId.is_number_float()) {
+ PLOG_WARNING << "Skipping feature with 'null' ID";
+ break;
}
- normalizePolygon(geometry["coordinates"][0]);
+ int32_t jId = propId;// feature["properties"]["ID"];
+ feature["properties"].clear();
+ feature["properties"]["width"] = roadWidthMap[jId].first;
- addtionalRoadJson["geometry"] = geometry;
- addtionalRoadJson["properties"] = nl::json::object();
- addtionalRoadJson["type"] = "Feature";
+ auto kvp = roadMap.find(roadWidthMap[jId].second);
+ if (kvp == roadMap.end()) {
+ kvp = roadMap.insert({ roadWidthMap[jId].second, nl::json() }).first;
+ }
+ kvp->second.push_back(feature);
+ }
- bridge.push_back(addtionalRoadJson);
+ // separate roads into main_road, track, etc.
+ for (auto& [key, value] : roadMap) {
+ writeGZJson((key + std::string(".geojson.gz")), basePathGeojsonRoads, value);
}
- writeGZJson((key + std::string("-bridge.geojson.gz")), basePathGeojsonRoads, bridge);
- }
+ for (auto& [key, value] : additionalRoads) {
+ nl::json bridge;
- /*
- for (auto& [key, value] : roadMap) {
- // Append additional roads
- auto kvp = additionalRoads.find(key);
- if (kvp != additionalRoads.end()) {
- for (auto& rectangles : kvp->second) {
+ for (auto& rectangles : value) {
nl::json addtionalRoadJson;
nl::json geometry;
@@ -508,27 +484,34 @@ void writeRoads(grad_aff::Wrp& wrp, const std::string& worldName, std::filesyste
geometry["coordinates"][0].push_back(points);
}
+ normalizePolygon(geometry["coordinates"][0]);
+
addtionalRoadJson["geometry"] = geometry;
addtionalRoadJson["properties"] = nl::json::object();
addtionalRoadJson["type"] = "Feature";
- value.push_back(addtionalRoadJson);
+ bridge.push_back(addtionalRoadJson);
}
+
+ writeGZJson((key + std::string("-bridge.geojson.gz")), basePathGeojsonRoads, bridge);
}
- writeGZJson((key + std::string(".geojson.gz")), basePathGeojsonRoads, value);
+
+ }
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << fmt::format("Exception in writeRoads");
+ PLOG_ERROR << ex.what();
+ throw;
}
- */
// Remove temp dir
fs::remove_all(basePathGeojsonTemp);
}
-void writeSpecialIcons(grad_aff::Wrp& wrp, fs::path& basePathGeojson, uint32_t id, const std::string& name) {
+void writeSpecialIcons(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson, uint32_t id, const std::string& name) {
auto treeLocations = nl::json();
- for (auto& mapInfo : wrp.mapInfo) {
- if (mapInfo->mapType == 1 && mapInfo->infoType == id) {
- auto mapInfo1Ptr = std::static_pointer_cast(mapInfo);
+ for (auto& mapInfo : wrp.map_infos_1) {
+ if (mapInfo.type_id == id) {
auto pointFeature = nl::json();
pointFeature["type"] = "Feature";
@@ -537,8 +520,8 @@ void writeSpecialIcons(grad_aff::Wrp& wrp, fs::path& basePathGeojson, uint32_t i
geometry["type"] = "Point";
auto posArray = nl::json::array();
- posArray.push_back((float_t)mapInfo1Ptr->x);
- posArray.push_back((float_t)mapInfo1Ptr->y);
+ posArray.push_back((float_t)mapInfo.x);
+ posArray.push_back((float_t)mapInfo.y);
geometry["coordinates"] = posArray;
pointFeature["geometry"] = geometry;
@@ -566,7 +549,7 @@ float_t calculateDistance(types::auto_array start, SimpleVect
return 0;
}
- return std::cos(angleInRad) * vec2Magnitude;
+ return static_cast(std::cos(angleInRad) * vec2Magnitude);
}
float_t calculateMaxDistance(types::auto_array ilsPos, types::auto_array ilsDirection,
@@ -589,7 +572,7 @@ float_t calculateMaxDistance(types::auto_array ilsPos, types:
return maxDistance;
}
-nl::json buildRunwayPolygon(sqf::config_entry& runwayConfig) {
+nl::json buildRunwayPolygon(sqf::config_entry &runwayConfig) {
auto ilsPos = sqf::get_array(runwayConfig >> "ilsPosition").to_array();
auto ilsDirection = sqf::get_array(runwayConfig >> "ilsDirection").to_array();
auto taxiIn = sqf::get_array(runwayConfig >> "ilsTaxiIn").to_array();
@@ -607,7 +590,7 @@ nl::json buildRunwayPolygon(sqf::config_entry& runwayConfig) {
auto dx = endPos[0] - startPos[0];
auto dy = endPos[1] - startPos[1];
- auto lineLength = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
+ auto lineLength = static_cast(std::sqrt(std::pow(dx, 2) + std::pow(dy, 2)));
dx /= lineLength;
dy /= lineLength;
@@ -665,7 +648,7 @@ void writeRunways(fs::path& basePathGeojson, const std::string& worldName) {
writeGZJson("runway.geojson.gz", basePathGeojson, runways);
}
-void writeGenericMapTypes(fs::path& basePathGeojson, const std::vector>& objectPairs, const std::string& name) {
+void writeGenericMapTypes(fs::path& basePathGeojson, const std::vector>& objectPairs, const std::string& name) {
if (objectPairs.size() == 0)
return;
@@ -678,8 +661,8 @@ void writeGenericMapTypes(fs::path& basePathGeojson, const std::vectormapType == 5) {
- auto mapInfo5Ptr = std::static_pointer_cast(mapInfo);
- nl::json mapFeature;
- mapFeature["type"] = "Feature";
+ for (auto& mapInfo : wrp.map_infos_5) {
+ nl::json mapFeature;
+ mapFeature["type"] = "Feature";
- auto coordArr = nl::json::array();
- coordArr.push_back({ mapInfo5Ptr->floats[0], mapInfo5Ptr->floats[1] });
- coordArr.push_back({ mapInfo5Ptr->floats[2], mapInfo5Ptr->floats[3] });
+ auto coordArr = nl::json::array();
+ coordArr.push_back({ mapInfo.floats[0], mapInfo.floats[1] });
+ coordArr.push_back({ mapInfo.floats[2], mapInfo.floats[3] });
- mapFeature["geometry"] = { { "type" , "LineString" }, { "coordinates" , coordArr } };
- mapFeature["properties"] = nl::json::object();
+ mapFeature["geometry"] = { { "type" , "LineString" }, { "coordinates" , coordArr } };
+ mapFeature["properties"] = nl::json::object();
- powerline.push_back(mapFeature);
- }
+ powerline.push_back(mapFeature);
}
if (!powerline.empty())
writeGZJson("powerline.geojson.gz", basePathGeojson, powerline);
}
-void writeRailways(fs::path& basePathGeojson, const std::vector>& objectPairs) {
+void writeRailways(fs::path& basePathGeojson, const std::vector>& objectPairs) {
if (objectPairs.size() == 0)
return;
nl::json railways = nl::json::array();
for (auto& objectPair : objectPairs) {
- XYZTriplet map1Triplet;
- XYZTriplet map2Triplet;
- std::optional map3Triplet;
- for (auto& namedSelection : objectPair.second.namedSelections) {
- if (namedSelection.selectedName == "map1") {
- map1Triplet = objectPair.second.lodPoints[namedSelection.vertexTableIndexes[0]];
+ rvff::cxx::XYZTripletCxx map1Triplet;
+ rvff::cxx::XYZTripletCxx map2Triplet;
+ std::optional map3Triplet;
+ for (auto& namedSelection : objectPair.second.named_selection) {
+ rvff::cxx::XYZTripletCxx triplet = {};
+ if (!namedSelection.vertex_indices.empty()) {
+ auto vertex_index = namedSelection.vertex_indices[0];
+ triplet = objectPair.second.vertices[vertex_index];
+ }
+ else if (!namedSelection.selected_vertices.edges.empty()) { // for older p3ds
+ auto vertex_index = namedSelection.selected_vertices.edges[0];
+ triplet = objectPair.second.vertices[vertex_index];
}
- if (namedSelection.selectedName == "map2") {
- map2Triplet = objectPair.second.lodPoints[namedSelection.vertexTableIndexes[0]];
+
+ if (namedSelection.name == "map1") {
+ map1Triplet = triplet;//objectPair.second.normals[vertex_index];
+ }
+ if (namedSelection.name == "map2") {
+ map2Triplet = triplet;//objectPair.second.normals[vertex_index];
}
- if (namedSelection.selectedName == "map3") {
- map3Triplet = objectPair.second.lodPoints[namedSelection.vertexTableIndexes[0]];
+ if (namedSelection.name == "map3") {
+ map3Triplet = triplet;//objectPair.second.normals[vertex_index];
}
}
- auto xPos = objectPair.first.transformMatrix[3][0];
- auto yPos = objectPair.first.transformMatrix[3][2];
+ auto xPos = objectPair.first.transform_matrx._3.x;
+ auto yPos = objectPair.first.transform_matrx._3.z;
- auto r11 = objectPair.first.transformMatrix[0][0];
- auto r13 = objectPair.first.transformMatrix[0][2];
+ auto r11 = objectPair.first.transform_matrx._0.x;
+ auto r13 = objectPair.first.transform_matrx._0.z;
auto r34 = 0;
- auto r31 = objectPair.first.transformMatrix[2][0];
- auto r32 = objectPair.first.transformMatrix[2][1];
- auto r33 = objectPair.first.transformMatrix[2][2];
+ auto r31 = objectPair.first.transform_matrx._2.x;
+ auto r32 = objectPair.first.transform_matrx._2.y;
+ auto r33 = objectPair.first.transform_matrx._2.z;
// calculate rotation in RADIANS
auto theta = std::atan2(-1 * r31, std::sqrt(std::pow(r32, 2) + std::pow(r33, 2)));
@@ -756,11 +746,11 @@ void writeRailways(fs::path& basePathGeojson, const std::vectorexportToJson();
+ river["geometry"] = nl::json::parse(ogrJson);
+ CPLFree(ogrJson);
+ OGRGeometryFactory::destroyGeometry(hull);
+
+ river["properties"] = nl::json::object();
+
+ rivers.push_back(river);
+ }
+
+ writeGZJson("river.geojson.gz", basePathGeojson, rivers);
+}
+
+void writeMounts(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson) {
+
+ auto mounts = nl::json();
+ for (auto& mount : wrp.mountains) {
+ auto pointFeature = nl::json();
+ pointFeature["type"] = "Feature";
+
+ auto geometry = nl::json();
+ geometry["type"] = "Point";
+
+ auto posArray = nl::json::array();
+ posArray.push_back((float_t)mount.x);
+ posArray.push_back((float_t)mount.z);
+ geometry["coordinates"] = posArray;
+
+ pointFeature["geometry"] = geometry;
+ pointFeature["properties"] = nl::json::object();
+ pointFeature["properties"]["elevation"] = mount.y;
+
+ mounts.push_back(pointFeature);
+ }
+ if (!mounts.is_null() && mounts.is_array() && !mounts.empty()) {
+ writeGZJson("mounts.geojson.gz", basePathGeojson, mounts);
+ }
+}
+
+void writeGeojsons(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson, const std::string& worldName)
{
- std::vector modelInfos;
+ using namespace rvff::cxx;
+
+ std::vector modelInfos;
modelInfos.resize(wrp.models.size());
- std::vector> modelMapTypes;
+ std::vector> modelMapTypes;
modelMapTypes.resize(wrp.models.size());
- std::map> pboMap;
+
+ std::map> pboMap;
for (int i = 0; i < wrp.models.size(); i++) {
- auto modelPath = wrp.models[i];
+ auto modelPath = static_cast(wrp.models[i]);
if (boost::starts_with(modelPath, "\\")) {
modelPath = modelPath.substr(1);
}
auto pboPath = findPboPath(modelPath);
+ try {
+ if (pboPath.empty()) {
+ PLOG_WARNING << fmt::format("Couldn't find path for model: {}", modelPath);
+ modelMapTypes[i] = {};
+ modelInfos[i] = {};
+ break;
+ }
- if (pboPath.empty()) {
- modelMapTypes[i] = {};
- modelInfos[i] = {};
- break;
- }
+ if (pboMap.count(pboPath) < 1) {
+ pboMap.emplace(pboPath, std::move(rvff::cxx::create_pbo_reader_path(pboPath.string())));
+ }
- std::shared_ptr pbo = {};
- auto res = pboMap.find(pboPath);
- if (res == pboMap.end()) {
- auto pboPtr = std::make_shared(pboPath.string());
- pboMap.insert({ pboPath, pboPtr });
- pbo = pboPtr;
- }
- else {
- pbo = res->second;
- }
+ int rvffIndex = -1;
+ int affIndex = -1;
- auto p3dData = pbo->getEntryData(modelPath);
+ auto p3dData = pboMap.find(pboPath)->second->get_entry_data(modelPath);
- auto odol = grad_aff::Odol(p3dData);
- odol.readOdol(false);
+ if (p3dData.empty()) {
+ PLOG_WARNING << fmt::format("Couldn't get data for model: {} (PBO: {})", modelPath, pboPath.string());
+ modelMapTypes[i] = {};
+ modelInfos[i] = {};
+ break;
+ }
- odol.peekLodTypes();
+ PLOG_INFO << fmt::format("Reading P3D: {} (PBO: {})", modelPath, pboPath.string());
- auto geoIndex = -1;
- for (int j = 0; j < odol.lods.size(); j++) {
- if (odol.lods[j].lodType == LodType::GEOMETRY) {
- geoIndex = j;
+ if (checkMagic(p3dData, "MLOD")) {
+ PLOG_INFO << fmt::format("Skipping MLOD!");
+ continue;
}
- }
- if (geoIndex != -1) {
- auto retLod = odol.readLod(geoIndex);
+ auto odol_reader = rvff::cxx::create_odol_lazy_reader_vec(p3dData);
+ auto odol2 = odol_reader->get_odol();
- auto findClass = retLod.tokens.find("map");
- if (findClass != retLod.tokens.end()) {
- if (findClass->second == "railway" || findClass->second == "road" || findClass->second == "track" || findClass->second == "main road") {
- geoIndex = -1;
- for (int j = 0; j < odol.lods.size(); j++) {
- if (odol.lods[j].lodType == LodType::SPECIAL_LOD) {
- geoIndex = j;
- break;
- }
- if (geoIndex == -1 && odol.lods[j].lodType == LodType::GEOMETRY) { // Fallback
- geoIndex = j;
+ rvff::cxx::LodCxx lod = {};
+
+ bool foundGeoLod = false;
+ bool foundMemoryLod = false;
+ for (auto& res : odol2.resolutions)
+ {
+ if (res.res == rvff::cxx::ResolutionEnumCxx::Geometry) {
+ foundGeoLod = true;
+ }
+ else if (res.res == rvff::cxx::ResolutionEnumCxx::Memory) {
+ foundMemoryLod = true;
+ }
+ if (foundGeoLod && foundMemoryLod) {
+ break;
+ }
+ }
+
+ if (foundGeoLod)
+ {
+ lod = odol_reader->read_lod(rvff::cxx::ResolutionEnumCxx::Geometry);
+ rvffIndex = 1;
+
+ for (auto& prop : lod.named_properties) {
+ if (static_cast(prop.property) == "map")
+ {
+ auto val = static_cast(prop.value);
+ if ((val == "railway" || val == "road" || val == "track" || val == "main road") && foundMemoryLod) {
+ lod = odol_reader->read_lod(rvff::cxx::ResolutionEnumCxx::Memory);
+ rvffIndex = 2;
}
+
+ modelMapTypes[i] = { static_cast(val), lod };
+ modelInfos[i] = lod;
+ break;
}
- auto memLod = odol.readLod(geoIndex);
- modelMapTypes[i] = { findClass->second, memLod };
- modelInfos[i] = memLod;
- }
- else {
- modelMapTypes[i] = { findClass->second, retLod };
- modelInfos[i] = retLod;
}
}
}
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << fmt::format("Exception while handling models P3D: {} (PBO: {})", modelPath, pboPath.string());
+ PLOG_ERROR << ex.what();
+ throw;
+ }
}
// mapType, [object, lod]
- std::map>> objectMap = {};
- /*
- std::vector forestPositions = {};
- std::vector forestSquarePositions = {};
- std::vector forestTrianglePositions = {};
- std::vector forestBroderPositions = {};
-
- std::vector forestFeulStatPositions = {};
- std::vector forestBroderPositions = {};
- */
+ std::map>> objectMap = {};
+
for (auto& object : wrp.objects) {
- auto mapType = modelMapTypes[object.modelIndex].first;
+ auto mapType = modelMapTypes[object.model_index].first;
auto res = objectMap.find(mapType);
if (res != objectMap.end()) {
- res->second.push_back({ object, modelMapTypes[object.modelIndex].second });
+ res->second.push_back({ object, modelMapTypes[object.model_index].second });
}
else {
- objectMap.insert({ mapType, {{object, modelMapTypes[object.modelIndex].second}} });
- }
- /*
- if (!forestModels[index].empty()) {
- Point_ p;
- p.x = object.transformMatrix[3][0];
- p.y = object.transformMatrix[3][2];
- p.z = 0;
- p.clusterID = UNCLASSIFIED;
- forestPositions.push_back(p);
- }
- */
+ objectMap.insert({ mapType, {{object, modelMapTypes[object.model_index].second}} });
+ }
}
std::vector genericMapTypes = { "bunker", "chapel", "church", "cross", "fuelstation", "lighthouse", "rock", "shipwreck", "transmitter", "tree", "rock", "watertower",
@@ -930,7 +980,10 @@ void writeGeojsons(grad_aff::Wrp& wrp, std::filesystem::path& basePathGeojson, c
writeSpecialIcons(wrp, basePathGeojson, 2, "bush");
writeSpecialIcons(wrp, basePathGeojson, 11, "rock");
writePowerlines(wrp, basePathGeojson);
+ writeMounts(wrp, basePathGeojson);
writeRunways(basePathGeojson, worldName);
+ writeRiver(wrp, basePathGeojson);
+
}
diff --git a/src/geojsons.h b/src/geojsons.h
index 2c6ccf8..13bcf95 100644
--- a/src/geojsons.h
+++ b/src/geojsons.h
@@ -4,15 +4,14 @@
#include "util.h"
+
#include
#include
#include
#include
-#include
-#include
-#include
-#include
+#include
+#include
#include
#include
@@ -29,13 +28,7 @@
#include
#include
#include
-
-// https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483
-EXTERN_C IMAGE_DOS_HEADER __ImageBase;
-#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
-#else
-#error Only Win is supported
-#endif // _WIN32
+#endif
using namespace intercept;
@@ -43,17 +36,19 @@ namespace fs = std::filesystem;
namespace nl = nlohmann;
void writeLocations(const std::string& worldName, std::filesystem::path& basePathGeojson);
-void writeHouses(grad_aff::Wrp& wrp, std::filesystem::path& basePathGeojson);
-void writeObjects(grad_aff::Wrp& wrp, std::filesystem::path& basePathGeojson);
-void writeRoads(grad_aff::Wrp& wrp, const std::string& worldName, std::filesystem::path& basePathGeojson,
- const std::map>>& mapObjects);
-void writeSpecialIcons(grad_aff::Wrp& wrp, fs::path& basePathGeojson, uint32_t id, const std::string& name);
+void writeHouses(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson);
+void writeObjects(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson);
+void writeRoads(rvff::cxx::OprwCxx& wrp, const std::string& worldName, std::filesystem::path& basePathGeojson,
+ const std::map>>& mapObjects);
+void writeSpecialIcons(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson, uint32_t id, const std::string& name);
float_t calculateDistance(types::auto_array start, SimpleVector vector, SimpleVector point);
float_t calculateMaxDistance(types::auto_array ilsPos, types::auto_array ilsDirection,
types::auto_array taxiIn, types::auto_array taxiOff);
nl::json buildRunwayPolygon(sqf::config_entry& runwayConfig);
void writeRunways(fs::path& basePathGeojson, const std::string& worldName);
-void writeGenericMapTypes(fs::path& basePathGeojson, const std::vector>& objectPairs, const std::string& name);
-void writePowerlines(grad_aff::Wrp& wrp, fs::path& basePathGeojson);
-void writeRailways(fs::path& basePathGeojson, const std::vector>& objectPairs);
-void writeGeojsons(grad_aff::Wrp& wrp, std::filesystem::path& basePathGeojson, const std::string& worldName);
\ No newline at end of file
+void writeGenericMapTypes(fs::path& basePathGeojson, const std::vector>& objectPairs, const std::string& name);
+void writePowerlines(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson);
+void writeRailways(fs::path& basePathGeojson, const std::vector>& objectPairs);
+void writeGeojsons(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson, const std::string& worldName);
+void writeRiver(rvff::cxx::OprwCxx& wrp, std::filesystem::path& basePathGeojson);
+void writeMounts(rvff::cxx::OprwCxx& wrp, fs::path& basePathGeojson);
diff --git a/src/main.cpp b/src/main.cpp
index bb22f0f..08949ca 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,6 +6,8 @@
#include "version.h"
+#include
+
// String
#include
#include
@@ -16,6 +18,8 @@
#include
#include
+// #include
+#include
#include
#include
@@ -31,11 +35,14 @@
#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
#include "findPbos.h"
#include "SimplePoint.h"
@@ -43,6 +50,7 @@
#include "../addons/main/status_codes.hpp"
using namespace intercept;
+using namespace OIIO;
namespace fs = std::filesystem;
namespace nl = nlohmann;
@@ -54,11 +62,11 @@ using SQFPar = game_value_parameter;
static bool gradMehIsRunning = false;
-int intercept::api_version() { //This is required for the plugin to work.
+int intercept::api_version() { // This is required for the plugin to work.
return INTERCEPT_SDK_API_VERSION;
}
-void writeMeta(const std::string& worldName, const int32_t& worldSize, fs::path& basePath)
+void writeMeta(const std::string &worldName, const int32_t &worldSize, fs::path &basePath)
{
client::invoker_lock threadLock;
auto mapConfig = sqf::config_entry(sqf::config_file()) >> "CfgWorlds" >> worldName;
@@ -75,13 +83,15 @@ void writeMeta(const std::string& worldName, const int32_t& worldSize, fs::path&
meta["gridOffsetY"] = sqf::get_number(mapConfig >> "Grid" >> "offsetY");
auto colorArray = sqf::get_array(mapConfig >> "OutsideTerrain" >> "colorOutside").to_array();
- if (!colorArray.empty()) {
+ if (!colorArray.empty())
+ {
meta["colorOutside"] = std::vector(colorArray.begin(), colorArray.end());
}
auto gridArray = nl::json::array();
- for (auto& grid : sqf::config_classes("true", (mapConfig >> "Grid"))) {
+ for (auto &grid : sqf::config_classes("true", (mapConfig >> "Grid")))
+ {
auto entry = sqf::config_entry(grid);
nl::json entryGrid;
@@ -103,22 +113,24 @@ void writeMeta(const std::string& worldName, const int32_t& worldSize, fs::path&
out.close();
}
-void writeDem(fs::path& basePath, grad_aff::Wrp& wrp, const int32_t& worldSize)
+void writeDem(fs::path &basePath, rvff::cxx::OprwCxx &wrp, const int32_t &worldSize)
{
- auto cellsize = (float_t)worldSize / wrp.mapSizeX;
+ auto cellsize = (float_t)worldSize / wrp.map_size_x;
std::stringstream demStringStream;
- demStringStream << "ncols " << wrp.mapSizeX << std::endl;
- demStringStream << "nrows " << wrp.mapSizeY << std::endl;
+ demStringStream << "ncols " << wrp.map_size_x << std::endl;
+ demStringStream << "nrows " << wrp.map_size_y << std::endl;
demStringStream << "xllcorner " << 0.0 << std::endl;
demStringStream << "yllcorner " << -cellsize << std::endl;
demStringStream << "cellsize " << cellsize << std::endl; // worldSize / mapsizex
demStringStream << "NODATA_value " << -9999;
demStringStream << std::endl;
- for (int64_t y = wrp.mapSizeY - 1; y >= 0; y--) {
- for (size_t x = 0; x < wrp.mapSizeX; x++) {
- demStringStream << wrp.elevation[x + wrp.mapSizeX * y] << " ";
+ for (int64_t y = wrp.map_size_y - 1; y >= 0; y--)
+ {
+ for (size_t x = 0; x < wrp.map_size_x; x++)
+ {
+ demStringStream << wrp.elevation[x + wrp.map_size_x * y] << " ";
}
demStringStream << std::endl;
}
@@ -132,7 +144,7 @@ void writeDem(fs::path& basePath, grad_aff::Wrp& wrp, const int32_t& worldSize)
demOut.close();
}
-void writePreviewImage(const std::string& worldName, std::filesystem::path& basePath)
+void writePreviewImage(const std::string &worldName, std::filesystem::path &basePath)
{
client::invoker_lock threadLock;
auto mapConfig = sqf::config_entry(sqf::config_file()) >> "CfgWorlds" >> worldName;
@@ -148,20 +160,33 @@ void writePreviewImage(const std::string& worldName, std::filesystem::path& base
}
auto pboPath = findPboPath(picturePath);
-
if (pboPath.empty()) {
return;
}
-
- grad_aff::Pbo prewviewPbo(pboPath.string());
-
- auto paa = grad_aff::Paa();
- paa.readPaa(prewviewPbo.getEntryData(picturePath));
- paa.writeImage((basePath / "preview.png").string());
+ try {
+ auto previewPbo = rvff::cxx::create_pbo_reader_path(pboPath.string());
+ auto previewData = previewPbo->get_entry_data(picturePath);
+ auto previewMipmap = rvff::cxx::get_mipmap_from_paa_vec(previewData, 0);
+
+ auto previewFileName = (basePath / "preview.png").string();
+
+ std::unique_ptr out = ImageOutput::create(previewFileName);
+ if (!out)
+ return;
+ ImageSpec spec(previewMipmap.width, previewMipmap.height, 4, TypeDesc::UINT8);
+ out->open(previewFileName, spec);
+ out->write_image(TypeDesc::UINT8, previewMipmap.data.data());
+ out->close();
+ }
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << fmt::format("Exception in writePreviewImage PBO: {} Picture Path: {}", pboPath.string(), picturePath);
+ PLOG_ERROR << ex.what();
+ throw;
+ }
}
-
-void extractMap(const std::string& worldName, const std::string& worldPath, std::array& steps) {
+void extractMap(const std::string &worldName, const std::string &worldPath, std::array &steps)
+{
auto lowerWorldName = boost::algorithm::to_lower_copy(worldName);
@@ -173,10 +198,11 @@ void extractMap(const std::string& worldName, const std::string& worldPath, std:
startMsg << "Starting export of " << worldName << " [";
startMsg << std::boolalpha;
- for(auto i = 0; i < steps.size(); i++)
+ for (auto i = 0; i < steps.size(); i++)
{
startMsg << steps[i];
- if (i < (steps.size() - 1)) {
+ if (i < (steps.size() - 1))
+ {
startMsg << ", ";
}
}
@@ -184,82 +210,96 @@ void extractMap(const std::string& worldName, const std::string& worldPath, std:
prettyDiagLog(startMsg.str());
- if (!fs::exists(basePath)) {
+ if (!fs::exists(basePath))
+ {
fs::create_directories(basePath);
}
- if (!fs::exists(basePathGeojson)) {
+ if (!fs::exists(basePathGeojson))
+ {
fs::create_directories(basePathGeojson);
}
- if (!fs::exists(basePathSat)) {
+ if (!fs::exists(basePathSat))
+ {
fs::create_directories(basePathSat);
}
std::string curWorldPath = "";
+ try {
+ // Find Wrp Path
+ auto wrpPath = findPboPath(worldPath);
+ curWorldPath = wrpPath.string();
+ auto wrpPboReader = rvff::cxx::create_pbo_reader_path(wrpPath.string());
- // Find Wrp Path
- auto wrpPath = findPboPath(worldPath);
- grad_aff::Pbo wrpPbo(wrpPath.string());
+ auto wrp_data = wrpPboReader->get_entry_data(worldPath);
- auto wrp = grad_aff::Wrp(wrpPbo.getEntryData(worldPath));
- wrp.wrpName = worldName + ".wrp";
- try {
- if (steps[0] || steps[1] || steps[3] || steps[4]) {
+ auto wrp = rvff::cxx::OprwCxx{};
+ // wrp.wrpName = worldName + ".wrp";
+
+ if (steps[0] || steps[1] || steps[3] || steps[4])
+ {
reportStatus(worldName, "read_wrp", "running");
- wrp.readWrp();
+ wrp = rvff::cxx::create_wrp_from_vec(wrp_data);
reportStatus(worldName, "read_wrp", "done");
}
- else {
+ else
+ {
reportStatus(worldName, "read_wrp", "canceled");
}
- }
- catch (std::exception & ex) { // most likely caused by unknown mapinfo type
- client::invoker_lock threadLock;
- prettyDiagLog(std::string("exception while reading the wrp: ").append(ex.what()));
- sqf::hint(ex.what());
- return;
- }
- auto worldSize = (uint32_t)wrp.layerCellSize * wrp.layerSizeX;
+ auto worldSize = (uint32_t)wrp.layer_cell_size * wrp.layer_size_x;
- if (steps[0]) {
- reportStatus(worldName, "write_sat", "running");
- prettyDiagLog("Exporting sat images");
- writeSatImages(wrp, worldSize, basePathSat, worldName);
- reportStatus(worldName, "write_sat", "done");
- }
- if (steps[1]) {
- reportStatus(worldName, "write_houses", "running");
- prettyDiagLog("Exporting geojson");
- writeGeojsons(wrp, basePathGeojson, worldName);
- reportStatus(worldName, "write_houses", "done");
- }
-
- if (steps[2]) {
- reportStatus(worldName, "write_preview", "running");
- prettyDiagLog("Exporting preview image");
- writePreviewImage(worldName, basePath);
- reportStatus(worldName, "write_preview", "done");
- }
-
- if (steps[3]) {
- reportStatus(worldName, "write_meta", "running");
- prettyDiagLog("Exporting meta json");
- writeMeta(worldName, worldSize, basePath);
- reportStatus(worldName, "write_meta", "done");
+ if (steps[0])
+ {
+ reportStatus(worldName, "write_sat", "running");
+ prettyDiagLog("Exporting sat images");
+ writeSatImages(wrp, worldSize, basePathSat, worldName);
+ reportStatus(worldName, "write_sat", "done");
+ }
+ if (steps[1])
+ {
+ reportStatus(worldName, "write_houses", "running");
+ prettyDiagLog("Exporting geojson");
+ writeGeojsons(wrp, basePathGeojson, worldName);
+ reportStatus(worldName, "write_houses", "done");
+ }
+
+ if (steps[2])
+ {
+ reportStatus(worldName, "write_preview", "running");
+ prettyDiagLog("Exporting preview image");
+ writePreviewImage(worldName, basePath);
+ reportStatus(worldName, "write_preview", "done");
+ }
+
+ if (steps[3])
+ {
+ reportStatus(worldName, "write_meta", "running");
+ prettyDiagLog("Exporting meta json");
+ writeMeta(worldName, worldSize, basePath);
+ reportStatus(worldName, "write_meta", "done");
+ }
+
+ if (steps[4])
+ {
+ reportStatus(worldName, "write_dem", "running");
+ prettyDiagLog("Exporting dem file");
+ writeDem(basePath, wrp, worldSize);
+ reportStatus(worldName, "write_dem", "done");
+ }
}
-
- if (steps[4]) {
- reportStatus(worldName, "write_dem", "running");
- prettyDiagLog("Exporting dem file");
- writeDem(basePath, wrp, worldSize);
- reportStatus(worldName, "write_dem", "done");
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << "Exception in extract map command";
+ PLOG_ERROR << fmt::format("WRP Path: {}", curWorldPath);
+ PLOG_ERROR << ex.what();
+ throw;
}
gradMehIsRunning = false;
return;
}
-game_value exportMapCommand(game_state& gs, SQFPar rightArg) {
+game_value exportMapCommand(game_state &gs, SQFPar rightArg)
+{
if (gradMehIsRunning)
return GRAD_MEH_STATUS_ERR_ALREADY_RUNNING;
@@ -272,106 +312,155 @@ game_value exportMapCommand(game_state& gs, SQFPar rightArg) {
// [sat image, houses, preview img, meta.json, dem.asc]
std::array steps = { true, true, true, true, true };
- if (rightArg.type_enum() == game_data_type::STRING) {
+ if (rightArg.type_enum() == game_data_type::STRING)
+ {
worldName = r_string(rightArg).c_str();
}
- else if (rightArg.type_enum() == game_data_type::ARRAY) {
+ else if (rightArg.type_enum() == game_data_type::ARRAY)
+ {
auto parArray = rightArg.to_array();
-
- if (parArray.size() <= 0 || parArray.size() >= 7) {
+
+ if (parArray.size() <= 0 || parArray.size() >= 7)
+ {
gs.set_script_error(iet::assertion_failed, "Wrong amount of arguments!"sv);
return GRAD_MEH_STATUS_ERR_ARGS;
}
- if (parArray[0].type_enum() == game_data_type::STRING) {
+ if (parArray[0].type_enum() == game_data_type::STRING)
+ {
worldName = r_string(parArray[0]);
- for (int i = 1; i < parArray.size(); i++) {
- if (parArray[i].type_enum() == game_data_type::BOOL) {
+ for (int i = 1; i < parArray.size(); i++)
+ {
+ if (parArray[i].type_enum() == game_data_type::BOOL)
+ {
auto b = (bool)parArray[i];
steps[i - 1] = b;
}
- else {
+ else
+ {
gs.set_script_error(iet::assertion_failed, types::r_string("Expected bool at index ").append(std::to_string(i)).append("!"));
return GRAD_MEH_STATUS_ERR_ARGS;
}
}
}
- else {
+ else
+ {
gs.set_script_error(iet::assertion_failed, "First element in the parameter array has to be a string!"sv);
return GRAD_MEH_STATUS_ERR_ARGS;
}
-
}
- else {
+ else
+ {
gs.set_script_error(iet::assertion_failed, "Expected a string or an array!"sv);
return GRAD_MEH_STATUS_ERR_ARGS;
}
auto configWorld = sqf::config_entry(sqf::config_file()) >> "CfgWorlds" >> worldName;
- if (!boost::iequals(sqf::config_name(configWorld), worldName)) {
+ if (!boost::iequals(sqf::config_name(configWorld), worldName))
+ {
gs.set_script_error(iet::assertion_failed, "Couldn't find the specified world!"sv);
return GRAD_MEH_STATUS_ERR_NOT_FOUND;
}
// check for leading /
std::string worldPath = sqf::get_text(configWorld >> "worldName");
- if (boost::starts_with(worldPath, "\\")) {
+ if (boost::starts_with(worldPath, "\\"))
+ {
worldPath = worldPath.substr(1);
}
// try to find pbo
auto wrpPboPath = findPboPath(worldPath);
- if (wrpPboPath == "") {
+ if (wrpPboPath == "")
+ {
return GRAD_MEH_STATUS_ERR_PBO_NOT_FOUND;
}
- else {
- try {
- grad_aff::Pbo wrpPbo(wrpPboPath.string());
- if (!wrpPbo.hasEntry(worldPath))
+ else
+ {
+ try
+ {
+ auto wrpPboReader = rvff::cxx::create_pbo_reader_path(wrpPboPath.string());
+ if (!wrpPboReader->has_entry(worldPath))
return GRAD_MEH_STATUS_ERR_PBO_NOT_FOUND;
- } catch (std::exception& ex) {
+ }
+ catch (std::exception &ex)
+ {
prettyDiagLog(std::string("Exception when opening PBO: ").append(ex.what()));
return GRAD_MEH_STATUS_ERR_PBO_NOT_FOUND;
}
}
- if (!gradMehIsRunning) {
+ if (!gradMehIsRunning)
+ {
gradMehIsRunning = true;
std::thread readWrpThread(extractMap, worldName, worldPath, steps);
readWrpThread.detach();
return GRAD_MEH_STATUS_OK;
}
- else {
+ else
+ {
prettyDiagLog("gradMeh is already running! Aborting!");
return GRAD_MEH_STATUS_ERR_ALREADY_RUNNING;
}
}
-void intercept::pre_start() {
+void intercept::pre_start()
+{
static auto grad_meh_export_map_string =
client::host::register_sqf_command("gradMehExportMap", "Exports the given map", exportMapCommand, game_data_type::SCALAR, game_data_type::STRING);
static auto grad_meh_export_map_array =
client::host::register_sqf_command("gradMehExportMap", "Exports the given map", exportMapCommand, game_data_type::SCALAR, game_data_type::ARRAY);
+#if WIN32
+ std::filesystem::path a3_log_path;
+ PWSTR path_tmp;
+
+ auto get_folder_path_ret = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path_tmp);
+
+ if (get_folder_path_ret == S_OK) {
+ a3_log_path = path_tmp;
+ }
+ CoTaskMemFree(path_tmp);
+
+ a3_log_path = a3_log_path / "Arma 3";
+
+#endif
+
+#if _DEBUG
+ auto severity = plog::Severity::debug;
+#else
+ auto severity = plog::Severity::warning;
+#endif
+
+ plog::init(severity, fmt::format("{}/grad_meh_{:%Y-%m-%d_%H-%M-%S}.log", a3_log_path.string(), std::chrono::system_clock::now()).c_str());
+
+ PLOG_INFO << "Starting PBO Mapping";
+
std::thread mapPopulateThread(populateMap);
mapPopulateThread.detach();
}
-void intercept::pre_init() {
+void intercept::pre_init()
+{
std::stringstream preInitMsg;
preInitMsg << "The grad_meh plugin is running! (";
- if (GRAD_MEH_GIT_TAG.empty()) {
+ if (GRAD_MEH_GIT_TAG.empty())
+ {
preInitMsg << GRAD_MEH_GIT_REV;
}
- else {
+ else
+ {
preInitMsg << GRAD_MEH_GIT_TAG;
}
preInitMsg << "@" << GRAD_MEH_GIT_BRANCH << ")";
intercept::sqf::system_chat(preInitMsg.str());
prettyDiagLog(preInitMsg.str());
+
+ PLOG_INFO << preInitMsg.str();
+
}
diff --git a/src/rvff-cxx b/src/rvff-cxx
new file mode 160000
index 0000000..ba3029d
--- /dev/null
+++ b/src/rvff-cxx
@@ -0,0 +1 @@
+Subproject commit ba3029dbf020789a305a534ee8adc9912383270b
diff --git a/src/satimages.cpp b/src/satimages.cpp
index 1c1b6f7..159f4f7 100644
--- a/src/satimages.cpp
+++ b/src/satimages.cpp
@@ -3,288 +3,327 @@
#include
#include
-using namespace OpenImageIO_v2_3;
+using namespace OpenImageIO_v2_4;
// Range TODO: replace with C++20 Range
#include
-float_t mean(std::vector& equalities)
-{
- float_t mean = 0;
- std::for_each(equalities.begin(), equalities.end(), [&mean](float_t& val) {
- mean += val;
- });
-
- mean /= equalities.size();
- return mean;
-}
-
-float_t compareColors(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2) {
- return (1 - (std::sqrt(std::pow(r2 - r1, 2) + std::pow(g2 - g1, 2) + std::pow(b2 - b1, 2)) / GRAD_MEH_MAX_COLOR_DIF));
-}
-
-float_t compareRows(std::vector::iterator mipmap1It, std::vector::iterator mipmap2It, uint32_t width) {
- std::vector equalities = {};
- for (size_t i = 0; i < width; i++) {
- equalities.push_back(compareColors(*mipmap1It, *(mipmap1It + 1), *(mipmap1It + 2), *mipmap2It, *(mipmap2It + 1), *(mipmap2It + 2)));
- mipmap1It += 4;
- mipmap2It += 4;
- }
-
- return mean(equalities);
-}
-
-uint32_t calcOverLap(MipMap& mipmap1, MipMap& mipmap2) {
- std::vector> bestMatchingOverlaps = {};
- auto filter = mipmap1.height > 512 ? 6 : 3;
-
- for (uint32_t overlap = 1; overlap < (mipmap1.height / filter); overlap++) {
- std::vector equalities = {};
+#include
- for (size_t i = 0; i < overlap; i++) {
- auto beginRowUpper = mipmap1.data.begin() + (mipmap2.height - 1 - (overlap - i)) * (size_t)mipmap1.width * 4;
- auto beginRowLower = mipmap2.data.begin() + i * (size_t)mipmap2.width * 4;
+#include
- equalities.push_back(compareRows(beginRowUpper,
- beginRowLower,
- (uint32_t)mipmap1.width));
- }
-
- auto meanVal = mean(equalities);
- if (meanVal > GRAD_MEH_OVERLAP_THRESHOLD) {
- bestMatchingOverlaps.push_back({ meanVal, overlap });
- }
- }
+struct SatMapTile {
+ fs::path path = {};
+ TileTransform tt = {};
+ rvff::cxx::MipmapCxx mipmap = {};
+};
- uint32_t calculatedOverlap = 0;
- float_t bestOverlap = 0;
+struct FillerMapTile {
+ fs::path path = {};
+ std::set tt = {};
+ rvff::cxx::MipmapCxx mipmap = {};
+};
- for (auto& kvp : bestMatchingOverlaps) {
- if (kvp.first > bestOverlap) {
- bestOverlap = kvp.first;
- calculatedOverlap = kvp.second;
- }
- }
+static const std::vector texture_config_path { "Stage0", "texture" };
+static const std::vector texgen_config_path { "Stage0", "texGen" };
- return calculatedOverlap + 1;
-}
-
-void writeSatImages(grad_aff::Wrp& wrp, const int32_t& worldSize, std::filesystem::path& basePathSat, const std::string& worldName)
+void writeSatImages(rvff::cxx::OprwCxx& wrp, const int32_t& worldSize, std::filesystem::path& basePathSat, const std::string& worldName)
{
std::vector rvmats = {};
- for (auto& rv : wrp.rvmats) {
- if (rv != "") {
- rvmats.push_back(rv);
+ for (auto& rv : wrp.texures) {
+ if (!rv.texture_filename.empty()) {
+ rvmats.push_back(static_cast(rv.texture_filename));
}
}
std::sort(rvmats.begin(), rvmats.end());
if (rvmats.size() > 1) {
- std::vector lcoPaths = {};
- lcoPaths.reserve(rvmats.size());
-
- auto rvmatPbo = grad_aff::Pbo::Pbo(findPboPath(rvmats[0]).string());
- rvmatPbo.readPbo(false);
-
- // Has to be not empty
- std::string lastValidRvMat = "1337";
- std::string fillerTile = "";
- std::string prefix = "s_";
-
- uint32_t maxX2 = 0;
- uint32_t maxY2 = 0;
+ std::vector satMapTiles = {};
+ satMapTiles.reserve(rvmats.size());
+
+ std::string pboPath = "";
+ try {
+ pboPath = findPboPath(rvmats[0]).string();
+ auto rvmatPbo = rvff::cxx::create_pbo_reader_path(pboPath);
+
+ // Has to be not empty
+ std::string lastValidRvMat = "1337";
+ std::optional fillerTile = std::nullopt;
+ std::optional firstPos = std::nullopt;
+ std::string prefix = "s_";
+
+ std::optional> pbo = std::nullopt;
+
+ for (auto& rvmatPath : rvmats) {
+ if (!boost::istarts_with(((fs::path)rvmatPath).filename().string(), lastValidRvMat)) {
+ PLOG_INFO << fmt::format("Getting data for {}", rvmatPath);
+ auto rap_data = rvmatPbo->get_entry_data(rvmatPath);
+ if (rap_data.empty()) {
+ PLOG_ERROR << "Rvmat data was empty!";
+ }
+ PLOG_INFO << fmt::format("Parsing rvmat {}", rvmatPath);
+ auto rap = rvff::cxx::create_cfg_vec(rap_data);
+ auto textureStr = static_cast(rap->get_entry_as_string(texture_config_path));
+ if (!textureStr.empty()) {
+ auto rvmatFilename = ((fs::path)textureStr).filename().string();
+ if (boost::istarts_with(rvmatFilename, prefix)) {
+ lastValidRvMat = rvmatFilename.substr(0, 9);
+
+ auto tt = getTileTransform(rap);
+
+ if (!firstPos.has_value()) {
+ firstPos = tt;
+ }
- // TODO: refactor this when implementing the new rap api
- for (auto& rvmatPath : rvmats) {
+ if (!pbo.has_value()) {
+ pbo = rvff::cxx::create_pbo_reader_path(findPboPath(textureStr).string());
+ }
- std::vector parts;
- boost::split(parts, ((fs::path)rvmatPath).filename().string(), boost::is_any_of("_"));
+ auto data = (*pbo)->get_entry_data(textureStr);
- if (parts.size() > 1) {
- std::vector numbers;
- boost::split(numbers, parts[1], boost::is_any_of("-"));
+ if (data.empty()) {
+ pbo = rvff::cxx::create_pbo_reader_path(findPboPath(textureStr).string());
+ data = (*pbo)->get_entry_data(textureStr);
+ }
- if (numbers.size() > 1) {
- auto x = std::stoi(numbers[0]);
- auto y = std::stoi(numbers[1]);
+ auto mipmap = rvff::cxx::get_mipmap_from_paa_vec(data, 0);
- if (x > maxX2) {
- maxX2 = x;
- }
+ struct SatMapTile tile = { textureStr, tt, mipmap };
+ satMapTiles.push_back(tile);
+ }
+ else {
+ if(!fillerTile.has_value()) {
+ auto fillerPbo = rvff::cxx::create_pbo_reader_path(findPboPath(textureStr).string());
+ auto fillerData = fillerPbo->get_entry_data(textureStr);
+ auto filler_mm = rvff::cxx::get_mipmap_from_paa_vec(fillerData, 0);
+
+ struct FillerMapTile tile = { textureStr, {}, filler_mm };
+ fillerTile = tile;
+ }
- if (y > maxY2) {
- maxY2 = y;
- }
- }
- }
+ auto tt = getTileTransform(rap);
- if (!boost::istarts_with(((fs::path)rvmatPath).filename().string(), lastValidRvMat)) {
- auto rap = grad_aff::Rap::Rap(rvmatPbo.getEntryData(rvmatPath));
- rap.readRap();
-
- for (auto& entry : rap.classEntries) {
- if (boost::iequals(entry->name, "Stage0")) {
- auto rapClassPtr = std::static_pointer_cast(entry);
-
- for (auto& subEntry : rapClassPtr->classEntries) {
- if (boost::iequals(subEntry->name, "texture")) {
- auto textureStr = std::get(std::static_pointer_cast(subEntry)->value);
- auto rvmatFilename = ((fs::path)textureStr).filename().string();
- if (!boost::istarts_with(rvmatFilename, prefix)) {
- if (fillerTile == "") {
- fillerTile = textureStr;
- }
- }
- else {
- lcoPaths.push_back(textureStr);
- lastValidRvMat = rvmatFilename.substr(0, 9);
- }
- break;
+ if (!firstPos.has_value()) {
+ firstPos = tt;
}
+ fillerTile->tt.emplace(tt);
}
}
+
}
}
- }
- // Remove Duplicates
- std::sort(lcoPaths.begin(), lcoPaths.end());
- auto last = std::unique(lcoPaths.begin(), lcoPaths.end());
- lcoPaths.erase(last, lcoPaths.end());
+ // Remove Duplicates
+ std::sort(satMapTiles.begin(), satMapTiles.end(), [](const auto& lhs, const auto& rhs) {
+ return lhs.path.string() < rhs.path.string();
+ });
+ auto last = std::unique(satMapTiles.begin(), satMapTiles.end(), [](const auto& lhs, const auto& rhs) {
+ return lhs.path == rhs.path;
+ });
+ satMapTiles.erase(last, satMapTiles.end());
+
+ auto tileSize = 0;
+
+ // Fix wrong file extensions (cup summer has png extension)
+ for (auto& smt : satMapTiles) {
+ fs::path lcoPathAsPath = smt.path;
+ if (!boost::iequals(lcoPathAsPath.extension().string(), ".paa")) {
+ smt.path = lcoPathAsPath.replace_extension(".paa").string();
+ }
- // Fix wrong file extensions (cup summer has png exten
- for (auto& lcoPath : lcoPaths) {
- fs::path lcoPathAsPath = (fs::path)lcoPath;
- if (!boost::iequals(lcoPathAsPath.extension().string(), ".paa")) {
- lcoPath = lcoPathAsPath.replace_extension(".paa").string();
+ auto mmSize = std::max(smt.mipmap.width, smt.mipmap.height);
+ if (tileSize < mmSize) {
+ tileSize = mmSize;
+ }
}
- }
- // "strechted border" overlap is og overlap/2
+ if (tileSize == 4 && ba::iequals(worldName, "vr")) {
+ tileSize = 1024;
+ }
- // find highest x/y
- std::vector splitResult = {};
- boost::split(splitResult, ((fs::path)lcoPaths[lcoPaths.size() - 1]).filename().string(), boost::is_any_of("_"));
- uint32_t maxX = maxX2; //std::stoi(splitResult[1]);
- uint32_t maxY = maxY2; //std::stoi(splitResult[2]);
+ auto& firstSmt = satMapTiles[0];
- // find two tiles "in the middle"
- uint32_t x = maxX / 2;
- uint32_t y = maxY / 2;
+ auto tileWidth = tileSize;
+ auto tileHeight = tileSize;
- prefix = "00";
+ TileTransform firstTT = firstSmt.tt;
+ if (firstPos.has_value()) {
+ firstTT = *firstPos;
+ }
- if (y > 9 && y < 100) {
- prefix = "0";
- }
- else if (y >= 100) {
- prefix = "";
- }
+ auto firstWorldPos = firstTT.pos;
+ auto firstAside = firstTT.aside;
- std::stringstream upperLcoPathStream;
- upperLcoPathStream << x << "_" << prefix << y << "_lco.paa";
- auto upperPaaPath = upperLcoPathStream.str();
+ auto scaleFactor = firstAside[0] * tileWidth;
- std::stringstream lowerLcoPathStream;
- lowerLcoPathStream << x << "_" << prefix << (y + 1) << "_lco.paa";
- auto lowerPaaPath = lowerLcoPathStream.str();
+ if (scaleFactor != 1) {
+ scaleFactor += 0.5;
- MipMap upperMipmap;
- MipMap lowerMipmap;
+ auto newWidth = static_cast(scaleFactor * tileWidth);
- for (auto& lcoPath : lcoPaths) {
- if (boost::iends_with(lcoPath, upperPaaPath)) {
- auto upperPbo = grad_aff::Pbo::Pbo(findPboPath(lcoPath).string());
- auto upperData = upperPbo.getEntryData(lcoPath);
- auto upperPaa = grad_aff::Paa::Paa();
- upperPaa.readPaa(upperData);
- upperMipmap = upperPaa.mipMaps[0];
- }
- if (boost::iends_with(lcoPath, lowerPaaPath)) {
- auto lowerPbo = grad_aff::Pbo::Pbo(findPboPath(lcoPath).string());
- auto lowerData = lowerPbo.getEntryData(lcoPath);
- auto lowerPaa = grad_aff::Paa::Paa();
- lowerPaa.readPaa(lowerData);
- lowerMipmap = lowerPaa.mipMaps[0];
+ tileWidth = newWidth;
+ tileHeight = newWidth;
}
- }
- auto overlap = calcOverLap(upperMipmap, lowerMipmap);
+ auto firstXPos = static_cast(firstWorldPos[0] * tileWidth);
+ auto firstYPos = static_cast(firstWorldPos[1] * tileHeight);
+
+ int32_t finalSatMapSize = firstYPos - firstXPos;
+
+ ImageBuf dst(ImageSpec(finalSatMapSize, finalSatMapSize, 4, TypeDesc::UINT8));
+ auto& dstSpec = dst.spec();
+
+ if (fillerTile.has_value()) {
+ auto filler_mm = fillerTile->mipmap;
- ImageBuf dst(ImageSpec(upperMipmap.width + maxX * (upperMipmap.width - overlap), upperMipmap.height + maxY * (upperMipmap.height - overlap), 4, TypeDesc::UINT8));
+ auto fillerWidth = filler_mm.width;
+ auto fillerHeight = filler_mm.height;
- if (fillerTile != "") {
- auto fillerPbo = grad_aff::Pbo::Pbo(findPboPath(fillerTile).string());
- auto fillerData = fillerPbo.getEntryData(fillerTile);
- auto fillerPaa = grad_aff::Paa::Paa();
- fillerPaa.readPaa(fillerData);
+ ImageBuf src(ImageSpec(fillerWidth, fillerHeight, 4, TypeDesc::UINT8), filler_mm.data.data());
- ImageBuf src(ImageSpec(fillerPaa.mipMaps[0].height, fillerPaa.mipMaps[0].height, 4, TypeDesc::UINT8), fillerPaa.mipMaps[0].data.data());
+ int32_t numOfSubTiles = tileWidth / filler_mm.width;
- size_t noOfTiles = (worldSize / fillerPaa.mipMaps[0].height);
+ ImageBuf fullFillerTile(ImageSpec(tileWidth, tileHeight, 4, TypeDesc::UINT8));
- auto cr = boost::counting_range(size_t(0), noOfTiles);
- std::for_each(std::execution::par_unseq, cr.begin(), cr.end(), [noOfTiles, &dst, fillerPaa, src](size_t i) {
- for (size_t j = 0; j < noOfTiles; j++) {
- ImageBufAlgo::paste(dst, (i * fillerPaa.mipMaps[0].height), (j * fillerPaa.mipMaps[0].height), 0, 0, src);
+ for (int32_t i = 0; i < numOfSubTiles; i++) {
+ for (int32_t j = 0; j < numOfSubTiles; j++) {
+ ImageBufAlgo::paste(fullFillerTile, i * fillerWidth, j * fillerHeight, 0, 0, src);
+ }
}
- });
- }
- auto pbo = grad_aff::Pbo::Pbo(findPboPath(lcoPaths[0]).string());
+ #ifdef _DEBUG
+ auto copy_filler = fullFillerTile.copy(TypeDesc::UINT8);
+ copy_filler.write((basePathSat / "debug_filler_tile.exr").string());
+ #endif
+
+ auto& fillterTTs = fillerTile->tt;
- for (auto& lcoPath : lcoPaths) {
- auto data = pbo.getEntryData(lcoPath);
+ for (auto& tt : fillterTTs) {
+ auto pos = tt.pos;
+ auto xPos = static_cast(pos[0] * tileWidth * -1);
+ auto yPos = static_cast(((pos[1] * tileHeight) - dstSpec.width) * -1);
+
+ ImageBufAlgo::paste(dst, xPos, yPos, 0, 0, fullFillerTile);
+ }
- if (data.empty()) {
- pbo = grad_aff::Pbo::Pbo(findPboPath(lcoPath).string());
- data = pbo.getEntryData(lcoPath);
+ //#ifdef _DEBUG
+ /*auto copy_full = dst.copy(TypeDesc::UINT8);
+ copy_full.write((basePathSat / "debug_sat_map_only_filler.exr").string());*/
+ //#endif
}
- auto paa = grad_aff::Paa::Paa();
- paa.readPaa(data);
+ //auto dstOG = dst.copy(dst.spec().format);
- std::vector splitResult = {};
- boost::split(splitResult, ((fs::path)lcoPath).filename().string(), boost::is_any_of("_"));
+ int c = 0;
+ for (auto& smt : satMapTiles) {
+ auto mipmap = smt.mipmap;
- ImageBuf src(ImageSpec(paa.mipMaps[0].height, paa.mipMaps[0].height, 4, TypeDesc::UINT8), paa.mipMaps[0].data.data());
+ ImageBuf src(ImageSpec(mipmap.width, mipmap.height, 4, TypeDesc::UINT8), mipmap.data.data());
- uint32_t x = std::stoi(splitResult[1]);
- uint32_t y = std::stoi(splitResult[2]);
+ if (mipmap.width == 4 && mipmap.height == 4) {
+ src = ImageBufAlgo::resize(src, "", 0, ROI(0, tileWidth, 0, tileHeight));
+ PLOG_INFO << fmt::format("4x4 filler method detected, resizing 4x4 tiles to {}x{} !", tileWidth, tileHeight);
+ }
- ImageBufAlgo::paste(dst, (x * paa.mipMaps[0].height - x * overlap), (y * paa.mipMaps[0].height - y * overlap), 0, 0, src);
- }
+ auto srcWidth = src.spec().width;
- // https://github.com/pennyworth12345/A3_MMSI/wiki/Mapframe-Information#stretching-at-edge-tiles
- maxX += 1;
+ auto asideX = smt.tt.aside[0];
+ auto scaleFactor = asideX * srcWidth;
- auto hasEqualStrechting = (worldSize == (upperMipmap.width * maxX)) || (worldSize == ((upperMipmap.width - overlap) * maxX));
- int32_t strechedBorderSizeStart = overlap / 2;
- int32_t strechedBorderSizeEnd = strechedBorderSizeStart;
+ if (scaleFactor != 1) {
+ scaleFactor += 0.5;
- if (!hasEqualStrechting) {
- int32_t tileSizeAfterOverlap = upperMipmap.width - overlap;
- int32_t temp = (tileSizeAfterOverlap * maxX) - worldSize;
- temp -= (overlap / 2);
- int32_t lastTileRealWidth = (tileSizeAfterOverlap - temp) % upperMipmap.width;
- strechedBorderSizeEnd = upperMipmap.width - lastTileRealWidth;
- }
+ auto newWidth = static_cast(scaleFactor * srcWidth);
- dst = ImageBufAlgo::cut(dst, ROI(strechedBorderSizeStart, dst.spec().width - strechedBorderSizeEnd, strechedBorderSizeStart, dst.spec().height - strechedBorderSizeEnd));
- size_t tileSize = dst.spec().width / 4;
+ src = ImageBufAlgo::resize(src, "", 0, ROI(0, newWidth, 0, newWidth));
+ }
- auto cr = boost::counting_range(0, 4);
- std::for_each(std::execution::par_unseq, cr.begin(), cr.end(), [basePathSat, dst, tileSize](int i) {
- auto curWritePath = basePathSat / std::to_string(i);
- if (!fs::exists(curWritePath)) {
- fs::create_directories(curWritePath);
- }
+ auto srcSpec = src.spec();
+
+ auto width = srcSpec.width;
+ auto height = srcSpec.height;
+
+ auto pos = smt.tt.pos;
+
+ auto xPos = static_cast(pos[0] * width * -1);
+ auto yPos = static_cast(((pos[1] * height) - dstSpec.width) * -1);
+
+ /*float red[4] = { 1, 0, 0, 1 };
+ ImageBufAlgo::render_box(src, 0, 0, width-1, height-1, red);*/
+
+ /*auto debugText = fmt::format("rvmat: {}\nxPos: {}\nyPos: {}\nwidth: {}\nheight: {}\nscaleFactor: {}\npos[0] {}\npos[1] {}\naside[0] {}",
+ smt.path.filename().string(), xPos, yPos, width, height, scaleFactor, pos[0], pos[1], asideX);
+ ImageBufAlgo::render_text(src, 50, 50, debugText, 20, "Arial", red, ImageBufAlgo::TextAlignX::Left);*/
+
+ ImageBufAlgo::paste(dst, xPos, yPos, 0, 0, src);
- for (int32_t j = 0; j < 4; j++) {
- ImageBuf out = ImageBufAlgo::cut(dst, ROI(i * tileSize, (i + 1) * tileSize, j * tileSize, (j + 1) * tileSize));
- out.write((curWritePath / std::to_string(j).append(".png")).string());
+ /*auto copy = dstOG.copy(dstOG.spec().format);
+ ImageBufAlgo::paste(copy, xPos, yPos, 0, 0, src);
+ copy.write((basePathSat / fmt::format("debug_part_{}.exr", c++)).string());*/
+
+ /*auto copy_party = dst.copy(TypeDesc::UINT8);
+ copy_party.write((basePathSat / fmt::format("debug_part_{}.exr", c++)).string());*/
}
+
+ //#ifdef _DEBUG
+ /*auto copy = dst.copy(TypeDesc::UINT8);
+ copy.write((basePathSat / "debug_full_streched.exr").string());*/
+ //#endif
+
+ int32_t finalTileSize = dst.spec().width / 4;
+
+ auto cr = boost::counting_range(0, 4);
+ std::for_each(std::execution::par_unseq, cr.begin(), cr.end(), [basePathSat, dst, finalTileSize](int i) {
+ auto curWritePath = basePathSat / std::to_string(i);
+ if (!fs::exists(curWritePath)) {
+ fs::create_directories(curWritePath);
+ }
+
+ for (int32_t j = 0; j < 4; j++) {
+ ImageBuf out = ImageBufAlgo::cut(dst, ROI(i * finalTileSize, (i + 1) * finalTileSize, j * finalTileSize, (j + 1) * finalTileSize));
+ out.write((curWritePath / std::to_string(j).append(".png")).string());
+ }
});
+ }
+ catch (const rust::Error& ex) {
+ PLOG_ERROR << fmt::format("Exception in writeSatImages PBO: {}", pboPath);
+ PLOG_ERROR << ex.what();
+ throw;
+ }
+ }
+}
+
+TileTransform getTileTransform(rust::Box& rap) {
+ auto texGenValue = rap->get_entry_as_number(texgen_config_path);
+
+ auto tex_gen_class = fmt::format("TexGen{}", texGenValue);
+ std::vector texgenDirConfigPath { tex_gen_class, "uvTransform", "pos" };
+ std::vector texgenAsideConfigPath { tex_gen_class, "uvTransform", "aside" };
+
+ auto posArr = rap->get_entry_as_array_float(texgenDirConfigPath);
+ std::vector posArrStd(posArr.begin(), posArr.end());
+
+ if (posArrStd.size() < 2) {
+ auto errMsg = fmt::format("arr size for '{}' was < 2", ba::join(texgenDirConfigPath, " >> "));
+ PLOG_ERROR << errMsg;
+ throw new std::runtime_error(errMsg);
}
-}
\ No newline at end of file
+
+ auto asideArr = rap->get_entry_as_array_float(texgenAsideConfigPath);
+ std::vector asideArrStd(asideArr.begin(), asideArr.end());
+
+ if (asideArrStd.size() < 2) {
+ auto errMsg = fmt::format("arr size for '{}' was < 2", ba::join(texgenAsideConfigPath, " >> "));
+ PLOG_ERROR << errMsg;
+ throw new std::runtime_error(errMsg);
+ }
+
+ TileTransform tt {
+ asideArrStd,
+ posArrStd
+ };
+
+ return tt;
+}
+
diff --git a/src/satimages.h b/src/satimages.h
index 97bc88b..5e004c4 100644
--- a/src/satimages.h
+++ b/src/satimages.h
@@ -5,16 +5,25 @@
#include
#include
#include
+#include
-#include
-#include
-#include
-#include
+#include
namespace fs = std::filesystem;
-float_t mean(std::vector& equalities);
-float_t compareColors(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2);
-float_t compareRows(std::vector::iterator mipmap1It, std::vector::iterator mipmap2It, uint32_t width);
-uint32_t calcOverLap(MipMap& mipmap1, MipMap& mipmap2);
-void writeSatImages(grad_aff::Wrp& wrp, const int32_t& worldSize, std::filesystem::path& basePathSat, const std::string& worldName);
\ No newline at end of file
+struct TileTransform {
+ std::vector aside = {};
+ std::vector pos = {};
+
+};
+
+struct CmpTileTransform
+{
+ bool operator()(const TileTransform& lhs, const TileTransform& rhs) const
+ {
+ return (lhs.aside < rhs.aside) || (rhs.pos < lhs.pos);
+ }
+};
+
+void writeSatImages(rvff::cxx::OprwCxx& wrp, const int32_t& worldSize, std::filesystem::path& basePathSat, const std::string& worldName);
+TileTransform getTileTransform(rust::Box& rap);
diff --git a/src/util.cpp b/src/util.cpp
index ea5c046..f20cfdf 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -7,11 +7,11 @@ static bool gradMehMapIsPopulating = false;
Returns full paths to every *.pbo from mod Paths + A3 Root
*/
std::vector getFullPboPaths() {
- std::vector pboList;
+ std::vector pboList;
#ifdef _WIN32
for (auto& pboW : generate_pbo_list()) {
- pboList.push_back(std::wstring_convert, wchar_t>().to_bytes(pboW).substr(4));
+ pboList.push_back(pboW.substr(4));
}
#else
pboList = generate_pbo_list();
@@ -37,28 +37,31 @@ std::vector getFullPboPaths() {
void populateMap() {
gradMehMapIsPopulating = true;
for (auto& p : getFullPboPaths()) {
- auto pbo = grad_aff::Pbo::Pbo(p.string());
try {
- pbo.readPbo(false);
+ auto pboReader = rvff::cxx::create_pbo_reader_path(p.string());
+ auto pbo = pboReader->get_pbo();
+ for (auto& entry : pbo.entries) {
+ for (auto& prop : pbo.properties) {
+ if (static_cast(prop.key) == "prefix") {
+ entryPboMap.insert({ static_cast(prop.value) , p });
+ break;
+ }
+ }
+ }
}
- catch (std::exception& ex) {
- client::invoker_lock threadLock;
- std::stringstream errorStream;
- errorStream << "[grad_meh] Couldn't open PBO: " << p.string() << " error msg: " << ex.what();
- sqf::diag_log(errorStream.str());
- threadLock.unlock();
- }
- for (auto& entry : pbo.entries) {
- entryPboMap.insert({ pbo.productEntries["prefix"] , p });
+ catch (const rust::Error& ex) {
+ PLOG_WARNING << fmt::format("Couldn't open PBO at: {} (Exception: {})", p.string(), ex.what());
}
}
gradMehMapIsPopulating = false;
+ PLOG_INFO << "Finished PBO Mapping";
}
/*
Finds pbo Path
*/
fs::path findPboPath(std::string path) {
+ PLOG_INFO << fmt::format("Searching pbos for: {}", path);
size_t matchLength = 0;
fs::path retPath;
@@ -69,9 +72,18 @@ fs::path findPboPath(std::string path) {
for (auto const& [key, val] : entryPboMap)
{
if (boost::istarts_with(path, key) && key.length() > matchLength) {
+ matchLength = key.length();
retPath = val;
}
}
+
+ if (retPath.empty()) {
+ PLOG_WARNING << fmt::format("No pbo found for: {}", path);
+ }
+ else {
+ PLOG_INFO << fmt::format("Found pbo at: {}", retPath.string());
+ }
+
return retPath;
}
@@ -90,7 +102,7 @@ void writeGZJson(const std::string& fileName, fs::path path, nl::json& json) {
strInStream << std::setw(4) << json;
bi::filtering_istream fis;
- fis.push(bi::gzip_compressor(bi::gzip_params(bi::gzip::best_compression)));
+ fis.push(bi::gzip_compressor(bi::gzip_params(bi::gzip::best_speed)));
fis.push(strInStream);
std::ofstream out(path / fileName, std::ios::binary);
@@ -106,4 +118,35 @@ bool isMapPopulating() {
void prettyDiagLog(std::string message) {
client::invoker_lock thread_lock;
sqf::diag_log(sqf::text("[GRAD] (meh): " + message));
-}
\ No newline at end of file
+}
+
+bool checkMagic(rust::Vec& data, std::string magic) {
+ if (data.size() < magic.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < magic.size(); i++) {
+ if (data[i] != magic[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+fs::path getDllPath() {
+#ifdef _WIN32
+ char filePath[MAX_PATH + 1];
+ GetModuleFileName(HINST_THISCOMPONENT, filePath, MAX_PATH + 1);
+#endif
+
+ auto dllPath = fs::path(filePath);
+ dllPath = dllPath.remove_filename();
+
+#ifdef _WIN32
+ // Remove win garbage
+ dllPath = dllPath.string().substr(4);
+#endif
+
+ return dllPath;
+}
+
diff --git a/src/util.h b/src/util.h
index 930166f..de95465 100644
--- a/src/util.h
+++ b/src/util.h
@@ -4,6 +4,12 @@
#include "findPbos.h"
+#include
+
+#include
+
+#include
+
// String
#include
#include
@@ -16,15 +22,25 @@
#include
#include
+#include
+#include
+#include
+#include
#include
-#include
+#include
#ifdef _WIN32
#define NOMINMAX
#include
#include
-#endif
+
+ // https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483
+ EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+ #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
+#else
+#error Only Win is supported
+#endif // _WIN32
#define GRAD_MEH_FORMAT_VERSION 0.1
#define GRAD_MEH_MAX_COLOR_DIF 441.6729559300637f
@@ -50,3 +66,8 @@ void writeGZJson(const std::string& fileName, fs::path path, nl::json& json);
bool isMapPopulating();
void prettyDiagLog(std::string message);
+
+bool checkMagic(rust::Vec& data, std::string magic);
+
+fs::path getDllPath();
+
diff --git a/vcpkg.json b/vcpkg.json
new file mode 100644
index 0000000..74584b3
--- /dev/null
+++ b/vcpkg.json
@@ -0,0 +1,27 @@
+{
+ "name": "grad-meh",
+ "version-string": "0.7.1",
+ "dependencies": [
+ "nlohmann-json",
+ "boost-filesystem",
+ "boost-iostreams",
+ {
+ "name": "openimageio",
+ "default-features": true,
+ "features": [
+ "freetype"
+ ]
+ },
+ "plog",
+ {
+ "name": "gdal",
+ "default-features": false,
+ "features": [
+ "geos"
+ ]
+ },
+ "pcl",
+ "polyclipping",
+ "fmt"
+ ]
+}
\ No newline at end of file