diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..688db333 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +*.zip filter=lfs diff=lfs merge=lfs -text + +* text=crlf +*.bat text eol=crlf +*.sh text eol=lf +*.jpg -text +*.png -text + +*.h eol=crlf +*.hpp eol=crlf +*.c eol=crlf +*.cpp eol=crlf +*.ui eol=crlf +*.cmake eol=crlf +*.txt eol=crlf +*.json eol=crlf +*.in eol=crlf +*.ts eol=crlf +*.md eol=crlf \ No newline at end of file diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml new file mode 100644 index 00000000..b4c229b1 --- /dev/null +++ b/.github/workflows/linux-build.yml @@ -0,0 +1,269 @@ +name: Linux Build +on: + pull_request: + push: + branches: + - main + - dev + +jobs: + gcc: + runs-on: ubuntu-latest + strategy: + matrix: + build_type: [ Debug, Release ] + gpu_api: [ None, OpenCL, Vulkan ] + env: + BUILD_TYPE: ${{ matrix.build_type }} + GPU_API: ${{ matrix.gpu_api }} + steps: + - name: echo matrix variables (build_type = ${{ matrix.build_type }}) + run: | + echo $BUILD_TYPE + echo $GPU_API + - name: apt update + run: sudo apt update + + - name: Make directories + run: mkdir build install pack + + #- name: List installed + # run: apt list --installed + + - name: Install cmake, ninja + run: sudo apt install cmake ninja-build + + - name: Install dependencies + run: + sudo apt install gcc-14 g++-14 zstd libzstd-dev xz-utils bzip2 libzip-dev zipcmp ziptool zipmerge libpng-dev libeigen3-dev libboost-all-dev libboost-tools-dev libboost-doc libexpected-dev p7zip-full + + - name: Install xsimd + run: | + cd build + git clone https://github.com/xtensor-stack/xsimd.git + cd xsimd + git checkout 11.1.0 + cmake -S . -B build-xsimd + sudo cmake --install ./build-xsimd + cd .. + rm -rf ./* + + - name: Install qt6 + run: sudo apt install libqt6widgets6 libqt6gui6 libqt6network6 qt6-base-dev qt6-tools-dev-tools qt6-tools-dev qt6-l10n-tools + + - name: Install additional qt-related packages + run: sudo apt install x11-utils libxcb-xinerama0 libxv1 libgl-dev + # Without these libs, cmake cannot find Qt6::Gui. But if you install nvidia-cuda-toolkit, they will be installed automatically + - name: Install OpenCL SDK + if: matrix.gpu_api == 'OpenCL' + run: | + sudo apt install ocl-icd-libopencl1 ocl-icd-opencl-dev ocl-icd-dev opencl-c-headers opencl-clhpp-headers clinfo + clinfo + + - name: Install Vulkan SDK + if: matrix.gpu_api == 'Vulkan' + run: | + sudo apt install vulkan-tools libvulkan1 libvulkan-dev glslang-tools libfmt-dev + cd .. + wget https://sdk.lunarg.com/sdk/download/1.3.275.0/linux/vulkansdk-linux-x86_64-1.3.275.0.tar.xz + tar xf vulkansdk-linux-x86_64-1.3.275.0.tar.xz + ls -l + sudo cp -r ./1.3.275.0/x86_64/* /usr + whereis glslc + glslc --version + whereis vulkaninfo + + # + #vulkan-sdk + # + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: CMake configure + run: cmake -S . -B ./build -G Ninja -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-14 -DCMAKE_INSTALL_PREFIX:PATH=./install -DSlopeCraft_GPU_API:STRING=$GPU_API -DCMAKE_BUILD_TYPE:STRING=$BUILD_TYPE -DKOMPUTE_OPT_DISABLE_VULKAN_VERSION_CHECK=ON + + - name: CMake build + run: cmake --build ./build --parallel + + - name: CMake install + run: cmake --install ./build + + - name: Get short SHA + run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + + - name: Make deb and tar.xz packs with cpack + run: | + cd build + cpack -G DEB -B ../pack + cpack -G TXZ -B ../pack + cd .. + + - name: Upload deb pack + uses: actions/upload-artifact@v4 + with: + name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-linux-gcc-deb + path: pack/SlopeCraft*.deb + if-no-files-found: error + compression-level: 0 + + - name: Upload tar.xz pack + uses: actions/upload-artifact@v4 + with: + name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-linux-gcc-tar-xz + path: pack/SlopeCraft*.tar.xz + if-no-files-found: error + compression-level: 0 + + - name: Run CTest + if: (matrix.build_type == 'Release')&&(matrix.gpu_api == 'None') + run: | + cd ./build + ctest -j20 --output-on-failure --stop-on-failure + - name: Test deb pack + run: sudo dpkg -i ./pack/*.deb + + # linux-test-deb: + # runs-on: ubunut-latest + # needs: linux-build + # strategy: + # matrix: + # build_type: [Debug, Release] + # gpu_api: [None, OpenCL] + # steps: + # - name: Get short SHA + # run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + # + # - name: Download deb + # uses: actions/download-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-linux-deb + # + # - name: ls + # run: ls . + # + # - name: Try installing deb + # run: sudo dpkg -i ./*.deb + + # clang: + # runs-on: ubuntu-latest + # strategy: + # matrix: + # build_type: [ Debug, Release ] + # gpu_api: [ None, OpenCL ] + # env: + # BUILD_TYPE: ${{ matrix.build_type }} + # GPU_API: ${{ matrix.gpu_api }} + # steps: + # - name: echo matrix variables (build_type = ${{ matrix.build_type }}) + # run: | + # echo $BUILD_TYPE + # echo $GPU_API + # - name: apt update + # run: sudo apt update + # + # - name: Make directories + # run: mkdir build install pack + # + # #- name: List installed + # # run: apt list --installed + # + # - name: Install clang-15, cmake, ninja + # run: sudo apt install clang-15 cmake ninja-build + # + # - name: Install libzip, libpng, Eigen3, openmp + # run: sudo apt install libzip-dev zipcmp ziptool zipmerge libpng-dev libeigen3-dev libomp5-15 + # + # - name: Install xsimd + # run: | + # cd build + # git clone https://github.com/xtensor-stack/xsimd.git + # cd xsimd + # git checkout 11.1.0 + # cmake -S . -B build-xsimd + # sudo cmake --install ./build-xsimd + # cd .. + # rm -rf ./* + # + # - name: Install qt6 + # run: sudo apt install libqt6widgets6 libqt6gui6 libqt6network6 qt6-base-dev qt6-tools-dev-tools qt6-tools-dev qt6-l10n-tools + # + # - name: Install additional qt-related packages + # run: sudo apt install x11-utils libxcb-xinerama0 libxv1 libgl-dev + # # Without these libs, cmake cannot find Qt6::Gui. But if you install nvidia-cuda-toolkit, they will be installed automatically + # - name: Install OpenCL sdk + # if: matrix.gpu_api == 'OpenCL' + # run: sudo apt install ocl-icd-libopencl1 ocl-icd-opencl-dev ocl-icd-dev opencl-c-headers opencl-clhpp-headers clinfo + # + # - name: See clinfo + # if: matrix.gpu_api == 'OpenCL' + # run: clinfo + # + # - name: Install xz (Release as tar.xz) + # run: sudo apt install xz-utils + # + # - name: Checkout repository + # uses: actions/checkout@v4 + # + # - name: CMake configure + # run: cmake -S . -B ./build -G Ninja -DCMAKE_C_COMPILER:FILEPATH=clang-15 -DCMAKE_CXX_COMPILER:FILEPATH=clang++-15 -DCMAKE_INSTALL_PREFIX:PATH=./install -DSlopeCraft_GPU_API:STRING=$GPU_API -DCMAKE_BUILD_TYPE:STRING=$BUILD_TYPE + # + # - name: CMake build + # run: cmake --build ./build --parallel + # + # - name: CMake install + # run: cmake --install ./build + # + # - name: Get short SHA + # run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + # + # - name: Make deb and tar.xz packs with cpack + # run: | + # cd build + # cpack -G DEB -B ../pack + # cpack -G TXZ -B ../pack + # cd .. + # + # - name: Upload deb pack + # uses: actions/upload-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-linux-clang-deb + # path: pack/SlopeCraft*.deb + # if-no-files-found: error + # + # - name: Upload tar.xz pack + # uses: actions/upload-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-linux-clang-tar-xz + # path: pack/SlopeCraft*.tar.xz + # if-no-files-found: error + # + # - name: Run CTest + # if: (matrix.build_type == 'Release')&&(matrix.gpu_api == 'None') + # run: | + # cd ./build + # ctest -j20 --output-on-failure --stop-on-failure + # - name: Test deb pack + # run: sudo dpkg -i ./pack/*.deb + + # linux-test-deb: + # runs-on: ubunut-latest + # needs: linux-build + # strategy: + # matrix: + # build_type: [Debug, Release] + # gpu_api: [None, OpenCL] + # steps: + # - name: Get short SHA + # run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + # + # - name: Download deb + # uses: actions/download-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-linux-deb + # + # - name: ls + # run: ls . + # + # - name: Try installing deb + # run: sudo dpkg -i ./*.deb diff --git a/.github/workflows/macos-build-arm64.yml b/.github/workflows/macos-build-arm64.yml new file mode 100644 index 00000000..1f1cca78 --- /dev/null +++ b/.github/workflows/macos-build-arm64.yml @@ -0,0 +1,223 @@ +name: macOS (Apple Silicon) Build +on: + pull_request: + push: + branches: + - main + - dev + +jobs: + clang: + runs-on: macos-15 + strategy: + matrix: + build_type: [ Debug, Release ] + gpu_api: [ None, + # Vulkan + ] + env: + BUILD_TYPE: ${{ matrix.build_type }} + GPU_API: ${{ matrix.gpu_api }} + + steps: + - name: Check environment + run: | + uname -r + brew install screenfetch + screenfetch + + - name: Checkout repository + uses: actions/checkout@v4 + # - name: Check env + # run: | + # whoami + # pwd + # ls $HOME + # ls $HOME/Documents/cpp/qt/qt6.6.0-static -l + + - name: Install dependencies + run: | + brew install llvm lld boost ninja libpng xsimd p7zip libzip zstd xz eigen boost tl-expected magic_enum fmt sevenzip + + - name: Check clang + run: /opt/homebrew/opt/llvm/bin/clang -v + + #https://github.com/SlopeCraft/QtBinaries/releases/download/qt6.6.0-arm64-apple-darwin/qt6.6.0-static-arm64-apple-darwin.tar.xz + - name: Install qt6.6.0-static + run: | + brew install qt@6 + # curl -JL -o ~/Downloads/qt6.6.0-static-arm64-apple-darwin.tar.xz https://github.com/SlopeCraft/QtBinaries/releases/download/qt6.6.0-arm64-apple-darwin/qt6.6.0-static-arm64-apple-darwin.tar.xz + # cd ~/Downloads + # tar xzf qt6.6.0-static-arm64-apple-darwin.tar.xz + # ls -l + # chmod +x ~/Downloads/qt6.6.0-arm64-apple-darwin/bin/* + # chmod +x ~/Downloads/qt6.6.0-arm64-apple-darwin/libexec/* + + - name: Install Vulkan sdk + if: matrix.gpu_api == 'Vulkan' + run: | + brew install molten-vk vulkan-headers vulkan-loader vulkan-tools glslang shaderc + whereis glslc + glslc --version + + - name: Configure CMake + run: cmake -S . -B ./build -G "Ninja" -DCMAKE_C_COMPILER=/opt/homebrew/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=/opt/homebrew/opt/llvm/bin/clang++ "-DCMAKE_PREFIX_PATH=$HOME/Downloads/qt6.6.0-arm64-apple-darwin;/opt/homebrew" -DCMAKE_INSTALL_PREFIX=./build/install -DSlopeCraft_GPU_API=$GPU_API -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DKOMPUTE_OPT_DISABLE_VULKAN_VERSION_CHECK=ON + - name: Build + run: | + cd build + export LDFLAGS="-L/opt/homebrew/opt/llvm/lib" + export CPPFLAGS="-I/opt/homebrew/opt/llvm/include" + cmake --build . --parallel + # - name: CPack + # run: | + # cd build + # cpack -G ZIP + - name: Install + run: | + cd build + cmake --install . + + # - name: Fix VisualCraft.app + # run: | + # cd build/install + # cd VisualCraft.app/Contents/Frameworks + # cp /opt/homebrew/lib/libzstd.1.dylib . + # cp /opt/homebrew/lib/liblzma.5.dylib . + # install_name_tool libzip.5.dylib -change @loader_path/../../../../opt/xz/lib/liblzma.5.dylib @loader_path/liblzma.5.dylib + # install_name_tool libzip.5.dylib -change @loader_path/../../../../opt/zstd/lib/libzstd.1.dylib @loader_path/libzstd.1.dylib + # cd ../../.. + # codesign --force --deep --sign=- VisualCraft.app + + - name: Compress as zip + run: | + cd build + 7z a SlopeCraft-5.2.1-macOS-Apple-Silicon.zip -tzip -mx=9 -mmt -m0=XZ -snl ./install/* + + - name: Get short SHA + run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-macos-m2-clang + path: build/SlopeCraft*.zip + if-no-files-found: error + compression-level: 0 + # nothing: + # runs-on: macos-latest + # steps: + # - name: checkout + # run: echo "rua" + + # clang-x86_64-to-arm64: + # runs-on: macos-latest + # steps: + # - name: Install dependents + # run: | + # brew remove zlib + # brew install cmake xz p7zip llvm ninja + # vcpkg install --triplet=arm64-osx zlib zstd + # wget https://github.com/SlopeCraft/QtBinaries/releases/download/qt6.6.0-x86_64-apple-darwin/qt6.6.0-static-x86_64-apple-darwin.tar.xz + # tar xf qt6.6.0-static-x86_64-apple-darwin.tar.xz + # mv qt6.6.0-static-x86_64-apple-darwin $HOME + # + # + # #vcpkg install --triplet=arm64-osx zlib zstd libzip eigen3 xsimd libpng boost + # + # - name: Checkout repository + # uses: actions/checkout@v4 + # + # - name: Cross compile qtbase 6.6.2 + # run: | + # cp cmake/tool_chain_files/x86_64-mac-to-arm64-mac.cmake $HOME + # mkdir $HOME/qt6.6.2-x86_64-mac-to-arm64-mac + # git clone https://github.com/qt/qtbase.git qtbase --recursive + # cd qtbase + # git checkout v6.6.2 + # mkdir build + # cd build + # ../configure -shared -release -nomake examples -nomake tests -prefix $HOME/qt6.6.2-x86_64-mac-to-arm64-mac -qt-host-path $HOME/qt6.6.0-static-x86_64-apple-darwin -platform macx-clang -- -DCMAKE_TOOLCHAIN_FILE=$HOME/x86_64-mac-to-arm64-mac.cmake -DCMAKE_PREFIX_PATH=/usr/local/share/vcpkg/installed/arm64-osx + # cmake --build . --parallel + # cmake --install . + # rm -rf ./* + # + # - name: Cross compile qttools 6.6.2 + # run: | + # git clone https://github.com/qt/qttools.git qttools --recursive + # cd qttools + # git checkout v6.6.2 + # mkdir build + # cd build + # $HOME/qt6.6.2-x86_64-mac-to-arm64-mac/bin/qt-configure-module .. + # cmake --build . --parallel + # cmake --install . + # rm -rf ./* + # + # - name: Compress installed Qt binaries + # run: | + # tar cvf qt6.6.2-x86_64-mac-to-arm64-mac.tar $HOME/qt6.6.2-x86_64-mac-to-arm64-mac/* + # xz -9 qt6.6.2-x86_64-mac-to-arm64-mac.tar -T0 + # + # - name: Upload Qt6.6.2-host-x86_64-mac-target-arm64-mac + # uses: actions/upload-artifact@v3 + # with: + # name: Qt6.6.2-host-x86_64-mac-target-arm64-mac + # path: qt6.6.2-x86_64-mac-to-arm64-mac.tar.xz + + + + + +# macos-build: +# runs-on: macos-latest +# strategy: +# matrix: +# build_type: [ Debug, Release ] +# gpu_api: [ None ] +# env: +# BUILD_TYPE: ${{ matrix.build_type }} +# GPU_API: ${{ matrix.gpu_api }} +# steps: +# - name: brew update +# run: brew update +# - name: echo matrix variables (build_type = ${{ matrix.build_type }}) +# run: | +# echo $BUILD_TYPE +# echo $GPU_API +# - name: Install build system +# run: brew install ninja cmake +# - name: Install dependencies +# run: | +# brew install libpng qt@6 eigen +# brew reinstall libomp +# - name: Add /usr/local/lib to PATH +# run: echo "/usr/local/lib" >> $GITHUB_PATH +# - name: Check PATH +# run: echo $PATH +# - name: Checkout repository +# uses: actions/checkout@v4 +# - name: Configure CMake +# run: cmake -S . -B ./build -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=./build/install -DSlopeCraft_GPU_API=$GPU_API -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=/System/Volumes/Data/usr/local/Cellar +# - name: Build and Install +# run: | +# cd build +# cmake --build . --parallel +# cmake --install . +# cd install +# chmod +x SlopeCraft.app/Contents/MacOS/SlopeCraft +# chmod +x MapViewer.app/Contents/MacOS/MapViewer +# chmod +x imageCutter.app/Contents/MacOS/imageCutter +# chmod +x VisualCraft.app/Contents/MacOS/VisualCraft +# chmod +x vccl-contents/vccl.app/Contents/MacOS/vccl +# # chmod +x vccl.app/Contents/MacOS/vccl +# - name: Compress as zip +# run: | +# cd ./build/install +# zip -9 -r -y SlopeCraft-5.2.0-macos.zip ./* +# +# - name: Get short SHA +# run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV +# - name: Upload Artifacts +# uses: actions/upload-artifact@v3 +# with: +# name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-macos-clang +# path: build/install/SlopeCraft*.zip diff --git a/.github/workflows/macos-build-x64.yml b/.github/workflows/macos-build-x64.yml new file mode 100644 index 00000000..6b076431 --- /dev/null +++ b/.github/workflows/macos-build-x64.yml @@ -0,0 +1,183 @@ +name: macOS (x64) Build +on: + pull_request: + push: + branches: + - main + - dev + +jobs: + clang: + runs-on: macos-15 + strategy: + matrix: + build_type: [ Debug, Release ] + gpu_api: [ None, + # Vulkan + ] + vectorize: [ ON, OFF ] + env: + BUILD_TYPE: ${{ matrix.build_type }} + GPU_API: ${{ matrix.gpu_api }} + VECTORIZE: ${{ matrix.vectorize }} + steps: + - name: Check environment + run: | + uname -r + brew install screenfetch + screenfetch + # - name: brew upgrade & update + # run: | + # brew update + # brew remove aws-sam-cli pipx cfn-lint + # brew remove python + # brew upgrade + - name: Install LLVM clang + run: | + brew install llvm lld + - name: Check clang + run: /opt/homebrew/opt/llvm/bin/clang -v + - name: echo matrix variables (build_type = ${{ matrix.build_type }}) + run: | + echo $BUILD_TYPE + echo $GPU_API + echo $VECTORIZE + - name: Install Ninja build system + run: brew install ninja + - name: Install dependencies + run: | + brew install libpng xsimd p7zip libzip zstd xz eigen boost tl-expected magic_enum fmt sevenzip + + # - name: Find liblzma and libzstd dylibs + # run: | + # find /usr/local/Cellar -name liblzma.5.dylib 2>/dev/null + # find /usr/local/Cellar -name libzstd.1.dylib 2>/dev/null + + - name: Install Vulkan sdk + if: matrix.gpu_api == 'Vulkan' + run: | + brew install molten-vk vulkan-headers vulkan-loader vulkan-tools glslang shaderc + whereis glslc + glslc --version + + - name: Checkout repository + uses: actions/checkout@v4 + - name: Download qt6.6.0-static-x86_64-apple-darwin.tar.xz and extract + run: | + curl -JL -o ~/Downloads/qt6.6.0-static-x86_64-apple-darwin.tar.xz https://github.com/SlopeCraft/QtBinaries/releases/download/qt6.6.0-x86_64-apple-darwin/qt6.6.0-static-x86_64-apple-darwin.tar.xz + cd ~/Downloads + tar xzf qt6.6.0-static-x86_64-apple-darwin.tar.xz + mv qt6.6.0-static-x86_64-apple-darwin Qt6.6.0-appleclang-static-x86_64 + chmod +x ~/Downloads/Qt6.6.0-appleclang-static-x86_64/bin/* + chmod +x ~/Downloads/Qt6.6.0-appleclang-static-x86_64/libexec/* + + - name: Add /usr/local/lib to PATH + run: echo "/usr/local/lib" >> $GITHUB_PATH + - name: Check PATH + run: echo $PATH + - name: Configure CMake + run: cmake -S . -B ./build -G "Ninja" -DCMAKE_C_COMPILER=/opt/homebrew/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=/opt/homebrew/opt/llvm/bin/clang++ -DCMAKE_INSTALL_PREFIX=./build/install -DSlopeCraft_GPU_API=$GPU_API -DSlopeCraft_vectorize=$VECTORIZE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$HOME/Downloads/Qt6.6.0-appleclang-static-x86_64;/System/Volumes/Data/usr/local/Cellar;/opt/homebrew" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DSlopeCraft_macdeployqt_flags_install="-no-plugins" -DKOMPUTE_OPT_DISABLE_VULKAN_VERSION_CHECK=ON + - name: Build + run: | + cd build + export LDFLAGS="-L/usr/local/opt/llvm/lib" + export CPPFLAGS="-I/usr/local/opt/llvm/include" + cmake --build . --parallel + # - name: CPack + # run: | + # cd build + # cpack -G ZIP + + - name: Install + run: | + cd build + cmake --install . + + # - name: Fix VisualCraft.app + # run: | + # cd build/install + # cd VisualCraft.app/Contents/Frameworks + # otool -L libzip.5.dylib + # cp /usr/local/Cellar/zstd/1.5.5/lib/libzstd.1.dylib . + # cp /usr/local/Cellar/xz/5.4.4/lib/liblzma.5.dylib . + # install_name_tool libzip.5.dylib -change @loader_path/../../../../opt/xz/lib/liblzma.5.dylib @loader_path/liblzma.5.dylib + # install_name_tool libzip.5.dylib -change @loader_path/../../../../opt/zstd/lib/libzstd.1.dylib @loader_path/libzstd.1.dylib + # cd ../../.. + # codesign --force --deep --sign=- VisualCraft.app + # chmod +x vccl.app/Contents/MacOS/vccl + + - name: Compress as zip + run: | + cd build + 7z a SlopeCraft-5.2.1-macOS-Intel.zip -tzip -mx=9 -mmt -m0=XZ -snl ./install/* + + - name: Get short SHA + run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-vectorize-${{ matrix.vectorize }}-macos-x64-clang + path: build/SlopeCraft*.zip + if-no-files-found: error + compression-level: 0 + + # gcc: + # runs-on: macos-latest + # strategy: + # matrix: + # build_type: [ Debug, Release ] + # gpu_api: [ None ] + # env: + # BUILD_TYPE: ${{ matrix.build_type }} + # GPU_API: ${{ matrix.gpu_api }} + # steps: + # # - name: brew upgrade & update + # # run: | + # # brew update + # # brew upgrade + # - name: echo matrix variables (build_type = ${{ matrix.build_type }}) + # run: | + # echo $BUILD_TYPE + # echo $GPU_API + # - name: Install Ninja build system + # run: brew install ninja + # - name: Install dependencies + # run: | + # brew install libpng xsimd libzip p7zip + # + # - name: Checkout repository + # uses: actions/checkout@v4 + # - name: Download qt6.6.0-static-x86_64-apple-darwin.tar.xz and extract + # run: | + # curl -JL -o ~/Downloads/qt6.6.0-static-x86_64-apple-darwin.tar.xz https://github.com/SlopeCraft/VisualCraft-binaries/releases/download/mac-binaries/qt6.6.0-static-x86_64-apple-darwin.tar.xz + # 7z x -o"$HOME/Downloads/Qt6.6.0-appleclang-static-x86_64" ~/Downloads/qt6.6.0-static-x86_64-apple-darwin.tar.xz + # chmod +x ~/Downloads/Qt6.6.0-appleclang-static-x86_64/bin/* + # chmod +x ~/Downloads/Qt6.6.0-appleclang-static-x86_64/libexec/* + # + # - name: Add /usr/local/lib to PATH + # run: echo "/usr/local/lib" >> $GITHUB_PATH + # - name: Check PATH + # run: echo $PATH + # - name: Configure CMake + # run: cmake -S . -B ./build -G "Ninja" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_INSTALL_PREFIX=./build/install -DSlopeCraft_GPU_API=$GPU_API -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$HOME/Downloads/Qt6.6.0-appleclang-static-x86_64;/System/Volumes/Data/usr/local/Cellar" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DSlopeCraft_macdeployqt_flags_install="-no-plugins" + # - name: Build + # run: | + # cd build + # cmake --build . --parallel + # - name: CPack + # run: | + # cd build + # cpack -G ZIP + # - name: Test installation + # run: | + # cd build + # cmake --install . + # # chmod +x vccl.app/Contents/MacOS/vccl + # + # - name: Get short SHA + # run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV + # - name: Upload Artifacts + # uses: actions/upload-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-macos-x64-gcc + # path: build/SlopeCraft*.zip \ No newline at end of file diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml new file mode 100644 index 00000000..2043201f --- /dev/null +++ b/.github/workflows/windows-build.yml @@ -0,0 +1,321 @@ +name: Windows Build +on: + pull_request: + push: + branches: + - main + - dev + - bugfix/windows-CI + +jobs: + msvc: + runs-on: windows-latest + strategy: + matrix: + build_type: [ Debug, Release ] + gpu_api: [ None, OpenCL ] + vectorize: [ true, false ] + exclude: + - gpu_api: OpenCL + vectorize: false + - gpu_api: Vulkan + vectorize: false + env: + BUILD_TYPE: ${{ matrix.build_type }} + GPU_API: ${{ matrix.gpu_api }} + VECTORIZE: ${{ matrix.vectorize }} + steps: + - name: echo matrix variables (build_type = ${{ matrix.build_type }}) + run: | + echo "env:BUILD_TYPE = $env:BUILD_TYPE" + echo "env:GPU_API = $env:GPU_API" + + - name: Install ninja + shell: cmd + run: | + "C:\Program Files\7-Zip\7z.exe" + choco install ninja + ninja --version + + - name: Install deps with vcpkg + shell: cmd + run: | + mkdir C:\vcpkg\downloads + curl -JL -o C:/vcpkg/downloads/tukaani-project-xz-v5.4.4.tar.gz "https://github.com/xz-mirror/xz/archive/refs/tags/v5.4.4.tar.gz" + vcpkg install --triplet=x64-windows zlib libpng bzip2 zstd liblzma libzip eigen3 xsimd boost-iostreams boost-uuid boost-multi-array tl-expected fmt cereal magic-enum + + - name: Install OpenCL with vcpkg + if: matrix.gpu_api == 'OpenCL' + run: vcpkg install opencl --triplet=x64-windows + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: make dirs + run: | + mkdir bin + mkdir bin/llvm + mkdir bin/Qt6.5.0-msvc-shared + mkdir 3rdParty + + - name: Get short SHA + run: | + echo "GITHUB_SHA = $env:GITHUB_SHA" + $short_sha=$env:GITHUB_SHA.substring(0,7) + echo "short_sha = $short_sha" + echo "SHORT_SHA=$short_sha" >> $env:GITHUB_ENV + + + - name: Install winget + if: matrix.gpu_api == 'Vulkan' + shell: pwsh + run: | + $progressPreference = 'silentlyContinue' + Write-Information "Downloading WinGet and its dependencies..." + Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle + Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile Microsoft.VCLibs.x64.14.00.Desktop.appx + Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx -OutFile Microsoft.UI.Xaml.2.8.x64.appx + Add-AppxPackage Microsoft.VCLibs.x64.14.00.Desktop.appx + Add-AppxPackage Microsoft.UI.Xaml.2.8.x64.appx + Add-AppxPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle + + - name: Install Vulkan with vcpkg + if: matrix.gpu_api == 'Vulkan' + run: winget install KhronosGroup.VulkanSDK + # cmd + # curl -JL -o bin/VulkanSDK.7z "https://github.com/SlopeCraft/QtBinaries/releases/download/vulkan1.3.250.1-windows/VulkanSDK-1.3.250.1.7z" + # 7z x -o"C:\Program Files\VulkanSDK" bin/VulkanSDK.7z + # exit + # tree /f "C:\Program Files\VulkanSDK" + + - name: Download Qt6.5.0-msvc-shared and extract + shell: cmd + run: | + cd bin + curl -JL -o .\Qt6.5.0-msvc-shared.7z "https://github.com/SlopeCraft/QtBinaries/releases/download/qt6.5.0-x86_64-msvc-windows/Qt6.5.0-msvc-shared.7z" + 7z x -oQt6.5.0-msvc-shared .\Qt6.5.0-msvc-shared.7z + + # - name: Configure libnbt++ + # shell: cmd + # run: | + # cd 3rdParty + # git clone https://github.com/PrismLauncher/libnbtplusplus + # cd libnbtplusplus + # git checkout 23b955121b8217c1c348a9ed2483167a6f3ff4ad + # mkdir build ../libnbt++ + # "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" & cmake -S . -B ./build -G Ninja -DCMAKE_C_COMPILER:FILEPATH=cl -DCMAKE_CXX_COMPILER:FILEPATH=cl -DCMAKE_PREFIX_PATH:PATH="C:/vcpkg/installed/x64-windows" -DCMAKE_INSTALL_PREFIX:PATH=../libnbt++ -DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" -DNBT_BUILD_SHARED=OFF -DNBT_USE_ZLIB=OFF -DNBT_BUILD_TESTS=OFF + # + # + # - name: Build and install libnbt++ + # shell: cmd + # run: | + # cd 3rdParty/libnbtplusplus + # cmake --build ./build --parallel + # cmake --install ./build + # cd .. + # tree /f libnbt++ + + - name: Download and extract llvm 20.1.3 + shell: cmd + run: | + cd /d bin + curl -JL -o .\LLVM-20.1.3-win64.exe "https://github.com/llvm/llvm-project/releases/download/llvmorg-20.1.3/LLVM-20.1.3-win64.exe" + 7z x .\LLVM-20.1.3-win64.exe -ollvm "-xr!$PLUGINSDIR" + .\llvm\bin\clang-cl --version + cd .. + tree /f D:\a\SlopeCraft\SlopeCraft\bin\llvm + + - name: CMake configure + shell: cmd + run: | + mkdir build + mkdir install + .\bin\llvm\bin\clang-cl --version + set PATH=D:\a\SlopeCraft\SlopeCraft\bin\llvm\bin;C:\Program Files\VulkanSDK\1.3.250.1\Bin;C:\Program Files\PowerShell\7;C:\vcpkg;C:\Program Files (x86)\NSIS\;C:\Program Files\dotnet;C:\Program Files (x86)\GitHub CLI;C:\Program Files\Git\bin;C:\Program Files\OpenSSL\bin;C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin;C:\Program Files\Microsoft\jdk-11.0.16.101-hotspot\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\dotnet\;C:\Program Files\PowerShell\7\;C:\Program Files\Microsoft\Web Platform Installer\;C:\Program Files\CMake\bin;C:\Program Files\Microsoft SDKs\Service Fabric\Tools\ServiceFabricLocalClusterManager;C:\Program Files\Git\cmd;C:\Program Files\GitHub CLI\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\LLVM\bin;C:\Users\runneradmin\.dotnet\tools;C:\Users\runneradmin\.cargo\bin;C:\Users\runneradmin\AppData\Local\Microsoft\WindowsApps + "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" & cmake -S . -B ./build -G Ninja -DCMAKE_C_COMPILER:FILEPATH="D:/a/SlopeCraft/SlopeCraft/bin/llvm/bin/clang-cl.exe" -DCMAKE_CXX_COMPILER:FILEPATH="D:/a/SlopeCraft/SlopeCraft/bin/llvm/bin/clang-cl.exe" -DCMAKE_RC_COMPILER:FILEPATH="D:/a/SlopeCraft/SlopeCraft/bin/llvm/bin/llvm-rc.exe" -DCMAKE_PREFIX_PATH:PATH="D:/a/SlopeCraft/SlopeCraft/bin/Qt6.5.0-msvc-shared;C:/vcpkg/installed/x64-windows/;D:/a/SlopeCraft/SlopeCraft/3rdParty/libnbt++;C:/Program Files/VulkanSDK/1.3.250.1" -DCMAKE_INSTALL_PREFIX:PATH=D:/a/SlopeCraft/SlopeCraft/install -DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" -DSlopeCraft_GPU_API:STRING="${{ matrix.gpu_api }}" -DSlopeCraft_vectorize:BOOL="${{ matrix.vectorize }}" -DSlopeCraft_windeployqt_flags_install:STRING=-release;--no-translations + # + - name: CMake build + shell: cmd + run: | + tree ./build + cmake --build ./build --parallel 10 + + - name: CMake install + shell: cmd + run: | + cmake --install ./build + + - name: Make zip and 7z packs with CPack + run: | + $basedir=(pwd).path.replace("\\","/") + cd ./build + cpack -G ZIP -B "$basedir/pack" + cpack -G 7Z -B "$basedir/pack" + + #- name: Upload zip pack + # uses: actions/upload-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-vec=${{ matrix.vectorize }}-windows-clang-zip + # path: pack/SlopeCraft*.zip + # if-no-files-found: error + + - name: Upload 7z pack + uses: actions/upload-artifact@v4 + with: + name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-vec=${{ matrix.vectorize }}-windows-clang-7z + path: pack/SlopeCraft*.7z + if-no-files-found: error + compression-level: 0 + + gcc: + runs-on: windows-latest + strategy: + matrix: + build_type: [ Debug, Release ] + gpu_api: [ None ] + vectorize: [ true, false ] + exclude: + - gpu_api: OpenCL + vectorize: false + env: + BUILD_TYPE: ${{ matrix.build_type }} + GPU_API: ${{ matrix.gpu_api }} + VECTORIZE: ${{ matrix.vectorize }} + steps: + - name: echo matrix variables (build_type = ${{ matrix.build_type }}) + run: | + echo "env:BUILD_TYPE = $env:BUILD_TYPE" + echo "env:GPU_API = $env:GPU_API" + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install ninja + run: | + choco install ninja + ninja --version + + - name: Get short SHA + run: | + echo "GITHUB_SHA = $env:GITHUB_SHA" + $short_sha=$env:GITHUB_SHA.substring(0,7) + echo "short_sha = $short_sha" + echo "SHORT_SHA=$short_sha" >> $env:GITHUB_ENV + + - name: Filter perl-related pathes from env:path, and append directory of gcc15 to path + run: | + $basedir=(pwd).path.replace("\\","/") + $exclude_key_words="perl","Strawberry" + $new_path="" + foreach($p in $env:path.split(';')) {$exclude=$false; foreach($ekw in $exclude_key_words) {if($p.contains($ekw)) {$exclude=$true;break;}}if(-not($exclude)) {$new_path="$new_path;$p"}} + $env:path="$basedir/bin/mingw64/bin;C:/Program Files/7-Zip;$new_path" + echo "New path : $env:path.split(';')" + echo "NEW_PATH=$env:path" >> $env:GITHUB_ENV + + - name: Install ninja + run: | + choco install ninja + ninja --version + + - name: Make dirs + run: | + mkdir bin + mkdir bin/mingw64 + mkdir bin/Qt6.5.0-gcc11-shared-offical + mkdir 3rdParty + + - name: Download mingw64 and extract + run: | + cmd + curl -JL -o bin/gcc15.7z "https://github.com/niXman/mingw-builds-binaries/releases/download/15.2.0-rt_v13-rev0/x86_64-15.2.0-release-posix-seh-ucrt-rt_v13-rev0.7z" + 7z x -obin bin/gcc15.7z + exit + # gcc installation dir: bin/mingw64 + # gcc.exe: bin/mingw64/bin/gcc.exe + # g++.exe: bin/mingw64/bin/g++.exe + - name: Install deps with vcpkg + run: | + $basedir=(pwd).path.replace("\\","/") + $BUILD_TYPE=$env:BUILD_TYPE + $GPU_API=$env:GPU_API + $env:path=$env:NEW_PATH + echo "env:path = $env:path" + mkdir C:\vcpkg\downloads + curl -JL -o C:/vcpkg/downloads/tukaani-project-xz-v5.4.4.tar.gz "https://github.com/xz-mirror/xz/archive/refs/tags/v5.4.4.tar.gz" + cd C:/vcpkg + git pull + vcpkg install --triplet=x64-mingw-dynamic zlib bzip2 zstd liblzma libpng libzip eigen3 xsimd boost-iostreams boost-uuid boost-multi-array tl-expected magic-enum fmt cereal + + + - name: Install OpenCL with vcpkg + if: matrix.gpu_api == 'OpenCL' + run: | + $basedir=(pwd).path.replace("\\","/") + $BUILD_TYPE=$env:BUILD_TYPE + $GPU_API=$env:GPU_API + $env:path=$env:NEW_PATH + echo "env:path = $env:path" + vcpkg install opencl --triplet=x64-mingw-dynamic + + - name: Download Qt6.5.0-gcc11-shared-offical and extract + run: | + cmd + curl -JL -o bin/Qt6.5.0-gcc11-shared-offical.7z "https://github.com/SlopeCraft/QtBinaries/releases/download/qt6.5.0-x86_64-mingw-windows/Qt6.5.0-gcc11-shared-offical.7z" + 7z x -o"bin/Qt6.5.0-gcc11-shared-offical" bin/Qt6.5.0-gcc11-shared-offical.7z + exit + tree /f bin/Qt6.5.0-gcc11-shared-offical + # Qt installation dir: bin/Qt6.5.0-gcc11-shared-offical + - name: CMake configure + run: | + $basedir=(pwd).path.replace("\\","/") + $BUILD_TYPE=$env:BUILD_TYPE + $GPU_API=$env:GPU_API + $env:path="$basedir/bin/mingw64/bin;C:/Program Files/7-Zip;$env:path" + echo "env:path = $env:path" + mkdir build + mkdir install + cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_C_COMPILER:FILEPATH="$basedir/bin/mingw64/bin/gcc.exe" -DCMAKE_CXX_COMPILER:FILEPATH="$basedir/bin/mingw64/bin/g++.exe" -DCMAKE_PREFIX_PATH:PATH="$basedir/bin/Qt6.5.0-gcc11-shared-offical;C:/vcpkg/installed/x64-mingw-dynamic" -DCMAKE_INSTALL_PREFIX:PATH="$basedir/install" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DSlopeCraft_GPU_API:STRING="$GPU_API" -DSlopeCraft_vectorize:BOOL=$VECTORIZE + + - name: CMake build + run: | + $basedir=(pwd).path.replace("\\","/") + $env:path=$env:NEW_PATH + echo "Number of processors = $env:NUMBER_OF_PROCESSORS" + cmake --build ./build --parallel 10 + + - name: CMake install + run: | + $basedir=(pwd).path.replace("\\","/") + $env:path=$env:NEW_PATH + cmake --install ./build + + #- name: Run CTest + # if: matrix.build_type == 'Release' + # run: | + # $basedir=(pwd).path.replace("\\","/") + # $env:path=$env:NEW_PATH + # cd ./build + # ctest -j10 --output-on-failure --stop-on-failure + + - name: Make zip and 7z packs with CPack + run: | + $basedir=(pwd).path.replace("\\","/") + $env:path=$env:NEW_PATH + cd ./build + cpack -G 7Z -B "$basedir/pack" + #cpack -G ZIP -B "$basedir/pack" + + #- name: Upload zip pack + # uses: actions/upload-artifact@v3 + # with: + # name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-vec=${{ matrix.vectorize }}-windows-zip + # path: pack/SlopeCraft*.zip + # if-no-files-found: error + + - name: Upload 7z pack + uses: actions/upload-artifact@v4 + with: + name: SlopeCraft-dev-${{ env.SHORT_SHA }}-${{ matrix.build_type }}-${{ matrix.gpu_api }}-vec=${{ matrix.vectorize }}-windows-gcc-7z + path: pack/SlopeCraft*.7z + if-no-files-found: error + compression-level: 0 diff --git a/.gitignore b/.gitignore index bc5c6fe7..6f1609b1 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,6 @@ Testing Makefile cmake_install.cmake install_manifest.txt -compile_commands.json CTestTestfile.cmake _deps @@ -66,9 +65,17 @@ install/ 3rdParty/* .vscode/settings.json -### build +### build ### build/* binaries/* .vscode/* .vs/* -.cache/* \ No newline at end of file +.cache/* + +### CLion ### +.idea/* +cmake-build-* + +### PyCharm ### +py/preprocessed +py/outputs \ No newline at end of file diff --git a/Blocks/CustomBlocks.json b/Blocks/CustomBlocks.json deleted file mode 100644 index 0ed4377f..00000000 --- a/Blocks/CustomBlocks.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "CustomBlocks": [ - // 这个文件列出了所有用户自定义的方块的信息", - // 你可以按照这样的格式自定义添加新的方块,包括 mod 方块", - // 但必须遵守 json 格式", - // 如果 json 格式出错,整个文件都会无法解析,SlopeCraft 会报错。 - { - // baseColor 是这种方块对应的基色,是必填项(非常非常重要!!!!!!!)", - "baseColor": 15, - // id 是这种方块的 id,也是必填项,最好附带完整的方块状态,必须加 minecraft:前缀", - "id": "minecraft:waxed_cut_copper_slab[type=top,waterlogged=false]", - // nameZH 是方块中文名称,必填", - "nameZH": "涂蜡切制铜台阶", - // nameEN 是方块英文名称,必填", - "nameEN": "Waxed cut copper slab", - // icon 是方块对应图片的文件名,它应当放在 CustomBlocks 文件夹下。必填", - "icon": "waxed_cut_copper.png", - // version 是方块最早出现的版本。0 代表早于 1.12,12 代表 1.12,13 代表 1.13,如此类推。255 代表尚未出现的版本", - "version": 17 - // idOld 是该方块在 1.12 的方块 id(如果有的话),必须加 minecraft:前缀,选填,默认为空字符串", - // needGlass 代表这个方块的下方需要依附其他方块,默认为否", - // isGlowing 代表这个方块是发光的,默认为否", - // endermanPickable 代表这个方块可以被末影人偷走,默认为否", - // burnable 代表这个方块会被火焰、岩浆或闪电点燃并燃烧,默认为否 - }, - // This json file includes information of all custom blocks. - // You can add your custom block in such format, including mod-blocks. - // But always remember to follow json format - // The whole file will failed to be parsed once an error occurred in json format, then SlopeCraft will complain. - { - // baseColor is the base color of this block on map, a compulsory item(EXTREMELY IMPORTANT!!!!) - "baseColor": 11, - // id is the block id with full blockstatus with minecraft: prefix. Compulsory item. - "id": "minecraft:stone_slab[type=top,waterlogged=false]", - // nameZH is the Chinese name. Compulsory item. - "nameZH": "石头台阶", - // nameEN is the English name. Compulsory item. - "nameEN": "Stone slab", - // icon is the image filename correspoing to this block, the image should be put under CustomBlocks directory. Compulsory item. - "icon": "stone.png", - // version is the earliest version when the block is added to Minecraft. 0 means earlier than 1.12, 12 means 1.12, 13 means 1.13, and so on. 255 means future version. - "version": 0, - // idOld is the blockid in 1.12 (if there is) with minecraft: prefix. default value is an empty string - "idOld": "minecraft:stone_slab[half=top,variant=stone]" - // needGlass means if this block must be setted on a solid block, default value is false - // isGlowing means if the block glows, default value is false. - // endermanPickable means if the block can be picked by enderman, default value is false - // burnable means if the block can be lit by fire, lava or lightning blot, default value is false - }, - { - "baseColor": 2, - "id": "minecraft:sandstone_slab[type=top,waterlogged=false]", - "nameZH": "砂岩台阶", - "nameEN": "Sandstone slab", - "icon": "smooth_sandstone.png", - "version": 0, - "idOld": "minecraft:stone_slab[half=top,variant=sandstone]" - }, - { - "baseColor": 11, - "id": "minecraft:cobblestone_slab[type=top,waterlogged=false]", - "nameZH": "圆石台阶", - "nameEN": "Cobblestone slab", - "icon": "cobblestone.png", - "version": 0, - "idOld": "minecraft:stone_slab[half=top,variant=cobblestone]" - }, - { - "baseColor": 28, - "id": "minecraft:brick_slab[type=top,waterlogged=false]", - "nameZH": "红砖台阶", - "nameEN": "Brick slab", - "icon": "bricks.png", - "version": 0, - "idOld": "minecraft:stone_slab[half=top,variant=brick]" - }, - { - "baseColor": 11, - "id": "minecraft:stone_brick_slab[type=top,waterlogged=false]", - "nameZH": "石砖台阶", - "nameEN": "Stonebrick slab", - "icon": "stone_bricks.png", - "version": 0, - "idOld": "minecraft:stone_slab[half=top,variant=stone_brick]" - }, - { - "baseColor": 35, - "id": "minecraft:nether_brick_slab[type=top,waterlogged=false]", - "nameZH": "下界砖台阶", - "nameEN": "Nether brick slab", - "icon": "nether_bricks.png", - "version": 0, - "idOld": "minecraft:stone_slab[half=top,variant=nether_brick]" - }, - { - "baseColor": 14, - "id": "minecraft:quartz_slab[type=top,waterlogged=false]", - "nameZH": "石英台阶", - "nameEN": "Quartz slab", - "icon": "quartz_block.png", - "version": 0, - "idOld": "minecraft:stone_slab[half=top,variant=quartz]" - }, - { - "baseColor": 13, - "id": "minecraft:oak_slab[type=top,waterlogged=false]", - "nameZH": "橡木台阶", - "nameEN": "Oak slab", - "icon": "oak_planks.png", - "version": 0, - "idOld": "minecraft:wooden_slab[half=top,variant=oak]" - }, - { - "baseColor": 34, - "id": "minecraft:spruce_slab[type=top,waterlogged=false]", - "nameZH": "云杉木台阶", - "nameEN": "Spruce slab", - "icon": "spruce_planks.png", - "version": 0, - "idOld": "minecraft:wooden_slab[half=top,variant=spruce]", - "burnable": true - }, - { - "baseColor": 2, - "id": "minecraft:birch_slab[type=top,waterlogged=false]", - "nameZH": "白桦木台阶", - "nameEN": "Birch slab", - "icon": "birch_planks.png", - "version": 0, - "idOld": "minecraft:wooden_slab[half=top,variant=birch]", - "burnable": true - }, - { - "baseColor": 10, - "id": "minecraft:jungle_slab[type=top,waterlogged=false]", - "nameZH": "丛林木台阶", - "nameEN": "Jungle slab", - "icon": "jungle_planks.png", - "version": 0, - "idOld": "minecraft:wooden_slab[half=top,variant=jungle]", - "burnable": true - }, - { - "baseColor": 15, - "id": "minecraft:acacia_slab[type=top,waterlogged=false]", - "nameZH": "金合欢木台阶", - "nameEN": "Acacia slab", - "icon": "acacia_planks.png", - "version": 0, - "idOld": "minecraft:wooden_slab[half=top,variant=acacia]", - "burnable": true - }, - { - "baseColor": 26, - "id": "minecraft:dark_oak_slab[type=top,waterlogged=false]", - "nameZH": "深色橡木台阶", - "nameEN": "Dark oak slab", - "icon": "dark_oak_planks.png", - "version": 0, - "idOld": "minecraft:wooden_slab[half=top,variant=dark_oak]", - "burnable": true - }, - { - "baseColor": 15, - "id": "minecraft:red_sandstone_slab[type=top,waterlogged=false]", - "nameZH": "红砂岩台阶", - "nameEN": "Red sandstone slab", - "icon": "smooth_red_sandstone.png", - "version": 0, - "idOld": "minecraft:stone_slab2[half=top,variant=red_sandstone]" - }, - { - "baseColor": 16, - "id": "minecraft:purpur_slab[type=top,waterlogged=false]", - "nameZH": "紫珀台阶", - "nameEN": "Purpur slab", - "icon": "purpur_block.png", - "version": 0, - "idOld": "minecraft:purpur_slab[half=top,variant=default]" - }, - { - "baseColor": 23, - "id": "minecraft:prismarine_slab[type=top,waterlogged=false]", - "nameZH": "海晶石台阶", - "nameEN": "Prismarine slab", - "icon": "prismarine.png", - "version": 13, - "idOld": "minecraft:stone_slab[half=top,variant=prismarine]" - }, - { - "baseColor": 31, - "id": "minecraft:prismarine_brick_slab[type=top,waterlogged=false]", - "nameZH": "海晶石砖台阶", - "nameEN": "Prismarine brick slab", - "icon": "prismarine_bricks.png", - "version": 13, - "idOld": "minecraft:stone_slab[half=top,variant=prismarine_bricks]" - }, - { - "baseColor": 31, - "id": "minecraft:dark_prismarine_slab[type=top,waterlogged=false]", - "nameZH": "暗海晶石台阶", - "nameEN": "Dark prismarine slab", - "icon": "dark_prismarine.png", - "version": 13, - "idOld": "minecraft:stone_slab[half=top,variant=dark_prismarine]" - }, - { - "baseColor": 2, - "id": "minecraft:sandstone", - "nameZH": "砂岩", - "nameEN": "Sand stone", - "icon": "sandstone.png", - "version": 0 - }, - { - "baseColor": 2, - "id": "minecraft:bone_block[axis=y]", - "nameZH": "骨块", - "nameEN": "Bone block", - "icon": "bone_block_top.png", - "version": 0 - }, - { - "baseColor": 10, - "id": "minecraft:granite", - "nameZH": "花岗岩", - "nameEN": "Granite", - "icon": "granite.png", - "version": 0, - "idOld": "minecraft:stone[variant=granite]" - }, - { - "baseColor": 11, - "id": "minecraft:andesite", - "nameZH": "安山岩", - "nameEN": "Andesite", - "icon": "andesite.png", - "version": 0, - "idOld": "minecraft:stone[variant=andesite]" - }, - { - "baseColor": 11, - "id": "minecraft:polished_andesite", - "nameZH": "磨制安山岩", - "nameEN": "Polished andesite", - "icon": "polished_andesite.png", - "version": 0, - "idOld": "minecraft:stone[variant=smooth_andesite]" - }, - { - "baseColor": 14, - "id": "minecraft:diorite", - "nameZH": "闪长岩", - "nameEN": "Diorite", - "icon": "diorite.png", - "version": 0, - "idOld": "minecraft:stone[variant=diorite]" - }, - { - "baseColor": 15, - "id": "minecraft:red_sandstone", - "nameZH": "红砂岩", - "nameEN": "Red sandstone", - "icon": "red_sandstone.png", - "version": 0, - "idOld": "minecraft:red_sandstone[type=red_sandstone]" - }, - { - "baseColor": 19, - "id": "minecraft:melon", - "nameZH": "西瓜", - "nameEN": "Melon", - "icon": "melon_top.png", - "version": 0, - "idOld": "minecraft:melon_block" - }, - { - "baseColor": 24, - "id": "minecraft:mycelium[snowy=false]", - "nameZH": "菌丝体", - "nameEN": "Mycelium", - "icon": "mycelium_top.png", - "version": 0 - }, - { - "baseColor": 27, - "id": "minecraft:moss_block", - "nameZH": "苔藓块", - "nameEN": "Moss block", - "icon": "moss_block.png", - "version": 17 - }, - { - "baseColor": 29, - "id": "minecraft:basalt[axis=y]", - "nameZH": "玄武岩", - "nameEN": "Basalt", - "icon": "basalt_top.png", - "version": 16 - }, - { - "baseColor": 29, - "id": "minecraft:blackstone", - "nameZH": "黑石", - "nameEN": "Black stone", - "icon": "blackstone.png", - "version": 16 - }, - { - "baseColor": 29, - "id": "minecraft:crying_obsidian", - "nameZH": "哭泣的黑曜石", - "nameEN": "Crying obsidian", - "icon": "crying_obsidian.png", - "version": 16 - }, - { - "baseColor": 29, - "id": "minecraft:netherite_block", - "nameZH": "下界合金块", - "nameEN": "Netherite block", - "icon": "netherite_block.png", - "version": 16 - }, - { - "baseColor": 43, - "id": "minecraft:tuff", - "nameZH": "凝灰岩", - "nameEN": "Tuff", - "icon": "tuff.png", - "version": 17 - } - ] -} \ No newline at end of file diff --git a/Blocks/CustomBlocks/acacia_planks.png b/Blocks/CustomBlocks/acacia_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/acacia_planks.png rename to Blocks/CustomBlocks/acacia_planks_slab.png diff --git a/Blocks/CustomBlocks/andesite_slab.png b/Blocks/CustomBlocks/andesite_slab.png new file mode 100644 index 00000000..73b3763d Binary files /dev/null and b/Blocks/CustomBlocks/andesite_slab.png differ diff --git a/Blocks/CustomBlocks/bamboo_mosaic_slab.png b/Blocks/CustomBlocks/bamboo_mosaic_slab.png new file mode 100644 index 00000000..2a40a896 Binary files /dev/null and b/Blocks/CustomBlocks/bamboo_mosaic_slab.png differ diff --git a/Blocks/CustomBlocks/bamboo_planks_slab.png b/Blocks/CustomBlocks/bamboo_planks_slab.png new file mode 100644 index 00000000..998a5538 Binary files /dev/null and b/Blocks/CustomBlocks/bamboo_planks_slab.png differ diff --git a/Blocks/CustomBlocks/birch_planks.png b/Blocks/CustomBlocks/birch_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/birch_planks.png rename to Blocks/CustomBlocks/birch_planks_slab.png diff --git a/Blocks/CustomBlocks/blackstone_slab.png b/Blocks/CustomBlocks/blackstone_slab.png new file mode 100644 index 00000000..c8219f02 Binary files /dev/null and b/Blocks/CustomBlocks/blackstone_slab.png differ diff --git a/Blocks/CustomBlocks/block_list.json b/Blocks/CustomBlocks/block_list.json new file mode 100644 index 00000000..36787117 --- /dev/null +++ b/Blocks/CustomBlocks/block_list.json @@ -0,0 +1,529 @@ +[ + // 这个文件列出了所有用户自定义的方块的信息", + // 你可以按照这样的格式自定义添加新的方块,包括 mod 方块", + // 但必须遵守 json 格式", + // 如果 json 格式出错,整个文件都会无法解析,SlopeCraft 会报错。 + { + // baseColor 是这种方块对应的基色,是必填项(非常非常重要!!!!!!!)", + "baseColor": 15, + // id 是这种方块的 id,也是必填项,最好附带完整的方块状态,必须加 minecraft:前缀", + "id": "minecraft:waxed_cut_copper_slab[type=top,waterlogged=false]", + // nameZH 是方块中文名称,必填", + "nameZH": "涂蜡的切制铜台阶", + // nameEN 是方块英文名称,必填", + "nameEN": "Waxed cut copper slab", + // icon 是方块对应图片的文件名,它应当放在 CustomBlocks 文件夹下。必填", + "icon": "waxed_cut_copper_slab.png", + // version 是方块最早出现的版本。0 代表早于 1.12,12 代表 1.12,13 代表 1.13,如此类推。255 代表尚未出现的版本", + "version": 17 + // idOld 是该方块在 1.12 的方块 id(如果有的话),必须加 minecraft:前缀,选填,默认为空字符串", + // needGlass 代表这个方块的下方需要依附其他方块,默认为否", + // isGlowing 代表这个方块是发光的,默认为否", + // endermanPickable 代表这个方块可以被末影人偷走,默认为否", + // burnable 代表这个方块会被火焰、岩浆或闪电点燃并燃烧,默认为否 + }, + // This json file includes information of all custom blocks. + // You can add your custom block in such format, including mod-blocks. + // But always remember to follow json format + // The whole file will failed to be parsed once an error occurred in json format, then SlopeCraft will complain. + { + // baseColor is the base color of this block on map, a compulsory item(EXTREMELY IMPORTANT!!!!) + "baseColor": 11, + // id is the block id with full blockstatus with minecraft: prefix. Compulsory item. + "id": "minecraft:stone_slab[type=top,waterlogged=false]", + // nameZH is the Chinese name. Compulsory item. + "nameZH": "石头台阶", + // nameEN is the English name. Compulsory item. + "nameEN": "Stone slab", + // icon is the image filename correspoing to this block, the image should be put under CustomBlocks directory. Compulsory item. + "icon": "stone_slab.png", + // version is the earliest version when the block is added to Minecraft. 0 means earlier than 1.12, 12 means 1.12, 13 means 1.13, and so on. 255 means future version. + "version": 14 + // idOld is the blockid in 1.12 (if there is) with minecraft: prefix. default value is an empty string + + // needGlass means if this block must be setted on a solid block, default value is false + // isGlowing means if the block glows, default value is false. + // endermanPickable means if the block can be picked by enderman, default value is false + // burnable means if the block can be lit by fire, lava or lightning blot, default value is false + }, + { + "baseColor": 2, + "id": "minecraft:sandstone_slab[type=top,waterlogged=false]", + "nameZH": "砂岩台阶", + "nameEN": "Sandstone slab", + "icon": "smooth_sandstone_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=sandstone]" + }, + { + "baseColor": 11, + "id": "minecraft:cobblestone_slab[type=top,waterlogged=false]", + "nameZH": "圆石台阶", + "nameEN": "Cobblestone slab", + "icon": "cobblestone_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=cobblestone]" + }, + { + "baseColor": 11, + "id": "minecraft:smooth_stone_slab[type=top,waterlogged=false]", + "nameZH": "平滑石头台阶", + "nameEN": "Smooth stone slab", + "icon": "smooth_stone_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=smooth_stone]" + }, + { + "baseColor": 28, + "id": "minecraft:brick_slab[type=top,waterlogged=false]", + "nameZH": "红砖台阶", + "nameEN": "Brick slab", + "icon": "bricks_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=brick]" + }, + { + "baseColor": 11, + "id": "minecraft:stone_brick_slab[type=top,waterlogged=false]", + "nameZH": "石砖台阶", + "nameEN": "Stonebrick slab", + "icon": "stone_bricks_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=stone_brick]" + }, + { + "baseColor": 35, + "id": "minecraft:nether_brick_slab[type=top,waterlogged=false]", + "nameZH": "下界砖台阶", + "nameEN": "Nether brick slab", + "icon": "nether_bricks_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=nether_brick]" + }, + { + "baseColor": 14, + "id": "minecraft:quartz_slab[type=top,waterlogged=false]", + "nameZH": "石英台阶", + "nameEN": "Quartz slab", + "icon": "quartz_block_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab[half=top,variant=quartz]" + }, + { + "baseColor": 13, + "id": "minecraft:oak_slab[type=top,waterlogged=false]", + "nameZH": "橡木台阶", + "nameEN": "Oak slab", + "icon": "oak_planks_slab.png", + "version": 0, + "idOld": "minecraft:wooden_slab[half=top,variant=oak]" + }, + { + "baseColor": 34, + "id": "minecraft:spruce_slab[type=top,waterlogged=false]", + "nameZH": "云杉木台阶", + "nameEN": "Spruce slab", + "icon": "spruce_planks_slab.png", + "version": 0, + "idOld": "minecraft:wooden_slab[half=top,variant=spruce]", + "burnable": true + }, + { + "baseColor": 2, + "id": "minecraft:birch_slab[type=top,waterlogged=false]", + "nameZH": "白桦木台阶", + "nameEN": "Birch slab", + "icon": "birch_planks_slab.png", + "version": 0, + "idOld": "minecraft:wooden_slab[half=top,variant=birch]", + "burnable": true + }, + { + "baseColor": 10, + "id": "minecraft:jungle_slab[type=top,waterlogged=false]", + "nameZH": "丛林木台阶", + "nameEN": "Jungle slab", + "icon": "jungle_planks_slab.png", + "version": 0, + "idOld": "minecraft:wooden_slab[half=top,variant=jungle]", + "burnable": true + }, + { + "baseColor": 15, + "id": "minecraft:acacia_slab[type=top,waterlogged=false]", + "nameZH": "金合欢木台阶", + "nameEN": "Acacia slab", + "icon": "acacia_planks_slab.png", + "version": 0, + "idOld": "minecraft:wooden_slab[half=top,variant=acacia]", + "burnable": true + }, + { + "baseColor": 26, + "id": "minecraft:dark_oak_slab[type=top,waterlogged=false]", + "nameZH": "深色橡木台阶", + "nameEN": "Dark oak slab", + "icon": "dark_oak_planks_slab.png", + "version": 0, + "idOld": "minecraft:wooden_slab[half=top,variant=dark_oak]", + "burnable": true + }, + { + "baseColor": 53, + "id": "minecraft:crimson_slab[type=top,waterlogged=false]", + "nameZH": "绯红木台阶", + "nameEN": "Crimson slab", + "icon": "crimson_planks_slab.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 56, + "id": "minecraft:warped_slab[type=top,waterlogged=false]", + "nameZH": "诡异木台阶", + "nameEN": "Warped slab", + "icon": "warped_planks_slab.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 28, + "id": "minecraft:mangrove_slab[type=top,waterlogged=false]", + "nameZH": "红树木台阶", + "nameEN": "Mangrove slab", + "icon": "mangrove_planks_slab.png", + "version": 19, + "burnable": true + }, + { + "baseColor": 36, + "id": "minecraft:cherry_slab[type=top,waterlogged=false]", + "nameZH": "樱花木台阶", + "nameEN": "Cherry slab", + "icon": "cherry_slab_slab.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 18, + "id": "minecraft:bamboo_slab[type=top,waterlogged=false]", + "nameZH": "竹台阶", + "nameEN": "Bamboo slab", + "icon": "bamboo_planks_slab.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 18, + "id": "minecraft:bamboo_mosaic_slab[type=top,waterlogged=false]", + "nameZH": "竹马赛克台阶", + "nameEN": "Bamboo mosaic slab", + "icon": "bamboo_mosaic_slab.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 15, + "id": "minecraft:red_sandstone_slab[type=top,waterlogged=false]", + "nameZH": "红砂岩台阶", + "nameEN": "Red sandstone slab", + "icon": "smooth_red_sandstone_slab.png", + "version": 0, + "idOld": "minecraft:stone_slab2[half=top,variant=red_sandstone]" + }, + { + "baseColor": 16, + "id": "minecraft:purpur_slab[type=top,waterlogged=false]", + "nameZH": "紫珀台阶", + "nameEN": "Purpur slab", + "icon": "purpur_block_slab.png", + "version": 0, + "idOld": "minecraft:purpur_slab[half=top,variant=default]" + }, + { + "baseColor": 23, + "id": "minecraft:prismarine_slab[type=top,waterlogged=false]", + "nameZH": "海晶石台阶", + "nameEN": "Prismarine slab", + "icon": "prismarine_slab.png", + "version": 13, + "idOld": "minecraft:stone_slab[half=top,variant=prismarine]" + }, + { + "baseColor": 31, + "id": "minecraft:prismarine_brick_slab[type=top,waterlogged=false]", + "nameZH": "海晶石砖台阶", + "nameEN": "Prismarine brick slab", + "icon": "prismarine_bricks_slab.png", + "version": 13, + "idOld": "minecraft:stone_slab[half=top,variant=prismarine_bricks]" + }, + { + "baseColor": 31, + "id": "minecraft:dark_prismarine_slab[type=top,waterlogged=false]", + "nameZH": "暗海晶石台阶", + "nameEN": "Dark prismarine slab", + "icon": "dark_prismarine_slab.png", + "version": 13, + "idOld": "minecraft:stone_slab[half=top,variant=dark_prismarine]" + }, + { + "baseColor": 2, + "id": "minecraft:end_stone_brick_slab[type=top,waterlogged=false]", + "nameZH": "末地石砖台阶", + "nameEN": "Endstone brick slab", + "icon": "end_stone_bricks_slab.png", + "version": 14 + }, + { + "baseColor": 2, + "id": "minecraft:sandstone", + "nameZH": "砂岩", + "nameEN": "Sand stone", + "icon": "sandstone.png", + "version": 0 + }, + { + "baseColor": 2, + "id": "minecraft:bone_block[axis=y]", + "nameZH": "骨块", + "nameEN": "Bone block", + "icon": "bone_block_top.png", + "version": 0 + }, + { + "baseColor": 10, + "id": "minecraft:granite", + "nameZH": "花岗岩", + "nameEN": "Granite", + "icon": "granite.png", + "version": 0, + "idOld": "minecraft:stone[variant=granite]" + }, + { + "baseColor": 10, + "id": "minecraft:granite_slab[type=top,waterlogged=false]", + "nameZH": "花岗岩台阶", + "nameEN": "Granite slab", + "icon": "granite_slab.png", + "version": 14 + }, + { + "baseColor": 10, + "id": "minecraft:polished_granite", + "nameZH": "磨制花岗岩", + "nameEN": "Polished granite", + "icon": "polished_granite.png", + "version": 0, + "idOld": "minecraft:stone[variant=smooth_granite]" + }, + { + "baseColor": 10, + "id": "minecraft:polished_granite_slab[type=top,waterlogged=false]", + "nameZH": "磨制花岗岩台阶", + "nameEN": "Polished granite slab", + "icon": "polished_granite_slab.png", + "version": 14 + }, + { + "baseColor": 11, + "id": "minecraft:andesite", + "nameZH": "安山岩", + "nameEN": "Andesite", + "icon": "andesite.png", + "version": 0, + "idOld": "minecraft:stone[variant=andesite]" + }, + { + "baseColor": 11, + "id": "minecraft:andesite_slab[type=top,waterlogged=false]", + "nameZH": "安山岩台阶", + "nameEN": "Andesite slab", + "icon": "andesite_slab.png", + "version": 14 + }, + { + "baseColor": 11, + "id": "minecraft:polished_andesite", + "nameZH": "磨制安山岩", + "nameEN": "Polished andesite", + "icon": "polished_andesite.png", + "version": 0, + "idOld": "minecraft:stone[variant=smooth_andesite]" + }, + { + "baseColor": 11, + "id": "minecraft:polished_andesite_slab[type=top,waterlogged=false]", + "nameZH": "磨制安山岩台阶", + "nameEN": "Polished andesite slab", + "icon": "polished_andesite_slab.png", + "version": 14 + }, + { + "baseColor": 14, + "id": "minecraft:diorite", + "nameZH": "闪长岩", + "nameEN": "Diorite", + "icon": "diorite.png", + "version": 0, + "idOld": "minecraft:stone[variant=diorite]" + }, + { + "baseColor": 14, + "id": "minecraft:diorite_slab[type=top,waterlogged=false]", + "nameZH": "闪长岩台阶", + "nameEN": "Diorite slab", + "icon": "diorite_slab.png", + "version": 14 + }, + { + "baseColor": 14, + "id": "minecraft:polished_diorite", + "nameZH": "磨制闪长岩", + "nameEN": "Polished diorite", + "icon": "polished_diorite.png", + "version": 0, + "idOld": "minecraft:stone[variant=smooth_diorite]" + }, + { + "baseColor": 14, + "id": "minecraft:polished_diorite_slab[type=top,waterlogged=false]", + "nameZH": "磨制闪长岩台阶", + "nameEN": "Polished diorite slab", + "icon": "polished_diorite_slab.png", + "version": 14 + }, + { + "baseColor": 15, + "id": "minecraft:red_sandstone", + "nameZH": "红砂岩", + "nameEN": "Red sandstone", + "icon": "red_sandstone.png", + "version": 0, + "idOld": "minecraft:red_sandstone[type=red_sandstone]" + }, + { + "baseColor": 19, + "id": "minecraft:melon", + "nameZH": "西瓜", + "nameEN": "Melon", + "icon": "melon_top.png", + "version": 0, + "idOld": "minecraft:melon_block", + "endermanPickable": true + }, + { + "baseColor": 24, + "id": "minecraft:mycelium[snowy=false]", + "nameZH": "菌丝体", + "nameEN": "Mycelium", + "icon": "mycelium_top.png", + "version": 0, + "endermanPickable": true + }, + { + "baseColor": 27, + "id": "minecraft:moss_block", + "nameZH": "苔藓块", + "nameEN": "Moss block", + "icon": "moss_block.png", + "version": 17, + "endermanPickable": true + }, + { + "baseColor": 29, + "id": "minecraft:basalt[axis=y]", + "nameZH": "玄武岩", + "nameEN": "Basalt", + "icon": "basalt_top.png", + "version": 16 + }, + { + "baseColor": 29, + "id": "minecraft:blackstone", + "nameZH": "黑石", + "nameEN": "Black stone", + "icon": "blackstone.png", + "version": 16 + }, + { + "baseColor": 29, + "id": "minecraft:blackstone_slab[type=top,waterlogged=false]", + "nameZH": "黑石台阶", + "nameEN": "Black stone slab", + "icon": "blackstone_slab.png", + "version": 16 + }, + { + "baseColor": 29, + "id": "minecraft:polished_blackstone_slab[type=top,waterlogged=false]", + "nameZH": "磨制黑石台阶", + "nameEN": "Polished black stone slab", + "icon": "polished_blackstone_slab.png", + "version": 16 + }, + { + "baseColor": 59, + "id": "minecraft:deepslate_brick_slab[type=top,waterlogged=false]", + "nameZH": "深板岩砖台阶", + "nameEN": "Deepslate bricks slab", + "icon": "deepslate_bricks_slab.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:deepslate_tile_slab[type=top,waterlogged=false]", + "nameZH": "深板岩瓦台阶", + "nameEN": "Deepslate tiles slab", + "icon": "deepslate_tiles_slab.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:cobbled_deepslate_slab[type=top,waterlogged=false]", + "nameZH": "深板岩圆石台阶", + "nameEN": "Cobbled deepslate slab", + "icon": "cobbled_deepslate_slab.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:polished_deepslate_slab[type=top,waterlogged=false]", + "nameZH": "磨制深板岩台阶", + "nameEN": "Polished deepslate slab", + "icon": "polished_deepslate_slab.png", + "version": 17 + }, + { + "baseColor": 29, + "id": "minecraft:crying_obsidian", + "nameZH": "哭泣的黑曜石", + "nameEN": "Crying obsidian", + "icon": "crying_obsidian.png", + "version": 16 + }, + { + "baseColor": 29, + "id": "minecraft:netherite_block", + "nameZH": "下界合金块", + "nameEN": "Netherite block", + "icon": "netherite_block.png", + "version": 16 + }, + { + "baseColor": 43, + "id": "minecraft:tuff", + "nameZH": "凝灰岩", + "nameEN": "Tuff", + "icon": "tuff.png", + "version": 17 + }, + { + "baseColor": 44, + "id": "minecraft:mud_brick_slab[type=top,waterlogged=false]", + "nameZH": "泥砖台阶", + "nameEN": "Mud bricks slab", + "icon": "mud_bricks_slab.png", + "version": 19 + } +] \ No newline at end of file diff --git a/Blocks/CustomBlocks/bricks.png b/Blocks/CustomBlocks/bricks_slab.png similarity index 100% rename from Blocks/CustomBlocks/bricks.png rename to Blocks/CustomBlocks/bricks_slab.png diff --git a/Blocks/CustomBlocks/cherry_slab_slab.png b/Blocks/CustomBlocks/cherry_slab_slab.png new file mode 100644 index 00000000..ec5d0802 Binary files /dev/null and b/Blocks/CustomBlocks/cherry_slab_slab.png differ diff --git a/Blocks/CustomBlocks/cobbled_deepslate_slab.png b/Blocks/CustomBlocks/cobbled_deepslate_slab.png new file mode 100644 index 00000000..3a21c4ba Binary files /dev/null and b/Blocks/CustomBlocks/cobbled_deepslate_slab.png differ diff --git a/Blocks/CustomBlocks/cobblestone.png b/Blocks/CustomBlocks/cobblestone_slab.png similarity index 100% rename from Blocks/CustomBlocks/cobblestone.png rename to Blocks/CustomBlocks/cobblestone_slab.png diff --git a/Blocks/CustomBlocks/crimson_planks.png b/Blocks/CustomBlocks/crimson_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/crimson_planks.png rename to Blocks/CustomBlocks/crimson_planks_slab.png diff --git a/Blocks/CustomBlocks/dark_oak_planks.png b/Blocks/CustomBlocks/dark_oak_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/dark_oak_planks.png rename to Blocks/CustomBlocks/dark_oak_planks_slab.png diff --git a/Blocks/CustomBlocks/dark_prismarine.png b/Blocks/CustomBlocks/dark_prismarine_slab.png similarity index 100% rename from Blocks/CustomBlocks/dark_prismarine.png rename to Blocks/CustomBlocks/dark_prismarine_slab.png diff --git a/Blocks/CustomBlocks/deepslate_bricks.png b/Blocks/CustomBlocks/deepslate_bricks_slab.png similarity index 100% rename from Blocks/CustomBlocks/deepslate_bricks.png rename to Blocks/CustomBlocks/deepslate_bricks_slab.png diff --git a/Blocks/CustomBlocks/deepslate_tiles_slab.png b/Blocks/CustomBlocks/deepslate_tiles_slab.png new file mode 100644 index 00000000..a765dc8b Binary files /dev/null and b/Blocks/CustomBlocks/deepslate_tiles_slab.png differ diff --git a/Blocks/CustomBlocks/diorite_slab.png b/Blocks/CustomBlocks/diorite_slab.png new file mode 100644 index 00000000..06389e30 Binary files /dev/null and b/Blocks/CustomBlocks/diorite_slab.png differ diff --git a/Blocks/CustomBlocks/end_stone_bricks.png b/Blocks/CustomBlocks/end_stone_bricks_slab.png similarity index 100% rename from Blocks/CustomBlocks/end_stone_bricks.png rename to Blocks/CustomBlocks/end_stone_bricks_slab.png diff --git a/Blocks/CustomBlocks/end_stone.png b/Blocks/CustomBlocks/end_stone_slab.png similarity index 100% rename from Blocks/CustomBlocks/end_stone.png rename to Blocks/CustomBlocks/end_stone_slab.png diff --git a/Blocks/CustomBlocks/granite_slab.png b/Blocks/CustomBlocks/granite_slab.png new file mode 100644 index 00000000..4c471159 Binary files /dev/null and b/Blocks/CustomBlocks/granite_slab.png differ diff --git a/Blocks/CustomBlocks/jungle_planks.png b/Blocks/CustomBlocks/jungle_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/jungle_planks.png rename to Blocks/CustomBlocks/jungle_planks_slab.png diff --git a/Blocks/CustomBlocks/mangrove_planks_slab.png b/Blocks/CustomBlocks/mangrove_planks_slab.png new file mode 100644 index 00000000..d3712ee7 Binary files /dev/null and b/Blocks/CustomBlocks/mangrove_planks_slab.png differ diff --git a/Blocks/CustomBlocks/mud_bricks_slab.png b/Blocks/CustomBlocks/mud_bricks_slab.png new file mode 100644 index 00000000..38a1152f Binary files /dev/null and b/Blocks/CustomBlocks/mud_bricks_slab.png differ diff --git a/Blocks/CustomBlocks/nether_bricks.png b/Blocks/CustomBlocks/nether_bricks_slab.png similarity index 100% rename from Blocks/CustomBlocks/nether_bricks.png rename to Blocks/CustomBlocks/nether_bricks_slab.png diff --git a/Blocks/CustomBlocks/oak_planks.png b/Blocks/CustomBlocks/oak_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/oak_planks.png rename to Blocks/CustomBlocks/oak_planks_slab.png diff --git a/Blocks/CustomBlocks/polished_andesite_slab.png b/Blocks/CustomBlocks/polished_andesite_slab.png new file mode 100644 index 00000000..ec083f5f Binary files /dev/null and b/Blocks/CustomBlocks/polished_andesite_slab.png differ diff --git a/Blocks/CustomBlocks/polished_blackstone_slab.png b/Blocks/CustomBlocks/polished_blackstone_slab.png new file mode 100644 index 00000000..a1a893a9 Binary files /dev/null and b/Blocks/CustomBlocks/polished_blackstone_slab.png differ diff --git a/Blocks/CustomBlocks/polished_deepslate_slab.png b/Blocks/CustomBlocks/polished_deepslate_slab.png new file mode 100644 index 00000000..98ffd373 Binary files /dev/null and b/Blocks/CustomBlocks/polished_deepslate_slab.png differ diff --git a/Blocks/FixedBlocks/polished_diorite.png b/Blocks/CustomBlocks/polished_diorite.png similarity index 100% rename from Blocks/FixedBlocks/polished_diorite.png rename to Blocks/CustomBlocks/polished_diorite.png diff --git a/Blocks/CustomBlocks/polished_diorite_slab.png b/Blocks/CustomBlocks/polished_diorite_slab.png new file mode 100644 index 00000000..91283e04 Binary files /dev/null and b/Blocks/CustomBlocks/polished_diorite_slab.png differ diff --git a/Blocks/FixedBlocks/polished_granite.png b/Blocks/CustomBlocks/polished_granite.png similarity index 100% rename from Blocks/FixedBlocks/polished_granite.png rename to Blocks/CustomBlocks/polished_granite.png diff --git a/Blocks/CustomBlocks/polished_granite_slab.png b/Blocks/CustomBlocks/polished_granite_slab.png new file mode 100644 index 00000000..3a82d4e8 Binary files /dev/null and b/Blocks/CustomBlocks/polished_granite_slab.png differ diff --git a/Blocks/CustomBlocks/prismarine_bricks.png b/Blocks/CustomBlocks/prismarine_bricks_slab.png similarity index 100% rename from Blocks/CustomBlocks/prismarine_bricks.png rename to Blocks/CustomBlocks/prismarine_bricks_slab.png diff --git a/Blocks/CustomBlocks/prismarine.png b/Blocks/CustomBlocks/prismarine_slab.png similarity index 100% rename from Blocks/CustomBlocks/prismarine.png rename to Blocks/CustomBlocks/prismarine_slab.png diff --git a/Blocks/CustomBlocks/purpur_block.png b/Blocks/CustomBlocks/purpur_block_slab.png similarity index 100% rename from Blocks/CustomBlocks/purpur_block.png rename to Blocks/CustomBlocks/purpur_block_slab.png diff --git a/Blocks/CustomBlocks/quartz_block.png b/Blocks/CustomBlocks/quartz_block_slab.png similarity index 100% rename from Blocks/CustomBlocks/quartz_block.png rename to Blocks/CustomBlocks/quartz_block_slab.png diff --git a/Blocks/CustomBlocks/smooth_red_sandstone.png b/Blocks/CustomBlocks/smooth_red_sandstone_slab.png similarity index 100% rename from Blocks/CustomBlocks/smooth_red_sandstone.png rename to Blocks/CustomBlocks/smooth_red_sandstone_slab.png diff --git a/Blocks/CustomBlocks/smooth_sandstone.png b/Blocks/CustomBlocks/smooth_sandstone_slab.png similarity index 100% rename from Blocks/CustomBlocks/smooth_sandstone.png rename to Blocks/CustomBlocks/smooth_sandstone_slab.png diff --git a/Blocks/CustomBlocks/smooth_stone.png b/Blocks/CustomBlocks/smooth_stone_slab.png similarity index 100% rename from Blocks/CustomBlocks/smooth_stone.png rename to Blocks/CustomBlocks/smooth_stone_slab.png diff --git a/Blocks/CustomBlocks/spruce_planks.png b/Blocks/CustomBlocks/spruce_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/spruce_planks.png rename to Blocks/CustomBlocks/spruce_planks_slab.png diff --git a/Blocks/CustomBlocks/stone_bricks.png b/Blocks/CustomBlocks/stone_bricks_slab.png similarity index 100% rename from Blocks/CustomBlocks/stone_bricks.png rename to Blocks/CustomBlocks/stone_bricks_slab.png diff --git a/Blocks/CustomBlocks/stone.png b/Blocks/CustomBlocks/stone_slab.png similarity index 100% rename from Blocks/CustomBlocks/stone.png rename to Blocks/CustomBlocks/stone_slab.png diff --git a/Blocks/CustomBlocks/warped_planks.png b/Blocks/CustomBlocks/warped_planks_slab.png similarity index 100% rename from Blocks/CustomBlocks/warped_planks.png rename to Blocks/CustomBlocks/warped_planks_slab.png diff --git a/Blocks/CustomBlocks/waxed_copper_block.png b/Blocks/CustomBlocks/waxed_copper_block_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_copper_block.png rename to Blocks/CustomBlocks/waxed_copper_block_slab.png diff --git a/Blocks/CustomBlocks/waxed_cut_copper.png b/Blocks/CustomBlocks/waxed_cut_copper_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_cut_copper.png rename to Blocks/CustomBlocks/waxed_cut_copper_slab.png diff --git a/Blocks/CustomBlocks/waxed_exposed_copper.png b/Blocks/CustomBlocks/waxed_exposed_copper_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_exposed_copper.png rename to Blocks/CustomBlocks/waxed_exposed_copper_slab.png diff --git a/Blocks/CustomBlocks/waxed_exposed_cut_copper.png b/Blocks/CustomBlocks/waxed_exposed_cut_copper_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_exposed_cut_copper.png rename to Blocks/CustomBlocks/waxed_exposed_cut_copper_slab.png diff --git a/Blocks/CustomBlocks/waxed_oxidized_copper.png b/Blocks/CustomBlocks/waxed_oxidized_copper_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_oxidized_copper.png rename to Blocks/CustomBlocks/waxed_oxidized_copper_slab.png diff --git a/Blocks/CustomBlocks/waxed_oxidized_cut_copper.png b/Blocks/CustomBlocks/waxed_oxidized_cut_copper_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_oxidized_cut_copper.png rename to Blocks/CustomBlocks/waxed_oxidized_cut_copper_slab.png diff --git a/Blocks/CustomBlocks/waxed_weathered_copper.png b/Blocks/CustomBlocks/waxed_weathered_copper_slab.png similarity index 100% rename from Blocks/CustomBlocks/waxed_weathered_copper.png rename to Blocks/CustomBlocks/waxed_weathered_copper_slab.png diff --git a/Blocks/FixedBlocks.json b/Blocks/FixedBlocks.json deleted file mode 100644 index b415758a..00000000 --- a/Blocks/FixedBlocks.json +++ /dev/null @@ -1,1672 +0,0 @@ -{ - "FixedBlocks": [ - { - "baseColor": 0, - "id": "minecraft:glass", - "nameZH": "玻璃", - "nameEN": "Glass", - "icon": "glass.png", - "version": 0 - }, - { - "baseColor": 1, - "id": "minecraft:grass_block[snowy=false]", - "nameZH": "草方块", - "nameEN": "Grass block", - "icon": "grass_block.png", - "version": 0, - "idOld": "minecraft:grass[snowy=false]", - "endermanPickable": true, - "wallUseable": false - }, - { - "baseColor": 1, - "id": "minecraft:slime_block", - "nameZH": "黏液块", - "nameEN": "Slime block", - "icon": "slime_block.png", - "version": 0, - "idOld": "minecraft:slime" - }, - { - "baseColor": 2, - "id": "minecraft:birch_planks", - "nameZH": "白桦木板", - "nameEN": "Birch plank", - "icon": "birch_planks.png", - "version": 0, - "idOld": "minecraft:planks[variant=birch]", - "burnable": true - }, - { - "baseColor": 2, - "id": "minecraft:smooth_sandstone", - "nameZH": "平滑砂岩", - "nameEN": "Smooth sandstone", - "icon": "smooth_sandstone.png", - "version": 0, - "idOld": "minecraft:sandstone[type=smooth_sandstone]" - }, - { - "baseColor": 2, - "id": "minecraft:glowstone", - "nameZH": "荧石", - "nameEN": "Glowstone", - "icon": "glowstone.png", - "version": 0, - "isGlowing": true - }, - { - "baseColor": 2, - "id": "minecraft:end_stone", - "nameZH": "末地石", - "nameEN": "End stone", - "icon": "end_stone.png", - "version": 0 - }, - { - "baseColor": 2, - "id": "minecraft:end_stone_bricks", - "nameZH": "末地石砖", - "nameEN": "Endstone bricks", - "icon": "end_stone_bricks.png", - "version": 0, - "idOld": "minecraft:end_bricks" - }, - { - "baseColor": 2, - "id": "minecraft:turtle_egg[eggs=4,hatch=0]", - "nameZH": "海龟蛋", - "nameEN": "Turtle egg", - "icon": "turtle_egg.png", - "version": 13, - "needGlass": true, - "wallUseable": false - }, - { - "baseColor": 2, - "id": "minecraft:ochre_froglight[axis=y]", - "nameZH": "赭黄蛙明灯", - "nameEN": "Ochre Froglight", - "icon": "ochre_froglight_top.png", - "version": 19, - "isGlowing": true - }, - { - "baseColor": 3, - "id": "minecraft:mushroom_stem[east=true,west=true,north=true,south=true,up=true,down=true]", - "nameZH": "蘑菇柄", - "nameEN": "Mushroom stem", - "icon": "mushroom_stem.png", - "version": 13, - "burnable": true - }, - { - "baseColor": 3, - "id": "minecraft:cobweb", - "nameZH": "蜘蛛网", - "nameEN": "Cobweb", - "icon": "cobweb.png", - "version": 0, - "idOld": "minecraft:web" - }, - { - "baseColor": 4, - "id": "minecraft:redstone_block", - "nameZH": "红石块", - "nameEN": "Redstone block", - "icon": "redstone_block.png", - "version": 0 - }, - { - "baseColor": 4, - "id": "minecraft:tnt[unstable=false]", - "nameZH": "TNT", - "nameEN": "TNT", - "icon": "tnt.png", - "version": 0, - "idOld": "minecraft:tnt[explode=false]", - "endermanPickable": true, - "burnable": true - }, - { - "baseColor": 5, - "id": "minecraft:ice", - "nameZH": "冰", - "nameEN": "Ice", - "icon": "ice.png", - "version": 0 - }, - { - "baseColor": 5, - "id": "minecraft:packed_ice", - "nameZH": "浮冰", - "nameEN": "Packed ice", - "icon": "packed_ice.png", - "version": 0 - }, - { - "baseColor": 5, - "id": "minecraft:blue_ice", - "nameZH": "蓝冰", - "nameEN": "Blue ice", - "icon": "blue_ice.png", - "version": 13 - }, - { - "baseColor": 6, - "id": "minecraft:iron_block", - "nameZH": "铁块", - "nameEN": "Iron block", - "icon": "iron_block.png", - "version": 0 - }, - { - "baseColor": 6, - "id": "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", - "nameZH": "酿造台", - "nameEN": "brewing stand", - "icon": "brewing_stand.png", - "version": 0, - "wallUseable": false - }, - { - "baseColor": 6, - "id": "minecraft:heavy_weighted_pressure_plate[power=0]", - "nameZH": "重质测重压力板", - "nameEN": "Iron pressure plate", - "icon": "heavy_weighted_pressure_plate.png", - "version": 0, - "needGlass": true, - "wallUseable": false - }, - { - "baseColor": 6, - "id": "minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false]", - "nameZH": "铁活板门", - "nameEN": "Iron trapdoor", - "icon": "iron_trapdoor.png", - "version": 0, - "idOld": "minecraft:iron_trapdoor[facing=north,half=top,open=false]", - "wallUseable": false - }, - { - "baseColor": 6, - "id": "minecraft:lantern[hanging=false]", - "nameZH": "灯笼", - "nameEN": "lantern", - "icon": "lantern.png", - "version": 14, - "wallUseable": false - }, - { - "baseColor": 6, - "id": "minecraft:grindstone[face=floor,facing=north]", - "nameZH": "砂轮", - "nameEN": "Grindstone", - "icon": "grindstone.png", - "version": 14, - "wallUseable": false - }, - { - "baseColor": 7, - "id": "minecraft:oak_leaves[distance=7,persistent=true]", - "nameZH": "橡树树叶", - "nameEN": "Oak leaves", - "icon": "oak_leaves.png", - "version": 0, - "idOld": "minecraft:leaves[variant=oak,check_decay=false,decayable=false]" - }, - { - "baseColor": 7, - "id": "minecraft:spruce_leaves[distance=7,persistent=true]", - "nameZH": "云杉树叶", - "nameEN": "Spurce leaves", - "icon": "spruce_leaves.png", - "version": 0, - "idOld": "minecraft:leaves[variant=spruce,check_decay=false,decayable=false]" - }, - { - "baseColor": 7, - "id": "minecraft:birch_leaves[distance=7,persistent=true]", - "nameZH": "白桦树叶", - "nameEN": "Brich leaves", - "icon": "birch_leaves.png", - "version": 0, - "idOld": "minecraft:leaves[variant=birch,check_decay=false,decayable=false]" - }, - { - "baseColor": 7, - "id": "minecraft:jungle_leaves[distance=7,persistent=true]", - "nameZH": "丛林树叶", - "nameEN": "Jungle leaves", - "icon": "jungle_leaves.png", - "version": 0, - "idOld": "minecraft:leaves[variant=jungle,check_decay=false,decayable=false]" - }, - { - "baseColor": 7, - "id": "minecraft:acacia_leaves[distance=7,persistent=true]", - "nameZH": "金合欢树叶", - "nameEN": "Acacia leaves", - "icon": "acacia_leaves.png", - "version": 0, - "idOld": "minecraft:leaves2[variant=acacia,check_decay=false,decayable=false]" - }, - { - "baseColor": 7, - "id": "minecraft:dark_oak_leaves[distance=7,persistent=true]", - "nameZH": "深色橡树树叶", - "nameEN": "Dark oat leaves", - "icon": "dark_oak_leaves.png", - "version": 0, - "idOld": "minecraft:leaves2[variant=dark_oak,check_decay=false,decayable=false]" - }, - { - "baseColor": 8, - "id": "minecraft:white_concrete", - "nameZH": "白色混凝土", - "nameEN": "White concrete", - "icon": "white_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=white]" - }, - { - "baseColor": 8, - "id": "minecraft:white_wool", - "nameZH": "白色羊毛", - "nameEN": "White wool", - "icon": "white_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=white]" - }, - { - "baseColor": 8, - "id": "minecraft:white_stained_glass", - "nameZH": "白色染色玻璃", - "nameEN": "White glass", - "icon": "white_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=white]" - }, - { - "baseColor": 8, - "id": "minecraft:snow_block", - "nameZH": "雪块", - "nameEN": "Snow", - "icon": "snow_block.png", - "version": 0, - "idOld": "minecraft:snow" - }, - { - "baseColor": 9, - "id": "minecraft:clay", - "nameZH": "黏土块", - "nameEN": "Clay block", - "icon": "clay.png", - "version": 0, - "endermanPickable": true - }, - { - "baseColor": 10, - "id": "minecraft:coarse_dirt", - "nameZH": "砂土", - "nameEN": "Coarse dirt", - "icon": "coarse_dirt.png", - "version": 13, - "idOld": "minecraft:dirt[variant=coarse_dirt,snowy=false]", - "endermanPickable": true - }, - { - "baseColor": 10, - "id": "minecraft:polished_granite", - "nameZH": "磨制花岗岩", - "nameEN": "Polished granite", - "icon": "polished_granite.png", - "version": 0, - "idOld": "minecraft:stone[variant=smooth_granite]" - }, - { - "baseColor": 10, - "id": "minecraft:jungle_planks", - "nameZH": "丛林木板", - "nameEN": "Jungle plank", - "icon": "jungle_planks.png", - "version": 0, - "idOld": "minecraft:planks[variant=jungle]", - "burnable": true - }, - { - "baseColor": 10, - "id": "minecraft:brown_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]", - "nameZH": "棕色蘑菇方块", - "nameEN": "Brown mushroom block", - "icon": "brown_mushroom_block.png", - "version": 0, - "idOld": "minecraft:brown_mushroom_block[variant=all_outside]" - }, - { - "baseColor": 10, - "id": "minecraft:dirt", - "nameZH": "泥土", - "nameEN": "Dirt", - "icon": "dirt.png", - "version": 0, - "idOld": "minecraft:dirt[variant=dirt,snowy=false]", - "endermanPickable": true - }, - { - "baseColor": 10, - "id": "minecraft:packed_mud", - "nameZH": "泥坯", - "nameEN": "Packed mud", - "icon": "packed_mud.png", - "version": 19 - }, - { - "baseColor": 11, - "id": "minecraft:cobblestone", - "nameZH": "圆石", - "nameEN": "Cobblestone", - "icon": "cobblestone.png", - "version": 0 - }, - { - "baseColor": 11, - "id": "minecraft:stone", - "nameZH": "石头", - "nameEN": "Stone", - "icon": "stone.png", - "version": 0, - "idOld": "minecraft:stone[variant=stone]" - }, - { - "baseColor": 11, - "id": "minecraft:smooth_stone", - "nameZH": "平滑石头", - "nameEN": "Smooth stone", - "icon": "smooth_stone.png", - "version": 13 - }, - { - "baseColor": 11, - "id": "minecraft:stone_bricks", - "nameZH": "石砖", - "nameEN": "Stone brick", - "icon": "stone_bricks.png", - "version": 0, - "idOld": "minecraft:stonebrick[variant=stonebrick]" - }, - { - "baseColor": 12, - "id": "minecraft:water[level=0]", - "nameZH": "水", - "nameEN": "Water", - "icon": "water.png", - "version": 0, - "wallUseable": false - }, - { - "baseColor": 13, - "id": "minecraft:oak_planks", - "nameZH": "橡木木板", - "nameEN": "Oak plank", - "icon": "oak_planks.png", - "version": 0, - "idOld": "minecraft:planks[variant=oak]", - "burnable": true - }, - { - "baseColor": 14, - "id": "minecraft:polished_diorite", - "nameZH": "磨制闪长岩", - "nameEN": "Polished diorite", - "icon": "polished_diorite.png", - "version": 0, - "idOld": "minecraft:stone[variant=smooth_diorite]" - }, - { - "baseColor": 14, - "id": "minecraft:quartz_block", - "nameZH": "石英块", - "nameEN": "Quartz block", - "icon": "quartz_block.png", - "version": 0, - "idOld": "minecraft:quartz_block[variant=default]" - }, - { - "baseColor": 14, - "id": "minecraft:sea_lantern", - "nameZH": "海晶灯", - "nameEN": "Sea lantern", - "icon": "sea_lantern.png", - "version": 0 - }, - { - "baseColor": 14, - "id": "minecraft:target[power=0]", - "nameZH": "标靶", - "nameEN": "Target", - "icon": "target.png", - "version": 16 - }, - { - "baseColor": 15, - "id": "minecraft:orange_concrete", - "nameZH": "橙色混凝土", - "nameEN": "Orange concrete", - "icon": "orange_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=orange]" - }, - { - "baseColor": 15, - "id": "minecraft:orange_wool", - "nameZH": "橙色羊毛", - "nameEN": "Orange wool", - "icon": "orange_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=orange]" - }, - { - "baseColor": 15, - "id": "minecraft:orange_stained_glass", - "nameZH": "橙色染色玻璃", - "nameEN": "Orange glass", - "icon": "orange_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=orange]" - }, - { - "baseColor": 15, - "id": "minecraft:acacia_planks", - "nameZH": "金合欢木板", - "nameEN": "Acacia plank", - "icon": "acacia_planks.png", - "version": 0, - "idOld": "minecraft:planks[variant=acacia]", - "burnable": true - }, - { - "baseColor": 15, - "id": "minecraft:pumpkin", - "nameZH": "南瓜", - "nameEN": "Pumpkin", - "icon": "pumpkin.png", - "version": 0, - "idOld": "minecraft:pumpkin[variant=north]", - "endermanPickable": true - }, - { - "baseColor": 15, - "id": "minecraft:terracotta", - "nameZH": "陶瓦", - "nameEN": "Terracotta", - "icon": "terracotta.png", - "version": 0, - "idOld": "minecraft:hardened_clay" - }, - { - "baseColor": 15, - "id": "minecraft:smooth_red_sandstone", - "nameZH": "平滑红砂岩", - "nameEN": "Smooth red sandstone", - "icon": "smooth_red_sandstone.png", - "version": 0, - "idOld": "minecraft:red_sandstone[type=smooth_red_sandstone]" - }, - { - "baseColor": 15, - "id": "minecraft:honey_block", - "nameZH": "蜂蜜块", - "nameEN": "Honey block", - "icon": "honey_block.png", - "version": 15 - }, - { - "baseColor": 15, - "id": "minecraft:honeycomb_block", - "nameZH": "蜜脾块", - "nameEN": "Honey comb block", - "icon": "honeycomb_block.png", - "version": 15 - }, - { - "baseColor": 15, - "id": "minecraft:raw_copper_block", - "nameZH": "粗铜块", - "nameEN": "Raw copper block", - "icon": "raw_copper_block.png", - "version": 17 - }, - { - "baseColor": 15, - "id": "minecraft:waxed_copper_block", - "nameZH": "涂蜡铜块", - "nameEN": "Waxed copper", - "icon": "waxed_copper_block.png", - "version": 17 - }, - { - "baseColor": 15, - "id": "minecraft:waxed_cut_copper", - "nameZH": "涂蜡切制铜块", - "nameEN": "Waxed cut copper", - "icon": "waxed_cut_copper.png", - "version": 17 - }, - { - "baseColor": 16, - "id": "minecraft:magenta_concrete", - "nameZH": "品红色混凝土", - "nameEN": "Magenta concrete", - "icon": "magenta_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=magenta]" - }, - { - "baseColor": 16, - "id": "minecraft:magenta_wool", - "nameZH": "品红色羊毛", - "nameEN": "Magenta wool", - "icon": "magenta_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=magenta]" - }, - { - "baseColor": 16, - "id": "minecraft:magenta_stained_glass", - "nameZH": "品红色染色玻璃", - "nameEN": "Magenta glass", - "icon": "magenta_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=magenta]" - }, - { - "baseColor": 16, - "id": "minecraft:purpur_block", - "nameZH": "紫珀块", - "nameEN": "Purpur block", - "icon": "purpur_block.png", - "version": 0 - }, - { - "baseColor": 17, - "id": "minecraft:light_blue_concrete", - "nameZH": "淡蓝色混凝土", - "nameEN": "Light blue concrete", - "icon": "light_blue_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=light_blue]" - }, - { - "baseColor": 17, - "id": "minecraft:light_blue_wool", - "nameZH": "淡蓝色羊毛", - "nameEN": "light blue wool", - "icon": "light_blue_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=light_blue]" - }, - { - "baseColor": 17, - "id": "minecraft:light_blue_stained_glass", - "nameZH": "淡蓝色染色玻璃", - "nameEN": "Light blue glass", - "icon": "light_blue_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=light_blue]" - }, - { - "baseColor": 18, - "id": "minecraft:yellow_concrete", - "nameZH": "黄色混凝土", - "nameEN": "Yellow concrete", - "icon": "yellow_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=yellow]" - }, - { - "baseColor": 18, - "id": "minecraft:yellow_wool", - "nameZH": "黄色羊毛", - "nameEN": "Yellow wool", - "icon": "yellow_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=yellow]" - }, - { - "baseColor": 18, - "id": "minecraft:yellow_stained_glass", - "nameZH": "黄色染色玻璃", - "nameEN": "yellow glass", - "icon": "yellow_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=yellow]" - }, - { - "baseColor": 18, - "id": "minecraft:hay_block[axis=y]", - "nameZH": "干草捆", - "nameEN": "Hay block", - "icon": "hay_block.png", - "version": 0 - }, - { - "baseColor": 19, - "id": "minecraft:lime_concrete", - "nameZH": "黄绿色混凝土", - "nameEN": "Lime concrete", - "icon": "lime_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=lime]" - }, - { - "baseColor": 19, - "id": "minecraft:lime_wool", - "nameZH": "黄绿色羊毛", - "nameEN": "Lime wool", - "icon": "lime_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=lime]" - }, - { - "baseColor": 19, - "id": "minecraft:lime_stained_glass", - "nameZH": "黄绿色染色玻璃", - "nameEN": "Lime glass", - "icon": "lime_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=lime]" - }, - { - "baseColor": 20, - "id": "minecraft:pink_concrete", - "nameZH": "粉红色混凝土", - "nameEN": "Pink concrete", - "icon": "pink_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=pink]" - }, - { - "baseColor": 20, - "id": "minecraft:pink_wool", - "nameZH": "粉红色羊毛", - "nameEN": "Pink wool", - "icon": "pink_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=pink]" - }, - { - "baseColor": 20, - "id": "minecraft:pink_stained_glass", - "nameZH": "粉红色染色玻璃", - "nameEN": "Pink glass", - "icon": "pink_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=pink]" - }, - { - "baseColor": 20, - "id": "minecraft:pearlescent_froglight[axis=y]", - "nameZH": "珠光蛙明灯", - "nameEN": "Verdant Froglight", - "icon": "pearlescent_froglight_top.png", - "version": 19, - "isGlowing": true - }, - { - "baseColor": 21, - "id": "minecraft:gray_concrete", - "nameZH": "灰色混凝土", - "nameEN": "Gray concrete", - "icon": "gray_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=gray]" - }, - { - "baseColor": 21, - "id": "minecraft:gray_wool", - "nameZH": "灰色羊毛", - "nameEN": "Gray wool", - "icon": "gray_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=gray]", - "burnable": true - }, - { - "baseColor": 21, - "id": "minecraft:gray_stained_glass", - "nameZH": "灰色染色玻璃", - "nameEN": "Gray glass", - "icon": "gray_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=gray]" - }, - { - "baseColor": 21, - "id": "minecraft:tinted_glass", - "nameZH": "遮光玻璃", - "nameEN": "Tinted glass", - "icon": "tinted_glass.png", - "version": 17 - }, - { - "baseColor": 22, - "id": "minecraft:light_gray_concrete", - "nameZH": "淡灰色混凝土", - "nameEN": "Light gray concrete", - "icon": "light_gray_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=silver]" - }, - { - "baseColor": 22, - "id": "minecraft:light_gray_wool", - "nameZH": "淡灰色羊毛", - "nameEN": "Light gray wool", - "icon": "light_gray_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=silver]", - "burnable": true - }, - { - "baseColor": 22, - "id": "minecraft:light_gray_stained_glass", - "nameZH": "淡灰色染色玻璃", - "nameEN": "Light gray glass", - "icon": "light_gray_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=silver]" - }, - { - "baseColor": 23, - "id": "minecraft:cyan_concrete", - "nameZH": "青色混凝土", - "nameEN": "Cyan concrete", - "icon": "cyan_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=cyan]" - }, - { - "baseColor": 23, - "id": "minecraft:cyan_wool", - "nameZH": "青色羊毛", - "nameEN": "Cyan wool", - "icon": "cyan_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=cyan]", - "burnable": true - }, - { - "baseColor": 23, - "id": "minecraft:cyan_stained_glass", - "nameZH": "青色染色玻璃", - "nameEN": "Cyan glass", - "icon": "cyan_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=cyan]" - }, - { - "baseColor": 23, - "id": "minecraft:prismarine", - "nameZH": "海晶石", - "nameEN": "Prismarine", - "icon": "prismarine.png", - "version": 0, - "idOld": "minecraft:prismarine[variant=prismarine]" - }, - { - "baseColor": 23, - "id": "minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false]", - "nameZH": "幽匿感测体", - "nameEN": "Sculk sensor", - "icon": "sculk_sensor_side.png", - "version": 19 - }, - { - "baseColor": 24, - "id": "minecraft:purple_concrete", - "nameZH": "紫色混凝土", - "nameEN": "Purple concrete", - "icon": "purple_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=purple]" - }, - { - "baseColor": 24, - "id": "minecraft:purple_wool", - "nameZH": "紫色羊毛", - "nameEN": "Purple wool", - "icon": "purple_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=purple]", - "burnable": true - }, - { - "baseColor": 24, - "id": "minecraft:purple_stained_glass", - "nameZH": "紫色染色玻璃", - "nameEN": "Purple glass", - "icon": "purple_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=purple]" - }, - { - "baseColor": 24, - "id": "minecraft:amethyst_block", - "nameZH": "紫水晶块", - "nameEN": "Amethyst block", - "icon": "amethyst_block.png", - "version": 17 - }, - { - "baseColor": 25, - "id": "minecraft:blue_concrete", - "nameZH": "蓝色混凝土", - "nameEN": "Blue concrete", - "icon": "blue_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=blue]" - }, - { - "baseColor": 25, - "id": "minecraft:blue_wool", - "nameZH": "蓝色羊毛", - "nameEN": "Blue wool", - "icon": "blue_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=blue]", - "burnable": true - }, - { - "baseColor": 25, - "id": "minecraft:blue_stained_glass", - "nameZH": "蓝色染色玻璃", - "nameEN": "Blue glass", - "icon": "blue_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=blue]" - }, - { - "baseColor": 26, - "id": "minecraft:brown_concrete", - "nameZH": "棕色混凝土", - "nameEN": "Brown concrete", - "icon": "brown_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=brown]" - }, - { - "baseColor": 26, - "id": "minecraft:brown_wool", - "nameZH": "棕色羊毛", - "nameEN": "Brown wool", - "icon": "brown_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=brown]", - "burnable": true - }, - { - "baseColor": 26, - "id": "minecraft:brown_stained_glass", - "nameZH": "棕色染色玻璃", - "nameEN": "Brown glass", - "icon": "brown_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=brown]" - }, - { - "baseColor": 26, - "id": "minecraft:dark_oak_planks", - "nameZH": "深色橡木木板", - "nameEN": "Dark oak plank", - "icon": "dark_oak_planks.png", - "version": 0, - "idOld": "minecraft:planks[variant=dark_oak]", - "burnable": true - }, - { - "baseColor": 26, - "id": "minecraft:soul_sand", - "nameZH": "灵魂沙", - "nameEN": "Soul sand", - "icon": "soul_sand.png", - "version": 0 - }, - { - "baseColor": 26, - "id": "minecraft:soul_soil", - "nameZH": "灵魂土", - "nameEN": "Soul soil", - "icon": "soul_soil.png", - "version": 16 - }, - { - "baseColor": 27, - "id": "minecraft:green_concrete", - "nameZH": "绿色混凝土", - "nameEN": "Green concrete", - "icon": "green_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=green]" - }, - { - "baseColor": 27, - "id": "minecraft:green_wool", - "nameZH": "绿色羊毛", - "nameEN": "Green wool", - "icon": "green_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=green]", - "burnable": true - }, - { - "baseColor": 27, - "id": "minecraft:green_stained_glass", - "nameZH": "绿色染色玻璃", - "nameEN": "Green glass", - "icon": "green_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=green]" - }, - { - "baseColor": 27, - "id": "minecraft:dried_kelp_block", - "nameZH": "海带块", - "nameEN": "Kelp block", - "icon": "dried_kelp_block.png", - "version": 13 - }, - { - "baseColor": 28, - "id": "minecraft:red_concrete", - "nameZH": "红色混凝土", - "nameEN": "Red concrete", - "icon": "red_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=red]" - }, - { - "baseColor": 28, - "id": "minecraft:red_wool", - "nameZH": "红色羊毛", - "nameEN": "Red wool", - "icon": "red_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=red]", - "burnable": true - }, - { - "baseColor": 28, - "id": "minecraft:red_stained_glass", - "nameZH": "红色染色玻璃", - "nameEN": "Red glass", - "icon": "red_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=red]" - }, - { - "baseColor": 28, - "id": "minecraft:bricks", - "nameZH": "红砖块", - "nameEN": "Bricks", - "icon": "bricks.png", - "version": 0, - "idOld": "minecraft:brick_block" - }, - { - "baseColor": 28, - "id": "minecraft:red_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]", - "nameZH": "红色蘑菇方块", - "nameEN": "Red mushroom block", - "icon": "red_mushroom_block.png", - "version": 0, - "idOld": "minecraft:red_mushroom_block[variant=all_outside]", - "burnable": true - }, - { - "baseColor": 28, - "id": "minecraft:nether_wart_block", - "nameZH": "下界疣块", - "nameEN": "Nether wart block", - "icon": "nether_wart_block.png", - "version": 0 - }, - { - "baseColor": 28, - "id": "minecraft:shroomlight", - "nameZH": "菌光体", - "nameEN": "Shroomlight", - "icon": "shroomlight.png", - "version": 16, - "isGlowing": true - }, - { - "baseColor": 28, - "id": "minecraft:mangrove_planks", - "nameZH": "红树木板", - "nameEN": "Mangrove planks", - "icon": "mangrove_planks.png", - "version": 19, - "burnable": true - }, - { - "baseColor": 29, - "id": "minecraft:black_concrete", - "nameZH": "黑色混凝土", - "nameEN": "Black concrete", - "icon": "black_concrete.png", - "version": 0, - "idOld": "minecraft:concrete[color=black]" - }, - { - "baseColor": 29, - "id": "minecraft:black_wool", - "nameZH": "黑色羊毛", - "nameEN": "Black wool", - "icon": "black_wool.png", - "version": 0, - "idOld": "minecraft:wool[color=black]", - "burnable": true - }, - { - "baseColor": 29, - "id": "minecraft:black_stained_glass", - "nameZH": "黑色染色玻璃", - "nameEN": "Blakc glass", - "icon": "black_stained_glass.png", - "version": 0, - "idOld": "minecraft:stained_glass[color=black]" - }, - { - "baseColor": 29, - "id": "minecraft:obsidian", - "nameZH": "黑曜石", - "nameEN": "Obsidian", - "icon": "obsidian.png", - "version": 0 - }, - { - "baseColor": 29, - "id": "minecraft:coal_block", - "nameZH": "煤炭块", - "nameEN": "Coal block", - "icon": "coal_block.png", - "version": 0 - }, - { - "baseColor": 29, - "id": "minecraft:polished_basalt[axis=y]", - "nameZH": "磨制玄武岩", - "nameEN": "Polished basalt", - "icon": "polished_basalt.png", - "version": 16 - }, - { - "baseColor": 29, - "id": "minecraft:polished_blackstone", - "nameZH": "磨制黑石", - "nameEN": "Polished blackstone", - "icon": "polished_blackstone.png", - "version": 16 - }, - { - "baseColor": 29, - "id": "minecraft:sculk", - "nameZH": "幽匿块", - "nameEN": "Sculk block", - "icon": "sculk.png", - "version": 19 - }, - { - "baseColor": 29, - "id": "minecraft:sculk_catalyst[bloom=false]", - "nameZH": "幽匿催发体", - "nameEN": "Sculk catalyst", - "icon": "sculk_catalyst_top.png", - "version": 19 - }, - { - "baseColor": 29, - "id": "minecraft:sculk_shrieker[shrieking=false,waterlogged=false]", - "nameZH": "幽匿尖啸体", - "nameEN": "Sculk shrieker", - "icon": "sculk_shrieker_side.png", - "version": 19 - }, - { - "baseColor": 29, - "id": "minecraft:sculk_vein[down=true,east=false,north=false,south=false,up=false,waterlogged=false,west=false]", - "nameZH": "幽匿脉络", - "nameEN": "Sculk vein", - "icon": "sculk_vein.png", - "version": 19, - "needGlass": true - }, - { - "baseColor": 30, - "id": "minecraft:gold_block", - "nameZH": "金块", - "nameEN": "Gold block", - "icon": "gold_block.png", - "version": 0 - }, - { - "baseColor": 30, - "id": "minecraft:light_weighted_pressure_plate[power=0]", - "nameZH": "轻质测重压力板", - "nameEN": "Gold pressure plate", - "icon": "light_weighted_pressure_plate.png", - "version": 0, - "needGlass": true - }, - { - "baseColor": 30, - "id": "minecraft:raw_gold_block", - "nameZH": "粗金块", - "nameEN": "Raw gold block", - "icon": "raw_gold_block.png", - "version": 17 - }, - { - "baseColor": 31, - "id": "minecraft:diamond_block", - "nameZH": "钻石块", - "nameEN": "Diamond block", - "icon": "diamond_block.png", - "version": 0 - }, - { - "baseColor": 31, - "id": "minecraft:prismarine_bricks", - "nameZH": "海晶石砖", - "nameEN": "Prismarine bricks", - "icon": "prismarine_bricks.png", - "version": 0, - "idOld": "minecraft:prismarine[variant=prismarine_bricks]" - }, - { - "baseColor": 31, - "id": "minecraft:dark_prismarine", - "nameZH": "暗海晶石", - "nameEN": "Dark prismarine", - "icon": "dark_prismarine.png", - "version": 0, - "idOld": "minecraft:prismarine[variant=dark_prismarine]" - }, - { - "baseColor": 32, - "id": "minecraft:lapis_block", - "nameZH": "青金石块", - "nameEN": "Lapis block", - "icon": "lapis_block.png", - "version": 0 - }, - { - "baseColor": 33, - "id": "minecraft:emerald_block", - "nameZH": "绿宝石块", - "nameEN": "Emerald block", - "icon": "emerald_block.png", - "version": 0 - }, - { - "baseColor": 34, - "id": "minecraft:podzol[snowy=false]", - "nameZH": "灰化土", - "nameEN": "Podzol", - "icon": "podzol.png", - "version": 0, - "idOld": "minecraft:dirt[variant=podzol,snowy=false]", - "endermanPickable": true - }, - { - "baseColor": 34, - "id": "minecraft:spruce_planks", - "nameZH": "云杉木板", - "nameEN": "Spruce plank", - "icon": "spruce_planks.png", - "version": 0, - "idOld": "minecraft:planks[variant=spruce]", - "burnable": true - }, - { - "baseColor": 34, - "id": "minecraft:campfire[facing=north,lit=true,signal_fire=false,waterlogged=false]", - "nameZH": "营火", - "nameEN": "Campfire", - "icon": "campfire.png", - "version": 14, - "isGlowing": true, - "wallUseable": false - }, - { - "baseColor": 35, - "id": "minecraft:netherrack", - "nameZH": "下界岩", - "nameEN": "Netherrack", - "icon": "netherrack.png", - "version": 0 - }, - { - "baseColor": 35, - "id": "minecraft:nether_bricks", - "nameZH": "下界砖块", - "nameEN": "Nether brick", - "icon": "nether_bricks.png", - "version": 0, - "idOld": "minecraft:nether_brick" - }, - { - "baseColor": 35, - "id": "minecraft:magma_block", - "nameZH": "岩浆块", - "nameEN": "Magma block", - "icon": "magma_block.png", - "version": 0, - "idOld": "minecraft:magma" - }, - { - "baseColor": 36, - "id": "minecraft:white_terracotta", - "nameZH": "白色陶瓦", - "nameEN": "White terracotta", - "icon": "white_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=white]" - }, - { - "baseColor": 36, - "id": "minecraft:calcite", - "nameZH": "方解石", - "nameEN": "Calcite", - "icon": "calcite.png", - "version": 17 - }, - { - "baseColor": 37, - "id": "minecraft:orange_terracotta", - "nameZH": "橙色陶瓦", - "nameEN": "Orange terracotta", - "icon": "orange_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=orange]" - }, - { - "baseColor": 38, - "id": "minecraft:magenta_terracotta", - "nameZH": "品红色陶瓦", - "nameEN": "Magenta terracotta", - "icon": "magenta_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=magenta]" - }, - { - "baseColor": 39, - "id": "minecraft:light_blue_terracotta", - "nameZH": "淡蓝色陶瓦", - "nameEN": "Light blue terracotta", - "icon": "light_blue_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=light_blue]" - }, - { - "baseColor": 40, - "id": "minecraft:yellow_terracotta", - "nameZH": "黄色陶瓦", - "nameEN": "Yellow terracotta", - "icon": "yellow_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=yellow]" - }, - { - "baseColor": 41, - "id": "minecraft:lime_terracotta", - "nameZH": "黄绿色陶瓦", - "nameEN": "Lime terracotta", - "icon": "lime_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=lime]" - }, - { - "baseColor": 42, - "id": "minecraft:pink_terracotta", - "nameZH": "粉红色陶瓦", - "nameEN": "Pink terracotta", - "icon": "pink_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=pink]" - }, - { - "baseColor": 43, - "id": "minecraft:gray_terracotta", - "nameZH": "灰色陶瓦", - "nameEN": "Gray terracotta", - "icon": "gray_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=gray]" - }, - { - "baseColor": 44, - "id": "minecraft:light_gray_terracotta", - "nameZH": "淡灰色陶瓦", - "nameEN": "Light gray terracotta", - "icon": "light_gray_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=silver]" - }, - { - "baseColor": 44, - "id": "minecraft:waxed_exposed_copper", - "nameZH": "斑驳的涂蜡铜块", - "nameEN": "Waxed exposed copper", - "icon": "waxed_exposed_copper.png", - "version": 17 - }, - { - "baseColor": 44, - "id": "minecraft:waxed_exposed_cut_copper", - "nameZH": "斑驳的涂蜡切制铜块", - "nameEN": "Waxed exposed cut copper", - "icon": "waxed_exposed_cut_copper.png", - "version": 17 - }, - { - "baseColor": 44, - "id": "minecraft:mud_bricks", - "nameZH": "泥砖", - "nameEN": "Mud bricks", - "icon": "mud_bricks.png", - "version": 19 - }, - { - "baseColor": 45, - "id": "minecraft:cyan_terracotta", - "nameZH": "青色陶瓦", - "nameEN": "Cyan terracotta", - "icon": "cyan_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=cyan]" - }, - { - "baseColor": 45, - "id": "minecraft:mud", - "nameZH": "泥巴", - "nameEN": "Mud", - "icon": "mud.png", - "version": 19 - }, - { - "baseColor": 46, - "id": "minecraft:purple_terracotta", - "nameZH": "紫色陶瓦", - "nameEN": "Purple terracotta", - "icon": "purple_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=purple]" - }, - { - "baseColor": 47, - "id": "minecraft:blue_terracotta", - "nameZH": "蓝色陶瓦", - "nameEN": "Blue terracotta", - "icon": "blue_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=blue]" - }, - { - "baseColor": 48, - "id": "minecraft:brown_terracotta", - "nameZH": "棕色陶瓦", - "nameEN": "Brown terracotta", - "icon": "brown_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=brown]" - }, - { - "baseColor": 48, - "id": "minecraft:dripstone_block", - "nameZH": "滴水石块", - "nameEN": "DripStone Block", - "icon": "dripstone_block.png", - "version": 17 - }, - { - "baseColor": 49, - "id": "minecraft:green_terracotta", - "nameZH": "绿色陶瓦", - "nameEN": "Green terracotta", - "icon": "green_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=green]" - }, - { - "baseColor": 50, - "id": "minecraft:red_terracotta", - "nameZH": "红色陶瓦", - "nameEN": "Red terracotta", - "icon": "red_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=red]" - }, - { - "baseColor": 51, - "id": "minecraft:black_terracotta", - "nameZH": "黑色陶瓦", - "nameEN": "Black terracotta", - "icon": "black_terracotta.png", - "version": 0, - "idOld": "minecraft:stained_hardened_clay[color=black]" - }, - { - "baseColor": 52, - "id": "minecraft:crimson_nylium", - "nameZH": "绯红菌岩", - "nameEN": "Crimson nylium", - "icon": "crimson_nylium.png", - "version": 16, - "endermanPickable": true, - "wallUseable": false - }, - { - "baseColor": 53, - "id": "minecraft:crimson_planks", - "nameZH": "绯红木板", - "nameEN": "Crimson plank", - "icon": "crimson_planks.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 53, - "id": "minecraft:stripped_crimson_stem[axis=y]", - "nameZH": "去皮绯红菌柄", - "nameEN": "Stripped crimson log", - "icon": "stripped_crimson_stem.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 54, - "id": "minecraft:crimson_hyphae[axis=y]", - "nameZH": "绯红菌核", - "nameEN": "Crimson hyphae", - "icon": "crimson_hyphae.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 54, - "id": "minecraft:stripped_crimson_hyphae[axis=y]", - "nameZH": "去皮绯红菌核", - "nameEN": "Stripped crimson hyphae", - "icon": "stripped_crimson_hyphae.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 55, - "id": "minecraft:warped_nylium", - "nameZH": "诡异菌岩", - "nameEN": "Warped nylium", - "icon": "warped_nylium.png", - "version": 16, - "endermanPickable": true, - "wallUseable": false - }, - { - "baseColor": 55, - "id": "minecraft:waxed_oxidized_copper", - "nameZH": "氧化的涂蜡铜块", - "nameEN": "Waxed oxided copper", - "icon": "waxed_oxidized_copper.png", - "version": 17 - }, - { - "baseColor": 55, - "id": "minecraft:waxed_oxidized_cut_copper", - "nameZH": "氧化的涂蜡切制铜块", - "nameEN": "Waxed oxided cut copper", - "icon": "waxed_oxidized_cut_copper.png", - "version": 17 - }, - { - "baseColor": 56, - "id": "minecraft:warped_planks", - "nameZH": "诡异木板", - "nameEN": "Warped plank", - "icon": "warped_planks.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 56, - "id": "minecraft:stripped_warped_stem[axis=y]", - "nameZH": "去皮诡异菌柄", - "nameEN": "Stripped warped log", - "icon": "stripped_warped_stem.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 56, - "id": "minecraft:waxed_weathered_copper", - "nameZH": "锈蚀的涂蜡铜块", - "nameEN": "Waxed weathered copper", - "icon": "waxed_weathered_copper.png", - "version": 17 - }, - { - "baseColor": 56, - "id": "minecraft:waxed_weathered_cut_copper", - "nameZH": "锈蚀的涂蜡切制铜块", - "nameEN": "Waxed weathered cut copper", - "icon": "waxed_weathered_cut_copper.png", - "version": 17 - }, - { - "baseColor": 57, - "id": "minecraft:warped_hyphae[axis=y]", - "nameZH": "诡异菌核", - "nameEN": "Warped hyphae", - "icon": "warped_hyphae.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 57, - "id": "minecraft:stripped_warped_hyphae[axis=y]", - "nameZH": "去皮诡异菌核", - "nameEN": "Stripped warped hyphae", - "icon": "stripped_warped_hyphae.png", - "version": 16, - "burnable": true - }, - { - "baseColor": 58, - "id": "minecraft:warped_wart_block", - "nameZH": "诡异疣块", - "nameEN": "Warped wart block", - "icon": "warped_wart_block.png", - "version": 16 - }, - { - "baseColor": 59, - "id": "minecraft:deepslate", - "nameZH": "深板岩", - "nameEN": "Deepslate", - "icon": "deepslate.png", - "version": 17 - }, - { - "baseColor": 59, - "id": "minecraft:chiseled_deepslate", - "nameZH": "雕纹深板岩", - "nameEN": "Chiseled deepslate", - "icon": "chiseled_deepslate.png", - "version": 17 - }, - { - "baseColor": 59, - "id": "minecraft:polished_deepslate", - "nameZH": "磨制深板岩", - "nameEN": "Polished deepslate", - "icon": "polished_deepslate.png", - "version": 17 - }, - { - "baseColor": 59, - "id": "minecraft:deepslate_bricks", - "nameZH": "深板岩砖", - "nameEN": "Deepslate bricks", - "icon": "deepslate_bricks.png", - "version": 17 - }, - { - "baseColor": 59, - "id": "minecraft:deepslate_tiles", - "nameZH": "深板岩瓦", - "nameEN": "Deepslate tiles", - "icon": "deepslate_tiles.png", - "version": 17 - }, - { - "baseColor": 59, - "id": "minecraft:cobbled_deepslate", - "nameZH": "深板岩圆石", - "nameEN": "Cobbled deepslate", - "icon": "cobbled_deepslate.png", - "version": 17 - }, - { - "baseColor": 60, - "id": "minecraft:raw_iron_block", - "nameZH": "粗铁块", - "nameEN": "Raw iron block", - "icon": "raw_iron_block.png", - "version": 17 - }, - { - "baseColor": 61, - "id": "minecraft:glow_lichen[down=true,east=false,north=false,south=false,up=false,waterlogged=false]", - "nameZH": "发光地衣", - "nameEN": "Glow lichen", - "icon": "glow_lichen.png", - "version": 17, - "needGlass": true, - "isGlowing": true, - "burnable": true, - "wallUseable": false - }, - { - "baseColor": 61, - "id": "minecraft:verdant_froglight[axis=y]", - "nameZH": "青翠蛙明灯", - "nameEN": "Verdant Froglight", - "icon": "verdant_froglight_top.png", - "version": 19, - "isGlowing": true - } - ] -} \ No newline at end of file diff --git a/Blocks/FixedBlocks/README.md b/Blocks/FixedBlocks/README.md index a5dc990b..9950c418 100644 --- a/Blocks/FixedBlocks/README.md +++ b/Blocks/FixedBlocks/README.md @@ -1,9 +1,3 @@ # 默认方块列表对应的图片放在这里 -我还不太确定是否应当再度引入这一堆的图片(170多个二进制文件呐! -所以这里先空着。 - -# Fixed blocks list images here -I'm not sure whether should i put images here (170+ binary files! - -So leave it empty now. \ No newline at end of file +# Fixed blocks list images here \ No newline at end of file diff --git a/Blocks/FixedBlocks/bamboo_block.png b/Blocks/FixedBlocks/bamboo_block.png new file mode 100644 index 00000000..330987e8 Binary files /dev/null and b/Blocks/FixedBlocks/bamboo_block.png differ diff --git a/Blocks/FixedBlocks/bamboo_planks.png b/Blocks/FixedBlocks/bamboo_planks.png new file mode 100644 index 00000000..2085be49 Binary files /dev/null and b/Blocks/FixedBlocks/bamboo_planks.png differ diff --git a/Blocks/FixedBlocks/block_list.json b/Blocks/FixedBlocks/block_list.json new file mode 100644 index 00000000..f8fb4baf --- /dev/null +++ b/Blocks/FixedBlocks/block_list.json @@ -0,0 +1,2069 @@ +[ + { + "baseColor": 0, + "id": "minecraft:glass", + "nameZH": "玻璃", + "nameEN": "Glass", + "icon": "glass.png", + "version": 0 + }, + { + "baseColor": 1, + "id": "minecraft:grass_block[snowy=false]", + "nameZH": "草方块", + "nameEN": "Grass block", + "icon": "grass_block.png", + "version": 0, + "idOld": "minecraft:grass[snowy=false]", + "endermanPickable": true, + "wallUseable": false + }, + { + "baseColor": 1, + "id": "minecraft:slime_block", + "nameZH": "黏液块", + "nameEN": "Slime block", + "icon": "slime_block.png", + "version": 0, + "idOld": "minecraft:slime", + "needStone": [ + 12 + ] + }, + { + "baseColor": 2, + "id": "minecraft:birch_planks", + "nameZH": "白桦木板", + "nameEN": "Birch plank", + "icon": "birch_planks.png", + "version": 0, + "idOld": "minecraft:planks[variant=birch]", + "burnable": true + }, + { + "baseColor": 2, + "id": "minecraft:smooth_sandstone", + "nameZH": "平滑砂岩", + "nameEN": "Smooth sandstone", + "icon": "smooth_sandstone.png", + "version": 0, + "idOld": "minecraft:sandstone[type=smooth_sandstone]" + }, + { + "baseColor": 2, + "id": "minecraft:glowstone", + "nameZH": "荧石", + "nameEN": "Glowstone", + "icon": "glowstone.png", + "version": 0, + "isGlowing": true + }, + { + "baseColor": 2, + "id": "minecraft:end_stone", + "nameZH": "末地石", + "nameEN": "End stone", + "icon": "end_stone.png", + "version": 0 + }, + { + "baseColor": 2, + "id": "minecraft:end_stone_bricks", + "nameZH": "末地石砖", + "nameEN": "Endstone bricks", + "icon": "end_stone_bricks.png", + "version": 0, + "idOld": "minecraft:end_bricks" + }, + { + "baseColor": 2, + "id": "minecraft:turtle_egg[eggs=4,hatch=0]", + "nameZH": "海龟蛋", + "nameEN": "Turtle egg", + "icon": "turtle_egg.png", + "version": 13, + "needGlass": true, + "wallUseable": false + }, + { + "baseColor": 2, + "id": "minecraft:ochre_froglight[axis=y]", + "nameZH": "赭黄蛙明灯", + "nameEN": "Ochre Froglight", + "icon": "ochre_froglight_top.png", + "version": 19, + "isGlowing": true + }, + { + "baseColor": 3, + "id": "minecraft:mushroom_stem[east=true,west=true,north=true,south=true,up=true,down=true]", + "nameZH": "蘑菇柄", + "nameEN": "Mushroom stem", + "icon": "mushroom_stem.png", + "version": 13, + "burnable": true + }, + { + "baseColor": 3, + "id": "minecraft:cobweb", + "nameZH": "蜘蛛网", + "nameEN": "Cobweb", + "icon": "cobweb.png", + "version": 0, + "idOld": "minecraft:web" + }, + { + "baseColor": 4, + "id": "minecraft:redstone_block", + "nameZH": "红石块", + "nameEN": "Redstone block", + "icon": "redstone_block.png", + "version": 0 + }, + { + "baseColor": 4, + "id": "minecraft:tnt[unstable=false]", + "nameZH": "TNT", + "nameEN": "TNT", + "icon": "tnt.png", + "version": 0, + "idOld": "minecraft:tnt[explode=false]", + "endermanPickable": true, + "burnable": true + }, + { + "baseColor": 5, + "id": "minecraft:ice", + "nameZH": "冰", + "nameEN": "Ice", + "icon": "ice.png", + "version": 0 + }, + { + "baseColor": 5, + "id": "minecraft:packed_ice", + "nameZH": "浮冰", + "nameEN": "Packed ice", + "icon": "packed_ice.png", + "version": 0 + }, + { + "baseColor": 5, + "id": "minecraft:blue_ice", + "nameZH": "蓝冰", + "nameEN": "Blue ice", + "icon": "blue_ice.png", + "version": 13 + }, + { + "baseColor": 6, + "id": "minecraft:iron_block", + "nameZH": "铁块", + "nameEN": "Iron block", + "icon": "iron_block.png", + "version": 0 + }, + { + "baseColor": 6, + "id": "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", + "nameZH": "酿造台", + "nameEN": "brewing stand", + "icon": "brewing_stand.png", + "version": 0, + "wallUseable": false, + "needStone": [ + 12 + ] + }, + { + "baseColor": 6, + "id": "minecraft:heavy_weighted_pressure_plate[power=0]", + "nameZH": "重质测重压力板", + "nameEN": "Iron pressure plate", + "icon": "heavy_weighted_pressure_plate.png", + "version": 0, + "needStone": true, + "wallUseable": false + }, + { + "baseColor": 6, + "id": "minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false]", + "nameZH": "铁活板门", + "nameEN": "Iron trapdoor", + "icon": "iron_trapdoor.png", + "version": 0, + "idOld": "minecraft:iron_trapdoor[facing=north,half=top,open=false]", + "wallUseable": false + }, + { + "baseColor": 6, + "id": "minecraft:lantern[hanging=false]", + "nameZH": "灯笼", + "nameEN": "lantern", + "icon": "lantern.png", + "version": 14, + "wallUseable": false + }, + { + "baseColor": 6, + "id": "minecraft:grindstone[face=floor,facing=north]", + "nameZH": "砂轮", + "nameEN": "Grindstone", + "icon": "grindstone.png", + "version": 14, + "wallUseable": false + }, + { + "baseColor": 6, + "id": "minecraft:pale_oak_leaves[distance=7,persistent=true]", + "nameZH": "苍白橡树树叶", + "nameEN": "Pale oak leaves", + "icon": "pale_oak_leaves.png", + "version": 21 + }, + { + "baseColor": 7, + "id": "minecraft:oak_leaves[distance=7,persistent=true]", + "nameZH": "橡树树叶", + "nameEN": "Oak leaves", + "icon": "oak_leaves.png", + "version": 0, + "idOld": "minecraft:leaves[variant=oak,check_decay=false,decayable=false]" + }, + { + "baseColor": 7, + "id": "minecraft:spruce_leaves[distance=7,persistent=true]", + "nameZH": "云杉树叶", + "nameEN": "Spurce leaves", + "icon": "spruce_leaves.png", + "version": 0, + "idOld": "minecraft:leaves[variant=spruce,check_decay=false,decayable=false]" + }, + { + "baseColor": 7, + "id": "minecraft:birch_leaves[distance=7,persistent=true]", + "nameZH": "白桦树叶", + "nameEN": "Brich leaves", + "icon": "birch_leaves.png", + "version": 0, + "idOld": "minecraft:leaves[variant=birch,check_decay=false,decayable=false]" + }, + { + "baseColor": 7, + "id": "minecraft:jungle_leaves[distance=7,persistent=true]", + "nameZH": "丛林树叶", + "nameEN": "Jungle leaves", + "icon": "jungle_leaves.png", + "version": 0, + "idOld": "minecraft:leaves[variant=jungle,check_decay=false,decayable=false]" + }, + { + "baseColor": 7, + "id": "minecraft:acacia_leaves[distance=7,persistent=true]", + "nameZH": "金合欢树叶", + "nameEN": "Acacia leaves", + "icon": "acacia_leaves.png", + "version": 0, + "idOld": "minecraft:leaves2[variant=acacia,check_decay=false,decayable=false]" + }, + { + "baseColor": 7, + "id": "minecraft:dark_oak_leaves[distance=7,persistent=true]", + "nameZH": "深色橡树树叶", + "nameEN": "Dark oat leaves", + "icon": "dark_oak_leaves.png", + "version": 0, + "idOld": "minecraft:leaves2[variant=dark_oak,check_decay=false,decayable=false]" + }, + { + "baseColor": 7, + "id": "minecraft:bamboo_block[axis=x]", + "nameZH": "竹块(侧面)", + "nameEN": "Bamboo block(axis=x)", + "icon": "bamboo_block.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 8, + "id": "minecraft:white_concrete", + "nameZH": "白色混凝土", + "nameEN": "White concrete", + "icon": "white_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=white]" + }, + { + "baseColor": 8, + "id": "minecraft:white_wool", + "nameZH": "白色羊毛", + "nameEN": "White wool", + "icon": "white_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=white]" + }, + { + "baseColor": 8, + "id": "minecraft:white_stained_glass", + "nameZH": "白色染色玻璃", + "nameEN": "White glass", + "icon": "white_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=white]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 8, + "id": "minecraft:white_carpet", + "nameZH": "白色地毯", + "nameEN": "White carpet", + "icon": "white_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=white]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 8, + "id": "minecraft:snow_block", + "nameZH": "雪块", + "nameEN": "Snow", + "icon": "snow_block.png", + "version": 0, + "idOld": "minecraft:snow" + }, + { + "baseColor": 9, + "id": "minecraft:clay", + "nameZH": "黏土", + "nameEN": "Clay block", + "icon": "clay.png", + "version": 0, + "endermanPickable": true + }, + { + "baseColor": 10, + "id": "minecraft:coarse_dirt", + "nameZH": "砂土", + "nameEN": "Coarse dirt", + "icon": "coarse_dirt.png", + "version": 13, + "idOld": "minecraft:dirt[variant=coarse_dirt,snowy=false]", + "endermanPickable": true + }, + { + "baseColor": 10, + "id": "minecraft:jungle_planks", + "nameZH": "丛林木板", + "nameEN": "Jungle plank", + "icon": "jungle_planks.png", + "version": 0, + "idOld": "minecraft:planks[variant=jungle]", + "burnable": true + }, + { + "baseColor": 10, + "id": "minecraft:brown_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]", + "nameZH": "棕色蘑菇方块", + "nameEN": "Brown mushroom block", + "icon": "brown_mushroom_block.png", + "version": 0, + "idOld": "minecraft:brown_mushroom_block[variant=all_outside]" + }, + { + "baseColor": 10, + "id": "minecraft:dirt", + "nameZH": "泥土", + "nameEN": "Dirt", + "icon": "dirt.png", + "version": 0, + "idOld": "minecraft:dirt[variant=dirt,snowy=false]", + "endermanPickable": true + }, + { + "baseColor": 10, + "id": "minecraft:packed_mud", + "nameZH": "泥坯", + "nameEN": "Packed mud", + "icon": "packed_mud.png", + "version": 19 + }, + { + "baseColor": 11, + "id": "minecraft:cobblestone", + "nameZH": "圆石", + "nameEN": "Cobblestone", + "icon": "cobblestone.png", + "version": 0 + }, + { + "baseColor": 11, + "id": "minecraft:stone", + "nameZH": "石头", + "nameEN": "Stone", + "icon": "stone.png", + "version": 0, + "idOld": "minecraft:stone[variant=stone]" + }, + { + "baseColor": 11, + "id": "minecraft:smooth_stone", + "nameZH": "平滑石头", + "nameEN": "Smooth stone", + "icon": "smooth_stone.png", + "version": 13 + }, + { + "baseColor": 11, + "id": "minecraft:stone_bricks", + "nameZH": "石砖", + "nameEN": "Stone brick", + "icon": "stone_bricks.png", + "version": 0, + "idOld": "minecraft:stonebrick[variant=stonebrick]" + }, + { + "baseColor": 12, + "id": "minecraft:water[level=0]", + "nameZH": "水", + "nameEN": "Water", + "icon": "water.png", + "version": 0, + "wallUseable": false, + "stackSize": 1 + }, + { + "baseColor": 13, + "id": "minecraft:oak_planks", + "nameZH": "橡木木板", + "nameEN": "Oak plank", + "icon": "oak_planks.png", + "version": 0, + "idOld": "minecraft:planks[variant=oak]", + "burnable": true + }, + { + "baseColor": 14, + "id": "minecraft:quartz_block", + "nameZH": "石英块", + "nameEN": "Quartz block", + "icon": "quartz_block.png", + "version": 0, + "idOld": "minecraft:quartz_block[variant=default]" + }, + { + "baseColor": 14, + "id": "minecraft:sea_lantern", + "nameZH": "海晶灯", + "nameEN": "Sea lantern", + "icon": "sea_lantern.png", + "version": 0 + }, + { + "baseColor": 14, + "id": "minecraft:target[power=0]", + "nameZH": "标靶", + "nameEN": "Target", + "icon": "target.png", + "version": 16 + }, + { + "baseColor": 14, + "id": "minecraft:pale_oak_planks", + "nameZH": "苍白橡木木板", + "nameEN": "pale oak plank", + "icon": "pale_oak_planks.png", + "version": 21, + "burnable": true + }, + { + "baseColor": 14, + "id": "minecraft:pale_oak_slab[type=top,waterlogged=false]", + "nameZH": "苍白橡木台阶", + "nameEN": "Pale oak slab", + "icon": "pale_oak_slab.png", + "version": 21, + "burnable": true + }, + { + "baseColor": 15, + "id": "minecraft:orange_concrete", + "nameZH": "橙色混凝土", + "nameEN": "Orange concrete", + "icon": "orange_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=orange]" + }, + { + "baseColor": 15, + "id": "minecraft:orange_wool", + "nameZH": "橙色羊毛", + "nameEN": "Orange wool", + "icon": "orange_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=orange]" + }, + { + "baseColor": 15, + "id": "minecraft:orange_stained_glass", + "nameZH": "橙色染色玻璃", + "nameEN": "Orange glass", + "icon": "orange_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=orange]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 15, + "id": "minecraft:orange_carpet", + "nameZH": "橙色地毯", + "nameEN": "Orange carpet", + "icon": "orange_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=orange]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 15, + "id": "minecraft:acacia_planks", + "nameZH": "金合欢木板", + "nameEN": "Acacia plank", + "icon": "acacia_planks.png", + "version": 0, + "idOld": "minecraft:planks[variant=acacia]", + "burnable": true + }, + { + "baseColor": 15, + "id": "minecraft:pumpkin", + "nameZH": "南瓜", + "nameEN": "Pumpkin", + "icon": "pumpkin.png", + "version": 0, + "idOld": "minecraft:pumpkin[variant=north]", + "endermanPickable": true + }, + { + "baseColor": 15, + "id": "minecraft:terracotta", + "nameZH": "陶瓦", + "nameEN": "Terracotta", + "icon": "terracotta.png", + "version": 0, + "idOld": "minecraft:hardened_clay" + }, + { + "baseColor": 15, + "id": "minecraft:smooth_red_sandstone", + "nameZH": "平滑红砂岩", + "nameEN": "Smooth red sandstone", + "icon": "smooth_red_sandstone.png", + "version": 0, + "idOld": "minecraft:red_sandstone[type=smooth_red_sandstone]" + }, + { + "baseColor": 15, + "id": "minecraft:honey_block", + "nameZH": "蜂蜜块", + "nameEN": "Honey block", + "icon": "honey_block.png", + "version": 15 + }, + { + "baseColor": 15, + "id": "minecraft:honeycomb_block", + "nameZH": "蜜脾块", + "nameEN": "Honey comb block", + "icon": "honeycomb_block.png", + "version": 15 + }, + { + "baseColor": 15, + "id": "minecraft:raw_copper_block", + "nameZH": "粗铜块", + "nameEN": "Raw copper block", + "icon": "raw_copper_block.png", + "version": 17 + }, + { + "baseColor": 15, + "id": "minecraft:waxed_copper_block", + "nameZH": "涂蜡的铜块", + "nameEN": "Waxed copper", + "icon": "waxed_copper_block.png", + "version": 17 + }, + { + "baseColor": 15, + "id": "minecraft:waxed_cut_copper", + "nameZH": "涂蜡的切制铜块", + "nameEN": "Waxed cut copper", + "icon": "waxed_cut_copper.png", + "version": 17 + }, + { + "baseColor": 15, + "id": "minecraft:waxed_copper_bulb", + "nameZH": "涂蜡的铜灯", + "nameEN": "Waxed copper bulb", + "icon": "copper_bulb.png", + "version": 21 + }, + { + "baseColor": 15, + "id": "minecraft:waxed_copper_grate", + "nameZH": "涂蜡的铜格栅", + "nameEN": "Waxed copper grate", + "icon": "copper_grate.png", + "version": 21 + }, + { + "baseColor": 16, + "id": "minecraft:magenta_concrete", + "nameZH": "品红色混凝土", + "nameEN": "Magenta concrete", + "icon": "magenta_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=magenta]" + }, + { + "baseColor": 16, + "id": "minecraft:magenta_wool", + "nameZH": "品红色羊毛", + "nameEN": "Magenta wool", + "icon": "magenta_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=magenta]" + }, + { + "baseColor": 16, + "id": "minecraft:magenta_stained_glass", + "nameZH": "品红色染色玻璃", + "nameEN": "Magenta glass", + "icon": "magenta_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=magenta]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 16, + "id": "minecraft:magenta_carpet", + "nameZH": "品红色地毯", + "nameEN": "Magenta carpet", + "icon": "magenta_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=magenta]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 16, + "id": "minecraft:purpur_block", + "nameZH": "紫珀块", + "nameEN": "Purpur block", + "icon": "purpur_block.png", + "version": 0 + }, + { + "baseColor": 17, + "id": "minecraft:light_blue_concrete", + "nameZH": "淡蓝色混凝土", + "nameEN": "Light blue concrete", + "icon": "light_blue_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=light_blue]" + }, + { + "baseColor": 17, + "id": "minecraft:light_blue_wool", + "nameZH": "淡蓝色羊毛", + "nameEN": "light blue wool", + "icon": "light_blue_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=light_blue]" + }, + { + "baseColor": 17, + "id": "minecraft:light_blue_stained_glass", + "nameZH": "淡蓝色染色玻璃", + "nameEN": "Light blue glass", + "icon": "light_blue_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=light_blue]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 17, + "id": "minecraft:light_blue_carpet", + "nameZH": "淡蓝色地毯", + "nameEN": "Light blue carpet", + "icon": "light_blue_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=light_blue]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 18, + "id": "minecraft:yellow_concrete", + "nameZH": "黄色混凝土", + "nameEN": "Yellow concrete", + "icon": "yellow_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=yellow]" + }, + { + "baseColor": 18, + "id": "minecraft:yellow_wool", + "nameZH": "黄色羊毛", + "nameEN": "Yellow wool", + "icon": "yellow_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=yellow]" + }, + { + "baseColor": 18, + "id": "minecraft:yellow_stained_glass", + "nameZH": "黄色染色玻璃", + "nameEN": "yellow glass", + "icon": "yellow_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=yellow]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 18, + "id": "minecraft:yellow_carpet", + "nameZH": "黄色地毯", + "nameEN": "Yellow carpet", + "icon": "yellow_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=yellow]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 18, + "id": "minecraft:hay_block[axis=y]", + "nameZH": "干草捆", + "nameEN": "Hay block", + "icon": "hay_block.png", + "version": 0 + }, + { + "baseColor": 19, + "id": "minecraft:lime_concrete", + "nameZH": "黄绿色混凝土", + "nameEN": "Lime concrete", + "icon": "lime_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=lime]" + }, + { + "baseColor": 19, + "id": "minecraft:lime_wool", + "nameZH": "黄绿色羊毛", + "nameEN": "Lime wool", + "icon": "lime_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=lime]" + }, + { + "baseColor": 19, + "id": "minecraft:lime_stained_glass", + "nameZH": "黄绿色染色玻璃", + "nameEN": "Lime glass", + "icon": "lime_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=lime]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 19, + "id": "minecraft:lime_carpet", + "nameZH": "黄绿色地毯", + "nameEN": "Lime carpet", + "icon": "lime_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=lime]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 20, + "id": "minecraft:pink_concrete", + "nameZH": "粉红色混凝土", + "nameEN": "Pink concrete", + "icon": "pink_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=pink]" + }, + { + "baseColor": 20, + "id": "minecraft:pink_wool", + "nameZH": "粉红色羊毛", + "nameEN": "Pink wool", + "icon": "pink_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=pink]" + }, + { + "baseColor": 20, + "id": "minecraft:pink_stained_glass", + "nameZH": "粉红色染色玻璃", + "nameEN": "Pink glass", + "icon": "pink_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=pink]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 20, + "id": "minecraft:pink_carpet", + "nameZH": "粉色地毯", + "nameEN": "Pink carpet", + "icon": "pink_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=pink]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 20, + "id": "minecraft:pearlescent_froglight[axis=y]", + "nameZH": "珠光蛙明灯", + "nameEN": "Verdant Froglight", + "icon": "pearlescent_froglight_top.png", + "version": 19, + "isGlowing": true + }, + { + "baseColor": 21, + "id": "minecraft:gray_concrete", + "nameZH": "灰色混凝土", + "nameEN": "Gray concrete", + "icon": "gray_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=gray]" + }, + { + "baseColor": 21, + "id": "minecraft:gray_wool", + "nameZH": "灰色羊毛", + "nameEN": "Gray wool", + "icon": "gray_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=gray]", + "burnable": true + }, + { + "baseColor": 21, + "id": "minecraft:gray_stained_glass", + "nameZH": "灰色染色玻璃", + "nameEN": "Gray glass", + "icon": "gray_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=gray]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 21, + "id": "minecraft:gray_carpet", + "nameZH": "灰色地毯", + "nameEN": "Gray carpet", + "icon": "gray_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=gray]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 21, + "id": "minecraft:tinted_glass", + "nameZH": "遮光玻璃", + "nameEN": "Tinted glass", + "icon": "tinted_glass.png", + "version": 17 + }, + { + "baseColor": 22, + "id": "minecraft:light_gray_concrete", + "nameZH": "淡灰色混凝土", + "nameEN": "Light gray concrete", + "icon": "light_gray_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=silver]" + }, + { + "baseColor": 22, + "id": "minecraft:light_gray_wool", + "nameZH": "淡灰色羊毛", + "nameEN": "Light gray wool", + "icon": "light_gray_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=silver]", + "burnable": true + }, + { + "baseColor": 22, + "id": "minecraft:light_gray_stained_glass", + "nameZH": "淡灰色染色玻璃", + "nameEN": "Light gray glass", + "icon": "light_gray_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=silver]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 22, + "id": "minecraft:light_gray_carpet", + "nameZH": "淡灰色地毯", + "nameEN": "Light gray carpet", + "icon": "light_gray_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=light_gray]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 22, + "id": "minecraft:pale_moss_block", + "nameZH": "苍白苔藓块", + "nameEN": "Pale moss block", + "icon": "pale_moss_block.png", + "version": 21, + "endermanPickable": true + }, + { + "baseColor": 23, + "id": "minecraft:cyan_concrete", + "nameZH": "青色混凝土", + "nameEN": "Cyan concrete", + "icon": "cyan_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=cyan]" + }, + { + "baseColor": 23, + "id": "minecraft:cyan_wool", + "nameZH": "青色羊毛", + "nameEN": "Cyan wool", + "icon": "cyan_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=cyan]", + "burnable": true + }, + { + "baseColor": 23, + "id": "minecraft:cyan_stained_glass", + "nameZH": "青色染色玻璃", + "nameEN": "Cyan glass", + "icon": "cyan_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=cyan]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 23, + "id": "minecraft:cyan_carpet", + "nameZH": "青色地毯", + "nameEN": "Cyan carpet", + "icon": "cyan_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=cyan]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 23, + "id": "minecraft:prismarine", + "nameZH": "海晶石", + "nameEN": "Prismarine", + "icon": "prismarine.png", + "version": 0, + "idOld": "minecraft:prismarine[variant=prismarine]" + }, + { + "baseColor": 23, + "id": "minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false]", + "nameZH": "幽匿感测体", + "nameEN": "Sculk sensor", + "icon": "sculk_sensor_side.png", + "version": 19 + }, + { + "baseColor": 24, + "id": "minecraft:purple_concrete", + "nameZH": "紫色混凝土", + "nameEN": "Purple concrete", + "icon": "purple_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=purple]" + }, + { + "baseColor": 24, + "id": "minecraft:purple_wool", + "nameZH": "紫色羊毛", + "nameEN": "Purple wool", + "icon": "purple_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=purple]", + "burnable": true + }, + { + "baseColor": 24, + "id": "minecraft:purple_stained_glass", + "nameZH": "紫色染色玻璃", + "nameEN": "Purple glass", + "icon": "purple_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=purple]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 24, + "id": "minecraft:purple_carpet", + "nameZH": "紫色地毯", + "nameEN": "Purple carpet", + "icon": "purple_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=purple]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 24, + "id": "minecraft:amethyst_block", + "nameZH": "紫水晶块", + "nameEN": "Amethyst block", + "icon": "amethyst_block.png", + "version": 17 + }, + { + "baseColor": 25, + "id": "minecraft:blue_concrete", + "nameZH": "蓝色混凝土", + "nameEN": "Blue concrete", + "icon": "blue_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=blue]" + }, + { + "baseColor": 25, + "id": "minecraft:blue_wool", + "nameZH": "蓝色羊毛", + "nameEN": "Blue wool", + "icon": "blue_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=blue]", + "burnable": true + }, + { + "baseColor": 25, + "id": "minecraft:blue_stained_glass", + "nameZH": "蓝色染色玻璃", + "nameEN": "Blue glass", + "icon": "blue_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=blue]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 25, + "id": "minecraft:blue_carpet", + "nameZH": "蓝色地毯", + "nameEN": "Blue carpet", + "icon": "blue_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=blue]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 26, + "id": "minecraft:brown_concrete", + "nameZH": "棕色混凝土", + "nameEN": "Brown concrete", + "icon": "brown_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=brown]" + }, + { + "baseColor": 26, + "id": "minecraft:brown_wool", + "nameZH": "棕色羊毛", + "nameEN": "Brown wool", + "icon": "brown_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=brown]", + "burnable": true + }, + { + "baseColor": 26, + "id": "minecraft:brown_stained_glass", + "nameZH": "棕色染色玻璃", + "nameEN": "Brown glass", + "icon": "brown_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=brown]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 26, + "id": "minecraft:brown_carpet", + "nameZH": "棕色地毯", + "nameEN": "Brown carpet", + "icon": "brown_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=brown]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 26, + "id": "minecraft:dark_oak_planks", + "nameZH": "深色橡木木板", + "nameEN": "Dark oak plank", + "icon": "dark_oak_planks.png", + "version": 0, + "idOld": "minecraft:planks[variant=dark_oak]", + "burnable": true + }, + { + "baseColor": 26, + "id": "minecraft:soul_sand", + "nameZH": "灵魂沙", + "nameEN": "Soul sand", + "icon": "soul_sand.png", + "version": 0 + }, + { + "baseColor": 26, + "id": "minecraft:soul_soil", + "nameZH": "灵魂土", + "nameEN": "Soul soil", + "icon": "soul_soil.png", + "version": 16 + }, + { + "baseColor": 27, + "id": "minecraft:green_concrete", + "nameZH": "绿色混凝土", + "nameEN": "Green concrete", + "icon": "green_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=green]" + }, + { + "baseColor": 27, + "id": "minecraft:green_wool", + "nameZH": "绿色羊毛", + "nameEN": "Green wool", + "icon": "green_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=green]", + "burnable": true + }, + { + "baseColor": 27, + "id": "minecraft:green_stained_glass", + "nameZH": "绿色染色玻璃", + "nameEN": "Green glass", + "icon": "green_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=green]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 27, + "id": "minecraft:green_carpet", + "nameZH": "绿色地毯", + "nameEN": "Green carpet", + "icon": "green_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=green]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 27, + "id": "minecraft:dried_kelp_block", + "nameZH": "海带块", + "nameEN": "Kelp block", + "icon": "dried_kelp_block.png", + "version": 13 + }, + { + "baseColor": 28, + "id": "minecraft:red_concrete", + "nameZH": "红色混凝土", + "nameEN": "Red concrete", + "icon": "red_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=red]" + }, + { + "baseColor": 28, + "id": "minecraft:red_wool", + "nameZH": "红色羊毛", + "nameEN": "Red wool", + "icon": "red_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=red]", + "burnable": true + }, + { + "baseColor": 28, + "id": "minecraft:red_stained_glass", + "nameZH": "红色染色玻璃", + "nameEN": "Red glass", + "icon": "red_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=red]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 28, + "id": "minecraft:red_carpet", + "nameZH": "红色地毯", + "nameEN": "Red carpet", + "icon": "red_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=red]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 28, + "id": "minecraft:bricks", + "nameZH": "红砖块", + "nameEN": "Bricks", + "icon": "bricks.png", + "version": 0, + "idOld": "minecraft:brick_block" + }, + { + "baseColor": 28, + "id": "minecraft:red_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]", + "nameZH": "红色蘑菇方块", + "nameEN": "Red mushroom block", + "icon": "red_mushroom_block.png", + "version": 0, + "idOld": "minecraft:red_mushroom_block[variant=all_outside]", + "burnable": true + }, + { + "baseColor": 28, + "id": "minecraft:nether_wart_block", + "nameZH": "下界疣块", + "nameEN": "Nether wart block", + "icon": "nether_wart_block.png", + "version": 0 + }, + { + "baseColor": 28, + "id": "minecraft:shroomlight", + "nameZH": "菌光体", + "nameEN": "Shroomlight", + "icon": "shroomlight.png", + "version": 16, + "isGlowing": true + }, + { + "baseColor": 28, + "id": "minecraft:mangrove_planks", + "nameZH": "红树木板", + "nameEN": "Mangrove planks", + "icon": "mangrove_planks.png", + "version": 19, + "burnable": true + }, + { + "baseColor": 29, + "id": "minecraft:black_concrete", + "nameZH": "黑色混凝土", + "nameEN": "Black concrete", + "icon": "black_concrete.png", + "version": 0, + "idOld": "minecraft:concrete[color=black]" + }, + { + "baseColor": 29, + "id": "minecraft:black_wool", + "nameZH": "黑色羊毛", + "nameEN": "Black wool", + "icon": "black_wool.png", + "version": 0, + "idOld": "minecraft:wool[color=black]", + "burnable": true + }, + { + "baseColor": 29, + "id": "minecraft:black_stained_glass", + "nameZH": "黑色染色玻璃", + "nameEN": "Blakc glass", + "icon": "black_stained_glass.png", + "version": 0, + "idOld": "minecraft:stained_glass[color=black]", + "needStone": [ + 12 + ] + }, + { + "baseColor": 29, + "id": "minecraft:black_carpet", + "nameZH": "黑色地毯", + "nameEN": "Black carpet", + "icon": "black_wool.png", + "version": 0, + "idOld": "minecraft:carpet[color=black]", + "burnable": true, + "needStone": true + }, + { + "baseColor": 29, + "id": "minecraft:obsidian", + "nameZH": "黑曜石", + "nameEN": "Obsidian", + "icon": "obsidian.png", + "version": 0 + }, + { + "baseColor": 29, + "id": "minecraft:coal_block", + "nameZH": "煤炭块", + "nameEN": "Coal block", + "icon": "coal_block.png", + "version": 0 + }, + { + "baseColor": 29, + "id": "minecraft:polished_basalt[axis=y]", + "nameZH": "磨制玄武岩", + "nameEN": "Polished basalt", + "icon": "polished_basalt.png", + "version": 16 + }, + { + "baseColor": 29, + "id": "minecraft:polished_blackstone", + "nameZH": "磨制黑石", + "nameEN": "Polished blackstone", + "icon": "polished_blackstone.png", + "version": 16 + }, + { + "baseColor": 29, + "id": "minecraft:sculk", + "nameZH": "幽匿块", + "nameEN": "Sculk block", + "icon": "sculk.png", + "version": 19 + }, + { + "baseColor": 29, + "id": "minecraft:sculk_catalyst[bloom=false]", + "nameZH": "幽匿催发体", + "nameEN": "Sculk catalyst", + "icon": "sculk_catalyst_top.png", + "version": 19 + }, + { + "baseColor": 29, + "id": "minecraft:sculk_shrieker[shrieking=false,waterlogged=false]", + "nameZH": "幽匿尖啸体", + "nameEN": "Sculk shrieker", + "icon": "sculk_shrieker_side.png", + "version": 19 + }, + { + "baseColor": 29, + "id": "minecraft:sculk_vein[down=true,east=false,north=false,south=false,up=false,waterlogged=false,west=false]", + "nameZH": "幽匿脉络", + "nameEN": "Sculk vein", + "icon": "sculk_vein.png", + "version": 19, + "needGlass": true + }, + { + "baseColor": 30, + "id": "minecraft:gold_block", + "nameZH": "金块", + "nameEN": "Gold block", + "icon": "gold_block.png", + "version": 0 + }, + { + "baseColor": 30, + "id": "minecraft:light_weighted_pressure_plate[power=0]", + "nameZH": "轻质测重压力板", + "nameEN": "Gold pressure plate", + "icon": "light_weighted_pressure_plate.png", + "version": 0, + "needStone": true + }, + { + "baseColor": 30, + "id": "minecraft:raw_gold_block", + "nameZH": "粗金块", + "nameEN": "Raw gold block", + "icon": "raw_gold_block.png", + "version": 17 + }, + { + "baseColor": 31, + "id": "minecraft:diamond_block", + "nameZH": "钻石块", + "nameEN": "Diamond block", + "icon": "diamond_block.png", + "version": 0 + }, + { + "baseColor": 31, + "id": "minecraft:prismarine_bricks", + "nameZH": "海晶石砖", + "nameEN": "Prismarine bricks", + "icon": "prismarine_bricks.png", + "version": 0, + "idOld": "minecraft:prismarine[variant=prismarine_bricks]" + }, + { + "baseColor": 31, + "id": "minecraft:dark_prismarine", + "nameZH": "暗海晶石", + "nameEN": "Dark prismarine", + "icon": "dark_prismarine.png", + "version": 0, + "idOld": "minecraft:prismarine[variant=dark_prismarine]" + }, + { + "baseColor": 32, + "id": "minecraft:lapis_block", + "nameZH": "青金石块", + "nameEN": "Lapis block", + "icon": "lapis_block.png", + "version": 0 + }, + { + "baseColor": 33, + "id": "minecraft:emerald_block", + "nameZH": "绿宝石块", + "nameEN": "Emerald block", + "icon": "emerald_block.png", + "version": 0 + }, + { + "baseColor": 34, + "id": "minecraft:podzol[snowy=false]", + "nameZH": "灰化土", + "nameEN": "Podzol", + "icon": "podzol.png", + "version": 0, + "idOld": "minecraft:dirt[variant=podzol,snowy=false]", + "endermanPickable": true + }, + { + "baseColor": 34, + "id": "minecraft:spruce_planks", + "nameZH": "云杉木板", + "nameEN": "Spruce plank", + "icon": "spruce_planks.png", + "version": 0, + "idOld": "minecraft:planks[variant=spruce]", + "burnable": true + }, + { + "baseColor": 34, + "id": "minecraft:campfire[facing=north,lit=true,signal_fire=false,waterlogged=false]", + "nameZH": "营火", + "nameEN": "Campfire", + "icon": "campfire.png", + "version": 14, + "isGlowing": true, + "wallUseable": false + }, + { + "baseColor": 35, + "id": "minecraft:netherrack", + "nameZH": "下界岩", + "nameEN": "Netherrack", + "icon": "netherrack.png", + "version": 0 + }, + { + "baseColor": 35, + "id": "minecraft:nether_bricks", + "nameZH": "下界砖块", + "nameEN": "Nether brick", + "icon": "nether_bricks.png", + "version": 0, + "idOld": "minecraft:nether_brick" + }, + { + "baseColor": 35, + "id": "minecraft:magma_block", + "nameZH": "岩浆块", + "nameEN": "Magma block", + "icon": "magma_block.png", + "version": 0, + "idOld": "minecraft:magma" + }, + { + "baseColor": 36, + "id": "minecraft:white_terracotta", + "nameZH": "白色陶瓦", + "nameEN": "White terracotta", + "icon": "white_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=white]" + }, + { + "baseColor": 36, + "id": "minecraft:calcite", + "nameZH": "方解石", + "nameEN": "Calcite", + "icon": "calcite.png", + "version": 17 + }, + { + "baseColor": 37, + "id": "minecraft:orange_terracotta", + "nameZH": "橙色陶瓦", + "nameEN": "Orange terracotta", + "icon": "orange_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=orange]" + }, + { + "baseColor": 37, + "id": "minecraft:redstone_lamp", + "nameZH": "红石灯", + "nameEN": "Redstone lamp", + "icon": "redstone_lamp.png", + "version": 21 + }, + { + "baseColor": 37, + "id": "minecraft:resin_block", + "nameZH": "树脂块", + "nameEN": "Resin block", + "icon": "block_of_resin.png", + "version": 21 + }, + { + "baseColor": 37, + "id": "minecraft:resin_bricks", + "nameZH": "树脂砖", + "nameEN": "Resin brick", + "icon": "resin_bricks.png", + "version": 21 + }, + { + "baseColor": 38, + "id": "minecraft:magenta_terracotta", + "nameZH": "品红色陶瓦", + "nameEN": "Magenta terracotta", + "icon": "magenta_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=magenta]" + }, + { + "baseColor": 39, + "id": "minecraft:light_blue_terracotta", + "nameZH": "淡蓝色陶瓦", + "nameEN": "Light blue terracotta", + "icon": "light_blue_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=light_blue]" + }, + { + "baseColor": 40, + "id": "minecraft:yellow_terracotta", + "nameZH": "黄色陶瓦", + "nameEN": "Yellow terracotta", + "icon": "yellow_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=yellow]" + }, + { + "baseColor": 41, + "id": "minecraft:lime_terracotta", + "nameZH": "黄绿色陶瓦", + "nameEN": "Lime terracotta", + "icon": "lime_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=lime]" + }, + { + "baseColor": 42, + "id": "minecraft:pink_terracotta", + "nameZH": "粉红色陶瓦", + "nameEN": "Pink terracotta", + "icon": "pink_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=pink]" + }, + { + "baseColor": 43, + "id": "minecraft:gray_terracotta", + "nameZH": "灰色陶瓦", + "nameEN": "Gray terracotta", + "icon": "gray_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=gray]" + }, + { + "baseColor": 44, + "id": "minecraft:light_gray_terracotta", + "nameZH": "淡灰色陶瓦", + "nameEN": "Light gray terracotta", + "icon": "light_gray_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=silver]" + }, + { + "baseColor": 44, + "id": "minecraft:waxed_exposed_copper", + "nameZH": "涂蜡的斑驳铜块", + "nameEN": "Waxed exposed copper", + "icon": "waxed_exposed_copper.png", + "version": 17 + }, + { + "baseColor": 44, + "id": "minecraft:waxed_exposed_cut_copper", + "nameZH": "涂蜡的斑驳切制铜块", + "nameEN": "Waxed exposed cut copper", + "icon": "waxed_exposed_cut_copper.png", + "version": 17 + }, + { + "baseColor": 44, + "id": "minecraft:mud_bricks", + "nameZH": "泥砖", + "nameEN": "Mud bricks", + "icon": "mud_bricks.png", + "version": 19 + }, + { + "baseColor": 44, + "id": "minecraft:waxed_exposed_copper_bulb", + "nameZH": "涂蜡的斑驳铜灯", + "nameEN": "Waxed exposed copper bulb", + "icon": "exposed_copper_bulb.png", + "version": 21 + }, + { + "baseColor": 44, + "id": "minecraft:waxed_exposed_copper_grate", + "nameZH": "涂蜡的斑驳铜格栅", + "nameEN": "Waxed exposed copper grate", + "icon": "exposed_copper_grate.png", + "version": 21 + }, + { + "baseColor": 45, + "id": "minecraft:cyan_terracotta", + "nameZH": "青色陶瓦", + "nameEN": "Cyan terracotta", + "icon": "cyan_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=cyan]" + }, + { + "baseColor": 45, + "id": "minecraft:mud", + "nameZH": "泥巴", + "nameEN": "Mud", + "icon": "mud.png", + "version": 19, + "endermanPickable": true + }, + { + "baseColor": 46, + "id": "minecraft:purple_terracotta", + "nameZH": "紫色陶瓦", + "nameEN": "Purple terracotta", + "icon": "purple_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=purple]" + }, + { + "baseColor": 47, + "id": "minecraft:blue_terracotta", + "nameZH": "蓝色陶瓦", + "nameEN": "Blue terracotta", + "icon": "blue_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=blue]" + }, + { + "baseColor": 48, + "id": "minecraft:brown_terracotta", + "nameZH": "棕色陶瓦", + "nameEN": "Brown terracotta", + "icon": "brown_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=brown]" + }, + { + "baseColor": 48, + "id": "minecraft:dripstone_block", + "nameZH": "滴水石块", + "nameEN": "DripStone Block", + "icon": "dripstone_block.png", + "version": 17 + }, + { + "baseColor": 49, + "id": "minecraft:green_terracotta", + "nameZH": "绿色陶瓦", + "nameEN": "Green terracotta", + "icon": "green_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=green]" + }, + { + "baseColor": 50, + "id": "minecraft:red_terracotta", + "nameZH": "红色陶瓦", + "nameEN": "Red terracotta", + "icon": "red_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=red]" + }, + { + "baseColor": 51, + "id": "minecraft:black_terracotta", + "nameZH": "黑色陶瓦", + "nameEN": "Black terracotta", + "icon": "black_terracotta.png", + "version": 0, + "idOld": "minecraft:stained_hardened_clay[color=black]" + }, + { + "baseColor": 52, + "id": "minecraft:crimson_nylium", + "nameZH": "绯红菌岩", + "nameEN": "Crimson nylium", + "icon": "crimson_nylium.png", + "version": 16, + "endermanPickable": true, + "wallUseable": false + }, + { + "baseColor": 53, + "id": "minecraft:crimson_planks", + "nameZH": "绯红木板", + "nameEN": "Crimson plank", + "icon": "crimson_planks.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 53, + "id": "minecraft:stripped_crimson_stem[axis=y]", + "nameZH": "去皮绯红菌柄", + "nameEN": "Stripped crimson log", + "icon": "stripped_crimson_stem.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 54, + "id": "minecraft:crimson_hyphae[axis=y]", + "nameZH": "绯红菌核", + "nameEN": "Crimson hyphae", + "icon": "crimson_hyphae.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 54, + "id": "minecraft:stripped_crimson_hyphae[axis=y]", + "nameZH": "去皮绯红菌核", + "nameEN": "Stripped crimson hyphae", + "icon": "stripped_crimson_hyphae.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 55, + "id": "minecraft:warped_nylium", + "nameZH": "诡异菌岩", + "nameEN": "Warped nylium", + "icon": "warped_nylium.png", + "version": 16, + "endermanPickable": true, + "wallUseable": false + }, + { + "baseColor": 55, + "id": "minecraft:waxed_oxidized_copper", + "nameZH": "涂蜡的氧化铜块", + "nameEN": "Waxed oxidized copper", + "icon": "waxed_oxidized_copper.png", + "version": 17 + }, + { + "baseColor": 55, + "id": "minecraft:waxed_oxidized_cut_copper", + "nameZH": "涂蜡的氧化切制铜块", + "nameEN": "Waxed oxidized cut copper", + "icon": "waxed_oxidized_cut_copper.png", + "version": 17 + }, + { + "baseColor": 55, + "id": "minecraft:waxed_oxidized_copper_bulb", + "nameZH": "涂蜡的氧化铜灯", + "nameEN": "Waxed oxidized copper bulb", + "icon": "oxidized_copper_bulb.png", + "version": 21 + }, + { + "baseColor": 55, + "id": "minecraft:waxed_oxidized_copper_grate", + "nameZH": "涂蜡的氧化铜格栅", + "nameEN": "Waxed oxidized copper grate", + "icon": "oxidized_copper_grate.png", + "version": 21 + }, + { + "baseColor": 56, + "id": "minecraft:warped_planks", + "nameZH": "诡异木板", + "nameEN": "Warped plank", + "icon": "warped_planks.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 56, + "id": "minecraft:stripped_warped_stem[axis=y]", + "nameZH": "去皮诡异菌柄", + "nameEN": "Stripped warped log", + "icon": "stripped_warped_stem.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 56, + "id": "minecraft:waxed_weathered_copper", + "nameZH": "涂蜡的锈蚀铜块", + "nameEN": "Waxed weathered copper", + "icon": "waxed_weathered_copper.png", + "version": 17 + }, + { + "baseColor": 56, + "id": "minecraft:waxed_weathered_cut_copper", + "nameZH": "涂蜡的锈蚀切制铜块", + "nameEN": "Waxed weathered cut copper", + "icon": "waxed_weathered_cut_copper.png", + "version": 17 + }, + { + "baseColor": 56, + "id": "minecraft:waxed_weathered_copper_bulb", + "nameZH": "涂蜡的锈蚀铜灯", + "nameEN": "Waxed weathered copper bulb", + "icon": "weathered_copper_bulb.png", + "version": 21 + }, + { + "baseColor": 56, + "id": "minecraft:waxed_weathered_copper_grate", + "nameZH": "涂蜡的锈蚀铜格栅", + "nameEN": "Waxed weathered copper grate", + "icon": "weathered_copper_grate.png", + "version": 21 + }, + { + "baseColor": 57, + "id": "minecraft:warped_hyphae[axis=y]", + "nameZH": "诡异菌核", + "nameEN": "Warped hyphae", + "icon": "warped_hyphae.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 57, + "id": "minecraft:stripped_warped_hyphae[axis=y]", + "nameZH": "去皮诡异菌核", + "nameEN": "Stripped warped hyphae", + "icon": "stripped_warped_hyphae.png", + "version": 16, + "burnable": true + }, + { + "baseColor": 58, + "id": "minecraft:warped_wart_block", + "nameZH": "诡异疣块", + "nameEN": "Warped wart block", + "icon": "warped_wart_block.png", + "version": 16 + }, + { + "baseColor": 59, + "id": "minecraft:deepslate", + "nameZH": "深板岩", + "nameEN": "Deepslate", + "icon": "deepslate.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:chiseled_deepslate", + "nameZH": "雕纹深板岩", + "nameEN": "Chiseled deepslate", + "icon": "chiseled_deepslate.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:polished_deepslate", + "nameZH": "磨制深板岩", + "nameEN": "Polished deepslate", + "icon": "polished_deepslate.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:deepslate_bricks", + "nameZH": "深板岩砖", + "nameEN": "Deepslate bricks", + "icon": "deepslate_bricks.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:deepslate_tiles", + "nameZH": "深板岩瓦", + "nameEN": "Deepslate tiles", + "icon": "deepslate_tiles.png", + "version": 17 + }, + { + "baseColor": 59, + "id": "minecraft:cobbled_deepslate", + "nameZH": "深板岩圆石", + "nameEN": "Cobbled deepslate", + "icon": "cobbled_deepslate.png", + "version": 17 + }, + { + "baseColor": 60, + "id": "minecraft:raw_iron_block", + "nameZH": "粗铁块", + "nameEN": "Raw iron block", + "icon": "raw_iron_block.png", + "version": 17 + }, + { + "baseColor": 61, + "id": "minecraft:glow_lichen[down=true,east=false,north=false,south=false,up=false,waterlogged=false]", + "nameZH": "发光地衣", + "nameEN": "Glow lichen", + "icon": "glow_lichen.png", + "version": 17, + "needGlass": true, + "isGlowing": true, + "burnable": true, + "wallUseable": false + }, + { + "baseColor": 61, + "id": "minecraft:verdant_froglight[axis=y]", + "nameZH": "青翠蛙明灯", + "nameEN": "Verdant Froglight", + "icon": "verdant_froglight_top.png", + "version": 19, + "isGlowing": true + }, + { + "baseColor": 43, + "id": "minecraft:cherry_wood", + "nameZH": "樱花原木", + "nameEN": "Cherry wood", + "icon": "cherry_log.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 36, + "id": "minecraft:cherry_planks", + "nameZH": "樱花木板", + "nameEN": "Cherry planks", + "icon": "cherry_planks.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 42, + "id": "minecraft:stripped_cherry_wood[axis=y]", + "nameZH": "去皮樱花原木", + "nameEN": "Stripped cherry wood", + "icon": "stripped_cherry_log.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 18, + "id": "minecraft:bamboo_planks", + "nameZH": "竹板", + "nameEN": "Bamboo planks", + "icon": "bamboo_planks.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 20, + "id": "minecraft:cherry_leaves[persistent=true]", + "nameZH": "樱花树叶", + "nameEN": "Cherry leaves", + "icon": "cherry_leaves.png", + "version": 20, + "burnable": true + }, + { + "baseColor": 50, + "id": "minecraft:decorated_pot", + "nameZH": "陶罐", + "nameEN": "Decorated pot", + "icon": "burn_pottery_pattern.png", + "version": 20 + } +] \ No newline at end of file diff --git a/Blocks/FixedBlocks/block_of_resin.png b/Blocks/FixedBlocks/block_of_resin.png new file mode 100644 index 00000000..a57614fd Binary files /dev/null and b/Blocks/FixedBlocks/block_of_resin.png differ diff --git a/Blocks/FixedBlocks/burn_pottery_pattern.png b/Blocks/FixedBlocks/burn_pottery_pattern.png new file mode 100644 index 00000000..824f7fd1 Binary files /dev/null and b/Blocks/FixedBlocks/burn_pottery_pattern.png differ diff --git a/Blocks/FixedBlocks/cherry_leaves.png b/Blocks/FixedBlocks/cherry_leaves.png new file mode 100644 index 00000000..707ef33b Binary files /dev/null and b/Blocks/FixedBlocks/cherry_leaves.png differ diff --git a/Blocks/FixedBlocks/cherry_log.png b/Blocks/FixedBlocks/cherry_log.png new file mode 100644 index 00000000..6ac7fd1b Binary files /dev/null and b/Blocks/FixedBlocks/cherry_log.png differ diff --git a/Blocks/FixedBlocks/cherry_planks.png b/Blocks/FixedBlocks/cherry_planks.png new file mode 100644 index 00000000..9e8755d5 Binary files /dev/null and b/Blocks/FixedBlocks/cherry_planks.png differ diff --git a/Blocks/FixedBlocks/copper_bulb.png b/Blocks/FixedBlocks/copper_bulb.png new file mode 100644 index 00000000..79275ba3 Binary files /dev/null and b/Blocks/FixedBlocks/copper_bulb.png differ diff --git a/Blocks/FixedBlocks/copper_grate.png b/Blocks/FixedBlocks/copper_grate.png new file mode 100644 index 00000000..67673242 Binary files /dev/null and b/Blocks/FixedBlocks/copper_grate.png differ diff --git a/Blocks/FixedBlocks/exposed_copper_bulb.png b/Blocks/FixedBlocks/exposed_copper_bulb.png new file mode 100644 index 00000000..fc8ce1dc Binary files /dev/null and b/Blocks/FixedBlocks/exposed_copper_bulb.png differ diff --git a/Blocks/FixedBlocks/exposed_copper_grate.png b/Blocks/FixedBlocks/exposed_copper_grate.png new file mode 100644 index 00000000..24c12ec9 Binary files /dev/null and b/Blocks/FixedBlocks/exposed_copper_grate.png differ diff --git a/Blocks/FixedBlocks/oxidized_copper_bulb.png b/Blocks/FixedBlocks/oxidized_copper_bulb.png new file mode 100644 index 00000000..2279b3b4 Binary files /dev/null and b/Blocks/FixedBlocks/oxidized_copper_bulb.png differ diff --git a/Blocks/FixedBlocks/oxidized_copper_grate.png b/Blocks/FixedBlocks/oxidized_copper_grate.png new file mode 100644 index 00000000..1cb5779c Binary files /dev/null and b/Blocks/FixedBlocks/oxidized_copper_grate.png differ diff --git a/Blocks/FixedBlocks/pale_moss_block.png b/Blocks/FixedBlocks/pale_moss_block.png new file mode 100644 index 00000000..249efbd3 Binary files /dev/null and b/Blocks/FixedBlocks/pale_moss_block.png differ diff --git a/Blocks/FixedBlocks/pale_oak_leaves.png b/Blocks/FixedBlocks/pale_oak_leaves.png new file mode 100644 index 00000000..11729ce7 Binary files /dev/null and b/Blocks/FixedBlocks/pale_oak_leaves.png differ diff --git a/Blocks/FixedBlocks/pale_oak_planks.png b/Blocks/FixedBlocks/pale_oak_planks.png new file mode 100644 index 00000000..82fcc1cf Binary files /dev/null and b/Blocks/FixedBlocks/pale_oak_planks.png differ diff --git a/Blocks/FixedBlocks/pale_oak_slab.png b/Blocks/FixedBlocks/pale_oak_slab.png new file mode 100644 index 00000000..bc7ee9f5 Binary files /dev/null and b/Blocks/FixedBlocks/pale_oak_slab.png differ diff --git a/Blocks/FixedBlocks/redstone_lamp.png b/Blocks/FixedBlocks/redstone_lamp.png new file mode 100644 index 00000000..5fe0df0b Binary files /dev/null and b/Blocks/FixedBlocks/redstone_lamp.png differ diff --git a/Blocks/FixedBlocks/resin_bricks.png b/Blocks/FixedBlocks/resin_bricks.png new file mode 100644 index 00000000..62f0834d Binary files /dev/null and b/Blocks/FixedBlocks/resin_bricks.png differ diff --git a/Blocks/FixedBlocks/stripped_cherry_log.png b/Blocks/FixedBlocks/stripped_cherry_log.png new file mode 100644 index 00000000..6ada3294 Binary files /dev/null and b/Blocks/FixedBlocks/stripped_cherry_log.png differ diff --git a/Blocks/FixedBlocks/water.png b/Blocks/FixedBlocks/water.png index cee211cc..1ce7b8e8 100644 Binary files a/Blocks/FixedBlocks/water.png and b/Blocks/FixedBlocks/water.png differ diff --git a/Blocks/FixedBlocks/weathered_copper_bulb.png b/Blocks/FixedBlocks/weathered_copper_bulb.png new file mode 100644 index 00000000..6312797d Binary files /dev/null and b/Blocks/FixedBlocks/weathered_copper_bulb.png differ diff --git a/Blocks/FixedBlocks/weathered_copper_grate.png b/Blocks/FixedBlocks/weathered_copper_grate.png new file mode 100644 index 00000000..20952131 Binary files /dev/null and b/Blocks/FixedBlocks/weathered_copper_grate.png differ diff --git a/Blocks/QuarkModBlocks/ancient_leaves.png b/Blocks/QuarkModBlocks/ancient_leaves.png new file mode 100644 index 00000000..9f4c7b02 Binary files /dev/null and b/Blocks/QuarkModBlocks/ancient_leaves.png differ diff --git a/Blocks/QuarkModBlocks/ancient_planks.png b/Blocks/QuarkModBlocks/ancient_planks.png new file mode 100644 index 00000000..75005089 Binary files /dev/null and b/Blocks/QuarkModBlocks/ancient_planks.png differ diff --git a/Blocks/QuarkModBlocks/ancient_planks_slab.png b/Blocks/QuarkModBlocks/ancient_planks_slab.png new file mode 100644 index 00000000..d451a33c Binary files /dev/null and b/Blocks/QuarkModBlocks/ancient_planks_slab.png differ diff --git a/Blocks/QuarkModBlocks/azalea_planks.png b/Blocks/QuarkModBlocks/azalea_planks.png new file mode 100644 index 00000000..2a514ce0 Binary files /dev/null and b/Blocks/QuarkModBlocks/azalea_planks.png differ diff --git a/Blocks/QuarkModBlocks/azalea_planks_slab.png b/Blocks/QuarkModBlocks/azalea_planks_slab.png new file mode 100644 index 00000000..393b9911 Binary files /dev/null and b/Blocks/QuarkModBlocks/azalea_planks_slab.png differ diff --git a/Blocks/QuarkModBlocks/black_shingles_slab.png b/Blocks/QuarkModBlocks/black_shingles_slab.png new file mode 100644 index 00000000..c2548a45 Binary files /dev/null and b/Blocks/QuarkModBlocks/black_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/blaze_lantern.png b/Blocks/QuarkModBlocks/blaze_lantern.png new file mode 100644 index 00000000..320d37ff Binary files /dev/null and b/Blocks/QuarkModBlocks/blaze_lantern.png differ diff --git a/Blocks/QuarkModBlocks/block_list.json b/Blocks/QuarkModBlocks/block_list.json new file mode 100644 index 00000000..ac5a370b --- /dev/null +++ b/Blocks/QuarkModBlocks/block_list.json @@ -0,0 +1,359 @@ +[ + { + "baseColor": 5, + "id": "quark:shale", + "nameZH": "页岩", + "nameEN": "Shale", + "icon": "shale.png", + "version": 18 + }, + { + "baseColor": 6, + "id": "quark:iron_plate", + "nameZH": "铁板块", + "nameEN": "Iron plate", + "icon": "iron_plate.png", + "version": 18 + }, + { + "baseColor": 7, + "id": "quark:ancient_leaves[persistent=true]", + "nameZH": "Ashen 树叶", + "nameEN": "Ashen leaves", + "burnable": true, + "icon": "ancient_leaves.png", + "version": 19 + }, + { + "baseColor": 11, + "id": "quark:limestone", + "nameZH": "石灰石", + "nameEN": "Lime stone", + "icon": "limestone.png", + "version": 18 + }, + { + "baseColor": 13, + "id": "quark:stick_block", + "nameZH": "木棍堆", + "nameEN": "Stick block", + "icon": "stick_block_top.png", + "version": 14 + }, + { + "baseColor": 15, + "id": "quark:shingles_slab[type=bottom]", + "nameZH": "陶瓦砖瓦台阶", + "nameEN": "Shingles slab", + "icon": "shingles_slab.png", + "version": 14 + }, + { + "baseColor": 15, + "id": "quark:raw_copper_bricks_slab[type=bottom]", + "nameZH": "生铜块台阶", + "nameEN": "Raw copper bricks slab", + "icon": "raw_copper_bricks.png", + "version": 19 + }, + { + "baseColor": 16, + "id": "quark:duskbound_block", + "nameZH": "黯缚块", + "nameEN": "Duskbound block", + "icon": "duskbound_block.png", + "version": 14 + }, + { + "baseColor": 17, + "id": "quark:permafrost", + "nameZH": "永冻石", + "nameEN": "Perma frost", + "icon": "permafrost.png", + "version": 18 + }, + { + "baseColor": 17, + "id": "quark:blue_blossom_leaves[persistent=true]", + "nameZH": "雪霜花树叶", + "nameEN": "Frosty trumpet leaves", + "burnable": true, + "icon": "blue_blossom_leaves.png", + "version": 20 + }, + { + "baseColor": 18, + "id": "quark:blaze_lantern", + "nameZH": "烈焰灯", + "nameEN": "Blaze lantern", + "isGlowing": true, + "icon": "blaze_lantern.png", + "version": 14 + }, + { + "baseColor": 18, + "id": "quark:yellow_blossom_leaves[persistent=true]", + "nameZH": "阳光花树叶", + "nameEN": "Sunny trumpet leaves", + "burnable": true, + "icon": "yellow_blossom_leaves.png", + "version": 20 + }, + { + "baseColor": 19, + "id": "quark:midori_block", + "nameZH": "绿块", + "nameEN": "Midori block", + "icon": "midori_block.png", + "version": 14 + }, + { + "baseColor": 19, + "id": "quark:azalea_planks", + "nameZH": "杜鹃木板", + "nameEN": "Azalea planks", + "burnable": true, + "icon": "azalea_planks.png", + "version": 18 + }, + { + "baseColor": 19, + "id": "quark:azalea_planks_slab[type=bottom]", + "nameZH": "杜鹃木台阶", + "nameEN": "Azalea plank slab", + "burnable": true, + "icon": "azalea_planks_slab.png", + "version": 18 + }, + { + "baseColor": 19, + "id": "quark:sugar_cane_block", + "nameZH": "甘蔗捆", + "nameEN": "Sugar cane block", + "icon": "sugar_cane_block.png", + "version": 14 + }, + { + "baseColor": 20, + "id": "quark:lavender_blossom_leaves[persistent=true]", + "nameZH": "幽静花树叶", + "nameEN": "Serene trumpet leaves", + "burnable": true, + "icon": "lavender_blossom_leaves.png", + "version": 20 + }, + { + "baseColor": 24, + "id": "quark:myalite", + "nameZH": "幻境石", + "nameEN": "Myalite", + "icon": "myalite.png", + "version": 18 + }, + { + "baseColor": 24, + "id": "quark:chorus_fruit_block", + "nameZH": "紫颂果块", + "nameEN": "Chorus fruit block", + "icon": "chorus_fruit_block.png", + "version": 16 + }, + { + "baseColor": 27, + "id": "quark:cactus_block", + "nameZH": "仙人掌块", + "nameEN": "Cactus block", + "icon": "cactus_block.png", + "version": 14 + }, + { + "baseColor": 28, + "id": "quark:blossom_planks", + "nameZH": "花木板", + "nameEN": "Trumpet planks", + "burnable": true, + "icon": "blossom_planks.png", + "version": 20 + }, + { + "baseColor": 28, + "id": "quark:blossom_planks_slab[type=bottom]", + "nameZH": "花木台阶", + "nameEN": "Trumpet plank slab", + "burnable": true, + "icon": "blossom_planks_slab.png", + "version": 20 + }, + { + "baseColor": 28, + "id": "quark:red_blossom_leaves[persistent=true]", + "nameZH": "火热花树叶", + "nameEN": "Fiery trumpet leaves", + "burnable": true, + "icon": "red_blossom_leaves.png", + "version": 20 + }, + { + "baseColor": 30, + "id": "quark:raw_gold_bricks_slab[type=bottom]", + "nameZH": "生金块台阶", + "nameEN": "Raw gold bricks slab", + "icon": "raw_gold_bricks_slab.png", + "version": 19 + }, + { + "baseColor": 36, + "id": "quark:ancient_planks", + "nameZH": "Ashen 木板", + "nameEN": "Ashen planks", + "burnable": true, + "icon": "ancient_planks.png", + "version": 19 + }, + { + "baseColor": 36, + "id": "quark:ancient_planks_slab[type=bottom]", + "nameZH": "Ashen 木台阶", + "nameEN": "Ashen plank slab", + "burnable": true, + "icon": "ancient_planks_slab.png", + "version": 19 + }, + { + "baseColor": 36, + "id": "quark:white_shingles_slab[type=bottom]", + "nameZH": "白色陶瓦砖瓦台阶", + "nameEN": "White shingles slab", + "icon": "white_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 37, + "id": "quark:orange_shingles_slab[type=bottom]", + "nameZH": "橙色陶瓦砖瓦台阶", + "nameEN": "Orange shingles slab", + "icon": "orange_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 37, + "id": "quark:orange_blossom_leaves[persistent=true]", + "nameZH": "温暖花树叶", + "nameEN": "Warm trumpet leaves", + "burnable": true, + "icon": "orange_blossom_leaves.png", + "version": 20 + }, + { + "baseColor": 38, + "id": "quark:magenta_shingles_slab[type=bottom]", + "nameZH": "品红色陶瓦砖瓦台阶", + "nameEN": "Magenta shingles slab", + "icon": "magenta_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 39, + "id": "quark:light_blue_shingles_slab[type=bottom]", + "nameZH": "浅蓝色陶瓦砖瓦台阶", + "nameEN": "Light blue shingles slab", + "icon": "light_blue_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 40, + "id": "quark:yellow_shingles_slab[type=bottom]", + "nameZH": "黄色陶瓦砖瓦台阶", + "nameEN": "Yellow shingles slab", + "icon": "yellow_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 42, + "id": "quark:pink_shingles_slab[type=bottom]", + "nameZH": "粉色陶瓦砖瓦台阶", + "nameEN": "Pink shingles slab", + "icon": "pink_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 41, + "id": "quark:lime_shingles_slab[type=bottom]", + "nameZH": "黄绿色陶瓦砖瓦台阶", + "nameEN": "Lime shingles slab", + "icon": "lime_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 45, + "id": "quark:cyan_shingles_slab[type=bottom]", + "nameZH": "青色陶瓦砖瓦台阶", + "nameEN": "Cyan shingles slab", + "icon": "cyan_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 46, + "id": "quark:purple_shingles_slab[type=bottom]", + "nameZH": "紫色陶瓦砖瓦台阶", + "nameEN": "Purple shingles slab", + "icon": "purple_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 47, + "id": "quark:blue_shingles_slab[type=bottom]", + "nameZH": "蓝色陶瓦砖瓦台阶", + "nameEN": "Blue shingles slab", + "icon": "blue_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 49, + "id": "quark:Green_shingles_slab[type=bottom]", + "nameZH": "绿色陶瓦砖瓦台阶", + "nameEN": "Green shingles slab", + "icon": "green_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 48, + "id": "quark:brown_shingles_slab[type=bottom]", + "nameZH": "棕色陶瓦砖瓦台阶", + "nameEN": "Brown shingles slab", + "icon": "brown_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 50, + "id": "quark:jasper", + "nameZH": "碧玉石", + "nameEN": "Jasper", + "icon": "jasper.png", + "version": 18 + }, + { + "baseColor": 50, + "id": "quark:red_shingles_slab[type=bottom]", + "nameZH": "红色陶瓦砖瓦台阶", + "nameEN": "Red shingles slab", + "icon": "red_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 51, + "id": "quark:black_shingles_slab[type=bottom]", + "nameZH": "黑色陶瓦砖瓦台阶", + "nameEN": "Black shingles slab", + "icon": "black_shingles_slab.png", + "version": 14 + }, + { + "baseColor": 60, + "id": "quark:raw_iron_bricks_slab[type=bottom]", + "nameZH": "生铁块台阶", + "nameEN": "Raw iron bricks slab", + "icon": "raw_iron_bricks_slab.png", + "version": 19 + } +] \ No newline at end of file diff --git a/Blocks/QuarkModBlocks/blossom_planks.png b/Blocks/QuarkModBlocks/blossom_planks.png new file mode 100644 index 00000000..8aa26c33 Binary files /dev/null and b/Blocks/QuarkModBlocks/blossom_planks.png differ diff --git a/Blocks/QuarkModBlocks/blossom_planks_slab.png b/Blocks/QuarkModBlocks/blossom_planks_slab.png new file mode 100644 index 00000000..948f20d4 Binary files /dev/null and b/Blocks/QuarkModBlocks/blossom_planks_slab.png differ diff --git a/Blocks/QuarkModBlocks/blue_blossom_leaves.png b/Blocks/QuarkModBlocks/blue_blossom_leaves.png new file mode 100644 index 00000000..8684d5e8 Binary files /dev/null and b/Blocks/QuarkModBlocks/blue_blossom_leaves.png differ diff --git a/Blocks/QuarkModBlocks/blue_shingles_slab.png b/Blocks/QuarkModBlocks/blue_shingles_slab.png new file mode 100644 index 00000000..447422ca Binary files /dev/null and b/Blocks/QuarkModBlocks/blue_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/brown_shingles_slab.png b/Blocks/QuarkModBlocks/brown_shingles_slab.png new file mode 100644 index 00000000..11fb3fd7 Binary files /dev/null and b/Blocks/QuarkModBlocks/brown_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/cactus_block.png b/Blocks/QuarkModBlocks/cactus_block.png new file mode 100644 index 00000000..cb67278f Binary files /dev/null and b/Blocks/QuarkModBlocks/cactus_block.png differ diff --git a/Blocks/QuarkModBlocks/chorus_fruit_block.png b/Blocks/QuarkModBlocks/chorus_fruit_block.png new file mode 100644 index 00000000..7aa41ab7 Binary files /dev/null and b/Blocks/QuarkModBlocks/chorus_fruit_block.png differ diff --git a/Blocks/QuarkModBlocks/cyan_shingles_slab.png b/Blocks/QuarkModBlocks/cyan_shingles_slab.png new file mode 100644 index 00000000..9c463355 Binary files /dev/null and b/Blocks/QuarkModBlocks/cyan_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/duskbound_block.png b/Blocks/QuarkModBlocks/duskbound_block.png new file mode 100644 index 00000000..9396cd7d Binary files /dev/null and b/Blocks/QuarkModBlocks/duskbound_block.png differ diff --git a/Blocks/QuarkModBlocks/green_shingles_slab.png b/Blocks/QuarkModBlocks/green_shingles_slab.png new file mode 100644 index 00000000..e83d43f8 Binary files /dev/null and b/Blocks/QuarkModBlocks/green_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/iron_plate.png b/Blocks/QuarkModBlocks/iron_plate.png new file mode 100644 index 00000000..c2dad8fc Binary files /dev/null and b/Blocks/QuarkModBlocks/iron_plate.png differ diff --git a/Blocks/QuarkModBlocks/jasper.png b/Blocks/QuarkModBlocks/jasper.png new file mode 100644 index 00000000..8bb195a1 Binary files /dev/null and b/Blocks/QuarkModBlocks/jasper.png differ diff --git a/Blocks/QuarkModBlocks/lavender_blossom_leaves.png b/Blocks/QuarkModBlocks/lavender_blossom_leaves.png new file mode 100644 index 00000000..d8ce4286 Binary files /dev/null and b/Blocks/QuarkModBlocks/lavender_blossom_leaves.png differ diff --git a/Blocks/QuarkModBlocks/light_blue_shingles_slab.png b/Blocks/QuarkModBlocks/light_blue_shingles_slab.png new file mode 100644 index 00000000..163f3319 Binary files /dev/null and b/Blocks/QuarkModBlocks/light_blue_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/light_gray_shingles_slab.png b/Blocks/QuarkModBlocks/light_gray_shingles_slab.png new file mode 100644 index 00000000..dcbb63e5 Binary files /dev/null and b/Blocks/QuarkModBlocks/light_gray_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/lime_shingles_slab.png b/Blocks/QuarkModBlocks/lime_shingles_slab.png new file mode 100644 index 00000000..3fab8113 Binary files /dev/null and b/Blocks/QuarkModBlocks/lime_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/limestone.png b/Blocks/QuarkModBlocks/limestone.png new file mode 100644 index 00000000..ca5175a5 Binary files /dev/null and b/Blocks/QuarkModBlocks/limestone.png differ diff --git a/Blocks/QuarkModBlocks/magenta_shingles_slab.png b/Blocks/QuarkModBlocks/magenta_shingles_slab.png new file mode 100644 index 00000000..7f886e7a Binary files /dev/null and b/Blocks/QuarkModBlocks/magenta_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/metainfo.json b/Blocks/QuarkModBlocks/metainfo.json new file mode 100644 index 00000000..70baca78 --- /dev/null +++ b/Blocks/QuarkModBlocks/metainfo.json @@ -0,0 +1,9 @@ +{ + "name prefix": { + "ZH": "[夸克]", + "EN": "[Quark]" + }, + "required mods": [ + "Quark" + ] +} \ No newline at end of file diff --git a/Blocks/QuarkModBlocks/midori_block.png b/Blocks/QuarkModBlocks/midori_block.png new file mode 100644 index 00000000..c82dd91b Binary files /dev/null and b/Blocks/QuarkModBlocks/midori_block.png differ diff --git a/Blocks/QuarkModBlocks/myalite.png b/Blocks/QuarkModBlocks/myalite.png new file mode 100644 index 00000000..4fe95e89 Binary files /dev/null and b/Blocks/QuarkModBlocks/myalite.png differ diff --git a/Blocks/QuarkModBlocks/orange_blossom_leaves.png b/Blocks/QuarkModBlocks/orange_blossom_leaves.png new file mode 100644 index 00000000..fad9259f Binary files /dev/null and b/Blocks/QuarkModBlocks/orange_blossom_leaves.png differ diff --git a/Blocks/QuarkModBlocks/orange_shingles_slab.png b/Blocks/QuarkModBlocks/orange_shingles_slab.png new file mode 100644 index 00000000..e9592154 Binary files /dev/null and b/Blocks/QuarkModBlocks/orange_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/permafrost.png b/Blocks/QuarkModBlocks/permafrost.png new file mode 100644 index 00000000..ad35d125 Binary files /dev/null and b/Blocks/QuarkModBlocks/permafrost.png differ diff --git a/Blocks/QuarkModBlocks/pink_shingles_slab.png b/Blocks/QuarkModBlocks/pink_shingles_slab.png new file mode 100644 index 00000000..d178a598 Binary files /dev/null and b/Blocks/QuarkModBlocks/pink_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/purple_shingles_slab.png b/Blocks/QuarkModBlocks/purple_shingles_slab.png new file mode 100644 index 00000000..8d8ad9b7 Binary files /dev/null and b/Blocks/QuarkModBlocks/purple_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/raw_copper_bricks.png b/Blocks/QuarkModBlocks/raw_copper_bricks.png new file mode 100644 index 00000000..b0677e19 Binary files /dev/null and b/Blocks/QuarkModBlocks/raw_copper_bricks.png differ diff --git a/Blocks/QuarkModBlocks/raw_gold_bricks_slab.png b/Blocks/QuarkModBlocks/raw_gold_bricks_slab.png new file mode 100644 index 00000000..5f30894b Binary files /dev/null and b/Blocks/QuarkModBlocks/raw_gold_bricks_slab.png differ diff --git a/Blocks/QuarkModBlocks/raw_iron_bricks_slab.png b/Blocks/QuarkModBlocks/raw_iron_bricks_slab.png new file mode 100644 index 00000000..79ecd817 Binary files /dev/null and b/Blocks/QuarkModBlocks/raw_iron_bricks_slab.png differ diff --git a/Blocks/QuarkModBlocks/red_blossom_leaves.png b/Blocks/QuarkModBlocks/red_blossom_leaves.png new file mode 100644 index 00000000..0eddbb6a Binary files /dev/null and b/Blocks/QuarkModBlocks/red_blossom_leaves.png differ diff --git a/Blocks/QuarkModBlocks/red_shingles_slab.png b/Blocks/QuarkModBlocks/red_shingles_slab.png new file mode 100644 index 00000000..c6cf1efa Binary files /dev/null and b/Blocks/QuarkModBlocks/red_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/shale.png b/Blocks/QuarkModBlocks/shale.png new file mode 100644 index 00000000..c8a0ec00 Binary files /dev/null and b/Blocks/QuarkModBlocks/shale.png differ diff --git a/Blocks/QuarkModBlocks/shingles_slab.png b/Blocks/QuarkModBlocks/shingles_slab.png new file mode 100644 index 00000000..3bfc44f5 Binary files /dev/null and b/Blocks/QuarkModBlocks/shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/stick_block_top.png b/Blocks/QuarkModBlocks/stick_block_top.png new file mode 100644 index 00000000..e9441021 Binary files /dev/null and b/Blocks/QuarkModBlocks/stick_block_top.png differ diff --git a/Blocks/QuarkModBlocks/sugar_cane_block.png b/Blocks/QuarkModBlocks/sugar_cane_block.png new file mode 100644 index 00000000..d4948948 Binary files /dev/null and b/Blocks/QuarkModBlocks/sugar_cane_block.png differ diff --git a/Blocks/QuarkModBlocks/white_shingles_slab.png b/Blocks/QuarkModBlocks/white_shingles_slab.png new file mode 100644 index 00000000..cda7a5cc Binary files /dev/null and b/Blocks/QuarkModBlocks/white_shingles_slab.png differ diff --git a/Blocks/QuarkModBlocks/yellow_blossom_leaves.png b/Blocks/QuarkModBlocks/yellow_blossom_leaves.png new file mode 100644 index 00000000..01ada507 Binary files /dev/null and b/Blocks/QuarkModBlocks/yellow_blossom_leaves.png differ diff --git a/Blocks/QuarkModBlocks/yellow_shingles_slab.png b/Blocks/QuarkModBlocks/yellow_shingles_slab.png new file mode 100644 index 00000000..558f1c53 Binary files /dev/null and b/Blocks/QuarkModBlocks/yellow_shingles_slab.png differ diff --git a/Blocks/README.md b/Blocks/README.md index 56a6f527..fbe168be 100644 --- a/Blocks/README.md +++ b/Blocks/README.md @@ -1,41 +1,49 @@ # 方块列表 -这个文件夹存储了方块列表相关。 -**FixedBlocks.json**是软件固定的方块列表,是最基础的那部分。每个方块相应的图片都存储在**FixedBlocks**文件夹下。正常情况下无需任何改动。 +这个压缩包存储了方块列表相关,包含`block_list.json`和若干方块对应的图片(必须为PNG格式)。 -**CustomBlocks.json**是用户自定义的方块列表,允许用户灵活编辑。我在里面写了一些半砖方块,可以作为参考。**CustomBlocks**文件夹里有用户自定义方块的图片。 +**FixedBlocks.zip**是软件固定的方块列表,是最基础的那部分。每个方块相应的图片都存储在**FixedBlocks**文件夹下。正常情况下无需任何改动。 -每个方块拥有以下属性: +**CustomBlocks.zip**是用户自定义的方块列表,允许用户灵活编辑。我在里面写了一些半砖方块,可以作为参考。**CustomBlocks** +文件夹里有用户自定义方块的图片。 -| 属性名 | 类型 | 是否必填 | 默认值 | 说明 | -| :--------------: | :----: | :------: | :------: | -------------------------------------------------------------------------- | -| baseColor | byte | 是 | | 方块的地图基色 | -| id | string | 是 | | 方块 id,附上详细方块状态,如*minecraft:target[power=0]* | -| version | byte | 是 | | 方块最早出现的版本。0 代表 1.12 以前,12 代表 1.12,17 代表 1.17,以此类推 | -| nameZH | string | 是 | | 方块的中文名 | -| nameEN | string | 是 | | 方块的英文名 | -| icon | string | 是 | | 方块对应图片的文件名(仅文件名,如*cobblestone.png*) | -| idOld | string | 否 | 空字符串 | 方块在 1.12 的 id | -| needGlass | bool | 否 | false | 指示方块底部是否必须有其他方块(如灯笼) | -| isGlowing | bool | 否 | false | 指示方块是否发光 | -| endermanPickable | bool | 否 | false | 指示方块是否可以被末影人偷走 | -| burnable | bool | 否 | false | 指示方块是否可以被烧毁 | +`block_list.json`记录了所有方块,每个方块拥有以下属性: + +| 属性名 | 类型 | 是否必填 | 默认值 | 说明 | +|:----------------:|:------:|:----:|:-----:|---------------------------------------------------| +| baseColor | byte | 是 | | 方块的地图基色 | +| id | string | 是 | | 方块 id,附上详细方块状态,如*minecraft:target[power=0]* | +| version | byte | 是 | | 方块最早出现的版本。0 代表 1.12 以前,12 代表 1.12,17 代表 1.17,以此类推 | +| nameZH | string | 是 | | 方块的中文名 | +| nameEN | string | 是 | | 方块的英文名 | +| icon | string | 是 | | 方块对应图片的文件名(仅文件名,如*cobblestone.png*) | +| idOld | string | 否 | 空字符串 | 方块在 1.12 的 id | +| needGlass | bool | 否 | false | 方块底部是否必须有其他方块(如灯笼) | +| isGlowing | bool | 否 | false | 是否发光 | +| endermanPickable | bool | 否 | false | 是否可以被末影人偷走 | +| burnable | bool | 否 | false | 是否可以被烧毁 | 其中选填项可以跳过不填,SlopeCraft 会自动补全为默认值。

-# BlockList -Everything about blocklist is in this directory. +# block_list + +Everything about blocklist is in this zip, including `block_list.json` and block images (must be PNG format). + +**FixedBlocks.zip** stores all default blocks, acting as a fundamental blocklist to make sure that each +basecolor_widgets has at least one block. Image of each block is stored in directory named **FixedBlocks**. Usually you +don't need to change anything about fixed blocklist. -**FixedBlocks.json** stores all default blocks, acting as a fundamental blocklist to make sure that each basecolor has at least one block. Image of each block is stored in directory named **FixedBlocks**. Usually you don't need to change anything about fixed blocklist. +**CustomBlocks.zip** stores all user custom blocks, enabling users to use their favorite blocks in map art. Some slab +blocks have been already written in this file as examples. Each block should have a corresponding image in * +*CustomBlocks** directory. Different blocks can share single image file. -**CustomBlocks.json** stores all user custom blocks, enabling users to use their favorite blocks in map art. Some slab blocks have been already written in this file as examples. Each block should have a corresponding image in **CustomBlocks** directory. Different blocks can share single image file. +Each block has the following attributes: -Each block has following attributes: | Name | Type | Is Compulsory Item | Default Value | Description | -| :--------------: | :----: | :----------------: | :-----------: | ---------------------------------------------------------------------------------------------------------- | +|:----------------:|:------:|:------------------:|:-------------:|:-----------------------------------------------------------------------------------------------------------| | baseColor | byte | Yes | | The base color of this block | | id | string | Yes | | Block id in minecraft with full blockstates, like *minecraft:target[power=0]* | | version | byte | Yes | | The earlist version when block is added. 0 means earlier than 1.12, 12 means 1.12, 17 means 1.17 and so on | diff --git a/CMakeLists.txt b/CMakeLists.txt index c4226ee9..d7830624 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,36 @@ cmake_minimum_required(VERSION 3.20) -set(SlopeCraft_version 5.0.0) +# set version ----------------------------------------------------------------- +set(SlopeCraft_version 5.3.3) +# set basic project attributes ------------------------------------------------ project(SlopeCraft VERSION ${SlopeCraft_version} LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) -set(SlopeCraft_GPU_API "OpenCL" CACHE STRING "API used to compute. Valid values : OpenCL, None. Metal may be supported.") +if (${LINUX}) + message(STATUS "setting runpath for linux") + set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") + + # set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + # set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) +endif () + +enable_testing() + +# configure options ----------------------------------------------------------- +if (${APPLE}) + set(SlopeCraft_GPU_API "None" CACHE STRING "API used to compute. Valid values : OpenCL, None. Metal may be supported.") + option(SlopeCraft_vectorize "Compile with vectorization" OFF) + message(STATUS "GPU boosting and vectorization have been disabled on mac by default") + +else () + set(SlopeCraft_GPU_API "OpenCL" CACHE STRING "API used to compute. Valid values : OpenCL, None. Metal may be supported.") + option(SlopeCraft_vectorize "Compile with vectorization" ON) +endif () -option(SlopeCraft_vectorize "Compile with vectorization" ON) option(SlopeCraft_update_ts_files "Update language files before build. If it is set to ON, everytime CMake reconfigure the project, all .ts files will be updated." OFF) @@ -17,131 +38,123 @@ option(SlopeCraft_update_ts_no_obsolete "Remove obsolete translations from ts fi option(SlopeCraft_gprof "Profile with gprof" OFF) -if(${MSVC}) - add_compile_options("/Zc:__cplusplus") - add_compile_options("/wd4819") - add_compile_definitions("ZLIB_WINAPI") - add_compile_definitions("Z_HAVE_UNISTD_H") -else() - add_compile_options(-Wall -Wreturn-type -Wsign-compare -Wextra) - add_link_options(-flto) -endif() +option(SlopeCraft_sanitize "Build with sanitizer" OFF) -enable_testing() +set(SlopeCraft_vccl_test_gpu_platform_idx 0 CACHE STRING "The opencl platform index used to test vccl") +set(SlopeCraft_vccl_test_gpu_device_idx 0 CACHE STRING "The opencl device index used to test vccl") + +if (${WIN32}) + set(SlopeCraft_windeployqt_flags_install "--no-translations" CACHE STRING "Flags to run windeployqt during installation") + set(SlopeCraft_windeployqt_flags_build "${SlopeCraft_windeployqt_flags_install};--force" CACHE STRING "Flags to run windeployqt in build dir") +endif () -if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - message(WARNING "MSVC or Clang are not fully supported. - You MAY meet werid errors if you continue to build. - Please use gcc.") -endif() +if (${APPLE}) + set(SlopeCraft_macdeployqt_flags_install "" CACHE STRING "Flags to run macdeployqt during installation") + # set(SlopeCraft_macdeployqt_flags_build "${SlopeCraft_windeployqt_flags_install};--force" CACHE STRING "Flags to run macdeployqt in build dir") +endif () +# manage the install prefix -------------------------------------------------- +if (NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install) +endif () -# manage the install prefix -set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install - CACHE PATH "Where to install SlopeCraft.") +# convert relative path to absolute ------------------------------------------ +cmake_path(ABSOLUTE_PATH CMAKE_INSTALL_PREFIX OUTPUT_VARIABLE CMAKE_INSTALL_PREFIX) +message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") -# set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}) -# set(CMAKE_INSTALL_BINDIR ${CMAKE_INSTALL_PREFIX}) -if(EXISTS ${CMAKE_SOURCE_DIR}/compile_commands.json) +# Remove old compilation database -------------------------------------------- +if (EXISTS ${CMAKE_SOURCE_DIR}/compile_commands.json) # delete files here file(REMOVE ${CMAKE_SOURCE_DIR}/compile_commands.json) -endif() - -include(cmake/add_compiler_path_to_prefix.cmake) - -list(LENGTH CMAKE_PREFIX_PATH temp_L) +endif () -include(cmake/find_nlohmann_json.cmake) -include(cmake/find_Eigen3.cmake) -include(cmake/find_HeuristicFlow.cmake) +# configure global dependencies ---------------------------------------------- -include(cmake/find_qt6.cmake) - -# find lupdate executable -if(${SlopeCraft_update_ts_files} AND(NOT DEFINED SlopeCraft_Qt_lupdate_executable)) - find_program(SlopeCraft_Qt_lupdate_executable name lupdate PATHS ${CMAKE_PREFIX_PATH} REQUIRED) - message(STATUS "Found lupdate at : " ${SlopeCraft_Qt_lupdate_executable}) +include(cmake/add_compiler_path_to_prefix.cmake) - if(${SlopeCraft_update_ts_no_obsolete}) - set(SlopeCraft_ts_flags) - else() - set(SlopeCraft_ts_flags "-no-obsolete") - endif() -endif() +file(GLOB required_dep_cmake_files "${CMAKE_SOURCE_DIR}/cmake/required_deps/*.cmake") +foreach (file ${required_dep_cmake_files}) + include(${file}) +endforeach () -add_definitions("-D_USE_MATH_DEFINES") -add_definitions("-DSCL_FIND_GlobalEnums_BY_PATH") +# Add compile options -------------------------------------------------------- # if don't vectorize, or no suitable flag found, this variable will be empty set(SlopeCraft_vectorize_flags) -if(${SlopeCraft_vectorize}) - include(CheckCXXCompilerFlag) - - add_definitions("-DSC_VECTORIZE_AVX2") - - if(${MSVC}) - set(SlopeCraft_vectorize_flags "/arch:AVX2") - else() - set(SlopeCraft_vectorize_flags -mavx -mavx2 -mfma) - endif() - - message(STATUS "Vectorize using " ${SlopeCraft_vectorize_flags}) +if (NOT ${SlopeCraft_vectorize}) + if (NOT ${SlopeCraft_GPU_API} STREQUAL "None") + message(STATUS "You enabled GPU boosting, but disabled vectorization") + endif () +endif () - # add_compile_options(${SlopeCraft_vectorize_flags}) +if (${SlopeCraft_vectorize}) + include(cmake/select_vectorize_flag.cmake) +endif () - # list(APPEND SlopeCraft_vectorize_flags "-O3") -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - message(STATUS "setting runpath for linux") - set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Windows") - add_compile_definitions("_CRT_SECURE_NO_WARNINGS") -endif() +if (${WIN32}) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) +endif () +# Add global compile options ------------------------------------------------- # profile with gprof -if(${SlopeCraft_gprof}) +if (${SlopeCraft_gprof}) add_compile_options("-pg") add_link_options("-pg") -endif() +endif () + +if (${SlopeCraft_sanitize}) + set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fsanitize=address") +endif () +add_compile_definitions(_USE_MATH_DEFINES) +add_compile_definitions(SCL_FIND_GlobalEnums_BY_PATH) add_compile_definitions(SLOPECRAFTL_NOT_INSTALLED) add_compile_definitions(VISUALCRAFTL_NOT_INSTALLED) + +# Add compile options for different compilers +if (${MSVC}) + add_compile_options("/Zc:__cplusplus") + add_compile_options("/wd4819") + add_compile_options("/utf-8") + add_compile_options("/EHsc") +else () + add_compile_options(-Wall -Wreturn-type -Wsign-compare -Wextra) + add_link_options(-flto) +endif () + +# Add global include dirs ---------------------------------------------------- include_directories(${CMAKE_BINARY_DIR}/utilities) -set(SlopeCraft_resource_file_to_touch_and_remove) -mark_as_advanced(SlopeCraft_resource_file_to_touch_and_remove) +# Add global custom targets on windows --------------------------------------- +if (${WIN32}) + add_custom_target(SC_create_all_symlinks + COMMENT "A interface target to create all symlinks on windows") + + add_custom_target(SC_deploy_all + COMMENT "A interface target to run windeployqt for all targets") +endif () + +# # Language related variables +# set(SC_lupdate_flags -no-ui-lines) +set(SC_lupdate_flags) +if (${SlopeCraft_update_ts_no_obsolete}) + list(APPEND SC_lupdate_flags "-no-obsolete") +endif () + +# Sub dirss ------------------------------------------------------------------ add_subdirectory(utilities) add_subdirectory(imageCutter) add_subdirectory(SlopeCraftL) -add_subdirectory(SlopeCraftMain) + +# add_subdirectory(SlopeCraftMain) +add_subdirectory(SlopeCraft) add_subdirectory(MapViewer) add_subdirectory(tests) add_subdirectory(VisualCraftL) add_subdirectory(VisualCraft) add_subdirectory(vccl) -if(EXISTS ${CMAKE_BINARY_DIR}/utilities/ColorManip/__rsc_ColorManip_cl_rc.c) - file(SIZE ${CMAKE_BINARY_DIR}/utilities/ColorManip/__rsc_ColorManip_cl_rc.c size_of_file) - - if(${size_of_file} LESS_EQUAL 0) - # file(REMOVE ${CMAKE_BINARY_DIR}/utilities/ColorManip/__rsc_ColorManip_cl_rc.c) - endif() -endif() - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(SlopeCraft_readme_and_license_files - - LICENSE - README.md - README-en.md - license-translations/LICENSE-zh.md -) - -install(FILES ${SlopeCraft_readme_and_license_files} - DESTINATION ${CMAKE_INSTALL_PREFIX}) +# install and pack ----------------------------------------------------------- +include(cmake/install.cmake) +include(cpack/make-packs.cmake) diff --git a/MapViewer/CMakeLists.txt b/MapViewer/CMakeLists.txt index f01401ca..b454e108 100644 --- a/MapViewer/CMakeLists.txt +++ b/MapViewer/CMakeLists.txt @@ -1,9 +1,7 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.20) project(SlopeCraft_MapViewer VERSION ${SlopeCraft_version}) set(CMAKE_CXX_STANDARD 17) -include_directories(${SlopeCraft_Eigen3_include_dir}) - set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) @@ -12,7 +10,7 @@ set(CMAKE_AUTORCC ON) find_package(ZLIB 1.2.11 REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets LinguistTools REQUIRED) +find_package(Qt6 COMPONENTS Widgets LinguistTools REQUIRED) set(MapViewer_header_files @@ -23,7 +21,6 @@ set(MapViewer_header_files set(MapViewer_source_files main.cpp - ../SlopeCraftL/ColorSource.cpp processMapFiles.cpp MapViewerWind.cpp resource_manually.cpp @@ -31,27 +28,21 @@ set(MapViewer_source_files set(MapViewer_windows_rc_files) -if(CMAKE_SYSTEM_NAME STREQUAL "Windows") +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") message("Configuring on Windows. Adding rc file to " ${PROJECT_NAME}) configure_file(others/MapViewer.rc.in others/MapViewer.rc) set(MapViewer_windows_rc_files ${CMAKE_CURRENT_BINARY_DIR}/others/MapViewer.rc) -endif() +endif () set(MapViewer_ts_files MapViewer_en_US.ts) -if(${SlopeCraft_update_ts_files}) - execute_process( - COMMAND ${SlopeCraft_Qt_lupdate_executable} MapViewerWind.ui ${MapViewer_header_files} ${MapViewer_source_files} "-ts" ${MapViewer_ts_files} ${SlopeCraft_ts_flags} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) -endif() - set(MapViewer_project_sources ${MapViewer_header_files} ${MapViewer_source_files} MapViewerWind.ui - ${MapViewer_ts_files} + + # ${MapViewer_ts_files} ${MapViewer_windows_rc_files} ) @@ -61,12 +52,16 @@ qt_add_executable(MapViewer # qt_create_translation(MapViewer_qm_files ${CMAKE_SOURCE_DIR} ${MapViewer_ts_files}) find_package(OpenMP REQUIRED) +find_package(Eigen3 REQUIRED) target_link_libraries(MapViewer PRIVATE + SlopeCraftL ZLIB::ZLIB - Qt${QT_VERSION_MAJOR}::Widgets - OpenMP::OpenMP_CXX) + Qt6::Widgets + OpenMP::OpenMP_CXX + Eigen3::Eigen) +target_compile_features(MapViewer PRIVATE cxx_std_23) set_target_properties(MapViewer PROPERTIES VERSION ${PROJECT_VERSION} @@ -78,6 +73,12 @@ set_target_properties(MapViewer PROPERTIES WIN32_EXECUTABLE TRUE ) +qt_add_lupdate(MapViewer + TS_FILES ${MapViewer_ts_files} + SOURCES ${MapViewer_project_sources} + OPTIONS ${SC_lupdate_flags} +) + # translation qt_add_lrelease(MapViewer TS_FILES ${MapViewer_ts_files} QM_FILES_OUTPUT_VARIABLE MapViewer_qm_files) diff --git a/MapViewer/MapViewerWind.cpp b/MapViewer/MapViewerWind.cpp index dfed0772..a68bc704 100644 --- a/MapViewer/MapViewerWind.cpp +++ b/MapViewer/MapViewerWind.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,8 +20,6 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "MapViewerWind.h" - #include #include #include @@ -31,6 +29,9 @@ This file is part of SlopeCraft. #include #include +#include "SlopeCraftL.h" + +#include "MapViewerWind.h" #include "processMapFiles.h" #include "ui_MapViewerWind.h" @@ -52,7 +53,7 @@ std::array make_map_LUT() { result.fill(0x7FFF0000); const Eigen::Map> src( - SlopeCraft::RGBBasicSource); + SlopeCraft::SCL_get_rgb_basic_colorset_source()); for (int row_idx = 0; row_idx < 256; row_idx++) { ARGB a, r, g, b; @@ -102,7 +103,7 @@ std::array make_inverse_map_LUT(const std::array &src) { const ARGB argb = src[idx]; ARGB r = (argb >> 16) & 0xFF; ARGB g = (argb >> 8) & 0xFF; - ARGB b = (argb)&0xFF; + ARGB b = (argb) & 0xFF; double x, y, z, _r, _g, _b; RGB2XYZ(r / 255.0, g / 255.0, g / 255.0, &x, &y, &z); @@ -186,8 +187,7 @@ MapViewerWind::MapViewerWind(QWidget *parent) MapViewerWind::~MapViewerWind() { for (QLabel *label : this->labels) { - if (label != nullptr) - delete label; + if (label != nullptr) delete label; } delete ui; } @@ -219,15 +219,14 @@ void MapViewerWind::reshape_tables() { for (int idx = 0; idx < int(this->maps.size()); idx++) { const int r = (is_col_major) ? (idx % rows) : (idx / cols); const int c = (is_col_major) ? (idx / rows) : (idx % cols); - if (r >= rows || c >= cols) - continue; + if (r >= rows || c >= cols) continue; QTableWidgetItem *item = new QTableWidgetItem(this->maps[idx].filename); ui->table_display_filename->setItem(r, c, item); } - for (QLabel *label : this->labels) { // detach label and layout + for (QLabel *label : this->labels) { // detach label and layout if (ui->grid_layout_compose_maps->indexOf(label) >= 0) { ui->grid_layout_compose_maps->removeWidget(label); } @@ -243,8 +242,7 @@ void MapViewerWind::reshape_tables() { for (int idx = 0; idx < int(this->maps.size()); idx++) { const int r = (is_col_major) ? (idx % rows) : (idx / cols); const int c = (is_col_major) ? (idx / rows) : (idx % cols); - if (r >= rows || c >= cols) - continue; + if (r >= rows || c >= cols) continue; ui->grid_layout_compose_maps->addWidget(this->labels[idx], r, c); } } @@ -312,8 +310,8 @@ void MapViewerWind::render_single_image() { std::list> error_list; - if (is_color_only_image_changed) { // if color only image is changed, repaint - // it + if (is_color_only_image_changed) { // if color only image is changed, repaint + // it new_image = QImage(cols, rows, QImage::Format_ARGB32); new_image.fill(QColor(255, 255, 255, 255)); @@ -333,7 +331,7 @@ void MapViewerWind::render_single_image() { .fill(image_map(r, c)); const int basecolor = this->maps[current_idx].content()(r, c) / 4; - if (basecolor > ::current_max_base_color) { // unlikely + if (basecolor > ::current_max_base_color) { // unlikely map_new_image.block(pixel_size * r, pixel_size * c, pixel_size, pixel_size) = map_unknown; error_list.emplace_back(std::make_pair(r, c)); @@ -385,12 +383,9 @@ void MapViewerWind::render_single_image() { // determine the value of draw_type single_map_draw_type draw_type = color_only; { - if (ui->radio_show_base_color->isChecked()) - (draw_type) = base_color; - if (ui->radio_show_map_color->isChecked()) - (draw_type) = map_color; - if (ui->radio_show_shade->isChecked()) - (draw_type) = shade; + if (ui->radio_show_base_color->isChecked()) (draw_type) = base_color; + if (ui->radio_show_map_color->isChecked()) (draw_type) = map_color; + if (ui->radio_show_shade->isChecked()) (draw_type) = shade; } // cout<<"draw type = "< displayed_numbers(new u8Array128RowMajor); switch (draw_type) { - case color_only: - abort(); - break; - case map_color: - //*displayed_numbers=this->maps[current_idx].content(); - memcpy(displayed_numbers->data(), this->maps[current_idx].content().data(), - this->maps[current_idx].content().size()); - break; - case base_color: - *displayed_numbers = (this->maps[current_idx].content()) / 4; - break; - case shade: - memcpy(displayed_numbers->data(), this->maps[current_idx].content().data(), - this->maps[current_idx].content().size()); - - constexpr uint64_t mask = - 0b0000001100000011000000110000001100000011000000110000001100000011; - - for (uint32_t idx = 0; - idx < displayed_numbers->size() * sizeof(uint8_t) / sizeof(uint64_t); - idx++) { - uint64_t &val = - *(idx + reinterpret_cast(displayed_numbers->data())); - val = val & mask; - } - /* - for(int64_t idx=0;idxsize();idx++) { - displayed_numbers->operator()(idx) - =displayed_numbers->operator()(idx)%4; - } - */ - break; + case color_only: + abort(); + break; + case map_color: + //*displayed_numbers=this->maps[current_idx].content(); + memcpy(displayed_numbers->data(), + this->maps[current_idx].content().data(), + this->maps[current_idx].content().size()); + break; + case base_color: + *displayed_numbers = (this->maps[current_idx].content()) / 4; + break; + case shade: + memcpy(displayed_numbers->data(), + this->maps[current_idx].content().data(), + this->maps[current_idx].content().size()); + + constexpr uint64_t mask = + 0b0000001100000011000000110000001100000011000000110000001100000011; + + for (uint32_t idx = 0; + idx < displayed_numbers->size() * sizeof(uint8_t) / sizeof(uint64_t); + idx++) { + uint64_t &val = + *(idx + reinterpret_cast(displayed_numbers->data())); + val = val & mask; + } + /* + for(int64_t idx=0;idxsize();idx++) { + displayed_numbers->operator()(idx) + =displayed_numbers->operator()(idx)%4; + } + */ + break; } QPixmap temp_image = QPixmap::fromImage(new_image); @@ -480,19 +477,19 @@ void MapViewerWind::render_single_image() { uint8_t cur_number = (*displayed_numbers)(r, c); switch (draw_type) { - case map_color: - text[0] = char((cur_number / 100) + '0'); - cur_number -= (cur_number / 100) * 100; - text[1] = char((cur_number / 10) + '0'); - text[2] = char((cur_number % 10) + '0'); - break; - case base_color: - text[0] = char(cur_number / 10 + '0'); - text[1] = char(cur_number % 10 + '0'); - break; - default: - text[0] = char((cur_number & 0b11) + '0'); - break; + case map_color: + text[0] = char((cur_number / 100) + '0'); + cur_number -= (cur_number / 100) * 100; + text[1] = char((cur_number / 10) + '0'); + text[2] = char((cur_number % 10) + '0'); + break; + case base_color: + text[0] = char(cur_number / 10 + '0'); + text[1] = char(cur_number % 10 + '0'); + break; + default: + text[0] = char((cur_number & 0b11) + '0'); + break; } painter.drawText(rect, Qt::AlignVCenter | Qt::AlignHCenter, text); @@ -543,7 +540,7 @@ void MapViewerWind::on_button_load_maps_clicked() { std::list> error_list; std::mutex lock; -#pragma omp parallel for +#pragma omp parallel for schedule(static) for (int idx = 0; idx < filenames.size(); idx++) { std::string error_info; if (!process_map_file(filenames[idx].toLocal8Bit().data(), @@ -591,8 +588,9 @@ void MapViewerWind::on_button_load_maps_clicked() { } } -#pragma omp parallel for - for (map &map : this->maps) { +#pragma omp parallel for schedule(static) + for (int idx = 0; idx < (int)this->maps.size(); idx++) { + auto &map = this->maps[idx]; map.image = QImage(128, 128, QImage::Format::Format_ARGB32); Eigen::Map> image_map( reinterpret_cast(map.image.scanLine(0))); @@ -601,7 +599,7 @@ void MapViewerWind::on_button_load_maps_clicked() { } } - update_contents(); + this->update_contents(); } void MapViewerWind::on_checkbox_composed_show_spacing_toggled(bool is_checked) { @@ -611,8 +609,7 @@ void MapViewerWind::on_checkbox_composed_show_spacing_toggled(bool is_checked) { } void MapViewerWind::on_button_save_single_clicked() { - if (this->maps.size() <= 0) - return; + if (this->maps.size() <= 0) return; if (ui->label_show_single_map->pixmap().isNull()) { return; @@ -631,13 +628,11 @@ void MapViewerWind::on_button_save_single_clicked() { } void MapViewerWind::on_button_save_composed_clicked() { - if (this->maps.size() <= 0) - return; + if (this->maps.size() <= 0) return; const QString dest = QFileDialog::getSaveFileName(this, tr("保存为图片"), "", "*.png;;*.jpg;;*.gif"); - if (dest.isEmpty()) - return; + if (dest.isEmpty()) return; const int map_rows = ui->spinbox_rows->value(); const int map_cols = ui->spinbox_cols->value(); diff --git a/MapViewer/MapViewerWind.h b/MapViewer/MapViewerWind.h index 415405b3..b816913a 100644 --- a/MapViewer/MapViewerWind.h +++ b/MapViewer/MapViewerWind.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,18 +24,13 @@ This file is part of SlopeCraft. #define MAPVIEWERWIND_H #include - +#include #include -#include - #include - +#include #include - #include -#include - using std::cout, std::endl; QT_BEGIN_NAMESPACE @@ -44,17 +39,13 @@ class MapViewerWind; } QT_END_NAMESPACE -namespace SlopeCraft { -extern const float RGBBasicSource[256 * 3]; -} - using ARGB = uint32_t; using u8Array128RowMajor = Eigen::Array; using u32Array128RowMajor = Eigen::Array; extern const std::array map_color_to_ARGB; -enum single_map_draw_type { // the value of draw_type is the digits required +enum single_map_draw_type { // the value of draw_type is the digits required color_only = 0, map_color = 3, base_color = 2, @@ -62,7 +53,7 @@ enum single_map_draw_type { // the value of draw_type is the digits required }; struct map { -public: + public: map() : map_content(new u8Array128RowMajor){}; ~map() = default; map(map &&) = default; @@ -90,18 +81,18 @@ struct map { class MapViewerWind : public QMainWindow { Q_OBJECT -public: + public: MapViewerWind(QWidget *parent = nullptr); ~MapViewerWind(); -private: + private: Ui::MapViewerWind *ui; std::vector maps; std::vector labels; -private: -private slots: + private: + private slots: void update_contents(); void reshape_tables(); void render_single_image(); @@ -113,4 +104,4 @@ private slots: void on_button_save_composed_clicked(); }; -#endif // MAPVIEWERWIND_H +#endif // MAPVIEWERWIND_H diff --git a/MapViewer/MapViewer_en_US.ts b/MapViewer/MapViewer_en_US.ts index e522e943..d81dbb7e 100644 --- a/MapViewer/MapViewer_en_US.ts +++ b/MapViewer/MapViewer_en_US.ts @@ -41,7 +41,7 @@ - + 请选择地图文件 Please select files @@ -97,8 +97,8 @@ - - + + 保存为图片 Save @@ -113,38 +113,38 @@ Map count: - + There is/are - + 个像素点出现未知的基色,它们使用了Mojang未定义的基色。这可能是因为软件版本较旧而游戏版本太新,或者地图文件损坏。 其行、列坐标分别为: pixel(s) which have unknown base color, they are using basecolors out of Mojang's defination. This may be caused by damaged map data file, or the software version is too old. - + 地图中有Mojang未定义基色 Undefined basecolor in map data files (undefined by Mojang) - + 选择地图数据文件 Select map data files - + 加载地图文件失败 Failed to load map data file(s) - + 出错的文件: Map data file: - + 错误信息: diff --git a/MapViewer/install.cmake b/MapViewer/install.cmake index 940e0b87..91f56371 100644 --- a/MapViewer/install.cmake +++ b/MapViewer/install.cmake @@ -1,45 +1,70 @@ set(AppName MapViewer) -configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) +#configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in +# ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake +# @ONLY) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") install(TARGETS MapViewer - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX} + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . ) - # Run windeployqt at build time - add_custom_target(Windeployqt-MapViewer ALL - COMMAND ${SlopeCraft_Qt_windeployqt_executable} MapViewer.exe - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS MapViewer) - - # Run windeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) + QD_add_deployqt(MapViewer + BUILD_MODE + FLAGS ${SlopeCraft_windeployqt_flags_build}) + QD_add_deployqt(MapViewer + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_windeployqt_flags_install}) + DLLD_add_deploy(MapViewer + BUILD_MODE + INSTALL_MODE INSTALL_DESTINATION .) return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") # set_target_properties(MapViewer PROPERTIES INSTALL_RPATH "../lib") install(TARGETS MapViewer + EXPORT SlopeCraftTargets RUNTIME DESTINATION bin BUNDLE DESTINATION lib ) + + install(FILES others/MapViewer_64.png + DESTINATION share/pixmaps + RENAME com.github.SlopeCraft.MapViewer.png) + install(FILES others/MapViewer.desktop + DESTINATION share/applications) + + # Install platforms and imageformats plugins + include(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake) return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") install(TARGETS MapViewer - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX} + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . + BUNDLE DESTINATION . ) # Install icon for macOS # MapViewer.app file(GLOB MapViewer_Icon ${CMAKE_SOURCE_DIR}/MapViewer/others/MapViewer.icns) install(FILES ${MapViewer_Icon} - DESTINATION ${CMAKE_INSTALL_PREFIX}/MapViewer.app/Contents/Resources) + DESTINATION MapViewer.app/Contents/Resources) + + QD_add_deployqt(MapViewer + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_macdeployqt_flags_install}) + + DylibD_add_deploy(MapViewer + INSTALL_DESTINATION . + RPATH_POLICY REPLACE) + RCS_add_codesign(MapViewer + INSTALL_DESTINATION .) + return() -endif() +endif () message(WARNING "No rule to install MapViewer, because the system is not Windows, linux or MacOS.") \ No newline at end of file diff --git a/MapViewer/main.cpp b/MapViewer/main.cpp index b4f19e75..b2c0b632 100644 --- a/MapViewer/main.cpp +++ b/MapViewer/main.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -30,7 +30,6 @@ This file is part of SlopeCraft. #include int main(int argc, char *argv[]) { - QApplication a(argc, argv); QTranslator translator; diff --git a/MapViewer/others/MapViewer.desktop b/MapViewer/others/MapViewer.desktop new file mode 100644 index 00000000..45bc8c27 --- /dev/null +++ b/MapViewer/others/MapViewer.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=MapViewer +Comment=Minecraft map browser +Exec=MapViewer +Icon=com.github.SlopeCraft.MapViewer.png +Type=Application +Keywords=SlopeCraft; \ No newline at end of file diff --git a/MapViewer/processMapFiles.cpp b/MapViewer/processMapFiles.cpp index 8c0dcf59..8a0108dc 100644 --- a/MapViewer/processMapFiles.cpp +++ b/MapViewer/processMapFiles.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -51,8 +51,9 @@ bool uncompress_map_file(const char *filename, std::vector *const dest, *error_info = "Failed to open map data file "; *error_info += filename; *error_info += ", the file may be invalid."; - *error_info += "\nThis error may also be caused by non-English " - "characters in the filename of path."; + *error_info += + "\nThis error may also be caused by non-English " + "characters in the filename of path."; } return false; } @@ -110,8 +111,9 @@ const uint8_t *find_color_begin(const std::vector &inflated, } if (error_info != nullptr) { - *error_info = "Failed to find map data array named color. The map data " - "file may be invalid."; + *error_info = + "Failed to find map data array named color. The map data " + "file may be invalid."; } // cout<<"failed to find feature"< *const dest, std::string *const error_info) { - if (filename == nullptr || strlen(filename) <= 0) { - if (error_info != nullptr) - *error_info = "Invalid input : filename"; + if (error_info != nullptr) *error_info = "Invalid input : filename"; return false; } if (dest == nullptr) { - if (error_info != nullptr) - *error_info = "Invalid input : dest"; + if (error_info != nullptr) *error_info = "Invalid input : dest"; return false; } diff --git a/MapViewer/processMapFiles.h b/MapViewer/processMapFiles.h index 2acf3faf..86fe3401 100644 --- a/MapViewer/processMapFiles.h +++ b/MapViewer/processMapFiles.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -38,4 +38,4 @@ bool process_map_file( Eigen::Array *const dest, std::string *const error_info); -#endif // PROCESSMAPFILES_H +#endif // PROCESSMAPFILES_H diff --git a/MapViewer/resource_manually.cpp b/MapViewer/resource_manually.cpp index 10745e98..a9ef67fa 100644 --- a/MapViewer/resource_manually.cpp +++ b/MapViewer/resource_manually.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,7 +23,6 @@ This file is part of SlopeCraft. #include #include - extern const uint8_t data_unknown_base_color[]; extern const size_t png_size; diff --git a/README-en.md b/README-en.md index 8f13c44f..e73d8cb0 100644 --- a/README-en.md +++ b/README-en.md @@ -1,13 +1,14 @@ [中文](README.md) | **English** -

SlopeCraft

+![SlopeCraft logo](docs/SlopeCraft_ba-style@nulla.top.png) +

Get your Minecraft pixel painting in multiple kinds of forms!

- + @@ -15,8 +16,8 @@
- - + +

@@ -24,27 +25,34 @@ SlopeCraft is created using Qt + Eigen + zlib for creating 3D pixel map arts within Minecraft. -The difference between this program and SpriteCraft is that this program focuses on pixel art on maps. Its purpose is to record the pixel art with the map, then display it in an item frame (the map here refers to the in-game item, and not the saving files). +The difference between this program and SpriteCraft is that this program focuses on pixel art on maps. Its purpose is to +record the pixel art with the map, then display it in an item frame (the map here refers to the in-game item, and not +the saving files). -The color modification module is targeted towards the map. The pixel art is meant to resemble the art in the "point of view" of the map - not of the player. +The color modification module is targeted towards the map. The pixel art is meant to resemble the art in the "point of +view" of the map - not of the player. -Since the color within the map is related to the relative height of the block, the pixel art created by SlopeCraft is usually in 3D, which I call 3D Pixel Art. +Since the color within the map is related to the relative height of the block, the pixel art created by SlopeCraft is +usually in 3D, which I call 3D Pixel Art. In summary, SlopeCraft is created for map pixel art. -This is why maps made by SlopeCraft have higher quality than exporting a picture from SpriteCraft then recording it with a map - because SlopeCraft is specifically designed for map pixel art. +This is why maps made by SlopeCraft have higher quality than exporting a picture from SpriteCraft then recording it with +a map - because SlopeCraft is specifically designed for map pixel art. ## ⚙️ Installation -1. Download the latest version of SlopeCraft from the [Release](https://github.com/SlopeCraft/SlopeCraft/releases/latest) page. +1. Download the latest version of SlopeCraft from + the [Release](https://github.com/SlopeCraft/SlopeCraft/releases/latest) page. 2. Run SlopeCraft - - Windows Users: Download `SlopeCraft-x.x.x-win.zip`, unzip and run `SlopeCraft.exe` - - macOS Users: Download `SlopeCraft-x.x.x-mac.zip`, unzip and drag `SlopeCraft.app` into the Applications folder and run `SlopeCraft` - - Linux Users: Download `SlopeCraft-x.x.x-linux.tar.xz`, unzip and run `SlopeCraft` + - Windows Users: Download `SlopeCraft-x.x.x-win.zip`, unzip and run `SlopeCraft.exe` + - macOS Users: Download `SlopeCraft-x.x.x-mac.zip`, unzip and drag `SlopeCraft.app` into the Applications folder and + run `SlopeCraft` + - Linux Users: Download `SlopeCraft-x.x.x-linux.tar.xz`, unzip and run `SlopeCraft` - ::: tips - The `x.x.x` mentioned above is the version number of SlopeCraft, for example, `5.0.0`. + ::: tips + The `x.x.x` mentioned above is the version number of SlopeCraft, for example, `5.0.0`. 3. Make sure you have read the [FAQ](https://slopecraft.readthedocs.io/en/faq/) and the tutorial before doing anything. @@ -65,13 +73,16 @@ This is why maps made by SlopeCraft have higher quality than exporting a picture ### Compilation Guide -- If you want to compile SlopeCraft yourself, you can refer to the [Compilation Guide](https://slopecraft.readthedocs.io/en/compilation-guide/) for operation. +- If you want to compile SlopeCraft yourself, you can refer to + the [Compilation Guide](https://slopecraft.readthedocs.io/en/compilation-guide/) for operation. ## 🛠️ Other Related Repositories * [NBTWriter](https://github.com/ToKiNoBug/NBTWriter-of-Toki) - Lib for writing NBT files. * [SlopeCraftTutorial](https://github.com/ToKiNoBug/SlopeCraftTutorial) - Tutorials -* [SlopeCraftCompressLib](https://github.com/ToKiNoBug/SlopeCraftCompressLib) - Lib for building height map and lossless compression lib. -* [SlopeCraftLossyCompression](https://github.com/ToKiNoBug/SlopeCraftLossyCompression) - Lossy compression lib, based on SlopeCraftCompressLib. +* [SlopeCraftCompressLib](https://github.com/ToKiNoBug/SlopeCraftCompressLib) - Lib for building height map and lossless + compression lib. +* [SlopeCraftLossyCompression](https://github.com/ToKiNoBug/SlopeCraftLossyCompression) - Lossy compression lib, based + on SlopeCraftCompressLib. * [SlopeCraftGlassBuilder](https://github.com/ToKiNoBug/SlopeCraftGlassBuilder) - Glass bridge building lib. * [HeuristicFlow](https://github.com/TokiNoBug/HeuristicFlow) - GA implementation. diff --git a/README.md b/README.md index 9280233f..557a920d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ **中文** | [English](README-en.md) -

SlopeCraft

+![SlopeCraft logo](docs/SlopeCraft_ba-style@nulla.top.png)

生成多种样式的 Minecraft 地图画!

- + @@ -15,8 +15,8 @@
- - + +

@@ -39,12 +39,12 @@ SlopeCraft 是一款基于 Qt + Eigen + zlib 开发的,用于在 Minecraft 中 1. 从 [Release](https://github.com/SlopeCraft/SlopeCraft/releases/latest) 页面下载最新版本的 SlopeCraft。 2. 运行 SlopeCraft - - Windows 用户:下载 `SlopeCraft-x.x.x-win.zip`,解压后运行 `SlopeCraft.exe` - - macOS 用户:下载 `SlopeCraft-x.x.x-mac.zip`,解压后将 `SlopeCraft.app` 拖入应用程序文件夹并运行 `SlopeCraft` - - Linux 用户:下载 `SlopeCraft-x.x.x-linux.tar.xz`,解压后运行 `SlopeCraft` + - Windows 用户:下载 `SlopeCraft-x.x.x-win.zip`,解压后运行 `SlopeCraft.exe` + - macOS 用户:下载 `SlopeCraft-x.x.x-mac.zip`,解压后将 `SlopeCraft.app` 拖入应用程序文件夹并运行 `SlopeCraft` + - Linux 用户:下载 `SlopeCraft-x.x.x-linux.tar.xz`,解压后运行 `SlopeCraft` - ::: tips - 此处的 `x.x.x` 为 SlopeCraft 的版本号,例如 `5.0.0`。 + ::: tips + 此处的 `x.x.x` 为 SlopeCraft 的版本号,例如 `5.0.0`。 3. 在进行任何操作前,请确保你已经阅读了 [常见问题](https://slopecraft.readthedocs.io/faq/) 和使用教程。 diff --git a/SlopeCraft-install-macOS.zsh b/SlopeCraft-install-macOS.zsh new file mode 100755 index 00000000..3f21631b --- /dev/null +++ b/SlopeCraft-install-macOS.zsh @@ -0,0 +1,55 @@ +#!/usr/bin/env zsh + +if [[ $OSTYPE != darwin* ]]; then + echo "This script can ONLY be used on macOS!" + exit 1 +fi + +# Check if Homebrew is installed +which -s brew + +if [[ $? != 0 ]] ; then + # Install Homebrew + echo "Installing Homebrew" + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +else + echo "Updating Homebrew" + brew update +fi + +echo "Installing dependencies" +brew install git +brew install llvm + +if [[ $CPUTYPE == arm64 ]]; then + echo "Configuring LLVM Clang for Apple Silicon Macs" + alias clang=/opt/homebrew/opt/llvm/bin/clang + alias clang++=/opt/homebrew/opt/llvm/bin/clang++ +else + echo "Configuring LLVM Clang for Intel Macs" + alias clang=/usr/local/opt/llvm/bin/clang + alias clang++=/usr/local/opt/llvm/bin/clang++ +fi + +brew install cmake ninja +brew install qt +brew install zlib libpng libzip xsimd + +echo "Cloning git repo" +cd ~ +git clone https://github.com/SlopeCraft/SlopeCraft.git && cd SlopeCraft + +echo "Configuring CMake... This step might take a while." +cmake -S . -B ./build -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=./build/install -DCMAKE_BUILD_TYPE=Release + +echo "Building... This step might take a while." +cd build +cmake --build . --parallel + +echo "Installing and Deploying" +cmake --install . +cd install +macdeployqt *.app + +echo "Get your app files at $(pwd)!" + diff --git a/SlopeCraft/AdaptiveListView.cpp b/SlopeCraft/AdaptiveListView.cpp new file mode 100644 index 00000000..7021ac97 --- /dev/null +++ b/SlopeCraft/AdaptiveListView.cpp @@ -0,0 +1,20 @@ +#include "AdaptiveListView.h" + +inline QSize compute_size(QSize widget_size, double ratio = 0.8) noexcept { + const int new_w = widget_size.width() * ratio; + return {new_w, new_w}; +} + +AdaptiveListView::AdaptiveListView(QWidget* parent) : QListView(parent) { + this->setIconSize(compute_size(this->size())); + this->setDragEnabled(true); + this->setAcceptDrops(true); + this->setDropIndicatorShown(true); +} + +AdaptiveListView::~AdaptiveListView() {} + +void AdaptiveListView::resizeEvent(QResizeEvent* event) { + this->setIconSize(compute_size(event->size())); + QListView::resizeEvent(event); +} \ No newline at end of file diff --git a/SlopeCraft/AdaptiveListView.h b/SlopeCraft/AdaptiveListView.h new file mode 100644 index 00000000..818845fa --- /dev/null +++ b/SlopeCraft/AdaptiveListView.h @@ -0,0 +1,14 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_ADAPTIVELISTVIEW_H +#define SLOPECRAFT_SLOPECRAFT_ADAPTIVELISTVIEW_H +#include +#include + +class AdaptiveListView : public QListView { + public: + explicit AdaptiveListView(QWidget* parent = nullptr); + ~AdaptiveListView(); + + void resizeEvent(QResizeEvent* event) override; +}; + +#endif // SLOPECRAFT_SLOPECRAFT_ADAPTIVELISTVIEW_H diff --git a/SlopeCraft/AiCvterParameterDialog.cpp b/SlopeCraft/AiCvterParameterDialog.cpp new file mode 100644 index 00000000..660785fc --- /dev/null +++ b/SlopeCraft/AiCvterParameterDialog.cpp @@ -0,0 +1,66 @@ +/* + Copyright © 2021-2026 TokiNoBug +This file is part of SlopeCraft. + + SlopeCraft is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SlopeCraft is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SlopeCraft. If not, see . + + Contact with me: + github:https://github.com/SlopeCraft/SlopeCraft + bilibili:https://space.bilibili.com/351429231 +*/ + +#include "AiCvterParameterDialog.h" +#include "ui_AiCvterParameterDialog.h" + +#include "SCWind.h" + +using namespace SlopeCraft; +AiCvterParameterDialog::AiCvterParameterDialog(SCWind* parent) + : QDialog(parent), ui(new Ui::AiCvterParameterDialog) { + this->ui->setupUi(this); + + SlopeCraft::GA_converter_option opt = + dynamic_cast(this->parent())->GA_option; + opt.caller_api_version = SC_VERSION_U64; + + this->ui->sb_pop_size->setValue(opt.popSize); + this->ui->sb_max_gen->setValue(opt.maxGeneration); + this->ui->sb_max_early_stop->setValue(opt.maxFailTimes); + this->ui->dsb_crossover_prob->setValue(opt.crossoverProb); + this->ui->dsb_mutate_prob->setValue(opt.mutationProb); +} + +AiCvterParameterDialog::~AiCvterParameterDialog() { delete this->ui; } + +SlopeCraft::GA_converter_option AiCvterParameterDialog::current_option() + const noexcept { + SlopeCraft::GA_converter_option ret{ + .caller_api_version = SC_VERSION_U64, + .popSize = static_cast(this->ui->sb_pop_size->value()), + .maxGeneration = static_cast(this->ui->sb_max_gen->value()), + .maxFailTimes = static_cast(this->ui->sb_max_early_stop->value()), + .crossoverProb = this->ui->dsb_crossover_prob->value(), + .mutationProb = this->ui->dsb_mutate_prob->value(), + }; + return ret; +} + +void AiCvterParameterDialog::on_buttonBox_accepted() noexcept { + auto opt = this->current_option(); + dynamic_cast(this->parent())->GA_option = opt; +} + +void AiCvterParameterDialog::on_buttonBox_rejected() noexcept { + this->deleteLater(); +} diff --git a/SlopeCraftMain/AiCvterParameterDialog.h b/SlopeCraft/AiCvterParameterDialog.h similarity index 72% rename from SlopeCraftMain/AiCvterParameterDialog.h rename to SlopeCraft/AiCvterParameterDialog.h index 450c8686..ed594cf1 100644 --- a/SlopeCraftMain/AiCvterParameterDialog.h +++ b/SlopeCraft/AiCvterParameterDialog.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -25,30 +25,33 @@ This file is part of SlopeCraft. #include #include +#include + +class AiCvterParameterDialog; namespace Ui { class AiCvterParameterDialog; } -class MainWindow; +class SCWind; class AiCvterParameterDialog : public QDialog { Q_OBJECT -public: - explicit AiCvterParameterDialog(QWidget *parent); + public: + explicit AiCvterParameterDialog(SCWind *parent); ~AiCvterParameterDialog(); -protected: - void closeEvent(QCloseEvent *event) override; -private slots: - void updateMaxFailTimes(); - void on_buttonBox_accepted(); + SlopeCraft::GA_converter_option current_option() const noexcept; + + private slots: + // void updateMaxFailTimes(); + void on_buttonBox_accepted() noexcept; - void on_buttonBox_clicked(QAbstractButton *button); + void on_buttonBox_rejected() noexcept; -private: + private: Ui::AiCvterParameterDialog *ui; }; -#endif // AICVTERPARAMETERDIALOG_H +#endif // AICVTERPARAMETERDIALOG_H diff --git a/SlopeCraftMain/AiCvterParameterDialog.ui b/SlopeCraft/AiCvterParameterDialog.ui similarity index 87% rename from SlopeCraftMain/AiCvterParameterDialog.ui rename to SlopeCraft/AiCvterParameterDialog.ui index 78bc0e41..74e2665f 100644 --- a/SlopeCraftMain/AiCvterParameterDialog.ui +++ b/SlopeCraft/AiCvterParameterDialog.ui @@ -14,18 +14,8 @@ Ai转化器参数 - - - - 允许提前收敛 - - - true - - - - + 最大提前收敛代数: @@ -33,7 +23,7 @@ 10 - 100 + 3000 10 @@ -41,7 +31,7 @@ - + 交叉概率: @@ -60,7 +50,7 @@ - + 150 @@ -88,7 +78,7 @@ - + 150 @@ -113,7 +103,7 @@ - + 变异概率: diff --git a/SlopeCraft/BlockListDialog.cpp b/SlopeCraft/BlockListDialog.cpp new file mode 100644 index 00000000..87415872 --- /dev/null +++ b/SlopeCraft/BlockListDialog.cpp @@ -0,0 +1,322 @@ +// +// Created by Joseph on 2024/4/9. +// + +#include +#include +#include +#include "BlockListDialog.h" +#include "ui_BlockListDialog.h" +#include "SCWind.h" + +int BLD_block_list_provider::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) { + return 0; + } + return this->available_block_lists().size(); +} + +QVariant BLD_block_list_provider::data(const QModelIndex &index, + int role) const { + if (not index.isValid()) { + return {}; + } + if (role not_eq Qt::ItemDataRole::DisplayRole) { + return {}; + } + const auto block_lists = this->available_block_lists(); + const int idx = index.row(); + + if (idx >= block_lists.size() or idx < 0) { + return {}; + } + if (block_lists[idx].second == nullptr) { + return tr("SlopeCraft 内部错误,方块列表的列表中出现 nullptr"); + } + return block_lists[idx].first; +} + +std::vector +BLD_block_provider::available_blocks() const noexcept { + auto bl = this->available_block_list(); + if (bl == nullptr) { + return {}; + } + const size_t num = bl->size(); + std::vector ret; + ret.resize(num); + [[maybe_unused]] const size_t num_ = + bl->get_blocks(ret.data(), nullptr, ret.size()); + assert(num == num_); + return ret; +} + +int BLD_block_provider::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) { + return 0; + } + auto bl = this->available_block_list(); + if (bl == nullptr) { + return 0; + } + return bl->size(); +} +QVariant BLD_block_provider::data(const QModelIndex &index, int role) const { + if (not index.isValid()) { + return {}; + } + if (role not_eq Qt::ItemDataRole::DisplayRole) { + return {}; + } + const auto blocks = this->available_blocks(); + const int idx = index.row(); + + if (idx >= blocks.size() or idx < 0) { + return {}; + } + if (blocks[idx] == nullptr) { + return tr("SlopeCraft 内部错误,方块列表中出现 nullptr"); + } + if (this->current_lang() == SCL_language::Chinese) { + return QString::fromUtf8(blocks[idx]->getNameZH()); + } + return QString::fromUtf8(blocks[idx]->getNameEN()); +} + +int BLD_block_info_provider::rowCount(const QModelIndex &qmi) const { + if (qmi.isValid()) { + return 0; + } + return 6; +} +int BLD_block_info_provider::columnCount(const QModelIndex &qmi) const { + if (qmi.isValid()) { + return 0; + } + return 2; +} + +QString BLD_block_info_provider::key_name(int index) noexcept { + const std::array keys{tr("最低版本"), tr("依附方块"), + tr("发光"), tr("末影人可搬走"), + tr("可燃"), tr("一组数量")}; + if (index < 0 or index >= keys.size()) { + return {}; + } + return keys[index]; +} +/* + * 0 -> version + * 1 -> need glass + * 2 -> do glow + * 3 -> enderman pickable + * 4 -> burnable + * 5 -> stack size + * */ +QVariant BLD_block_info_provider::value_of_attribute( + const SlopeCraft::mc_block_interface &blk, int index) noexcept { + auto bool_to_str = [](bool val) { + if (val) + return "Yes"; + else + return "No"; + }; + switch (index) { + case 0: { // version + const auto ver = blk.getVersion(); + if (ver < uint8_t(SCL_gameVersion::MC12)) { + return tr("远古版本"); + } + if (ver > (uint8_t)SlopeCraft::SCL_maxAvailableVersion()) { + return tr("未来版本"); + } + return QStringLiteral("1.%1").arg(int(ver)); + } + case 1: // need glass + return bool_to_str(blk.getNeedGlass()); + case 2: + return bool_to_str(blk.getDoGlow()); + case 3: + return bool_to_str(blk.getEndermanPickable()); + case 4: + return bool_to_str(blk.getBurnable()); + case 5: + return blk.getStackSize(); + } + return {}; +} + +QVariant BLD_block_info_provider::data(const QModelIndex &qmi, + int role) const noexcept { + if (not qmi.isValid()) { + return {}; + } + if (role not_eq Qt::ItemDataRole::DisplayRole) { + return {}; + } + const auto current_block = this->selected_block(); + switch (qmi.column()) { + case 0: + return key_name(qmi.row()); + case 1: { + if (current_block == nullptr) { + return {}; + } + return value_of_attribute(*current_block, qmi.row()); + } + } + return {}; +} + +BlockListDialog::BlockListDialog(SCWind *parent, BlockListManager *blm) + : QDialog{parent}, ui{new Ui::BlockListDialog}, block_list_manager{blm} { + this->ui->setupUi(this); + + { + auto get_block_lists = [blm]() + -> std::vector< + std::pair> { + return blm->get_block_lists(); + }; + this->block_list_provider.reset( + new BLD_block_list_provider{this, get_block_lists}); + this->ui->lv_block_lists->setModel(this->block_list_provider.get()); + } + { + auto get_selected_block_list = + [this]() -> const SlopeCraft::block_list_interface * { + const auto qmi = this->ui->lv_block_lists->currentIndex(); + if (not qmi.isValid()) { + return nullptr; + } + const int idx = qmi.row(); + const auto available_lists = + this->block_list_provider->available_block_lists(); + if (idx < 0 or idx >= available_lists.size()) { + return nullptr; + } + return available_lists[idx].second; + }; + auto get_lang = [parent]() -> SCL_language { return parent->lang(); }; + this->block_provider.reset( + new BLD_block_provider{this, get_selected_block_list, get_lang}); + this->ui->lv_blocks->setModel(this->block_provider.get()); + } + { + auto get_selected_block = + [this]() -> const SlopeCraft::mc_block_interface * { + const auto qmi = this->ui->lv_blocks->currentIndex(); + if (not qmi.isValid()) { + return nullptr; + } + const int idx = qmi.row(); + const auto available_blocks = this->block_provider->available_blocks(); + if (idx < 0 or idx >= available_blocks.size()) { + return nullptr; + } + return available_blocks[idx]; + }; + this->block_info_provider.reset( + new BLD_block_info_provider{this, get_selected_block}); + this->ui->tv_block_props->setModel(this->block_info_provider.get()); + } + + connect(this->ui->lv_block_lists->selectionModel(), + &QItemSelectionModel::selectionChanged, [this]() { + this->block_provider->dataChanged({}, {}, + {Qt::ItemDataRole::DisplayRole}); + }); + connect(this->ui->lv_blocks->selectionModel(), + &QItemSelectionModel::selectionChanged, [this]() { + this->block_info_provider->dataChanged( + {}, {}, {Qt::ItemDataRole::DisplayRole}); + + auto blk = this->block_info_provider->selected_block(); + this->update_info(blk); + }); + + connect(this->ui->pb_ok, &QPushButton::clicked, this, &QDialog::accept); +} + +BlockListDialog::~BlockListDialog() { + // delete this->ui; +} + +void BlockListDialog::update_info( + const SlopeCraft::mc_block_interface *blk) noexcept { + if (blk == nullptr) { + this->ui->lb_image->setPixmap({}); + this->ui->le_id->clear(); + this->ui->le_id_old->clear(); + this->ui->le_name_cn->clear(); + this->ui->le_name_en->clear(); + return; + } + { + QImage img{blk->imageCols(), blk->imageRows(), QImage::Format_ARGB32}; + blk->getImage(reinterpret_cast(img.scanLine(0))); + img = img.scaledToHeight(64); + this->ui->lb_image->setPixmap(QPixmap::fromImage(img)); + } + this->ui->le_id->setText(blk->getId()); + this->ui->le_id_old->setText(blk->getIdOld()); + this->ui->le_name_cn->setText(QString::fromUtf8(blk->getNameZH())); + this->ui->le_name_en->setText(QString::fromUtf8(blk->getNameEN())); +} + +void BlockListDialog::on_pb_add_block_list_clicked() noexcept { + const auto files = QFileDialog::getOpenFileNames( + this, tr("选择方块列表"), + QStringLiteral("%1/Blocks").arg(QCoreApplication::applicationDirPath()), + "*.zip"); + if (files.empty()) { + return; + } + + for (auto &file : files) { + this->block_list_manager->add_blocklist(file); + } + this->block_list_manager->finish_blocklist(); + this->block_list_provider->dataChanged({}, {}); +} + +void BlockListDialog::on_pb_remove_block_list_clicked() noexcept { + const auto selected_indices = + this->ui->lv_block_lists->selectionModel()->selectedIndexes(); + if (selected_indices.empty()) { + return; + } + std::vector names; + for (auto &qmi : selected_indices) { + if (not qmi.isValid()) { + continue; + } + names.emplace_back( + this->block_list_provider->available_block_lists()[qmi.row()].first); + } + + int num_lists = 0; + size_t remove_counter = 0; + for (auto &name : names) { + if (name == "FixedBlocks.zip") { + QMessageBox::warning(this, tr("不能删除基础方块列表"), + tr("FixedBlocks.zip 是基础方块列表,不允许移除。")); + continue; + } + auto res = this->block_list_manager->remove_blocklist(name); + if (not res) { + QMessageBox::warning(this, tr("删除方块列表 %1 失败").arg(name), + res.error()); + } else { + remove_counter += res.value(); + num_lists++; + } + } + if (num_lists > 0) { + QMessageBox::information(this, tr("删除方块列表成功"), + tr("删除了 %1 个方块列表,移除了 %2 个方块") + .arg(num_lists) + .arg(remove_counter)); + } + this->block_list_provider->dataChanged({}, {}); +} \ No newline at end of file diff --git a/SlopeCraft/BlockListDialog.h b/SlopeCraft/BlockListDialog.h new file mode 100644 index 00000000..1c3beca3 --- /dev/null +++ b/SlopeCraft/BlockListDialog.h @@ -0,0 +1,110 @@ +// +// Created by Joseph on 2024/4/9. +// + +#ifndef SLOPECRAFT_BLOCKLISTDIALOG_H +#define SLOPECRAFT_BLOCKLISTDIALOG_H + +#include +#include +#include + +#include +#include + +#include "BlockListManager.h" + +class BlockListDialog; + +namespace Ui { +class BlockListDialog; +} + +class BLD_block_list_provider : public QAbstractListModel { + Q_OBJECT + public: + const std::function>()> + available_block_lists; + + public: + explicit BLD_block_list_provider( + QWidget* parent, + std::function>()> + cb) + : QAbstractListModel{parent}, available_block_lists{std::move(cb)} {} + BLD_block_list_provider(const BLD_block_list_provider&) = delete; + + int rowCount(const QModelIndex& parent = QModelIndex()) const final; + + QVariant data(const QModelIndex& index, + int role = Qt::DisplayRole) const final; +}; +class BLD_block_provider : public QAbstractListModel { + Q_OBJECT + private: + const std::function + available_block_list; + const std::function current_lang; + + public: + explicit BLD_block_provider( + QWidget* parent, + std::function&& cb, + std::function&& lang_cb) + : QAbstractListModel{parent}, + available_block_list{std::move(cb)}, + current_lang{std::move(lang_cb)} {} + + std::vector available_blocks() + const noexcept; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; +}; + +class BLD_block_info_provider : public QAbstractTableModel { + Q_OBJECT + public: + const std::function selected_block; + + public: + explicit BLD_block_info_provider( + QWidget* parent, + std::function&& cb) + : QAbstractTableModel{parent}, selected_block{std::move(cb)} {} + + int rowCount(const QModelIndex& qmi) const final; + + int columnCount(const QModelIndex& qmi) const final; + + static QString key_name(int index) noexcept; + static QVariant value_of_attribute(const SlopeCraft::mc_block_interface& blk, + int index) noexcept; + + QVariant data(const QModelIndex& qmi, int role) const noexcept final; +}; + +class SCWind; +class BlockListDialog : public QDialog { + Q_OBJECT + private: + std::unique_ptr ui; + std::unique_ptr block_list_provider{nullptr}; + std::unique_ptr block_provider{nullptr}; + std::unique_ptr block_info_provider{nullptr}; + + BlockListManager* const block_list_manager; + + void update_info(const SlopeCraft::mc_block_interface*) noexcept; + + public: + explicit BlockListDialog(SCWind* parent, BlockListManager* blm); + ~BlockListDialog(); + + private slots: + void on_pb_add_block_list_clicked() noexcept; + void on_pb_remove_block_list_clicked() noexcept; +}; + +#endif // SLOPECRAFT_BLOCKLISTDIALOG_H diff --git a/SlopeCraft/BlockListDialog.ui b/SlopeCraft/BlockListDialog.ui new file mode 100644 index 00000000..31aa5107 --- /dev/null +++ b/SlopeCraft/BlockListDialog.ui @@ -0,0 +1,167 @@ + + + BlockListDialog + + + + 0 + 0 + 820 + 481 + + + + 方块列表 + + + false + + + false + + + + + + 0 + + + + + 添加方块列表 + + + + + + + 删除方块列表 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 确定 + + + + + + + + + + 0 + 0 + + + + + + + + 0 + + + + + + 0 + 0 + + + + false + + + QFrame::Shape::StyledPanel + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + true + + + 方块 id + + + + + + + true + + + 1.12 id + + + + + + + true + + + 中文名 + + + + + + + true + + + 英文名 + + + + + + + true + + + false + + + + + + + + + + 0 + 0 + + + + + + + + + diff --git a/SlopeCraft/CMakeLists.txt b/SlopeCraft/CMakeLists.txt new file mode 100644 index 00000000..ab7a15c6 --- /dev/null +++ b/SlopeCraft/CMakeLists.txt @@ -0,0 +1,139 @@ +cmake_minimum_required(VERSION 3.20) +project(SlopeCraft_NewGUI VERSION ${SlopeCraft_version} LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(Qt6 COMPONENTS Widgets LinguistTools Network REQUIRED) +find_package(magic_enum REQUIRED) +find_package(tl-expected REQUIRED) + +set(SlopeCraft_rc_files) + +if (${WIN32}) + configure_file(others/SlopeCraft.rc.in ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.rc) + set(SlopeCraft_rc_files ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.rc) +endif () + +set(SlopeCraft_headers + SCWind.h + cvt_task.h + PoolModel.h + AdaptiveListView.h + PreviewWind.h + ExportTableModel.h + AiCvterParameterDialog.h + CopyableTableView.h + TransparentStrategyWind.h + CompressEffectViewer.h + BlockListDialog.h +) + +set(SlopeCraft_sources + SCWind.cpp + SCWind_slots.cpp + cvt_task.cpp + PoolModel.cpp + AdaptiveListView.cpp + PreviewWind.cpp + ExportTableModel.cpp + AiCvterParameterDialog.cpp + CopyableTableView.cpp + TransparentStrategyWind.cpp + CompressEffectViewer.cpp + BlockListDialog.cpp + + main.cpp + ${SlopeCraft_rc_files}) + +set(SlopeCraft_uis + SCWind.ui + PreviewWind.ui + AiCvterParameterDialog.ui + TransparentStrategyWind.ui + CompressEffectViewer.ui + BlockListDialog.ui +) + +set(SlopeCraft_ts_files + others/SlopeCraft_en_US.ts +) + +set(SlopeCraft_project_files + ${SlopeCraft_headers} + ${SlopeCraft_sources} + ${SlopeCraft_uis} + + # ${SlopeCraft_ts_files} +) + +qt_add_executable(SlopeCraft + MANUAL_FINALIZATION + ${SlopeCraft_project_files}) + +target_compile_features(SlopeCraft PRIVATE cxx_std_23) +target_precompile_headers(SlopeCraft PRIVATE ${SlopeCraft_headers}) + +target_link_libraries(SlopeCraft PRIVATE + Qt6::Core + Qt6::Widgets + Qt6::Network + magic_enum::magic_enum + tl::expected + + SlopeCraftL + AdaptiveLabel + VersionDialog + BlockListManager + StatMemory + MemoryPolicyDialog +) + +target_include_directories(SlopeCraft PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +set_target_properties(SlopeCraft PROPERTIES + VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_ICON_FILE SlopeCraft.icns + MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.ToKiNoBug.SlopeCraft" + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +file(GLOB SlopeCraft_qrc_images "${CMAKE_CURRENT_SOURCE_DIR}/others/images/*.png") +message(STATUS "SlopeCraft_qrc_images = ${SlopeCraft_qrc_images}") +qt_add_resources(SlopeCraft "SC_images" + PREFIX "/images/" + BASE ${CMAKE_CURRENT_SOURCE_DIR}/others/images + FILES ${SlopeCraft_qrc_images}) + +qt_add_lupdate(SlopeCraft + TS_FILES ${SlopeCraft_ts_files} + SOURCES ${SlopeCraft_project_files} + OPTIONS ${SC_lupdate_flags} +) + +qt_add_lrelease(SlopeCraft TS_FILES ${SlopeCraft_ts_files} + QM_FILES_OUTPUT_VARIABLE SC_qm_files) + +qt_add_resources(SlopeCraft "SC_translations" + PREFIX "/i18n" + BASE ${CMAKE_CURRENT_BINARY_DIR} + FILES ${SC_qm_files}) + +qt_finalize_executable(SlopeCraft) + +if (${WIN32}) + add_custom_target(SC_create_symlink_SC + COMMAND mklink SlopeCraftL.dll "..\\SlopeCraftL\\SlopeCraftL.dll" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS SlopeCraftL + COMMENT "Create symlink to SlopeCraftL.dll for SlopeCraft.exe") + add_dependencies(SC_create_all_symlinks SC_create_symlink_SC) +endif () + +include(install.cmake) \ No newline at end of file diff --git a/SlopeCraft/CompressEffectViewer.cpp b/SlopeCraft/CompressEffectViewer.cpp new file mode 100644 index 00000000..f3aef59b --- /dev/null +++ b/SlopeCraft/CompressEffectViewer.cpp @@ -0,0 +1,41 @@ +#include +#include "CompressEffectViewer.h" +#include "ui_CompressEffectViewer.h" + +#include "SCWind.h" +#include +#include + +CompressEffectViewer::CompressEffectViewer( + SCWind* parent, const SlopeCraft::converted_image& cvted, + const SlopeCraft::structure_3D& structure) + : QDialog{parent}, ui{new Ui::CompressEffectViewer} { + this->ui->setupUi(this); + + const int rows = cvted.rows(); + const int cols = cvted.cols(); + QImage img{cols, rows, QImage::Format_ARGB32}; + cvted.get_compressed_image(structure, + reinterpret_cast(img.scanLine(0))); + + this->ui->lb_display->setPixmap(QPixmap::fromImage(img)); +} + +CompressEffectViewer::~CompressEffectViewer() {} + +void CompressEffectViewer::on_pb_save_image_clicked() noexcept { + static QString prev_dir; + const QString filename = QFileDialog::getSaveFileName( + this, tr("保存压缩后图片"), prev_dir, "*.png"); + if (filename.isEmpty()) { + return; + } + + prev_dir = QFileInfo{filename}.dir().path(); + + auto pixmap = this->ui->lb_display->pixmap(); + if (!pixmap.save(filename)) { + QMessageBox::warning(this, tr("保存图像失败"), + tr("无法保存图像 %1").arg(filename)); + } +} \ No newline at end of file diff --git a/SlopeCraft/CompressEffectViewer.h b/SlopeCraft/CompressEffectViewer.h new file mode 100644 index 00000000..f9b40453 --- /dev/null +++ b/SlopeCraft/CompressEffectViewer.h @@ -0,0 +1,30 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_COMPRESSEFFECTVIEWER_H +#define SLOPECRAFT_SLOPECRAFT_COMPRESSEFFECTVIEWER_H + +#include +#include +#include + +class SCWind; + +class CompressEffectViewer; + +namespace Ui { +class CompressEffectViewer; +} + +class CompressEffectViewer : public QDialog { + Q_OBJECT + private: + std::unique_ptr ui; + + public: + explicit CompressEffectViewer(SCWind* parent, + const SlopeCraft::converted_image&, + const SlopeCraft::structure_3D&); + ~CompressEffectViewer(); + private slots: + void on_pb_save_image_clicked() noexcept; +}; + +#endif // SLOPECRAFT_SLOPECRAFT_COMPRESSEFFECTVIEWER_H \ No newline at end of file diff --git a/SlopeCraft/CompressEffectViewer.ui b/SlopeCraft/CompressEffectViewer.ui new file mode 100644 index 00000000..678f56c2 --- /dev/null +++ b/SlopeCraft/CompressEffectViewer.ui @@ -0,0 +1,60 @@ + + + CompressEffectViewer + + + + 0 + 0 + 555 + 460 + + + + + 0 + 0 + + + + 预览压缩效果 + + + + + + 保存图像 + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + Qt::AlignCenter + + + + + + + + AdaptiveLabel + QLabel +
AdaptiveLabel.h
+
+
+ + +
diff --git a/SlopeCraft/CopyableTableView.cpp b/SlopeCraft/CopyableTableView.cpp new file mode 100644 index 00000000..9067888d --- /dev/null +++ b/SlopeCraft/CopyableTableView.cpp @@ -0,0 +1,68 @@ +#include "CopyableTableView.h" +#include +#include +#include +#include + +CopyableTableView::CopyableTableView(QWidget* parent) : QTableView(parent) {} + +CopyableTableView::~CopyableTableView() = default; + +bool qmi_sorter(const QModelIndex& a, const QModelIndex& b) noexcept { + if (a.row() != b.row()) { + return a.row() < b.row(); + } + + return a.column() < b.column(); +} + +bool CopyableTableView::event(QEvent* event) noexcept { + if (event->type() == QEvent::Type::KeyPress) { + QKeyEvent* const ke = dynamic_cast(event); + if (ke != nullptr) { + if (ke->matches(QKeySequence::StandardKey::Copy)) { + auto sel = this->selectedIndexes(); + + std::sort(sel.begin(), sel.end(), qmi_sorter); + + QString text; + text.reserve(sel.size() * 64); + + int prev_row = -1; + + for (const auto& qmi : sel) { + if (!qmi.isValid()) { + continue; + } + const int r = qmi.row(); + const bool is_row_changed = (prev_row != r); + const bool is_prev_row_valid = (prev_row >= 0); + + prev_row = r; + + if (is_row_changed) { + if (is_prev_row_valid) { + text.push_back('\n'); + // another row + } + // the first element, do not append any char + + } else { + text.push_back('\t'); // same row, but different col + } + + text.append(qmi.data(Qt::ItemDataRole::DisplayRole).toString()); + } + + QApplication::clipboard()->setText(text); + + event->accept(); + emit this->copied(); + return true; + // do copy here + } + } + } + + return QTableView::event(event); +} \ No newline at end of file diff --git a/SlopeCraft/CopyableTableView.h b/SlopeCraft/CopyableTableView.h new file mode 100644 index 00000000..4b36e397 --- /dev/null +++ b/SlopeCraft/CopyableTableView.h @@ -0,0 +1,21 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_COPYABLETABLEVIEW_H +#define SLOPECRAFT_SLOPECRAFT_COPYABLETABLEVIEW_H + +#include +#include + +class CopyableTableView : public QTableView { + Q_OBJECT + private: + public: + explicit CopyableTableView(QWidget* parent = nullptr); + ~CopyableTableView(); + + protected: + bool event(QEvent* event) noexcept override; + + signals: + void copied(); +}; + +#endif // SLOPECRAFT_SLOPECRAFT_COPYABLETABLEVIEW_H \ No newline at end of file diff --git a/SlopeCraft/ExportTableModel.cpp b/SlopeCraft/ExportTableModel.cpp new file mode 100644 index 00000000..27795c7d --- /dev/null +++ b/SlopeCraft/ExportTableModel.cpp @@ -0,0 +1,137 @@ +#include "ExportTableModel.h" +#include "SCWind.h" + +ExportTableModel::ExportTableModel(SCWind* parent) + : QAbstractTableModel(parent), pool{parent->tasks} {} + +ExportTableModel::~ExportTableModel() {} + +SCWind* ExportTableModel::scwind() const noexcept { + return dynamic_cast(this->parent()); +} + +QSize map_size_of_images(QSize image_size) noexcept { + const int width = std::ceil(image_size.width() / 128.0); + const int height = std::ceil(image_size.height() / 128.0); + return QSize{width, height}; +} + +// map_range map_range_at_index(const task_pool_t& pool, int first_map_seq_num, +// int asked_idx) noexcept { +// assert(asked_idx >= 0 && asked_idx < (int)pool.size()); +// +// int current_start_seq = first_map_seq_num; +// +// for (int idx = 0; idx < asked_idx; idx++) { +// const auto current_map_size = +// map_size_of_images(pool[idx].original_image.size()); +// const int current_map_num = +// current_map_size.height() * current_map_size.width(); +// +// current_start_seq += current_map_num; +// } +// +// const auto cms = map_size_of_images(pool[asked_idx].original_image.size()); +// const int cmn = cms.height() * cms.width(); +// +// return map_range{current_start_seq, current_start_seq + cmn - 1}; +// } + +QString map_data_filename(QString dir, int seq_number) noexcept { + return QStringLiteral("%1/map_%2.dat").arg(dir).arg(seq_number); +} + +void ExportTableModel::refresh() noexcept { + // emit + emit this->layoutChanged(); + emit this->dataChanged(this->index(0, 0), this->index(this->rowCount({}), + this->columnCount({}))); +} + +int ExportTableModel::rowCount(const QModelIndex&) const noexcept { + return this->pool.converted_count(this->scwind()->current_color_table(), + this->scwind()->current_convert_option()); +} + +int ExportTableModel::columnCount(const QModelIndex&) const noexcept { + return 6; +} + +QVariant ExportTableModel::data(const QModelIndex& qmi, + int role) const noexcept { + if (!qmi.isValid()) { + return {}; + } + + const int r = qmi.row(); + const int c = qmi.column(); + + if (r < 0 || r >= (int)this->rowCount({})) { + return {}; + } + + const auto& pair = this->pool.converted_task_at_index( + this->scwind()->current_color_table(), + this->scwind()->current_convert_option(), static_cast(r)); + if (!pair) { + return {}; + } + // const auto task_global_index = pair.value().first; + const auto& task = *pair.value().second; + + if (role == Qt::ItemDataRole::DisplayRole) { + const QSize map_size = map_size_of_images(task.original_image.size()); + + const int map_count = map_size.height() * map_size.width(); + const int beg_idx = this->scwind()->current_map_begin_seq_number(); + const auto range = this->pool.map_range_of(beg_idx, r); + + switch (c) { + case 0: + return task.filename; + case 1: + return QStringLiteral("%1 × %2") + .arg(task.original_image.height()) + .arg(task.original_image.width()); + case 2: + return QStringLiteral("%1 × %2 = %3") + .arg(map_size.height()) + .arg(map_size.width()) + .arg(map_count); + case 3: + return QStringLiteral("%1 ~ %2").arg(range.first).arg(range.last); + case 4: + return QStringLiteral("map_%1.dat").arg(range.first); + case 5: + return QStringLiteral("map_%1.dat").arg(range.last); + } + } + + return {}; +} + +QVariant ExportTableModel::headerData(int section, Qt::Orientation orientation, + int role) const noexcept { + if (orientation != Qt::Orientation::Horizontal || + role != Qt::ItemDataRole::DisplayRole || + section > this->columnCount({})) { + return QAbstractTableModel::headerData(section, orientation, role); + } + + switch (section) { + case 0: + return tr("原图文件名"); + case 1: + return tr("图像大小"); + case 2: + return tr("地图大小"); + case 3: + return tr("地图序号范围"); + case 4: + return tr("第一个地图文件名"); + case 5: + return tr("最后一个地图文件名"); + default: + return QAbstractTableModel::headerData(section, orientation, role); + } +} \ No newline at end of file diff --git a/SlopeCraft/ExportTableModel.h b/SlopeCraft/ExportTableModel.h new file mode 100644 index 00000000..7688c72f --- /dev/null +++ b/SlopeCraft/ExportTableModel.h @@ -0,0 +1,36 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_EXPORTTABLEMODEL_H +#define SLOPECRAFT_SLOPECRAFT_EXPORTTABLEMODEL_H + +#include +#include "cvt_task.h" + +class SCWind; + +class ExportTableModel : public QAbstractTableModel { + Q_OBJECT + private: + task_pool& pool; + + public: + explicit ExportTableModel(SCWind* parent); + ~ExportTableModel(); + + int rowCount(const QModelIndex&) const noexcept override; + int columnCount(const QModelIndex&) const noexcept override; + + QVariant data(const QModelIndex&, int role) const noexcept override; + + SCWind* scwind() const noexcept; + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const noexcept override; + + public slots: + void refresh() noexcept; +}; + +QSize map_size_of_images(QSize image_size) noexcept; + +QString map_data_filename(QString dir, int seq_number) noexcept; + +#endif // SLOPECRAFT_SLOPECRAFT_EXPORTTABLEMODEL_H \ No newline at end of file diff --git a/SlopeCraft/PoolModel.cpp b/SlopeCraft/PoolModel.cpp new file mode 100644 index 00000000..75f9cf27 --- /dev/null +++ b/SlopeCraft/PoolModel.cpp @@ -0,0 +1,352 @@ +#include "PoolModel.h" +#include +#include +#include +#include +#include +#include +#include + +PoolModel::PoolModel(SCWind* scw) + : QAbstractListModel(scw), pool{scw->tasks}, scwind{scw} { + assert(scw != nullptr); +} + +PoolModel::~PoolModel() {} + +const QPixmap& PoolModel::icon_empty() noexcept { + static QPixmap img{":/images/empty.png"}; + return img; +} + +const QPixmap& PoolModel::icon_converted() noexcept { + static QPixmap img{":/images/converted.png"}; + return img; +} + +QVariant PoolModel::data(const QModelIndex& idx, int role) const { + const auto& task = this->pool.at(idx.row()); + if (role == Qt::ItemDataRole::DisplayRole) { + return task.filename; + } + + if (role == Qt::ItemDataRole::DecorationRole) { + assert(this->_listview != nullptr); + if (this->_listview->viewMode() == QListView::ViewMode::ListMode) { + return QVariant{}; + } + auto raw_image = QPixmap::fromImage(task.original_image); + auto img = raw_image.scaledToWidth(this->_listview->size().width()); + + if (!task.is_converted_with(this->scwind->current_color_table(), + this->scwind->current_convert_option())) { + this->draw_icon(img, icon_empty(), 0); + } else { + this->draw_icon(img, icon_converted(), 0); + } + + return QIcon{img}; + } + + return QVariant{}; +} + +QPixmap scale_up_to_3232(const QPixmap& original_pixmap, + QSize min_size) noexcept { + const QSize old_size = original_pixmap.size(); + const QSize new_size{std::min(old_size.width(), min_size.width()), + std::min(old_size.height(), min_size.height())}; + if (old_size == new_size) { + return original_pixmap; + } + QImage new_img{new_size, QImage::Format_ARGB32}; + memset(new_img.scanLine(0), 0, new_img.sizeInBytes()); + { + QPainter p{&new_img}; + p.drawPixmap(0, 0, original_pixmap); + p.end(); + } + + return QPixmap::fromImage(new_img); +} + +void PoolModel::draw_icon(QPixmap& image, const QPixmap& icon, int index, + QWidget* ptr_to_report_error) noexcept { + assert(index >= 0); + if (icon.size() != QSize{32, 32}) [[unlikely]] { + QMessageBox::critical(ptr_to_report_error, + QObject::tr("绘制图标时发现错误"), + tr("被绘制的图标尺寸应当是 32*32,但实际上是%1*%" + "2。这属于 SlopeCraft " + "内部错误,请向开发者反馈。SlopeCraft 必须崩溃。") + .arg(icon.size().height()) + .arg(icon.size().width())); + abort(); + // return; + } + { + const QSize expected_min_size{(index + 1) * 32, 32}; + + if (image.height() < expected_min_size.height() || + image.width() < expected_min_size.width()) [[unlikely]] { + image = scale_up_to_3232(image, expected_min_size); + } + } + QPainter painter{&image}; + + const QSize img_size = image.size(); + + const int x = img_size.width() - (index + 1) * 32; + const int y = img_size.height() - 32; + painter.drawPixmap(x, y, icon); + painter.end(); +} + +CvtPoolModel::CvtPoolModel(SCWind* scw) : PoolModel{scw} {} + +CvtPoolModel::~CvtPoolModel() {} + +Qt::DropActions CvtPoolModel::supportedDropActions() const { + return Qt::DropActions{Qt::DropAction::MoveAction, + Qt::DropAction::CopyAction}; +} + +Qt::ItemFlags CvtPoolModel::flags(const QModelIndex& index) const { + Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); + + if (index.isValid()) + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; +} + +QStringList CvtPoolModel::mimeTypes() const { + return QStringList{"text/plain", "image/png"}; +} + +const char mime_data_type[] = "application/slopecraft_pool_index"; + +QByteArray encode_indices(const std::vector& temp) noexcept { + QByteArray qba{reinterpret_cast(temp.data()), + qsizetype(temp.size() * sizeof(int))}; + return qba; +} + +std::vector decode_indices(const QByteArray& qbav) noexcept { + if (qbav.size() % sizeof(int) != 0) { + return {}; + } + + const int size = qbav.size() / sizeof(int); + + const int* const data = reinterpret_cast(qbav.data()); + + std::vector ret{data, data + size}; + return ret; +} + +QMimeData* CvtPoolModel::mimeData(const QModelIndexList& indexes) const { + std::vector temp; + temp.reserve(indexes.size()); + + for (const auto& midx : indexes) { + temp.emplace_back(midx.row()); + } + + QMimeData* ret = new QMimeData; + + ret->setData(mime_data_type, encode_indices(temp)); + + return ret; +} + +bool CvtPoolModel::canDropMimeData(const QMimeData* data, Qt::DropAction, int, + int col, const QModelIndex& parent) const { + // if (parent.isValid()) { + // return false; + // } + + // if (col > 0) { + // return false; + // } + + if (data->hasFormat(mime_data_type)) { + const int bytes = data->data(mime_data_type).size(); + + if (bytes % sizeof(int) not_eq 0) { + return false; + } + return true; + } + return false; + // // disable moving multiple items, because the behavior is incorrect + // if (bytes / sizeof(int) == 1) { + // return true; + // } + // + // return false; +} + +template +void iterator_add(it_t& it, int n) noexcept { + assert(n >= 0); + for (int i = 0; i < n; i++) { + ++it; + } +} + +template +void map_indices(std::vector& pool, std::vector moved_indices, + int begin_idx) noexcept { + std::list temp_pool; + for (T& t : pool) { + temp_pool.emplace_back(std::move(t)); + } + + std::sort(moved_indices.begin(), moved_indices.end()); + std::vector::iterator> src_it_vec; + src_it_vec.reserve(moved_indices.size()); + { + int idx = 0; + auto it = temp_pool.begin(); + for (int sidx : moved_indices) { + const int offset = sidx - idx; + assert(offset >= 0); + iterator_add(it, offset); + + src_it_vec.emplace_back(it); + } + } + + auto begin_it = temp_pool.begin(); + + iterator_add(begin_it, begin_idx); + + for (auto srcit : src_it_vec) { + temp_pool.emplace(begin_it, std::move(*srcit)); + } + + for (auto srcit : src_it_vec) { + temp_pool.erase(srcit); + } + + pool.clear(); + for (auto& t : temp_pool) { + pool.emplace_back(std::move(t)); + } +} + +bool CvtPoolModel::dropMimeData(const QMimeData* data, Qt::DropAction action, + int row, int column, + const QModelIndex& parent) { + if (not this->canDropMimeData(data, action, row, column, parent)) { + return false; + } + + if (action == Qt::IgnoreAction) { + return true; + } + + int begin_row; + + if (row != -1) + begin_row = row; + else if (parent.isValid()) + begin_row = parent.row(); + else + begin_row = this->rowCount(QModelIndex{}); + + { + auto src_indices = decode_indices(data->data(mime_data_type)); + + if (src_indices.size() <= 0) { + return true; + } + std::sort(src_indices.begin(), src_indices.end(), std::greater{}); + + // Move all moved tasks into moved_tasks + std::stack moved_tasks; + for (int src_idx : src_indices) { + cvt_task temp; + std::swap(temp, this->pool[src_idx]); + moved_tasks.emplace(std::move(temp)); + this->pool.erase(this->pool.begin() + src_idx); + } + assert(moved_tasks.size() == src_indices.size()); + const int insert_dest = begin_row - moved_tasks.size(); + assert(insert_dest <= this->pool.size()); + auto it_dest = this->pool.begin() + insert_dest; + while (not moved_tasks.empty()) { + cvt_task temp; + std::swap(temp, moved_tasks.top()); + moved_tasks.pop(); + it_dest = this->pool.insert(it_dest, std::move(temp)); + } + } + this->refresh(); + + return true; +} + +ExportPoolModel::ExportPoolModel(SCWind* scw) : PoolModel(scw) {} + +ExportPoolModel::~ExportPoolModel() {} + +int ExportPoolModel::rowCount(const QModelIndex& midx) const { + if (midx.isValid()) { + return 0; + } + return this->pool.converted_count(this->scwind->current_color_table(), + this->scwind->current_convert_option()); +} + +std::optional ExportPoolModel::export_index_to_global_index( + int eidx) const noexcept { + if (eidx < 0) { + return std::nullopt; + } + return this->pool.export_index_to_global_index( + this->scwind->current_color_table(), + this->scwind->current_convert_option(), eidx); +} + +cvt_task* ExportPoolModel::export_idx_to_task_ptr(int eidx) const noexcept { + auto global_index = this->export_index_to_global_index(eidx); + if (!global_index) { + return nullptr; + } + return &this->pool[global_index.value()]; +} + +QVariant ExportPoolModel::data(const QModelIndex& midx, int role) const { + const auto taskp = this->export_idx_to_task_ptr(midx.row()); + assert(taskp); + if (taskp == nullptr) { + return {}; + } + + auto& task = *taskp; + + if (role == Qt::ItemDataRole::DisplayRole) { + return task.filename; + } + + if (role == Qt::ItemDataRole::DecorationRole) { + assert(this->_listview != nullptr); + if (this->_listview->viewMode() == QListView::ViewMode::ListMode) { + return QVariant{}; + } + auto raw_image = QPixmap::fromImage(task.original_image); + auto img = raw_image.scaledToWidth(this->_listview->size().width()); + if (!task.is_built_with(this->scwind->current_color_table(), + this->scwind->current_convert_option(), + this->scwind->current_build_option())) { + this->draw_icon(img, icon_empty(), 0); + } else { + this->draw_icon(img, icon_converted(), 0); + } + return QIcon{img}; + } + + return QVariant{}; +} \ No newline at end of file diff --git a/SlopeCraft/PoolModel.h b/SlopeCraft/PoolModel.h new file mode 100644 index 00000000..c9cb3366 --- /dev/null +++ b/SlopeCraft/PoolModel.h @@ -0,0 +1,103 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_POOLMODEL_H +#define SLOPECRAFT_SLOPECRAFT_POOLMODEL_H + +#include +#include +#include +#include +#include + +class SCWind; + +class PoolModel : public QAbstractListModel { + Q_OBJECT + protected: + task_pool& pool; + QListView* _listview{nullptr}; + SCWind* const scwind; + + static const QPixmap& icon_empty() noexcept; + static const QPixmap& icon_converted() noexcept; + + public: + explicit PoolModel(SCWind* scw); + ~PoolModel(); + + int rowCount(const QModelIndex& midx) const override { + if (midx.isValid()) { + return 0; + } + return this->pool.size(); + } + + QModelIndex parent(const QModelIndex&) const override { + return QModelIndex{}; + } + + QVariant data(const QModelIndex& idx, int role) const override; + + public slots: + void refresh() noexcept { + emit dataChanged(this->index(0, 0), this->index(this->rowCount({}), 0)); + } + + public: + task_pool& attached_pool() noexcept { return this->pool; } + const task_pool& attached_pool() const noexcept { return this->pool; } + // void set_pool(task_pool_t* _pool) noexcept { this->pool = _pool; } + + QListView* attached_listview() noexcept { return this->_listview; } + const QListView* attached_listview() const noexcept { + return this->_listview; + } + void set_listview(QListView* lv) noexcept { this->_listview = lv; } + + static void draw_icon(QPixmap& image, const QPixmap& icon, int index, + QWidget* ptr_to_report_error) noexcept; + + void draw_icon(QPixmap& image, const QPixmap& icon, + int index) const noexcept { + draw_icon(image, icon, index, this->_listview); + } +}; + +class CvtPoolModel : public PoolModel { + Q_OBJECT + public: + explicit CvtPoolModel(SCWind* scw); + ~CvtPoolModel(); + + Qt::DropActions supportedDropActions() const override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + QStringList mimeTypes() const override; + + QMimeData* mimeData(const QModelIndexList& indexes) const override; + + bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, + int column, const QModelIndex& parent) const override; + + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, + int column, const QModelIndex& parent) override; +}; + +class ExportPoolModel : public PoolModel { + Q_OBJECT + public: + explicit ExportPoolModel(SCWind* scw); + ~ExportPoolModel(); + + int rowCount(const QModelIndex& midx) const override; + + // std::vector iteration_map() const noexcept { + // return ::iteration_map(*this->pool); + // } + + std::optional export_index_to_global_index(int eidx) const noexcept; + + cvt_task* export_idx_to_task_ptr(int eidx) const noexcept; + + QVariant data(const QModelIndex& idx, int role) const override; +}; + +#endif // SLOPECRAFT_SLOPECRAFT_POOLMODEL_H \ No newline at end of file diff --git a/SlopeCraft/PreviewWind.cpp b/SlopeCraft/PreviewWind.cpp new file mode 100644 index 00000000..243a97f9 --- /dev/null +++ b/SlopeCraft/PreviewWind.cpp @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include "PreviewWind.h" +#include "ui_PreviewWind.h" +#include "SCWind.h" +#include "CopyableTableView.h" + +PreviewWind::PreviewWind(QWidget* parent) + : QDialog(parent), ui(new Ui::PreviewWind) { + this->ui->setupUi(this); + + this->mmp = new MaterialModel(this); + this->mmp->set_mat_list_pointer(&this->mat_list); + // this->ui->tv_mat->setModel(this->mmp); + + // connect(this->mmp,&QAbstractTableModel::dataChanged,this->ui->tv_mat,&QTableView::) + + connect(this->ui->cb_show_in_stacks, &QCheckBox::toggled, this->mmp, + &MaterialModel::refresh); + connect(this->ui->cb_sort_option, &QComboBox::currentIndexChanged, this->mmp, + &MaterialModel::refresh); + this->ui->tv_mat->setModel(this->mmp); + + auto on_copied = [this]() noexcept { + this->setWindowTitle( + tr("%1 -- 表格内容已复制到剪贴板").arg(tr("查看材料列表"))); + }; + connect(this->ui->tv_mat, &CopyableTableView::copied, on_copied); + connect(this->ui->pb_copy_to_clipboard, &QPushButton::clicked, + this->ui->tv_mat, &CopyableTableView::copied); +} + +PreviewWind::~PreviewWind() {} + +void PreviewWind::set_size(std::span size) & noexcept { + QString size_str = + tr("大小:%1 × %2 × %3").arg(size[0]).arg(size[1]).arg(size[2]); + + this->ui->lb_show_size->setText(size_str); + + this->ui->lb_show_volume->setText( + tr("体积:%1").arg(size[0] * size[1] * size[2])); +} + +void PreviewWind::set_total_count(size_t count) & noexcept { + this->ui->lb_block_count->setText(tr("方块总数:%1").arg(count)); +} + +void PreviewWind::setup_data( + const SlopeCraft::color_table& table, + const SlopeCraft::structure_3D& structure) noexcept { + std::array count_list; + count_list.fill(0); + table.stat_blocks(structure, count_list.data()); + { + size_t total_blocks{0}; + for (auto c : count_list) { + total_blocks += c; + } + this->set_total_count(total_blocks); + } + + std::vector blkp_arr; + table.visit_blocks([&blkp_arr](const SlopeCraft::mc_block_interface* b) { + blkp_arr.emplace_back(b); + }); + + this->mat_list.reserve(blkp_arr.size()); + for (size_t idx = 0; idx < blkp_arr.size(); idx++) { + if (count_list[idx] > 0) { + this->mat_list.emplace_back( + material_item{.blk = blkp_arr[idx], .count = count_list[idx]}); + } + } + + { + std::array sz{structure.shape_x(), structure.shape_y(), + structure.shape_z()}; + this->set_size(sz); + } + + // this->ui->tv_mat->setModel(this->mmp); + emit this->mmp->layoutChanged(); + // this->mmp->refresh(); + // this->ui->tv_mat->doItemsLayout(); +} + +bool PreviewWind::is_unit_stack() const noexcept { + return this->ui->cb_show_in_stacks->isChecked(); +} + +PreviewWind::sort_option PreviewWind::current_sort_option() const noexcept { + const int cidx = this->ui->cb_sort_option->currentIndex(); + switch (cidx) { + case 1: + return sort_option::ascending; + case 2: + return sort_option::descending; + default: + return sort_option::no_sort; + } +} + +void PreviewWind::on_pb_export_file_clicked() noexcept { + const QString out_file = QFileDialog::getSaveFileName( + this, tr("保存材料表"), this->export_mat_list_prev_dir, "*.csv"); + if (out_file.isEmpty()) { + return; + } + this->export_mat_list_prev_dir = QFileInfo{out_file}.dir().path(); + + QFile ofile{out_file, this}; + if (not ofile.open(QIODevice::OpenMode::enum_type::WriteOnly)) { + QMessageBox::warning(this, tr("保存材料表失败"), + tr("无法打开文件 \"%1\",详细信息:%2") + .arg(out_file, ofile.errorString())); + return; + } + + ofile.write("\"Block name\",\"Block id\",\"Count\"\n"); + const auto current_version = + dynamic_cast(this->parent())->selected_version(); + for (const auto& mat : this->mat_list) { + QString line = + QStringLiteral("\"%1\",\"%2\",%3\n") + .arg(mat.blk->getNameEN(), mat.blk->idForVersion(current_version)) + .arg(mat.count); + ofile.write(line.toLocal8Bit()); + } + ofile.close(); +} + +MaterialModel::MaterialModel(PreviewWind* parent) + : QAbstractTableModel(parent), pwind(parent) {} + +MaterialModel::~MaterialModel() {} + +void MaterialModel::refresh() noexcept { + emit this->dataChanged( + this->index(0, 0), + this->index(this->rowCount() - 1, this->columnCount() - 1)); +} + +QString format_num(int num, int stack_size) noexcept { + assert(stack_size > 0); + const int sb_size = stack_size * 27; + + const int sb_num = num / sb_size; + num -= sb_num * sb_size; + + const int stack_num = num / stack_size; + num -= stack_num * stack_size; + + const int left_num = num; + + if (sb_num > 0) { + return MaterialModel::tr("%1 盒 + %2 组 + %3 个") + .arg(sb_num) + .arg(stack_num) + .arg(left_num); + } + + if (stack_num > 0) { + return MaterialModel::tr("%1 组 + %2 个").arg(stack_num).arg(left_num); + } + + return MaterialModel::tr("%1 个").arg(left_num); +} + +QVariant MaterialModel::data(const QModelIndex& qmi, int role) const noexcept { + if (!qmi.isValid()) { + return {}; + } + + const int r = qmi.row(); + const int c = qmi.column(); + + if (r >= (int)this->mat_list->size()) { + return {}; + } + if (c >= 2) { + return {}; + } + + using it_t = std::vector::const_iterator; + std::vector its; + { + its.reserve(this->mat_list->size()); + + for (auto it = this->mat_list->begin(); it != this->mat_list->end(); ++it) { + its.emplace_back(it); + } + + const auto sort_opt = this->pwind->current_sort_option(); + + if (sort_opt != PreviewWind::sort_option::no_sort) { + auto sort_fun = [sort_opt](it_t a, it_t b) -> bool { + if (a->count == b->count) { + return a->blk < b->blk; + } + + if (sort_opt == PreviewWind::sort_option::ascending) { + return a->count < b->count; + } + + return a->count > b->count; + }; + + std::sort(its.begin(), its.end(), sort_fun); + } + } + + const auto& mat = *its[r]; + + if (role == Qt::ItemDataRole::DisplayRole) { + if (c == 0) { + const auto lang = + dynamic_cast(this->pwind->parent())->lang(); + if (lang == ::SCL_language::Chinese) { + return QString::fromUtf8(mat.blk->getNameZH()); + } else { + return QString::fromUtf8(mat.blk->getNameEN()); + } + } + + if (c == 1) { + if (!this->pwind->is_unit_stack()) { + return QString::number(mat.count); + } + const int stack_size = std::max(1, mat.blk->getStackSize()); + return format_num(mat.count, stack_size); + } + } + + if (role == Qt::ItemDataRole::DecorationRole) { + if (c == 0) { + QImage img{16, 16, QImage::Format_ARGB32}; + mat.blk->getImage((uint32_t*)img.scanLine(0)); + return QIcon{QPixmap::fromImage(img)}; + } + } + + return {}; +} + +int MaterialModel::columnCount(const QModelIndex&) const noexcept { return 2; } + +int MaterialModel::rowCount(const QModelIndex&) const noexcept { + // const int rows = this->mat_list->size(); + return this->mat_list->size(); +} \ No newline at end of file diff --git a/SlopeCraft/PreviewWind.h b/SlopeCraft/PreviewWind.h new file mode 100644 index 00000000..fd8b7941 --- /dev/null +++ b/SlopeCraft/PreviewWind.h @@ -0,0 +1,80 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_PREVIEWWIND_H +#define SLOPECRAFT_SLOPECRAFT_PREVIEWWIND_H + +#include +#include +#include +#include +#include +#include + +class PreviewWind; +class MaterialModel; + +namespace Ui { +class PreviewWind; +} + +struct material_item { + const SlopeCraft::mc_block_interface* blk{nullptr}; + size_t count{0}; +}; + +class PreviewWind : public QDialog { + Q_OBJECT + private: + std::unique_ptr ui; + std::vector mat_list; + MaterialModel* mmp{nullptr}; + + QString export_mat_list_prev_dir; + + void set_size(std::span size) & noexcept; + void set_total_count(size_t count) & noexcept; + + public: + explicit PreviewWind(QWidget* parent = nullptr); + ~PreviewWind(); + + const auto& material_list() const noexcept { return this->mat_list; } + void setup_data(const SlopeCraft::color_table&, + const SlopeCraft::structure_3D&) noexcept; + + enum class sort_option { no_sort, ascending, descending }; + + bool is_unit_stack() const noexcept; + sort_option current_sort_option() const noexcept; + + private slots: + void on_pb_export_file_clicked() noexcept; +}; + +class MaterialModel : public QAbstractTableModel { + Q_OBJECT + private: + const std::vector* mat_list{nullptr}; + const PreviewWind* const pwind; + + public: + explicit MaterialModel(PreviewWind* parent = nullptr); + ~MaterialModel(); + + auto mat_list_pointer() const noexcept { return this->mat_list; } + + void set_mat_list_pointer(const std::vector* mlp) noexcept { + this->mat_list = mlp; + } + + public slots: + void refresh() noexcept; + + public: + QVariant data(const QModelIndex& qmi, + int role = Qt::DisplayRole) const noexcept override; + int columnCount( + const QModelIndex& parent = QModelIndex{}) const noexcept override; + int rowCount( + const QModelIndex& parent = QModelIndex{}) const noexcept override; +}; + +#endif // SLOPECRAFT_SLOPECRAFT_PREVIEWWIND_H \ No newline at end of file diff --git a/SlopeCraft/PreviewWind.ui b/SlopeCraft/PreviewWind.ui new file mode 100644 index 00000000..ff1533cb --- /dev/null +++ b/SlopeCraft/PreviewWind.ui @@ -0,0 +1,160 @@ + + + PreviewWind + + + + 0 + 0 + 604 + 444 + + + + 查看材料列表 + + + + + + + 0 + 24 + + + + + + + QFrame::Shape::StyledPanel + + + + + + + + + + true + + + false + + + false + + + 200 + + + true + + + true + + + + + + + 2 + + + + 不排序 + + + + + 升序 + + + + + 降序 + + + + + + + + + + + QFrame::Shape::StyledPanel + + + + + + + + + + + + + QFrame::Shape::StyledPanel + + + + + + + + + + + 0 + 0 + + + + 按组显示 + + + true + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 导出为文件 + + + + + + + 复制到剪贴板 + + + + + + + + CopyableTableView + QTableView +
CopyableTableView.h
+
+
+ + +
diff --git a/SlopeCraft/SCWind.cpp b/SlopeCraft/SCWind.cpp new file mode 100644 index 00000000..7a5c56fc --- /dev/null +++ b/SlopeCraft/SCWind.cpp @@ -0,0 +1,1381 @@ +#include "SCWind.h" +#include "ui_SCWind.h" +#include +#include +#include +#include +#include +#include + +const QString SCWind::update_url{ + "https://api.github.com/repos/SlopeCraft/SlopeCraft/releases"}; + +// #include "PoolWidget.h" +SCWind::SCWind(QWidget *parent) : QMainWindow(parent), ui(new Ui::SCWind) { + this->ui->setupUi(this); + + this->connect_slots(); + { + // create translators + const char *const translator_filenames[] = { + ":/i18n/SlopeCraft_en_US.qm", ":/i18n/BlockListManager_en_US.qm", + ":/i18n/VersionDialog_en_US.qm", ":/i18n/MemoryPolicyDialog_en_US.qm"}; + /*this->translators.reserve(sizeof(translator_filenames) / + sizeof(const char *)); + */ + for (const char *tf : translator_filenames) { + QTranslator *t = new QTranslator{this}; + QString filename = QString::fromUtf8(tf); + const bool ok = t->load(filename); + if (!ok) { + QMessageBox::warning(this, "Failed to load translate file", + QStringLiteral("Failed to load %1").arg(filename)); + } + + this->translators.emplace_back(t); + } + } + + // initialize blm + { + this->ui->blm->setup_basecolors(); + this->ui->blm->set_version_callback( + [this]() { return this->selected_version(); }); + + QDir::setCurrent(QCoreApplication::applicationDirPath()); + const QString blocks_dir_path = QStringLiteral("./Blocks"); + const QDir blocks_dir{blocks_dir_path}; + if (not blocks_dir.exists()) { + QMessageBox::critical( + this, tr("无法加载方块列表"), + tr("存储方块列表的文件夹 \"%1\" 不存在,或不是文件夹。") + .arg(blocks_dir_path) + + tr("SlopeCraft 必须退出。")); + exit(1); + } + if (not this->ui->blm->add_blocklist( + QStringLiteral("%1/%2").arg(blocks_dir_path, "FixedBlocks.zip"))) { + QMessageBox::critical( + this, tr("无法加载方块列表"), + tr("无法加载 FixedBlocks.zip ,SlopeCraft 缺乏最基础的方块列表。") + + tr("SlopeCraft 必须退出。")); + exit(1); + } + QString default_loaded[] = {"CustomBlocks.zip"}; + QString fail_list; + int fail_counter = 0; + for (auto file : default_loaded) { + assert(file not_eq "FixedBlocks.zip"); + const QString abs_name = + QStringLiteral("%1/%2").arg(blocks_dir_path, file); + if (not this->ui->blm->add_blocklist(abs_name)) { + fail_counter++; + fail_list.append(abs_name); + fail_list.append('\n'); + } + } + this->ui->blm->finish_blocklist(); + if (fail_counter > 0) { + QMessageBox::warning( + this, tr("部分方块列表加载失败"), + tr("以下 %1 " + "个方块列表文件无法被加载:\n%" + "2\n由于它们不是必需,你可以忽略此错误并继续使用。") + .arg(fail_counter) + .arg(fail_list)); + } + + for (auto btnp : this->version_buttons()) { + connect(btnp, &QRadioButton::toggled, this, + &SCWind::when_version_buttons_toggled); + } + + for (auto btnp : this->type_buttons()) { + connect(btnp, &QRadioButton::toggled, this, + &SCWind::when_type_buttons_toggled); + } + + connect(this->ui->blm, &BlockListManager::changed, this, + &SCWind::when_blocklist_changed); + } + + // initialize cvt pool model + { + this->cvt_pool_model = new CvtPoolModel{this}; + this->ui->lview_pool_cvt->setModel(this->cvt_pool_model); + this->cvt_pool_model->set_listview(this->ui->lview_pool_cvt); + connect(this->ui->lview_pool_cvt->selectionModel(), + &QItemSelectionModel::selectionChanged, this, + &SCWind::when_cvt_pool_selectionChanged); + + this->export_pool_model = new ExportPoolModel{this}; + this->ui->lview_pool_export->setModel(this->export_pool_model); + this->export_pool_model->set_listview(this->ui->lview_pool_export); + connect(this->ui->lview_pool_export->selectionModel(), + &QItemSelectionModel::selectionChanged, this, + &SCWind::when_export_pool_selectionChanged); + + connect(this, &SCWind::image_changed, this->cvt_pool_model, + &CvtPoolModel::refresh); + connect(this, &SCWind::image_changed, this->export_pool_model, + &ExportPoolModel::refresh); + connect(this, &SCWind::image_changed, [this]() { + this->ui->lview_pool_cvt->doItemsLayout(); + this->ui->lview_pool_export->doItemsLayout(); + }); + + connect(this->ui->tw_cvt_image, &QTabWidget::currentChanged, this, + &SCWind::when_cvt_pool_selectionChanged); + } + { + this->export_table_model = new ExportTableModel{this}; + this->ui->tview_export_fileonly->setModel(this->export_table_model); + + connect(this, &SCWind::image_changed, this->export_table_model, + &ExportTableModel::refresh); + connect(this->ui->sb_file_start_idx, &QSpinBox::valueChanged, + this->export_table_model, &ExportTableModel::refresh); + connect(this, &SCWind::image_changed, this->export_table_model, + &ExportTableModel::refresh); + + connect(this->ui->lview_pool_export->selectionModel(), + &QItemSelectionModel::selectionChanged, this, + &SCWind::when_data_file_command_changed); + connect(this->ui->sb_file_start_idx, &QSpinBox::valueChanged, this, + &SCWind::when_data_file_command_changed); + connect(this->ui->cb_mc_version_geq_1_20_5, &QCheckBox::clicked, this, + &SCWind::when_data_file_command_changed); + } + + for (QRadioButton *rbp : this->export_type_buttons()) { + connect(rbp, &QRadioButton::clicked, this, + &SCWind::when_export_type_toggled); + } + for (QRadioButton *rbp : this->preset_buttons_no_custom()) { + connect(rbp, &QRadioButton::clicked, this, &SCWind::when_preset_clicked); + } + { + for (QRadioButton *rbp : this->algo_buttons()) { + connect(rbp, &QRadioButton::clicked, this, + &SCWind::when_algo_btn_clicked); + } + connect(this->ui->cb_algo_dither, &QCheckBox::clicked, this, + &SCWind::when_algo_btn_clicked); + } + // setup presets + { + try { + this->default_presets[0] = + load_preset("./Blocks/Presets/vanilla.sc_preset_json"); + this->default_presets[1] = + load_preset("./Blocks/Presets/cheap.sc_preset_json"); + this->default_presets[2] = + load_preset("./Blocks/Presets/elegant.sc_preset_json"); + this->default_presets[3] = + load_preset("./Blocks/Presets/shiny.sc_preset_json"); + } catch (std::exception &e) { + QMessageBox::critical(this, tr("加载默认预设失败"), + tr("一个或多个内置的预设不能被解析。SlopeCraft " + "可能已经损坏,请重新安装。\n具体报错信息:\n%1") + .arg(e.what())); + abort(); + } + } + // initialize combobox for map facing + { + const std::array key1{tr("墙面"), tr("顶面"), tr("底面")}; + const std::array key2{tr("北"), tr("南"), tr("东"), tr("西")}; + for (auto facing : magic_enum::enum_values()) { + const int idx = static_cast(facing); + const QString text = tr("%1,向%2").arg(key1[idx / 4], key2[idx % 4]); + this->ui->cb_map_direction->addItem(text, QVariant::fromValue(facing)); + } + this->ui->cb_map_direction->setCurrentIndex(0); + } + + this->when_preset_clicked(); + + connect(this->ui->pb_manage_block_list, &QPushButton::clicked, this, + &SCWind::on_ac_blocklist_triggered); +} + +SCWind::~SCWind() { + delete this->ui; + { + QDir cache_dir{this->cache_root_dir()}; + if (cache_dir.exists()) { + cache_dir.removeRecursively(); + } + } +} + +QString SCWind::cache_root_dir() const noexcept { + const auto pid = QApplication::applicationPid(); + + const QString sys_cache_dir = QDir::tempPath(); + const QString cache_dir = + QStringLiteral("%1/SlopeCraft/pid=%2").arg(sys_cache_dir).arg(pid); + return cache_dir; +} + +SlopeCraft::color_table *SCWind::current_color_table() noexcept { + auto settings = colortable_settings{this->ui->blm->current_selection(), + this->selected_type()}; + { + auto find = this->color_tables.find(settings); + if (find not_eq this->color_tables.end()) { + return find->second.get(); + } + } + { + std::vector a; + std::vector b; + + this->ui->blm->get_blocklist(a, b); + SlopeCraft::color_table_create_info ci; + ci.map_type = this->selected_type(); + ci.mc_version = this->selected_version(); + for (size_t i = 0; i < 64; i++) { + ci.blocks[i] = b[i]; + ci.basecolor_allow_LUT[i] = a[i]; + } + std::unique_ptr ptr{ + SlopeCraft::SCL_create_color_table(ci)}; + if (ptr == nullptr) { + // QMessageBox::warning(this, tr("设置方块列表失败"), + // tr("您设置的方块列表可能存在错误")); + return nullptr; + } + + auto it = this->color_tables.emplace(settings, std::move(ptr)); + return it.first->second.get(); + } +} + +SlopeCraft::ui_callbacks SCWind::ui_callbacks() const noexcept { + return SlopeCraft::ui_callbacks{ + .wind = const_cast(this), + .cb_keep_awake = [](void *) { QApplication::processEvents(); }, + .cb_report_error = + [](void *wind, SCL_errorFlag ef, const char *msg) { + reinterpret_cast(wind)->report_error(ef, msg); + }, + .cb_report_working_status = + [](void *wind, SCL_workStatus ws) { + SCWind *self = reinterpret_cast(wind); + const QString status_str = SCWind::workStatus_to_string(ws); + QString wind_title; + if (status_str.isEmpty()) { + wind_title = SCWind::default_wind_title(); + } else { + wind_title = QStringLiteral("%1 | %2") + .arg(SCWind::default_wind_title(), status_str); + } + self->setWindowTitle(wind_title); + }, + }; +} + +SlopeCraft::progress_callbacks progress_callback(QProgressBar *bar) noexcept { + return SlopeCraft::progress_callbacks{ + .widget = bar, + .cb_set_range = + [](void *widget, int min, int max, int val) { + if (widget == nullptr) { + return; + } + QProgressBar *bar = reinterpret_cast(widget); + bar->setMinimum(min); + bar->setMaximum(max); + bar->setValue(val); + }, + .cb_add = + [](void *widget, int delta) { + if (widget == nullptr) { + return; + } + QProgressBar *bar = reinterpret_cast(widget); + bar->setValue(bar->value() + delta); + }}; +} + +void SCWind::when_cvt_pool_selectionChanged() noexcept { + const auto selected_idx = this->selected_cvt_task_idx(); + + this->refresh_current_cvt_display(selected_idx); +} + +#define SC_SLOPECRAFT_PRIVATEMACRO_VERSION_BUTTON_LIST \ + { \ + this->ui->rb_ver12, this->ui->rb_ver13, this->ui->rb_ver14, \ + this->ui->rb_ver15, this->ui->rb_ver16, this->ui->rb_ver17, \ + this->ui->rb_ver18, this->ui->rb_ver19, this->ui->rb_ver20, \ + this->ui->rb_ver21 \ + } + +std::array SCWind::version_buttons() noexcept { + return SC_SLOPECRAFT_PRIVATEMACRO_VERSION_BUTTON_LIST; +} + +std::array SCWind::version_buttons() + const noexcept { + return SC_SLOPECRAFT_PRIVATEMACRO_VERSION_BUTTON_LIST; +} + +#define SC_SLOPECRAFT_PRIVATEMACRO_TYPE_BUTTON_LIST \ + { this->ui->rb_type_3d, this->ui->rb_type_flat, this->ui->rb_type_fileonly } + +std::array SCWind::type_buttons() noexcept { + return SC_SLOPECRAFT_PRIVATEMACRO_TYPE_BUTTON_LIST; +} + +std::array SCWind::type_buttons() const noexcept { + return SC_SLOPECRAFT_PRIVATEMACRO_TYPE_BUTTON_LIST; +} + +SCL_gameVersion SCWind::selected_version() const noexcept { + auto btns = this->version_buttons(); + for (size_t idx = 0; idx < btns.size(); idx++) { + if (btns[idx]->isChecked()) { + return SCL_gameVersion(idx + 12); + } + } + return SCL_gameVersion::ANCIENT; +} + +SCL_mapTypes SCWind::selected_type() const noexcept { + if (this->ui->rb_type_3d->isChecked()) { + return SCL_mapTypes::Slope; + } + + if (this->ui->rb_type_flat->isChecked()) { + return SCL_mapTypes::Flat; + } + + if (this->ui->rb_type_fileonly->isChecked()) { + return SCL_mapTypes::FileOnly; + } + return SCL_mapTypes::Slope; + // return {}; +} + +std::vector SCWind::selected_indices() const noexcept { + std::vector ret; + auto sel = this->ui->lview_pool_cvt->selectionModel()->selectedIndexes(); + ret.reserve(sel.size()); + for (auto &midx : sel) { + ret.emplace_back(midx.row()); + } + return ret; +} + +std::optional SCWind::selected_cvt_task_idx() const noexcept { + auto sel = this->ui->lview_pool_cvt->selectionModel()->selectedIndexes(); + if (sel.size() <= 0) { + return std::nullopt; + } + auto front = sel.front(); + if (not front.isValid()) { + return std::nullopt; + } + + const int idx = front.row(); + if (idx < 0 or idx >= this->tasks.size()) { + // In some corner cases this is true + // Return nullopt so that we don't return a invalid index + return std::nullopt; + } + return idx; +} + +std::vector SCWind::selected_export_task_list() const noexcept { + auto selected_eidx = + this->ui->lview_pool_export->selectionModel()->selectedIndexes(); + std::vector ret; + ret.reserve(selected_eidx.size()); + for (auto &midx : selected_eidx) { + ret.emplace_back( + this->export_pool_model->export_idx_to_task_ptr(midx.row())); + } + return ret; +} +cvt_task *SCWind::selected_export_task() const noexcept { + auto selected = this->selected_export_task_list(); + if (selected.empty()) { + return nullptr; + } + + return selected.front(); +} + +SCL_convertAlgo SCWind::selected_algo() const noexcept { + if (this->ui->rb_algo_RGB->isChecked()) { + return SCL_convertAlgo::RGB; + } + if (this->ui->rb_algo_RGB_plus->isChecked()) { + return SCL_convertAlgo::RGB_Better; + } + if (this->ui->rb_algo_Lab94->isChecked()) { + return SCL_convertAlgo::Lab94; + } + if (this->ui->rb_algo_Lab00->isChecked()) { + return SCL_convertAlgo::Lab00; + } + if (this->ui->rb_algo_XYZ->isChecked()) { + return SCL_convertAlgo::XYZ; + } + if (this->ui->rb_algo_GACvter->isChecked()) { + return SCL_convertAlgo::gaCvter; + } + return SCL_convertAlgo::RGB_Better; + // return {}; +} + +bool SCWind::is_dither_selected() const noexcept { + return this->ui->cb_algo_dither->isChecked(); +} + +bool SCWind::is_lossless_compression_selected() const noexcept { + return this->ui->cb_compress_lossless->isChecked(); +} +bool SCWind::is_lossy_compression_selected() const noexcept { + return this->ui->cb_compress_lossy->isChecked(); +} +int SCWind::current_max_height() const noexcept { + return this->ui->sb_max_height->value(); +} + +SCL_compressSettings SCWind::current_compress_method() const noexcept { + auto result = static_cast(SCL_compressSettings::noCompress); + if (this->is_lossless_compression_selected()) { + result = result bitor int(SCL_compressSettings::NaturalOnly); + } + if (this->is_lossy_compression_selected()) { + result = result bitor int(SCL_compressSettings::ForcedOnly); + } + return static_cast(result); +} + +bool SCWind::is_glass_bridge_selected() const noexcept { + return this->ui->cb_glass_bridge->isChecked(); +} +int SCWind::current_glass_brigde_interval() const noexcept { + return this->ui->sb_glass_bridge_interval->value(); +} +SCL_glassBridgeSettings SCWind::current_glass_method() const noexcept { + if (this->is_glass_bridge_selected()) { + return SCL_glassBridgeSettings::withBridge; + } + return SCL_glassBridgeSettings::noBridge; +} + +bool SCWind::is_fire_proof_selected() const noexcept { + return this->ui->cb_fireproof->isChecked(); +} +bool SCWind::is_enderman_proof_selected() const noexcept { + return this->ui->cb_enderproof->isChecked(); +} +bool SCWind::is_connect_mushroom_selected() const noexcept { + return this->ui->cb_connect_mushroom->isChecked(); +} + +SlopeCraft::build_options SCWind::current_build_option() const noexcept { + return SlopeCraft::build_options{ + .max_allowed_height = (uint16_t)this->current_max_height(), + .bridge_interval = (uint16_t)this->current_glass_brigde_interval(), + .compress_method = this->current_compress_method(), + .glass_method = this->current_glass_method(), + .fire_proof = this->is_fire_proof_selected(), + .enderman_proof = this->is_enderman_proof_selected(), + .connect_mushrooms = this->is_connect_mushroom_selected(), + .ui = this->ui_callbacks(), + .main_progressbar = progress_callback(this->ui->pbar_export), + .sub_progressbar = {}}; +} + +SCWind::export_type SCWind::selected_export_type() const noexcept { + auto btns = this->export_type_buttons(); + static_assert(btns.size() == 5); + + constexpr std::array export_type_list{ + export_type::litematica, export_type::vanilla_structure, + export_type::WE_schem, export_type::flat_diagram, + export_type::data_file, + }; + for (int i = 0; i < 5; i++) { + if (btns[i]->isChecked()) { + return export_type_list[i]; + } + } + + return SCWind::export_type::litematica; + // return {}; +} + +void SCWind::when_version_buttons_toggled(bool checked) noexcept { + if (not checked) { + return; + } + this->ui->blm->when_version_updated(); + this->when_blocklist_changed(); + + // When mc version is not 1.20, it must be greater or less than 1.20.5, this + // checkbox is useless and can be fixed + const bool fix_geq_btn = + (this->selected_version() not_eq SCL_gameVersion::MC20); + if (this->selected_version() not_eq SCL_gameVersion::MC20) { + this->ui->cb_mc_version_geq_1_20_5->setChecked(this->selected_version() > + SCL_gameVersion::MC20); + } + this->ui->cb_mc_version_geq_1_20_5->setDisabled(fix_geq_btn); +} + +void SCWind::when_type_buttons_toggled(bool checked) noexcept { + if (not checked) { + return; + } + this->when_blocklist_changed(); + this->update_button_states(); + { + auto valid_buttons = this->valid_export_type_buttons(this->selected_type()); + for (auto btnp : valid_buttons) { + if (btnp->isChecked()) { + return; + } + } + valid_buttons[0]->click(); + } + // this->when_export_type_toggled(); +} + +void SCWind::when_blocklist_changed() noexcept { + this->set_colorset(); + this->ui->rb_preset_custom->setChecked(true); + // this->ui->rb_preset_ +} + +void SCWind::set_colorset() noexcept { + auto color_table = this->current_color_table(); + const int num_colors = + (color_table not_eq nullptr) ? color_table->colors().num_colors : 0; + + this->ui->lb_avaliable_colors->setText( + tr("可用颜色数量:%1").arg(num_colors)); +} + +#define SC_SLOPECRAFT_PREIVATEMACRO_EXPORT_TYPE_BUTTONS \ + { \ + this->ui->rb_export_lite, this->ui->rb_export_nbt, this->ui->rb_export_WE, \ + this->ui->rb_export_flat_diagram, this->ui->rb_export_fileonly \ + } + +std::array SCWind::export_type_buttons() noexcept { + return SC_SLOPECRAFT_PREIVATEMACRO_EXPORT_TYPE_BUTTONS; +} +std::array SCWind::export_type_buttons() + const noexcept { + return SC_SLOPECRAFT_PREIVATEMACRO_EXPORT_TYPE_BUTTONS; +} + +std::vector SCWind::valid_export_type_buttons( + SCL_mapTypes type) const noexcept { + switch (type) { + case SCL_mapTypes::Slope: + return {this->ui->rb_export_lite, this->ui->rb_export_nbt, + this->ui->rb_export_WE, this->ui->rb_export_fileonly}; + case SCL_mapTypes::Flat: + return SC_SLOPECRAFT_PREIVATEMACRO_EXPORT_TYPE_BUTTONS; + case SCL_mapTypes::FileOnly: + return {this->ui->rb_export_fileonly}; + } + return {}; +} + +std::array SCWind::preset_buttons_no_custom() noexcept { + return {this->ui->rb_preset_vanilla, this->ui->rb_preset_cheap, + this->ui->rb_preset_elegant, this->ui->rb_preset_shiny}; +} + +#define SC_SLOPECRAFT_PRIVATEMARCO_ALGO_BUTTONS \ + { \ + this->ui->rb_algo_RGB, this->ui->rb_algo_RGB_plus, \ + this->ui->rb_algo_Lab94, this->ui->rb_algo_Lab00, \ + this->ui->rb_algo_XYZ, this->ui->rb_algo_GACvter \ + } + +std::array SCWind::algo_buttons() const noexcept { + return SC_SLOPECRAFT_PRIVATEMARCO_ALGO_BUTTONS; +} + +std::array SCWind::algo_buttons() noexcept { + return SC_SLOPECRAFT_PRIVATEMARCO_ALGO_BUTTONS; +} + +QProgressBar *SCWind::current_bar() noexcept { + const int cid = this->ui->tw_main->currentIndex(); + switch (cid) { + case 1: + return this->ui->pbar_cvt; + case 2: + return this->ui->pbar_export; + default: + return nullptr; + } +} + +void SCWind::update_button_states() noexcept { + { + const bool disable_3d = (this->selected_type() == SCL_mapTypes::FileOnly); + std::array rb_export_3d_types{this->ui->rb_export_lite, + this->ui->rb_export_nbt, + this->ui->rb_export_WE}; + for (auto rbp : rb_export_3d_types) { + rbp->setDisabled(disable_3d); + } + + std::array pb_export{ + this->ui->pb_export_all, this->ui->pb_build3d, + this->ui->pb_preview_compress_effect, this->ui->pb_preview_materials}; + for (QPushButton *pbp : pb_export) { + pbp->setDisabled(disable_3d); + } + + if (disable_3d) { + this->ui->rb_export_fileonly->setChecked(true); + } + } + { + const bool enable_flatdiagram = + (this->selected_type() == SCL_mapTypes::Flat); + + this->ui->rb_export_flat_diagram->setEnabled(enable_flatdiagram); + } +} + +void SCWind::when_preset_clicked() noexcept { + int final_idx = -1; + + for (int idx = 0; idx < (int)this->preset_buttons_no_custom().size(); idx++) { + if (this->preset_buttons_no_custom()[idx]->isChecked()) { + final_idx = idx; + break; + } + } + + assert(final_idx >= 0); + + if (!this->ui->blm->loadPreset(this->default_presets[final_idx])) { + QMessageBox::warning(this, tr("应用预设失败"), ""); + return; + } + + this->preset_buttons_no_custom()[final_idx]->setChecked(true); +} + +void SCWind::when_export_type_toggled() noexcept { + const bool page_3d = !this->ui->rb_export_fileonly->isChecked(); + if (page_3d) { + this->ui->sw_export->setCurrentIndex(0); + } else { + this->ui->sw_export->setCurrentIndex(1); + } + + const auto btns = this->export_type_buttons(); + + for (int idx = 0; idx < 4; idx++) { + if (btns[idx]->isChecked()) { + this->ui->tw_export_options->setCurrentIndex(idx); + } + } + + this->update_button_states(); +} + +SlopeCraft::convert_option SCWind::current_convert_option() noexcept { + return SlopeCraft::convert_option{ + .caller_api_version = SC_VERSION_U64, + .algo = this->selected_algo(), + .dither = this->is_dither_selected(), + .ai_cvter_opt = this->GA_option, + .progress = progress_callback(this->ui->pbar_cvt), + .ui = this->ui_callbacks(), + }; +} + +std::unique_ptr +SCWind::convert_image(int idx) noexcept { + assert(idx >= 0); + assert(idx < (int)this->tasks.size()); + return this->convert_image(this->tasks[idx]); +} + +std::unique_ptr +SCWind::convert_image(const cvt_task &task) noexcept { + auto ctable = this->current_color_table(); + if (ctable == nullptr) { + QMessageBox::critical( + this, tr("没有可用颜色"), + tr("没有勾选任何颜色,无法转化图像。请至少勾选3~16种颜色。")); + return nullptr; + } + + const auto num_blocks = ctable->num_blocks(); + if (num_blocks <= 3) { + const auto reply = QMessageBox::warning( + this, tr("勾选颜色太少"), + tr("仅仅勾选了%" + "1种颜色,颜色过少,转化效率可能非常差。您可以点Yes继续" + "转化,但非常建议请尽量多勾选一些颜色。") + .arg(num_blocks), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Yes, + QMessageBox::StandardButton::No}, + QMessageBox::StandardButton::No); + if (reply not_eq QMessageBox::StandardButton::Yes) { + return nullptr; + } + } + + const QImage &raw = task.original_image; + { + SlopeCraft::const_image_reference img{ + .data = (const uint32_t *)raw.scanLine(0), + .rows = static_cast(raw.height()), + .cols = static_cast(raw.width()), + }; + auto cvted_img = ctable->convert_image(img, this->current_convert_option()); + + return std::unique_ptr{ + cvted_img}; + } +} + +const SlopeCraft::converted_image &SCWind::convert_if_need( + cvt_task &task) noexcept { + const auto table = this->current_color_table(); + const auto opt = this->current_convert_option(); + if (not task.is_converted_with(table, opt)) { + auto cvted = this->convert_image(task); + assert(cvted); + task.set_converted(table, opt, std::move(cvted)); + } + + auto &cvted = task.converted_images[{table, opt}].converted_image; + assert(cvted != nullptr); + return *cvted; +} + +std::unique_ptr SCWind::build_3D( + const SlopeCraft::converted_image &cvted) noexcept { + auto ctable = this->current_color_table(); + const auto opt = this->current_build_option(); + auto str = ctable->build(cvted, opt); + return std::unique_ptr{str}; +} + +std::tuple +SCWind::convert_and_build_if_need(cvt_task &task) noexcept { + const auto table = this->current_color_table(); + const auto &cvted = this->convert_if_need(task); + + assert(task.is_converted_with(table, this->current_convert_option())); + auto &cvt_result = + task.converted_images.at({table, this->current_convert_option()}); + const auto build_opt = this->current_build_option(); + + if (auto str_3D = cvt_result.load_build_cache(*table, build_opt, + this->cache_root_dir())) { + // The 3D structure is built, it exists in memory or can be loaded + return {cvted, *str_3D}; + } + // Build 3D structure now + auto s = this->build_3D(cvted); + auto ptr = s.get(); + assert(ptr != nullptr); + cvt_result.set_built(build_opt, std::move(s)); + return {cvted, *ptr}; +} + +// void SCWind::kernel_make_cvt_cache() noexcept { +// std::string err; +// err.resize(4096); +// SlopeCraft::string_deliver sd{err.data(), err.size()}; +// +// if (!this->kernel->saveConvertCache(sd)) { +// QString qerr = QString::fromUtf8(sd.data); +// QMessageBox::warning(this, tr("缓存失败"), +// tr("未能创建缓存文件,错误信息:\n%1").arg(qerr)); +// } +// } + +// QImage SCWind::get_converted_image_from_kernel() const noexcept { +// assert(this->kernel->queryStep() >= SCL_step::converted); +// +// const int rows = this->kernel->getImageRows(); +// const int cols = this->kernel->getImageCols(); +// +// QImage img{cols, rows, QImage::Format_ARGB32}; +// +// this->kernel->getConvertedImage(nullptr, nullptr, (uint32_t +// *)img.scanLine(0), +// false); +// +// return img; +// } + +QImage get_converted_image(const SlopeCraft::converted_image &cvted) noexcept { + QImage img{ + QSize{static_cast(cvted.cols()), static_cast(cvted.rows())}, + QImage::Format::Format_ARGB32}; + cvted.get_converted_image(reinterpret_cast(img.scanLine(0))); + return img; +} + +void SCWind::refresh_current_cvt_display( + std::optional selected_idx) noexcept { + if (not selected_idx.has_value()) { + this->ui->lb_raw_image->setPixmap({}); + this->ui->lb_cvted_image->setPixmap({}); + this->ui->lb_map_shape->setText(""); + return; + } + + const int idx = selected_idx.value(); + + this->ui->lb_raw_image->setPixmap( + QPixmap::fromImage(this->tasks[idx].original_image)); + { + auto shape = this->tasks[idx].original_image.size(); + const int map_rows = std::ceil(shape.height() / 128.0f); + const int map_cols = std::ceil(shape.width() / 128.0f); + this->ui->lb_map_shape->setText( + tr("%1行,%2列").arg(map_rows).arg(map_cols)); + } + + auto &task = this->tasks[selected_idx.value()]; + + auto it = task.converted_images.find( + {this->current_color_table(), this->current_convert_option()}); + if (it != task.converted_images.end() && + it->second.converted_image != nullptr) { + this->ui->lb_cvted_image->setPixmap( + QPixmap::fromImage(get_converted_image(*it->second.converted_image))); + return; + } + + this->ui->lb_cvted_image->setPixmap({}); +} + +// void SCWind::mark_all_task_unconverted() noexcept { +// for (auto &task : this->tasks) { +// task.converted_img = nullptr; +// task.structure = nullptr; +// } +// } + +void SCWind::when_algo_btn_clicked() noexcept { + this->cvt_pool_model->refresh(); + this->refresh_current_cvt_display(this->selected_cvt_task_idx()); +} + +void SCWind::export_current_cvted_image(int idx, QString filename) noexcept { + assert(idx >= 0); + assert(idx < (int)this->tasks.size()); + + auto &task = this->tasks[idx]; + auto cvted = task.get_converted_image(this->current_color_table(), + this->current_convert_option()); + if (cvted == nullptr) { + const auto ret = QMessageBox::warning( + this, tr("无法保存第%1个转化后图像").arg(idx + 1), + tr("该图像未被转化,或者转化之后修改了颜色表/转化算法。请重新转化它。"), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ok, + QMessageBox::StandardButton::Ignore}); + if (ret == QMessageBox::StandardButton::Ok) { + auto cvted_uptr = this->convert_image(task); + if (cvted_uptr == nullptr) { + return; + } + task.set_converted(this->current_color_table(), + this->current_convert_option(), std::move(cvted_uptr)); + cvted = task.get_converted_image(this->current_color_table(), + this->current_convert_option()); + if (cvted == nullptr) { + return; + } + } else { + return; + } + } + + auto img = get_converted_image(*cvted); + bool ok = img.save(filename); + + if (!ok) { + QMessageBox::warning( + this, tr("保存图像失败"), + tr("保存%1时失败。可能是因为文件路径错误,或者图片格式不支持。") + .arg(filename)); + return; + } +} + +// void SCWind::kernel_build_3d() noexcept { +// if (!this->kernel->build(this->current_build_option())) { +// QMessageBox::warning(this, tr("构建三维结构失败"), +// tr("构建三维结构时,出现错误。可能是因为尝试跳步。")); +// return; +// } +// } + +void SCWind::refresh_current_build_display(cvt_task *taskp) noexcept { + this->ui->lb_show_3dsize->setText(tr("大小:")); + this->ui->lb_show_block_count->setText(tr("方块数量:")); + if (taskp == nullptr) { + return; + } + + auto &task = *taskp; + const auto cvted_it = task.get_convert_result(this->current_color_table(), + this->current_convert_option()); + if (cvted_it == task.converted_images.end()) { + return; + } + if (auto str_with_info = cvted_it->second.get_build_cache_with_info_noload( + *this->current_color_table(), this->current_build_option(), + this->cache_root_dir())) { + // const SlopeCraft::structure_3D *str_3D = str_with_info->handle.get(); + const auto x = str_with_info->shape[0], y = str_with_info->shape[1], + z = str_with_info->shape[2]; + + const auto block_count = str_with_info->block_count; + this->ui->lb_show_3dsize->setText( + tr("大小: %1 × %2 × %3").arg(x).arg(y).arg(z)); + this->ui->lb_show_block_count->setText(tr("方块数量:%1").arg(block_count)); + } + + // if (this->selected_export_type() == SCWind::export_type::data_file) { + // this->when_data_file_command_changed(); + // } +} + +tl::expected SCWind::get_command( + const SlopeCraft::converted_image &cvted, int begin_idx) const noexcept { + QString command; + SlopeCraft::ostream_wrapper os{ + .handle = &command, + .callback_write_data = + [](const void *data, size_t len, void *handle) { + QString *buf = reinterpret_cast(handle); + QString temp = + QString::fromUtf8(reinterpret_cast(data), + static_cast(len)); + buf->append(temp); + }, + }; + bool after_1_20_5; + if (this->selected_version() not_eq SCL_gameVersion::MC20) { + after_1_20_5 = true; + } else { + after_1_20_5 = this->ui->cb_mc_version_geq_1_20_5->isChecked(); + } + + SlopeCraft::map_data_file_give_command_options opt{}; + opt.destination = &os; + opt.begin_index = begin_idx; + opt.after_1_12 = (this->selected_version() > SCL_gameVersion::MC12); + opt.after_1_20_5 = after_1_20_5; + const bool ok = cvted.get_map_command(opt); + if (!ok) { + return tl::make_unexpected(tr("生成命令失败:\n%1").arg(command)); + } + return command; +} + +void SCWind::when_export_pool_selectionChanged() noexcept { + this->refresh_current_build_display(this->selected_export_task()); +} + +QString extension_of_export_type(SCWind::export_type et) noexcept { + switch (et) { + case SCWind::export_type::litematica: + return "litematic"; + case SCWind::export_type::vanilla_structure: + return "nbt"; + case SCWind::export_type::WE_schem: + return "schem"; + case SCWind::export_type::flat_diagram: + return "flat-diagram.png"; + case SCWind::export_type::data_file: + return "dat"; + } + + return "Invalid_export_type"; +} + +std::optional SCWind::current_litematic_option( + QString &err) const noexcept { + err.clear(); + static std::string litename; + static std::string region_name; + + litename = this->ui->le_lite_name->text().toUtf8().data(); + region_name = this->ui->le_lite_region_name->text().toUtf8().data(); + if (litename.empty()) { + litename = "UnnamedLitematica"; + } + if (region_name.empty()) { + region_name = "UnnamedRegion"; + } + + return SlopeCraft::litematic_options{ + .caller_api_version = SC_VERSION_U64, + .litename_utf8 = litename.data(), + .region_name_utf8 = region_name.data(), + .ui = this->ui_callbacks(), + .progressbar = progress_callback(this->ui->pbar_export), + }; +} + +std::optional SCWind::current_nbt_option( + QString &err) const noexcept { + err.clear(); + + return SlopeCraft::vanilla_structure_options{ + .is_air_structure_void = this->ui->cb_nbt_air_void->isChecked(), + .ui{}, + .progressbar{}, + }; +} + +std::optional SCWind::current_schem_option( + QString &err) const noexcept { + err.clear(); + + SlopeCraft::WE_schem_options ret; + + { + const std::array le_offset{this->ui->le_WE_offset_X, + this->ui->le_WE_offset_Y, + this->ui->le_WE_offset_Z}; + + for (size_t idx = 0; idx < le_offset.size(); idx++) { + bool ok; + + ret.offset[idx] = le_offset[idx]->text().toInt(&ok); + if (!ok) { + err = tr("WE 原理图参数有错:输入给 offset 的值\"%" + "1\"不是一个有效的坐标,应当输入一个整数。") + .arg(le_offset[idx]->text()); + return std::nullopt; + } + } + } + + { + const std::array le_weoffset{this->ui->le_WE_weoffset_X, + this->ui->le_WE_weoffset_Y, + this->ui->le_WE_weoffset_Z}; + + for (size_t idx = 0; idx < le_weoffset.size(); idx++) { + bool ok; + + ret.we_offset[idx] = le_weoffset[idx]->text().toInt(&ok); + if (!ok) { + err = tr("WE 原理图参数有错:输入给 we offset 的值\"%" + "1\"不是一个有效的数字,应当输入一个整数。") + .arg(le_weoffset[idx]->text()); + return std::nullopt; + } + } + } + static std::string region_name; + region_name = this->ui->le_WE_region_name->text().toUtf8().data(); + + static std::vector mod_charp; + { + static std::vector mod_names; + + const auto mod_names_q = + this->ui->le_WE_mods->toPlainText().replace("\r\n", "\n").split('\n'); + + mod_names.resize(mod_names_q.size()); + mod_charp.resize(mod_names_q.size()); + + for (int idx = 0; idx < mod_names_q.size(); idx++) { + mod_names[idx] = mod_names_q[idx].toUtf8().data(); + mod_charp[idx] = mod_names[idx].c_str(); + } + } + + ret.num_required_mods = mod_charp.size(); + ret.required_mods_name_utf8 = mod_charp.data(); + + ret.ui = this->ui_callbacks(); + ret.progressbar = progress_callback(this->ui->pbar_export); + + return ret; +} + +std::optional +SCWind::current_flatdiagram_option(QString &err) const noexcept { + err.clear(); + + int row_margin = this->ui->sb_flatdiagram_hmargin->value(); + int col_margin = this->ui->sb_flatdiagram_vmargin->value(); + + if (row_margin <= 0 || col_margin <= 0) { + err = + tr("平面示意图的分割线间距无效:水平间距为 %1,垂直间距为 %2, " + "但间距必须为正数。"); + return std::nullopt; + } + + if (!this->ui->cb_flatdiagram_hline->isChecked()) { + row_margin = -1; + } + + if (!this->ui->cb_flatdiagram_vline->isChecked()) { + col_margin = -1; + } + + return SlopeCraft::flag_diagram_options{ + .caller_api_version = SC_VERSION_U64, + .split_line_row_margin = row_margin, + .split_line_col_margin = col_margin, + .ui = this->ui_callbacks(), + .progressbar = progress_callback(this->ui->pbar_export), + }; +} + +int SCWind::current_map_begin_seq_number() const noexcept { + return this->ui->sb_file_start_idx->value(); +} + +void SCWind::report_error(::SCL_errorFlag flag, const char *msg) noexcept { + if (flag == SCL_errorFlag::NO_ERROR_OCCUR) { + return; + } + using sb = QMessageBox::StandardButton; + using sbs = QMessageBox::StandardButtons; + + auto flag_name = magic_enum::enum_name(flag); + + const QString errmsg = tr("错误类型:%1,错误码:%2。详细信息:\n%3") + .arg(flag_name.data()) + .arg(int(flag)) + .arg(msg); + + const auto ret = QMessageBox::critical( + this, tr("SlopeCraft 出现错误"), + tr("%1\n\n点击 Ok 以忽略这个错误,点击 Close 将退出 SlopeCraft。") + .arg(errmsg), + sbs{sb::Ok, sb::Close}); + + if (ret == sb::Close) { + exit(0); + } + + return; +} + +void SCWind::set_lang(::SCL_language lang) noexcept { + this->language = lang; + for (auto trans : this->translators) { + if (this->language == ::SCL_language::Chinese) { + QApplication::removeTranslator(trans); + } else { + QApplication::installTranslator(trans); + } + } + this->ui->retranslateUi(this); + + this->ui->blm->when_lang_updated(lang); +} + +QString impl_default_title() noexcept { + return QStringLiteral("SlopeCraft %1").arg(SlopeCraft::SCL_getSCLVersion()); +} + +const QString &SCWind::default_wind_title() noexcept { + static const QString title = impl_default_title(); + return title; +} + +QString SCWind::workStatus_to_string(::SCL_workStatus status) noexcept { + switch (status) { + case SlopeCraft::workStatus::none: + break; + case SlopeCraft::workStatus::buidingHeighMap: + return tr("正在构建高度矩阵"); + case SlopeCraft::workStatus::building3D: + return tr("正在构建三维结构"); + case SlopeCraft::workStatus::collectingColors: + return tr("正在收集整张图片的颜色"); + case SlopeCraft::workStatus::compressing: + return tr("正在压缩立体地图画"); + case SlopeCraft::workStatus::constructingBridges: + return tr("正在为立体地图画搭桥"); + case SlopeCraft::workStatus::converting: + return tr("正在匹配颜色"); + case SlopeCraft::workStatus::dithering: + return tr("正在使用抖动仿色"); + case SlopeCraft::workStatus::flippingToWall: + return tr("正在将平板地图画变为墙面地图画"); + case SlopeCraft::workStatus::writing3D: + return tr("正在写入三维结构"); + case SlopeCraft::workStatus::writingBlockPalette: + return tr("正在写入方块列表"); + case SlopeCraft::workStatus::writingMapDataFiles: + return tr("正在写入地图数据文件"); + case SlopeCraft::workStatus::writingMetaInfo: + return tr("正在写入基础信息"); + } + + return {}; +} + +std::tuple +SCWind::load_selected_3D() noexcept { + auto taskp = this->selected_export_task(); + if (taskp == nullptr) { + QMessageBox::warning(this, tr("未选择图像"), + tr("请在左侧任务池选择一个图像")); + return {nullptr, nullptr}; + } + assert(taskp != nullptr); + + cvt_task &task = *taskp; + const ptrdiff_t index = &task - this->tasks.data(); + assert(index >= 0 && index < ptrdiff_t(this->tasks.size())); + if (!task.is_converted_with(this->current_color_table(), + this->current_convert_option())) { + QMessageBox::warning(this, tr("该图像尚未被转化"), + tr("必须先转化一个图像,然后再为它构建三维结构")); + return {nullptr, nullptr}; + } + QString errtitle; + QString errmsg; + // try to load cache + auto [cvted_img, structure_3D] = + [this, &task, &errtitle, + &errmsg]() -> std::pair { + auto it = task.converted_images.find(convert_input{ + this->current_color_table(), this->current_convert_option()}); + if (it == task.converted_images.end()) { + errtitle = tr("该图像尚未被转化"); + errmsg = + tr("可能是在转化完成之后又修改了转化算法,因此之前的转化无效。必须重" + "新转化该图像。"); + return {nullptr, nullptr}; + } + auto str_3D = it->second.load_build_cache(*this->current_color_table(), + this->current_build_option(), + this->cache_root_dir()); + + if (str_3D == nullptr) { + errtitle = tr("尚未构建三维结构"); + errmsg = tr( + "在预览材料表之前,必须先构建三维结构。出现这个警告,可能是因为你" + "在构建三维结构之后,又修改了三维结构的选项,因此之前的结果无效。"); + return {it->second.converted_image.get(), nullptr}; + } + return {it->second.converted_image.get(), str_3D}; + }(); + + if (!errtitle.isEmpty()) { + QMessageBox::warning(this, errtitle, errmsg); + return {nullptr, nullptr}; + } + + return {cvted_img, structure_3D}; +} + +bool SCWind::should_auto_cache(bool suppress_warnings) noexcept { + bool result = false; + const auto self_used = get_self_memory_info(); + QString error_template = + tr("这不是严重的问题,你可以直接忽略这个警告,或者把它反馈给开发者," + "不影响正常使用。只是 Slopecraft " + "可能占用更多的内存。\n详细信息:\n%1"); + + if (not self_used) { + if (not suppress_warnings) { + QMessageBox::warning( + this, tr("获取本进程的内存占用失败"), + error_template.arg(QString::fromLocal8Bit(self_used.error().c_str())), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ok}); + } + } else { + result = this->mem_policy.should_cache(self_used.value()) or result; + } + + const auto system_info = get_system_memory_info(); + if (not system_info) { + if (not suppress_warnings) { + QMessageBox::warning( + this, tr("获取操作系统内存占用失败"), + error_template.arg(QString::fromLocal8Bit(self_used.error().c_str())), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ok}); + } + } else { + result = this->mem_policy.should_cache(system_info.value()) or result; + } + return result; +} + +SCWind::auto_cache_report SCWind::auto_cache_3D( + [[maybe_unused]] bool cache_all) noexcept { + // const auto colortable = this->current_color_table(); + // const auto build_opt = this->current_build_option(); + auto_cache_report report{ + .structures_cached = 0, + .memory_saved = 0, + }; + const auto self_mem_before = get_self_memory_info(); + const QString cache_root = this->cache_root_dir(); + + auto go_through = [this, cache_root, cache_all]() -> size_t { + size_t cached = 0; + for (auto &task : this->tasks) { + for (auto &[cvt_input, cvted] : task.converted_images) { + // if we don't need to cache, return. + if ((not cache_all) and (not this->should_auto_cache(true))) { + return cached; + } + + auto report = cvted.cache_all_structures( + *cvt_input.table, *cvted.converted_image, cache_root); + cached += report.cache_num; + } + } + return cached; + }; + + report.structures_cached = go_through(); + const auto self_mem_current = get_self_memory_info(); + if (self_mem_before and self_mem_current) { + report.memory_saved = + self_mem_before.value().used - self_mem_current.value().used; + } + + return report; +} + +SlopeCraft::assembled_maps_options SCWind::current_assembled_maps_option() + const noexcept { + SlopeCraft::assembled_maps_options option; + option.mc_version = this->selected_version(); + option.frame_variant = this->ui->cb_glowing_item_frame->isChecked() + ? SCL_item_frame_variant::glowing + : SCL_item_frame_variant::common; + option.after_1_20_5 = this->ui->cb_mc_version_geq_1_20_5->isChecked(); + option.fixed_frame = this->ui->cb_fixed_frame->isChecked(); + option.invisible_frame = this->ui->cb_invisible_frame->isChecked(); + option.map_facing = + this->ui->cb_map_direction->currentData().value(); + + return option; +} \ No newline at end of file diff --git a/SlopeCraft/SCWind.h b/SlopeCraft/SCWind.h new file mode 100644 index 00000000..a2be7a01 --- /dev/null +++ b/SlopeCraft/SCWind.h @@ -0,0 +1,323 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_SCWIND_H +#define SLOPECRAFT_SLOPECRAFT_SCWIND_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cvt_task.h" +#include "PoolModel.h" +#include "ExportTableModel.h" +#include "MemoryPolicyDialog.h" + +class SCWind; + +namespace Ui { +class SCWind; +} + +struct colortable_settings { + colortable_settings() = delete; + colortable_settings(const selection& s, SCL_mapTypes t) + : selection_{s}, map_type{t} {} + + colortable_settings(selection&& s, SCL_mapTypes t) + : selection_{std::move(s)}, map_type{t} {} + + selection selection_; + SCL_mapTypes map_type; + + [[nodiscard]] bool operator==(const colortable_settings& b) const noexcept { + if (this->selection_ not_eq b.selection_) { + return false; + } + if (this->map_type not_eq b.map_type) { + return false; + } + return true; + } +}; + +template <> +struct std::hash { + uint64_t operator()(const colortable_settings& s) const noexcept { + return std::hash{}(s.selection_) xor + static_cast(s.map_type); + } +}; + +SlopeCraft::progress_callbacks progress_callback(QProgressBar* bar) noexcept; + +SlopeCraft::const_image_reference wrap_image(const QImage& img) noexcept; + +[[nodiscard]] QImage get_converted_image( + const SlopeCraft::converted_image&) noexcept; + +class SCWind : public QMainWindow { + Q_OBJECT + private: + void connect_slots() noexcept; + + public: + explicit SCWind(QWidget* parent = nullptr); + ~SCWind(); + + inline static QNetworkAccessManager& network_manager() noexcept { + static QNetworkAccessManager manager; + return manager; + } + + static const QString& default_wind_title() noexcept; + + static QString workStatus_to_string(::SCL_workStatus) noexcept; + + const static QString update_url; + + enum class export_type { + litematica, + vanilla_structure, + WE_schem, + flat_diagram, + data_file + }; + + private slots: + void on_pb_add_image_clicked() noexcept; + void on_pb_remove_image_clicked() noexcept; + void on_pb_replace_image_clicked() noexcept; + + void on_cb_lv_cvt_icon_mode_clicked() noexcept; + void on_cb_compress_lossy_toggled(bool checked) noexcept; + + void on_pb_load_preset_clicked() noexcept; + void on_pb_save_preset_clicked() noexcept; + + void on_pb_prefer_concrete_clicked() noexcept; + void on_pb_prefer_wool_clicked() noexcept; + void on_pb_prefer_glass_clicked() noexcept; + void on_pb_prefer_planks_clicked() noexcept; + void on_pb_prefer_logs_clicked() noexcept; + void on_pb_prefer_slabs_clicked() noexcept; + void on_pb_prefer_carpets_clicked() noexcept; + void on_pb_prefer_pressure_plates_clicked() noexcept; + + void on_pb_select_all_clicked() noexcept; + void on_pb_deselect_all_clicked() noexcept; + void on_pb_invselect_clicked() noexcept; + + void when_cvt_pool_selectionChanged() noexcept; + void when_export_pool_selectionChanged() noexcept; + void when_version_buttons_toggled(bool) noexcept; + void when_type_buttons_toggled(bool) noexcept; + void when_blocklist_changed() noexcept; + void when_preset_clicked() noexcept; + void when_export_type_toggled() noexcept; + void when_algo_btn_clicked() noexcept; + + void on_pb_cvt_current_clicked() noexcept; + void on_pb_cvt_all_clicked() noexcept; + void on_pb_save_converted_clicked() noexcept; + + void on_pb_build3d_clicked() noexcept; + void on_pb_preview_materials_clicked() noexcept; + void on_pb_preview_compress_effect_clicked() noexcept; + void on_pb_export_all_clicked() noexcept; + // exports for data file + void on_pb_export_file_clicked() noexcept; + void on_pb_export_data_command_clicked() noexcept; + void on_pb_export_data_vanilla_structure_clicked() noexcept; + void when_data_file_command_changed() noexcept; + + private slots: + void on_ac_GAcvter_options_triggered() noexcept; + + void on_ac_cache_dir_open_triggered() noexcept; + void on_ac_clear_cache_triggered() noexcept; + + void on_ac_about_triggered() noexcept; + + void on_ac_get_current_colorlist_triggered() noexcept; + void on_ac_test_blocklist_triggered() noexcept; + + void on_ac_cache_all_3d_triggered() noexcept; + + void on_ac_memory_policy_triggered() noexcept; + + void on_ac_blocklist_triggered() noexcept; + + private: + Ui::SCWind* ui; + + std::unordered_map< + colortable_settings, + std::unique_ptr> + color_tables; + // SlopeCraft::Kernel* kernel; + + CvtPoolModel* cvt_pool_model{nullptr}; + ExportPoolModel* export_pool_model{nullptr}; + ExportTableModel* export_table_model{nullptr}; + + std::array default_presets; + + SCL_language language{SCL_language::Chinese}; + std::vector translators; + + QString prev_load_image_dir{""}; + + memory_policy mem_policy{}; + // QString fileonly_export_dir{""}; + + public: + SlopeCraft::GA_converter_option GA_option{}; + task_pool tasks; + + QString cache_root_dir() const noexcept; + SlopeCraft::color_table* current_color_table() noexcept; + SlopeCraft::convert_option current_convert_option() noexcept; + + SlopeCraft::ui_callbacks ui_callbacks() const noexcept; + + std::array version_buttons() noexcept; + std::array version_buttons() const noexcept; + + std::array type_buttons() noexcept; + std::array type_buttons() const noexcept; + + std::array export_type_buttons() noexcept; + std::array export_type_buttons() const noexcept; + std::vector valid_export_type_buttons( + SCL_mapTypes type) const noexcept; + + std::array preset_buttons_no_custom() noexcept; + + std::array algo_buttons() const noexcept; + std::array algo_buttons() noexcept; + + QProgressBar* current_bar() noexcept; + + SCL_gameVersion selected_version() const noexcept; + + SCL_mapTypes selected_type() const noexcept; + + std::vector selected_indices() const noexcept; + std::optional selected_cvt_task_idx() const noexcept; + + std::vector selected_export_task_list() const noexcept; + cvt_task* selected_export_task() const noexcept; + + SCL_convertAlgo selected_algo() const noexcept; + bool is_dither_selected() const noexcept; + + bool is_lossless_compression_selected() const noexcept; + bool is_lossy_compression_selected() const noexcept; + int current_max_height() const noexcept; + SCL_compressSettings current_compress_method() const noexcept; + + bool is_glass_bridge_selected() const noexcept; + int current_glass_brigde_interval() const noexcept; + SCL_glassBridgeSettings current_glass_method() const noexcept; + + bool is_fire_proof_selected() const noexcept; + bool is_enderman_proof_selected() const noexcept; + bool is_connect_mushroom_selected() const noexcept; + + SlopeCraft::build_options current_build_option() const noexcept; + + export_type selected_export_type() const noexcept; + + std::optional current_litematic_option( + QString& err) const noexcept; + std::optional current_nbt_option( + QString& err) const noexcept; + std::optional current_schem_option( + QString& err) const noexcept; + std::optional current_flatdiagram_option( + QString& err) const noexcept; + + int current_map_begin_seq_number() const noexcept; + + SlopeCraft::assembled_maps_options current_assembled_maps_option() + const noexcept; + + inline auto lang() const noexcept { return this->language; } + void set_lang(::SCL_language lang) noexcept; + + private: + // kernel related functions + void set_colorset() noexcept; + + void update_button_states() noexcept; + + [[nodiscard]] std::unique_ptr + convert_image(int idx) noexcept; + + [[nodiscard]] std::unique_ptr + convert_image(const cvt_task&) noexcept; + + [[nodiscard]] const SlopeCraft::converted_image& convert_if_need( + cvt_task&) noexcept; + + [[nodiscard]] std::tuple + convert_and_build_if_need(cvt_task&) noexcept; + + [[nodiscard]] std::unique_ptr + build_3D(const SlopeCraft::converted_image&) noexcept; + + std::tuple + load_selected_3D() noexcept; + + // [[deprecated]] void kernel_set_image(int idx) noexcept; + // [[deprecated]] void kernel_convert_image() noexcept; + // + // [[deprecated]] void kernel_make_cvt_cache() noexcept; + + // [[deprecated]] void kernel_build_3d() noexcept; + // [[deprecated]] void kernel_make_build_cache() noexcept; + + void refresh_current_cvt_display(std::optional idx) noexcept; + void refresh_current_cvt_display() noexcept { + this->refresh_current_cvt_display(this->selected_cvt_task_idx()); + } + + // [[deprecated]] QImage get_converted_image_from_kernel() const noexcept; + + void refresh_current_build_display(cvt_task* taskp) noexcept; + void refresh_current_build_display() noexcept { + this->refresh_current_build_display(this->selected_export_task()); + } + + // void mark_all_task_unconverted() noexcept; + + void export_current_cvted_image(int idx, QString filename) noexcept; + + void report_error(::SCL_errorFlag flag, const char* msg) noexcept; + + [[nodiscard]] bool should_auto_cache(bool suppress_warnings) noexcept; + + struct auto_cache_report { + size_t structures_cached; + int64_t memory_saved; + }; + auto_cache_report auto_cache_3D(bool cache_all = false) noexcept; + + [[nodiscard]] tl::expected get_command( + const SlopeCraft::converted_image&, int begin_idx) const noexcept; + + signals: + void image_changed(); +}; + +QString extension_of_export_type(SCWind::export_type) noexcept; + +#endif // SLOPECRAFT_SLOPECRAFT_SCWIND_H \ No newline at end of file diff --git a/SlopeCraft/SCWind.ui b/SlopeCraft/SCWind.ui new file mode 100644 index 00000000..65fc90d1 --- /dev/null +++ b/SlopeCraft/SCWind.ui @@ -0,0 +1,1834 @@ + + + SCWind + + + + 0 + 0 + 920 + 865 + + + + SlopeCraft + + + + + + + 0 + + + + 地图画配置 + + + + + + 方块列表预设 + + + + + + 可用方块数量: + + + + + + + 保存当前预设 + + + + + + + 加载预设 + + + + + + + 0 + + + + + Vanilla + + + + + + + Cheap + + + + + + + Elegant + + + true + + + + + + + Shiny + + + + + + + Custom + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 游戏版本 + + + + + + 1.13 + + + + + + + 1.18 + + + + + + + 1.17 + + + + + + + 1.19 + + + false + + + + + + + 1.15 + + + + + + + 1.12 + + + + + + + 1.16 + + + + + + + true + + + 1.20 + + + true + + + + + + + 1.14 + + + + + + + 1.21 + + + + + + + + + + 地图画类型 + + + + + + 立体地图画 + + + true + + + + + + + 平板地图画 + + + + + + + 纯文件地图画 + + + + + + + + + + 管理方块列表 + + + + + + + + 0 + 0 + + + + 默认只加载原版方块,点击上方按钮可加载其他方块列表。 + + + Qt::TextFormat::AutoText + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + + + 优先木板 + + + + + + + 全选 + + + + + + + 优先彩色玻璃 + + + + + + + 优先原木 + + + + + + + 优先混凝土 + + + + + + + 反选 + + + + + + + 优先台阶 + + + + + + + 优先羊毛 + + + + + + + 全不选 + + + + + + + 优先地毯 + + + + + + + 优先压力板 + + + + + + + + + true + + + + + 0 + 0 + 750 + 624 + + + + + + + + + + + 导入图像并转化 + + + + + + + 0 + 0 + + + + 全部转化 + + + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DragDropMode::DragDrop + + + Qt::DropAction::IgnoreAction + + + QAbstractItemView::SelectionMode::ExtendedSelection + + + Qt::TextElideMode::ElideLeft + + + QListView::Movement::Snap + + + QListView::Flow::TopToBottom + + + false + + + QListView::ResizeMode::Adjust + + + 10 + + + QListView::ViewMode::IconMode + + + 0 + + + true + + + true + + + Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + + + + + + + + 0 + 0 + + + + 删除 + + + + + + + 项目池 + + + + + + + + 0 + 0 + + + + 添加 + + + + + + + true + + + + 0 + 0 + + + + 显示缩略图 + + + true + + + + + + + + 0 + 0 + + + + 替换 + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + 保存转化后图像 + + + + + + + 转化算法 + + + + + + 抖动 + + + + + + + Lab00 + + + + + + + Lab94 + + + + + + + RGB+ + + + true + + + + + + + RGB + + + + + + + XYZ + + + + + + + GACvter + + + + + + + + + + + 0 + 0 + + + + 转化当前图像 + + + + + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 0 + + + + + + + + 500 + 400 + + + + QTabWidget::TabPosition::North + + + QTabWidget::TabShape::Rounded + + + 0 + + + Qt::TextElideMode::ElideLeft + + + false + + + false + + + false + + + + 原图 + + + + + + + 0 + 0 + + + + true + + + QFrame::Shape::NoFrame + + + 1 + + + + + + false + + + + + + + + 转化后 + + + + + + + 0 + 0 + + + + true + + + QFrame::Shape::NoFrame + + + 1 + + + + + + + + + + + + + + + 导出 + + + + + + 0 + + + 0 + + + + + 项目池 + + + + + + + false + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DragDropMode::NoDragDrop + + + Qt::DropAction::IgnoreAction + + + QAbstractItemView::SelectionMode::ExtendedSelection + + + Qt::TextElideMode::ElideLeft + + + QListView::Movement::Static + + + QListView::Flow::TopToBottom + + + false + + + QListView::ResizeMode::Adjust + + + QListView::LayoutMode::SinglePass + + + 10 + + + QListView::ViewMode::IconMode + + + 0 + + + true + + + true + + + Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + + + + + + + + + 0 + + + + + + + 0 + + + + + Litematica + + + true + + + + + + + 结构方块文件 + + + + + + + WE原理图 + + + + + + + false + + + 平面示意图 + + + + + + + 地图文件 + + + + + + + + + + 0 + 0 + + + + 0 + + + + + + + 其他选项 + + + + + + 防火 + + + + + + + 防末影人 + + + + + + + 连接蘑菇块 + + + true + + + + + + + + + + 压缩高度 + + + + + + 有损压缩 + + + true + + + + + + + 无损压缩 + + + + + + + + + + 最大允许高度: + + + 14 + + + 32767 + + + 256 + + + + + + + + + + 搭桥 + + + + + + 允许搭桥 + + + + + + + + + + 搭桥间隔: + + + 1 + + + 4096 + + + 5 + + + + + + + + + + + 0 + 0 + + + + 0 + + + + Litematica + + + + + + + + + 投影区域名称 + + + + + + + 投影名称 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 结构方块文件 + + + + + + 用结构空位替代空气 + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + WE原理图 + + + + + + offset + + + + + + + + 0 + 0 + + + + + + + + WEOffset + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + 原理图名称 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + 依赖 mod 名称 + + + + + + + + 0 + 0 + + + + 在这里输入依赖 mod 的名字。用换行符分割多个 mod + + + + + + + + 平面示意图 + + + + + + 方块 + + + 间距: + + + 1 + + + 65536 + + + 16 + + + + + + + 垂直分割线 + + + true + + + + + + + 方块 + + + 间距: + + + 1 + + + 65536 + + + 16 + + + + + + + 水平分割线 + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + 大小与方块数量 + + + + + + + 0 + 0 + + + + 大小: + + + + + + + + 0 + 0 + + + + 方块数量: + + + + + + + + + + 0 + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + 全部导出 + + + + + + + 预览 + + + + + + 预览材料表 + + + + + + + 预览压缩效果 + + + + + + + 预构建三维结构 + + + + + + + + + + + + + + + + true + + + true + + + + + + + + 0 + 0 + + + + + + + true + + + + + + 批量获得地图物品的指令 + + + + + + + + + + others/images/map_icon.png + + + + + + + 💡tips: 设置地图画起始序号后,点击“导出”,选择输出地图数据文件的位置,就完成导出任务了。 + +在下面的表格里可以看到每个图像对应的文件名。 + +图像数据存储在地图数据文件中,必须先为存档导入地图数据文件,地图画结构才能生效。 + + + true + + + + + + + 0 + + + + + + 0 + 0 + + + + 地图画起始序号: + + + 65536 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + 20 + + + + + 导出批量获得地图物品的命令(txt) + + + 导出命令 + + + + + + + 生存模式不可破坏展示框;移除它依附的方块,展示框也不会消失。 + + + 展示框不可破坏 + + + true + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 1.20.5后,物品格式发生重大改变 + + + MC版本≥1.20.5+ + + + + + + + 采用荧光物品显示框 + + + 荧光物品显示框 + + + + + + + 导出包含物品展示框的投影/结构方块文件 + + + 导出组装的地图画 + + + + + + + 导出map_i.dat的地图数据文件 + + + 导出地图文件 + + + + + + + 展示框背景将不可见 + + + 展示框透明 + + + + + + + 地图画可依附于方块的侧面、顶面和底面 + + + 13 + + + 地图画方向 + + + + + + + + + + 导出组装地图画的格式 + + + + 导出Litematica + + + + + 导出结构方块文件 + + + + + + + + + + + + + + + + + + + + 0 + 0 + 920 + 33 + + + + + 语言 + + + + + + + 高级 + + + + 缓存 + + + + + + + + + + + + + + + 关于 + + + + 联系作者 + + + + + + + + + + + + 帮助 + + + + + + + + + + + + + GA转化器参数 + + + + + open_cache_dir + + + + + 打开缓存文件夹 + + + + + 清除缓存 + + + + + 简体中文 + + + 简体中文 + + + + + English + + + English + + + + + 关于 SlopeCraft + + + + + Github + + + Github + + + Github + + + + + Bilibili + + + Bilibili + + + Bilibili + + + + + 反馈 bug + + + + + 检查更新 + + + + + 测试方块列表 + + + + + 输出当前颜色表 + + + + + 查看 SlopeCraft 文档 + + + + + 使用教程 + + + + + 常见问题 + + + + + 内存使用策略 + + + + + 缓存全部三维结构 + + + + + 查看方块列表 + + + + + + AdaptiveLabel + QLabel +
AdaptiveLabel.h
+
+ + AdaptiveListView + QListView +
AdaptiveListView.h
+
+ + BlockListManager + QWidget +
BlockListManager.h
+ 1 +
+
+ + +
diff --git a/SlopeCraft/SCWind_slots.cpp b/SlopeCraft/SCWind_slots.cpp new file mode 100644 index 00000000..b1cf8789 --- /dev/null +++ b/SlopeCraft/SCWind_slots.cpp @@ -0,0 +1,1202 @@ +#include "SCWind.h" +#include "ui_SCWind.h" +#include +#include +#include +#include +#include +#include + +#include + +#include "PreviewWind.h" +#include "AiCvterParameterDialog.h" +#include "VersionDialog.h" +#include "TransparentStrategyWind.h" +#include "CompressEffectViewer.h" +#include "BlockListDialog.h" +#include "stat_memory.h" + +#ifdef WIN32 +const char *SC_image_filter = "*.png;*.jpg"; +#else +const char *SC_image_filter = "*.png;;*.jpg"; +#endif + +void SCWind::on_pb_add_image_clicked() noexcept { + auto files = QFileDialog::getOpenFileNames( + this, tr("选择图片"), this->prev_load_image_dir, SC_image_filter); + + if (files.empty()) { + return; + } + this->prev_load_image_dir = QFileInfo{files.front()}.dir().absolutePath(); + + std::optional strategy_opt{std::nullopt}; + + for (const auto &filename : files) { + auto task_res = cvt_task::load(filename); + + if (!task_res) { + auto ret [[maybe_unused]] = QMessageBox::critical( + this, tr("打开图像失败"), + tr("无法打开图像 %1。\n详细信息: %2") + .arg(filename, task_res.error()), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}); + + continue; + } + + auto task = std::move(task_res.value()); + // have transparent pixels + if (SlopeCraft::SCL_haveTransparentPixel( + (const uint32_t *)task.original_image.scanLine(0), + task.original_image.sizeInBytes() / sizeof(uint32_t))) { + if (!strategy_opt.has_value()) { + strategy_opt = TransparentStrategyWind::ask_for_strategy(this); + } + + if (!strategy_opt.has_value()) { + continue; + } + const auto &st = strategy_opt.value(); + SlopeCraft::SCL_preprocessImage( + (uint32_t *)task.original_image.scanLine(0), + task.original_image.sizeInBytes() / sizeof(uint32_t), + st.pure_transparent, st.half_transparent, st.background_color); + } + + this->tasks.emplace_back(std::move(task)); + } + + emit this->image_changed(); + if (this->ui->lview_pool_cvt->viewMode() == QListView::ViewMode::IconMode) { + this->ui->lview_pool_cvt->doItemsLayout(); + } +} + +void SCWind::on_pb_remove_image_clicked() noexcept { + auto selected = this->ui->lview_pool_cvt->selectionModel()->selectedIndexes(); + + if (selected.empty()) { + return; + } + + std::vector row; + { + row.reserve(selected.size()); + for (const auto &qmi : selected) { + row.emplace_back(qmi.row()); + } + std::sort(row.begin(), row.end(), [](int a, int b) { return a > b; }); + } + + for (const int idx : row) { + this->tasks.erase(this->tasks.begin() + idx); + } + + emit this->image_changed(); +} + +void SCWind::on_pb_replace_image_clicked() noexcept { + auto selected = this->ui->lview_pool_cvt->selectionModel()->selectedIndexes(); + if (selected.empty()) { + QMessageBox::warning(this, tr("请选择将被替换的图像"), + tr("必须先选择一个或多个图像,然后才能替换它们。")); + return; + } + + const QString file = QFileDialog::getOpenFileName( + this, tr("选择图片"), this->prev_load_image_dir, SC_image_filter); + if (file.isEmpty()) { + return; + } + this->prev_load_image_dir = QFileInfo{file}.dir().absolutePath(); + + auto task_res = cvt_task::load(file); + if (!task_res) { + auto ret[[maybe_unused]] = QMessageBox::critical( + this, tr("打开图像失败"), + tr("无法打开图像 %1。常见原因:图像尺寸太大。\n详细信息: %2") + .arg(file, task_res.error()), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, + QMessageBox::StandardButton::Ignore}); + + return; + } + for (const auto &qmi : selected) { + this->tasks[qmi.row()] = std::move(task_res.value()); + } + this->cvt_pool_model->refresh(); +} + +void SCWind::on_cb_lv_cvt_icon_mode_clicked() noexcept { + const bool is_icon_mode = this->ui->cb_lv_cvt_icon_mode->isChecked(); + this->ui->lview_pool_cvt->setViewMode((is_icon_mode) + ? (QListView::ViewMode::IconMode) + : (QListView::ViewMode::ListMode)); + + this->ui->lview_pool_cvt->setFlow(QListView::Flow::TopToBottom); + + this->ui->lview_pool_cvt->setSpacing(is_icon_mode ? 16 : 0); + + this->cvt_pool_model->refresh(); +} + +void SCWind::on_pb_load_preset_clicked() noexcept { + static QString prev_dir{""}; + QString file = QFileDialog::getOpenFileName(this, tr("选择预设文件"), + prev_dir, "*.sc_preset_json"); + + if (file.isEmpty()) { + return; + } + + prev_dir = QFileInfo{file}.dir().canonicalPath(); + QString err; + blockListPreset preset = load_preset(file, err); + if (!err.isEmpty()) { + QMessageBox::warning(this, tr("解析预设文件失败"), + tr("预设文件%1存在错误:%2").arg(file, err)); + return; + } + + this->ui->blm->loadPreset(preset); +} + +void SCWind::on_pb_save_preset_clicked() noexcept { + static QString prev_dir{""}; + QString file = QFileDialog::getSaveFileName(this, tr("保存当前预设"), + prev_dir, "*.sc_preset_json"); + + if (file.isEmpty()) { + return; + } + + prev_dir = QFileInfo{file}.dir().canonicalPath(); + + blockListPreset preset = this->ui->blm->to_preset(); + { + QString str = serialize_preset(preset); + + QFile qf{file}; + qf.open(QFile::OpenMode{QIODevice::WriteOnly | QIODevice::Text}); + + if (!qf.isOpen()) { + QMessageBox::warning( + this, tr("保存预设文件失败"), + tr("无法生成预设文件%1,错误信息:%2").arg(file, qf.errorString())); + return; + } + + qf.write(str.toUtf8()); + qf.close(); + } +} + +inline int impl_select_blk_by_id( + const std::vector &blks, + std::string_view keyword) noexcept { + int result = -1; + for (int idx = 0; idx < int(blks.size()); idx++) { + std::string_view id{blks[idx]->getId()}; + const auto find = id.find(keyword); + + if (find != std::string_view::npos) { + result = idx; + break; + } + } + + return result; +} + +void SCWind::on_pb_prefer_concrete_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "concrete"); }); +} + +void SCWind::on_pb_prefer_wool_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "wool"); }); +} + +void SCWind::on_pb_prefer_glass_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "stained_glass"); }); +} + +void SCWind::on_pb_prefer_planks_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "planks"); }); +} + +void SCWind::on_pb_prefer_logs_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "log"); }); +} + +void SCWind::on_pb_prefer_slabs_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "_slab"); }); +} + +void SCWind::on_pb_prefer_carpets_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "_carpet"); }); +} + +void SCWind::on_pb_prefer_pressure_plates_clicked() noexcept { + this->ui->blm->select_block_by_callback( + [](const std::vector &blks) + -> int { return impl_select_blk_by_id(blks, "_pressure_plate"); }); +} + +void SCWind::on_pb_select_all_clicked() noexcept { + for (size_t bc = 0; bc < this->ui->blm->num_basecolor_widgets(); bc++) { + this->ui->blm->basecolorwidget_at(bc)->set_enabled(true); + } +} +void SCWind::on_pb_deselect_all_clicked() noexcept { + for (size_t bc = 1; bc < this->ui->blm->num_basecolor_widgets(); bc++) { + this->ui->blm->basecolorwidget_at(bc)->set_enabled(false); + } +} +void SCWind::on_pb_invselect_clicked() noexcept { + for (size_t bc = 1; bc < this->ui->blm->num_basecolor_widgets(); bc++) { + BaseColorWidget *bcw = this->ui->blm->basecolorwidget_at(bc); + assert(bcw not_eq nullptr); + bcw->set_enabled(not(bcw->is_enabled())); + } +} + +void SCWind::on_pb_cvt_current_clicked() noexcept { + const auto sel = this->selected_cvt_task_idx(); + + if (!sel.has_value()) { + QMessageBox::warning(this, tr("未选择图像"), + tr("请在左侧任务池选择一个图像")); + return; + } + + { + auto cvted = this->convert_image(sel.value()); + if (!cvted) { + return; + } + this->tasks[sel.value()].set_converted(this->current_color_table(), + this->current_convert_option(), + std::move(cvted)); + } + + // this->kernel_make_cvt_cache(); + this->refresh_current_cvt_display(sel.value()); + this->ui->tw_cvt_image->setCurrentIndex(1); + + emit this->image_changed(); + // this->ui-> +} + +void SCWind::on_pb_cvt_all_clicked() noexcept { + for (int idx = 0; idx < (int)this->tasks.size(); idx++) { + auto &task = this->tasks[idx]; + if (task.is_converted_with(this->current_color_table(), + this->current_convert_option())) { + continue; + } + auto cvted = this->convert_image(idx); + task.set_converted(this->current_color_table(), + this->current_convert_option(), std::move(cvted)); + } + emit this->image_changed(); +} + +void SCWind::on_pb_save_converted_clicked() noexcept { + const auto selected = this->selected_indices(); + if (selected.size() <= 0) { + QMessageBox::warning(this, tr("未选择图像"), + tr("请在左侧任务池选择一个或多个图像")); + return; + } + static QString prev_dir{""}; + if (selected.size() == 1) { + QString filename = QFileDialog::getSaveFileName(this, tr("保存转化后图像"), + prev_dir, "*.png;*.jpg"); + if (filename.isEmpty()) { + return; + } + prev_dir = QFileInfo{filename}.dir().dirName(); + + const int idx = selected.front(); + + this->export_current_cvted_image(idx, filename); + return; + } + + QString out_dir = + QFileDialog::getExistingDirectory(this, tr("保存转化后图像"), prev_dir); + if (out_dir.isEmpty()) { + return; + } + prev_dir = out_dir; + + bool yes_to_all_replace_existing{false}; + bool no_to_all_replace_existing{false}; + for (int idx : selected) { + auto &task = this->tasks[idx]; + QString filename = QStringLiteral("%1/%2.png") + .arg(out_dir, QFileInfo{task.filename}.baseName()); + + if (QFile{filename}.exists()) { + if (no_to_all_replace_existing) { + continue; + } + if (yes_to_all_replace_existing) { + goto save_image; + } + + const auto ret = QMessageBox::warning( + this, tr("将要覆盖已存在的图像"), + tr("%1将被覆盖,确认覆盖吗?").arg(filename), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Yes, + QMessageBox::StandardButton::No, + QMessageBox::StandardButton::YesToAll, + QMessageBox::StandardButton::NoToAll}); + + bool replace; + switch (ret) { + case QMessageBox::StandardButton::Yes: + replace = true; + break; + case QMessageBox::StandardButton::YesToAll: + replace = true; + yes_to_all_replace_existing = true; + break; + case QMessageBox::StandardButton::NoToAll: + replace = false; + no_to_all_replace_existing = true; + break; + default: + replace = false; + break; + } + + if (not replace) { + continue; + } + } + + save_image: + + this->export_current_cvted_image(idx, filename); + } +} + +void SCWind::on_cb_compress_lossy_toggled(bool checked) noexcept { + this->ui->sb_max_height->setEnabled(checked); +} + +void SCWind::on_pb_build3d_clicked() noexcept { + if (this->should_auto_cache(false)) { + [[maybe_unused]] const auto report = this->auto_cache_3D(); + ; + } + + auto task_ptr = this->selected_export_task(); + if (task_ptr == nullptr) { + QMessageBox::warning(this, tr("未选择图像"), + tr("请在左侧任务池选择一个图像")); + return; + } + assert(task_ptr != nullptr); + + cvt_task &task = *task_ptr; + + if (!task.is_converted_with(this->current_color_table(), + this->current_convert_option())) [[unlikely]] { + QMessageBox::warning(this, tr("该图像尚未被转化"), + tr("必须先转化一个图像,然后再为它构建三维结构")); + return; + } + + const int gidx = task_ptr - this->tasks.data(); + auto table = this->current_color_table(); + const auto cvt_option = this->current_convert_option(); + if (!task.is_converted_with(table, cvt_option)) { + task.set_converted(table, cvt_option, this->convert_image(gidx)); + } + auto &cvted = + task.converted_images.find(convert_input{table, cvt_option})->second; + // this->kernel_set_image(gidx); + // if (!this->kernel->loadConvertCache(this->selected_algo(), + // this->is_dither_selected())) { + // this->kernel_convert_image(); + // } + + const auto build_option = this->current_build_option(); + if (!cvted.is_built_with(build_option)) { + auto structure = this->build_3D(*cvted.converted_image); + if (structure not_eq nullptr) { + cvted.set_built(build_option, std::move(structure)); + } + } + // load cache if converted + this->refresh_current_build_display(&task); +} + +void SCWind::on_pb_preview_materials_clicked() noexcept { + if (this->should_auto_cache(false)) { + this->auto_cache_3D(); + } + + auto [cvted_img, structure_3D] = this->load_selected_3D(); + if (cvted_img == nullptr || structure_3D == nullptr) { + return; + } + + PreviewWind *pw = new PreviewWind{this}; + + pw->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); + pw->setAttribute(Qt::WidgetAttribute::WA_AlwaysStackOnTop, true); + // bb->setAttribute(Qt::WidgetAttribute::WA_NativeWindow, true); + pw->setWindowFlag(Qt::WindowType::Window, true); + + // connect(this, &VCWind::signal_allowed_colorset_changed, pw, + // &QWidget::deleteLater); + + pw->show(); + + pw->setup_data(*this->current_color_table(), *structure_3D); +} + +void SCWind::on_pb_preview_compress_effect_clicked() noexcept { + if (this->should_auto_cache(false)) { + this->auto_cache_3D(); + } + + auto [cvted_img, structure_3D] = this->load_selected_3D(); + if (cvted_img == nullptr || structure_3D == nullptr) { + return; + } + + CompressEffectViewer *cev = + new CompressEffectViewer{this, *cvted_img, *structure_3D}; + + cev->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); + cev->setWindowFlag(Qt::WindowType::Window, true); + // cev->setAttribute(Qt::WidgetAttribute::WA_AlwaysStackOnTop, true); + // bb->setAttribute(Qt::WidgetAttribute::WA_NativeWindow, true); + cev->show(); +} + +#define SC_PRIVATE_MACRO_PROCESS_IF_ERR_on_pb_export_all_clicked \ + if (!temp.has_value()) { \ + process_err(err); \ + return; \ + } + +#define SC_PRIVATE_MARCO_PROCESS_EXPORT_ERROR_on_pb_export_all_clicked \ + if (report_err_fun(export_name, taskp) == \ + QMessageBox::StandardButton::Ignore) { \ + continue; \ + } else { \ + return; \ + } + +void SCWind::on_pb_export_all_clicked() noexcept { + { + auto ctable = this->current_color_table(); + if (ctable == nullptr) { + QMessageBox::critical( + this, tr("没有可用颜色"), + tr("没有勾选任何颜色,无法转化图像。请至少勾选3~16种颜色。")); + return; + } + } + + auto tasks_to_export = this->selected_export_task_list(); + + const auto export_type = this->selected_export_type(); + SlopeCraft::litematic_options opt_lite; + SlopeCraft::vanilla_structure_options opt_nbt; + SlopeCraft::WE_schem_options opt_WE; + SlopeCraft::flag_diagram_options opt_fd; + { + QString err; + auto process_err = [this](const QString &err) { + QMessageBox::warning(this, tr("导出设置有错"), + tr("导出设置存在如下错误:\n%1").arg(err)); + }; + switch (export_type) { + case export_type::litematica: { + auto temp = this->current_litematic_option(err); + SC_PRIVATE_MACRO_PROCESS_IF_ERR_on_pb_export_all_clicked; + opt_lite = temp.value(); + break; + } + case export_type::vanilla_structure: { + auto temp = this->current_nbt_option(err); + SC_PRIVATE_MACRO_PROCESS_IF_ERR_on_pb_export_all_clicked; + opt_nbt = temp.value(); + break; + } + case export_type::WE_schem: { + auto temp = this->current_schem_option(err); + SC_PRIVATE_MACRO_PROCESS_IF_ERR_on_pb_export_all_clicked; + opt_WE = temp.value(); + break; + } + case export_type::flat_diagram: { + auto temp = this->current_flatdiagram_option(err); + SC_PRIVATE_MACRO_PROCESS_IF_ERR_on_pb_export_all_clicked; + opt_fd = temp.value(); + break; + } + default: + QMessageBox::warning(this, tr("你点错按钮了"), + tr("导出为纯文件地图画的按钮在另外一页。按理来说" + "你不应该能点击这个" + "按钮,这可能是一个小小的 bug(特性)。")); + return; + } + } + + if (tasks_to_export.empty()) { + QMessageBox::warning(this, tr("无可导出的任务"), + tr("任务池为空,请先转化一个或一些图像")); + return; + } + QString dir; + { + static QString prev_dir{""}; + dir = QFileDialog::getExistingDirectory(this, tr("选择导出位置"), prev_dir, + QFileDialog::Options{}); + if (dir.isEmpty()) { + return; + } + prev_dir = dir; + } + + auto get_export_name = [dir, this](const cvt_task &t) -> QString { + return QStringLiteral("%1/%2.%3") + .arg(dir, QFileInfo{t.filename}.baseName(), + extension_of_export_type(this->selected_export_type())); + }; + + // warn if some files will be covered + { + QString warning_list{}; + + for (auto taskp : tasks_to_export) { + QString filename = get_export_name(*taskp); + if (QFile{filename}.exists()) { + if (warning_list.isEmpty()) { + warning_list = filename; + } else { + warning_list.append('\n'); + warning_list.append(filename); + } + } + } + + if (!warning_list.isEmpty()) { + auto ret = QMessageBox::warning( + this, tr("将要覆盖已经存在的文件"), + tr("确定要覆盖这些文件吗?以下文件将被覆盖:\n%1").arg(warning_list), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Yes, + QMessageBox::StandardButton::No}); + if (ret != QMessageBox::StandardButton::Yes) { + return; + } + } + } + + for (auto taskp : tasks_to_export) { + if (this->should_auto_cache(false)) { + this->auto_cache_3D(); + } + + auto [cvted, str3D] = this->convert_and_build_if_need(*taskp); + + const std::string export_name = + get_export_name(*taskp).toLocal8Bit().data(); + + auto report_err_fun = [this](std::string_view export_name, + const cvt_task *taskp) { + return QMessageBox::warning( + this, tr("导出失败"), + tr("导出%1时失败。原图像文件名为%" + "2\n点击 Ignore 将跳过这个图像,点击 Cancel 将放弃导出任务。") + .arg(export_name.data(), taskp->filename), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, + QMessageBox::StandardButton::Cancel}); + }; + + switch (export_type) { + case export_type::litematica: + if (!str3D.export_litematica(export_name.c_str(), opt_lite)) { + SC_PRIVATE_MARCO_PROCESS_EXPORT_ERROR_on_pb_export_all_clicked; + } + break; + + case export_type::vanilla_structure: + if (!str3D.export_vanilla_structure(export_name.c_str(), opt_nbt)) { + SC_PRIVATE_MARCO_PROCESS_EXPORT_ERROR_on_pb_export_all_clicked; + } + break; + case export_type::WE_schem: + if (!str3D.export_WE_schem(export_name.c_str(), opt_WE)) { + SC_PRIVATE_MARCO_PROCESS_EXPORT_ERROR_on_pb_export_all_clicked; + } + break; + case export_type::flat_diagram: + if (!str3D.export_flat_diagram(export_name.c_str(), + *this->current_color_table(), opt_fd)) { + SC_PRIVATE_MARCO_PROCESS_EXPORT_ERROR_on_pb_export_all_clicked; + } + break; + default: + return; + } + } +} + +void SCWind::on_pb_export_file_clicked() noexcept { + static QString prev_dir{""}; + const QString dir = + QFileDialog::getExistingDirectory(this, tr("设置导出位置"), prev_dir); + + if (dir.isEmpty()) { + return; + } + prev_dir = dir; + + const int seq_first = this->current_map_begin_seq_number(); + { + int exisiting_num = 0; + QString to_be_replaced{""}; + to_be_replaced.reserve(4096); + + const int seq_last = + this->tasks.map_range_of(seq_first, this->tasks.size() - 1).last; + + for (int seq = seq_first; seq <= seq_last; seq++) { + QString filename = map_data_filename(dir, seq); + if (QFile{filename}.exists()) { + if (exisiting_num > 0) { + to_be_replaced.push_back('\n'); + } + to_be_replaced.append(filename); + exisiting_num++; + } + } + + if (exisiting_num > 0) { + QMessageBox msg_box{this}; + { + msg_box.setStandardButtons(QMessageBox::StandardButtons{ + QMessageBox::StandardButton::Yes, QMessageBox::StandardButton::No}); + msg_box.setWindowTitle(tr("%1 个文件将被替换").arg(exisiting_num)); + msg_box.setText( + tr("%1 个文件将被替换。点击 Show Details 可以查看它们。\n点击 Yes " + "将替换它们,点击 No 将取消这次导出。") + .arg(exisiting_num)); + msg_box.setDetailedText(to_be_replaced); + // Click the button "Show Detail". If not found, let it go + for (QAbstractButton *abtn : msg_box.buttons()) { + QPushButton *btn = dynamic_cast(abtn); + if (not btn) [[unlikely]] { + continue; + } + const auto str = btn->text().toLower(); + if (str.contains("detail")) { + emit btn->click(); + break; + } + } + } + const auto ret = msg_box.exec(); + + if (ret != QMessageBox::StandardButton::Yes) { + return; + } + } + } + + size_t fail_count = 0; + QString fail_tasks = ""; + for (int idx = 0; idx < int(this->tasks.size()); idx++) { + auto &task = this->tasks.at(idx); + auto &cvted_img = this->convert_if_need(task); + // this->kernel_set_image(idx); + // bool need_to_convert{true}; + // if (task.is_converted()) { + // if (this->kernel->loadConvertCache(this->selected_algo(), + // this->is_dither_selected())) { + // need_to_convert = false; + // } + // } + // + // if (need_to_convert) { + // this->kernel_convert_image(); + // } + // + const int cur_seq_beg = this->tasks.map_range_of(seq_first, idx).first; + const auto dir_name = dir.toLocal8Bit(); + // + const SlopeCraft::map_data_file_options option{ + .caller_api_version = SC_VERSION_U64, + .folder_path = dir_name.data(), + .begin_index = cur_seq_beg, + .progress = progress_callback(this->ui->pbar_export), + .ui = this->ui_callbacks(), + }; + const bool ok = cvted_img.export_map_data(option); + if (!ok) { + fail_count++; + fail_tasks.append(task.filename); + fail_tasks.push_back('\n'); + } + } + + if (fail_count > 0) { + QMessageBox::warning(this, tr("%1 个图片导出失败").arg(fail_count), + tr("导出失败的图片依次为:\n%1").arg(fail_tasks)); + } +} + +void SCWind::on_ac_GAcvter_options_triggered() noexcept { + AiCvterParameterDialog *acpd = new AiCvterParameterDialog{this}; + + acpd->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); + acpd->setAttribute(Qt::WidgetAttribute::WA_AlwaysStackOnTop, true); + acpd->setWindowFlag(Qt::WindowType::Window, true); + // bb->setAttribute(Qt::WidgetAttribute::WA_NativeWindow, true); + acpd->show(); +} + +void SCWind::on_ac_cache_dir_open_triggered() noexcept { + auto cache_dir = this->cache_root_dir(); + QDesktopServices::openUrl(QUrl::fromLocalFile(cache_dir)); +} + +void SCWind::on_ac_clear_cache_triggered() noexcept { + const QString cache_dir_name = this->cache_root_dir(); + QDir cache_dir{cache_dir_name}; + + if (!cache_dir.exists()) { + return; + } + + emit this->image_changed(); + + const auto entries = cache_dir.entryList(); + + auto remove_cache_fun = [](QString filename) -> bool { + if (QFileInfo{filename}.isFile()) { + return QFile{filename}.remove(); + } else { + return QDir{filename}.removeRecursively(); + } + }; + + for (const auto &name : entries) { + if (name == "." || name == "..") { + continue; + } + + QString filename = QStringLiteral("%1/%2").arg(cache_dir_name, name); + + while (true) { + if (remove_cache_fun(filename)) { + break; + } + + const auto ret = QMessageBox::warning( + this, tr("删除缓存失败"), + tr("无法删除文件或文件夹\"%1\"。\n点击 Ignore 以跳过,点击 Retry " + "以重试,点击 Cancel 以取消这次操作") + .arg(filename), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, + QMessageBox::StandardButton::Retry, + QMessageBox::StandardButton::Cancel}); + + if (ret == QMessageBox::StandardButton::Retry) { + continue; + } + if (ret == QMessageBox::StandardButton::Ignore) { + break; + } + return; // QMessageBox::StandardButton::Cancel and for closing the + // dialog + } + } +} + +void SCWind::connect_slots() noexcept { + // ac_contact_Github + + connect(this->ui->ac_lang_ZH, &QAction::triggered, + [this]() { this->set_lang(::SCL_language::Chinese); }); + connect(this->ui->ac_lang_EN, &QAction::triggered, + [this]() { this->set_lang(::SCL_language::English); }); + + connect(this->ui->ac_contact_Github, &QAction::triggered, []() { + QDesktopServices::openUrl(QUrl("https://github.com/SlopeCraft/SlopeCraft")); + }); + connect(this->ui->ac_contact_Bilibili, &QAction::triggered, []() { + QDesktopServices::openUrl(QUrl("https://space.bilibili.com/351429231")); + }); + connect(this->ui->ac_report_bugs, &QAction::triggered, []() { + QDesktopServices::openUrl( + QUrl("https://github.com/SlopeCraft/SlopeCraft/issues/new/choose")); + }); + // ac_check_updates + + connect(this->ui->ac_tutorial, &QAction::triggered, []() { + QDesktopServices::openUrl(QUrl("https://slopecraft.readthedocs.io/")); + }); + connect(this->ui->ac_faq, &QAction::triggered, []() { + QDesktopServices::openUrl(QUrl("https://slopecraft.readthedocs.io/faq/")); + }); + + connect(this->ui->ac_check_updates, &QAction::triggered, [this]() { + VersionDialog::start_network_request(this, "SlopeCraft", + QUrl{SCWind::update_url}, + SCWind::network_manager(), true); + }); +} + +void SCWind::on_ac_about_triggered() noexcept { + QString info; + info += QStringLiteral("SlopeCraft %1").arg(SlopeCraft::SCL_getSCLVersion()); + info += "\n\n"; + info += + tr("SlopeCraft 是一款由 ToKiNoBug 开发的立体地图画生成器,主要用于" + "在 Minecraft " + "中制造可以生存实装的立体地图画(但同样支持传统的平板地图画)。"); + info += "\n"; + info += + tr("本软件的开发持续集成与 macOS 软件适配由 iXOR Technology (Cubik65536 " + "以及贡献者) 提供"); + info += "\n\n"; + info += tr("感谢 AbrasiveBoar902 为本软件的设计和优化贡献的力量"); + info += "\n"; + info += tr("感谢 67au 为本软件的 macOS 与 Linux 适配做出的贡献"); + info += "\n"; + info += + tr("SlopeCraft 在开发时使用了 Qt,zlib 和 eigen " + "等开源库,对上述库的开发者表示感谢。"); + info += "\n\n"; + info += tr("本软件遵循 GPL-3.0 及以后版本 (GPL-3.0 or later) 协议开放源码。"); + info += "\n\n"; + info += + tr("Copyright © 2021-2026 SlopeCraft 开发者 (TokiNoBug, AbrasiveBoar, " + "iXOR Technology, Mifan-T, 以及贡献者). 版权所有"); + QMessageBox::information(this, tr("关于 SlopeCraft"), info); +} + +void SCWind::on_ac_get_current_colorlist_triggered() noexcept { + constexpr int basecolors_per_row = 4; + constexpr int basecolors_per_col = 16; + + static_assert(basecolors_per_row * basecolors_per_col == 64); + + constexpr int row_pixels = basecolors_per_row * 4; + constexpr int col_pixels = basecolors_per_col * 1; + + static_assert(row_pixels * col_pixels == 256); + static QString prev_dir{""}; + const QString dest_file = + QFileDialog::getSaveFileName(this, tr("保存颜色表"), prev_dir, "*.png"); + + if (dest_file.isEmpty()) { + return; + } + + prev_dir = QFileInfo{dest_file}.dir().path(); + + QImage img(row_pixels, col_pixels, QImage::Format::Format_ARGB32); + + if (img.isNull()) { + QMessageBox::warning(this, tr("保存颜色表失败"), tr("分配内存失败")); + return; + } + + img.fill(0x00FFFFFFU); + + uint32_t *const img_data = reinterpret_cast(img.scanLine(0)); + /* + Eigen::Map< + Eigen::Array> + map(reinterpret_cast(img.scanLine(0)), row_pixels, + col_pixels); +*/ + // uint32_t argb_colors[256]; + // uint8_t map_colors[256]; + const auto color_ptrs = this->current_color_table()->colors(); + const int available_colors = color_ptrs.num_colors; + + for (int cidx = 0; cidx < available_colors; cidx++) { + /* + const int basecolor = (map_colors[cidx] / 4); + const int shade = (map_colors[cidx] % 4); + const int pixel_row = basecolor / basecolors_per_col; + const int pixel_col = (basecolor % basecolors_per_col) * 4 + shade; + */ + const uint32_t argb = + QColor{static_cast(color_ptrs.r_data[cidx] * 255), + static_cast(color_ptrs.g_data[cidx] * 255), + static_cast(color_ptrs.b_data[cidx] * 255)} + .rgb(); + + img_data[color_ptrs.map_data[cidx]] = argb; + // map(map_colors[cidx]) = argb_colors[cidx]; + } + + if (!img.save(dest_file)) { + QMessageBox::warning(this, tr("保存颜色表失败"), + tr("无法生成文件 %1").arg(dest_file)); + } +} + +void SCWind::on_ac_test_blocklist_triggered() noexcept { + static QString prev_dir; + QString filename = + QFileDialog::getSaveFileName(this, tr("保存测试文件"), prev_dir, "*.nbt"); + if (filename.isEmpty()) { + return; + } + prev_dir = QFileInfo{filename}.dir().path(); + + std::vector blks; + std::vector basecolors; + for (uint8_t basecolor = 0; basecolor <= SlopeCraft::SCL_maxBaseColor(); + basecolor++) { + const auto bcwp = this->ui->blm->basecolorwidget_at(basecolor); + for (const auto &bwp : bcwp->block_widgets()) { + blks.emplace_back(bwp->attached_block()); + basecolors.emplace_back(basecolor); + } + } + + assert(blks.size() == basecolors.size()); + + std::string err; + err.resize(4096); + auto sd = SlopeCraft::string_deliver::from_string(err); + + SlopeCraft::test_blocklist_options opt; + opt.block_count = blks.size(); + opt.block_ptrs = blks.data(); + opt.basecolors = basecolors.data(); + opt.err = &sd; + + if (!this->current_color_table()->generate_test_schematic( + filename.toLocal8Bit().data(), opt)) { + QString qerr = QString::fromUtf8(err.data()); + QMessageBox::warning( + this, tr("输出测试文件失败"), + tr("保存测试文件 %1 时出现错误。详细信息:\n%2").arg(filename, qerr)); + } +} + +void SCWind::on_ac_cache_all_3d_triggered() noexcept { + this->auto_cache_3D(true); +} + +void SCWind::on_ac_memory_policy_triggered() noexcept { + MemoryPolicyDialog diag{this, this->mem_policy}; + const auto result = diag.exec(); + + if (result) { + this->mem_policy = diag.current_value(); + this->auto_cache_3D(); + } +} + +void SCWind::when_data_file_command_changed() noexcept { + const auto export_tasks = this->selected_export_task_list(); + if (export_tasks.empty()) { + this->ui->pte_command->clear(); + return; + } + if (export_tasks.size() >= 2) { + this->ui->pte_command->setPlainText( + tr("同时选中多个图片时,不显示 /give " + "命令。如果想预览导出的命令,请只选择一个图片。")); + return; + } + const auto task = export_tasks.front(); + if (task == nullptr) { + this->ui->pte_command->clear(); + return; + } + const ptrdiff_t global_index = task - this->tasks.data(); + assert(global_index >= 0 and global_index < this->tasks.size()); + + auto it = task->converted_images.find( + {this->current_color_table(), this->current_convert_option()}); + if (it == task->converted_images.end() or + it->second.converted_image == nullptr) { + this->ui->pte_command->clear(); + return; + } + const SlopeCraft::converted_image &cvted = *it->second.converted_image; + const auto range = this->tasks.map_range_of( + this->ui->sb_file_start_idx->value(), global_index); + + auto command_res = this->get_command(cvted, range.first); + this->ui->pte_command->setPlainText(command_res ? command_res.value() + : command_res.error()); +} + +void SCWind::on_pb_export_data_command_clicked() noexcept { + static QString prev_dir{""}; + const QString dir = + QFileDialog::getExistingDirectory(this, tr("设置导出位置"), prev_dir); + + if (dir.isEmpty()) { + return; + } + prev_dir = dir; + const auto converted_tasks = this->tasks.converted_tasks( + this->current_color_table(), this->current_convert_option()); + const int begin_idx = this->ui->sb_file_start_idx->value(); + int fail_count = 0; + QString fail_messages; + for (const auto [g_idx, task] : converted_tasks) { + assert(task != nullptr); + assert(g_idx < this->tasks.size()); + + const auto map_range = this->tasks.map_range_of(begin_idx, g_idx); + const auto command_res = this->get_command( + *(task->converted_images + .at({this->current_color_table(), this->current_convert_option()}) + .converted_image), + map_range.first); + const QString ofilename = + QStringLiteral("%1/command_%2txt") + .arg(dir, QFileInfo{task->filename}.baseName()); + if (not command_res) { + fail_count++; + fail_messages.append( + tr("无法为 %1 生成命令:%2\n").arg(ofilename, command_res.error())); + continue; + } + QFile ofile{ofilename, this}; + if (not ofile.open(QIODevice::OpenModeFlag::Text bitor + QIODevice::OpenModeFlag::Truncate bitor + QIODevice::OpenModeFlag::WriteOnly)) { + fail_count++; + fail_messages.append( + tr("无法创建/打开文件 %1:%2\n").arg(ofilename, ofile.errorString())); + continue; + } + ofile.write(command_res.value().toLocal8Bit()); + if (ofile.error()) { + fail_count++; + fail_messages.append( + tr("无法写入文件 %1:%2\n").arg(ofilename, ofile.errorString())); + continue; + } + ofile.close(); + } + if (fail_count > 0) { + QMessageBox::critical(this, tr("%1 个文件保存失败").arg(fail_count), + fail_messages); + } +} +void SCWind::on_pb_export_data_vanilla_structure_clicked() noexcept { + static QString prev_dir{""}; + const QString dir = + QFileDialog::getExistingDirectory(this, tr("设置导出位置"), prev_dir); + + if (dir.isEmpty()) { + return; + } + prev_dir = dir; + + const bool export_as_lite = + this->ui->cb_export_assembled_format->currentIndex() <= 0; + const auto converted_tasks = this->tasks.converted_tasks( + this->current_color_table(), this->current_convert_option()); + const int begin_idx = this->ui->sb_file_start_idx->value(); + + int fail_count = 0; + QString fail_messages; + auto lite_option = this->current_litematic_option(fail_messages).value(); + auto nbt_option = this->current_nbt_option(fail_messages).value(); + + this->ui->pbar_export->setValue(0); + this->ui->pbar_export->setMinimum(0); + this->ui->pbar_export->setMaximum(converted_tasks.size()); + + for (const auto [g_idx, task] : converted_tasks) { + assert(task not_eq nullptr); + assert(g_idx < this->tasks.size()); + + const auto map_range = this->tasks.map_range_of(begin_idx, g_idx); + auto option = this->current_assembled_maps_option(); + option.begin_index = map_range.first; + + const QString ofilename = + QStringLiteral("%1/maps_%2.%3") + .arg(dir, QFileInfo{task->filename}.baseName(), + export_as_lite ? "litematic" : "nbt"); + + const auto &cvted = + (task->converted_images + .at({this->current_color_table(), this->current_convert_option()}) + .converted_image); + + QString err_temp = tr("SlopeCraftL 未提供详细报错信息。"); + auto report_err_cb = [&err_temp](SCL_errorFlag e, const char *msg) { + err_temp = + tr("错误码:%1,详情:%2").arg(magic_enum::enum_name(e).data(), msg); + }; + // Set up ui callbacks; + { + SlopeCraft::ui_callbacks ui{}; + ui.wind = reinterpret_cast(&report_err_cb); + using cb_type = decltype(report_err_cb); + ui.cb_report_error = [](void *lambda, SCL_errorFlag e, const char *msg) { + (reinterpret_cast(lambda))->operator()(e, msg); + }; + lite_option.ui = ui; + nbt_option.ui = ui; + } + + bool ok; + if (export_as_lite) { + ok = cvted->export_assembled_maps_litematic( + ofilename.toLocal8Bit().data(), option, lite_option); + } else { + ok = cvted->export_assembled_maps_vanilla_structure( + ofilename.toLocal8Bit().data(), option, nbt_option); + } + if (not ok) { + fail_count++; + fail_messages.append(tr("%1 生成失败,%2\n").arg(ofilename, err_temp)); + } + + this->ui->pbar_export->setValue(this->ui->pbar_export->value() + 1); + } + if (fail_count > 0) { + QMessageBox::critical(this, tr("%1 个文件保存失败").arg(fail_count), + fail_messages); + } + this->ui->pbar_export->setValue(converted_tasks.size()); +} + +void SCWind::on_ac_blocklist_triggered() noexcept { + auto dialog = new BlockListDialog{this, this->ui->blm}; + dialog->exec(); +} \ No newline at end of file diff --git a/SlopeCraft/TransparentStrategyWind.cpp b/SlopeCraft/TransparentStrategyWind.cpp new file mode 100644 index 00000000..9af2df16 --- /dev/null +++ b/SlopeCraft/TransparentStrategyWind.cpp @@ -0,0 +1,93 @@ +#include "TransparentStrategyWind.h" +#include "ui_TransparentStrategyWind.h" +#include + +TransparentStrategyWind::TransparentStrategyWind(QWidget* parent) + : QDialog{parent}, ui{new Ui::TransparentStrategyWind} { + this->ui->setupUi(this); + + this->on_pb_reset_clicked(); +} + +TransparentStrategyWind::~TransparentStrategyWind() {} + +SCL_PureTpPixelSt TransparentStrategyWind::pure_strategy() const noexcept { + if (this->ui->cb_pure_air->isChecked()) { + return SCL_PureTpPixelSt::ReserveAsAir; + } + return SCL_PureTpPixelSt::ReplaceWithBackGround; +} + +SCL_HalfTpPixelSt TransparentStrategyWind::half_strategy() const noexcept { + if (this->ui->cb_half_background->isChecked()) { + return SCL_HalfTpPixelSt::ReplaceWithBackGround; + } + if (this->ui->cb_half_compose->isChecked()) { + return SCL_HalfTpPixelSt::ComposeWithBackGround; + } + return SCL_HalfTpPixelSt::IgnoreAlpha; +} + +uint32_t TransparentStrategyWind::background_color() const noexcept { + if (this->ui->cb_background_white->isChecked()) { + return 0xFFFFFFFF; + } + if (this->ui->cb_background_gray->isChecked()) { + return 0xFFDCDCDC; + } + auto pl = this->ui->lb_show_custom_color->palette(); + auto color = pl.color(QPalette::ColorRole::Window).toRgb(); + + return qRgb(color.red(), color.green(), color.blue()); +} + +TransparentStrategyWind::strategy TransparentStrategyWind::current_strategy() + const noexcept { + return {this->pure_strategy(), this->half_strategy(), + this->background_color()}; +} + +void TransparentStrategyWind::on_pb_confirm_clicked() noexcept { + emit this->accept(); +} + +void TransparentStrategyWind::on_pb_cancel_clicked() noexcept { + emit this->reject(); +} + +void TransparentStrategyWind::on_cb_background_custom_clicked() noexcept { + auto color = + QColorDialog::getColor(Qt::GlobalColor::white, this, tr("设置背景色")); + if (!color.isValid()) { + this->ui->cb_background_gray->setChecked(true); + } + + QPalette pl; + pl.setColor(QPalette::ColorRole::Window, color); + + this->ui->lb_show_custom_color->setPalette(pl); +} + +void TransparentStrategyWind::on_pb_reset_clicked() noexcept { + this->ui->cb_pure_background->setChecked(true); + this->ui->cb_half_compose->setChecked(true); + this->ui->cb_background_gray->setChecked(true); + QPalette pl; + pl.setColor(QPalette::ColorRole::Window, QColor(Qt::GlobalColor::white)); + + this->ui->lb_show_custom_color->setPalette(pl); +} + +std::optional +TransparentStrategyWind::ask_for_strategy(QWidget* parent) noexcept { + TransparentStrategyWind tpswind{parent}; + + const auto dialog_code = tpswind.exec(); + + if (dialog_code == QDialog::DialogCode::Rejected) { + return std::nullopt; + } + + const auto ret = tpswind.current_strategy(); + return ret; +} \ No newline at end of file diff --git a/SlopeCraft/TransparentStrategyWind.h b/SlopeCraft/TransparentStrategyWind.h new file mode 100644 index 00000000..2d84cd51 --- /dev/null +++ b/SlopeCraft/TransparentStrategyWind.h @@ -0,0 +1,42 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_TRANSPARENCYSTRATEGYWIND_H +#define SLOPECRAFT_SLOPECRAFT_TRANSPARENCYSTRATEGYWIND_H +#include +#include +#include + +class TransparentStrategyWind; +namespace Ui { +class TransparentStrategyWind; +} + +class TransparentStrategyWind : public QDialog { + Q_OBJECT + private: + std::unique_ptr ui; + private slots: + void on_pb_confirm_clicked() noexcept; + void on_pb_cancel_clicked() noexcept; + + void on_cb_background_custom_clicked() noexcept; + + void on_pb_reset_clicked() noexcept; + + public: + explicit TransparentStrategyWind(QWidget *parent = nullptr); + ~TransparentStrategyWind(); + + struct strategy { + SCL_PureTpPixelSt pure_transparent; + SCL_HalfTpPixelSt half_transparent; + uint32_t background_color; + }; + + SCL_PureTpPixelSt pure_strategy() const noexcept; + SCL_HalfTpPixelSt half_strategy() const noexcept; + uint32_t background_color() const noexcept; + + strategy current_strategy() const noexcept; + + static std::optional ask_for_strategy(QWidget *parent) noexcept; +}; +#endif // SLOPECRAFT_SLOPECRAFT_TRANSPARENCYSTRATEGYWIND_H \ No newline at end of file diff --git a/SlopeCraft/TransparentStrategyWind.ui b/SlopeCraft/TransparentStrategyWind.ui new file mode 100644 index 00000000..784259a7 --- /dev/null +++ b/SlopeCraft/TransparentStrategyWind.ui @@ -0,0 +1,225 @@ + + + TransparentStrategyWind + + + + 0 + 0 + 480 + 320 + + + + 透明像素处理策略 + + + true + + + + + + 背景色 + + + + + + 高级灰(#DCDCDC) + + + true + + + + + + + + 20 + 20 + + + + + 20 + 16777215 + + + + background-color: rgb(255, 255, 255); + + + QFrame::Box + + + + + + + + + + 自定义 + + + + + + + true + + + QFrame::Box + + + + + + + + + + background-color: rgb(220, 220, 220); + + + QFrame::Box + + + + + + + + + + 纯白(#FFFFFF) + + + + + + + + + + + 0 + 0 + + + + 地图画中几乎不能使用透明像素。立体地图画和平板地图画都不能实现纯透明像素,纯文件地图画虽然理论上支持透明像素,但只会透出地图/物品展示框的背景色。因此有必要对透明/半透明像素进行处理。 +”替换为背景色“会将像素替换为背景色;”替换为空气“将像素替换为空气。 +”与背景色叠加“会按照像素的透明度与背景色叠加;“保留颜色”会忽视半透明像素的透明度,直接使用它们的颜色。 + + + true + + + 4 + + + + + + + 纯透明像素 + + + + + + 替换为空气 + + + + + + + 替换为背景色 + + + true + + + + + + + + + + 半透明像素 + + + + + + 保留颜色 + + + + + + + 与背景色叠加 + + + true + + + + + + + 替换为背景色 + + + + + + + + + + 12 + + + 6 + + + 6 + + + 0 + + + + + 确定 + + + + + + + 取消 + + + + + + + 恢复默认设定 + + + + + + + + + + diff --git a/SlopeCraft/cvt_task.cpp b/SlopeCraft/cvt_task.cpp new file mode 100644 index 00000000..143f2a44 --- /dev/null +++ b/SlopeCraft/cvt_task.cpp @@ -0,0 +1,128 @@ + +#include +#include "cvt_task.h" +#include "ExportTableModel.h" + +tl::expected cvt_task::load(QString filename) noexcept { + cvt_task ret; + + ret.filename = filename; + + QImage temp; + if (!temp.load(filename)) { + return tl::make_unexpected(QObject::tr("加载图片 %1 失败。").arg(filename)); + } + + ret.original_image = temp.convertToFormat(QImage::Format_ARGB32); + return ret; +} + +size_t task_pool::converted_count( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option) const noexcept { + size_t counter = 0; + for (const auto& t : *this) { + if (t.is_converted_with(table, cvt_option)) { + counter += 1; + } + } + return counter; +} + +const std::vector> task_pool::converted_tasks( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option) noexcept { + std::vector> ret; + ret.reserve(this->size()); + for (size_t idx = 0; idx < this->size(); idx++) { + auto& task = this->at(idx); + if (task.is_converted_with(table, cvt_option)) { + ret.emplace_back(std::pair{idx, &task}); + } + } + return ret; +} +std::optional> task_pool::converted_task_at_index( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option, size_t eidx) noexcept { + for (size_t idx = 0; idx < this->size(); idx++) { + auto& task = this->at(idx); + if (task.is_converted_with(table, cvt_option)) { + if (eidx == 0) { + return std::pair{idx, &task}; + } + eidx--; + } + } + return std::nullopt; +} + +std::optional task_pool::export_index_to_global_index( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option, size_t e_idx) const noexcept { + for (size_t gidx = 0; gidx < this->size(); gidx++) { + auto& task = this->at(gidx); + if (!task.is_converted_with(table, cvt_option)) { + continue; + } + + if (e_idx <= 0) { + return gidx; + } + e_idx--; + } + return std::nullopt; +} + +map_range task_pool::map_range_of(int map_begin_index, + size_t global_index) const noexcept { + assert(global_index < this->size()); + for (size_t i = 0; i < global_index; i++) { + const auto& task = this->at(i); + const auto current_map_size = + map_size_of_images(task.original_image.size()); + const int current_map_num = + current_map_size.height() * current_map_size.width(); + map_begin_index += current_map_num; + } + const auto cms = + map_size_of_images(this->at(global_index).original_image.size()); + const int cmn = cms.height() * cms.width(); + return map_range{map_begin_index, map_begin_index + cmn - 1}; +} + +// int converted_task_count(const task_pool_t& pool) noexcept { +// int num = 0; +// for (const auto& i : pool) { +// if (i.is_converted()) { +// num++; +// } +// } +// return num; +// } +// +// int map_export_idx_to_full_idx(const task_pool_t& pool, int eidx) noexcept { +// assert(eidx >= 0); +// for (int fidx = 0; fidx < (int)pool.size(); fidx++) { +// if (pool.at(fidx).is_converted()) { +// eidx--; +// } +// if (eidx < 0) { +// return fidx; +// } +// } +// +// assert(false); +// return INT_MAX; +// } + +// std::vector iteration_map(const task_pool_t& pool) noexcept { +// std::vector ret; +// ret.reserve(pool.size()); +// for (int i = 0; i < (int)pool.size(); i++) { +// if (pool.at(i).is_converted()) { +// ret.emplace_back(i); +// } +// } +// return ret; +// } \ No newline at end of file diff --git a/SlopeCraft/cvt_task.h b/SlopeCraft/cvt_task.h new file mode 100644 index 00000000..60761b6e --- /dev/null +++ b/SlopeCraft/cvt_task.h @@ -0,0 +1,381 @@ +#ifndef SLOPECRAFT_SLOPECRAFT_CVT_TASK_H +#define SLOPECRAFT_SLOPECRAFT_CVT_TASK_H + +#include +#include +#include +#include +#include + +struct convert_input { + convert_input() = default; + convert_input(convert_input&&) = default; + convert_input(const SlopeCraft::color_table* t, + const SlopeCraft::convert_option& o) + : table{t}, option{o} {} + + const SlopeCraft::color_table* table{nullptr}; + SlopeCraft::convert_option option; +}; + +struct hasher { + uint64_t operator()( + const SlopeCraft::GA_converter_option& opt) const noexcept { + uint64_t h = 0; + h ^= std::hash()(opt.popSize); + h ^= std::hash()(opt.maxGeneration); + h ^= std::hash()(opt.maxFailTimes); + h ^= std::hash()(opt.crossoverProb); + h ^= std::hash()(opt.mutationProb); + return h; + } + uint64_t operator()(const SlopeCraft::convert_option& opt) const noexcept { + uint64_t h = 0; + h |= static_cast(opt.algo); + h <<= 1; + h |= static_cast(opt.dither); + h ^= hasher{}(opt.ai_cvter_opt); + return h; + } + uint64_t operator()(const convert_input& pair) const noexcept { + return hasher{}(pair.option) ^ std::hash{}(pair.table); + } + uint64_t operator()(const SlopeCraft::build_options& opt) const noexcept { + uint64_t h = 0; + h ^= static_cast(opt.max_allowed_height); + h <<= sizeof(opt.max_allowed_height) * 8; // 16 bits + h ^= static_cast(opt.bridge_interval); + h <<= sizeof(opt.bridge_interval) * 8; // 32 bits + h ^= static_cast(opt.compress_method); + h <<= sizeof(opt.compress_method) * 8; // 40 bits + h ^= static_cast(opt.glass_method); + h <<= sizeof(opt.glass_method) * 8; // 48 bits + h ^= static_cast(opt.fire_proof); + h <<= 1; // 49 bits + h ^= static_cast(opt.enderman_proof); + h <<= 1; // 50 bits + h ^= static_cast(opt.connect_mushrooms); + h <<= 1; // 51 bits + + return h; + } +}; + +struct equal { + bool operator()(const SlopeCraft::GA_converter_option& a, + const SlopeCraft::GA_converter_option& b) const noexcept { + if (a.popSize != b.popSize) return false; + if (a.maxGeneration != b.maxGeneration) return false; + if (a.maxFailTimes != b.maxFailTimes) return false; + if (a.crossoverProb != b.crossoverProb) return false; + if (a.mutationProb != b.mutationProb) return false; + return true; + } + bool operator()(const SlopeCraft::convert_option& a, + const SlopeCraft::convert_option& b) const noexcept { + if (a.algo != b.algo) return false; + if (a.dither != b.dither) return false; + if (!equal{}(a.ai_cvter_opt, b.ai_cvter_opt)) return false; + return true; + } + bool operator()(const convert_input& a, + const convert_input& b) const noexcept { + if (a.table != b.table) return false; + if (!equal{}(a.option, b.option)) return false; + return true; + } + bool operator()(const SlopeCraft::build_options& a, + const SlopeCraft::build_options& b) const noexcept { + if (a.max_allowed_height != b.max_allowed_height) return false; + if (a.bridge_interval != b.bridge_interval) return false; + if (a.compress_method != b.compress_method) return false; + if (a.glass_method != b.glass_method) return false; + if (a.fire_proof != b.fire_proof) return false; + if (a.enderman_proof != b.enderman_proof) return false; + return true; + } +}; + +struct structure_with_info { + std::unique_ptr handle; + + const std::array shape; + const uint64_t block_count; + + explicit structure_with_info( + std::unique_ptr&& src) + : handle{std::move(src)}, + shape{{handle->shape_x(), handle->shape_y(), handle->shape_z()}}, + block_count{handle->block_count()} {} +}; + +struct convert_result { + // convert_result() = delete; + + std::unique_ptr + converted_image{nullptr}; + std::unordered_map + built_structures; + + [[nodiscard]] bool is_built_with( + const SlopeCraft::build_options& opt) const noexcept { + return this->built_structures.contains(opt); + } + + struct cache_report { + size_t cache_num{0}; + }; + + cache_report cache_all_structures(const SlopeCraft::color_table& table, + const SlopeCraft::converted_image& cvted, + const QString& cache_root_dir) noexcept { + return this->cache_all_structures(table, cvted, cache_root_dir, + [](auto) { return true; }); + } + + cache_report cache_all_structures( + const SlopeCraft::color_table& table, + const SlopeCraft::converted_image& cvted, const QString& cache_root_dir, + const std::function& + cache_or_not) noexcept { + size_t num = 0; + for (auto& pair : this->built_structures) { + if (pair.second.handle.get() == nullptr) { // already cached + continue; + } + if (!cache_or_not(pair.first)) { + continue; + } + const bool ok = + table.save_build_cache(cvted, pair.first, *pair.second.handle, + cache_root_dir.toLocal8Bit().data(), nullptr); + pair.second.handle.reset(); + if (ok) { + num++; + } + } + return cache_report{.cache_num = num}; + } + + void cache_structure(const SlopeCraft::color_table& table, + const SlopeCraft::build_options& opt, + const QString& cache_root_dir) noexcept { + auto it = this->built_structures.find(opt); + if (it == this->built_structures.end()) { + return; + } + if (it->second.handle == nullptr) { + return; + } + const bool ok = + table.save_build_cache(*this->converted_image, opt, *it->second.handle, + cache_root_dir.toLocal8Bit().data(), nullptr); + if (ok) { + it->second.handle.reset(); + } + return; + } + + const structure_with_info* get_build_cache_with_info_noload( + const SlopeCraft::color_table& table, + const SlopeCraft::build_options& opt, + const QString& cache_root_dir) const noexcept { + auto it = this->built_structures.find(opt); + if (it == this->built_structures.end()) { + return nullptr; + } + return &it->second; + } + + const SlopeCraft::structure_3D* load_build_cache( + const SlopeCraft::color_table& table, + const SlopeCraft::build_options& opt, + const QString& cache_root_dir) noexcept { + auto it = this->built_structures.find(opt); + if (it == this->built_structures.end()) { + return nullptr; + } + + if (it->second.handle != nullptr) { // the structure exist in memory + return it->second.handle.get(); + } + + // the structure is cached + SlopeCraft::structure_3D* p = + table.load_build_cache(*this->converted_image, opt, + cache_root_dir.toLocal8Bit().data(), nullptr); + if (p == nullptr) { // failed to load cache + return nullptr; + } + it->second.handle.reset(p); + return it->second.handle.get(); + } + + void set_built(SlopeCraft::build_options opt, + std::unique_ptr + structure) noexcept { + opt.ui = {}; + opt.main_progressbar = {}; + opt.sub_progressbar = {}; + auto it = this->built_structures.find(opt); + if (it == built_structures.end()) { + this->built_structures.emplace(opt, + structure_with_info{std::move(structure)}); + return; + } + it->second.handle = std::move(structure); + } +}; + +struct cvt_task { + cvt_task() = default; + cvt_task(cvt_task&&) = default; + + QString filename{""}; + QImage original_image; + + std::unordered_map + converted_images; + + cvt_task& operator=(cvt_task&&) = default; + + // std::unique_ptr + // structure; + + [[nodiscard]] bool is_converted_with( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& option) const noexcept { + return this->converted_images.contains(convert_input{table, option}); + } + + [[nodiscard]] std::unordered_map::iterator + get_convert_result(const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& option) noexcept { + return this->converted_images.find({table, option}); + } + + [[nodiscard]] std::unordered_map::const_iterator + get_convert_result(const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& option) const noexcept { + return this->converted_images.find({table, option}); + } + + [[nodiscard]] const SlopeCraft::converted_image* get_converted_image( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& option) const noexcept { + auto it = this->get_convert_result(table, option); + if (it == this->converted_images.cend()) { + return nullptr; + } + return it->second.converted_image.get(); + } + + void set_converted( + const SlopeCraft::color_table* table, SlopeCraft::convert_option option, + std::unique_ptr + converted_image) noexcept { + if (converted_image == nullptr) { + return; + } + option.ui = {}; + option.progress = {}; + convert_input cvt_input = convert_input{table, option}; + auto it = this->converted_images.find(cvt_input); + if (it == this->converted_images.end()) { + this->converted_images.emplace( + std::move(cvt_input), + convert_result{.converted_image = std::move(converted_image), + .built_structures = {}}); + return; + } + it->second.converted_image = std::move(converted_image); + } + + [[nodiscard]] bool is_built_with( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option, + const SlopeCraft::build_options& build_option) const noexcept { + auto it = this->converted_images.find(convert_input{table, cvt_option}); + if (it == this->converted_images.end()) { + return false; + } + return it->second.is_built_with(build_option); + } + + // [[nodiscard]] const SlopeCraft::structure_3D* load_build_cache( + // const SlopeCraft::color_table* table, + // const SlopeCraft::convert_option& cvt_option, + // const SlopeCraft::build_options& build_option, + // const QString& cache_dir) noexcept { + // + // } + + void cache_all_structures(const SlopeCraft::color_table& table, + const QString& cache_root_dir) noexcept { + this->cache_all_structures(table, cache_root_dir, + [](auto, auto) { return true; }); + } + + void cache_all_structures( + const SlopeCraft::color_table& table, const QString& cache_root_dir, + const std::function& + cache_or_not) noexcept { + for (auto& cvt : this->converted_images) { + const auto& cvted_img = cvt.second.converted_image; + cvt.second.cache_all_structures( + table, *cvted_img, cache_root_dir, + [cache_or_not, &cvt](const SlopeCraft::build_options& bopt) { + return cache_or_not(cvt.first.option, bopt); + }); + } + } + + static tl::expected load(QString filename) noexcept; +}; + +// Q_DECLARE_METATYPE(cvt_task) + +struct map_range { + int first{-1}; + int last{-1}; +}; + +// map_range map_range_at_index(const task_pool_t& pool, int first_map_seq_num, +// int asked_idx) noexcept; +class task_pool : public std::vector { + public: + [[nodiscard]] size_t converted_count( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option) const noexcept; + + [[nodiscard]] const std::vector> converted_tasks( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option) noexcept; + + [[nodiscard]] std::optional> + converted_task_at_index(const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option, + size_t idx) noexcept; + + [[nodiscard]] std::optional export_index_to_global_index( + const SlopeCraft::color_table* table, + const SlopeCraft::convert_option& cvt_option, + size_t e_idx) const noexcept; + + [[nodiscard]] map_range map_range_of(int map_begin_index, + size_t global_index) const noexcept; +}; + +// using task_pool_t = ; +// +// int converted_task_count(const task_pool_t&) noexcept; +// +// int map_export_idx_to_full_idx(const task_pool_t&, int eidx) noexcept; +// +// std::vector iteration_map(const task_pool_t& pool) noexcept; + +#endif // SLOPECRAFT_SLOPECRAFT_CVT_TASK_H \ No newline at end of file diff --git a/SlopeCraft/install.cmake b/SlopeCraft/install.cmake new file mode 100644 index 00000000..4c8deffa --- /dev/null +++ b/SlopeCraft/install.cmake @@ -0,0 +1,130 @@ +file(GLOB SlopeCraft_block_list_archives + "${CMAKE_BINARY_DIR}/SCL_block_lists/*.zip") + +list(LENGTH SlopeCraft_block_list_archives num_archives) +if (${num_archives} LESS_EQUAL 0) + message(FATAL_ERROR "No archive preset found") +endif () + +file(GLOB SlopeCraft_install_presets + "${CMAKE_CURRENT_SOURCE_DIR}/others/presets/*.sc_preset_json") + +set(AppName SlopeCraft) +#configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in +# ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake +# @ONLY) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + # Install for macOS + # Install app + install(TARGETS SlopeCraft + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . + BUNDLE DESTINATION . + ) + + # Install icons + set(SlopeCraft_Icon + ${CMAKE_SOURCE_DIR}/SlopeCraft/others/SlopeCraftIconNew.icns) + install(FILES ${SlopeCraft_Icon} + DESTINATION SlopeCraft.app/Contents/Resources + RENAME SlopeCraft.icns) + + # Install all block list archives + install(FILES ${SlopeCraft_install_presets} + DESTINATION SlopeCraft.app/Contents/MacOS/Blocks) + file(COPY ${SlopeCraft_block_list_archives} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.app/Contents/MacOS/Blocks/) + + # Install presets + install(FILES ${SlopeCraft_install_presets} + DESTINATION SlopeCraft.app/Contents/MacOS/Blocks/Presets) + file(COPY ${SlopeCraft_install_presets} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.app/Contents/MacOS/Blocks/Presets) + + install(TARGETS SlopeCraftL + #EXPORT SlopeCraftTargets + RUNTIME DESTINATION SlopeCraft.app/Contents/Frameworks + LIBRARY DESTINATION SlopeCraft.app/Contents/Frameworks) + + QD_add_deployqt(SlopeCraft + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_macdeployqt_flags_install}) + + DylibD_add_deploy(SlopeCraft + INSTALL_DESTINATION . + RPATH_POLICY REPLACE) + RCS_add_codesign(SlopeCraft + INSTALL_DESTINATION .) + + return() +endif () + +if (CMAKE_SYSTEM_NAME MATCHES "Windows") + # Install app + install(TARGETS SlopeCraft + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . + ) + + QD_add_deployqt(SlopeCraft + BUILD_MODE + FLAGS ${SlopeCraft_windeployqt_flags_build}) + QD_add_deployqt(SlopeCraft + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_windeployqt_flags_install}) + DLLD_add_deploy(SlopeCraft + BUILD_MODE + INSTALL_MODE INSTALL_DESTINATION . + OPTIONAL_DLLS "imageformats/*.dll") + + # Install all block list archives + install(FILES ${SlopeCraft_block_list_archives} + DESTINATION Blocks) + file(COPY ${SlopeCraft_block_list_archives} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks) + + # Install presets + install(FILES ${SlopeCraft_install_presets} + DESTINATION Blocks/Presets) + file(COPY ${SlopeCraft_install_presets} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/Presets) + + return() +endif () + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + # set_target_properties(SlopeCraft PROPERTIES INSTALL_RPATH "../lib") + # Install for Linux + # Install app + install(TARGETS SlopeCraft + EXPORT SlopeCraftTargets + RUNTIME DESTINATION bin + ) + + # Install all block list archives + install(FILES ${SlopeCraft_block_list_archives} + DESTINATION bin/Blocks) + file(COPY ${SlopeCraft_block_list_archives} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks) + + + install(FILES others/SlopeCraftIconNew.png + DESTINATION share/pixmaps + RENAME com.github.SlopeCraft.SlopeCraft.png) + install(FILES others/SlopeCraft.desktop + DESTINATION share/applications) + + # Install presets + install(FILES ${SlopeCraft_install_presets} + DESTINATION bin/Blocks/Presets) + file(COPY ${SlopeCraft_install_presets} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/Presets) + + # Install platforms and imageformats plugins + include(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake) + + return() +endif () + +message(WARNING "No rule to install SlopeCraft and images, because the system is not Windows, linux or MacOS.") diff --git a/SlopeCraft/main.cpp b/SlopeCraft/main.cpp new file mode 100644 index 00000000..8846296c --- /dev/null +++ b/SlopeCraft/main.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include "SCWind.h" +#include "VersionDialog.h" + +int main(int argc, char** argv) { + QApplication qapp(argc, argv); + + QDir::setCurrent(QCoreApplication::applicationDirPath()); + QImageReader::setAllocationLimit(INT32_MAX); + + SCWind wind; + + wind.show(); + wind.setWindowTitle(SCWind::default_wind_title()); + + bool is_language_ZH = QLocale::system().uiLanguages().contains("zh"); + + // this line is used to test the translation + + for (int i = 0; i < argc; i++) { + if (std::string_view(argv[i]) == "--lang-force-to-en") { + is_language_ZH = false; + break; + } + if (std::string_view(argv[i]) == "--lang-force-to-zh") { + is_language_ZH = true; + break; + } + } + + if (is_language_ZH) { + wind.set_lang(::SCL_language::Chinese); + } else { + wind.set_lang(::SCL_language::English); + } + + VersionDialog::start_network_request(&wind, "SlopeCraft", + QUrl{SCWind::update_url}, + SCWind::network_manager(), false); + + return qapp.exec(); +} \ No newline at end of file diff --git a/SlopeCraftMain/others/MCStyledNumbers.png b/SlopeCraft/others/MCStyledNumbers.png similarity index 100% rename from SlopeCraftMain/others/MCStyledNumbers.png rename to SlopeCraft/others/MCStyledNumbers.png diff --git a/SlopeCraft/others/SlopeCraft.desktop b/SlopeCraft/others/SlopeCraft.desktop new file mode 100644 index 00000000..937add58 --- /dev/null +++ b/SlopeCraft/others/SlopeCraft.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=SlopeCraft +Comment=Map Pixel Art Generator for Minecraft +Exec=SlopeCraft +Icon=com.github.SlopeCraft.SlopeCraft.png +Type=Application +Keywords=SlopeCraft; \ No newline at end of file diff --git a/SlopeCraftMain/others/SlopeCraft.icns b/SlopeCraft/others/SlopeCraft.icns similarity index 100% rename from SlopeCraftMain/others/SlopeCraft.icns rename to SlopeCraft/others/SlopeCraft.icns diff --git a/SlopeCraftMain/others/SlopeCraft.ico b/SlopeCraft/others/SlopeCraft.ico similarity index 100% rename from SlopeCraftMain/others/SlopeCraft.ico rename to SlopeCraft/others/SlopeCraft.ico diff --git a/SlopeCraftMain/others/SlopeCraft.png b/SlopeCraft/others/SlopeCraft.png similarity index 100% rename from SlopeCraftMain/others/SlopeCraft.png rename to SlopeCraft/others/SlopeCraft.png diff --git a/SlopeCraftMain/others/SlopeCraft.rc.in b/SlopeCraft/others/SlopeCraft.rc.in similarity index 95% rename from SlopeCraftMain/others/SlopeCraft.rc.in rename to SlopeCraft/others/SlopeCraft.rc.in index ae9a1a84..0ac37af6 100644 --- a/SlopeCraftMain/others/SlopeCraft.rc.in +++ b/SlopeCraft/others/SlopeCraft.rc.in @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -44,7 +44,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "CompanyName", "\0" VALUE "FileDescription", "Minecraft Map Pixel Art Tool\0" VALUE "FileVersion", "@PROJECT_VERSION@.0\0" - VALUE "LegalCopyright", "Copyright TokiNoBug 2021-2022\0" + VALUE "LegalCopyright", "Copyright TokiNoBug 2021-2026\0" VALUE "OriginalFilename", "SlopeCraft.exe\0" VALUE "ProductName", "SlopeCraft\0" VALUE "ProductVersion", "@PROJECT_VERSION@.0\0" diff --git a/SlopeCraftMain/others/SlopeCraftIconNew.icns b/SlopeCraft/others/SlopeCraftIconNew.icns similarity index 100% rename from SlopeCraftMain/others/SlopeCraftIconNew.icns rename to SlopeCraft/others/SlopeCraftIconNew.icns diff --git a/SlopeCraftMain/others/SlopeCraftIconNew.ico b/SlopeCraft/others/SlopeCraftIconNew.ico similarity index 100% rename from SlopeCraftMain/others/SlopeCraftIconNew.ico rename to SlopeCraft/others/SlopeCraftIconNew.ico diff --git a/SlopeCraftMain/others/SlopeCraftIconNew.png b/SlopeCraft/others/SlopeCraftIconNew.png similarity index 100% rename from SlopeCraftMain/others/SlopeCraftIconNew.png rename to SlopeCraft/others/SlopeCraftIconNew.png diff --git a/SlopeCraft/others/SlopeCraft_en_US.ts b/SlopeCraft/others/SlopeCraft_en_US.ts new file mode 100644 index 00000000..b392d9e3 --- /dev/null +++ b/SlopeCraft/others/SlopeCraft_en_US.ts @@ -0,0 +1,1673 @@ + + + + + AiCvterParameterDialog + + + Ai转化器参数 + Set AiConverter parameters + + + + 最大提前收敛代数: + Max fail times : + + + + 交叉概率: + Crossover prob : + + + + 种群数量: + Population size : + + + + 最大进化代数: + Max generation : + + + + 变异概率: + Mutation prob : + + + + BLD_block_info_provider + + + 最低版本 + Minimum version + + + + 依附方块 + Attached block + + + + 发光 + Glowing + + + + 末影人可搬走 + Enderman pickable + + + + 可燃 + Burnable + + + + 一组数量 + Stack size + + + + 远古版本 + Very old + + + + 未来版本 + Future + + + + BLD_block_list_provider + + + SlopeCraft 内部错误,方块列表的列表中出现 nullptr + SlopeCraft internal error, nullptr found in list of blocklist + + + + BLD_block_provider + + + SlopeCraft 内部错误,方块列表中出现 nullptr + SlopeCraft internal error, nullptr found in list of blocklist + + + + BlockListDialog + + + 方块列表 + Block list + + + + 添加方块列表 + Add block list + + + + 删除方块列表 + Remove block list + + + + 确定 + OK + + + + 方块 id + Block id + + + + 1.12 id + id in 1.12 + + + + 中文名 + Chinese name + + + + 英文名 + English name + + + + 选择方块列表 + Select block list + + + + 不能删除基础方块列表 + Can not remove fundamental block list + + + + FixedBlocks.zip 是基础方块列表,不允许移除。 + FixedBlocks.zip is the fundamental block list, not allowed to remove. + + + + 删除方块列表 %1 失败 + Failed to deleta block list %1 + + + + 删除方块列表成功 + Removed block list + + + + 删除了 %1 个方块列表,移除了 %2 个方块 + Removed %1 block list(s) and %2 block(s) + + + + CompressEffectViewer + + + 预览压缩效果 + Compress effect + + + + 保存图像 + + + + + 保存压缩后图片 + + + + + 保存图像失败 + Failed to save image + + + + 无法保存图像 %1 + + + + + ExportTableModel + + + 原图文件名 + Image file + + + + 图像大小 + Image size + + + + 地图大小 + Map size + + + + 地图序号范围 + Map index range + + + + 第一个地图文件名 + First map + + + + 最后一个地图文件名 + Last map + + + + MaterialModel + + + %1 盒 + %2 组 + %3 个 + %1 shulker box(es) + %2 stack(s) + %3 + + + + %1 组 + %2 个 + %1 stack(s) + %2 + + + + %1 个 + %1 + + + + PoolModel + + + 被绘制的图标尺寸应当是 32*32,但实际上是%1*%2。这属于 SlopeCraft 内部错误,请向开发者反馈。SlopeCraft 必须崩溃。 + The size of icon to be drawn should be 32*32, but actually it is %1*%2. This is a SlopeCraft internal error, please report to developer. SlopeCraft mush crash. + + + + PreviewWind + + + + 查看材料列表 + Material List + + + + 按组显示 + Display by stacks + + + + 不排序 + Do not sort + + + + 升序 + Acsending + + + + 降序 + Decsending + + + + 导出为文件 + Export as file + + + + 复制到剪贴板 + Copy to clipboard + + + + %1 -- 表格内容已复制到剪贴板 + %1 -- Table content copied to clipboart + + + + 大小:%1 × %2 × %3 + Size: %1 × %2 × %3 + + + + 体积:%1 + Volume: %1 + + + + 方块总数:%1 + Block count: %1 + + + + 保存材料表 + Save material list + + + + 保存材料表失败 + Failed to save material list + + + + 无法打开文件 "%1",详细信息:%2 + Failed to open file "%1", detail: %2 + + + + QObject + + + 加载图片 %1 失败。 + Failed to load image %1. + + + + 绘制图标时发现错误 + An error occurred when drawing icon + + + + SCWind + + + 地图画配置 + Map configurations + + + + 方块列表预设 + Blocklist preset + + + + 加载预设 + Load preset + + + + + 保存当前预设 + Save as preset + + + + 可用方块数量: + Available block count: + + + + 游戏版本 + Game Version + + + + 地图画类型 + Map type + + + + 立体地图画 + 3D Map + + + + 平板地图画 + Flat Map + + + + 纯文件地图画 + File-only Map + + + + 优先彩色玻璃 + Use stained glass + + + + 优先混凝土 + Use Concrete + + + + 优先羊毛 + Use Wool + + + + 优先木板 + Use planks + + + + 优先原木 + Use logs + + + + 导入图像并转化 + Load image and convert + + + + 原图 + Original image + + + + 转化后 + Converted image + + + + 删除 + Delete + + + + + 项目池 + Task pool + + + + 添加 + Add + + + + 转化算法 + Convert algorithm + + + + 抖动 + Dithering + + + + 转化当前图像 + Convert current image + + + + + + 保存转化后图像 + Save converted image + + + + 全部转化 + Convert all + + + + 导出 + Export + + + + Litematica + + + + + + 结构方块文件 + Vanilla structure + + + + 管理方块列表 + Manage block lists + + + + 默认只加载原版方块,点击上方按钮可加载其他方块列表。 + Only vanilla blocks are loaded by default, click button above to load other block lists. + + + + 优先台阶 + Use slabs + + + + 全选 + Select all + + + + 全不选 + Deselect all + + + + 反选 + Invert selection + + + + 优先地毯 + Use carpets + + + + 优先压力板 + Use pressure plates + + + + 显示缩略图 + View images + + + + 替换 + Replace + + + + + WE原理图 + WE Schem + + + + + 平面示意图 + Flat diagram + + + + 地图文件 + Map data files + + + + 其他选项 + Others + + + + 防火 + Fire Proof + + + + 防末影人 + Enderman Proof + + + + 连接蘑菇块 + Connect mushroom blocks + + + + 压缩高度 + Compress + + + + 有损压缩 + Lossy Compression + + + + 无损压缩 + Lossless Compress + + + + 最大允许高度: + Max height: + + + + 搭桥 + Glass Bridge + + + + 允许搭桥 + Construct Glass Bridge + + + + 层 + layer(s) + + + + 搭桥间隔: + Glass bridge interval: + + + + 投影区域名称 + Region name + + + + 投影名称 + Litematic name + + + + 用结构空位替代空气 + Replace air with structure void + + + + offset + + + + + WEOffset + + + + + + + + + + 0 + + + + + 原理图名称 + Name of schematic + + + + 依赖 mod 名称 + Depend mods + + + + 在这里输入依赖 mod 的名字。用换行符分割多个 mod + Input names of required mods here. To represent multiple mods, split them with a line break + + + + + 方块 + block(s) + + + + + 间距: + Line interval: + + + + 垂直分割线 + Vertical split line + + + + 水平分割线 + Horizontal split line + + + + 大小与方块数量 + Size and block count + + + + + 大小: + Size: + + + + + 方块数量: + Block count: + + + + 全部导出 + Export all + + + + 预览 + Preview + + + + 预览材料表 + Material list + + + + 预览压缩效果 + Compress effect + + + + 预构建三维结构 + Construct 3d structure + + + + 批量获得地图物品的指令 + /give command to get map items + + + + 💡tips: 设置地图画起始序号后,点击“导出”,选择输出地图数据文件的位置,就完成导出任务了。 + +在下面的表格里可以看到每个图像对应的文件名。 + +图像数据存储在地图数据文件中,必须先为存档导入地图数据文件,地图画结构才能生效。 + 💡tips: After setting map beginning index, click "Export", select path to export map data files. + +You can see filenames of each image. + +Map data are stored in map data files, assembled maps can take effect only after you must import map data files into the save. + + + + 地图画起始序号: + Map beginning index: + + + + 导出批量获得地图物品的命令(txt) + Export command to get map items + + + + 导出命令 + Export commands + + + + 生存模式不可破坏展示框;移除它依附的方块,展示框也不会消失。 + Item frams can not be removed in survivial mode; it will not disappear even the attached block is removed. + + + + 展示框不可破坏 + Fixed frame + + + + 1.20.5后,物品格式发生重大改变 + Item format is changed greatly after 1.20.5 + + + + MC版本≥1.20.5+ + MC ≥ 1.20.5 + + + + 采用荧光物品显示框 + Use glowing item frame + + + + 荧光物品显示框 + Glowing item frame + + + + 导出包含物品展示框的投影/结构方块文件 + Export the litematica / vanilla structure containing item frames + + + + 导出组装的地图画 + Export assembled maps + + + + 导出map_i.dat的地图数据文件 + Export map data files like map_i.dat + + + + 导出地图文件 + Export map data files + + + + 展示框背景将不可见 + The background of item frames will be invisible + + + + 展示框透明 + Transprant frame + + + + 地图画可依附于方块的侧面、顶面和底面 + Map can attach to a block by side, top and bottom + + + + 地图画方向 + Map direction + + + + 导出组装地图画的格式 + Format to export assembled maps + + + + 导出Litematica + Export as litematica + + + + 导出结构方块文件 + Export as structure + + + + 内存使用策略 + Memory policy + + + + 缓存全部三维结构 + Cache all 3D structures + + + + 查看方块列表 + View block lists + + + + 语言 + Language + + + + 高级 + Advanced + + + + 缓存 + Cache + + + + 关于 + About + + + + 联系作者 + Contact with me + + + + 帮助 + Help + + + + GA转化器参数 + GACvter parameters + + + + open_cache_dir + + + + + 打开缓存文件夹 + Open cache directory + + + + 清除缓存 + Clean caches + + + + + 关于 SlopeCraft + About SlopeCraft + + + + 反馈 bug + Report bugs + + + + 检查更新 + Check updates + + + + 测试方块列表 + Test block list + + + + 输出当前颜色表 + Export current colorset + + + + 查看 SlopeCraft 文档 + Documentation + + + + 使用教程 + Tutorial + + + + 常见问题 + FAQ + + + + 加载默认预设失败 + Failed to load default presets + + + + 一个或多个内置的预设不能被解析。SlopeCraft 可能已经损坏,请重新安装。 +具体报错信息: +%1 + One or more internal presets failed to be parsed. SlopeCraft may have been damaged, please reinstall it. +Detail information: +%1 + + + + 可用颜色数量:%1 + Avaliable colors: %1 + + + + 应用预设失败 + Failed to apply preset + + + + 无法保存第%1个转化后图像 + Failed to save the %1-th converted image + + + + 该图像未被转化,或者转化之后修改了颜色表/转化算法。请重新转化它。 + This image is not converted, or you have changed the color palette/convert algo. Please convert it again. + + + + 保存图像失败 + Failed to save image + + + + 保存%1时失败。可能是因为文件路径错误,或者图片格式不支持。 + Failed when saving %1. This may because of a file path error, or the image format is not supported. + + + + 大小: %1 × %2 × %3 + Size: %1 × %2 × %3 + + + + 方块数量:%1 + Block count: %1 + + + + WE 原理图参数有错:输入给 offset 的值"%1"不是一个有效的坐标,应当输入一个整数。 + Invalid option for WE schem: the given value of offset "%1" is not a valid coordinate, please input an integer. + + + + WE 原理图参数有错:输入给 we offset 的值"%1"不是一个有效的数字,应当输入一个整数。 + Invalid option for WE schem: the given value of we offset "%1" is not a valid coordinate, please input an integer. + + + + 平面示意图的分割线间距无效:水平间距为 %1,垂直间距为 %2, 但间距必须为正数。 + Invalid split line interval for flat diagram: the horzontal interval is %1, and that of vertical is %2, but intervals must be positive numbers. + + + + 错误类型:%1,错误码:%2。详细信息: +%3 + Error type: %1, error code: %2, details: +%3 + + + + SlopeCraft 出现错误 + An error occurred to SlopeCraft + + + + %1 + +点击 Ok 以忽略这个错误,点击 Close 将退出 SlopeCraft。 + %1 + +Click Ok to ignore, and click Close to exit SlopeCraft. + + + + + 选择图片 + Select Image + + + + + 打开图像失败 + Failed to open image + + + + 无法打开图像 %1。常见原因:图像尺寸太大。 +详细信息: %2 + Failed to open image %1. Possible reason: the image is too large. +Detailed information: %2 + + + + 请选择将被替换的图像 + Please images to be replaced + + + + 无法打开图像 %1。 +详细信息: %2 + Failed to open image %1 . +Details: %2 + + + + 必须先选择一个或多个图像,然后才能替换它们。 + You should select one or more images, and then replace them. + + + + 选择预设文件 + Select a preset file + + + + 解析预设文件失败 + Failed to parse thes preset file + + + + 预设文件%1存在错误:%2 + The preset file "%1" is invalid. Detail: %2 + + + + 保存预设文件失败 + Failed to save preset file + + + + 无法生成预设文件%1,错误信息:%2 + Failed to generate preset file "%1", detail: %2 + + + + + + + 未选择图像 + No image selected + + + + + + 请在左侧任务池选择一个图像 + Please select a image in the left + + + + 请在左侧任务池选择一个或多个图像 + Please select one or more images in the left + + + + 将要覆盖已存在的图像 + Existing file(s) will be replaced + + + + %1将被覆盖,确认覆盖吗? + %1 will be replaced, are you sure to replace it? + + + + + + 该图像尚未被转化 + The image is not converted + + + + + 必须先转化一个图像,然后再为它构建三维结构 + You must convert a image before building 3d structure for it + + + + 可能是在转化完成之后又修改了转化算法,因此之前的转化无效。必须重新转化该图像。 + You may have changed the convertion algorithm after the convertion finished. You must convert it again. + + + + 尚未构建三维结构 + 3d structure not built + + + + 在预览材料表之前,必须先构建三维结构。出现这个警告,可能是因为你在构建三维结构之后,又修改了三维结构的选项,因此之前的结果无效。 + You must construct 3d structure before you view the material list. This error may because you changed the option of 3d structure after you built it. Thus, previous result is useless. + + + + 导出设置有错 + Error in export options + + + + 导出设置存在如下错误: +%1 + There is an error in your export option: +%1 + + + + 你点错按钮了 + Wrong button + + + + 导出为纯文件地图画的按钮在另外一页。按理来说你不应该能点击这个按钮,这可能是一个小小的 bug(特性)。 + the button to export file only maps is on another page. Generally you are not able to click this button, this may be a bug(FEATURE). + + + + 无可导出的任务 + Nothing to export + + + + 任务池为空,请先转化一个或一些图像 + The task pool is empty, please convert one or more images + + + + 选择导出位置 + Select export directory + + + + 将要覆盖已经存在的文件 + Existing files will be replaced + + + + 确定要覆盖这些文件吗?以下文件将被覆盖: +%1 + Are you sure to replace these files: +%1 + + + + 导出失败 + Failed to export + + + + 导出%1时失败。原图像文件名为%2 +点击 Ignore 将跳过这个图像,点击 Cancel 将放弃导出任务。 + Failed when exporting %1. The corresponding image is %2 +Click Ignore to skip this image, and Cancel to cancel. + + + + + + 设置导出位置 + Set export directory + + + + %1 个文件将被替换 + %1 files will be replaced + + + + %1 个文件将被替换。点击 Show Details 可以查看它们。 +点击 Yes 将替换它们,点击 No 将取消这次导出。 + %1 file(s) will be replaced. Click "Show Details" to view them. +Click Yes to replace, and No to cancel. + + + + %1 个图片导出失败 + Failed to export %1 image(s) + + + + 导出失败的图片依次为: +%1 + Following image(s) failed to export: +%1 + + + + 删除缓存失败 + Failed to clean cache + + + + 无法删除文件或文件夹"%1"。 +点击 Ignore 以跳过,点击 Retry 以重试,点击 Cancel 以取消这次操作 + Failed to remove file or directory named "%1". +Click Ignore to skip, Retry to retry and Cancel to cancel + + + + SlopeCraft 是一款由 ToKiNoBug 开发的立体地图画生成器,主要用于在 Minecraft 中制造可以生存实装的立体地图画(但同样支持传统的平板地图画)。 + SlopeCraft is a Minecraft 3D pixel map arts generator developed by ToKiNoBug, mainly used to generate 3D pixel map arts that can be implemented in vanilla Minecraft survival mode (but also supports traditional flat pixel map arts). + + + + 本软件的开发持续集成与 macOS 软件适配由 iXOR Technology (Cubik65536 以及贡献者) 提供 + The continuous integration solution and macOS version maintenance of this software is provided by iXOR Technology (Cubik65536 and contributors) + + + + 感谢 AbrasiveBoar902 为本软件的设计和优化贡献的力量 + Thanks to AbrasiveBoar902 for his contribution to the design and optimization of this software + + + + 感谢 67au 为本软件的 macOS 与 Linux 适配做出的贡献 + Thanks to 67au for his contribution during the development of macOS and Linux version of this software + + + + SlopeCraft 在开发时使用了 Qt,zlib 和 eigen 等开源库,对上述库的开发者表示感谢。 + Open source libraries such as Qt, zlib and eigen are used during the development of SlopeCraft, thanks to the developers of these libraries. + + + + 本软件遵循 GPL-3.0 及以后版本 (GPL-3.0 or later) 协议开放源码。 + This program is released under license GPL-3.0 or later. + + + + Copyright © 2021-2026 SlopeCraft 开发者 (TokiNoBug, AbrasiveBoar, iXOR Technology, Mifan-T, 以及贡献者). 版权所有 + Copyright © 2021-2026 SlopeCraft Developers (TokiNoBug, AbrasiveBoar, iXOR Technology, Mifan-T, and contributors). All rights reserved. + + + + 同时选中多个图片时,不显示 /give 命令。如果想预览导出的命令,请只选择一个图片。 + Can't show /give command when you selecte multiple images. To view the command, select one image only. + + + + 无法为 %1 生成命令:%2 + + Failed to generate command for %1: %2 + + + + + 无法创建/打开文件 %1:%2 + + Failed to create/open file %1: %2 + + + + + 无法写入文件 %1:%2 + + Failed to write file %1: %2 + + + + + + %1 个文件保存失败 + Failed to save %1 file(s) + + + + SlopeCraftL 未提供详细报错信息。 + SlopeCraftL doesn't give further details. + + + + 错误码:%1,详情:%2 + Error code: %1, details: %2 + + + + %1 生成失败,%2 + + Failed to generate %1, %2 + + + + + + 无法加载方块列表 + Failed to load block list + + + + 存储方块列表的文件夹 "%1" 不存在,或不是文件夹。 + Directory of block list zips \"%1\" doesn't exist or is not a directory. + + + + + SlopeCraft 必须退出。 + SlopeCraft must exit. + + + + 无法加载 FixedBlocks.zip ,SlopeCraft 缺乏最基础的方块列表。 + Failed to load FixedBlocks.zip, fundamental blocks are missing for SlopeCraft. + + + + 部分方块列表加载失败 + Some block lists failed to be loaded + + + + 以下 %1 个方块列表文件无法被加载: +%2 +由于它们不是必需,你可以忽略此错误并继续使用。 + Failed to load %1 following block list(s): +%2 +Since they are not essential, you can ignore this error and go on. + + + + 墙面 + Wall + + + + 顶面 + Top + + + + 底面 + Bottom + + + + 北 + north + + + + 南 + south + + + + 东 + east + + + + 西 + west + + + + %1,向%2 + %1, %2 + + + + + 没有可用颜色 + No available color + + + + + 没有勾选任何颜色,无法转化图像。请至少勾选3~16种颜色。 + You didn't enable ANY COLOR, unable to convert image. Please enable at least 3~16 colors. + + + + 勾选颜色太少 + Too few colors enabled + + + + 仅仅勾选了%1种颜色,颜色过少,转化效率可能非常差。您可以点Yes继续转化,但非常建议请尽量多勾选一些颜色。 + You only enabled %1 color(s), it is so few that the conversion effect may be terrible. You can click Yes to go on, but it's strongly recommended to enable more colors. + + + + %1行,%2列 + %1 row(s), %2 col(s) + + + + 生成命令失败: +%1 + Failed to generate command: +%1 + + + + 正在构建高度矩阵 + Making height matrix + + + + 正在构建三维结构 + Building 3D structure + + + + 正在收集整张图片的颜色 + Collecting colors of the whole image + + + + 正在压缩立体地图画 + Compressing 3D map pixel arts + + + + 正在为立体地图画搭桥 + Constructing glass bridge + + + + 正在匹配颜色 + Matching colors + + + + 正在使用抖动仿色 + Dithering + + + + 正在将平板地图画变为墙面地图画 + Converting flat map art to wall map art + + + + 正在写入三维结构 + Writing 3D structure + + + + 正在写入方块列表 + Writing block palette + + + + 正在写入地图数据文件 + Writing map data + + + + 正在写入基础信息 + Writing meta data + + + + 这不是严重的问题,你可以直接忽略这个警告,或者把它反馈给开发者,不影响正常使用。只是 Slopecraft 可能占用更多的内存。 +详细信息: +%1 + It's not a fatal problem, you can either ignore this warning, or report to the developer. You can feel free to use SlopeCraft with more memory cost. +Details: +%1 + + + + 获取本进程的内存占用失败 + Failed to query memory usage of this process + + + + 获取操作系统内存占用失败 + Failed to query system memory usage + + + + 保存颜色表 + Save palette + + + + + 保存颜色表失败 + Failed to save colormap image + + + + 分配内存失败 + Failed to allocate memory for image + + + + 无法生成文件 %1 + Failed to generate %1 + + + + 保存测试文件 + Save testing file + + + + 输出测试文件失败 + Failed to save testing file + + + + 保存测试文件 %1 时出现错误。详细信息: +%2 + An error occured when saving test file %1. Details: +%2 + + + + TransparentStrategyWind + + + 透明像素处理策略 + Transparent pixels processing strategy + + + + 背景色 + Background color + + + + 高级灰(#DCDCDC) + Silver gray(#DCDCDC) + + + + 自定义 + Custom + + + + 纯白(#FFFFFF) + Pure white(#FFFFFF) + + + + 地图画中几乎不能使用透明像素。立体地图画和平板地图画都不能实现纯透明像素,纯文件地图画虽然理论上支持透明像素,但只会透出地图/物品展示框的背景色。因此有必要对透明/半透明像素进行处理。 +”替换为背景色“会将像素替换为背景色;”替换为空气“将像素替换为空气。 +”与背景色叠加“会按照像素的透明度与背景色叠加;“保留颜色”会忽视半透明像素的透明度,直接使用它们的颜色。 + Transparency is hardly supported in map paintings. 3d maps and flat maps cannot implement transparent pixels. Although transparent pixels do exist in file-only maps, they just let the background of map/item go through. Thus it's necessary to deal with full/semi trasnparent pixels. +"Replace with background" replace pixels with background color, while "Replace with air" with air. +"Compose with backgroundcolor" compose pixel's color with background color accroding to alpha value, and "Reserve color" simply ignore the alpha value, using its RGB directly. + + + + 纯透明像素 + Full transparent + + + + 替换为空气 + Replace with air + + + + + 替换为背景色 + Replace with background + + + + 半透明像素 + Semi-transparent + + + + 保留颜色 + Reserve color + + + + 与背景色叠加 + Compose with backgroundcolor + + + + 确定 + OK + + + + 取消 + Cancel + + + + 恢复默认设定 + Restore to default + + + + 设置背景色 + Set background color + + + diff --git a/SlopeCraft/others/images/converted.png b/SlopeCraft/others/images/converted.png new file mode 100644 index 00000000..cae9ffbe Binary files /dev/null and b/SlopeCraft/others/images/converted.png differ diff --git a/SlopeCraft/others/images/empty.png b/SlopeCraft/others/images/empty.png new file mode 100644 index 00000000..bae1aa58 Binary files /dev/null and b/SlopeCraft/others/images/empty.png differ diff --git a/SlopeCraft/others/images/map_icon.png b/SlopeCraft/others/images/map_icon.png new file mode 100644 index 00000000..9cb5e28d Binary files /dev/null and b/SlopeCraft/others/images/map_icon.png differ diff --git a/SlopeCraftMain/others/cheap.sc_preset_json b/SlopeCraft/others/presets/cheap.sc_preset_json similarity index 88% rename from SlopeCraftMain/others/cheap.sc_preset_json rename to SlopeCraft/others/presets/cheap.sc_preset_json index 78b510c7..f7ef0b1f 100644 --- a/SlopeCraftMain/others/cheap.sc_preset_json +++ b/SlopeCraft/others/presets/cheap.sc_preset_json @@ -11,7 +11,7 @@ }, { "baseColor": 2, - "blockId": "minecraft:end_stone", + "blockId": "minecraft:birch_planks", "enabled": true }, { @@ -62,7 +62,7 @@ { "baseColor": 12, "blockId": "minecraft:water[level=0]", - "enabled": true + "enabled": false }, { "baseColor": 13, @@ -76,77 +76,77 @@ }, { "baseColor": 15, - "blockId": "minecraft:orange_concrete", + "blockId": "minecraft:orange_carpet", "enabled": true }, { "baseColor": 16, - "blockId": "minecraft:magenta_concrete", + "blockId": "minecraft:magenta_carpet", "enabled": true }, { "baseColor": 17, - "blockId": "minecraft:light_blue_concrete", + "blockId": "minecraft:light_blue_carpet", "enabled": true }, { "baseColor": 18, - "blockId": "minecraft:yellow_concrete", + "blockId": "minecraft:yellow_carpet", "enabled": true }, { "baseColor": 19, - "blockId": "minecraft:lime_concrete", + "blockId": "minecraft:lime_carpet", "enabled": true }, { "baseColor": 20, - "blockId": "minecraft:pink_concrete", + "blockId": "minecraft:pink_carpet", "enabled": true }, { "baseColor": 21, - "blockId": "minecraft:gray_concrete", + "blockId": "minecraft:gray_carpet", "enabled": true }, { "baseColor": 22, - "blockId": "minecraft:light_gray_concrete", + "blockId": "minecraft:light_gray_carpet", "enabled": true }, { "baseColor": 23, - "blockId": "minecraft:cyan_concrete", + "blockId": "minecraft:cyan_carpet", "enabled": true }, { "baseColor": 24, - "blockId": "minecraft:purple_concrete", + "blockId": "minecraft:purple_carpet", "enabled": true }, { "baseColor": 25, - "blockId": "minecraft:blue_concrete", + "blockId": "minecraft:blue_carpet", "enabled": true }, { "baseColor": 26, - "blockId": "minecraft:brown_concrete", + "blockId": "minecraft:brown_carpet", "enabled": true }, { "baseColor": 27, - "blockId": "minecraft:green_concrete", + "blockId": "minecraft:green_carpet", "enabled": true }, { "baseColor": 28, - "blockId": "minecraft:red_concrete", + "blockId": "minecraft:red_carpet", "enabled": true }, { "baseColor": 29, - "blockId": "minecraft:black_concrete", + "blockId": "minecraft:black_carpet", "enabled": true }, { diff --git a/SlopeCraftMain/others/elegant.sc_preset_json b/SlopeCraft/others/presets/elegant.sc_preset_json similarity index 97% rename from SlopeCraftMain/others/elegant.sc_preset_json rename to SlopeCraft/others/presets/elegant.sc_preset_json index 60bb60d0..e73cbfcb 100644 --- a/SlopeCraftMain/others/elegant.sc_preset_json +++ b/SlopeCraft/others/presets/elegant.sc_preset_json @@ -62,7 +62,7 @@ { "baseColor": 12, "blockId": "minecraft:water[level=0]", - "enabled": true + "enabled": false }, { "baseColor": 13, @@ -306,7 +306,7 @@ }, { "baseColor": 61, - "blockId": "minecraft:glow_lichen[down=true,east=false,north=false,south=false,up=false,waterlogged=false]", + "blockId": "minecraft:verdant_froglight[axis=y]", "enabled": true } ] \ No newline at end of file diff --git a/SlopeCraftMain/others/shiny.sc_preset_json b/SlopeCraft/others/presets/shiny.sc_preset_json similarity index 97% rename from SlopeCraftMain/others/shiny.sc_preset_json rename to SlopeCraft/others/presets/shiny.sc_preset_json index a7ad88ce..41356ae5 100644 --- a/SlopeCraftMain/others/shiny.sc_preset_json +++ b/SlopeCraft/others/presets/shiny.sc_preset_json @@ -62,7 +62,7 @@ { "baseColor": 12, "blockId": "minecraft:water[level=0]", - "enabled": true + "enabled": false }, { "baseColor": 13, @@ -101,7 +101,7 @@ }, { "baseColor": 20, - "blockId": "minecraft:pink_concrete", + "blockId": "minecraft:pearlescent_froglight[axis=y]", "enabled": true }, { @@ -306,7 +306,7 @@ }, { "baseColor": 61, - "blockId": "minecraft:glow_lichen[down=true,east=false,north=false,south=false,up=false,waterlogged=false]", + "blockId": "minecraft:verdant_froglight[axis=y]", "enabled": true } ] \ No newline at end of file diff --git a/SlopeCraftMain/others/vanilla.sc_preset_json b/SlopeCraft/others/presets/vanilla.sc_preset_json similarity index 99% rename from SlopeCraftMain/others/vanilla.sc_preset_json rename to SlopeCraft/others/presets/vanilla.sc_preset_json index c516508d..b1ef6ff3 100644 --- a/SlopeCraftMain/others/vanilla.sc_preset_json +++ b/SlopeCraft/others/presets/vanilla.sc_preset_json @@ -62,7 +62,7 @@ { "baseColor": 12, "blockId": "minecraft:water[level=0]", - "enabled": true + "enabled": false }, { "baseColor": 13, diff --git a/SlopeCraftL/AiCvterOpt.cpp b/SlopeCraftL/AiCvterOpt.cpp deleted file mode 100644 index 1a08b9b9..00000000 --- a/SlopeCraftL/AiCvterOpt.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "AiCvterOpt.h" - -using namespace SlopeCraft; - -AiCvterOpt::AiCvterOpt() { - popSize = 50; - maxGeneration = 200; - maxFailTimes = 50; - crossoverProb = 0.8; - mutationProb = 0.01; -} diff --git a/SlopeCraftL/AiCvterOpt.h b/SlopeCraftL/AiCvterOpt.h deleted file mode 100644 index e126419a..00000000 --- a/SlopeCraftL/AiCvterOpt.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef AICVTEROPT_H -#define AICVTEROPT_H -#include "SCLDefines.h" -#include "SlopeCraftL.h" - -#ifndef SCL_CAPI -namespace SlopeCraft { -#endif - -struct AiCvterOpt { - AiCvterOpt(); - size_t popSize; - size_t maxGeneration; - size_t maxFailTimes; - double crossoverProb; - double mutationProb; -}; - -#ifndef SCL_CAPI -} // namespace SlopeCraft -#endif - -#endif // AICVTEROPT_H diff --git a/SlopeCraftL/CMakeLists.txt b/SlopeCraftL/CMakeLists.txt index d8febe54..17e1b737 100644 --- a/SlopeCraftL/CMakeLists.txt +++ b/SlopeCraftL/CMakeLists.txt @@ -1,92 +1,121 @@ project(SlopeCraft_SlopeCraftL VERSION ${SlopeCraft_version} LANGUAGES CXX) -include(${CMAKE_SOURCE_DIR}/cmake/find_nlohmann_json.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/configure_fmtlib.cmake) - find_package(ZLIB 1.2.11 REQUIRED) find_package(OpenMP REQUIRED) -find_package(fmt REQUIRED) +find_package(Eigen3 REQUIRED) +find_package(Heu REQUIRED) +find_package(Boost COMPONENTS iostreams OPTIONAL_COMPONENTS multi_array CONFIG REQUIRED) +find_package(libzip REQUIRED) +find_package(tl-expected REQUIRED) +find_package(PNG REQUIRED) +find_package(cereal REQUIRED) +find_package(libnbt++ REQUIRED) +#find_package(zstd REQUIRED) set(SlopeCraft_SCL_windows_rc_files) -if(CMAKE_SYSTEM_NAME STREQUAL "Windows") +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") message(STATUS "Configuring on Windows. Adding rc file to " ${PROJECT_NAME}) configure_file(others/SlopeCraftL.rc.in others/SlopeCraftL.rc) set(SlopeCraft_SCL_windows_rc_files ${CMAKE_CURRENT_BINARY_DIR}/others/SlopeCraftL.rc) -endif() +endif () -set(SlopeCraft_SCL_sources +set(Slope_SCL_internal_headers ${CMAKE_SOURCE_DIR}/utilities/SC_GlobalEnums.h - AiCvterOpt.h - Colors.h - HeightLine.h - OptiChain.h - PrimGlassBuilder.h + # GA_converter_option.h SCLDefines.h - TokiSlopeCraft.h - WaterItem.h - lossyCompressor.h + water_item.h + string_deliver.h + structure_3D.h + + color_table.h + converted_image.h + height_line.h + lossy_compressor.h + mc_block.h + optimize_chain.h + prim_glass_builder.h +) + +set(SlopeCraft_SCL_sources + - simpleBlock.h SlopeCraftL.h - SlopeCraftL_global.h - - AiCvterOpt.cpp - Colors.cpp - ColorSource.cpp - HeightLine.cpp - OptiChain.cpp - PrimGlassBuilder.cpp - TokiSlopeCraft.cpp - TokiSlopeCraft_static_funs.cpp - TokiSlopeCraft_build.cpp - TokiSlopeCraft_convert.cpp - WaterItem.cpp - imagePreprocess.cpp - lossyCompressor.cpp - simpleBlock.cpp + # GA_converter_option.cpp + color_source.cpp + height_line.cpp + optimize_chain.cpp + prim_glass_builder.cpp + image_preprocess.cpp + lossy_compressor.cpp + mc_block.cpp SlopeCraftL.cpp + color_table.cpp + structure_3D.cpp + converted_image.cpp + #${SlopeCraft_SCL_internal_headers} ${SlopeCraft_SCL_windows_rc_files} + block_list.cpp + blocklist.h ) -# add this definations to both targets -add_definitions(-DSLOPECRAFTL_LIBRARY) +add_library(SlopeCraftL SHARED + ${SlopeCraft_SCL_sources} + ${Slope_SCL_internal_headers} +) -add_library(SlopeCraftL SHARED ${SlopeCraft_SCL_sources}) +include(GenerateExportHeader) +generate_export_header(SlopeCraftL BASE_NAME SCL EXPORT_FILE_NAME SlopeCraftL_export.h) # add_library(SlopeCraftL_C SHARED ${SlopeCraft_SCL_sources}) -target_compile_features(SlopeCraftL PRIVATE cxx_std_20) -target_include_directories(SlopeCraftL INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_features(SlopeCraftL PRIVATE cxx_std_23) +target_compile_definitions(SlopeCraftL PRIVATE + SLOPECRAFTL_LIBRARY +) +target_compile_options(SlopeCraftL PRIVATE ${SlopeCraft_vectorize_flags}) +target_precompile_headers(SlopeCraftL PRIVATE ${SlopeCraft_SCL_internal_headers}) +target_include_directories(SlopeCraftL PUBLIC + $ + $ + $) target_include_directories(SlopeCraftL PRIVATE ${SlopeCraft_Nlohmann_json_include_dir} - ${SlopeCraft_Eigen3_include_dir} - ${SlopeCraft_HeuristicFlow_include_dir} - + ${SlopeCraft_Chocobo1_Hash_include_dir} ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/utilities) set(SlopeCraft_SCL_link_libs - - ZLIB::ZLIB - OpenMP::OpenMP_CXX GAConverter ColorManip NBTWriter Schem MapImageCvter - fmt::fmt) + FlatDiagram + libpng_reader + sNBT-formatter + version_set -# link to dependents -target_link_libraries(SlopeCraftL PUBLIC ${SlopeCraft_SCL_link_libs}) + ZLIB::ZLIB + OpenMP::OpenMP_CXX + Eigen3::Eigen + Heu::Genetic + Boost::iostreams + Boost::multi_array + libzip::zip + tl::expected + cereal::cereal + nbt++ + ${SC_zstd_target_name}) -target_compile_options(SlopeCraftL PRIVATE ${SlopeCraft_vectorize_flags}) +# link to dependents +target_link_libraries(SlopeCraftL PRIVATE $) -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") target_compile_options(SlopeCraftL PRIVATE "-fvisibility=hidden" "-fPIC") -endif() +endif () # set shared-lib properties set_target_properties(SlopeCraftL PROPERTIES @@ -97,9 +126,55 @@ set_target_properties(SlopeCraftL PROPERTIES MACOSX_BUNDLE TRUE ) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") set_target_properties(SlopeCraftL PROPERTIES PREFIX "") -endif() +endif () + +function(SC_create_SCL_block_list src_dir archive_path) + if (NOT EXISTS "${src_dir}/block_list.json") + message(FATAL_ERROR "${src_dir}/block_list.json doesn't exist, block list json is missing") + endif () + + cmake_path(GET archive_path PARENT_PATH archive_dir) + file(MAKE_DIRECTORY ${archive_dir}) + + if (EXISTS ${archive_path}) + file(REMOVE ${archive_path}) + endif () + + # file(GLOB_RECURSE file_list "${src_dir}/*.png") + # + # file(ARCHIVE_CREATE OUTPUT temp.zip PATHS ${src_dir} "${src_dir}/block_list.json" FORMAT zip + # #COMPRESSION Zstd + # #COMPRESSION_LEVEL 9 + # ) + + include(${CMAKE_SOURCE_DIR}/cmake/optional_deps/7z.cmake) + + execute_process(COMMAND ${z7_exe} a ${archive_path} -scsUTF-8 "${src_dir}/*" -tZIP + COMMAND_ERROR_IS_FATAL ANY) + + +endfunction() + +file(GLOB block_list_folders "${CMAKE_SOURCE_DIR}/Blocks/*") +foreach (dir ${block_list_folders}) + if (NOT IS_DIRECTORY ${dir}) + continue() + endif () + cmake_path(GET dir FILENAME block_list_name) + message(STATUS "Processing block list: ${dir}, block list name: \"${block_list_name}\"") + SC_create_SCL_block_list(${dir} "${CMAKE_BINARY_DIR}/SCL_block_lists/${block_list_name}.zip") +endforeach () + +add_executable(test_scl_load_blocklist tests/load_scl_blocklist.cpp) +target_link_libraries(test_scl_load_blocklist PRIVATE SlopeCraftL) +target_compile_features(test_scl_load_blocklist PRIVATE cxx_std_23) +if (${WIN32}) + DLLD_add_deploy(SlopeCraftL BUILD_MODE) + DLLD_add_deploy(test_scl_load_blocklist BUILD_MODE VERBOSE) +endif () + include(install.cmake) \ No newline at end of file diff --git a/SlopeCraftL/Colors.cpp b/SlopeCraftL/Colors.cpp deleted file mode 100644 index 38d04d83..00000000 --- a/SlopeCraftL/Colors.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "Colors.h" -#include "TokiSlopeCraft.h" - -#include - -template <> -const colorset_allowed_t *const TokiColor::Allowed = &TokiSlopeCraft::Allowed; -template <> -const colorset_basic_t *const TokiColor::Basic = &TokiSlopeCraft::Basic; - -namespace SlopeCraft { -int colorCount4External() { return TokiSlopeCraft::Allowed.color_count(); } - -extern Eigen::Map BasicRGB4External(int channel) { - return Eigen::Map( - &TokiSlopeCraft::Basic.RGB_mat()(0, channel), - TokiSlopeCraft::Basic.color_count()); -} - -Eigen::Map AllowedRGB4External(int channel) { - return Eigen::Map( - TokiSlopeCraft::Allowed.rgb_data(channel), colorCount4External()); -} - -extern Eigen::Map> -AllowedMapList4External() { - return Eigen::Map>( - TokiSlopeCraft::Allowed.map_data(), colorCount4External()); -} - -} // end namespace SlopeCraft - -void SCL_testHSV() { - const float rgb[][3] = {{0, 0, 0}, {1, 1, 1}, {0.99, 0, 0}, {0, 1, 0}, - {0, 0, 1}, {1, 1, 0}, {1, 0, 1}, {0, 1, 1}}; - - const int N = sizeof(rgb) / sizeof(float[3]); - - float hsv[N][3], irgb[N][3]; - - for (int r = 0; r < N; r++) { - RGB2HSV(rgb[r][0], rgb[r][1], rgb[r][2], hsv[r][0], hsv[r][1], hsv[r][2]); - - HSV2RGB(hsv[r][0], hsv[r][1], hsv[r][2], irgb[r][0], irgb[r][1], - irgb[r][2]); - - printf("RGB = [%f, %f, %f] , ", rgb[r][0], rgb[r][1], rgb[r][2]); - printf("HSV = [%f, %f, %f] , ", hsv[r][0], hsv[r][1], hsv[r][2]); - printf("iRGB = [%f, %f, %f]\n", irgb[r][0], irgb[r][1], irgb[r][2]); - } -} \ No newline at end of file diff --git a/SlopeCraftL/Colors.h b/SlopeCraftL/Colors.h deleted file mode 100644 index df621fb3..00000000 --- a/SlopeCraftL/Colors.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef SCL_COLORS_H -#define SCL_COLORS_H -#include -#include - -#include "SCLDefines.h" -#include - -#include "SCLDefines.h" - -using colorset_allowed_t = colorset_new; -using colorset_basic_t = colorset_new; - -using TokiColor = newTokiColor; - -#endif // SCL_COLORS_H diff --git a/SlopeCraftL/SCLDefines.h b/SlopeCraftL/SCLDefines.h index 59fcf58e..8df58367 100644 --- a/SlopeCraftL/SCLDefines.h +++ b/SlopeCraftL/SCLDefines.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -32,6 +32,8 @@ This file is part of SlopeCraft. #define EIGEN_NO_DEBUG #include #include +#include +#include #include "SlopeCraftL.h" @@ -39,15 +41,30 @@ This file is part of SlopeCraft. #define M_PI 3.14159265358979323846 #endif +#define mapColor2Index(mapColor) (64 * ((mapColor) % 4) + ((mapColor) / 4)) +#define index2mapColor(index) (4 * ((index) % 64) + ((index) / 64)) +#define mapColor2baseColor(mapColor) ((mapColor) >> 2) +#define index2baseColor(index) (mapColor2baseColor(index2mapColor(index))) +#define mapColor2depth(mapColor) ((mapColor) % 4) +#define index2depth(index) (mapColor2depth(index2mapColor(index))) + using std::cout, std::cerr, std::endl; using Eigen::Dynamic; +using colorset_allowed_t = colorset_new; +using colorset_basic_t = colorset_new; + +using TokiColor = newTokiColor; + namespace SlopeCraft { extern const float RGBBasicSource[256 * 3]; +extern const std::unique_ptr basic_colorset; + +} // namespace SlopeCraft -} +#define SC_HASH_ADD_DATA(hasher, obj) hasher.process_bytes(&obj, sizeof(obj)); using ARGB = uint32_t; using EImage = Eigen::Array; @@ -55,4 +72,4 @@ using MapList = Eigen::Array; using ColorList = Eigen::Array; using TempVectorXf = Eigen::Array; -#endif // SCLDEFINES_H +#endif // SCLDEFINES_H diff --git a/SlopeCraftL/SlopeCraftL.cpp b/SlopeCraftL/SlopeCraftL.cpp index cc0e1527..413e2569 100644 --- a/SlopeCraftL/SlopeCraftL.cpp +++ b/SlopeCraftL/SlopeCraftL.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,212 +20,179 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "SlopeCraftL.h" -#include "TokiSlopeCraft.h" -#include "simpleBlock.h" -#include +#include #include +#include +#include +#include -using namespace SlopeCraft; - -// AbstractBlock *AbstractBlock::create() { return new simpleBlock; } - -void AbstractBlock::clear() noexcept { - setBurnable(false); - setDoGlow(false); - setEndermanPickable(false); - setId("minecraft:air"); - setIdOld(""); - setNeedGlass(false); - setVersion(0); - setNameZH(""); - setNameEN(""); - // setWallUseable(false); -} - -Kernel::Kernel() {} +#include "SlopeCraftL.h" +#include "mc_block.h" +#include "blocklist.h" +#include "string_deliver.h" +#include "color_table.h" using namespace SlopeCraft; extern "C" { -SCL_EXPORT Kernel *SCL_createKernel() { return new TokiSlopeCraft; } -SCL_EXPORT void SCL_destroyKernel(Kernel *k) { - delete static_cast(k); +SCL_EXPORT const float *SCL_get_rgb_basic_colorset_source() { + return SlopeCraft::RGBBasicSource; } -SCL_EXPORT AbstractBlock *SCL_createBlock() { return new simpleBlock; } -SCL_EXPORT void SCL_destroyBlock(AbstractBlock *b) { delete b; } - -std::pair parse_block( - const nlohmann::json &jo, std::string_view image_dir) noexcept(false) { - simpleBlock ret; - const int basecolor = jo.at("baseColor"); - if (basecolor < 0 || basecolor >= 64) { - throw std::runtime_error{fmt::format("Invalid base color: {}", basecolor)}; - } - - ret.id = jo.at("id"); - ret.nameZH = jo.at("nameZH"); - ret.nameEN = jo.at("nameEN"); - { - std::string filename = image_dir.data(); - filename += '/'; - filename += jo.at("icon"); - ret.imageFilename = filename; - } - ret.version = jo.at("version"); - if (jo.contains("idOld")) { - ret.idOld = jo.at("idOld"); - } else { - ret.idOld = ret.id; - } - - if (jo.contains("endermanPickable")) { - ret.endermanPickable = jo.at("endermanPickable"); - } - - if (jo.contains("isGlowing")) { - ret.doGlow = jo.at("isGlowing"); - } +SCL_EXPORT mc_block_interface *SCL_create_block() { return new mc_block; } +SCL_EXPORT void SCL_destroy_block(mc_block_interface *b) { delete b; } - if (jo.contains("burnable")) { - ret.burnable = jo.at("burnable"); - } +SCL_EXPORT block_list_interface *SCL_create_block_list( + const char *zip_filename, const block_list_create_info &option) { + auto [res, warnings] = create_block_list_from_file(zip_filename); - if (jo.contains("needGlass")) { - ret.needGlass = jo.at("needGlass"); + SlopeCraft::write_to_sd(option.warnings, warnings); + if (not res) { + SlopeCraft::write_to_sd(option.error, res.error()); + return nullptr; } - return {basecolor, ret}; + auto bl = new block_list{std::move(res.value())}; + assert(bl not_eq nullptr); + return bl; } -BlockListInterface *impl_createBlockList(const char *filename, - const blockListOption &option, - std::string &errmsg) noexcept { - errmsg.reserve(4096); - errmsg.clear(); - - BlockList *bl = new BlockList; - using njson = nlohmann::json; - try { - std::ifstream ifs(filename); - njson jo = njson::parse(ifs, nullptr, true, true); - - njson::array_t arr; - if (jo.contains("FixedBlocks")) { - arr = std::move(jo.at("FixedBlocks")); - } else { - arr = std::move(jo.at("CustomBlocks")); - } - - // parse blocks - for (size_t idx = 0; idx < arr.size(); idx++) { - auto temp = parse_block(arr[idx], option.image_dir); - - auto ptr = new simpleBlock; - *ptr = std::move(temp.second); - bl->blocks().emplace(ptr, temp.first); - } - - } catch (std::exception &e) { - delete bl; - errmsg += fmt::format( - "Exception occured when parsing blocklist json: \"{}\"\n", e.what()); +SCL_EXPORT block_list_interface *SCL_create_block_list_from_buffer( + const void *buffer, size_t buffer_bytes, + const block_list_create_info &option) { + if (buffer == nullptr or buffer_bytes <= 0) { + SlopeCraft::write_to_sd( + option.error, + "SCL_create_block_list_from_buffer met invalid value, either buffer is " + "nullptr or buffer size is 0"); return nullptr; } - // load images - for (auto &pair : bl->blocks()) { - pair.first->image.resize(16, 16); - if (!option.callback_load_image(pair.first->getImageFilename(), - pair.first->image.data())) { - errmsg += fmt::format( - "Failed to load image \"{}\", this error will be ignored.\n", - pair.first->getImageFilename()); - continue; - } - pair.first->image.transposeInPlace(); + auto [res, warnings] = create_block_list_from_buffer( + {reinterpret_cast(buffer), buffer_bytes}); + SlopeCraft::write_to_sd(option.warnings, warnings); + if (not res) { + SlopeCraft::write_to_sd(option.error, res.error()); + return nullptr; } + auto bl = new block_list{std::move(res.value())}; + assert(bl not_eq nullptr); return bl; } -SCL_EXPORT BlockListInterface *SCL_createBlockList( - const char *filename, const blockListOption &option) { - const bool can_write_err = - (option.errmsg != nullptr) && (option.errmsg_capacity > 0); - - std::string errmsg; - - BlockListInterface *ret = impl_createBlockList(filename, option, errmsg); - - if (can_write_err) { - memset(option.errmsg, 0, option.errmsg_capacity); - const size_t copy_len = std::min(option.errmsg_capacity, errmsg.size()); - - memcpy(option.errmsg, errmsg.c_str(), copy_len); - if (option.errmsg_len_dest != nullptr) { - *option.errmsg_len_dest = copy_len; - } - } else { - if (option.errmsg_len_dest != nullptr) { - *option.errmsg_len_dest = 0; - } - } - - return ret; +SCL_EXPORT void SCL_destroy_block_list(block_list_interface *bli) { + delete bli; } -SCL_EXPORT void SCL_destroyBlockList(BlockListInterface *) {} - -SCL_EXPORT AiCvterOpt *SCL_createAiCvterOpt() { return new AiCvterOpt; } -void SCL_EXPORT SCL_destroyAiCvterOpt(AiCvterOpt *a) { delete a; } +SCL_EXPORT GA_converter_option *SCL_createAiCvterOpt() { + return new GA_converter_option; +} +void SCL_EXPORT SCL_destroyAiCvterOpt(GA_converter_option *a) { delete a; } -void SCL_EXPORT SCL_setPopSize(AiCvterOpt *a, unsigned int p) { +void SCL_EXPORT SCL_setPopSize(GA_converter_option *a, unsigned int p) { a->popSize = p; } -void SCL_EXPORT SCL_setMaxGeneration(AiCvterOpt *a, unsigned int p) { +void SCL_EXPORT SCL_setMaxGeneration(GA_converter_option *a, unsigned int p) { a->maxGeneration = p; } -void SCL_EXPORT SCL_setMaxFailTimes(AiCvterOpt *a, unsigned int p) { +void SCL_EXPORT SCL_setMaxFailTimes(GA_converter_option *a, unsigned int p) { a->maxFailTimes = p; } -void SCL_EXPORT SCL_setCrossoverProb(AiCvterOpt *a, double p) { +void SCL_EXPORT SCL_setCrossoverProb(GA_converter_option *a, double p) { a->crossoverProb = p; } -void SCL_EXPORT SCL_setMutationProb(AiCvterOpt *a, double p) { +void SCL_EXPORT SCL_setMutationProb(GA_converter_option *a, double p) { a->mutationProb = p; } -unsigned int SCL_EXPORT SCL_getPopSize(const AiCvterOpt *a) { +unsigned int SCL_EXPORT SCL_getPopSize(const GA_converter_option *a) { return a->popSize; } -unsigned int SCL_EXPORT SCL_getMaxGeneration(const AiCvterOpt *a) { +unsigned int SCL_EXPORT SCL_getMaxGeneration(const GA_converter_option *a) { return a->maxGeneration; } -unsigned int SCL_EXPORT SCL_getMaxFailTimes(const AiCvterOpt *a) { +unsigned int SCL_EXPORT SCL_getMaxFailTimes(const GA_converter_option *a) { return a->maxFailTimes; } -double SCL_EXPORT SCL_getCrossoverProb(const AiCvterOpt *a) { +double SCL_EXPORT SCL_getCrossoverProb(const GA_converter_option *a) { return a->crossoverProb; } -double SCL_EXPORT SCL_getMutationProb(const AiCvterOpt *a) { +double SCL_EXPORT SCL_getMutationProb(const GA_converter_option *a) { return a->mutationProb; } -SCL_EXPORT void SCL_getColorMapPtrs(const float **const rdata, - const float **const gdata, - const float **const bdata, - const uint8_t **mapdata, int *num) { - TokiSlopeCraft::getColorMapPtrs(rdata, gdata, bdata, mapdata, num); +// SCL_EXPORT void SCL_getColorMapPtrs(const float **const rdata, +// const float **const gdata, +// const float **const bdata, +// const uint8_t **mapdata, int *num) { +// TokiSlopeCraft::getColorMapPtrs(rdata, gdata, bdata, mapdata, num); +// } + +SCL_EXPORT const char *SCL_getSCLVersion() { return SC_VERSION_STR; } + +SCL_EXPORT SCL_gameVersion SCL_basecolor_version(uint8_t basecolor) { + if (basecolor <= 51) { + return SCL_gameVersion::ANCIENT; + } + + if (basecolor <= 58) { + return SCL_gameVersion::MC16; + } + + if (basecolor <= 61) { + return SCL_gameVersion::MC17; + } + return SCL_gameVersion::FUTURE; } -SCL_EXPORT const float *SCL_getBasicColorMapPtrs() { - return TokiSlopeCraft::getBasicColorMapPtrs(); +SCL_EXPORT uint8_t SCL_maxBaseColor() { return 61; } + +SCL_EXPORT color_table *SCL_create_color_table( + const color_table_create_info &args) { + auto opt = color_table_impl::create(args); + if (opt) { + return new color_table_impl{std::move(opt.value())}; + } + return nullptr; +} + +SCL_EXPORT void SCL_destroy_color_table(color_table *c) { delete c; } + +SCL_EXPORT void SCL_destroy_converted_image(converted_image *c) { delete c; } +SCL_EXPORT void SCL_destroy_structure_3D(structure_3D *s) { delete s; } + +SCL_EXPORT void SCL_get_base_color_ARGB32(uint32_t dest[64]) { + for (int bc = 0; bc < 64; bc++) { + const int row = bc + 128; + const std::array rgb_f32{ + SlopeCraft::basic_colorset->RGB(row, 0), + SlopeCraft::basic_colorset->RGB(row, 1), + SlopeCraft::basic_colorset->RGB(row, 2), + }; + std::array rgb_u8; + for (int i = 0; i < 3; ++i) { + assert(rgb_f32[i] >= 0); + assert(rgb_f32[i] <= 1.0); + rgb_u8[i] = rgb_f32[i] * 255; + } + + dest[bc] = ARGB32(rgb_u8[0], rgb_u8[1], rgb_u8[2]); + } } -SCL_EXPORT const char *SCL_getSCLVersion() { - return TokiSlopeCraft::getSCLVersion(); +// SCL_EXPORT int SCL_getBlockPalette(const mc_block_interface **blkpp, +// size_t capacity) { +// return TokiSlopeCraft::getBlockPalette(blkpp, capacity); +// } } + +#include +namespace SlopeCraft { +Eigen::Map BasicRGB4External(int channel) { + return Eigen::Map( + &SlopeCraft::basic_colorset->RGB_mat()(0, channel), + SlopeCraft::basic_colorset->color_count()); } +} // namespace SlopeCraft \ No newline at end of file diff --git a/SlopeCraftL/SlopeCraftL.h b/SlopeCraftL/SlopeCraftL.h index 99568d19..e8aab3e4 100644 --- a/SlopeCraftL/SlopeCraftL.h +++ b/SlopeCraftL/SlopeCraftL.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,10 +22,17 @@ This file is part of SlopeCraft. #ifndef KERNEL_H #define KERNEL_H -#include -#include +#include +#include +#include -#include "SlopeCraftL_global.h" +#include "SlopeCraftL_export.h" + +#ifdef SLOPECRAFTL_NOT_INSTALLED +#include +#else +#include "SC_version_buildtime.h" +#endif // SLOPECRAFTL_NOT_INSTALLED // define enumerations first #ifdef SCL_FIND_GlobalEnums_BY_PATH @@ -34,31 +41,82 @@ This file is part of SlopeCraft. #include "SC_GlobalEnums.h" #endif -// declare classes -namespace SlopeCraft { -class AbstractBlock; -struct AiCvterOpt; -class Kernel; -} // namespace SlopeCraft +enum class SCL_item_frame_variant : uint16_t { + common = 0, + glowing = 1, +}; namespace SlopeCraft { -using step = ::SCL_step; + +// using step = ::SCL_step; using mapTypes = ::SCL_mapTypes; using compressSettings = ::SCL_compressSettings; using convertAlgo = ::SCL_convertAlgo; using glassBridgeSettings = ::SCL_glassBridgeSettings; using gameVersion = ::SCL_gameVersion; -using workStatues = ::SCL_workStatues; +using workStatus = ::SCL_workStatus; using errorFlag = ::SCL_errorFlag; -}; // namespace SlopeCraft +struct GA_converter_option { + uint64_t caller_api_version{SC_VERSION_U64}; + size_t popSize{50}; + size_t maxGeneration{200}; + size_t maxFailTimes{50}; + double crossoverProb{0.8}; + double mutationProb{0.01}; +}; -namespace SlopeCraft { +/// struct to deliver string via ABI +struct string_deliver { + string_deliver() = default; + string_deliver(char *p, size_t cap) : data(p), capacity(cap) {} + char *const data{nullptr}; + const size_t capacity{0}; + size_t size{0}; + bool is_complete{true}; + + constexpr bool is_valid() const noexcept { + return this->data != nullptr && this->capacity > 0; + } + + template + [[nodiscard]] static string_deliver from_string(string_t &s) noexcept { + return string_deliver{s.data(), s.size()}; + } +}; +/// Struct to wrap anything like ostream, safe to use in ABI +struct ostream_wrapper { + void *handle{nullptr}; + + using write_fun = void (*)(const void *data, size_t len_bytes, void *handle); + write_fun callback_write_data{nullptr}; + + inline void write(const void *data, size_t len_bytes) { + this->callback_write_data(data, len_bytes, this->handle); + } + inline void write(const char *str) { + this->write(reinterpret_cast(str), strlen(str)); + } + + template + [[nodiscard]] inline static ostream_wrapper wrap_std_ostream( + ostream_t &os) noexcept { + return ostream_wrapper{ + .handle = reinterpret_cast(&os), + .callback_write_data = [](const void *data, size_t len_bytes, + void *h) -> size_t { + ostream_t &os = *reinterpret_cast(h); + os.write(reinterpret_cast(data), len_bytes); + return len_bytes; + }, + }; + } +}; -class AbstractBlock { +class mc_block_interface { public: - AbstractBlock() = default; - virtual ~AbstractBlock() = default; + mc_block_interface() = default; + virtual ~mc_block_interface() = default; /// id of this block virtual const char *getId() const noexcept = 0; @@ -75,6 +133,8 @@ class AbstractBlock { /// if this block can be burnt virtual bool getBurnable() const noexcept = 0; + virtual uint8_t getStackSize() const noexcept = 0; + virtual const char *getNameZH() const noexcept = 0; virtual const char *getNameEN() const noexcept = 0; virtual const char *getImageFilename() const noexcept = 0; @@ -82,7 +142,7 @@ class AbstractBlock { constexpr int imageRows() const noexcept { return 16; } constexpr int imageCols() const noexcept { return 16; } - virtual void getImage(uint32_t *dest, bool is_row_major) const noexcept = 0; + virtual void getImage(uint32_t *dest_row_major) const noexcept = 0; /// set block id virtual void setId(const char *) noexcept = 0; @@ -99,6 +159,8 @@ class AbstractBlock { /// set if this block can be burnt virtual void setBurnable(bool) noexcept = 0; + virtual void setStackSize(uint8_t) noexcept = 0; + virtual void setNameZH(const char *) noexcept = 0; virtual void setNameEN(const char *) noexcept = 0; virtual void setImageFilename(const char *) noexcept = 0; @@ -106,148 +168,318 @@ class AbstractBlock { virtual void setImage(const uint32_t *src, bool is_row_major) noexcept = 0; /// let *b equal to *this - virtual void copyTo(AbstractBlock *b) const noexcept = 0; + virtual void copyTo(mc_block_interface *b) const noexcept = 0; /// set this block to air virtual void clear() noexcept; + + virtual const char *idForVersion(SCL_gameVersion ver) const noexcept = 0; + + virtual bool getNeedStone(SCL_gameVersion v) const noexcept = 0; + virtual void setNeedStone(SCL_gameVersion v, bool) noexcept = 0; }; -class BlockListInterface { +class block_list_interface { public: - BlockListInterface() = default; - virtual ~BlockListInterface() = default; - virtual size_t size() const noexcept = 0; - virtual size_t get_blocks(AbstractBlock **, uint8_t *, - size_t capacity_in_elements) noexcept = 0; + block_list_interface() = default; + virtual ~block_list_interface() = default; + [[nodiscard]] virtual size_t size() const noexcept = 0; + [[nodiscard]] virtual size_t get_blocks( + mc_block_interface **, uint8_t *, + size_t capacity_in_elements) noexcept = 0; + + [[nodiscard]] virtual size_t get_blocks( + const mc_block_interface **, uint8_t *, + size_t capacity_in_elements) const noexcept = 0; + + [[nodiscard]] virtual bool contains( + const mc_block_interface *) const noexcept = 0; +}; + +struct color_map_ptrs { + const float *r_data; + const float *g_data; + const float *b_data; + const uint8_t *map_data; + int num_colors; +}; - virtual size_t get_blocks(const AbstractBlock **, uint8_t *, - size_t capacity_in_elements) const noexcept = 0; +struct progress_callbacks { + void *widget{nullptr}; + void (*cb_set_range)(void *, int, int, int){nullptr}; + void (*cb_add)(void *, int){nullptr}; + + inline void set_range(int min, int max, int val) const { + if (this->cb_set_range) { + this->cb_set_range(this->widget, min, max, val); + } + } + inline void add(int delta) const { + if (this->cb_add) { + this->cb_add(this->widget, delta); + } + } +}; + +struct ui_callbacks { + void *wind{nullptr}; + void (*cb_keep_awake)(void *){nullptr}; + void (*cb_report_error)(void *, errorFlag, const char *){nullptr}; + void (*cb_report_working_status)(void *, workStatus){nullptr}; + + inline void keep_awake() const { + if (this->cb_keep_awake) { + this->cb_keep_awake(this->wind); + } + } + + inline void report_error(errorFlag e, const char *msg) const { + if (this->cb_report_error) { + this->cb_report_error(this->wind, e, msg); + } + } + inline void report_working_status(workStatus ws) const { + if (this->cb_report_working_status) { + this->cb_report_working_status(this->wind, ws); + } + } +}; + +struct convert_option { + uint64_t caller_api_version{SC_VERSION_U64}; + SCL_convertAlgo algo{SCL_convertAlgo::RGB_Better}; + bool dither{false}; + GA_converter_option ai_cvter_opt{}; + progress_callbacks progress{}; + ui_callbacks ui{}; +}; + +struct map_data_file_options { + uint64_t caller_api_version{SC_VERSION_U64}; + const char *folder_path{""}; + int begin_index{0}; + progress_callbacks progress{}; + ui_callbacks ui{}; +}; + +struct map_data_file_give_command_options { + uint64_t caller_api_version{SC_VERSION_U64}; + ostream_wrapper *destination{}; + int begin_index{0}; + uint8_t stack_count{1}; /// <- Stack count of filed_map + bool set_name_as_index{true}; + bool after_1_12{false}; + bool after_1_20_5{false}; +}; - virtual bool contains(const AbstractBlock *) const noexcept = 0; +struct assembled_maps_options { + uint64_t caller_api_version{SC_VERSION_U64}; + SCL_item_frame_variant frame_variant{SCL_item_frame_variant::common}; + SCL_map_facing map_facing{SCL_map_facing::wall_north}; + SCL_gameVersion mc_version{SCL_gameVersion::MC19}; + bool after_1_20_5{false}; + bool fixed_frame{true}; + bool invisible_frame{true}; + int begin_index{0}; }; -class Kernel { +struct build_options { + uint64_t caller_api_version{SC_VERSION_U64}; + uint16_t max_allowed_height{256}; + uint16_t bridge_interval{3}; + compressSettings compress_method{::SCL_compressSettings::noCompress}; + glassBridgeSettings glass_method{::SCL_glassBridgeSettings::noBridge}; + bool fire_proof{false}; + bool enderman_proof{false}; + + // added in v5.1.0 + bool connect_mushrooms{false}; + // added in v5.3 + ui_callbacks ui; + progress_callbacks main_progressbar; + progress_callbacks sub_progressbar; +}; + +struct litematic_options { + uint64_t caller_api_version{SC_VERSION_U64}; + const char *litename_utf8 = "by SlopeCraft"; + const char *region_name_utf8 = "by SlopeCraft"; + ui_callbacks ui; + progress_callbacks progressbar; +}; +struct vanilla_structure_options { + uint64_t caller_api_version{SC_VERSION_U64}; + bool is_air_structure_void{true}; + ui_callbacks ui; + progress_callbacks progressbar; +}; +struct WE_schem_options { + uint64_t caller_api_version{SC_VERSION_U64}; + int offset[3] = {0, 0, 0}; + int we_offset[3] = {0, 0, 0}; + const char *const *required_mods_name_utf8{nullptr}; + int num_required_mods{0}; + ui_callbacks ui; + progress_callbacks progressbar; +}; + +struct flag_diagram_options { + uint64_t caller_api_version{SC_VERSION_U64}; + + // 0 or negative number means no split lines + int32_t split_line_row_margin{0}; + // 0 or negative number means no split lines + int32_t split_line_col_margin{0}; + int png_compress_level{9}; + int png_compress_memory_level{8}; + + ui_callbacks ui{}; + progress_callbacks progressbar{}; +}; + +struct test_blocklist_options { + uint64_t caller_api_version{SC_VERSION_U64}; + const mc_block_interface *const *block_ptrs{nullptr}; + const uint8_t *basecolors{nullptr}; + size_t block_count{0}; + string_deliver *err{nullptr}; +}; + +struct const_image_reference { + const uint32_t *data{nullptr}; + size_t rows{0}; + size_t cols{0}; +}; + +class color_table; +class converted_image; +class structure_3D; + +class color_table { + public: + virtual ~color_table() = default; + [[nodiscard]] virtual color_map_ptrs colors() const noexcept = 0; + [[nodiscard]] virtual ::SCL_mapTypes map_type() const noexcept = 0; + + [[nodiscard]] inline bool is_vanilla() const noexcept { + return this->map_type() != SCL_mapTypes::FileOnly; + } + + [[nodiscard]] inline bool is_flat() const noexcept { + return this->map_type() == SCL_mapTypes::Flat; + } + + [[nodiscard]] virtual ::SCL_gameVersion mc_version() const noexcept = 0; + [[nodiscard]] virtual size_t num_blocks() const noexcept = 0; + virtual void visit_blocks(void (*)(const mc_block_interface *, + void *custom_data), + void *custom_data) const = 0; + + template + void visit_blocks(visitor v) const { + this->visit_blocks( + [](const mc_block_interface *b, void *custom_data) { + visitor *fun = reinterpret_cast(custom_data); + (*fun)(b); + }, + &v); + } + + [[nodiscard]] virtual converted_image *convert_image( + const_image_reference original_img, + const convert_option &option) const noexcept = 0; + + [[nodiscard]] virtual bool has_convert_cache( + const_image_reference original_img, const convert_option &option, + const char *cache_dir) const noexcept = 0; + [[nodiscard]] virtual bool save_convert_cache( + const_image_reference original_img, const convert_option &option, + const converted_image &, const char *cache_dir, + string_deliver *error) const noexcept = 0; + [[nodiscard]] virtual converted_image *load_convert_cache( + const_image_reference original_img, const convert_option &option, + const char *cache_dir, string_deliver *error) const noexcept = 0; + + [[nodiscard]] virtual structure_3D *build( + const converted_image &, const build_options &) const noexcept = 0; + // Once you cache a structure_3D, it can be released to save memory + [[nodiscard]] virtual bool save_build_cache( + const converted_image &, const build_options &, const structure_3D &, + const char *cache_root_dir, string_deliver *error) const noexcept = 0; + [[nodiscard]] virtual bool has_build_cache( + const converted_image &, const build_options &, + const char *cache_root_dir) const noexcept = 0; + [[nodiscard]] virtual structure_3D *load_build_cache( + const converted_image &, const build_options &, + const char *cache_root_dir, string_deliver *error) const noexcept = 0; + + virtual void stat_blocks(const structure_3D &, + size_t buffer[64]) const noexcept = 0; + + [[nodiscard]] virtual bool generate_test_schematic( + const char *filename, + const test_blocklist_options &option) const noexcept = 0; +}; + +class converted_image { public: - Kernel(); - // virtual ~Kernel() {}; + virtual ~converted_image() = default; + [[nodiscard]] virtual size_t rows() const noexcept = 0; + [[nodiscard]] virtual size_t cols() const noexcept = 0; + [[nodiscard]] inline size_t height() const noexcept { return this->rows(); } + [[nodiscard]] inline size_t width() const noexcept { return this->cols(); } + [[nodiscard]] inline size_t size() const noexcept { + return this->rows() * this->cols(); + } + + [[nodiscard]] inline size_t size_in_bytes() const noexcept { + return sizeof(uint32_t) * this->rows() * this->cols(); + } + + virtual void get_original_image(uint32_t *buffer) const noexcept = 0; + // virtual void get_dithered_image(uint32_t *buffer) const noexcept = 0; + virtual void get_converted_image(uint32_t *buffer) const noexcept = 0; + + virtual void get_compressed_image(const structure_3D &structure, + uint32_t *buffer) const noexcept = 0; + + [[nodiscard]] virtual bool export_map_data( + const map_data_file_options &option) const noexcept = 0; + + [[nodiscard]] virtual bool is_converted_from( + const color_table &) const noexcept = 0; + + [[nodiscard]] virtual bool get_map_command( + const map_data_file_give_command_options &option) const = 0; + + [[nodiscard]] virtual bool export_assembled_maps_litematic( + const char *filename, const assembled_maps_options &, + const litematic_options &) const noexcept = 0; + [[nodiscard]] virtual bool export_assembled_maps_vanilla_structure( + const char *filename, const assembled_maps_options &, + const vanilla_structure_options &) const noexcept = 0; +}; +class structure_3D { public: - /// function ptr to window object - virtual void setWindPtr(void *) = 0; - /// a function ptr to show progress of converting and exporting - virtual void setProgressRangeSet(void (*)(void *, int, int, int)) = 0; - /// a function ptr to add progress value - virtual void setProgressAdd(void (*)(void *, int)) = 0; - /// a function ptr to prevent window from being syncoped - virtual void setKeepAwake(void (*)(void *)) = 0; - - /// a function ptr to show progress of compressing and bridge-building - virtual void setAlgoProgressRangeSet(void (*)(void *, int, int, int)) = 0; - /// a function ptr to add progress value of compressing and bridge-building - virtual void setAlgoProgressAdd(void (*)(void *, int)) = 0; - - /// a function ptr to report error when something wrong happens - virtual void setReportError(void (*)(void *, ::SCL_errorFlag, - const char *)) = 0; - /// a function ptr to report working statue especially when busy - virtual void setReportWorkingStatue(void (*)(void *, ::SCL_workStatues)) = 0; - - virtual void setAiCvterOpt(const AiCvterOpt *) = 0; - - virtual const AiCvterOpt *aiCvterOpt() const = 0; - - // can do in nothing: - /// real size of kernel - virtual unsigned long long size() = 0; - /// revert to a previous step - virtual void decreaseStep(::SCL_step) = 0; - - // can do in colorSetReady: - /// get current step - virtual ::SCL_step queryStep() const = 0; - /// set map type and blocklist - virtual bool setType(::SCL_mapTypes, ::SCL_gameVersion, const bool[64], - const AbstractBlock *const *const) = 0; - /// get palette (base colors only) in ARGB32 - virtual void getBaseColorInARGB32(uint32_t *const) const = 0; - - // can do in wait4Image: - /// set original image from ARGB32 matrix (col-major) - virtual void setRawImage(const uint32_t *src, int rows, int cols) = 0; - /// get accessible color count - virtual unsigned short getColorCount() const = 0; - /// get usable colors in ARGB32 - virtual void getAvailableColors(uint32_t *const ARGB32_dest = nullptr, - uint8_t *const map_color_dest = nullptr, - int *const num = nullptr) const = 0; - /// make a structure that includes all accessible blocks - virtual bool makeTests(const AbstractBlock **, const uint8_t *, const char *, - char *) = 0; - - // can do in convertionReady: - /// convert original image to map - virtual bool convert(::SCL_convertAlgo = ::SCL_convertAlgo::RGB_Better, - bool dither = false) = 0; - /// get image rows - virtual int getImageRows() const = 0; - /// get image cols - virtual int getImageCols() const = 0; - virtual const uint32_t *getRawImage() const = 0; - /// query if map is buildable in vanilla survival - virtual bool isVanilla() const = 0; - /// query if map is a flat one - virtual bool isFlat() const = 0; - - // can do in converted: - /// construct 3D structure - virtual bool build( - ::SCL_compressSettings = ::SCL_compressSettings::noCompress, - unsigned short = 256, - ::SCL_glassBridgeSettings = ::SCL_glassBridgeSettings::noBridge, - unsigned short = 3, bool fireProof = false, - bool endermanProof = false) = 0; - - /// get converted image - virtual void getConvertedImage(int *rows, int *cols, - uint32_t *dest) const = 0; - /// export as map data files, returns failed files. - virtual void exportAsData(const char *FolderPath, const int indexStart, - int *fileCount, char **dest) const = 0; - /// get converted map(in mapColor array) - virtual void getConvertedMap(int *rows, int *cols, uint8_t *) const = 0; - - // can do in builded: - /// export map into litematica files (*.litematic) - virtual void exportAsLitematic(const char *localEncoding_TargetName, - const char *utf8_LiteName, - const char *utf8_RegionName, - char *localEncoding_returnVal) const = 0; - /// export map into Structure files (*.NBT) - virtual void exportAsStructure(const char *localEncoding_TargetName, - char *localEncoding_FileName) const = 0; - virtual void exportAsWESchem( - const char *localEncoding_fileName, const int (&offset)[3] = {0, 0, 0}, - const int (&weOffset)[3] = {0, 0, 0}, const char *utf8_Name = "", - const char *const *const utf8_requiredMods = nullptr, - const int requiredModsCount = 0, - char *localEncoding_returnVal = nullptr) const = 0; - - /// get x,y,z size - virtual void get3DSize(int *x, int *y, int *z) const = 0; - - /// get 3d structure's size - virtual int getHeight() const = 0; - /// get 3d structure's size - virtual int getXRange() const = 0; - /// get 3d structure's size - virtual int getZRange() const = 0; - /// get block count in total and in detail - virtual void getBlockCounts(int *total, int detail[64]) const = 0; - /// get sum block count - virtual int64_t getBlockCounts() const = 0; - /// get 3d structure in 3d-matrix (col major) - virtual const unsigned short *getBuild(int *xSize = nullptr, - int *ySize = nullptr, - int *zSize = nullptr) const = 0; + virtual ~structure_3D() = default; + [[nodiscard]] virtual size_t shape_x() const noexcept = 0; + [[nodiscard]] virtual size_t shape_y() const noexcept = 0; + [[nodiscard]] virtual size_t shape_z() const noexcept = 0; + [[nodiscard]] virtual size_t palette_length() const noexcept = 0; + virtual void get_palette(const char **buffer_block_id) const noexcept = 0; + + [[nodiscard]] virtual bool export_litematica( + const char *filename, const litematic_options &option) const noexcept = 0; + [[nodiscard]] virtual bool export_vanilla_structure( + const char *filename, + const vanilla_structure_options &option) const noexcept = 0; + [[nodiscard]] virtual bool export_WE_schem( + const char *filename, const WE_schem_options &option) const noexcept = 0; + [[nodiscard]] virtual bool export_flat_diagram( + const char *filename, const color_table &table, + const flag_diagram_options &option) const noexcept = 0; + + [[nodiscard]] virtual uint64_t block_count() const noexcept = 0; }; } // namespace SlopeCraft @@ -256,39 +488,38 @@ class Kernel { extern "C" { namespace SlopeCraft { -[[nodiscard]] SCL_EXPORT Kernel *SCL_createKernel(); -SCL_EXPORT void SCL_destroyKernel(Kernel *); +[[nodiscard]] SCL_EXPORT const float *SCL_get_rgb_basic_colorset_source(); -[[nodiscard]] SCL_EXPORT AbstractBlock *SCL_createBlock(); -SCL_EXPORT void SCL_destroyBlock(AbstractBlock *); +[[nodiscard]] SCL_EXPORT mc_block_interface *SCL_create_block(); +SCL_EXPORT void SCL_destroy_block(mc_block_interface *); -struct blockListOption { - const char *image_dir; - bool (*callback_load_image)(const char *, uint32_t *dst_row_major){nullptr}; - char *errmsg{nullptr}; - size_t errmsg_capacity{0}; - size_t *errmsg_len_dest{nullptr}; +struct color_table_create_info { + ::SCL_mapTypes map_type; + ::SCL_gameVersion mc_version; + bool basecolor_allow_LUT[64]; + const mc_block_interface *blocks[64]; + ui_callbacks ui; }; -[[nodiscard]] SCL_EXPORT BlockListInterface *SCL_createBlockList( - const char *filename, const blockListOption &option); +[[nodiscard]] SCL_EXPORT color_table *SCL_create_color_table( + const color_table_create_info &); +SCL_EXPORT void SCL_destroy_color_table(color_table *); -SCL_EXPORT void SCL_destroyBlockList(BlockListInterface *); +SCL_EXPORT void SCL_destroy_converted_image(converted_image *); +SCL_EXPORT void SCL_destroy_structure_3D(structure_3D *); -[[nodiscard]] SCL_EXPORT AiCvterOpt *SCL_createAiCvterOpt(); -SCL_EXPORT void SCL_destroyAiCvterOpt(AiCvterOpt *); - -SCL_EXPORT void SCL_setPopSize(AiCvterOpt *, uint32_t p); -SCL_EXPORT void SCL_setMaxGeneration(AiCvterOpt *, uint32_t p); -SCL_EXPORT void SCL_setMaxFailTimes(AiCvterOpt *, uint32_t p); -SCL_EXPORT void SCL_setCrossoverProb(AiCvterOpt *, double p); -SCL_EXPORT void SCL_setMutationProb(AiCvterOpt *, double p); +struct block_list_create_info { + uint64_t caller_api_version{SC_VERSION_U64}; + string_deliver *warnings{nullptr}; + string_deliver *error{nullptr}; +}; -SCL_EXPORT uint32_t SCL_getPopSize(const AiCvterOpt *); -SCL_EXPORT uint32_t SCL_getMaxGeneration(const AiCvterOpt *); -SCL_EXPORT uint32_t SCL_getMaxFailTimes(const AiCvterOpt *); -SCL_EXPORT double SCL_getCrossoverProb(const AiCvterOpt *); -SCL_EXPORT double SCL_getMutationProb(const AiCvterOpt *); +[[nodiscard]] SCL_EXPORT block_list_interface *SCL_create_block_list( + const char *zip_filename, const block_list_create_info &option); +[[nodiscard]] SCL_EXPORT block_list_interface * +SCL_create_block_list_from_buffer(const void *buffer, size_t buffer_bytes, + const block_list_create_info &option); +SCL_EXPORT void SCL_destroy_block_list(block_list_interface *); SCL_EXPORT void SCL_preprocessImage( uint32_t *ARGB32ptr, const uint64_t imageSize, @@ -296,19 +527,39 @@ SCL_EXPORT void SCL_preprocessImage( const SCL_HalfTpPixelSt = SCL_HalfTpPixelSt::ComposeWithBackGround, uint32_t backGround = 0xFFFFFFFF); -SCL_EXPORT uint8_t SCL_maxAvailableVersion(); +SCL_EXPORT bool SCL_haveTransparentPixel(const uint32_t *ARGB32, + const uint64_t imageSize); + +SCL_EXPORT SCL_gameVersion SCL_maxAvailableVersion(); SCL_EXPORT const char *SCL_getSCLVersion(); -SCL_EXPORT void SCL_getColorMapPtrs(const float **const rdata, - const float **const gdata, - const float **const bdata, const uint8_t **, - int *); -// full palette SCL_EXPORT const float *SCL_getBasicColorMapPtrs(); +SCL_EXPORT uint8_t SCL_maxBaseColor(); +SCL_EXPORT SCL_gameVersion SCL_basecolor_version(uint8_t basecolor); +SCL_EXPORT void SCL_get_base_color_ARGB32(uint32_t dest[64]); + +// SCL_EXPORT SCL_gameVersion SCL_basecolor_version(uint8_t basecolor); // SCL_EXPORT uint64_t SCL_mcVersion2VersionNumber(::SCL_gameVersion); +class deleter { + public: + // void operator()(Kernel *k) const noexcept { SCL_destroyKernel(k); } + void operator()(mc_block_interface *b) const noexcept { + SCL_destroy_block(b); + } + void operator()(block_list_interface *b) const noexcept { + SCL_destroy_block_list(b); + } + void operator()(color_table *c) const noexcept { SCL_destroy_color_table(c); } + void operator()(converted_image *c) const noexcept { + SCL_destroy_converted_image(c); + } + void operator()(structure_3D *s) const noexcept { + SCL_destroy_structure_3D(s); + } +}; } // namespace SlopeCraft } // extern "C" diff --git a/SlopeCraftL/SlopeCraftL_global.h b/SlopeCraftL/SlopeCraftL_global.h deleted file mode 100644 index 182f12a8..00000000 --- a/SlopeCraftL/SlopeCraftL_global.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef SLOPECRAFTL_GLOBAL_H -#define SLOPECRAFTL_GLOBAL_H - -#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || \ - defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || \ - defined(__WIN32__) || defined(__NT__) -#define Q_DECL_EXPORT __declspec(dllexport) -#define Q_DECL_IMPORT __declspec(dllimport) -#else -#define Q_DECL_EXPORT __attribute__((visibility("default"))) -#define Q_DECL_IMPORT __attribute__((visibility("default"))) -#endif - -#if defined(SLOPECRAFTL_LIBRARY) -#define SLOPECRAFTL_EXPORT Q_DECL_EXPORT -#else -#define SLOPECRAFTL_EXPORT Q_DECL_IMPORT -#endif - -#define SCL_EXPORT SLOPECRAFTL_EXPORT - -#ifndef SCL_external_class -#ifdef SCL_CAPI -#define SCL_external_class struct -#else -#define SCL_external_class class SCL_EXPORT -#endif -#endif - -#ifdef SLOPECRAFTL_NOT_INSTALLED -#include -#else -#include "SC_version_buildtime.h" -#endif // SLOPECRAFTL_NOT_INSTALLED - -#endif // SLOPECRAFTL_GLOBAL_H diff --git a/SlopeCraftL/TokiSlopeCraft.cpp b/SlopeCraftL/TokiSlopeCraft.cpp deleted file mode 100644 index 8e9a2f5c..00000000 --- a/SlopeCraftL/TokiSlopeCraft.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TokiSlopeCraft.h" -#include - -const Eigen::Array TokiSlopeCraft::DitherMapLR = { - {0.0 / 16.0, 0.0 / 16.0, 7.0 / 16.0}, {3.0 / 16.0, 5.0 / 16.0, 1.0 / 16.0}}; -const Eigen::Array TokiSlopeCraft::DitherMapRL = { - {7.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0}, {1.0 / 16.0, 5.0 / 16.0, 3.0 / 16.0}}; - -const colorset_basic_t TokiSlopeCraft::Basic(SlopeCraft::RGBBasicSource); -colorset_allowed_t TokiSlopeCraft::Allowed; - -gameVersion TokiSlopeCraft::mcVer{ - SCL_gameVersion::MC17}; // 12,13,14,15,16,17,18,19 -mapTypes TokiSlopeCraft::mapType; -std::vector TokiSlopeCraft::blockPalette(0); - -std::unordered_set TokiSlopeCraft::kernel_hash_set; - -std::mutex SCL_internal_lock; - -template <> -const colorset_basic_t &libImageCvt::ImageCvter::basic_colorset = - TokiSlopeCraft::Basic; - -template <> -const colorset_allowed_t &libImageCvt::ImageCvter::allowed_colorset = - TokiSlopeCraft::Allowed; - -TokiSlopeCraft::TokiSlopeCraft() { - kernelStep = step::nothing; - this->image_cvter.clear_images(); - this->image_cvter.clear_color_hash(); - // rawImage.setZero(0, 0); - - glassBuilder = new PrimGlassBuilder; - Compressor = new LossyCompressor; - // GAConverter = new GACvter::GAConverter; - setProgressRangeSet([](void *, int, int, int) {}); - setProgressAdd([](void *, int) {}); - setKeepAwake([](void *) {}); - setReportError([](void *, errorFlag, const char *) {}); - setReportWorkingStatue([](void *, workStatues) {}); - setAlgoProgressAdd([](void *, int) {}); - setAlgoProgressRangeSet([](void *, int, int, int) {}); - - glassBuilder->windPtr = &wind; - glassBuilder->progressAddPtr = &this->algoProgressAdd; - glassBuilder->progressRangeSetPtr = &this->algoProgressRangeSet; - glassBuilder->keepAwakePtr = &this->keepAwake; - - Compressor->windPtr = &wind; - Compressor->progressAddPtr = &this->algoProgressAdd; - Compressor->progressRangeSetPtr = &this->algoProgressRangeSet; - Compressor->keepAwakePtr = &this->keepAwake; - - ::SCL_internal_lock.lock(); - TokiSlopeCraft::kernel_hash_set.emplace(this); - for (auto kptr : kernel_hash_set) { - if (kptr->kernelStep >= step::wait4Image) { - this->kernelStep = step::wait4Image; - break; - } - } - ::SCL_internal_lock.unlock(); -} - -TokiSlopeCraft::~TokiSlopeCraft() { - delete Compressor; - delete glassBuilder; - // delete GAConverter; - - ::SCL_internal_lock.lock(); - TokiSlopeCraft::kernel_hash_set.erase(this); - ::SCL_internal_lock.unlock(); -} - -/// function ptr to window object -void TokiSlopeCraft::setWindPtr(void *_w) { - wind = _w; - this->image_cvter.ui._uiPtr = _w; - // GAConverter->setUiPtr(_w); -} -/// a function ptr to show progress of converting and exporting -void TokiSlopeCraft::setProgressRangeSet(void (*prs)(void *, int, int, int)) { - progressRangeSet = prs; - this->image_cvter.ui.progressRangeSet = prs; - // GAConverter->setProgressRangeFun(prs); -} -/// a function ptr to add progress value -void TokiSlopeCraft::setProgressAdd(void (*pa)(void *, int)) { - progressAdd = pa; - this->image_cvter.ui.progressAdd = pa; - // GAConverter->setProgressAddFun(pa); -} -/// a function ptr to prevent window from being syncoped -void TokiSlopeCraft::setKeepAwake(void (*ka)(void *)) { keepAwake = ka; } - -/// a function ptr to show progress of compressing and bridge-building -void TokiSlopeCraft::setAlgoProgressRangeSet(void (*aprs)(void *, int, int, - int)) { - algoProgressRangeSet = aprs; -} -/// a function ptr to add progress value of compressing and bridge-building -void TokiSlopeCraft::setAlgoProgressAdd(void (*apa)(void *, int)) { - algoProgressAdd = apa; -} - -/// a function ptr to report error when something wrong happens -void TokiSlopeCraft::setReportError(void (*re)(void *, errorFlag, - const char *)) { - reportError = re; -} -/// a function ptr to report working statue especially when busy -void TokiSlopeCraft::setReportWorkingStatue(void (*rws)(void *, workStatues)) { - reportWorkingStatue = rws; -} - -void TokiSlopeCraft::decreaseStep(step _step) { - if (kernelStep <= _step) return; - kernelStep = _step; -} - -void TokiSlopeCraft::trySkipStep(step s) { - if (this->kernelStep >= s) { - return; - } - - if (Allowed.color_count() != 0 && blockPalette.size() != 0) { - this->kernelStep = step::wait4Image; - } -} -/* -bool compressFile(const char *inputPath, const char *outputPath) { - const size_t BUFFER_SIZE = 4096; - std::ifstream fin; - fin.open(inputPath, std::ifstream::binary | std::ifstream::in); - if (!fin) - return false; - std::vector buffer(BUFFER_SIZE, 0); - gzFile fout = gzopen(outputPath, "wb"); - while (!fin.eof()) { - fin.read(buffer.data(), buffer.size()); - std::streamsize s = fin.gcount(); - gzwrite(fout, buffer.data(), s); - } - gzclose(fout); - return true; -} - -*/ - -step TokiSlopeCraft::queryStep() const { return kernelStep; } - -void TokiSlopeCraft::setAiCvterOpt(const AiCvterOpt *_a) { AiOpt = *_a; } - -const AiCvterOpt *TokiSlopeCraft::aiCvterOpt() const { return &AiOpt; } - -bool TokiSlopeCraft::setType(mapTypes type, gameVersion ver, - const bool *allowedBaseColor, - const AbstractBlock *const *const palettes) { - return __impl_setType(type, ver, allowedBaseColor, palettes, this); -} - -bool TokiSlopeCraft::__impl_setType(mapTypes type, gameVersion ver, - const bool allowedBaseColor[64], - const AbstractBlock *const *const palettes, - const TokiSlopeCraft *reporter) noexcept { - ::SCL_internal_lock.lock(); - /* - if (kernelStep < colorSetReady) - { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You must load colorset before you set the map type."); - return false; - } - */ - - TokiSlopeCraft::mapType = type; - TokiSlopeCraft::mcVer = ver; - - TokiColor::needFindSide = (TokiSlopeCraft::mapType == mapTypes::Slope); - - TokiSlopeCraft::blockPalette.resize(64); - - // cerr<<__FILE__<<__LINE__<copyTo(&TokiSlopeCraft::blockPalette[i]); - - if (TokiSlopeCraft::blockPalette[i].id.find(':') == - TokiSlopeCraft::blockPalette[i].id.npos) { - TokiSlopeCraft::blockPalette[i].id = - "minecraft:" + TokiSlopeCraft::blockPalette[i].id; - } - - if (TokiSlopeCraft::blockPalette[i].idOld.empty()) { - TokiSlopeCraft::blockPalette[i].idOld = blockPalette[i].id; - } - - if (TokiSlopeCraft::blockPalette[i].idOld.size() > 0 && - (TokiSlopeCraft::blockPalette[i].idOld.find(':') == - TokiSlopeCraft::blockPalette[i].idOld.npos)) { - TokiSlopeCraft::blockPalette[i].idOld = - "minecraft:" + TokiSlopeCraft::blockPalette[i].idOld; - } - } - - // cerr<<__FILE__<<__LINE__<reportWorkingStatue(reporter->wind, workStatues::collectingColors); - - Eigen::ArrayXi baseColorVer(64); // 基色对应的版本 - baseColorVer.setConstant((int)SCL_gameVersion::FUTURE); - baseColorVer.segment(0, 52).setConstant((int)SCL_gameVersion::ANCIENT); - baseColorVer.segment(52, 7).setConstant((int)SCL_gameVersion::MC16); - baseColorVer.segment(59, 3).setConstant((int)SCL_gameVersion::MC17); - - bool MIndex[256]; - - for (short index = 0; index < 256; index++) { - MIndex[index] = true; // 默认可以使用这种颜色,逐次判断各个不可以使用的条件 - - if (!allowedBaseColor[index2baseColor( - index)]) { // 在 allowedBaseColor 中被禁止 - MIndex[index] = false; - continue; - } - if (index2baseColor(index) == 0) { // 空气禁用 - MIndex[index] = false; - continue; - } - if ((int)mcVer < - baseColorVer(index2baseColor(index))) { // 版本低于基色版本 - MIndex[index] = false; - continue; - } - if (TokiSlopeCraft::blockPalette[index2baseColor(index)] - .id.empty()) { // 空 id - MIndex[index] = false; - continue; - } - /* - if ((mapType == mapTypes::Wall) && - !blockPalette[index2baseColor(index)] - .wallUseable) { //墙面像素画且当前方块不适合墙面 - - MIndex[index] = false; - continue; - }*/ - if (is_vanilla_static() && - (index2depth(index) >= 3)) { // 可实装的地图画不允许第四种阴影 - MIndex[index] = false; - continue; - } - if (index2baseColor(index) == 12) { // 如果是水且非墙面 - if (is_flat_static() && index2depth(index) != 2) { // 平板且水深不是 1 格 - MIndex[index] = false; - continue; - } - } else { - if (is_flat_static() && index2depth(index) != 1) { // 平板且阴影不为 1 - MIndex[index] = false; - continue; - } - } - } - - if (!TokiSlopeCraft::Allowed.apply_allowed(Basic, MIndex)) { - std::string msg = "Too few usable color(s) : only " + - std::to_string(Allowed.color_count()) + " colors\n"; - msg += "Avaliable base color(s) : "; - - for (int idx = 0; idx < Allowed.color_count(); idx++) { - msg += std::to_string(Allowed.Map(idx)) + ", "; - } - - reporter->reportError(reporter->wind, errorFlag::USEABLE_COLOR_TOO_FEW, - msg.data()); - ::SCL_internal_lock.unlock(); - return false; - } - - GACvter::updateMapColor2GrayLUT(); - - reporter->reportWorkingStatue(reporter->wind, workStatues::none); - for (auto kernel_ptr : TokiSlopeCraft::kernel_hash_set) { - kernel_ptr->image_cvter.on_color_set_changed(); - kernel_ptr->kernelStep = SCL_step::wait4Image; - } - - ::SCL_internal_lock.unlock(); - return true; -} - -uint16_t TokiSlopeCraft::getColorCount() const { - if (kernelStep < SCL_step::wait4Image) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can only query for avaliable color count after you set " - "the map type and gameversion"); - return 0; - } - return Allowed.color_count(); -} - -void TokiSlopeCraft::getAvailableColors(ARGB *const ARGBDest, - uint8_t *const mapColorDest, - int *const num) const { - if (num != nullptr) { - *num = getColorCount(); - } - - for (int idx = 0; idx < TokiSlopeCraft::Allowed.color_count(); idx++) { - if (mapColorDest != nullptr) { - mapColorDest[idx] = Allowed.Map(idx); - } - - if (ARGBDest != nullptr) { - ARGB r, g, b, a; - if (mapColor2baseColor(Allowed.Map(idx)) != 0) - a = 255; - else - a = 0; - - r = ARGB(Allowed.RGB(idx, 0) * 255); - g = ARGB(Allowed.RGB(idx, 1) * 255); - b = ARGB(Allowed.RGB(idx, 2) * 255); - - ARGBDest[idx] = (a << 24) | (r << 16) | (g << 8) | (b); - } - } -} - -void TokiSlopeCraft::setRawImage(const ARGB *src, int rows, int cols) { - if (kernelStep < SCL_step::wait4Image) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can only import the raw image count after you set the map " - "type and gameversion"); - return; - } - if (rows <= 0 || cols <= 0) { - reportError(wind, errorFlag::EMPTY_RAW_IMAGE, - "The size of your raw image is 0. You loaded an empty image."); - return; - } - - this->image_cvter.set_raw_image(src, rows, cols); - - kernelStep = SCL_step::convertionReady; - - return; -} - -void TokiSlopeCraft::getBaseColorInARGB32(ARGB *const dest) const { - if (dest == nullptr) return; - - for (uchar base = 0; base < 64; base++) - dest[base] = - ARGB32(255 * Basic.RGB(128 + base, 0), 255 * Basic.RGB(128 + base, 1), - 255 * Basic.RGB(128 + base, 2), 255); -} - -int64_t TokiSlopeCraft::sizePic(short dim) const { - if (dim == 0) return this->image_cvter.rows(); - if (dim == 1) return this->image_cvter.cols(); - return this->image_cvter.size(); -} - -TokiSlopeCraft::ColorSpace TokiSlopeCraft::getColorSpace() const { - switch (this->image_cvter.convert_algo()) { - case SCL_convertAlgo::RGB: - return R; - case SCL_convertAlgo::RGB_Better: - return R; - case SCL_convertAlgo::HSV: - return H; - case SCL_convertAlgo::Lab94: - return L; - case convertAlgo::Lab00: - return L; - case SCL_convertAlgo::XYZ: - return X; - case convertAlgo::gaCvter: - return R; - } - return R; -} - -void TokiSlopeCraft::getConvertedImage(int *rows, int *cols, ARGB *dest) const { - EImage result = getConovertedImage(); - if (rows != nullptr) *rows = result.rows(); - if (cols != nullptr) *cols = result.cols(); - if (dest != nullptr) memcpy(dest, result.data(), sizeof(ARGB) * sizePic(2)); -} - -EImage TokiSlopeCraft::getConovertedImage() const { - EImage cvtedImg(sizePic(0), sizePic(1)); - cvtedImg.setZero(); - if (kernelStep < SCL_step::converted) { - reportError( - wind, errorFlag::HASTY_MANIPULATION, - "You can get the converted image only after you converted a map."); - return cvtedImg; - } - - Eigen::Array argbLUT; - for (int idx = 0; idx < 256; idx++) { - argbLUT[idx] = - RGB2ARGB(Basic.RGB(idx, 0), Basic.RGB(idx, 1), Basic.RGB(idx, 2)); - } - - // RGBint =(RGBint > 255).select(Eigen::ArrayXXi::Constant(256, 3, 255), - // RGBint); - // short Index; - for (short r = 0; r < sizePic(0); r++) { - for (short c = 0; c < sizePic(1); c++) { - if (mapColor2baseColor(this->mapPic(r, c)) == 0) { // if base ==0 - cvtedImg(r, c) = ARGB32(0, 0, 0, 0); - continue; - } - const int Index = mapColor2Index(this->mapPic(r, c)); - - cvtedImg(r, c) = argbLUT[Index]; - } - } - return cvtedImg; -} - -void TokiSlopeCraft::getConvertedMap(int *rows, int *cols, - unsigned char *dst) const { - if (rows != nullptr) { - *rows = this->image_cvter.rows(); - } - if (cols != nullptr) { - *cols = this->image_cvter.cols(); - } - - Eigen::Map> dest( - dst, getImageRows(), getImageCols()); - - dest = this->image_cvter.color_id(); -} - -int TokiSlopeCraft::getImageRows() const { - if (kernelStep < SCL_step::convertionReady) { - reportError( - wind, errorFlag::HASTY_MANIPULATION, - "You can call getImageRows only after you imported the raw image."); - return -1; - } - return this->image_cvter.rows(); -} - -int TokiSlopeCraft::getImageCols() const { - if (kernelStep < SCL_step::convertionReady) { - reportError( - wind, errorFlag::HASTY_MANIPULATION, - "You can call getImageRows only after you imported the raw image."); - return -1; - } - return this->image_cvter.cols(); -} - -void TokiSlopeCraft::get3DSize(int *x, int *y, int *z) const { - if (kernelStep < SCL_step::builded) return; - if (x != nullptr) *x = schem.x_range(); - if (y != nullptr) *y = schem.y_range(); - if (z != nullptr) *z = schem.z_range(); - return; -} - -int TokiSlopeCraft::getHeight() const { - if (kernelStep < SCL_step::builded) return -1; - return schem.y_range(); -} - -void TokiSlopeCraft::getBlockCounts(int *total, int detail[64]) const { - std::vector temp; - if (total != nullptr) *total = getBlockCounts(&temp); - if (detail != nullptr) - for (uint16_t idx = 0; idx < temp.size(); idx++) { - detail[idx] = temp[idx]; - } -} - -int64_t TokiSlopeCraft::getBlockCounts(std::vector *dest) const { - if (dest == nullptr) { - return -1; - } - if (kernelStep < SCL_step::builded) return -1; - dest->resize(64); - for (int i = 0; i < 64; i++) (*dest)[i] = 0; - for (int i = 0; i < schem.size(); i++) { - if (schem(i)) (*dest)[schem(i) - 1]++; - } - int totalBlockCount = 0; - for (int i = 0; i < 64; i++) totalBlockCount += (*dest)[i]; - return totalBlockCount; -} - -int64_t TokiSlopeCraft::getBlockCounts() const { - if (kernelStep < SCL_step::builded) return -1; - int totalCount = 0; - for (int i = 0; i < schem.size(); i++) { - if (schem(i)) totalCount++; - } - return totalCount; -} - -const uint16_t *TokiSlopeCraft::getBuild(int *xSize, int *ySize, - int *zSize) const { - if (xSize != nullptr) *xSize = getXRange(); - if (ySize != nullptr) *ySize = getHeight(); - if (zSize != nullptr) *zSize = getZRange(); - return schem.data(); -} - -int TokiSlopeCraft::getXRange() const { - if (kernelStep < SCL_step::builded) return -1; - return schem.x_range(); -} -int TokiSlopeCraft::getZRange() const { - if (kernelStep < SCL_step::builded) return -1; - return schem.x_range(); -} diff --git a/SlopeCraftL/TokiSlopeCraft.h b/SlopeCraftL/TokiSlopeCraft.h deleted file mode 100644 index 6e9a558c..00000000 --- a/SlopeCraftL/TokiSlopeCraft.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef TOKISLOPECRAFT_H -#define TOKISLOPECRAFT_H -///////////////////////////// - -///////////////////////////// - -#include -#include -#include -#include -#include -#include -#include - -#include "Colors.h" -#include "SCLDefines.h" -#include "WaterItem.h" -#include "simpleBlock.h" -#include - -#include "PrimGlassBuilder.h" -#include "lossyCompressor.h" - -#include - -#include "AiCvterOpt.h" - -#include -#include -#include -#include -#include - -/* -namespace SlopeCraft -{ - void *AllowedRGBList4AiCvters(); - void *AllowedMapList4AiCvters(); - void *BasicalRGBList4AiCvters(); -} -*/ - -using namespace SlopeCraft; -#include - -#define mapColor2Index(mapColor) (64 * (mapColor % 4) + (mapColor / 4)) -#define index2mapColor(index) (4 * (index % 64) + (index / 64)) -#define mapColor2baseColor(mapColor) (mapColor >> 2) -#define index2baseColor(index) (mapColor2baseColor(index2mapColor(index))) -#define mapColor2depth(mapColor) (mapColor % 4) -#define index2depth(index) (mapColor2depth(index2mapColor(index))) - -class PrimGlassBuilder; -class LossyCompressor; - -namespace NBT { -template -class NBTWriter; -}; - -class TokiSlopeCraft : public ::SlopeCraft::Kernel { - public: - static const colorset_basic_t Basic; - static colorset_allowed_t Allowed; - - static void getColorMapPtrs(const float **const rdata, - const float **const gdata, - const float **const bdata, const uint8_t **, - int *); - // full palette - static const float *getBasicColorMapPtrs(); - - static const char *getSCLVersion(); - - static uint64_t mcVersion2VersionNumber(::SCL_gameVersion); - - public: - TokiSlopeCraft(); - virtual ~TokiSlopeCraft(); - - // can do in nothing: - void trySkipStep(step); - /// function ptr to window object - void setWindPtr(void *) override; - /// a function ptr to show progress of converting and exporting - void setProgressRangeSet(void (*)(void *, int, int, int)) override; - /// a function ptr to add progress value - void setProgressAdd(void (*)(void *, int)) override; - /// a function ptr to prevent window from being syncoped - void setKeepAwake(void (*)(void *)) override; - - /// a function ptr to show progress of compressing and bridge-building - void setAlgoProgressRangeSet(void (*)(void *, int, int, int)) override; - /// a function ptr to add progress value of compressing and bridge-building - void setAlgoProgressAdd(void (*)(void *, int)) override; - - /// a function ptr to report error when something wrong happens - void setReportError(void (*)(void *, errorFlag, const char *)) override; - /// a function ptr to report working statue especially when busy - void setReportWorkingStatue(void (*)(void *, workStatues)) override; - unsigned long long size() override { return sizeof(TokiSlopeCraft); } - // void destroy() override { delete this; } - void decreaseStep(step) override; - bool makeTests(const AbstractBlock **, const unsigned char *, const char *, - char *) override; - std::string makeTests(const AbstractBlock **, const uint8_t *, - const std::string &); - void setAiCvterOpt(const AiCvterOpt *) override; - const AiCvterOpt *aiCvterOpt() const override; - - // can do in colorSetReady: - step queryStep() const override; - - bool setType(mapTypes, gameVersion, const bool[64], - const AbstractBlock *const *const) override; - - private: - static bool __impl_setType(mapTypes, gameVersion, const bool[64], - const AbstractBlock *const *const, - const TokiSlopeCraft *reporter) noexcept; - - public: - void getBaseColorInARGB32(unsigned int *const) const override; - // can do in wait4Image: - void setRawImage(const unsigned int *src, int rows, int cols) override; - - uint16_t getColorCount() const override; - void getAvailableColors(ARGB *const, uint8_t *const, - int *const num = nullptr) const override; - // can do in convertionReady: - bool convert(convertAlgo = SCL_convertAlgo::RGB_Better, - bool dither = false) override; - const uint32_t *getRawImage() const override { - return this->image_cvter.raw_image().data(); - } - int getImageRows() const override; - int getImageCols() const override; - - bool isVanilla() const override { return is_vanilla_static(); } - static inline bool is_vanilla_static() noexcept { - return mapType != SCL_mapTypes::FileOnly; - } - - bool isFlat() const override { return is_flat_static(); } - static inline bool is_flat_static() noexcept { - return mapType == SCL_mapTypes::Flat; - } - - // can do in converted: - bool build(compressSettings = SCL_compressSettings::noCompress, - unsigned short = 256, - glassBridgeSettings = SCL_glassBridgeSettings::noBridge, - unsigned short = 3, bool fireProof = false, - bool endermanProof = false) override; // 构建三维结构 - void getConvertedImage(int *rows, int *cols, - unsigned int *dest) const override; - EImage getConovertedImage() const; - void getConvertedMap(int *rows, int *cols, unsigned char *) const override; - // void getConvertedMap(Eigen::Arra) const; - void exportAsData(const char *, const int, int *fileCount, - char **) const override; - std::vector exportAsData(std::string, int) const; - // can do in builded: - void exportAsLitematic(const char *TargetName, const char *LiteName, - const char *RegionName, char *FileName) const override; - std::string exportAsLitematic(const std::string &TargetName, // Local - const std::string &LiteName, // Utf8 - const std::string &RegionName // Utf8 - ) const; - - void exportAsStructure(const char *TargetName, char *FileName) const override; - std::string exportAsStructure(const std::string &) const; - - void exportAsWESchem(const char *fileName, const int (&offset)[3], - const int (&weOffset)[3], const char *Name, - const char *const *const requiredMods, - const int requiredModsCount, - char *returnVal) const override; - - std::string exportAsWESchem( - const std::string &, const std::array &offset, - const std::array &weOffset, const char *Name, - const std::vector &requiredMods) const; - - void get3DSize(int *x, int *y, int *z) const override; - int getHeight() const override; - int getXRange() const override; - int getZRange() const override; - - void getBlockCounts(int *total, int detail[64]) const override; - int64_t getBlockCounts(std::vector *) const; - int64_t getBlockCounts() const override; - - const unsigned short *getBuild(int *xSize, int *ySize, - int *zSize) const override; - // const Eigen::Tensor &getBuild() const; - - private: -#ifdef SCL_CAPI - friend struct Kernel; -#else - friend class Kernel; -#endif // #ifdef SLOPECRAFTL_CAPI - // friend class TokiColor; - // friend void * allowedRGB(); - // friend void * allowedMap(); - enum ColorSpace { R = 'R', H = 'H', L = 'L', X = 'X' }; - static const Eigen::Array DitherMapLR, DitherMapRL; - static const uint32_t reportRate = 100; - - void *wind; - void (*progressRangeSet)(void *, int, int, int); - void (*progressAdd)(void *, int); - void (*keepAwake)(void *); - void (*algoProgressRangeSet)(void *, int, int, int); - void (*algoProgressAdd)(void *, int); - void (*reportError)(void *, errorFlag, const char *); - void (*reportWorkingStatue)(void *, workStatues); - - static gameVersion mcVer; // 12,13,14,15,16,17 - static mapTypes mapType; - static std::vector blockPalette; - - private: - static std::unordered_set kernel_hash_set; - - public: - step kernelStep; - // convertAlgo ConvertAlgo; - - libMapImageCvt::MapImageCvter image_cvter; - - // std::array size3D; // x,y,z - PrimGlassBuilder *glassBuilder; - LossyCompressor *Compressor; - - // std::shared_ptr GAConverter{nullptr}; - - AiCvterOpt AiOpt; - Eigen::ArrayXXi mapPic; // stores mapColor - Eigen::ArrayXXi Base; - Eigen::ArrayXXi HighMap; - Eigen::ArrayXXi LowMap; - std::unordered_map WaterList; - uint16_t maxAllowedHeight; - uint16_t bridgeInterval; - compressSettings compressMethod; - glassBridgeSettings glassMethod; - libSchem::Schem schem; - // Eigen::Tensor Build; // x,y,z - - // for setType: - - // for setImage: - - // for convert: - ColorSpace getColorSpace() const; - int64_t sizePic(short dim) const; - - // for build - // void makeHeight_old();//构建 HighMap 和 LowMap - void makeHeight_new(); - // void makeHeightInLine(const uint16_t c); - void buildHeight(bool = false, bool = false); // 构建 Build - void makeBridge(); - // for Litematic - /* - static void writeBlock(const std::string &netBlockId, - const std::vector &Property, - const std::vector &ProVal, - NBT::NBTWriter &); - static void writeTrash(int count, NBT::NBTWriter &); - */ - std::string Noder(const short *src, int size) const; - - Kernel *toBaseClassPtr() { return this; } -}; - -// bool compressFile(const char *sourcePath, const char *destPath); -#endif // TOKISLOPECRAFT_H diff --git a/SlopeCraftL/TokiSlopeCraft_build.cpp b/SlopeCraftL/TokiSlopeCraft_build.cpp deleted file mode 100644 index 0034e8e2..00000000 --- a/SlopeCraftL/TokiSlopeCraft_build.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TokiSlopeCraft.h" - -bool TokiSlopeCraft::makeTests(const AbstractBlock **src, - const unsigned char *baseColor, const char *dst, - char *_unFileName) { - if (kernelStep < step::wait4Image) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "Skipping is not allowed.\nYou can only make tests only after " - "you finished the map type and gameversion."); - - if (_unFileName != nullptr) *_unFileName = '\0'; - - return false; - } - - std::string s = makeTests(src, baseColor, std::string(dst)); - if (_unFileName != nullptr) std::strcpy(_unFileName, s.data()); - - return s.empty(); -} - -std::string TokiSlopeCraft::makeTests(const AbstractBlock **src, - const uint8_t *src_baseColor, - const std::string &fileName) { - if (!fileName.ends_with(".nbt")) { - return "File name should ends with \".nbt\""; - } - - libSchem::Schem test; - test.set_MC_major_version_number(::TokiSlopeCraft::mcVer); - test.set_MC_version_number( - MCDataVersion::suggested_version(::TokiSlopeCraft::mcVer)); - // const simpleBlock ** realSrc=(const simpleBlock **)src; - std::vector realSrc; - std::vector realBaseColor; - realSrc.clear(); - realBaseColor.clear(); - for (uint32_t idx = 0; src[idx] != nullptr; idx++) { - if (src[idx]->getVersion() > (int)mcVer) { - continue; - } - realSrc.emplace_back(static_cast(src[idx])); - realBaseColor.emplace_back(src_baseColor[idx]); - } - - std::vector> blocks; - blocks.resize(64); - - for (auto &it : blocks) { - while (!it.empty()) { - it.clear(); - // it.reserve(16); - } - } - - for (uint32_t idx = 0; idx < realSrc.size(); idx++) { - blocks[realBaseColor[idx]].push_back(idx); - } - - { - std::vector ids; - ids.reserve(realSrc.size() + 1); - ids.emplace_back("minecraft:air"); - for (auto i : realSrc) { - if (TokiSlopeCraft::mcVer == SCL_gameVersion::MC12) { - ids.emplace_back(i->getIdOld()); - } else { - ids.emplace_back(i->getId()); - } - } - - test.set_block_id(ids.data(), ids.size()); - } - - int xSize = 0; - constexpr int zSize = 64, ySize = 1; - for (const auto &it : blocks) { - xSize = std::max(size_t(xSize), it.size()); - } - test.resize(xSize, ySize, zSize); - test.set_zero(); - - for (uint8_t base = 0; base < 64; base++) { - for (uint32_t idx = 0; idx < blocks[base].size(); idx++) { - int xPos = idx; - int yPos = 0; - int zPos = base; - - test(xPos, yPos, zPos) = blocks[base][idx] + 1; - } - } - - SCL_errorFlag err; - std::string detail; - const bool success = test.export_structure(fileName, true, &err, &detail); - - if (!success) { - return std::string("Failed to export structure file ") + fileName + - ", error code = " + std::to_string(int(err)) + ", detail: " + detail; - } else { - return {}; - } -} - -void TokiSlopeCraft::exportAsLitematic(const char *TargetName, - const char *LiteName, - const char *RegionName, - char *FileName) const { - std::string temp = exportAsLitematic(TargetName, LiteName, RegionName); - - if (FileName != nullptr) std::strcpy(temp.data(), FileName); -} - -std::string TokiSlopeCraft::exportAsLitematic( - const std::string &TargetName, const std::string &LiteName, - const std::string &RegionName) const { - if (kernelStep < SCL_step::builded) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can only export a map to litematic after you build the 3D " - "structure."); - return "Too hasty! export litematic after you built!"; - } - reportWorkingStatue(wind, workStatues::writingMetaInfo); - progressRangeSet(wind, 0, 100 + schem.size(), 0); - - libSchem::litematic_info info; - info.litename_utf8 = LiteName; - info.regionname_utf8 = RegionName; - - errorFlag flag = errorFlag::NO_ERROR_OCCUR; - std::string error_string; - const bool success = - schem.export_litematic(TargetName, info, &flag, &error_string); - - if (!success) { - // this->reportError(this->wind,errorFlag::) - this->reportError(this->wind, flag, error_string.data()); - - return "Failed to export as litematic.\n" + error_string; - } - - progressRangeSet(wind, 0, 100, 100); - reportWorkingStatue(wind, workStatues::none); - - return ""; -} - -bool TokiSlopeCraft::build(compressSettings cS, uint16_t mAH, - glassBridgeSettings gBS, uint16_t bI, bool fireProof, - bool endermanProof) { - if (kernelStep < SCL_step::converted) { - reportError( - wind, errorFlag::HASTY_MANIPULATION, - "You can build 3D strcuture only after you converted the raw image."); - cerr << "hasty!" << endl; - return false; - } - if (mAH < 14) { - cerr << "maxAllowedHeight is less than 14!" << endl; - reportError(wind, errorFlag::MAX_ALLOWED_HEIGHT_LESS_THAN_14, - "Your maximum allowed height is less than 14, which made lossy " - "compressing almost impossible."); - return false; - } - // cerr << "Setting block palette..." << endl; - { - std::vector temp; - temp.reserve(64); - temp.emplace_back("minecraft:air"); - - for (const auto &block : TokiSlopeCraft::blockPalette) { - const char *id_at_curversion = (this->mcVer == SCL_gameVersion::MC12) - ? (block.getIdOld()) - : (block.getId()); - if (std::strcmp("minecraft:air", id_at_curversion) == 0) { - break; - } else { - temp.emplace_back(id_at_curversion); - } - } - - schem.set_block_id(temp.data(), temp.size()); - } - - schem.set_MC_major_version_number(TokiSlopeCraft::mcVer); - schem.set_MC_version_number(MCDataVersion::MCDataVersion_t( - TokiSlopeCraft::mcVersion2VersionNumber(TokiSlopeCraft::mcVer))); - - // cerr << "ready to build" << endl; - - compressMethod = cS; - glassMethod = gBS; - if (isFlat() || !isVanilla()) { - compressMethod = compressSettings::noCompress; - glassMethod = glassBridgeSettings::noBridge; - } - - maxAllowedHeight = mAH; - bridgeInterval = bI; - - reportWorkingStatue(wind, workStatues::buidingHeighMap); - - progressRangeSet(wind, 0, 9 * sizePic(2), 0); - // cerr << "start makeHeight" << endl; - - mapPic = this->image_cvter.mapcolor_matrix().cast(); - progressAdd(wind, sizePic(2)); - - makeHeight_new(); - // cerr << "makeHeight finished" << endl; - progressRangeSet(wind, 0, 9 * sizePic(2), 5 * sizePic(2)); - - reportWorkingStatue(wind, workStatues::building3D); - // cerr << "start buildHeight" << endl; - buildHeight(fireProof, endermanProof); - // cerr << "buildHeight finished" << endl; - progressRangeSet(wind, 0, 9 * sizePic(2), 8 * sizePic(2)); - - reportWorkingStatue(wind, workStatues::constructingBridges); - // cerr << "start makeBridge" << endl; - makeBridge(); - // cerr << "makeBridge finished" << endl; - progressRangeSet(wind, 0, 9 * sizePic(2), 9 * sizePic(2)); - - reportWorkingStatue(wind, workStatues::none); - - kernelStep = SCL_step::builded; - - return true; -} - -void TokiSlopeCraft::makeHeight_new() { - Base.setZero(sizePic(0) + 1, sizePic(1)); - WaterList.clear(); - HighMap.setZero(sizePic(0) + 1, sizePic(1)); - LowMap.setZero(sizePic(0) + 1, sizePic(1)); - bool allowNaturalCompress = compressMethod == compressSettings::Both || - compressMethod == compressSettings::NaturalOnly; - // std::vector src; - // cerr << "makeHeight_new\n"; - - if ((mapPic - 4 * (mapPic / 4) >= 3).any()) { - std::string msg = - "Fatal error : SlopeCraftLib3 found map color with depth 3 in a " - "vanilla map.\n Map contents (map color matrix in col-major) :\n["; - - for (int c = 0; c < mapPic.cols(); c++) { - for (int r = 0; r < mapPic.rows(); r++) { - msg += std::to_string(mapPic(r, c)) + ','; - } - msg += ";\n"; - } - msg += "];\n"; - - reportError(wind, errorFlag::DEPTH_3_IN_VANILLA_MAP, msg.data()); - return; - } - - for (uint16_t c = 0; c < sizePic(1); c++) { - // cerr << "Coloumn " << c << '\n'; - HeightLine HL; - // getTokiColorPtr(c,&src[0]); - HL.make(mapPic.col(c), allowNaturalCompress); - - if (HL.maxHeight() > maxAllowedHeight && - (compressMethod == compressSettings::ForcedOnly || - compressMethod == compressSettings::Both)) { - std::vector ptr(getImageRows()); - - this->image_cvter.col_TokiColor_ptrs(c, ptr.data()); - // getTokiColorPtr(c, &ptr[0]); - - Compressor->setSource(HL.getBase(), &ptr[0]); - bool success = - Compressor->compress(maxAllowedHeight, allowNaturalCompress); - if (!success) { - std::string msg = "Failed to compress the 3D structure at coloum " + - std::to_string(c); - reportError(wind, SCL_errorFlag::LOSSYCOMPRESS_FAILED, msg.data()); - return; - } - Eigen::ArrayXi temp; - HL.make(&ptr[0], Compressor->getResult(), allowNaturalCompress, &temp); - mapPic.col(c) = temp; - } - - Base.col(c) = HL.getBase(); - HighMap.col(c) = HL.getHighLine(); - LowMap.col(c) = HL.getLowLine(); - auto HLM = &HL.getWaterMap(); - - for (auto it = HLM->cbegin(); it != HLM->cend(); it++) { - WaterList[TokiRC(it->first, c)] = it->second; - } - - progressAdd(wind, 4 * sizePic(0)); - } - // cerr << "makeHeight_new finished\n"; - schem.resize(2 + sizePic(1), HighMap.maxCoeff() + 1, 2 + sizePic(0)); - schem.set_zero(); - // schem.z_range() = 2 + sizePic(0); // z - // schem.x_range() = 2 + sizePic(1); // x - // schem.y_range() = HighMap.maxCoeff() + 1; // y -} - -void TokiSlopeCraft::buildHeight(bool fireProof, bool endermanProof) { - /* -{ - std::array tempSize3D({schem.x_range(), schem.y_range(), -schem.z_range()}); Build.resize(tempSize3D); -} -*/ - schem.set_zero(); - // Base(r+1,c)<->High(r+1,c)<->Build(c+1,High(r+1,c),r+1) - // 为了区分玻璃与空气,张量中存储的是 Base+1.所以元素为 1 对应着玻璃,0 - // 对应空气 - int x = 0, y = 0, z = 0; - int yLow = 0; - - // cerr << WaterList.size() << " water columns in map\n"; - for (auto it = WaterList.begin(); it != WaterList.end(); - it++) // 水柱周围的玻璃 - { - x = TokiCol(it->first) + 1; - z = TokiRow(it->first); - y = waterHigh(it->second); - yLow = waterLow(it->second); - schem(x, y + 1, z) = 0 + 1; // 柱顶玻璃 - for (short yDynamic = yLow; yDynamic <= y; yDynamic++) { - schem(x - 1, yDynamic, z - 0) = 1; - schem(x + 1, yDynamic, z + 0) = 1; - schem(x + 0, yDynamic, z - 1) = 1; - schem(x + 0, yDynamic, z + 1) = 1; - } - if (yLow >= 1) schem(x, yLow - 1, z) = 1; // 柱底玻璃 - } - - progressAdd(wind, sizePic(2)); - - for (short r = -1; r < sizePic(0); r++) // 普通方块 - { - for (short c = 0; c < sizePic(1); c++) { - if (Base(r + 1, c) == 12 || Base(r + 1, c) == 0) continue; - x = c + 1; - y = LowMap(r + 1, c); - z = r + 1; - if (y >= 1 && blockPalette[Base(r + 1, c)].needGlass) - schem(x, y - 1, z) = 0 + 1; - if ((fireProof && blockPalette[Base(r + 1, c)].burnable) || - (endermanProof && blockPalette[Base(r + 1, c)].endermanPickable)) { - if (y >= 1 && schem(x, y - 1, z) == 0) schem(x, y - 1, z) = 0 + 1; - if (x >= 1 && schem(x - 1, y, z) == 0) schem(x - 1, y, z) = 0 + 1; - if (z >= 1 && schem(x, y, z - 1) == 0) schem(x, y, z - 1) = 0 + 1; - if (y + 1 < schem.y_range() && schem(x, y + 1, z) == 0) - schem(x, y + 1, z) = 0 + 1; - if (x + 1 < schem.x_range() && schem(x + 1, y, z) == 0) - schem(x + 1, y, z) = 0 + 1; - if (z + 1 < schem.z_range() && schem(x, y, z + 1) == 0) - schem(x, y, z + 1) = 0 + 1; - } - - schem(x, y, z) = Base(r + 1, c) + 1; - } - progressAdd(wind, sizePic(1)); - } - - progressAdd(wind, sizePic(2)); - - for (auto it = WaterList.cbegin(); it != WaterList.cend(); it++) { - x = TokiCol(it->first) + 1; - z = TokiRow(it->first); - y = waterHigh(it->second); - yLow = waterLow(it->second); - for (short yDynamic = yLow; yDynamic <= y; yDynamic++) { - schem(x, yDynamic, z) = 13; - } - } - /* - for(short c=0;c start, extension; // x,z,y - start[0] = 0; - start[1] = 0; - start[2] = y; - extension[0] = schem.x_range(); - extension[1] = schem.z_range(); - extension[2] = 1; - TokiMap targetMap = ySlice2TokiMap( - schem.tensor().slice(start, extension).cast()); - glassMap glass; - cerr << "Construct glass bridge at y=" << y << endl; - glass = glassBuilder->makeBridge(targetMap); - for (int r = 0; r < glass.rows(); r++) - for (int c = 0; c < glass.cols(); c++) - if (schem(r, y, c) == PrimGlassBuilder::air && - glass(r, c) == PrimGlassBuilder::glass) - schem(r, y, c) = PrimGlassBuilder::glass; - } else { - continue; - std::array start, extension; - start[0] = 0; - start[1] = y; - start[2] = 0; - extension[0] = schem.x_range(); - extension[1] = 1; - extension[2] = schem.z_range(); - TokiMap yCur = ySlice2TokiMap( - schem.tensor().slice(start, extension).cast()); - start[1] = y - 1; - TokiMap yBelow = ySlice2TokiMap( - schem.tensor().slice(start, extension).cast()); - cerr << "Construct glass bridge between y=" << y << " and y=" << y - 1 - << endl; - glassMap glass = connectBetweenLayers(yCur, yBelow, nullptr); - - for (int r = 0; r < glass.rows(); r++) - for (int c = 0; c < glass.cols(); c++) - if (schem(r, y, c) == PrimGlassBuilder::air && - glass(r, c) == PrimGlassBuilder::glass) - schem(r, y, c) = PrimGlassBuilder::glass; - } - } - algoProgressRangeSet(wind, 0, 100, 100); - cerr << "makeBridge finished\n"; -} - -void TokiSlopeCraft::exportAsStructure(const char *TargetName, - char *FileName) const { - std::string temp = exportAsStructure(TargetName); - - if (FileName != nullptr) std::strcpy(temp.data(), FileName); -} - -std::string TokiSlopeCraft::exportAsStructure( - const std::string &TargetName) const { - if (kernelStep < SCL_step::builded) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can only export a map to structure after you build the 3D " - "structure."); - return "Too hasty! export structure after you built!"; - } - - reportWorkingStatue(wind, workStatues::writingMetaInfo); - progressRangeSet(wind, 0, 100 + schem.size(), 0); - - errorFlag flag = errorFlag::NO_ERROR_OCCUR; - std::string error_string; - const bool success = - schem.export_structure(TargetName, true, &flag, &error_string); - - if (!success) { - this->reportError(this->wind, flag, error_string.data()); - return "Failed to export structure.\n" + error_string; - } - - progressRangeSet(wind, 0, 100, 100); - reportWorkingStatue(wind, workStatues::none); - - return ""; -} - -void TokiSlopeCraft::exportAsWESchem(const char *fileName, - const int (&offset)[3], - const int (&weOffset)[3], const char *Name, - const char *const *const requiredMods, - const int requiredModsCount, - char *returnVal) const { - if (fileName == nullptr || strlen(fileName) == 0) { - return; - } - - const std::array _offset({offset[0], offset[1], offset[2]}); - const std::array _weOffset({weOffset[0], weOffset[1], weOffset[2]}); - - std::vector _reqMods; - _reqMods.reserve(requiredModsCount); - if (requiredMods != nullptr) - for (int idx = 0; idx < requiredModsCount; idx++) { - if (requiredMods[idx] == nullptr) continue; - _reqMods.emplace_back(requiredMods[idx]); - } - - const std::string retVal = this->exportAsWESchem( - std::string(fileName), _offset, _weOffset, Name, _reqMods); - if (returnVal != nullptr) { - strcpy(returnVal, retVal.data()); - } - return; -} - -std::string TokiSlopeCraft::exportAsWESchem( - const std::string &targetName, const std::array &offset, - const std::array &, const char *Name, - const std::vector &requiredMods) const { - if (kernelStep < SCL_step::builded) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can only export a map to structure after you build the 3D " - "structure."); - return "Too hasty! export structure after you built!"; - } - - // progress bar value : 10(open+others) + 64(palette) + Build + 10 - // (others+compress+remove) - - progressRangeSet(wind, 0, 100, 0); - - libSchem::WorldEditSchem_info info; - - info.schem_name_utf8 = Name; - info.offset = offset; - - info.required_mods_utf8.resize(requiredMods.size()); - - for (int idx = 0; idx < int(requiredMods.size()); idx++) { - info.required_mods_utf8[idx] = requiredMods[idx]; - } - - progressRangeSet(wind, 0, 100, 5); - - errorFlag flag = errorFlag::NO_ERROR_OCCUR; - std::string error_string; - const bool success = - schem.export_WESchem(targetName, info, &flag, &error_string); - - if (!success) { - this->reportError(this->wind, flag, error_string.data()); - return "Failed to export as WE schem files.\n" + error_string; - } - - progressRangeSet(wind, 0, 100, 100); - - return ""; -} diff --git a/SlopeCraftL/TokiSlopeCraft_convert.cpp b/SlopeCraftL/TokiSlopeCraft_convert.cpp deleted file mode 100644 index cf893724..00000000 --- a/SlopeCraftL/TokiSlopeCraft_convert.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TokiSlopeCraft.h" -#include - -bool TokiSlopeCraft::convert(convertAlgo algo, bool dither) { - if (kernelStep < SCL_step::convertionReady) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can call convert only after you imported the raw image"); - return false; - } - - if (algo == convertAlgo::gaCvter) { - algo = convertAlgo::RGB_Better; - } - - // this->ConvertAlgo = algo; - - // TokiColor::convertAlgo() = algo; - progressRangeSet(wind, 0, 100, 100); - reportWorkingStatue(wind, workStatues::converting); - - { - heu::GAOption opt; - opt.crossoverProb = AiOpt.crossoverProb; - opt.mutateProb = AiOpt.mutationProb; - opt.maxGenerations = AiOpt.maxGeneration; - opt.maxFailTimes = AiOpt.maxFailTimes; - opt.populationSize = AiOpt.popSize; - // here opt is passed as a const ptr, it won't be changed in this function - // call - this->image_cvter.convert_image(algo, dither, &opt); - } - - /* - if (algo == convertAlgo::gaCvter) { - - convertAlgo algos[6] = {RGB, RGB_Better, HSV, Lab94, Lab00, XYZ}; - Eigen::ArrayXX CvtedMap[6]; - std::vector *> seeds(6); - for (int a = 0; a < 6; a++) { - this->convert(algos[a]); - CvtedMap[a].resize(getImageRows(), getImageCols()); - this->getConvertedMap(nullptr, nullptr, CvtedMap[a].data()); - seeds[a] = &CvtedMap[a]; - } - - { - heu::GAOption opt; - opt.crossoverProb = AiOpt.crossoverProb; - opt.mutateProb = AiOpt.mutationProb; - opt.maxGenerations = AiOpt.maxGeneration; - opt.maxFailTimes = AiOpt.maxFailTimes; - opt.populationSize = AiOpt.popSize; - GAConverter->setOption(opt); - } - - GAConverter->setSeeds(seeds); - - GAConverter->run(); - - // replace raw image with ai result - GAConverter->resultImage(&rawImage); - - algo = convertAlgo::RGB_Better; - } - - ConvertAlgo = algo; - colorHash.clear(); - - progressRangeSet(wind, 0, 4 * sizePic(2), 0); - - reportWorkingStatue(wind, workStatues::collectingColors); - pushToHash(); - - keepAwake(wind); - progressRangeSet(wind, 0, 4 * sizePic(2), 1 * sizePic(2)); - - reportWorkingStatue(wind, workStatues::converting); - applyTokiColor(); - - keepAwake(wind); - progressRangeSet(wind, 0, 4 * sizePic(2), 2 * sizePic(2)); - - fillMapMat(); - keepAwake(wind); - progressRangeSet(wind, 0, 4 * sizePic(2), 3 * sizePic(2)); - - ditheredImage = this->rawImage; - - if (dither) { - reportWorkingStatue(wind, workStatues::dithering); - Dither(); - } - */ - this->mapPic = this->image_cvter.mapcolor_matrix().cast(); - progressRangeSet(wind, 0, 4 * sizePic(2), 4 * sizePic(2)); - keepAwake(wind); - - reportWorkingStatue(wind, workStatues::none); - - kernelStep = SCL_step::converted; - return true; -} - -void TokiSlopeCraft::exportAsData(const char *FolderPath, const int indexStart, - int *fileCount, char **dest) const { - std::vector uFL = exportAsData(FolderPath, indexStart); - if (fileCount != nullptr) - *fileCount = uFL.size(); - if (dest != nullptr) - for (uint16_t i = 0; i < uFL.size(); i++) { - if (dest[i] != nullptr) - std::strcpy(dest[i], uFL[i].data()); - } -} - -std::vector TokiSlopeCraft::exportAsData(std::string FolderPath, - int indexStart) const { - - std::vector failed_file_list; - - if (kernelStep < SCL_step::converted) { - reportError(wind, errorFlag::HASTY_MANIPULATION, - "You can export the map as map data files only after the image " - "isSCL_step::converted."); - failed_file_list.push_back( - "Too hasty! export after youSCL_step::converted the map!"); - return failed_file_list; - } - - if (FolderPath.back() == '/') { - FolderPath.pop_back(); - } - const int rows = ceil(mapPic.rows() / 128.0f); - const int cols = ceil(mapPic.cols() / 128.0f); - // const int maxrr=rows*128; - // const int maxcc=cols*128; - progressRangeSet(wind, 0, 128 * rows * cols, 0); - - int offset[2] = {0, 0}; // r,c - int currentIndex = indexStart; - - reportWorkingStatue(wind, workStatues::writingMapDataFiles); - - for (int c = 0; c < cols; c++) { - for (int r = 0; r < rows; r++) { - offset[0] = r * 128; - offset[1] = c * 128; - std::string current_filename = - FolderPath + "/map_" + std::to_string(currentIndex) + ".dat"; - // string - // currentFile=FolderPath+"/map_"+std::to_string(currentIndex)+".dat"; - - // cerr << "Export map of (" << r << "," << c << ")" << current_filename - // << endl; - - NBT::NBTWriter MapFile; - - if (!MapFile.open(current_filename.data())) { - cerr << "Failed to create nbt file " << current_filename << endl; - failed_file_list.emplace_back(current_filename); - continue; - } - - switch (mcVer) { - case SCL_gameVersion::MC12: - case SCL_gameVersion::MC13: - break; - case SCL_gameVersion::MC14: - case SCL_gameVersion::MC15: - case SCL_gameVersion::MC16: - case SCL_gameVersion::MC17: - case SCL_gameVersion::MC18: - case SCL_gameVersion::MC19: - MapFile.writeInt("DataVersion", mcVersion2VersionNumber(mcVer)); - break; - default: - cerr << "Wrong game version!\n"; - break; - } - static const std::string ExportedBy = - "Exported by SlopeCraft " + - std::string(TokiSlopeCraft::getSCLVersion()) + - ", developed by TokiNoBug"; - MapFile.writeString("ExportedBy", ExportedBy.data()); - MapFile.writeCompound("data"); - { - MapFile.writeByte("scale", 0); - MapFile.writeByte("trackingPosition", 0); - MapFile.writeByte("unlimitedTracking", 0); - MapFile.writeInt("xCenter", 0); - MapFile.writeInt("zCenter", 0); - switch (mcVer) { - case SCL_gameVersion::MC12: - MapFile.writeByte("dimension", 114); - MapFile.writeShort("height", 128); - MapFile.writeShort("width", 128); - break; - case SCL_gameVersion::MC13: - MapFile.writeListHead("banners", NBT::Compound, 0); - MapFile.writeListHead("frames", NBT::Compound, 0); - MapFile.writeInt("dimension", 889464); - break; - case SCL_gameVersion::MC14: - MapFile.writeListHead("banners", NBT::Compound, 0); - MapFile.writeListHead("frames", NBT::Compound, 0); - MapFile.writeInt("dimension", 0); - MapFile.writeByte("locked", 1); - break; - case SCL_gameVersion::MC15: - MapFile.writeListHead("banners", NBT::Compound, 0); - MapFile.writeListHead("frames", NBT::Compound, 0); - MapFile.writeInt("dimension", 0); - MapFile.writeByte("locked", 1); - break; - case SCL_gameVersion::MC16: - case SCL_gameVersion::MC17: - case SCL_gameVersion::MC18: - case SCL_gameVersion::MC19: - MapFile.writeListHead("banners", NBT::Compound, 0); - MapFile.writeListHead("frames", NBT::Compound, 0); - MapFile.writeString("dimension", "minecraft:overworld"); - MapFile.writeByte("locked", 1); - break; - default: - cerr << "Wrong game version!\n"; - this->reportError(this->wind, errorFlag::UNKNOWN_MAJOR_GAME_VERSION, - "Unknown major game version!"); - failed_file_list.emplace_back(current_filename); - continue; - } - - MapFile.writeByteArrayHead("colors", 16384); - { - uchar ColorCur = 0; - for (short rr = 0; rr < 128; rr++) { - for (short cc = 0; cc < 128; cc++) { - if (rr + offset[0] < mapPic.rows() && - cc + offset[1] < mapPic.cols()) - ColorCur = mapPic(rr + offset[0], cc + offset[1]); - else - ColorCur = 0; - MapFile.writeByte("this should never be seen", ColorCur); - } - progressAdd(wind, 1); - } - } - } - MapFile.endCompound(); - MapFile.close(); - - currentIndex++; - } - } - - reportWorkingStatue(wind, workStatues::none); - - return failed_file_list; -} diff --git a/SlopeCraftL/TokiSlopeCraft_static_funs.cpp b/SlopeCraftL/TokiSlopeCraft_static_funs.cpp deleted file mode 100644 index 96adbc55..00000000 --- a/SlopeCraftL/TokiSlopeCraft_static_funs.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TokiSlopeCraft.h" -#include - -const char *TokiSlopeCraft::getSCLVersion() { return SC_VERSION_STR; } - -void TokiSlopeCraft::getColorMapPtrs(const float **const r, - const float **const g, - const float **const b, - const unsigned char **m, int *rows) { - if (r != nullptr) - *r = TokiSlopeCraft::Allowed.rgb_data(0); - if (g != nullptr) - *g = TokiSlopeCraft::Allowed.rgb_data(1); - if (b != nullptr) - *b = TokiSlopeCraft::Allowed.rgb_data(2); - - if (m != nullptr) - *m = TokiSlopeCraft::Allowed.map_data(); - if (rows != nullptr) - *rows = TokiSlopeCraft::Allowed.color_count(); -} - -const float *TokiSlopeCraft::getBasicColorMapPtrs() { - return TokiSlopeCraft::Basic.RGB_mat().data(); -} - -uint64_t TokiSlopeCraft::mcVersion2VersionNumber(SCL_gameVersion g) { - switch (g) { - case SCL_gameVersion::ANCIENT: - return 114514; - case SCL_gameVersion::MC12: - return 1631; - case SCL_gameVersion::MC13: - return 1976; - case SCL_gameVersion::MC14: - return 2230; - case SCL_gameVersion::MC15: - return 2230; - case SCL_gameVersion::MC16: - return 2586; - case SCL_gameVersion::MC17: - return 2730; - case SCL_gameVersion::MC18: - return 2865; - case SCL_gameVersion::MC19: - return 3105; // 1.19.0 - default: - return 1919810; - } -} \ No newline at end of file diff --git a/SlopeCraftL/WaterItem.cpp b/SlopeCraftL/WaterItem.cpp deleted file mode 100644 index 78a62a68..00000000 --- a/SlopeCraftL/WaterItem.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "WaterItem.h" - -const TokiPos nullPos = TokiRC(-1, -1); -const waterItem nullWater = TokiRC(-32768, -32768); -const short WaterColumnSize[3] = {11, 6, 1}; -// waterItem (*TokiWater)(int,int)=TokiRC; -// short (*waterHigh)(waterItem)=TokiRow; -// short (*waterLow)(waterItem)=TokiCol; -TokiPos TokiRC(int row, int col) { - /*unsigned int u; - *((short*)&u)=row; - *(((short*)&u)+1)=col; - return u;*/ - return (row << 16) | (col & 0x0000FFFF); -} -short TokiRow(TokiPos pos) { return pos >> 16; } -short TokiCol(TokiPos pos) { return pos & 0x0000FFFF; } -/* -waterItem TokiWater(short high,short low) -{ - unsigned int u; - *((short*)&u)=high; - *(((short*)&u)+1)=low; - return u; -} -short waterHigh(waterItem item) -{ - return *((short*)&item); -} -short waterLow(waterItem item) -{ - return *(((short*)&item)+1); -} -*/ diff --git a/SlopeCraftL/block_list.cpp b/SlopeCraftL/block_list.cpp new file mode 100644 index 00000000..644fd0a4 --- /dev/null +++ b/SlopeCraftL/block_list.cpp @@ -0,0 +1,339 @@ +/* +Copyright © 2021-2026 TokiNoBug +This file is part of SlopeCraft. + +SlopeCraft is free software: you can redistribute it and/or modify + it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + + SlopeCraft is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SlopeCraft. If not, see . + + Contact with me: + github:https://github.com/SlopeCraft/SlopeCraft + bilibili:https://space.bilibili.com/351429231 +*/ + +#include +#include +#include +#include +#include + +#include "SlopeCraftL.h" +#include "mc_block.h" +#include "blocklist.h" +#include "string_deliver.h" +#include "color_table.h" + +using namespace SlopeCraft; + +// mc_block_interface *mc_block_interface::create() { return new mc_block; } + +std::pair parse_block(const nlohmann::json &jo) noexcept( + false) { + mc_block ret; + const int basecolor = jo.at("baseColor"); + if (basecolor < 0 || basecolor >= 64) { + throw std::runtime_error{std::format("invalid base color: {}", basecolor)}; + } + + ret.id = jo.at("id"); + ret.nameZH = jo.at("nameZH"); + ret.nameEN = jo.at("nameEN"); + ret.imageFilename = jo.at("icon"); + ret.version = jo.at("version"); + if (jo.contains("idOld")) { + ret.idOld = jo.at("idOld"); + } else { + ret.idOld = ret.id; + } + + if (jo.contains("endermanPickable")) { + ret.endermanPickable = jo.at("endermanPickable"); + } + + if (jo.contains("isGlowing")) { + ret.doGlow = jo.at("isGlowing"); + } + + if (jo.contains("burnable")) { + ret.burnable = jo.at("burnable"); + } + + if (jo.contains("needGlass")) { + ret.needGlass = jo.at("needGlass"); + } + if (jo.contains("stackSize")) { + const int val = jo.at("stackSize"); + if (val <= 0 or val > 64) { + throw std::runtime_error{std::format("Invalid stack size: {}", val)}; + } + ret.stackSize = val; + } + if (jo.contains("needStone")) { + auto &need_stone = jo.at("needStone"); + if (need_stone.is_boolean()) { + ret.needStone = version_set::all(); + } else if (need_stone.is_array()) { + for (auto ver : need_stone) { + if (not ver.is_number_integer()) { + throw std::runtime_error{ + std::format("needStone must be boolean or array of versions, but " + "found non-integer element in array")}; + } + const int ver_int = ver; + if (ver_int < static_cast(SCL_gameVersion::MC12) or + ver_int > static_cast(SCL_maxAvailableVersion())) { + throw std::runtime_error{std::format( + "Found invalid version {} in version list of needStone", + ver_int)}; + } + ret.needStone[static_cast(ver_int)] = true; + } + } else { + throw std::runtime_error{ + std::format("needStone must be boolean or array of versions")}; + } + } + + return {basecolor, ret}; +} + +struct zip_deleter { + void operator()(zip_t *archive) const noexcept { + if (archive == nullptr) { + return; + } + zip_close(archive); + } +}; + +tl::expected parse_meta_info( + std::function(const char *filename, + std::vector &dest)> + extract_file, + std::vector &buffer) noexcept { + using njson = nlohmann::json; + // parse meta data + auto res = extract_file("metainfo.json", buffer); + if (res) { + block_list_metainfo ret; + try { + njson jo = njson::parse(buffer, nullptr, true, true); + auto &prefix = jo.at("name prefix"); + ret.prefix_ZH = prefix.at("ZH"); + ret.prefix_EN = prefix.at("EN"); + auto &mods = jo.at("required mods"); + ret.required_mods.reserve(mods.size()); + for (size_t i = 0; i < mods.size(); i++) { + std::string mod_name = mods[i]; + ret.required_mods.emplace_back(std::move(mod_name)); + } + + } catch (const std::exception &e) { + return tl::make_unexpected( + std::format("Failed to parse \"metainfo.json\": {}", e.what())); + } + + return ret; + } + return tl::make_unexpected( + std::format("Failed to extract \"metainfo.json\": {}", res.error())); +} + +block_list_create_result parse_block_list(zip_t *archive) noexcept { + std::string warnings{}; + assert(archive not_eq nullptr); + + auto extract_file = + [archive](const char *filename, + std::vector &dest) -> tl::expected { + dest.clear(); + + int error_code = ZIP_ER_OK; + const int64_t index_i = + zip_name_locate(archive, filename, ZIP_FL_UNCHANGED); + if (index_i < 0) { + return tl::make_unexpected( + std::format("File \"{}\" doesn't exist in archive", filename)); + } + const uint64_t index = uint64_t(index_i); + + zip_stat_t stat; + error_code = zip_stat_index(archive, index, ZIP_FL_UNCHANGED, &stat); + if (error_code != ZIP_ER_OK) { + return tl::make_unexpected( + std::format("Failed to get size of file \"{}\" in archive: \"{}\", " + "error code = {}", + filename, zip_strerror(archive), error_code)); + } + + const uint64_t file_size = stat.size; + dest.resize(file_size); + + auto file = zip_fopen(archive, filename, ZIP_FL_UNCHANGED); + if (file == nullptr) { + return tl::make_unexpected( + std::format("Failed to extract \"{}\" from archive : \"{}\" ", + filename, zip_strerror(archive))); + } + + const int64_t read_bytes = zip_fread(file, dest.data(), dest.size()); + if (read_bytes != int64_t(file_size)) { + return tl::make_unexpected( + std::format("Failed to extract \"{}\" from archive, expected " + "{} bytes, but extracted {} bytes : \"{}\" ", + filename, file_size, read_bytes, zip_strerror(archive))); + } + return {}; + }; + + std::vector buffer; + + block_list bl{}; + + using njson = nlohmann::json; + block_list_metainfo meta_info; + { + const char metainfo_name[] = "metainfo.json"; + const int64_t index = + zip_name_locate(archive, metainfo_name, ZIP_FL_UNCHANGED); + if (index >= 0) { + // metainfo.json exists in the archive + auto mi_res = parse_meta_info(extract_file, buffer); + if (not mi_res) { + std::format_to(std::back_inserter(warnings), + "metainfo.json exist in the archive, but failed to " + "parse it: {}\n", + mi_res.error()); + } + meta_info = std::move(mi_res).value_or(block_list_metainfo{}); + } + } + // parse json array of blocks + { + auto err = extract_file("block_list.json", buffer); + if (!err) { + return {tl::make_unexpected(err.error()), warnings}; + } + } + try { + njson jo = njson::parse(buffer, nullptr, true, true); + if (not jo.is_array()) { + return {tl::make_unexpected( + std::format("Json should contain an array directly")), + warnings}; + } + + // parse blocks + for (size_t idx = 0; idx < jo.size(); idx++) { + try { + auto [version, block] = parse_block(jo[idx]); + + block.nameZH = meta_info.prefix_ZH + block.nameZH; + block.nameEN = meta_info.prefix_EN + block.nameEN; + + bl.blocks().emplace(std::make_unique(block), version); + } catch (const std::exception &e) { + return {tl::make_unexpected(std::format( + "Failed to parse block at index {}:\n{}", idx, e.what())), + warnings}; + } + } + + } catch (const std::exception &e) { + return {tl::make_unexpected( + std::format("nlohmann json exception : {}", e.what())), + warnings}; + } + // load images + std::vector buf_pixel; + for (auto &pair : bl.blocks()) { + { + auto err = extract_file(pair.first->imageFilename.c_str(), buffer); + if (not err) { + warnings += + std::format("{}, required by {}", err.error(), pair.first->id); + continue; + } + } + + pair.first->image.resize(16, 16); + { + auto [result, warns] = parse_png_into_argb32(buffer, buf_pixel); + warnings += warns; + + if (!result) { + std::format_to(std::back_insert_iterator{warnings}, + "Failed to load image \"{}\" because \"{}\"\n", + pair.first->getImageFilename(), result.error()); + // for (uint8_t byte : buffer) { + // printf("%02X ", int(byte)); + // } + // printf("\n"); + continue; + } + auto image_size = result.value(); + if (image_size.rows != 16 || image_size.cols != 16) { + std::format_to(std::back_insert_iterator{warnings}, + "{} has invalid shape, expected 16x16, but found {} " + "rows x {} cols.\n", + pair.first->getImageFilename(), image_size.rows, + image_size.cols); + continue; + } + } + assert(buf_pixel.size() == 16 * 16); + memcpy(pair.first->image.data(), buf_pixel.data(), 256 * sizeof(uint32_t)); + } + + return block_list_create_result{.result{std::move(bl)}, .warnings{warnings}}; +} + +block_list_create_result create_block_list_from_file( + const char *zip_path) noexcept { + std::string warnings{}; + int error_code = ZIP_ER_OK; + std::unique_ptr archive{ + zip_open(zip_path, ZIP_RDONLY | ZIP_CHECKCONS, &error_code)}; + if (error_code not_eq ZIP_ER_OK or archive == nullptr) { + auto ret = tl::make_unexpected(std::format( + "Failed to open archive \"{}\" : \"{}\" libzip error code = {}", + zip_path, zip_strerror(archive.get()), error_code)); + return {ret, warnings}; + } + + return parse_block_list(archive.get()); +} + +block_list_create_result create_block_list_from_buffer( + std::span buffer) noexcept { + zip_error_t err; + zip_source_t *const source = + zip_source_buffer_create(buffer.data(), buffer.size_bytes(), 0, &err); + if (source == nullptr) { + return {tl::make_unexpected(std::format("Failed to create zip_source_t: {}", + zip_error_strerror(&err))), + {}}; + } + + std::unique_ptr archive{ + zip_open_from_source(source, ZIP_RDONLY | ZIP_CHECKCONS, &err)}; + if (archive == nullptr) { + zip_source_free(source); + return {tl::make_unexpected( + std::format("Failed to open zip, zip_err = {}, sys_err = {}", + err.zip_err, err.sys_err)), + {}}; + } + + return parse_block_list(archive.get()); +} \ No newline at end of file diff --git a/SlopeCraftL/blocklist.h b/SlopeCraftL/blocklist.h new file mode 100644 index 00000000..be7915af --- /dev/null +++ b/SlopeCraftL/blocklist.h @@ -0,0 +1,60 @@ +// +// Created by Joseph on 2024/8/11. +// + +#ifndef SLOPECRAFT_BLOCKLIST_H +#define SLOPECRAFT_BLOCKLIST_H + +#include +#include + +#include "mc_block.h" + +struct block_list_metainfo { + std::string prefix_ZH; + std::string prefix_EN; + std::vector required_mods; +}; + +class block_list : public ::SlopeCraft::block_list_interface { + private: + std::map, uint8_t> m_blocks; + + public: + block_list() = default; + block_list(block_list &&) = default; + ~block_list(); + + public: + size_t size() const noexcept override { return m_blocks.size(); } + size_t get_blocks(mc_block_interface **dst, uint8_t *, + size_t capacity_in_elements) noexcept override; + + size_t get_blocks(const mc_block_interface **dst, uint8_t *, + size_t capacity_in_elements) const noexcept override; + + bool contains(const mc_block_interface *cp) const noexcept override { + const mc_block *ptr = dynamic_cast(cp); + return this->m_blocks.contains( + reinterpret_cast &>(ptr)); + } + + public: + const auto &blocks() const noexcept { return this->m_blocks; } + auto &blocks() noexcept { return this->m_blocks; } + + void clear() noexcept; +}; + +struct block_list_create_result { + tl::expected result; + std::string warnings; +}; + +[[nodiscard]] block_list_create_result create_block_list_from_file( + const char *zip_filename) noexcept; + +[[nodiscard]] block_list_create_result create_block_list_from_buffer( + std::span) noexcept; + +#endif // SLOPECRAFT_BLOCKLIST_H diff --git a/SlopeCraftL/ColorSource.cpp b/SlopeCraftL/color_source.cpp similarity index 98% rename from SlopeCraftL/ColorSource.cpp rename to SlopeCraftL/color_source.cpp index 10b34e84..51033dac 100644 --- a/SlopeCraftL/ColorSource.cpp +++ b/SlopeCraftL/color_source.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,10 +20,14 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -// #include "SCLDefines.h" +#include "SCLDefines.h" + namespace SlopeCraft { extern const float RGBBasicSource[256 * 3]; +const std::unique_ptr basic_colorset{ + new colorset_basic_t{RGBBasicSource}}; + const float RGBBasicSource[256 * 3] = { 0 / 255.0f, 90 / 255.0f, 174 / 255.0f, 140 / 255.0f, 180 / 255.0f, 113 / 255.0f, 118 / 255.0f, 0 / 255.0f, 180 / 255.0f, 116 / 255.0f, @@ -181,4 +185,4 @@ const float RGBBasicSource[256 * 3] = { 79 / 255.0f, 0 / 255.0f, 0 / 255.0f, }; -} // namespace SlopeCraft +} // namespace SlopeCraft diff --git a/SlopeCraftL/color_table.cpp b/SlopeCraftL/color_table.cpp new file mode 100644 index 00000000..ce053754 --- /dev/null +++ b/SlopeCraftL/color_table.cpp @@ -0,0 +1,514 @@ +// +// Created by joseph on 4/15/24. +// + +#include +#include +#include "SCLDefines.h" +#include "color_table.h" +#include "water_item.h" +#include "structure_3D.h" +#include "utilities/ProcessBlockId/process_block_id.h" +#include "utilities/Schem/mushroom.h" + +std::optional color_table_impl::create( + const color_table_create_info &args) noexcept { + color_table_impl result; + result.mc_version_ = args.mc_version; + result.map_type_ = args.map_type; + { + colorset_allowed_t a; + a.need_find_side = (args.map_type == mapTypes::Slope); + result.allowed = std::make_shared(std::move(a)); + } + + auto report_err = [&args](errorFlag flag, std::string_view msg) { + args.ui.report_error(flag, msg.data()); + }; + + // block palette + { + for (size_t i = 0; i < 64; i++) { + if (args.blocks[i] == nullptr) { + result.blocks[i].clear(); + continue; + } + args.blocks[i]->copyTo(&result.blocks[i]); + // fix block id to prevent potential errors + { + if (result.blocks[i].id.find(':') == result.blocks[i].id.npos) { + result.blocks[i].id = "minecraft:" + result.blocks[i].id; + } + + if (result.blocks[i].idOld.empty()) { + result.blocks[i].idOld = result.blocks[i].id; + } + + if (result.blocks[i].idOld.size() > 0 && + (result.blocks[i].idOld.find(':') == result.blocks[i].idOld.npos)) { + result.blocks[i].idOld = "minecraft:" + result.blocks[i].idOld; + } + } + } + } + + args.ui.report_working_status(workStatus::collectingColors); + + Eigen::ArrayXi baseColorVer(64); // 基色对应的版本 + baseColorVer.setConstant((int)SCL_gameVersion::FUTURE); + baseColorVer.segment(0, 52).setConstant((int)SCL_gameVersion::ANCIENT); + baseColorVer.segment(52, 7).setConstant((int)SCL_gameVersion::MC16); + baseColorVer.segment(59, 3).setConstant((int)SCL_gameVersion::MC17); + + std::array m_index; + for (short index = 0; index < 256; index++) { + m_index[index] = + true; // 默认可以使用这种颜色,逐次判断各个不可以使用的条件 + + if (!args.basecolor_allow_LUT[index2baseColor( + index)]) { // 在 allowedBaseColor 中被禁止 + m_index[index] = false; + continue; + } + if (index2baseColor(index) == 0) { // 空气禁用 + m_index[index] = false; + continue; + } + if ((int)result.mc_version_ < + baseColorVer(index2baseColor(index))) { // 版本低于基色版本 + m_index[index] = false; + continue; + } + if (result.blocks[index2baseColor(index)].id.empty()) { // 空 id + m_index[index] = false; + continue; + } + /* + if ((mapType == mapTypes::Wall) && + !blockPalette[index2baseColor(index)] + .wallUseable) { //墙面像素画且当前方块不适合墙面 + + m_index[index] = false; + continue; + }*/ + if (result.is_vanilla() && + (index2depth(index) >= 3)) { // 可实装的地图画不允许第四种阴影 + m_index[index] = false; + continue; + } + if (index2baseColor(index) == 12) { // 如果是水且非墙面 + if (result.is_flat() && index2depth(index) != 2) { // 平板且水深不是 1 格 + m_index[index] = false; + continue; + } + } else { + if (result.is_flat() && index2depth(index) != 1) { // 平板且阴影不为 1 + m_index[index] = false; + continue; + } + } + } + + if (!result.allowed->apply_allowed(*SlopeCraft::basic_colorset, m_index)) { + std::string msg = std::format( + "Too few usable color(s) : only {} colors\nAvaliable base color(s) : ", + result.allowed->color_count()); + + for (int idx = 0; idx < result.allowed->color_count(); idx++) { + msg += std::to_string(result.allowed->Map(idx)) + ", "; + } + + report_err(errorFlag::USEABLE_COLOR_TOO_FEW, msg); + return std::nullopt; + } + + args.ui.report_working_status(workStatus::none); + + return result; +} + +structure_3D *color_table_impl::build( + const converted_image &cvted, const build_options &option) const noexcept { + auto opt = structure_3D_impl::create( + *this, dynamic_cast(cvted), option); + if (opt) { + return new structure_3D_impl{std::move(opt.value())}; + } + return nullptr; +} + +std::vector color_table_impl::block_id_list( + bool contain_air) const noexcept { + std::vector dest; + dest.reserve(this->blocks.size() + 1); + if (contain_air) { + dest.emplace_back("minecraft:air"); + } + for (auto &blk : this->blocks) { + dest.emplace_back(blk.idForVersion(this->mc_version_)); + } + return dest; +} + +const mc_block *color_table_impl::find_block_for_index( + std::string_view blkid) const noexcept { + if (blkid.empty()) { + return nullptr; + } + blkid::char_range pure_id_range; + // invalid block id + if (not blkid::process_blk_id(blkid, nullptr, &pure_id_range, nullptr)) { + return nullptr; + } + std::string_view pure_id{pure_id_range}; + + // if (idx < 0) { + // return nullptr; + // } + // + // if (idx < (int)this->blocks.size()) { + // // assert(this->blocks[idx].id == blkid); + // return &this->blocks[idx]; + // } + // + // // the block must be mushroom + // namespace lsi = libSchem::internal; + // using lsi::mushroom_type; + // + // blkid::char_range pure_id_range; + // // invalid block id + // if (not blkid::process_blk_id(blkid, nullptr, &pure_id_range, nullptr)) { + // return nullptr; + // } + // + // std::string_view pure_id{pure_id_range.begin(), pure_id_range.end()}; + // + // auto mush_type_opt = lsi::pureid_to_type(pure_id); + // if (not mush_type_opt.has_value()) { + // return nullptr; + // } + // + // uint8_t expected_basecolor = 0; + // switch (mush_type_opt.value()) { + // case mushroom_type::red: + // expected_basecolor = 28; + // break; + // case mushroom_type::brown: + // expected_basecolor = 10; + // break; + // case mushroom_type::stem: + // expected_basecolor = 3; + // break; + // } + // + // const auto *blkp = this->find_block_for_index(expected_basecolor, {}); + // + // if (blkp == nullptr) { + // return nullptr; + // } + // + // if (lsi::pureid_to_type(pure_id) != mush_type_opt) { + // return nullptr; + // } + // + // return blkp; +} + +uint64_t color_table_impl::hash() const noexcept { + boost::uuids::detail::md5 hash; + SC_HASH_ADD_DATA(hash, this->map_type_) + SC_HASH_ADD_DATA(hash, this->mc_version_) + + this->allowed->hash_add_data(hash); + decltype(hash)::digest_type digest; + hash.get_digest(digest); + std::array result; + memcpy(result.data(), digest, sizeof digest); + static_assert(sizeof(digest) == sizeof(result)); + return result[0] ^ result[1]; +} + +std::filesystem::path color_table_impl::self_cache_dir( + const char *cache_root_dir) const noexcept { + return std::format("{}/{:x}", cache_root_dir, this->hash()); +} + +std::filesystem::path color_table_impl::convert_task_cache_filename( + const_image_reference original_img, const convert_option &option, + const char *cache_root_dir) const noexcept { + auto self_cache_dir = this->self_cache_dir(cache_root_dir); + self_cache_dir.append("convert"); + self_cache_dir.append(std::format( + "{:x}", converted_image_impl::convert_task_hash(original_img, option))); + return self_cache_dir; +} + +bool color_table_impl::has_convert_cache( + const_image_reference original_img, const convert_option &option, + const char *cache_root_dir) const noexcept { + auto path = + this->convert_task_cache_filename(original_img, option, cache_root_dir); + return std::filesystem::is_regular_file(path); +} + +std::string color_table_impl::save_convert_cache( + const_image_reference original_img, const convert_option &option, + const converted_image &cvted, const char *cache_root_dir) const noexcept { + try { + auto filename = + this->convert_task_cache_filename(original_img, option, cache_root_dir); + std::filesystem::create_directories(filename.parent_path()); + + auto err = + dynamic_cast(cvted).save_cache(filename); + if (!err.empty()) { + return std::format("Failed to save cache to file \"{}\": {}", + filename.string(), err); + } + } catch (const std::exception &e) { + return std::format("Caught exception: {}", e.what()); + } + + return {}; +} + +[[nodiscard]] tl::expected +color_table_impl::load_convert_cache( + const_image_reference original_img, const convert_option &option, + const char *cache_root_dir) const noexcept { + return converted_image_impl::load_cache( + *this, + this->convert_task_cache_filename(original_img, option, cache_root_dir)); +} + +std::filesystem::path color_table_impl::build_task_cache_filename( + const converted_image &cvted_, const build_options &opt, + const char *cache_root_dir) const noexcept { + boost::uuids::detail::md5 hash; + SC_HASH_ADD_DATA(hash, opt.max_allowed_height) + SC_HASH_ADD_DATA(hash, opt.bridge_interval) + SC_HASH_ADD_DATA(hash, opt.compress_method) + SC_HASH_ADD_DATA(hash, opt.glass_method) + SC_HASH_ADD_DATA(hash, opt.fire_proof) + SC_HASH_ADD_DATA(hash, opt.enderman_proof) + SC_HASH_ADD_DATA(hash, opt.connect_mushrooms) + + auto &cvted = dynamic_cast(cvted_); + // this can be optimized + auto map_mat = cvted.converter.mapcolor_matrix(); + hash.process_bytes(map_mat.data(), map_mat.size() * sizeof(uint8_t)); + + decltype(hash)::digest_type dig; + hash.get_digest(dig); + std::array dig_u64{0, 0}; + memcpy(dig_u64.data(), dig, sizeof(dig)); + const uint64_t hash_u64 = dig_u64[0] ^ dig_u64[1]; + + auto path = this->self_cache_dir(cache_root_dir); + path.append("build"); + path.append(std::format("{:x}", hash_u64)); + return path; +} + +bool color_table_impl::save_build_cache(const converted_image &cvted, + const build_options &option, + const structure_3D &structure, + const char *cache_root_dir, + string_deliver *error) const noexcept { + const auto filename = + this->build_task_cache_filename(cvted, option, cache_root_dir); + auto err_msg = + dynamic_cast(structure).save_cache(filename); + write_to_sd(error, err_msg); + + return err_msg.empty(); +} + +bool color_table_impl::has_build_cache( + const SlopeCraft::converted_image &cvted, + const SlopeCraft::build_options &option, + const char *cache_root_dir) const noexcept { + const auto filename = + this->build_task_cache_filename(cvted, option, cache_root_dir); + return std::filesystem::is_regular_file(filename); +} + +structure_3D *color_table_impl::load_build_cache( + const SlopeCraft::converted_image &cvted, + const SlopeCraft::build_options &option, const char *cache_root_dir, + SlopeCraft::string_deliver *error) const noexcept { + const auto filename = + this->build_task_cache_filename(cvted, option, cache_root_dir); + auto res = structure_3D_impl::load_cache(filename); + if (res) { + write_to_sd(error, ""); + return new structure_3D_impl{std::move(res.value())}; + } + write_to_sd(error, res.error()); + return nullptr; +} + +void color_table_impl::stat_blocks(const structure_3D &s, + size_t buffer[64]) const noexcept { + std::fill(buffer, buffer + 64, 0); + const auto &structure = dynamic_cast(s); + + const auto schem_stat = structure.schem.stat_blocks(); + assert(schem_stat.size() == structure.palette_length()); + assert(schem_stat.size() == structure.schem.palette().size()); + for (size_t idx_table = 0; idx_table < this->blocks.size(); idx_table++) { + const auto &blk_info = this->blocks[idx_table]; + size_t count = 0; + blkid::char_range pure_id; + const bool ok = blkid::process_blk_id( + blk_info.idForVersion(this->mc_version()), nullptr, &pure_id, nullptr); + assert(ok); + if (not ok) { + continue; + } + + for (size_t idx_schem = 0; idx_schem < structure.schem.palette_size(); + idx_schem++) { + auto &schem_blkid = structure.schem.palette()[idx_schem]; + if (schem_blkid == "minecraft:air") { + continue; + } + assert(not schem_blkid.empty()); + blkid::char_range pure_id_schem; + const bool ok_schem = + blkid::process_blk_id(schem_blkid, nullptr, &pure_id_schem, nullptr); + assert(ok_schem); + if (not ok_schem) { + continue; + } + if (std::string_view{pure_id} == std::string_view{pure_id_schem}) { + count += schem_stat[idx_schem]; + } + } + buffer[idx_table] = count; + } +} + +std::string color_table_impl::impl_generate_test_schematic( + std::string_view filename, + const test_blocklist_options &option) const noexcept { + if (!filename.ends_with(".nbt")) { + return "File name should end with \".nbt\""; + } + libSchem::Schem test; + test.set_MC_major_version_number(this->mc_version_); + test.set_MC_version_number( + MCDataVersion::suggested_version(this->mc_version_)); + // const simpleBlock ** realSrc=(const simpleBlock **)src; + std::vector realSrc; + std::vector realBaseColor; + realSrc.clear(); + realBaseColor.clear(); + for (size_t idx = 0; idx < option.block_count; idx++) { + if (option.block_ptrs[idx]->getVersion() > (int)this->mc_version_) { + continue; + } + realSrc.emplace_back(static_cast(option.block_ptrs[idx])); + realBaseColor.emplace_back(option.basecolors[idx]); + } + + std::vector> block_counter; + block_counter.resize(64); + + for (uint32_t idx = 0; idx < realSrc.size(); idx++) { + block_counter[realBaseColor[idx]].push_back(idx); + } + + { + std::vector ids; + ids.reserve(realSrc.size() + 1); + ids.emplace_back("minecraft:air"); + for (auto i : realSrc) { + ids.emplace_back(i->idForVersion(this->mc_version_)); + } + + test.set_block_id(ids.data(), ids.size()); + } + + int xSize = 0; + constexpr int zSize = 64, ySize = 2; + for (const auto &it : block_counter) { + xSize = std::max(size_t(xSize), it.size()); + } + test.resize(xSize + 1, ySize, zSize); + test.set_zero(); + + for (uint8_t base = 0; base < 64; base++) { + for (uint32_t idx = 0; idx < block_counter[base].size(); idx++) { + int xPos = idx; + int yPos = 0; + int zPos = base; + + test(xPos, yPos, zPos) = block_counter[base][idx] + 1; + } + test(block_counter[base].size(), 1, base) = 1; // glass block + } + + // SCL_errorFlag err; + // std::string detail; + auto ok = test.export_structure(filename, true); + // const bool success = test.export_structure(filename, true, &err, &detail); + + if (not ok) { + auto &err = ok.error(); + return std::format( + "Failed to export structure file {}, error code = {}, detail: {}", + filename, int(err.first), err.second); + } else { + return {}; + } +} + +std::expected +color_table_impl::build_indexer() const noexcept { + color_table_searching_index indexer; + for (const auto &blk : this->blocks) { + auto info_opt = blk.detail_info(this->mc_version()); + if (not info_opt) { + return std::unexpected{std::format("Found invalid block id: \"{}\"", + blk.idForVersion(this->mc_version()))}; + } + indexer.block_LUT.emplace(info_opt.value(), &blk); + } + + return indexer; +} + +const mc_block *color_table_searching_index::find( + std::string_view id) const noexcept { + using namespace blkid; + char_range namespace_, pure_id; + if (not process_blk_id(id, &namespace_, &pure_id, nullptr)) { + return nullptr; + } + const block_detail_info detail{ + .id_namespace{namespace_}, + .pure_id{pure_id}, + }; + auto it = this->block_LUT.find(detail); + if (it == this->block_LUT.end()) { + return nullptr; + } + return it->second; +} + +std::array LUT_map_color_to_ARGB() noexcept { + const auto &basic = *SlopeCraft::basic_colorset; + std::array ret; + ret.fill(0); + for (size_t idx = 0; idx < 256; idx++) { + const auto map_color = index2mapColor(idx); + if (index2baseColor(idx) == 0) { + ret[map_color] = 0x00000000; + continue; + } + ret[map_color] = + RGB2ARGB(basic.RGB(idx, 0), basic.RGB(idx, 1), basic.RGB(idx, 2)); + } + return ret; +} \ No newline at end of file diff --git a/SlopeCraftL/color_table.h b/SlopeCraftL/color_table.h new file mode 100644 index 00000000..8f0d9a04 --- /dev/null +++ b/SlopeCraftL/color_table.h @@ -0,0 +1,154 @@ +// +// Created by joseph on 4/15/24. +// + +#ifndef SLOPECRAFT_COLOR_TABLE_H +#define SLOPECRAFT_COLOR_TABLE_H + +#include +#include +#include +#include +#include "SlopeCraftL.h" +#include "SCLDefines.h" +#include "mc_block.h" +#include "string_deliver.h" +#include "converted_image.h" + +struct color_table_searching_index { + std::unordered_map block_LUT; + + [[nodiscard]] const mc_block *find(std::string_view id) const noexcept; +}; + +class color_table_impl : public SlopeCraft::color_table { + public: + std::shared_ptr allowed{new colorset_allowed_t}; + SCL_mapTypes map_type_; + SCL_gameVersion mc_version_; + std::array blocks; + + color_map_ptrs colors() const noexcept final { + return color_map_ptrs{.r_data = allowed->rgb_data(0), + .g_data = allowed->rgb_data(1), + .b_data = allowed->rgb_data(2), + .map_data = allowed->map_data(), + .num_colors = allowed->color_count()}; + } + + SCL_mapTypes map_type() const noexcept final { return this->map_type_; } + + SCL_gameVersion mc_version() const noexcept final { + return this->mc_version_; + } + + size_t num_blocks() const noexcept final { return this->blocks.size(); } + + void visit_blocks(void (*fun)(const mc_block_interface *, void *custom_data), + void *custom_data) const final { + for (auto &blk : this->blocks) { + fun(static_cast(&blk), custom_data); + } + } + + [[nodiscard]] converted_image *convert_image( + const_image_reference original_img, + const convert_option &option) const noexcept final; + + [[nodiscard]] static std::optional create( + const color_table_create_info &args) noexcept; + + [[nodiscard]] std::vector block_id_list( + bool contain_air) const noexcept; + + [[nodiscard]] const mc_block *find_block_for_index( + std::string_view block_id) const noexcept; + + [[nodiscard]] uint64_t hash() const noexcept; + + [[nodiscard]] std::filesystem::path self_cache_dir( + const char *cache_root_dir) const noexcept; + + [[nodiscard]] std::filesystem::path convert_task_cache_filename( + const_image_reference original_img, const convert_option &option, + const char *cache_root_dir) const noexcept; + + [[nodiscard]] bool has_convert_cache( + const_image_reference original_img, const convert_option &option, + const char *cache_root_dir) const noexcept final; + + [[nodiscard]] bool save_convert_cache( + const_image_reference original_img, const convert_option &option, + const converted_image &cvted, const char *cache_root_dir, + string_deliver *error) const noexcept final { + auto err = + this->save_convert_cache(original_img, option, cvted, cache_root_dir); + write_to_sd(error, err); + return err.empty(); + } + + [[nodiscard]] std::string save_convert_cache( + const_image_reference original_img, const convert_option &option, + const converted_image &, const char *cache_root_dir) const noexcept; + + [[nodiscard]] converted_image *load_convert_cache( + const_image_reference original_img, const convert_option &option, + const char *cache_root_dir, string_deliver *error) const noexcept final { + auto res = this->load_convert_cache(original_img, option, cache_root_dir); + if (!res) { + write_to_sd(error, res.error()); + return nullptr; + } + + write_to_sd(error, ""); + + return new converted_image_impl{std::move(res.value())}; + } + + [[nodiscard]] tl::expected + load_convert_cache(const_image_reference original_img, + const convert_option &option, + const char *cache_root_dir) const noexcept; + + [[nodiscard]] structure_3D *build(const converted_image &, + const build_options &) const noexcept final; + + [[nodiscard]] std::filesystem::path build_task_cache_filename( + const converted_image &, const build_options &, + const char *cache_root_dir) const noexcept; + + [[nodiscard]] bool save_build_cache( + const converted_image &, const build_options &, const structure_3D &, + const char *cache_root_dir, string_deliver *error) const noexcept final; + [[nodiscard]] bool has_build_cache( + const converted_image &, const build_options &, + const char *cache_root_dir) const noexcept final; + [[nodiscard]] structure_3D *load_build_cache( + const converted_image &, const build_options &, + const char *cache_root_dir, string_deliver *error) const noexcept final; + + void stat_blocks(const structure_3D &s, + size_t buffer[64]) const noexcept final; + + bool generate_test_schematic( + const char *filename, + const test_blocklist_options &option) const noexcept final { + auto err = this->impl_generate_test_schematic(filename, option); + write_to_sd(option.err, err); + return err.empty(); + } + + std::string impl_generate_test_schematic( + std::string_view filename, + const test_blocklist_options &option) const noexcept; + + [[nodiscard]] std::expected + build_indexer() const noexcept; +}; + +[[nodiscard]] std::array LUT_map_color_to_ARGB() noexcept; + +//[[nodiscard]] std::string digest_to_string( +// std::span hash) noexcept; + +#endif // SLOPECRAFT_COLOR_TABLE_H diff --git a/SlopeCraftL/converted_image.cpp b/SlopeCraftL/converted_image.cpp new file mode 100644 index 00000000..312107e9 --- /dev/null +++ b/SlopeCraftL/converted_image.cpp @@ -0,0 +1,737 @@ +// +// Created by joseph on 4/17/24. +// + +#include +#include +#include +#include "SCLDefines.h" +#include "converted_image.h" +#include "color_table.h" +#include "height_line.h" +#include "lossy_compressor.h" +#include "NBTWriter/NBTWriter.h" +#include "structure_3D.h" + +converted_image_impl::converted_image_impl(const color_table_impl &table) + : converter{*SlopeCraft::basic_colorset, *table.allowed}, + game_version{table.mc_version()}, + colorset{table.allowed} {} + +converted_image *color_table_impl::convert_image( + const_image_reference original_img, + const convert_option &option) const noexcept { + converted_image_impl cvted{*this}; + + const auto algo = (option.algo == convertAlgo::gaCvter) + ? convertAlgo::RGB_Better + : option.algo; + cvted.converter.set_raw_image(original_img.data, original_img.rows, + original_img.cols, false); + { + heu::GAOption opt; + opt.crossoverProb = option.ai_cvter_opt.crossoverProb; + opt.mutateProb = option.ai_cvter_opt.mutationProb; + opt.maxGenerations = option.ai_cvter_opt.maxGeneration; + opt.maxFailTimes = option.ai_cvter_opt.maxFailTimes; + opt.populationSize = option.ai_cvter_opt.popSize; + + cvted.converter.convert_image(algo, option.dither, &opt); + } + + option.progress.set_range(0, 4 * cvted.size(), 4 * cvted.size()); + option.ui.report_working_status(workStatus::none); + + return new converted_image_impl{std::move(cvted)}; +} + +void converted_image_impl::get_compressed_image( + const structure_3D &structure_, uint32_t *buffer) const noexcept { + const auto &structure = dynamic_cast(structure_); + assert(this->rows() == structure.map_color.rows()); + assert(this->cols() == structure.map_color.cols()); + + const auto LUT = LUT_map_color_to_ARGB(); + Eigen::Map< + Eigen::Array> + dest{buffer, static_cast(this->rows()), + static_cast(this->cols())}; + dest.fill(0); + for (size_t r = 0; r < this->rows(); r++) { + for (size_t c = 0; c < this->cols(); c++) { + const auto map_color = structure.map_color(r, c); + assert(map_color >= 0); + assert(map_color <= 255); + dest(r, c) = LUT[map_color]; + } + } +} + +bool converted_image_impl::export_map_data( + const SlopeCraft::map_data_file_options &option) const noexcept { + const std::filesystem::path dir{option.folder_path}; + const auto mapPic = this->converter.mapcolor_matrix(); + const int rows = this->map_rows(); + const int cols = this->map_cols(); + // const int rows = ceil(mapPic.rows() / 128.0f); + // const int cols = ceil(mapPic.cols() / 128.0f); + option.progress.set_range(0, 128 * rows * cols, 0); + + // int offset[2] = {0, 0}; // r,c + int currentIndex = option.begin_index; + + option.ui.report_working_status(workStatus::writingMapDataFiles); + + int fail_count = 0; + for (int c = 0; c < cols; c++) { + for (int r = 0; r < rows; r++) { + const std::array offset = {r * 128, c * 128}; + std::filesystem::path current_filename = dir; + current_filename.append(std::format("map_{}.dat", currentIndex)); + + NBT::NBTWriter MapFile; + + if (!MapFile.open(current_filename.string().c_str())) { + option.ui.report_error(errorFlag::EXPORT_MAP_DATA_FAILURE, + std::format("Failed to create nbt file {}", + current_filename.string()) + .c_str()); + fail_count += 1; + continue; + } + switch (this->game_version) { + case SCL_gameVersion::MC12: + case SCL_gameVersion::MC13: + break; + case SCL_gameVersion::MC14: + case SCL_gameVersion::MC15: + case SCL_gameVersion::MC16: + case SCL_gameVersion::MC17: + case SCL_gameVersion::MC18: + case SCL_gameVersion::MC19: + case SCL_gameVersion::MC20: + case SCL_gameVersion::MC21: + MapFile.writeInt( + "DataVersion", + static_cast( + MCDataVersion::suggested_version(this->game_version))); + break; + default: + cerr << "Wrong game version!\n"; + break; + } + + static const std::string ExportedBy = std::format( + "Exported by SlopeCraft {}, developed by TokiNoBug", SC_VERSION_STR); + MapFile.writeString("ExportedBy", ExportedBy.data()); + MapFile.writeCompound("data"); + { + MapFile.writeByte("scale", 0); + MapFile.writeByte("trackingPosition", 0); + MapFile.writeByte("unlimitedTracking", 0); + MapFile.writeInt("xCenter", 0); + MapFile.writeInt("zCenter", 0); + switch (this->game_version) { + case SCL_gameVersion::MC12: + MapFile.writeByte("dimension", 114); + MapFile.writeShort("height", 128); + MapFile.writeShort("width", 128); + break; + case SCL_gameVersion::MC13: + MapFile.writeListHead("banners", NBT::Compound, 0); + MapFile.writeListHead("frames", NBT::Compound, 0); + MapFile.writeInt("dimension", 889464); + break; + case SCL_gameVersion::MC14: + MapFile.writeListHead("banners", NBT::Compound, 0); + MapFile.writeListHead("frames", NBT::Compound, 0); + MapFile.writeInt("dimension", 0); + MapFile.writeByte("locked", 1); + break; + case SCL_gameVersion::MC15: + MapFile.writeListHead("banners", NBT::Compound, 0); + MapFile.writeListHead("frames", NBT::Compound, 0); + MapFile.writeInt("dimension", 0); + MapFile.writeByte("locked", 1); + break; + case SCL_gameVersion::MC16: + case SCL_gameVersion::MC17: + case SCL_gameVersion::MC18: + case SCL_gameVersion::MC19: + case SCL_gameVersion::MC20: + case SCL_gameVersion::MC21: + MapFile.writeListHead("banners", NBT::Compound, 0); + MapFile.writeListHead("frames", NBT::Compound, 0); + MapFile.writeString("dimension", "minecraft:overworld"); + MapFile.writeByte("locked", 1); + break; + default: + cerr << "Wrong game version!\n"; + option.ui.report_error(errorFlag::UNKNOWN_MAJOR_GAME_VERSION, + "Unknown major game version!"); + fail_count += 1; + continue; + } + + MapFile.writeByteArrayHead("colors", 16384); + { + for (short rr = 0; rr < 128; rr++) { + for (short cc = 0; cc < 128; cc++) { + uint8_t ColorCur; + if (rr + offset[0] < mapPic.rows() && + cc + offset[1] < mapPic.cols()) + ColorCur = mapPic(rr + offset[0], cc + offset[1]); + else + ColorCur = 0; + MapFile.writeByte("this should never be seen", ColorCur); + } + option.progress.add(1); + } + } + } + MapFile.endCompound(); + MapFile.close(); + + currentIndex++; + } + } + option.ui.report_working_status(workStatus::none); + return (fail_count == 0); +} + +std::optional +converted_image_impl::height_info(const build_options &option) const noexcept { + // + // std::unordered_map water_list; + + Eigen::ArrayXXi map_color = this->converter.mapcolor_matrix().cast(); + + const bool allow_lossless_compress = + int(option.compress_method) bitand int(SCL_compressSettings::NaturalOnly); + const bool allow_lossy_compress = + int(option.compress_method) bitand int(compressSettings::ForcedOnly); + + if (((map_color - 4 * (map_color / 4)) >= 3).any()) { + std::string msg = + "Fatal error : SlopeCraftL found map color with depth 3 in a " + "vanilla map.\n Map contents (map color matrix in col-major) :\n["; + for (int c = 0; c < map_color.cols(); c++) { + for (int r = 0; r < map_color.rows(); r++) { + std::format_to(std::back_insert_iterator{msg}, "{},", map_color(r, c)); + } + msg += ";\n"; + } + msg += "];\n"; + option.ui.report_error(errorFlag::DEPTH_3_IN_VANILLA_MAP, msg.c_str()); + return std::nullopt; + } + + Eigen::ArrayXXi base, high_map, low_map; + base.setZero(this->rows() + 1, this->cols()); + high_map.setZero(this->rows() + 1, this->cols()); + low_map.setZero(this->rows() + 1, this->cols()); + std::unordered_map water_list; + + lossy_compressor compressor; + compressor.ui = option.ui; + compressor.progress_bar = option.sub_progressbar; + for (int64_t c = 0; c < map_color.cols(); c++) { + // cerr << "Coloumn " << c << '\n'; + height_line HL; + // getTokiColorPtr(c,&src[0]); + HL.make(map_color.col(c), allow_lossless_compress); + + if ((HL.maxHeight() > option.max_allowed_height) and allow_lossy_compress) { + std::vector ptr(map_color.rows()); + + this->converter.col_TokiColor_ptrs(c, ptr); + // getTokiColorPtr(c, &ptr[0]); + + compressor.setSource(HL.getBase(), ptr); + bool success = compressor.compress(option.max_allowed_height, + allow_lossless_compress); + Eigen::ArrayXi temp; + HL.make(&ptr[0], compressor.getResult(), allow_lossless_compress, &temp); + if (!success) { + option.ui.report_error( + SCL_errorFlag::LOSSYCOMPRESS_FAILED, + std::format("Failed to compress the 3D structure at column {}. You " + "have required that max height <= {}, but SlopeCraft " + "is only able to this column to max height = {}.", + c, option.max_allowed_height, HL.maxHeight()) + .data()); + return std::nullopt; + } + map_color.col(c) = temp; + } + base.col(c) = HL.getBase(); + high_map.col(c) = HL.getHighLine(); + low_map.col(c) = HL.getLowLine(); + + auto hl_water_list = HL.getWaterMap(); + water_list.reserve(water_list.size() + hl_water_list.size()); + for (const auto &[r, water_item] : hl_water_list) { + water_list.emplace( + rc_pos{static_cast(r), static_cast(c)}, water_item); + } + + option.main_progressbar.add(4 * this->size()); + } + + return height_maps{.map_color = map_color, + .base = base, + .high_map = high_map, + .low_map = low_map, + .water_list = water_list}; +} + +uint64_t converted_image_impl::convert_task_hash( + const_image_reference original_img, const convert_option &option) noexcept { + boost::uuids::detail::md5 hash; + + SC_HASH_ADD_DATA(hash, option.algo) + SC_HASH_ADD_DATA(hash, option.dither) + if (option.algo == SCL_convertAlgo::gaCvter) { + SC_HASH_ADD_DATA(hash, option.ai_cvter_opt.popSize) + SC_HASH_ADD_DATA(hash, option.ai_cvter_opt.maxGeneration) + SC_HASH_ADD_DATA(hash, option.ai_cvter_opt.maxFailTimes) + SC_HASH_ADD_DATA(hash, option.ai_cvter_opt.crossoverProb) + SC_HASH_ADD_DATA(hash, option.ai_cvter_opt.mutationProb) + } + + hash.process_bytes(original_img.data, original_img.rows * original_img.cols); + + decltype(hash)::digest_type dig; + hash.get_digest(dig); + std::array temp; + memcpy(temp.data(), dig, sizeof(temp)); + return temp[0] ^ temp[1]; +} + +std::string converted_image_impl::save_cache( + const std::filesystem::path &file) const noexcept { + if (this->converter.save_cache(file.string().c_str())) { + return "Failed to open file."; + } + return {}; +} + +tl::expected +converted_image_impl::load_cache(const color_table_impl &table, + const std::filesystem::path &file) noexcept { + converted_image_impl ret{table}; + if (!std::filesystem::is_regular_file(file)) { + return tl::make_unexpected("No such file"); + } + if (!ret.converter.load_cache(file.string().c_str())) { + return tl::make_unexpected("Failed to load cache, the cache is incorrect"); + } + return ret; +} + +bool converted_image_impl::is_converted_from( + const color_table &table_) const noexcept { + const auto &table = dynamic_cast(table_); + return (this->colorset.get() == table.allowed.get()); +} +#include +#include +#include +#include +#include +#include + +constexpr int chest_rows = 3; +constexpr int chest_cols = 9; + +int8_t chest_index_to_slot(int row, int col) noexcept { + assert(row >= 0 and row < chest_rows); + assert(col >= 0 and col < chest_cols); + return static_cast(row * chest_cols + col); +} + +nbt::tag_compound &get_or_setup_field(nbt::tag_compound &parent, + const std::string &key) noexcept { + if (parent.has_key(key, nbt::tag_type::Compound)) { + return parent[key].as(); + } + auto ret = parent.emplace(key, nbt::tag_compound{}); + return ret.first->second.as(); +} + +// Merge each 3*9 zone into a chest recursively, until whole item matrix is +// merged into one chest item +nbt::tag_compound merge_with_chest( + boost::multi_array item_matrix, + const map_data_file_give_command_options &option) noexcept { + const size_t new_rows = std::ceil(float(item_matrix.shape()[0]) / chest_rows); + const size_t new_cols = std::ceil(float(item_matrix.shape()[1]) / chest_cols); + boost::multi_array merged_chests{ + boost::extents[new_rows][new_cols]}; + // Move and merge previous data into new 2d array + for (int merged_col = 0; merged_col < new_cols; merged_col++) { + for (int merged_row = 0; merged_row < new_rows; merged_row++) { + nbt::tag_list item_list; + for (int c_offset = 0; c_offset < chest_cols; c_offset++) { + for (int r_offset = 0; r_offset < chest_rows; r_offset++) { + const int c_original = c_offset + merged_col * chest_cols; + const int r_original = r_offset + merged_row * chest_rows; + if (r_original >= item_matrix.shape()[0] or + c_original >= item_matrix.shape()[1]) { + continue; + } + nbt::tag_compound cur_item{ + std::move(item_matrix[r_original][c_original])}; + if (cur_item.size() <= 0) { + // skip empty item + continue; + } + const int8_t slot = chest_index_to_slot(r_offset, c_offset); + if (not option.after_1_20_5) { + // set slot + cur_item.emplace("Slot", slot); + item_list.emplace_back(std::move(cur_item)); + } else { + nbt::tag_compound item; + item.emplace("item", std::move(cur_item)); + item.emplace("slot", slot); + item_list.emplace_back(std::move(item)); + } + } + } + + nbt::tag_compound chest_item; + chest_item.emplace("id", "minecraft:chest"); + if (not option.after_1_20_5) { + chest_item.emplace("Count", 1); + } else { + chest_item.emplace("count", 1); + } + if (not option.after_1_20_5) { + nbt::tag_compound blk_entity_tag; + blk_entity_tag.emplace("Items", std::move(item_list)); + get_or_setup_field(chest_item, "tag") + .emplace("BlockEntityTag", + std::move(blk_entity_tag)); + } else { + get_or_setup_field(chest_item, "components") + .emplace("minecraft:container", + std::move(item_list)); + } + + merged_chests[merged_row][merged_col] = std::move(chest_item); + } + } + + if (new_rows <= 1 and new_cols <= 1) { + return merged_chests[0][0]; + } + + return merge_with_chest(std::move(merged_chests), option); +} + +bool converted_image_impl::get_map_command( + const map_data_file_give_command_options &option) const { + if (option.destination == nullptr) { + return false; + } + + const int map_rows = this->map_rows(); + const int map_cols = this->map_cols(); + if (map_rows <= 0 or map_cols <= 0) { + std::string err_msg = + std::format("Invalid map size: {} rows, {} cols", map_rows, map_cols); + option.destination->write(err_msg.c_str(), err_msg.size()); + return false; + } + + auto item_of_location = [map_rows, map_cols, option]( + int r, int c) -> nbt::tag_compound { + assert(r >= 0 and r < map_rows); + assert(c >= 0 and c < map_cols); + // Maps are placed in col-major + const int index = option.begin_index + r + c * map_rows; + + std::string name; + if (option.set_name_as_index) + name = std::format("{{\"text\":\"[{},{}]\"}}", r, c); + + nbt::tag_compound result; + nbt::tag_compound tag; + result.emplace("id", "minecraft:filled_map"); + // set up item name + if (option.set_name_as_index and not option.after_1_20_5) { + nbt::tag_compound display; + display.emplace("Name", + nbt::tag_string{std::move(name)}); + tag.emplace("display", std::move(display)); + } + // set up item properties + if (not option.after_1_12) { + result.emplace("Count", option.stack_count); + result.emplace("Damage", index); + result.emplace("tag", std::move(tag)); + return result; + } + if (not option.after_1_20_5) { + result.emplace("Count", option.stack_count); + tag.emplace("map", index); + result.emplace("tag", std::move(tag)); + return result; + } + result.emplace("count", option.stack_count); + tag.emplace("map_id", index); + tag.emplace("custom_name", + nbt::tag_string{std::move(name)}); + result.emplace("components", std::move(tag)); + return result; + }; + + nbt::tag_compound chest_all_in_one; + auto erase_if = [&chest_all_in_one](const char *key) noexcept { + if (chest_all_in_one.has_key(key)) { + chest_all_in_one.erase(key); + } + }; + { + boost::multi_array maps{ + boost::extents[map_rows][map_cols]}; + for (int r = 0; r < map_rows; r++) { + for (int c = 0; c < map_cols; c++) { + maps[r][c] = item_of_location(r, c); + } + } + chest_all_in_one = merge_with_chest(std::move(maps), option); + } + + const std::string item_id = + chest_all_in_one.at("id").as().get(); + option.destination->write(std::format("/give @p {}", item_id).c_str()); + erase_if("Count"); + erase_if("count"); + erase_if("id"); + std::ostringstream oss; + { + sNBT::sNBT_format_visitor formatter{oss}; + if (not option.after_1_20_5) { + chest_all_in_one.at("tag").as().accept(formatter); + } else { + const nbt::tag_compound &components = + chest_all_in_one.at("components").as(); + oss << '['; + for (auto &[key, val] : components) { + oss << key << '='; + val.get().accept(formatter); + oss << ','; + } + oss << ']'; + } + } + std::string_view snbt = oss.view(); + option.destination->write(snbt.data(), snbt.size()); + option.destination->write(" 1"); + + return true; +} + +Eigen::Matrix transform_mat_of(SCL_map_facing facing) noexcept { + switch (facing) { + case SCL_map_facing::wall_west: + return Eigen::Matrix{{0, 0}, // + {-1, 0}, // r y- + {0, 1}}; // c z+ + case SCL_map_facing::wall_north: + return Eigen::Matrix{{0, -1}, // c x- + {-1, 0}, // r y- + {0, 0}}; // + case SCL_map_facing::wall_east: + return Eigen::Matrix{{0, 0}, // + {-1, 0}, // r y- + {0, -1}}; // c z- + case SCL_map_facing::wall_south: + return Eigen::Matrix{{0, 1}, // c x+ + {-1, 0}, // r y- + {0, 0}}; // + case SCL_map_facing::top_south: + return Eigen::Matrix{{0, -1}, // c x- + {0, 0}, // + {-1, 0}}; // r z- + case SCL_map_facing::top_north: + return Eigen::Matrix{{0, 1}, // c x+ + {0, 0}, // + {1, 0}}; // r z+ + case SCL_map_facing::top_east: + return Eigen::Matrix{{-1, 0}, // r x- + {0, 0}, // + {0, 1}}; // c z+ + case SCL_map_facing::top_west: + return Eigen::Matrix{{1, 0}, // r x+ + {0, 0}, // + {0, -1}}; // c z- + case SCL_map_facing::bottom_north: + return Eigen::Matrix{{0, -1}, // c x- + {0, 0}, // + {1, 0}}; // r z+ + case SCL_map_facing::bottom_south: + return Eigen::Matrix{{0, 1}, // c x+ + {0, 0}, // + {-1, 0}}; // r z- + case SCL_map_facing::bottom_east: + return Eigen::Matrix{{-1, 0}, // r x- + {0, 0}, // + {0, -1}}; // c z- + case SCL_map_facing::bottom_west: + return Eigen::Matrix{{1, 0}, // r x+ + {0, 0}, // + {0, 1}}; // c z+ + } + std::unreachable(); +} + +uint8_t rotation_of(SCL_map_facing facing) noexcept { + switch (facing) { + case SCL_map_facing::top_south: + case SCL_map_facing::bottom_north: + return 2; + case SCL_map_facing::top_north: + case SCL_map_facing::bottom_south: + return 0; + case SCL_map_facing::top_east: + case SCL_map_facing::bottom_east: + return 1; + case SCL_map_facing::top_west: + case SCL_map_facing::bottom_west: + return 3; + default: + return 0; + } +} + +libSchem::Schem converted_image_impl::assembled_maps( + const assembled_maps_options &option) const noexcept { + const auto transform_mat = transform_mat_of(option.map_facing); + const auto transform_mat_abs = transform_mat.array().abs().matrix(); + const Eigen::Vector3i offset = + (transform_mat_abs - transform_mat) / 2 * + Eigen::Vector2i{{int(this->map_rows() - 1), int(this->map_cols() - 1)}}; + // Shape of schematic + const Eigen::Vector3i shape = [this, transform_mat_abs]() { + Eigen::Vector3i s = + transform_mat_abs * + Eigen::Vector2i{{int(this->map_rows()), int(this->map_cols())}}; + s = s.array().max(1).matrix(); + return s; + }(); + + const MCDataVersion::MCDataVersion_t data_version = [option]() { + if (option.mc_version == SCL_gameVersion::MC20) { + if (not option.after_1_20_5) { + return MCDataVersion::MCDataVersion_t::Java_1_20_4; + } + return MCDataVersion::MCDataVersion_t::Java_1_20_5; + } else { + return MCDataVersion::suggested_version(option.mc_version); + } + }(); + + libSchem::Schem schem; + { + std::string_view id_list[1]{std::string_view{"minecraft:air"}}; + schem.set_block_id(id_list); + schem.resize(shape[0], shape[1], shape[2]); + schem.fill(0); + schem.set_MC_major_version_number(option.mc_version); + schem.set_MC_version_number(data_version); + } + + const libSchem::item_frame_variant variant = [&option, this]() { + if (this->game_version < SCL_gameVersion::MC17) { + return libSchem::item_frame_variant::common; + } + if (option.frame_variant == SCL_item_frame_variant::common) { + return libSchem::item_frame_variant::common; + } else { + return libSchem::item_frame_variant::glowing; + } + }(); + + for (int r = 0; r < this->map_rows(); r++) { + for (int c = 0; c < this->map_cols(); c++) { + const int map_index = option.begin_index + r + c * this->map_rows(); + const Eigen::Vector3i position = + transform_mat * Eigen::Vector2i{{r, c}} + offset; + // Check map position + for (int dim = 0; dim < 3; dim++) { + assert(position[dim] >= 0); + assert(position[dim] < shape[dim]); + } + auto map_entity = std::make_unique(); + map_entity->tile_position_ = {position[0], position[1], position[2]}; + map_entity->variant_ = variant; + map_entity->invisible_ = option.invisible_frame; + map_entity->fixed_ = option.fixed_frame; + map_entity->direction_ = + [](SCL_map_facing f) -> libSchem::hangable_facing_direction { + switch (f) { + case SCL_map_facing::wall_south: + return libSchem::hangable_facing_direction::south; + case SCL_map_facing::wall_east: + return libSchem::hangable_facing_direction::east; + case SCL_map_facing::wall_north: + return libSchem::hangable_facing_direction::north; + case SCL_map_facing::wall_west: + return libSchem::hangable_facing_direction::west; + case SCL_map_facing::top_north: + case SCL_map_facing::top_south: + case SCL_map_facing::top_east: + case SCL_map_facing::top_west: + return libSchem::hangable_facing_direction::top; + case SCL_map_facing::bottom_north: + case SCL_map_facing::bottom_south: + case SCL_map_facing::bottom_east: + case SCL_map_facing::bottom_west: + return libSchem::hangable_facing_direction::bottom; + } + return {}; + }(option.map_facing); + map_entity->item_rotation = rotation_of(option.map_facing); + { + auto map_item = std::make_unique(); + map_item->map_id = map_index; + map_entity->item_ = std::move(map_item); + } + + schem.entity_list().emplace_back(std::move(map_entity)); + } + } + + return schem; +} + +bool converted_image_impl::export_assembled_maps_litematic( + const char *filename, const SlopeCraft::assembled_maps_options &map_opt, + const SlopeCraft::litematic_options &export_opt) const noexcept { + auto schem = this->assembled_maps(map_opt); + libSchem::litematic_info info{}; + info.litename_utf8 = export_opt.litename_utf8; + info.regionname_utf8 = export_opt.region_name_utf8; + + auto err = schem.export_litematic(filename, info); + if (not err) { + export_opt.ui.report_error(err.error().first, err.error().second.c_str()); + return false; + } + return true; +} + +bool converted_image_impl::export_assembled_maps_vanilla_structure( + const char *filename, const SlopeCraft::assembled_maps_options &map_opt, + const SlopeCraft::vanilla_structure_options &export_opt) const noexcept { + auto schem = this->assembled_maps(map_opt); + auto err = schem.export_structure(filename, export_opt.is_air_structure_void); + if (not err) { + export_opt.ui.report_error(err.error().first, err.error().second.c_str()); + return false; + } + return true; +} diff --git a/SlopeCraftL/converted_image.h b/SlopeCraftL/converted_image.h new file mode 100644 index 00000000..c85d201b --- /dev/null +++ b/SlopeCraftL/converted_image.h @@ -0,0 +1,100 @@ +// +// Created by joseph on 4/17/24. +// + +#ifndef SLOPECRAFT_CONVERTED_IMAGE_H +#define SLOPECRAFT_CONVERTED_IMAGE_H +#include +#include +#include +#include +#include + +#include "SlopeCraftL.h" +#include "SCLDefines.h" +#include "mc_block.h" +#include "Schem/Schem.h" +#include "water_item.h" +#include "string_deliver.h" + +class color_table_impl; + +class converted_image_impl : public converted_image { + public: + converted_image_impl(converted_image_impl &&) = default; + explicit converted_image_impl(const color_table_impl &table); + using eimg_row_major = + Eigen::Array; + libMapImageCvt::MapImageCvter converter; + + private: + const SCL_gameVersion game_version; + const std::shared_ptr colorset; + + public: + // [[nodiscard]] uint64_t hash() const noexcept; + + size_t rows() const noexcept final { return this->converter.rows(); } + size_t cols() const noexcept final { return this->converter.cols(); } + + int map_rows() const noexcept { return ceil(this->rows() / 128.0f); } + int map_cols() const noexcept { return ceil(this->cols() / 128.0f); } + + void get_original_image(uint32_t *buffer) const noexcept final { + Eigen::Map buf{buffer, static_cast(this->rows()), + static_cast(this->cols())}; + buf = this->converter.raw_image(); + } + // void get_dithered_image(uint32_t *buffer) const noexcept final {} + void get_converted_image(uint32_t *buffer) const noexcept final { + this->converter.converted_image(buffer, nullptr, nullptr, false); + } + + void get_compressed_image(const structure_3D &structure, + uint32_t *buffer) const noexcept final; + + bool export_map_data( + const map_data_file_options &option) const noexcept final; + + struct height_maps { + Eigen::ArrayXXi map_color; + Eigen::ArrayXXi base; + Eigen::ArrayXXi high_map; + Eigen::ArrayXXi low_map; + std::unordered_map water_list; + }; + std::optional height_info( + const build_options &option) const noexcept; + + [[nodiscard]] static uint64_t convert_task_hash( + const_image_reference original_img, + const convert_option &option) noexcept; + + std::string save_cache(const std::filesystem::path &file) const noexcept; + + [[nodiscard]] static tl::expected + load_cache(const color_table_impl &table, + const std::filesystem::path &file) noexcept; + + bool is_converted_from(const color_table &table_) const noexcept final; + + bool get_map_command( + const map_data_file_give_command_options &option) const final; + + [[nodiscard]] libSchem::Schem assembled_maps( + const assembled_maps_options &) const noexcept; + + bool export_assembled_maps_litematic( + const char *filename, const assembled_maps_options &, + const litematic_options &) const noexcept final; + virtual bool export_assembled_maps_vanilla_structure( + const char *filename, const assembled_maps_options &, + const vanilla_structure_options &) const noexcept final; +}; + +[[nodiscard]] Eigen::Matrix transform_mat_of( + SCL_map_facing) noexcept; + +[[nodiscard]] uint8_t rotation_of(SCL_map_facing) noexcept; + +#endif // SLOPECRAFT_CONVERTED_IMAGE_H diff --git a/SlopeCraftL/HeightLine.cpp b/SlopeCraftL/height_line.cpp similarity index 68% rename from SlopeCraftL/HeightLine.cpp rename to SlopeCraftL/height_line.cpp index a53d3423..a51d89cc 100644 --- a/SlopeCraftL/HeightLine.cpp +++ b/SlopeCraftL/height_line.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,55 +20,53 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "HeightLine.h" +#include "height_line.h" -const ARGB HeightLine::BlockColor = ARGB32(0, 0, 0); -const ARGB HeightLine::AirColor = ARGB32(255, 255, 255); -const ARGB HeightLine::WaterColor = ARGB32(0, 64, 255); -const ARGB HeightLine::greyColor = ARGB32(192, 192, 192); +const ARGB height_line::BlockColor = ARGB32(0, 0, 0); +const ARGB height_line::AirColor = ARGB32(255, 255, 255); +const ARGB height_line::WaterColor = ARGB32(0, 64, 255); +const ARGB height_line::greyColor = ARGB32(192, 192, 192); -HeightLine::HeightLine() {} +height_line::height_line() {} -float HeightLine::make(const TokiColor **src, - const Eigen::Array &g, - bool allowNaturalCompress, Eigen::ArrayXi *dst) { +float height_line::make(const TokiColor **src, + const Eigen::Array &g, + bool allowNaturalCompress, Eigen::ArrayXi *dst) { float sumDiff = 0; Eigen::ArrayXi mapColorCol(g.rows()); - for (uint16_t r = 0; r < g.rows(); r++) { - + for (uint32_t r = 0; r < g.rows(); r++) { if (src[r] == nullptr) { std::cerr << "Fatal Error! nullptr found in src\n"; return 0; } switch (g(r)) { - case 0: - mapColorCol(r) = src[r]->Result; - sumDiff += src[r]->ResultDiff; - break; - case 1: - mapColorCol(r) = src[r]->sideResult[0]; - sumDiff += src[r]->sideSelectivity[0]; - break; - default: - mapColorCol(r) = src[r]->sideResult[1]; - sumDiff += src[r]->sideSelectivity[1]; - break; + case 0: + mapColorCol(r) = src[r]->Result; + sumDiff += src[r]->ResultDiff; + break; + case 1: + mapColorCol(r) = src[r]->sideResult[0]; + sumDiff += src[r]->sideSelectivity[0]; + break; + default: + mapColorCol(r) = src[r]->sideResult[1]; + sumDiff += src[r]->sideSelectivity[1]; + break; } } - if (dst != nullptr) - *dst = mapColorCol; + if (dst != nullptr) *dst = mapColorCol; make(mapColorCol, allowNaturalCompress); return sumDiff; } -void HeightLine::make(const Eigen::ArrayXi &mapColorCol, - bool allowNaturalCompress) { +void height_line::make(const Eigen::ArrayXi &mapColorCol, + bool allowNaturalCompress) { ///////////////////////1 waterMap.clear(); - const uint16_t picRows = mapColorCol.rows(); + const uint32_t picRows = mapColorCol.rows(); base.setConstant(1 + picRows, 11); HighLine.setZero(1 + picRows); LowLine.setZero(1 + picRows); @@ -80,13 +78,16 @@ void HeightLine::make(const Eigen::ArrayXi &mapColorCol, base.segment(1, picRows) = mapColorCol / 4; Eigen::ArrayXi rawShadow = mapColorCol - 4 * (mapColorCol / 4); - if ((rawShadow >= 3).any()) { - std::cerr << "Fatal Error: depth=3 in vanilla map!" << std::endl; - std::cerr << "SlopeCraft will crash." << std::endl; - exit(1); - // delete &rawShadow; - return; - } + assert(!(rawShadow >= 3).any()); + // + // if () { + // #warning "Fix this error handling" + // std::cerr << "Fatal Error: depth=3 in vanilla map!" << std::endl; + // std::cerr << "SlopeCraft will crash." << std::endl; + // exit(1); + // // delete &rawShadow; + // return; + // } Eigen::ArrayXi dealedDepth(picRows + 1); dealedDepth.setZero(); dealedDepth.segment(1, picRows) = rawShadow - 1; @@ -95,18 +96,20 @@ void HeightLine::make(const Eigen::ArrayXi &mapColorCol, base(0) = 0; dealedDepth(1) = 0; } - for (uint16_t r = 1; r < picRows; r++) { + for (uint32_t r = 1; r < picRows; r++) { if (base(r + 1) == 0) { dealedDepth(r + 1) = 0; continue; } if (base(r + 1) == 12) { dealedDepth(r + 1) = 0; - waterMap[r + 1] = nullWater; + if (waterMap.contains(r + 1)) { + waterMap.erase(r + 1); + } } } ///////////////////////3 - for (uint16_t r = 0; r < picRows; r++) { + for (uint32_t r = 0; r < picRows; r++) { // HighMap.row(r+1)=HighMap.row(r)+dealedDepth.row(r+1); HighLine(r + 1) = HighLine(r) + dealedDepth(r + 1); } @@ -116,57 +119,52 @@ void HeightLine::make(const Eigen::ArrayXi &mapColorCol, /* LowMap(TokiRow(it->first),TokiCol(it->first))= HighMap(TokiRow(it->first),TokiCol(it->first)) - -WaterColumnSize[rawShadow(TokiRow(it->first)-1,TokiCol(it->first))]+1; + -WATER_COLUMN_SIZE[rawShadow(TokiRow(it->first)-1,TokiCol(it->first))]+1; */ LowLine(it->first) = - HighLine(it->first) - WaterColumnSize[rawShadow(it->first - 1)] + 1; + HighLine(it->first) - WATER_COLUMN_SIZE[rawShadow(it->first - 1)] + 1; } /////////////////5 HighLine -= LowLine.minCoeff(); LowLine -= LowLine.minCoeff(); if (allowNaturalCompress) { - OptiChain OC(base, HighLine, LowLine); - OC.divideAndCompress(); - HighLine = OC.getHighLine(); - LowLine = OC.getLowLine(); + optimize_chain OC(base, HighLine, LowLine); + OC.divide_and_compress(); + HighLine = OC.high_line(); + LowLine = OC.low_line(); } for (auto it = waterMap.begin(); it != waterMap.end(); it++) { - waterMap[it->first] = TokiWater(HighLine(it->first), LowLine(it->first)); + waterMap[it->first] = + water_y_range{HighLine(it->first), LowLine(it->first)}; HighLine(it->first) += 1; } } -uint16_t HeightLine::maxHeight() const { +uint32_t height_line::maxHeight() const { return HighLine.maxCoeff() - LowLine.minCoeff() + 1; } -void HeightLine::updateWaterMap() { +void height_line::updateWaterMap() { waterMap.clear(); - for (uint16_t r = 1; r < base.rows(); r++) { - if (base(r) != 12) - continue; - waterMap[r] = TokiWater(HighLine(r) - 1, LowLine(r)); + for (uint32_t r = 1; r < base.rows(); r++) { + if (base(r) != 12) continue; + waterMap[r] = water_y_range{HighLine(r) - 1, LowLine(r)}; } } -const Eigen::ArrayXi &HeightLine::getHighLine() const { return HighLine; } -const Eigen::ArrayXi &HeightLine::getLowLine() const { return LowLine; } - -const Eigen::ArrayXi &HeightLine::getBase() const { return base; } - -const std::map &HeightLine::getWaterMap() const { +const std::map &height_line::getWaterMap() const { return waterMap; } -EImage HeightLine::toImg() const { - const short rMax = maxHeight() - 1; +EImage height_line::toImg() const { + const int rMax = maxHeight() - 1; EImage img(maxHeight(), HighLine.size()); img.setConstant(AirColor); - short y = 0, r = rMax - y; - for (uint16_t x = 0; x < HighLine.size(); x++) { - y = HighLine(x); - r = rMax - y; + // short y = 0, r = rMax - y; + for (uint32_t x = 0; x < HighLine.size(); x++) { + const int y = HighLine(x); + int r = rMax - y; if (base(x)) { if (base(x) != 12) { img(r, x) = BlockColor; @@ -253,7 +251,7 @@ Base.setConstant(sizePic(0)+1,sizePic(1),11); { LowMap(TokiRow(it->first),TokiCol(it->first))= HighMap(TokiRow(it->first),TokiCol(it->first)) - -WaterColumnSize[rawShadow(TokiRow(it->first)-1,TokiCol(it->first))]+1; + -WATER_COLUMN_SIZE[rawShadow(TokiRow(it->first)-1,TokiCol(it->first))]+1; } cerr<<"LowMap updated"< #include #include +#include "optimize_chain.h" +#include "SCLDefines.h" +#include "water_item.h" -class HeightLine { -public: - HeightLine(); +class height_line { + public: + height_line(); float make(const TokiColor *[], const Eigen::Array &, bool allowNaturalCompress, Eigen::ArrayXi *dst = nullptr); void make(const Eigen::ArrayXi &mapColorCol, bool allowNaturalCompress); void updateWaterMap(); - uint16_t maxHeight() const; - const Eigen::ArrayXi &getHighLine() const; - const Eigen::ArrayXi &getLowLine() const; - const Eigen::ArrayXi &getBase() const; - const std::map &getWaterMap() const; + uint32_t maxHeight() const; + + const Eigen::ArrayXi &getHighLine() const noexcept { return HighLine; } + const Eigen::ArrayXi &getLowLine() const noexcept { return LowLine; } + + auto &getBase() const noexcept { return this->base; } + const std::map &getWaterMap() const; EImage toImg() const; static const ARGB BlockColor; @@ -51,11 +52,11 @@ class HeightLine { static const ARGB WaterColor; static const ARGB greyColor; -private: + private: Eigen::ArrayXi base; Eigen::ArrayXi HighLine; Eigen::ArrayXi LowLine; - std::map waterMap; + std::map waterMap; }; -#endif // HEIGHTLINE_H +#endif // HEIGHTLINE_H diff --git a/SlopeCraftL/imagePreprocess.cpp b/SlopeCraftL/image_preprocess.cpp similarity index 64% rename from SlopeCraftL/imagePreprocess.cpp rename to SlopeCraftL/image_preprocess.cpp index 329e9764..fbbf48c5 100644 --- a/SlopeCraftL/imagePreprocess.cpp +++ b/SlopeCraftL/image_preprocess.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -48,40 +48,54 @@ void SCL_EXPORT SCL_preprocessImage(ARGB *data, const uint64_t imageSize, ARGB backGround) { backGround |= 0xFF000000; - if (data == nullptr) - return; - if (imageSize <= 0) - return; + if (data == nullptr) return; + if (imageSize <= 0) return; for (uint64_t i = 0; i < imageSize; i++) { - if (getA(data[i]) == 0) { // pure transparent + if (getA(data[i]) == 0) { // pure transparent switch (pSt) { - case SCL_PureTpPixelSt::ReplaceWithBackGround: - data[i] = backGround; - break; - default: - break; + case SCL_PureTpPixelSt::ReplaceWithBackGround: + data[i] = backGround; + break; + default: + break; } + continue; } - if (getA(data[i]) < 255) { // half transparent + if (getA(data[i]) < 255) { // half transparent switch (hSt) { - case SCL_HalfTpPixelSt::ReplaceWithBackGround: - data[i] = backGround; - break; - case SCL_HalfTpPixelSt::ComposeWithBackGround: - data[i] = composeColor(data[i], backGround); - break; - default: - data[i] |= 0xFF000000; - break; + case SCL_HalfTpPixelSt::ReplaceWithBackGround: + data[i] = backGround; + break; + case SCL_HalfTpPixelSt::ComposeWithBackGround: + data[i] = composeColor(data[i], backGround); + break; + default: + data[i] |= 0xFF000000; + break; } } } } -unsigned char SCL_EXPORT SCL_maxAvailableVersion() { return 19; } +SCL_EXPORT bool SCL_haveTransparentPixel(const uint32_t *ARGB32, + const uint64_t imageSize) { + for (uint64_t i = 0; i < imageSize; i++) { + const uint32_t argb = ARGB32[i]; + + if (getA(argb) != 255) { + return true; + } + } + + return false; +} + +SCL_gameVersion SCL_EXPORT SCL_maxAvailableVersion() { + return SCL_gameVersion::MC21; +} #ifndef SCL_CAPI -} // namespace SlopeCraft +} // namespace SlopeCraft #endif diff --git a/SlopeCraftL/install.cmake b/SlopeCraftL/install.cmake index a52016ba..26c4dc21 100644 --- a/SlopeCraftL/install.cmake +++ b/SlopeCraftL/install.cmake @@ -10,77 +10,55 @@ set(SlopeCraft_SCL_Cpp_include_files ${CMAKE_SOURCE_DIR}/utilities/SC_GlobalEnums.h - SlopeCraftL.h - SlopeCraftL_global.h + ${CMAKE_CURRENT_SOURCE_DIR}/SlopeCraftL.h + ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraftL_export.h ) +install(FILES ${SlopeCraft_SCL_Cpp_include_files} + DESTINATION include/SlopeCraft) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") # install for applications install(TARGETS SlopeCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}) - - # install to lib dirs - install(TARGETS SlopeCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/bin - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib - - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib - ) + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . + LIBRARY DESTINATION .) # install(TARGETS SlopeCraftL_C - # RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/bin - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/lib + # RUNTIME DESTINATION ./../install_SlopeCraftL/C/bin + # LIBRARY DESTINATION ./../install_SlopeCraftL/C/lib - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/lib + # LIBRARY DESTINATION ./../install_SlopeCraftL/C/lib # ) - install(FILES ${SlopeCraft_SCL_Cpp_include_files} - DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/include) + + DLLD_add_deploy(SlopeCraftL + INSTALL_MODE INSTALL_DESTINATION .) # install(FILES ${SlopeCraft_SCL_C_include_files} - # DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/include) + # DESTINATION ./../install_SlopeCraftL/C/include) + # SlopeCraft_install_if_is_shared(ZLIB::ZLIB .) + # SlopeCraft_install_if_is_shared(PNG::PNG .) + return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") message(STATUS "Install SlopeCraft for linux") # install for applications install(TARGETS SlopeCraftL + EXPORT SlopeCraftTargets RUNTIME DESTINATION bin LIBRARY DESTINATION lib) - install(FILES ${SlopeCraft_SCL_Cpp_include_files} - DESTINATION include) return() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") - # install for applications - install(TARGETS SlopeCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}) +endif () - # install to lib dirs +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") install(TARGETS SlopeCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/bin - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib - - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib - ) - - # install(TARGETS SlopeCraftL_C - # RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/bin - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/lib - # - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/lib - # ) - install(FILES ${SlopeCraft_SCL_Cpp_include_files} - DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/include) - - # install(FILES ${SlopeCraft_SCL_C_include_files} - # DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/C/include) + EXPORT SlopeCraftTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib) return() -endif() +endif () # add_subdirectory(SCL_C) diff --git a/SlopeCraftL/lossyCompressor.cpp b/SlopeCraftL/lossy_compressor.cpp similarity index 73% rename from SlopeCraftL/lossyCompressor.cpp rename to SlopeCraftL/lossy_compressor.cpp index dce6feda..fa982968 100644 --- a/SlopeCraftL/lossyCompressor.cpp +++ b/SlopeCraftL/lossy_compressor.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "lossyCompressor.h" +#include "lossy_compressor.h" #define heu_NO_OUTPUT #define heu_USE_THREADS @@ -29,12 +29,12 @@ This file is part of SlopeCraft. const double initializeNonZeroRatio = 0.05; -const uint16_t popSize = 50; +constexpr uint16_t popSize = 50; uint16_t maxFailTimes = 30; -uint16_t LossyCompressor::maxGeneration = 600; -const double crossoverProb = 0.9; -const double mutateProb = 0.01; -const uint32_t reportRate = 50; +uint16_t lossy_compressor::maxGeneration = 600; +constexpr double crossoverProb = 0.9; +constexpr double mutateProb = 0.01; +constexpr uint32_t reportRate = 50; using Var_t = Eigen::ArrayX; @@ -46,7 +46,7 @@ struct args_t : public heu::FixedDiscreteBox { const TokiColor **src; bool allowNaturalCompress; size_t maxHeight; - const LossyCompressor *ptr; + const lossy_compressor *ptr; std::clock_t prevClock; }; @@ -64,7 +64,7 @@ void iFun(Var_t *v, const args_t *arg) { } void fFun(const Var_t *v, const args_t *arg, double *fitness) { - HeightLine HL; + height_line HL; const TokiColor **src = arg->src; const bool allowNaturalCompress = arg->allowNaturalCompress; float meanColorDiff = HL.make(src, *v, allowNaturalCompress); @@ -83,30 +83,29 @@ class solver_t heu::RecordOption::DONT_RECORD_FITNESS, heu::SelectMethod::Tournament, args_t, iFun, fFun, heu::GADefaults::cFunSwapXs, heu::GADefaults::mFun> { -public: + public: void customOptAfterEachGeneration() { if (this->generation() % reportRate == 0) { std::clock_t &prevClock = this->_args.prevClock; std::clock_t curClock = std::clock(); if (curClock - prevClock >= CLOCKS_PER_SEC / 2) { prevClock = curClock; - (*(this->_args.ptr->progressRangeSetPtr))( - *(this->_args.ptr->windPtr), 0, LossyCompressor::maxGeneration, - this->generation()); + this->_args.ptr->progress_bar.set_range( + 0, lossy_compressor::maxGeneration, this->generation()); } } } }; -LossyCompressor::LossyCompressor() { - solver = new solver_t; +lossy_compressor::lossy_compressor() : solver{new solver_t{}} { solver->setTournamentSize(3); } -LossyCompressor::~LossyCompressor() { delete solver; } +lossy_compressor::~lossy_compressor() {} -void LossyCompressor::setSource(const Eigen::ArrayXi &_base, - const TokiColor **src) { +void lossy_compressor::setSource(const Eigen::ArrayXi &_base, + std::span src) { + assert(_base.rows() == static_cast(src.size() + 1)); source.resize(_base.rows() - 1); for (uint16_t idx = 0; idx < _base.rows() - 1; idx++) { @@ -118,15 +117,18 @@ void LossyCompressor::setSource(const Eigen::ArrayXi &_base, // std::cerr<<"source set\n"; } -void LossyCompressor::runGenetic(uint16_t maxHeight, - bool allowNaturalCompress) { +void lossy_compressor::runGenetic(uint16_t maxHeight, + bool allowNaturalCompress) { { - static heu::GAOption opt; + heu::GAOption opt; opt.crossoverProb = crossoverProb; opt.maxFailTimes = maxFailTimes; opt.maxGenerations = maxGeneration; opt.mutateProb = mutateProb; opt.populationSize = popSize; + solver->setOption(opt); + } + { solver_t::ArgsType args; args.setDimensions(source.size()); args.src = source.data(); @@ -134,26 +136,23 @@ void LossyCompressor::runGenetic(uint16_t maxHeight, args.maxHeight = maxHeight; args.ptr = this; args.prevClock = std::clock(); - - solver->setOption(opt); solver->setArgs(args); - solver->initializePop(); } + solver->initializePop(); solver->run(); } -bool LossyCompressor::compress(uint16_t maxHeight, bool allowNaturalCompress) { - - (*progressRangeSetPtr)(*windPtr, 0, maxGeneration, 0); +bool lossy_compressor::compress(uint16_t maxHeight, bool allowNaturalCompress) { + this->progress_bar.set_range(0, maxGeneration, 0); // std::cerr<<"Genetic algorithm started\n"; uint16_t tryTimes = 0; maxFailTimes = 30; maxGeneration = 200; while (tryTimes < 3) { - runGenetic(maxHeight, allowNaturalCompress); - if (resultFitness() <= 0) { + this->runGenetic(maxHeight, allowNaturalCompress); + if (this->resultFitness() <= 0) { tryTimes++; maxFailTimes = -1; maxGeneration *= 2; @@ -163,8 +162,10 @@ bool LossyCompressor::compress(uint16_t maxHeight, bool allowNaturalCompress) { return tryTimes < 3; } -const Eigen::ArrayX &LossyCompressor::getResult() const { - return solver->result(); +const Eigen::ArrayX &lossy_compressor::getResult() const { + return this->solver->result(); } -double LossyCompressor::resultFitness() const { return solver->bestFitness(); } +double lossy_compressor::resultFitness() const { + return this->solver->bestFitness(); +} diff --git a/SlopeCraftL/lossyCompressor.h b/SlopeCraftL/lossy_compressor.h similarity index 71% rename from SlopeCraftL/lossyCompressor.h rename to SlopeCraftL/lossy_compressor.h index d7c43144..971e59e3 100644 --- a/SlopeCraftL/lossyCompressor.h +++ b/SlopeCraftL/lossy_compressor.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -30,32 +30,30 @@ This file is part of SlopeCraft. #include #include #include +#include -#include "Colors.h" -#include "HeightLine.h" +#include "height_line.h" #include "SCLDefines.h" -#include "WaterItem.h" +#include "water_item.h" // Eigen::Array class solver_t; -class LossyCompressor { -public: - LossyCompressor(); - ~LossyCompressor(); - void setSource(const Eigen::ArrayXi &, const TokiColor *[]); - bool compress(uint16_t maxHeight, bool allowNaturalCompress = false); +class lossy_compressor { + public: + lossy_compressor(); + ~lossy_compressor(); + void setSource(const Eigen::ArrayXi &, std::span); + bool compress(uint16_t maxHeight, bool allowNaturalCompress); const Eigen::ArrayX &getResult() const; double resultFitness() const; - void **windPtr; - void (**progressRangeSetPtr)(void *, int min, int max, int val); - void (**progressAddPtr)(void *, int); - void (**keepAwakePtr)(void *); + SlopeCraft::ui_callbacks ui; + SlopeCraft::progress_callbacks progress_bar; -private: + private: friend class solver_t; - solver_t *solver; + std::unique_ptr solver; std::vector source; static uint16_t maxGeneration; @@ -64,4 +62,4 @@ class LossyCompressor { }; double randD(); -#endif // LOSSYCOMPRESSOR_H +#endif // LOSSYCOMPRESSOR_H diff --git a/SlopeCraftL/mc_block.cpp b/SlopeCraftL/mc_block.cpp new file mode 100644 index 00000000..70ae5f30 --- /dev/null +++ b/SlopeCraftL/mc_block.cpp @@ -0,0 +1,118 @@ +/* + Copyright © 2021-2026 TokiNoBug +This file is part of SlopeCraft. + + SlopeCraft is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SlopeCraft is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SlopeCraft. If not, see . + + Contact with me: + github:https://github.com/SlopeCraft/SlopeCraft + bilibili:https://space.bilibili.com/351429231 +*/ + +#include "mc_block.h" +#include "blocklist.h" + +#include "process_block_id.h" + +mc_block::mc_block() { + // id = ""; + // version = 0; + // idOld = ""; + // needGlass = false; + // doGlow = false; + // endermanPickable = false; + // burnable = false; + + image.resize(16, 16); + // wallUseable=true; +} + +std::optional mc_block::detail_info( + SCL_gameVersion current_version) const { + using namespace blkid; + char_range namespace_name; + char_range pure_id; + const bool ok = blkid::process_blk_id(this->idForVersion(current_version), + &namespace_name, &pure_id, nullptr); + if (not ok) { + return std::nullopt; + } + return block_detail_info{.id_namespace{namespace_name}, .pure_id{pure_id}}; +} + +void mc_block_interface::clear() noexcept { + setBurnable(false); + setDoGlow(false); + setEndermanPickable(false); + setId("minecraft:air"); + setIdOld(""); + setNeedGlass(false); + setVersion(0); + setNameZH(""); + setNameEN(""); + for (auto ver = static_cast(SCL_gameVersion::MC12); + ver <= static_cast(SCL_maxAvailableVersion()); ver++) { + this->setNeedStone(static_cast(ver), false); + } + // setWallUseable(false); +} + +size_t block_list::get_blocks(mc_block_interface **dst, uint8_t *dst_basecolor, + size_t capacity_in_elements) noexcept { + if (capacity_in_elements <= 0) { + return 0; + } + if (dst == nullptr and dst_basecolor == nullptr) { + return 0; + } + + size_t counter = 0; + for (auto &ptr : this->m_blocks) { + if (dst) dst[counter] = ptr.first.get(); + + if (dst_basecolor) dst_basecolor[counter] = ptr.second; + + counter++; + if (counter >= capacity_in_elements) { + return counter; + } + } + return counter; +} + +size_t block_list::get_blocks(const mc_block_interface **dst, + uint8_t *dst_basecolor, + size_t capacity_in_elements) const noexcept { + if (capacity_in_elements <= 0) { + return 0; + } + if (dst == nullptr and dst_basecolor == nullptr) { + return 0; + } + + size_t counter = 0; + for (auto &ptr : this->m_blocks) { + if (dst) dst[counter] = ptr.first.get(); + if (dst_basecolor) dst_basecolor[counter] = ptr.second; + counter++; + if (counter >= capacity_in_elements) { + return counter; + } + } + return counter; +} + +void block_list::clear() noexcept { this->m_blocks.clear(); } + +block_list::~block_list() { this->clear(); } \ No newline at end of file diff --git a/SlopeCraftL/simpleBlock.h b/SlopeCraftL/mc_block.h similarity index 54% rename from SlopeCraftL/simpleBlock.h rename to SlopeCraftL/mc_block.h index 8d4e104f..e5ea09cb 100644 --- a/SlopeCraftL/simpleBlock.h +++ b/SlopeCraftL/mc_block.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,33 +23,61 @@ This file is part of SlopeCraft. #ifndef SIMPLEBLOCK_H #define SIMPLEBLOCK_H -#include "SCLDefines.h" -using namespace SlopeCraft; #include +#include #include #include #include #include +#include -typedef unsigned char uchar; +#include +#include "SCLDefines.h" +using namespace SlopeCraft; typedef std::vector stringList; -class simpleBlock : public ::SlopeCraft::AbstractBlock { +struct block_detail_info { + std::string_view id_namespace; + std::string_view pure_id; + + [[nodiscard]] inline bool operator==( + const block_detail_info &b) const noexcept { + return (this->id_namespace == b.id_namespace) and + (this->pure_id == b.pure_id); + } +}; + +template <> +struct std::hash { + [[nodiscard]] static inline size_t operator()( + const block_detail_info &info) noexcept { + size_t hash = std::hash{}(info.id_namespace); + hash ^= std::hash{}(info.pure_id); + return hash; + } +}; + +class mc_block : public ::SlopeCraft::mc_block_interface { public: - simpleBlock(); - ~simpleBlock(){}; - std::string id; - uchar version; - std::string idOld; - std::string nameZH; - std::string nameEN; - std::string imageFilename; - Eigen::ArrayXX image; + mc_block(); + ~mc_block(){}; + std::string id{}; + std::string idOld{}; + std::string nameZH{}; + std::string nameEN{}; + std::string imageFilename{}; + Eigen::Array image; + version_set needStone{0}; + uint8_t version{0}; bool needGlass{false}; bool doGlow{false}; bool endermanPickable{false}; bool burnable{false}; + uint8_t stackSize{64}; + + [[nodiscard]] std::optional detail_info( + SCL_gameVersion current_version) const; const char *getId() const noexcept override { return id.data(); }; uint8_t getVersion() const noexcept override { return version; }; @@ -60,6 +88,7 @@ class simpleBlock : public ::SlopeCraft::AbstractBlock { return endermanPickable; }; bool getBurnable() const noexcept override { return burnable; }; + uint8_t getStackSize() const noexcept override { return this->stackSize; } const char *getNameZH() const noexcept override { return this->nameZH.c_str(); } @@ -71,13 +100,12 @@ class simpleBlock : public ::SlopeCraft::AbstractBlock { return this->imageFilename.c_str(); } - void getImage(uint32_t *dest, bool is_row_major) const noexcept override { - if (is_row_major) { - Eigen::Map> map(dest, 16, 16); - map = this->image.transpose(); - } else { - memcpy(dest, this->image.data(), this->image.size() * sizeof(uint32_t)); - } + void getImage(uint32_t *dest_row_major) const noexcept override { + memcpy(dest_row_major, this->image.data(), + this->image.size() * sizeof(uint32_t)); + } + bool getNeedStone(SCL_gameVersion v) const noexcept override { + return this->needStone[v]; } void setId(const char *_id) noexcept override { id = _id; }; @@ -91,9 +119,15 @@ class simpleBlock : public ::SlopeCraft::AbstractBlock { endermanPickable = _enderman; }; void setBurnable(bool _burn) noexcept override { burnable = _burn; }; + void setStackSize(uint8_t stack_size) noexcept override { + this->stackSize = std::max(stack_size, 1); + } + void setNeedStone(SCL_gameVersion v, bool ns) noexcept override { + this->needStone[v] = ns; + } - void setNameZH(const char *__nzh) noexcept override { this->nameZH = __nzh; } - void setNameEN(const char *__nen) noexcept override { this->nameEN = __nen; } + void setNameZH(const char *nzh) noexcept override { this->nameZH = nzh; } + void setNameEN(const char *nzh) noexcept override { this->nameEN = nzh; } void setImage(const uint32_t *src, bool is_row_major) noexcept override { if (is_row_major) { Eigen::Map> map(src, 16, 16); @@ -108,41 +142,25 @@ class simpleBlock : public ::SlopeCraft::AbstractBlock { this->imageFilename = _ifn; } - void copyTo(AbstractBlock *dst) const noexcept override { - *static_cast(dst) = *this; + void copyTo(mc_block_interface *dst) const noexcept override { + *static_cast(dst) = *this; } - static bool dealBlockId(const std::string &id, std::string &netBlockId, - stringList *proName, stringList *proVal); - // simpleBlock& operator =(const simpleBlock &); -}; - -class BlockList : public ::SlopeCraft::BlockListInterface { - private: - std::map m_blocks; - - public: - BlockList() = default; - ~BlockList(); - - public: - size_t size() const noexcept override { return m_blocks.size(); } - size_t get_blocks(AbstractBlock **dst, uint8_t *, - size_t capacity_in_elements) noexcept override; + // static bool processBlockId(std::string_view id, std::string &netBlockId, + // std::vector &proName, + // std::vector &proVal); - size_t get_blocks(const AbstractBlock **dst, uint8_t *, - size_t capacity_in_elements) const noexcept override; - - bool contains(const AbstractBlock *cp) const noexcept override { - return this->m_blocks.contains( - static_cast(const_cast(cp))); - } - - public: - const auto &blocks() const noexcept { return this->m_blocks; } - auto &blocks() noexcept { return this->m_blocks; } + const char *idForVersion(SCL_gameVersion ver) const noexcept override { + if (ver >= SCL_gameVersion::MC13) { + return this->getId(); + } - void clear() noexcept; + if (this->idOld.empty()) { + return this->getId(); + } + return this->getIdOld(); + }; + // mc_block& operator =(const mc_block &); }; #endif // SIMPLEBLOCK_H diff --git a/SlopeCraftL/object_pool.hpp b/SlopeCraftL/object_pool.hpp deleted file mode 100644 index abee2d99..00000000 --- a/SlopeCraftL/object_pool.hpp +++ /dev/null @@ -1,778 +0,0 @@ -// 2020/03/13 - modified by Tsung-Wei Huang -// - fixed bug in aligning memory -// -// 2020/02/02 - modified by Tsung-Wei Huang -// - new implementation motivated by Hoard -// -// 2019/07/10 - modified by Tsung-Wei Huang -// - replace raw pointer with smart pointer -// -// 2019/06/13 - created by Tsung-Wei Huang -// - implemented an object pool class - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace tf { - -#define TF_ENABLE_POOLABLE_ON_THIS \ - template friend class ObjectPool; \ - void* _object_pool_block - -// Class: ObjectPool -// -// The class implements an efficient thread-safe object pool motivated -// by the Hoard memory allocator algorithm. -// Different from the normal memory allocator, object pool allocates -// only one object at a time. -// -// Internall, we use the following variables to maintain blocks and heaps: -// X: size in byte of a item slot -// M: number of items per block -// F: emptiness threshold -// B: number of bins per local heap (bin[B-1] is the full list) -// W: number of items per bin -// K: shrinkness constant -// -// Example scenario 1: -// M = 30 -// F = 4 -// W = (30+4-1)/4 = 8 -// -// b0: 0, 1, 2, 3, 4, 5, 6, 7 -// b1: 8, 9, 10, 11, 12, 13, 14, 15 -// b2: 16, 17, 18, 19, 20, 21, 22, 23 -// b3: 24, 25, 26, 27, 28, 29 -// b4: 30 (anything equal to M) -// -// Example scenario 2: -// M = 32 -// F = 4 -// W = (32+4-1)/4 = 8 -// b0: 0, 1, 2, 3, 4, 5, 6, 7 -// b1: 8, 9, 10, 11, 12, 13, 14, 15 -// b2: 16, 17, 18, 19, 20, 21, 22, 23 -// b3: 24, 25, 26, 27, 28, 29, 30, 31 -// b4: 32 (anything equal to M) -// -template -class ObjectPool { - - // the data column must be sufficient to hold the pointer in freelist - constexpr static size_t X = (std::max)(sizeof(T*), sizeof(T)); - //constexpr static size_t X = sizeof(long double) + std::max(sizeof(T*), sizeof(T)); - //constexpr static size_t M = (S - offsetof(Block, data)) / X; - constexpr static size_t M = S / X; - constexpr static size_t F = 4; - constexpr static size_t B = F + 1; - constexpr static size_t W = (M + F - 1) / F; - constexpr static size_t K = 4; - - static_assert( - S && (!(S & (S-1))), "block size S must be a power of two" - ); - - static_assert( - M >= 128, "block size S must be larger enough to pool at least 128 objects" - ); - - struct Blocklist { - Blocklist* prev; - Blocklist* next; - }; - - struct GlobalHeap { - std::mutex mutex; - Blocklist list; - }; - - struct LocalHeap { - std::mutex mutex; - Blocklist lists[B]; - size_t u {0}; - size_t a {0}; - }; - - struct Block { - std::atomic heap; - Blocklist list_node; - size_t i; - size_t u; - T* top; - // long double padding; - char data[S]; - }; - - public: - - /** - @brief constructs an object pool from a number of anticipated threads - */ - explicit ObjectPool(unsigned = std::thread::hardware_concurrency()); - - /** - @brief destructs the object pool - */ - ~ObjectPool(); - - /** - @brief acquires a pointer to a object constructed from a given argument list - */ - template - T* animate(ArgsT&&... args); - - /** - @brief recycles a object pointed by @c ptr and destroys it - */ - void recycle(T* ptr); - - size_t num_bins_per_local_heap() const; - size_t num_objects_per_bin() const; - size_t num_objects_per_block() const; - size_t num_available_objects() const; - size_t num_allocated_objects() const; - size_t capacity() const; - size_t num_local_heaps() const; - size_t num_global_heaps() const; - size_t num_heaps() const; - - float emptiness_threshold() const; - - private: - - const size_t _lheap_mask; - - GlobalHeap _gheap; - - std::vector _lheaps; - - LocalHeap& _this_heap(); - - constexpr unsigned _next_pow2(unsigned n) const; - - template - constexpr size_t _offset_in_class(const Q P::*member) const; - - template - constexpr P* _parent_class_of(Q*, const Q P::*member); - - template - constexpr P* _parent_class_of(const Q*, const Q P::*member) const; - - constexpr Block* _block_of(Blocklist*); - constexpr Block* _block_of(const Blocklist*) const; - - size_t _bin(size_t) const; - - T* _allocate(Block*); - - void _deallocate(Block*, T*); - void _blocklist_init_head(Blocklist*); - void _blocklist_add_impl(Blocklist*, Blocklist*, Blocklist*); - void _blocklist_push_front(Blocklist*, Blocklist*); - void _blocklist_push_back(Blocklist*, Blocklist*); - void _blocklist_del_impl(Blocklist*, Blocklist*); - void _blocklist_del(Blocklist*); - void _blocklist_replace(Blocklist*, Blocklist*); - void _blocklist_move_front(Blocklist*, Blocklist*); - void _blocklist_move_back(Blocklist*, Blocklist*); - bool _blocklist_is_first(const Blocklist*, const Blocklist*); - bool _blocklist_is_last(const Blocklist*, const Blocklist*); - bool _blocklist_is_empty(const Blocklist*); - bool _blocklist_is_singular(const Blocklist*); - - template - void _for_each_block_safe(Blocklist*, C&&); - - template - void _for_each_block(Blocklist*, C&&); - -}; - -// ---------------------------------------------------------------------------- -// ObjectPool definition -// ---------------------------------------------------------------------------- - -// Constructor -template -ObjectPool::ObjectPool(unsigned t) : - //_heap_mask {(_next_pow2(t) << 1) - 1u}, - //_heap_mask { _next_pow2(t<<1) - 1u }, - //_heap_mask {(t << 1) - 1}, - _lheap_mask { _next_pow2((t+1) << 1) - 1 }, - _lheaps { _lheap_mask + 1 } { - - _blocklist_init_head(&_gheap.list); - - for(auto& h : _lheaps) { - for(size_t i=0; i -ObjectPool::~ObjectPool() { - - // clear local heaps - for(auto& h : _lheaps) { - for(size_t i=0; i -size_t ObjectPool::num_bins_per_local_heap() const { - return B; -} - -// Function: num_objects_per_bin -template -size_t ObjectPool::num_objects_per_bin() const { - return W; -} - -// Function: num_objects_per_block -template -size_t ObjectPool::num_objects_per_block() const { - return M; -} - -// Function: emptiness_threshold -template -float ObjectPool::emptiness_threshold() const { - return 1.0f/F; -} - -// Function: num_global_heaps -template -size_t ObjectPool::num_global_heaps() const { - return 1; -} - -// Function: num_lheaps -template -size_t ObjectPool::num_local_heaps() const { - return _lheaps.size(); -} - -// Function: num_heaps -template -size_t ObjectPool::num_heaps() const { - return _lheaps.size() + 1; -} - -// Function: capacity -template -size_t ObjectPool::capacity() const { - - size_t n = 0; - - // global heap - for(auto p=_gheap.list.next; p!=&_gheap.list; p=p->next) { - n += M; - }; - - // local heap - for(auto& h : _lheaps) { - n += h.a; - } - - return n; -} - -// Function: num_available_objects -template -size_t ObjectPool::num_available_objects() const { - - size_t n = 0; - - // global heap - for(auto p=_gheap.list.next; p!=&_gheap.list; p=p->next) { - n += (M - _block_of(p)->u); - }; - - // local heap - for(auto& h : _lheaps) { - n += (h.a - h.u); - } - return n; -} - -// Function: num_allocated_objects -template -size_t ObjectPool::num_allocated_objects() const { - - size_t n = 0; - - // global heap - for(auto p=_gheap.list.next; p!=&_gheap.list; p=p->next) { - n += _block_of(p)->u; - }; - - // local heap - for(auto& h : _lheaps) { - n += h.u; - } - return n; -} - -// Function: _bin -template -size_t ObjectPool::_bin(size_t u) const { - return u == M ? F : u/W; -} - -// Function: _offset_in_class -template -template -constexpr size_t ObjectPool::_offset_in_class( - const Q P::*member) const { - return (size_t) &( reinterpret_cast(0)->*member); -} - -// C macro: parent_class_of(list_pointer, Block, list) -// C++: parent_class_of(list_pointer, &Block::list) -template -template -constexpr P* ObjectPool::_parent_class_of( - Q* ptr, const Q P::*member -) { - return (P*)( (char*)ptr - _offset_in_class(member)); -} - -// Function: _parent_class_of -template -template -constexpr P* ObjectPool::_parent_class_of( - const Q* ptr, const Q P::*member -) const { - return (P*)( (char*)ptr - _offset_in_class(member)); -} - -// Function: _block_of -template -constexpr typename ObjectPool::Block* -ObjectPool::_block_of(Blocklist* list) { - return _parent_class_of(list, &Block::list_node); -} - -// Function: _block_of -template -constexpr typename ObjectPool::Block* -ObjectPool::_block_of(const Blocklist* list) const { - return _parent_class_of(list, &Block::list_node); -} - -// Procedure: initialize a list head -template -void ObjectPool::_blocklist_init_head(Blocklist *list) { - list->next = list; - list->prev = list; -} - -// Procedure: _blocklist_add_impl -// Insert a new entry between two known consecutive entries. -// -// This is only for internal list manipulation where we know -// the prev/next entries already! -template -void ObjectPool::_blocklist_add_impl( - Blocklist *curr, Blocklist *prev, Blocklist *next -) { - next->prev = curr; - curr->next = next; - curr->prev = prev; - prev->next = curr; -} - -// list_push_front - add a new entry -// @curr: curr entry to be added -// @head: list head to add it after -// -// Insert a new entry after the specified head. -// This is good for implementing stacks. -// -template -void ObjectPool::_blocklist_push_front( - Blocklist *curr, Blocklist *head -) { - _blocklist_add_impl(curr, head, head->next); -} - -// list_add_tail - add a new entry -// @curr: curr entry to be added -// @head: list head to add it before -// -// Insert a new entry before the specified head. -// This is useful for implementing queues. -// -template -void ObjectPool::_blocklist_push_back( - Blocklist *curr, Blocklist *head -) { - _blocklist_add_impl(curr, head->prev, head); -} - -// Delete a list entry by making the prev/next entries -// point to each other. -// -// This is only for internal list manipulation where we know -// the prev/next entries already! -// -template -void ObjectPool::_blocklist_del_impl( - Blocklist * prev, Blocklist * next -) { - next->prev = prev; - prev->next = next; -} - -// _blocklist_del - deletes entry from list. -// @entry: the element to delete from the list. -// Note: list_empty() on entry does not return true after this, the entry is -// in an undefined state. -template -void ObjectPool::_blocklist_del(Blocklist *entry) { - _blocklist_del_impl(entry->prev, entry->next); - entry->next = nullptr; - entry->prev = nullptr; -} - -// list_replace - replace old entry by new one -// @old : the element to be replaced -// @curr : the new element to insert -// -// If @old was empty, it will be overwritten. -template -void ObjectPool::_blocklist_replace( - Blocklist *old, Blocklist *curr -) { - curr->next = old->next; - curr->next->prev = curr; - curr->prev = old->prev; - curr->prev->next = curr; -} - -// list_move - delete from one list and add as another's head -// @list: the entry to move -// @head: the head that will precede our entry -template -void ObjectPool::_blocklist_move_front( - Blocklist *list, Blocklist *head -) { - _blocklist_del_impl(list->prev, list->next); - _blocklist_push_front(list, head); -} - -// list_move_tail - delete from one list and add as another's tail -// @list: the entry to move -// @head: the head that will follow our entry -template -void ObjectPool::_blocklist_move_back( - Blocklist *list, Blocklist *head -) { - _blocklist_del_impl(list->prev, list->next); - _blocklist_push_back(list, head); -} - -// list_is_first - tests whether @list is the last entry in list @head -// @list: the entry to test -// @head: the head of the list -template -bool ObjectPool::_blocklist_is_first( - const Blocklist *list, const Blocklist *head -) { - return list->prev == head; -} - -// list_is_last - tests whether @list is the last entry in list @head -// @list: the entry to test -// @head: the head of the list -template -bool ObjectPool::_blocklist_is_last( - const Blocklist *list, const Blocklist *head -) { - return list->next == head; -} - -// list_empty - tests whether a list is empty -// @head: the list to test. -template -bool ObjectPool::_blocklist_is_empty(const Blocklist *head) { - return head->next == head; -} - -// list_is_singular - tests whether a list has just one entry. -// @head: the list to test. -template -bool ObjectPool::_blocklist_is_singular( - const Blocklist *head -) { - return !_blocklist_is_empty(head) && (head->next == head->prev); -} - -// Procedure: _for_each_block -template -template -void ObjectPool::_for_each_block(Blocklist* head, C&& c) { - Blocklist* p; - for(p=head->next; p!=head; p=p->next) { - c(_block_of(p)); - } -} - -// Procedure: _for_each_block_safe -// Iterate each item of a list - safe to free -template -template -void ObjectPool::_for_each_block_safe(Blocklist* head, C&& c) { - Blocklist* p; - Blocklist* t; - for(p=head->next, t=p->next; p!=head; p=t, t=p->next) { - c(_block_of(p)); - } -} - -// Function: _allocate -// allocate a spot from the block -template -T* ObjectPool::_allocate(Block* s) { - if(s->top == nullptr) { - return reinterpret_cast(s->data + s->i++ * X); - } - else { - T* retval = s->top; - s->top = *(reinterpret_cast(s->top)); - return retval; - } -} - -// Procedure: _deallocate -template -void ObjectPool::_deallocate(Block* s, T* ptr) { - *(reinterpret_cast(ptr)) = s->top; - s->top = ptr; -} - -// Function: allocate -template -template -T* ObjectPool::animate(ArgsT&&... args) { - - //std::cout << "construct a new item\n"; - - // my logically mapped heap - LocalHeap& h = _this_heap(); - - Block* s {nullptr}; - - h.mutex.lock(); - - // scan the list of superblocks from most full to least - int f = static_cast(F-1); - for(; f>=0; f--) { - if(!_blocklist_is_empty(&h.lists[f])) { - s = _block_of(h.lists[f].next); - break; - } - } - - // no superblock found - if(f == -1) { - - // check heap 0 for a superblock - _gheap.mutex.lock(); - if(!_blocklist_is_empty(&_gheap.list)) { - - s = _block_of(_gheap.list.next); - - //printf("get a superblock from global heap %lu\n", s->u); - assert(s->u < M && s->heap == nullptr); - f = static_cast(_bin(s->u + 1)); - - _blocklist_move_front(&s->list_node, &h.lists[f]); - - s->heap = &h; // must be within the global heap lock - _gheap.mutex.unlock(); - - h.u = h.u + s->u; - h.a = h.a + M; - } - // create a new block - else { - //printf("create a new superblock\n"); - _gheap.mutex.unlock(); - f = 0; - //s = static_cast(std::malloc(sizeof(Block))); - s = new Block(); - - if(s == nullptr) { - throw std::bad_alloc(); - } - - s->heap = &h; - s->i = 0; - s->u = 0; - s->top = nullptr; - - _blocklist_push_front(&s->list_node, &h.lists[f]); - - h.a = h.a + M; - } - } - - // the superblock must have at least one space - //assert(s->u < M); - //printf("%lu %lu %lu\n", h.u, h.a, s->u); - //assert(h.u < h.a); - - h.u = h.u + 1; - s->u = s->u + 1; - - // take one item from the superblock - T* mem = _allocate(s); - - int b = static_cast(_bin(s->u)); - - if(b != f) { - //printf("move superblock from list[%d] to list[%d]\n", f, b); - _blocklist_move_front(&s->list_node, &h.lists[b]); - } - - //std::cout << "s.i " << s->i << '\n' - // << "s.u " << s->u << '\n' - // << "h.u " << h.u << '\n' - // << "h.a " << h.a << '\n'; - - h.mutex.unlock(); - - //printf("allocate %p (s=%p)\n", mem, s); - - new (mem) T(std::forward(args)...); - - mem->_object_pool_block = s; - - return mem; -} - -// Function: destruct -template -void ObjectPool::recycle(T* mem) { - - //Block* s = *reinterpret_cast( - // reinterpret_cast(mem) - sizeof(Block**) - //); - - //Block* s= *(reinterpret_cast(mem) - O); // (mem) - 1 - - Block* s = static_cast(mem->_object_pool_block); - - mem->~T(); - - //printf("deallocate %p (s=%p) M=%lu W=%lu X=%lu\n", mem, s, M, W, X); - - // here we need a loop because when we lock the heap, - // other threads may have removed the superblock to another heap - bool sync = false; - - do { - LocalHeap* h = s->heap.load(std::memory_order_relaxed); - - // the block is in global heap - if(h == nullptr) { - std::lock_guard glock(_gheap.mutex); - if(s->heap == h) { - sync = true; - _deallocate(s, mem); - s->u = s->u - 1; - } - } - else { - std::lock_guard llock(h->mutex); - if(s->heap == h) { - sync = true; - // deallocate the item from the superblock - size_t f = _bin(s->u); - _deallocate(s, mem); - s->u = s->u - 1; - h->u = h->u - 1; - - size_t b = _bin(s->u); - - if(b != f) { - //printf("move superblock from list[%d] to list[%d]\n", f, b); - _blocklist_move_front(&s->list_node, &h->lists[b]); - } - - // transfer a mostly-empty superblock to global heap - if((h->u + K*M < h->a) && (h->u < ((F-1) * h->a / F))) { - for(size_t i=0; ilists[i])) { - Block* x = _block_of(h->lists[i].next); - //printf("transfer a block (x.u=%lu/x.i=%lu) to the global heap\n", x->u, x->i); - assert(h->u > x->u && h->a > M); - h->u = h->u - x->u; - h->a = h->a - M; - x->heap = nullptr; - std::lock_guard glock(_gheap.mutex); - _blocklist_move_front(&x->list_node, &_gheap.list); - break; - } - } - } - } - } - } while(!sync); - - //std::cout << "s.i " << s->i << '\n' - // << "s.u " << s->u << '\n'; -} - -// Function: _this_heap -template -typename ObjectPool::LocalHeap& -ObjectPool::_this_heap() { - // here we don't use thread local since object pool might be - // created and destroyed multiple times - //thread_local auto hv = std::hash()(std::this_thread::get_id()); - //return _lheaps[hv & _lheap_mask]; - - return _lheaps[ - std::hash()(std::this_thread::get_id()) & _lheap_mask - ]; -} - -// Function: _next_pow2 -template -constexpr unsigned ObjectPool::_next_pow2(unsigned n) const { - if(n == 0) return 1; - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; -} - -} // end namespace tf -------------------------------------------------------- diff --git a/SlopeCraftL/OptiChain.cpp b/SlopeCraftL/optimize_chain.cpp similarity index 54% rename from SlopeCraftL/OptiChain.cpp rename to SlopeCraftL/optimize_chain.cpp index 9087a5c0..a9630871 100644 --- a/SlopeCraftL/OptiChain.cpp +++ b/SlopeCraftL/optimize_chain.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,111 +20,99 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "OptiChain.h" +#include "optimize_chain.h" #define NInf -100000 #define MapSize (Base.rows()) -// ArrayXXi OptiChain::Base=MatrixXi::Zero(0,0); -const Eigen::Array3i OptiChain::Both(-1, 2, -1); -const Eigen::Array3i OptiChain::Left(-1, 1, 0); -const Eigen::Array3i OptiChain::Right(0, 1, -1); -// QLabel* OptiChain::SinkIDP=nullptr; +// ArrayXXi optimize_chain::Base=MatrixXi::Zero(0,0); +const Eigen::Array3i optimize_chain::Both(-1, 2, -1); +const Eigen::Array3i optimize_chain::Left(-1, 1, 0); +const Eigen::Array3i optimize_chain::Right(0, 1, -1); +// QLabel* optimize_chain::SinkIDP=nullptr; #ifdef showImg -QLabel *OptiChain::SinkAll = nullptr; -bool OptiChain::AllowSinkHang = false; +QLabel *optimize_chain::SinkAll = nullptr; +bool optimize_chain::AllowSinkHang = false; #else #define AllowSinkHang true #endif -Region::Region(short _Beg, short _End, RegionType _Type) { - Beg = _Beg; - End = _End; - type = _Type; -} -inline bool Region::isHang() const { return (type == Hang); } +inline bool region::isHang() const { return (type == region_type::hanging); } -inline bool Region::isIDP() const { return (type == idp); } +inline bool region::isIDP() const { return (type == region_type::independent); } -inline bool Region::isValid() const { - return (type != Invalid) && (size() >= 1); -} +inline bool region::isValid() const { return (size() >= 1); } -inline int Region::size() const { return (End - Beg + 1); } +inline int region::size() const { return (end - begin + 1); } -inline short Region::indexLocal2Global(short indexLocal) const { - return indexLocal + Beg; +inline int region::indexLocal2Global(int indexLocal) const { + return indexLocal + begin; } -inline short Region::indexGlobal2Local(short indexGlobal) const { - return indexGlobal - Beg; +inline int region::indexGlobal2Local(int indexGlobal) const { + return indexGlobal - begin; } -std::string Region::toString() const { +std::string region::toString() const { if (!isValid()) - return '{' + std::to_string(Beg) + ',' + std::to_string(End) + - '}'; // 无效区间用大括号 + return '{' + std::to_string(begin) + ',' + std::to_string(end) + + '}'; // 无效区间用大括号 if (isHang()) - return '[' + std::to_string(Beg) + ',' + std::to_string(End) + - ']'; // 悬空区间用中括号 + return '[' + std::to_string(begin) + ',' + std::to_string(end) + + ']'; // 悬空区间用中括号 - return '(' + std::to_string(Beg) + ',' + std::to_string(End) + - ')'; // 可沉降区间用括号 + return '(' + std::to_string(begin) + ',' + std::to_string(end) + + ')'; // 可沉降区间用括号 } -OptiChain::OptiChain(int size) { +optimize_chain::optimize_chain(int size) { SubChain.clear(); - if (size < 0) - return; + if (size < 0) return; } -OptiChain::OptiChain(const Eigen::ArrayXi &base, const Eigen::ArrayXi &High, - const Eigen::ArrayXi &Low) { +optimize_chain::optimize_chain(const Eigen::ArrayXi &base, + const Eigen::ArrayXi &High, + const Eigen::ArrayXi &Low) { Base = base; HighLine = High; LowLine = Low; SubChain.clear(); } /* -OptiChain::OptiChain(const HeightLine& src) { +optimize_chain::optimize_chain(const height_line& src) { HighLine=src.HighLine; LowLine=src.LowLine; Base=src.base; SubChain.clear(); }*/ -OptiChain::~OptiChain() { return; } +optimize_chain::~optimize_chain() { return; } -const Eigen::ArrayXi &OptiChain::getHighLine() { return HighLine; } -const Eigen::ArrayXi &OptiChain::getLowLine() { return LowLine; } +const Eigen::ArrayXi &optimize_chain::high_line() { return HighLine; } +const Eigen::ArrayXi &optimize_chain::low_line() { return LowLine; } -int OptiChain::validHeight(int index) const { - if (index < 0 || index >= MapSize) - return NInf; - if (isAir(index)) - return NInf; +int optimize_chain::valid_height(int index) const { + if (index < 0 || index >= MapSize) return NInf; + if (is_air(index)) return NInf; return HighLine(index); } -inline bool OptiChain::isAir(int index) const { - if (index < 0 || index >= MapSize) - return true; +inline bool optimize_chain::is_air(int index) const { + if (index < 0 || index >= MapSize) return true; return (Base(index) == 0); } -inline bool OptiChain::isWater(int index) const { - if (index < 0 || index >= MapSize) - return false; +inline bool optimize_chain::is_water(int index) const { + if (index < 0 || index >= MapSize) return false; return (Base(index) == 12); } -inline bool OptiChain::isSolidBlock(int index) const { - if (index < 0 || index >= MapSize) - return false; +inline bool optimize_chain::is_solid_block(int index) const { + if (index < 0 || index >= MapSize) return false; return (Base(index) != 0 && Base(index) != 12); } -void OptiChain::dispSubChain() const { +void optimize_chain::dispSubChain() const { std::string out = ""; for (auto it = SubChain.cbegin(); it != SubChain.cend(); it++) { out += it->toString(); @@ -136,7 +124,7 @@ void OptiChain::dispSubChain() const { } /* -void HeightLine::segment2Brackets(list&List,short sBeg,short sEnd) +void height_line::segment2Brackets(list&List,short sBeg,short sEnd) { if(sEnd&List,short sBeg,short sEnd) return; } - queuePure; - queue disPure;//极大值区间 - Region Temp; + queuePure; + queue disPure;//极大值区间 + region Temp; bool isReady=false; VectorXi VHL=ValidHighLine(); VectorXi ScanBoth=VHL,ScanLeft=VHL,ScanRight=VHL; @@ -209,7 +197,7 @@ ScanRight=(ScanRight.array()>=0).select(ScanRight,0); #endif } -inline void HeightLine::DealRegion(Region PR, list &List) +inline void height_line::DealRegion(region PR, list &List) { if(PR.Begin<0||PR.End &List) } */ -void OptiChain::divideAndCompress() { +void optimize_chain::divide_and_compress() { divideToChain(); while (!Chain.empty()) { - divideToSubChain(); + divide_into_subchain(); for (auto it = SubChain.begin(); it != SubChain.end(); it++) - if (it->isIDP()) - Sink(*it); + if (it->isIDP()) sink(*it); for (auto it = SubChain.begin(); it != SubChain.end(); it++) - if (AllowSinkHang && it->isHang()) - Sink(*it); + if (AllowSinkHang && it->isHang()) sink(*it); } #ifdef showImg @@ -238,54 +224,53 @@ void OptiChain::divideAndCompress() { #endif } -void OptiChain::divideToChain() { - while (!Chain.empty()) - Chain.pop(); - Region Temp(0, MapSize - 1, idp); +void optimize_chain::divideToChain() { + while (!Chain.empty()) Chain.pop(); + region Temp(0, MapSize - 1, region_type::independent); for (int i = 1; i < MapSize; i++) { - if (isAir(i)) { - Temp.End = i - 1; + if (is_air(i)) { + Temp.end = i - 1; Chain.push(Temp); - Temp.Beg = i; + Temp.begin = i; } - if (isWater(i)) { - Temp.End = i - 1; + if (is_water(i)) { + Temp.end = i - 1; Chain.push(Temp); - Temp.Beg = i; + Temp.begin = i; } - if (isSolidBlock(i) && isAir(i)) { - Temp.End = i - 1; + if (is_solid_block(i) && is_air(i)) { + Temp.end = i - 1; Chain.push(Temp); - Temp.Beg = i; + Temp.begin = i; } } - Temp.End = MapSize - 1; + Temp.end = MapSize - 1; Chain.push(Temp); #ifdef sendInfo - std::cout << "Divided coloum " - << " into " << Chain.size() << "isolated region(s)" << std::endl; + std::cout << "Divided coloum " << " into " << Chain.size() + << "isolated region(s)" << std::endl; #endif } -void OptiChain::divideToSubChain() { +void optimize_chain::divide_into_subchain() { if (!Chain.front().isValid()) { Chain.pop(); return; } SubChain.clear(); - divideToSubChain(Chain.front()); + divide_into_subchain(Chain.front()); Chain.pop(); } -void OptiChain::divideToSubChain(const Region &Cur) { +void optimize_chain::divide_into_subchain(const region &Cur) { #ifdef sendInfo std::cout << "ready to analyse" << Cur.toString() << std::endl; #endif if (Cur.size() <= 3) { - SubChain.push_back(Region(Cur.Beg, Cur.End, idp)); + SubChain.push_back(region(Cur.begin, Cur.end, region_type::independent)); #ifdef sendInfo - std::cout << "Region" << Cur.toString() + std::cout << "region" << Cur.toString() << " in Chain is too thin, sink directly." << std::endl; #endif return; @@ -293,7 +278,7 @@ void OptiChain::divideToSubChain(const Region &Cur) { Eigen::ArrayXi HL; HL.setZero(Cur.size() + 1); - HL.segment(0, Cur.size()) = HighLine.segment(Cur.Beg, Cur.size()); + HL.segment(0, Cur.size()) = HighLine.segment(Cur.begin, Cur.size()); HL(Cur.size()) = NInf; Eigen::ArrayXi ScanBoth, ScanLeft, ScanRight; @@ -301,7 +286,7 @@ void OptiChain::divideToSubChain(const Region &Cur) { ScanLeft.setZero(Cur.size()); ScanRight.setZero(Cur.size()); // qDebug("开始用三个一维算子扫描HighLine和LowLine"); - for (int i = 1; i < Cur.size(); i++) // 用三个算子扫描一个大孤立区间 + for (int i = 1; i < Cur.size(); i++) // 用三个算子扫描一个大孤立区间 { ScanBoth(i) = (HL.segment(i - 1, 3) * Both).sum() > 0; ScanLeft(i) = (HL.segment(i - 1, 3) * Left).sum() > 0; @@ -317,19 +302,19 @@ void OptiChain::divideToSubChain(const Region &Cur) { bool isReady = false; // 表示已经检测出极大值区间的入口,找到出口就装入一个极大值区间 - Region Temp(-1, -1, Hang); // 均写入绝对index而非相对index + region Temp(-1, -1, region_type::hanging); // 均写入绝对index而非相对index for (int i = 0; i < Cur.size(); i++) { if (!isReady && ScanLeft(i)) { isReady = true; - Temp.Beg = Cur.indexLocal2Global(i); - Temp.End = -1; + Temp.begin = Cur.indexLocal2Global(i); + Temp.end = -1; } if (isReady && ScanRight(i)) { isReady = false; - Temp.End = Cur.indexLocal2Global(i); + Temp.end = Cur.indexLocal2Global(i); SubChain.push_back(Temp); - Temp.Beg = -1; - Temp.End = -1; + Temp.begin = -1; + Temp.end = -1; } } // qDebug("已将极大值区间串联成链,即将开始填充孤立区间。此时的SubChain为:"); @@ -337,55 +322,54 @@ void OptiChain::divideToSubChain(const Region &Cur) { auto prev = SubChain.begin(); for (auto it = SubChain.begin(); it != SubChain.end(); prev = it++) { if (it == SubChain.begin()) { - Temp.Beg = Cur.indexLocal2Global(0); - Temp.End = (it->Beg - 1); - Temp.type = idp; - if (Temp.isValid()) - SubChain.insert(it, Temp); + Temp.begin = Cur.indexLocal2Global(0); + Temp.end = (it->begin - 1); + Temp.type = region_type::independent; + if (Temp.isValid()) SubChain.insert(it, Temp); } else { - Temp.Beg = (prev->End + 1); - Temp.End = (it->Beg - 1); - Temp.type = idp; - if (Temp.isValid()) - SubChain.insert(it, Temp); + Temp.begin = (prev->end + 1); + Temp.end = (it->begin - 1); + Temp.type = region_type::independent; + if (Temp.isValid()) SubChain.insert(it, Temp); } } if (SubChain.size() <= 0) { SubChain.push_back(Cur); - } else if (SubChain.back().End < Cur.End) - SubChain.push_back(Region(SubChain.back().End + 1, Cur.End, idp)); + } else if (SubChain.back().end < Cur.end) + SubChain.push_back( + region(SubChain.back().end + 1, Cur.end, region_type::independent)); #ifdef sendInfo std::cout << "SubChain constructed" << std::endl; #endif } -void OptiChain::Sink(const Region &Reg) { +void optimize_chain::sink(const region &Reg) { if (!Reg.isValid()) { - std::cout << "Invalid region: " << Reg.toString() << std::endl; + std::cout << "invalid region: " << Reg.toString() << std::endl; return; } if (Reg.isIDP()) { - HighLine.segment(Reg.Beg, Reg.size()) -= - LowLine.segment(Reg.Beg, Reg.size()).minCoeff(); - LowLine.segment(Reg.Beg, Reg.size()) -= - LowLine.segment(Reg.Beg, Reg.size()).minCoeff(); + HighLine.segment(Reg.begin, Reg.size()) -= + LowLine.segment(Reg.begin, Reg.size()).minCoeff(); + LowLine.segment(Reg.begin, Reg.size()) -= + LowLine.segment(Reg.begin, Reg.size()).minCoeff(); return; } if (AllowSinkHang && Reg.isHang()) { int BegGap; - BegGap = validHeight(Reg.Beg) - validHeight(Reg.Beg - 1); + BegGap = valid_height(Reg.begin) - valid_height(Reg.begin - 1); int EndGap; - if (isSolidBlock(Reg.End + 1)) - EndGap = validHeight(Reg.End) - validHeight(Reg.End + 1); + if (is_solid_block(Reg.end + 1)) + EndGap = valid_height(Reg.end) - valid_height(Reg.end + 1); else - EndGap = validHeight(Reg.End) - NInf; + EndGap = valid_height(Reg.end) - NInf; int offset = std::min(std::max(std::min(BegGap, EndGap) - 1, 0), - LowLine.segment(Reg.Beg, Reg.size()).minCoeff()); - HighLine.segment(Reg.Beg, Reg.size()) -= offset; - LowLine.segment(Reg.Beg, Reg.size()) -= offset; + LowLine.segment(Reg.begin, Reg.size()).minCoeff()); + HighLine.segment(Reg.begin, Reg.size()) -= offset; + LowLine.segment(Reg.begin, Reg.size()) -= offset; } } diff --git a/SlopeCraftL/OptiChain.h b/SlopeCraftL/optimize_chain.h similarity index 61% rename from SlopeCraftL/OptiChain.h rename to SlopeCraftL/optimize_chain.h index 7d866a51..75f4b6d1 100644 --- a/SlopeCraftL/OptiChain.h +++ b/SlopeCraftL/optimize_chain.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -34,7 +34,7 @@ This file is part of SlopeCraft. // using namespace std; // using namespace Eigen; -enum RegionType { idp, Hang, Invalid }; +enum class region_type { independent, hanging }; #ifndef removeQt extern ARGB isTColor; @@ -43,33 +43,34 @@ extern ARGB WaterColor; extern ARGB greyColor; #endif -class Region { -public: - Region(short = -1, short = -1, RegionType = Invalid); - short Beg; - short End; - RegionType type; +class region { + public: + region() = delete; + region(int b, int e, region_type t) : begin{b}, end{e}, type{t} {} + int begin; + int end; + region_type type; bool isIDP() const; bool isHang() const; bool isValid() const; int size() const; - short indexLocal2Global(short) const; - short indexGlobal2Local(short) const; + int indexLocal2Global(int) const; + int indexGlobal2Local(int) const; std::string toString() const; }; -class OptiChain { -public: +class optimize_chain { + public: // Index一律以Height矩阵中的索引为准。Height(r,c+1)<->Base(r,c) // 高度矩阵一律不含水柱顶的玻璃方块 - OptiChain(int Size = -1); // default Random Constructor - OptiChain(const Eigen::ArrayXi &base, const Eigen::ArrayXi &High, - const Eigen::ArrayXi &Low); - ~OptiChain(); - - void divideAndCompress(); - const Eigen::ArrayXi &getHighLine(); - const Eigen::ArrayXi &getLowLine(); + optimize_chain(int Size = -1); // default Random Constructor + optimize_chain(const Eigen::ArrayXi &base, const Eigen::ArrayXi &High, + const Eigen::ArrayXi &Low); + ~optimize_chain(); + + void divide_and_compress(); + const Eigen::ArrayXi &high_line(); + const Eigen::ArrayXi &low_line(); // ArrayXi toDepth() const; // static ArrayXXi Base; @@ -83,27 +84,27 @@ class OptiChain { static bool AllowSinkHang; #endif -private: + private: // int Col; Eigen::ArrayXi Base; Eigen::ArrayXi HighLine; Eigen::ArrayXi LowLine; - std::queue Chain; // 将一整列按水/空气切分为若干个大的孤立区间 - std::list - SubChain; // 将Chain中每个大的孤立区间切分为若干“最大单孤立区间”和“悬空区间”组成的串 + std::queue Chain; // 将一整列按水/空气切分为若干个大的孤立区间 + std::list + SubChain; // 将Chain中每个大的孤立区间切分为若干“最大单孤立区间”和“悬空区间”组成的串 void divideToChain(); - void divideToSubChain(); + void divide_into_subchain(); - bool isAir(int index) const; - bool isWater(int index) const; - bool isSolidBlock(int index) const; + bool is_air(int index) const; + bool is_water(int index) const; + bool is_solid_block(int index) const; - void Sink(const Region &); - int validHeight(int index) const; + void sink(const region &); + int valid_height(int index) const; void dispSubChain() const; // private: - void divideToSubChain(const Region &); + void divide_into_subchain(const region &); }; -#endif // OPTICHAIN_H +#endif // OPTICHAIN_H diff --git a/SlopeCraftL/others/SlopeCraftL.rc.in b/SlopeCraftL/others/SlopeCraftL.rc.in index 6b440d44..bbd10fdb 100644 --- a/SlopeCraftL/others/SlopeCraftL.rc.in +++ b/SlopeCraftL/others/SlopeCraftL.rc.in @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -42,7 +42,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "CompanyName", "\0" VALUE "FileDescription", "SlopeCraft Library\0" VALUE "FileVersion", "@PROJECT_VERSION@.0\0" - VALUE "LegalCopyright", "TokiNoBug\0" + VALUE "LegalCopyright", "Copyright TokiNoBug 2021-2026\0" VALUE "OriginalFilename", "SlopeCraftL.dll\0" VALUE "ProductName", "SlopeCraftL\0" VALUE "ProductVersion", "@PROJECT_VERSION@.0\0" diff --git a/SlopeCraftL/PrimGlassBuilder.cpp b/SlopeCraftL/prim_glass_builder.cpp similarity index 56% rename from SlopeCraftL/PrimGlassBuilder.cpp rename to SlopeCraftL/prim_glass_builder.cpp index 5ecf6c29..89ae942c 100644 --- a/SlopeCraftL/PrimGlassBuilder.cpp +++ b/SlopeCraftL/prim_glass_builder.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,45 +20,44 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "PrimGlassBuilder.h" +#include "prim_glass_builder.h" const ARGB airColor = ARGB32(255, 255, 255); const ARGB targetColor = ARGB32(0, 0, 0); const ARGB glassColor = ARGB32(192, 192, 192); -const std::vector *edge::vertexes = nullptr; - -edge::edge() { - // beg=TokiRC(0,0); - // end=TokiRC(0,0); - begIdx = 0; - endIdx = 0; - lengthSquare = 0; +// const std::vector *edge::vertexes = nullptr; + +// edge::edge() { +// // beg=TokiRC(0,0); +// // end=TokiRC(0,0); +// begIdx = 0; +// endIdx = 0; +// lengthSquare = 0; +// } + +edge::edge(uint32_t b, uint32_t e, std::span v) + : begIdx{b}, endIdx{e}, lengthSquare{[b, e, v]() { + const auto beg = v[b]; + const auto end = v[e]; + const int rowSpan = beg.row - end.row; + const int colSpan = beg.col - end.col; + return (rowSpan * rowSpan + colSpan * colSpan); + }()} {} + +rc_pos edge::beg(std::span vertices) const { + return vertices[begIdx]; } -edge::edge(uint32_t _begIdx, uint32_t _endIdx) { - begIdx = _begIdx; - endIdx = _endIdx; - int r1 = TokiRow(beg()), c1 = TokiCol(beg()); - int r2 = TokiRow(end()), c2 = TokiCol(end()); - - int rowSpan = r1 - r2; - int colSpan = c1 - c2; - lengthSquare = (rowSpan * rowSpan + colSpan * colSpan); +rc_pos edge::end(std::span vertices) const { + return vertices[endIdx]; } -TokiPos edge::beg() const { return vertexes->at(begIdx); } - -TokiPos edge::end() const { return vertexes->at(endIdx); } - -pairedEdge::pairedEdge() { - first = TokiRC(0, 0); - second = TokiRC(0, 0); - lengthSquare = 0; -} -pairedEdge::pairedEdge(TokiPos A, TokiPos B) { - int r1 = TokiRow(A), c1 = TokiCol(A); - int r2 = TokiRow(B), c2 = TokiCol(B); +pairedEdge::pairedEdge() + : std::pair{{0, 0}, {0, 0}}, lengthSquare{0} {} +pairedEdge::pairedEdge(rc_pos A, rc_pos B) { + int r1 = A.row, c1 = A.col; + int r2 = B.row, c2 = B.col; first = A; second = B; int rowSpan = r1 - r2; @@ -66,21 +65,21 @@ pairedEdge::pairedEdge(TokiPos A, TokiPos B) { lengthSquare = (rowSpan * rowSpan + colSpan * colSpan); } -pairedEdge::pairedEdge(uint16_t r1, uint16_t c1, uint16_t r2, uint16_t c2) { - first = TokiRC(r1, c1); - second = TokiRC(r2, c2); +pairedEdge::pairedEdge(uint32_t r1, uint32_t c1, uint32_t r2, uint32_t c2) { + first = rc_pos{static_cast(r1), static_cast(c1)}; + second = rc_pos{static_cast(r2), static_cast(c2)}; int rowSpan = r1 - r2; int colSpan = c1 - c2; lengthSquare = (rowSpan * rowSpan + colSpan * colSpan); } -pairedEdge::pairedEdge(const edge &src) { - first = src.beg(); - second = src.end(); +pairedEdge::pairedEdge(const edge &src, std::span v) { + first = src.beg(v); + second = src.end(v); lengthSquare = src.lengthSquare; } /* -bool edge::connectWith(TokiPos P) const { +bool edge::connectWith(rc_pos P) const { return pairedEdge(*this).connectWith(P); } @@ -89,16 +88,15 @@ void edge::drawEdge(glassMap & map, bool drawHead) const { return; } */ -bool pairedEdge::connectWith(TokiPos P) const { +bool pairedEdge::connectWith(rc_pos P) const { return (first == P) || (second == P); } void pairedEdge::drawEdge(glassMap &map, bool drawHead) const { - if (lengthSquare <= 2) - return; + if (lengthSquare <= 2) return; float length = sqrt(lengthSquare); - Eigen::Vector2f startPoint(TokiRow(first), TokiCol(first)); - Eigen::Vector2f endPoint(TokiRow(second), TokiCol(second)); + Eigen::Vector2f startPoint(first.row, first.col); + Eigen::Vector2f endPoint(second.row, second.col); Eigen::Vector2f step = (endPoint - startPoint) / ceil(2.0 * length); Eigen::Vector2f cur; int stepCount = ceil(2.0 * length); @@ -108,90 +106,80 @@ void pairedEdge::drawEdge(glassMap &map, bool drawHead) const { r = floor(cur(0)); c = floor(cur(1)); if (r >= 0 && r < map.rows() && c >= 0 && c < map.cols()) { - map(r, c) = PrimGlassBuilder::glass; + map(r, c) = prim_glass_builder::glass; continue; } r = ceil(cur(0)); c = ceil(cur(1)); if (r >= 0 && r < map.rows() && c >= 0 && c < map.cols()) - map(r, c) = PrimGlassBuilder::glass; + map(r, c) = prim_glass_builder::glass; } - map(TokiRow(first), TokiCol(first)) = - (drawHead ? PrimGlassBuilder::target : PrimGlassBuilder::air); - map(TokiRow(second), TokiCol(second)) = - (drawHead ? PrimGlassBuilder::target : PrimGlassBuilder::air); + map(first.row, first.col) = + (drawHead ? prim_glass_builder::target : prim_glass_builder::air); + map(second.row, second.col) = + (drawHead ? prim_glass_builder::target : prim_glass_builder::air); } -#ifdef WITH_QT -PrimGlassBuilder::PrimGlassBuilder(QObject *parent) - : QObject(parent) -#else -PrimGlassBuilder::PrimGlassBuilder() -#endif -{ -} -glassMap PrimGlassBuilder::makeBridge(const TokiMap &_targetMap, - walkableMap *walkable) { +prim_glass_builder::prim_glass_builder() {} +glassMap prim_glass_builder::makeBridge(const TokiMap &_targetMap, + walkableMap *walkable) { // clock_t lastTime=std::clock(); const int rowCount = ceil(double(_targetMap.rows()) / unitL); const int colCount = ceil(double(_targetMap.cols()) / unitL); - std::vector> algos(rowCount); + std::vector> algos(rowCount); std::vector> glassMaps(rowCount); std::vector> walkableMaps(rowCount); std::vector> targetMaps(rowCount); - // std::cerr<<"开始分区分块,共["<make4SingleMap( + // qDebug()<<"开始处理第 ["<progress_bar.set_range(0, rowCount, r); } // qDebug("每个分区内的搭桥完毕,开始在分区间搭桥"); std::stack interRegionEdges; for (int r = 0; r < rowCount; r++) for (int c = 0; c < colCount; c++) { if (r + 1 < rowCount) { - pairedEdge temp = connectSingleMaps( - algos[r][c], TokiRC(unitL * r, unitL * c), algos[r + 1][c], - TokiRC(unitL * (r + 1), unitL * c)); - if (temp.lengthSquare > 2) - interRegionEdges.emplace(temp); + pairedEdge temp = + connectSingleMaps(algos[r][c], + rc_pos{static_cast(unitL * r), + static_cast(unitL * c)}, + algos[r + 1][c], + rc_pos{static_cast(unitL * (r + 1)), + static_cast(unitL * c)}); + if (temp.lengthSquare > 2) interRegionEdges.emplace(temp); } if (c + 1 < colCount) { - pairedEdge temp = connectSingleMaps( - algos[r][c], TokiRC(unitL * r, unitL * c), algos[r][c + 1], - TokiRC(unitL * r, unitL * (c + 1))); - if (temp.lengthSquare > 2) - interRegionEdges.emplace(temp); + pairedEdge temp = + connectSingleMaps(algos[r][c], + rc_pos{static_cast(unitL * r), + static_cast(unitL * c)}, + algos[r][c + 1], + rc_pos{static_cast(unitL * r), + static_cast(unitL * (c + 1))}); + if (temp.lengthSquare > 2) interRegionEdges.emplace(temp); } } // qDebug()<<"分区间搭桥完毕,将搭建"<progress_bar.set_range(0, 100, 100); // qDebug()<<"用时"< unitL || _targetMap.cols() > unitL) { - // qDebug("错误!make4SingleMap不应当收到超过unitL*unitL的图"); + // qDebug("错误!make4SingleMap 不应当收到超过 unitL*unitL 的图"); return glassMap(0, 0); } targetPoints.clear(); @@ -255,7 +241,7 @@ glassMap PrimGlassBuilder::make4SingleMap(const TokiMap &_targetMap, _targetMap(r, c - 1)) continue; else - targetPoints.emplace_back(TokiRC(r, c)); + targetPoints.emplace_back(rc_pos{r, c}); } } @@ -271,74 +257,69 @@ glassMap PrimGlassBuilder::make4SingleMap(const TokiMap &_targetMap, return result; } - for (auto it = tree.cbegin(); it != tree.cend(); it++) - it->drawEdge(result); + for (auto it = tree.cbegin(); it != tree.cend(); it++) it->drawEdge(result); for (auto it = targetPoints.cbegin(); it != targetPoints.cend(); it++) - result(TokiRow(*it), TokiCol(*it)) = 0; + result(it->row, it->col) = 0; - if (walkable != nullptr) - *walkable = result; + if (walkable != nullptr) *walkable = result; for (auto it = targetPoints.cbegin(); it != targetPoints.cend(); it++) { - result(TokiRow(*it), TokiCol(*it)) = 0; + result(it->row, it->col) = 0; if (walkable != nullptr) - walkable->operator()(TokiRow(*it), TokiCol(*it)) = blockType::target; + walkable->operator()(it->row, it->col) = blockType::target; } return result; } -pairedEdge PrimGlassBuilder::connectSingleMaps(const PrimGlassBuilder *map1, - TokiPos offset1, - const PrimGlassBuilder *map2, - TokiPos offset2) { - - if (map1->targetPoints.size() <= 0 || map2->targetPoints.size() <= 0) +pairedEdge prim_glass_builder::connectSingleMaps(const prim_glass_builder &map1, + rc_pos offset1, + const prim_glass_builder &map2, + rc_pos offset2) { + if (map1.targetPoints.size() <= 0 || map2.targetPoints.size() <= 0) return pairedEdge(); - uint16_t offsetR1 = TokiRow(offset1), offsetC1 = TokiCol(offset1); - uint16_t offsetR2 = TokiRow(offset2), offsetC2 = TokiCol(offset2); + uint32_t offsetR1 = offset1.row, offsetC1 = offset1.col; + uint32_t offsetR2 = offset2.row, offsetC2 = offset2.col; - uint16_t r1, r2, c1, c2; + uint32_t r1, r2, c1, c2; pairedEdge current; pairedEdge min; min.lengthSquare = 0x7FFFFFFF; - for (auto it = map1->targetPoints.cbegin(); it != map1->targetPoints.cend(); + for (auto it = map1.targetPoints.cbegin(); it != map1.targetPoints.cend(); it++) - for (auto jt = map2->targetPoints.cbegin(); jt != map2->targetPoints.cend(); + for (auto jt = map2.targetPoints.cbegin(); jt != map2.targetPoints.cend(); jt++) { - r1 = offsetR1 + TokiRow(*it); - c1 = offsetC1 + TokiCol(*it); - r2 = offsetR2 + TokiRow(*jt); - c2 = offsetC2 + TokiCol(*jt); + r1 = offsetR1 + it->row; + c1 = offsetC1 + it->col; + r2 = offsetR2 + jt->row; + c2 = offsetC2 + jt->col; current = pairedEdge(r1, c1, r2, c2); - if (current.lengthSquare <= 2) - return current; + if (current.lengthSquare <= 2) return current; - if (min.lengthSquare > current.lengthSquare) - min = current; + if (min.lengthSquare > current.lengthSquare) min = current; } return min; } -void PrimGlassBuilder::addEdgesToGraph() { +void prim_glass_builder::addEdgesToGraph() { edges.clear(); - edge::vertexes = std::addressof(targetPoints); + // edge::vertexes = std::addressof(targetPoints); // int taskCount=(targetPoints.size()*(targetPoints.size()-1))/2; // progressRangeSet(*windPtr,0,taskCount,0); for (uint32_t i = 0; i < targetPoints.size(); i++) { for (uint32_t j = i + 1; j < targetPoints.size(); j++) { - edges.emplace_back(edge(i, j)); + edges.emplace_back(edge(i, j, this->targetPoints)); } // emit keepAwake(); // emit progressAdd(targetPoints.size()-i); } // qDebug("插入了所有的边"); } -void PrimGlassBuilder::runPrim() { +void prim_glass_builder::runPrim() { tree.clear(); tree.reserve(targetPoints.size() - 1); @@ -351,11 +332,9 @@ void PrimGlassBuilder::runPrim() { std::stack::iterator> eraseTask; - while (!eraseTask.empty()) - eraseTask.pop(); + while (!eraseTask.empty()) eraseTask.pop(); while (foundCount < targetPoints.size()) { - while (!eraseTask.empty()) { edges.erase(eraseTask.top()); eraseTask.pop(); @@ -369,8 +348,8 @@ void PrimGlassBuilder::runPrim() { std::cerr << "Error: failed to find valid edge!\n"; break; } - // TokiPos z=selectedEdge->beg(); - // TokiPos w=selectedEdge->end(); + // rc_pos z=selectedEdge->beg(); + // rc_pos w=selectedEdge->end(); bool fz = isFound[(selectedEdge)->begIdx]; bool fw = isFound[(selectedEdge)->endIdx]; @@ -392,12 +371,12 @@ void PrimGlassBuilder::runPrim() { // 从找到的第一条边开始,寻找长度最小的可行边 for (auto it = selectedEdge; it != edges.end();) { // if(selectedEdge->lengthSquare<=2)break; - // TokiPos x=it->beg(),y=it->end(); + // rc_pos x=it->beg(),y=it->end(); bool fx = isFound[(it)->begIdx]; bool fy = isFound[(it)->endIdx]; if (fx && fy) { eraseTask.emplace(it); - it++; // 如果一条边的首尾都是已经被连接到的点,那么移除这条边 + it++; // 如果一条边的首尾都是已经被连接到的点,那么移除这条边 continue; } bool ux = !fx; @@ -411,11 +390,11 @@ void PrimGlassBuilder::runPrim() { } // 将选中边装入树中, - // 并从集合unsearched中删除选中边的两个端点, - // 向集合found中加入选中边的两个端点 + // 并从集合 unsearched 中删除选中边的两个端点, + // 向集合 found 中加入选中边的两个端点 { - // TokiPos x=selectedEdge->beg(); - // TokiPos y=selectedEdge->end(); + // rc_pos x=selectedEdge->beg(); + // rc_pos y=selectedEdge->end(); isFound[(selectedEdge)->begIdx] = true; isFound[(selectedEdge)->endIdx] = true; foundCount++; @@ -423,40 +402,38 @@ void PrimGlassBuilder::runPrim() { // found.emplace(y); // unsearched.erase(x); // unsearched.erase(y); - tree.emplace_back(*selectedEdge); + tree.emplace_back(*selectedEdge, this->targetPoints); } // if(foundCount%reportRate==0) { // //progressRangeSet(*windPtr,0,targetPoints.size(),foundCount); // emit keepAwake(); // } } - // qDebug("prim算法完毕"); + // qDebug("prim 算法完毕"); } EImage TokiMap2EImage(const TokiMap &tm) { EImage result(tm.rows(), tm.cols()); result.setConstant(airColor); - for (uint16_t r = 0; r < tm.rows(); r++) - for (uint16_t c = 0; c < tm.cols(); c++) { - if (tm(r, c) == 1) - result(r, c) = glassColor; - if (tm(r, c) > 1) - result(r, c) = targetColor; + for (uint32_t r = 0; r < tm.rows(); r++) + for (uint32_t c = 0; c < tm.cols(); c++) { + if (tm(r, c) == 1) result(r, c) = glassColor; + if (tm(r, c) > 1) result(r, c) = targetColor; } return result; } glassMap connectBetweenLayers(const TokiMap &map1, const TokiMap &map2, walkableMap *walkable) { - std::list target1, target2; + std::list target1, target2; target1.clear(); target2.clear(); for (int r = 0; r < map1.rows(); r++) for (int c = 0; c < map1.cols(); c++) { - if (map1(r, c) >= PrimGlassBuilder::target) - target1.emplace_back(TokiRC(r, c)); - if (map2(r, c) >= PrimGlassBuilder::target) - target2.emplace_back(TokiRC(r, c)); + if (map1(r, c) >= prim_glass_builder::target) + target1.emplace_back(rc_pos{r, c}); + if (map2(r, c) >= prim_glass_builder::target) + target2.emplace_back(rc_pos{r, c}); } std::list linkEdges; linkEdges.clear(); @@ -465,10 +442,8 @@ glassMap connectBetweenLayers(const TokiMap &map1, const TokiMap &map2, min.lengthSquare = 0x7FFFFFFF; for (auto t2 = target2.cbegin(); t2 != target2.cend(); t2++) { temp = pairedEdge(*t1, *t2); - if (min.lengthSquare > temp.lengthSquare) - min = temp; - if (min.lengthSquare <= 2) - break; + if (min.lengthSquare > temp.lengthSquare) min = temp; + if (min.lengthSquare <= 2) break; } linkEdges.emplace_back(min); } @@ -479,29 +454,46 @@ glassMap connectBetweenLayers(const TokiMap &map1, const TokiMap &map2, for (auto it = linkEdges.cbegin(); it != linkEdges.cend(); it++) it->drawEdge(result); - if (walkable != nullptr) - *walkable = result; + if (walkable != nullptr) *walkable = result; for (auto t = target1.cbegin(); t != target1.cend(); t++) { - result(TokiRow(*t), TokiCol(*t)) = PrimGlassBuilder::air; + result(t->row, t->col) = prim_glass_builder::air; if (walkable != nullptr) - walkable->operator()(TokiRow(*t), TokiCol(*t)) = PrimGlassBuilder::target; + walkable->operator()(t->row, t->col) = prim_glass_builder::target; } for (auto t = target2.cbegin(); t != target2.cend(); t++) { - result(TokiRow(*t), TokiCol(*t)) = PrimGlassBuilder::air; + result(t->row, t->col) = prim_glass_builder::air; if (walkable != nullptr) - walkable->operator()(TokiRow(*t), TokiCol(*t)) = PrimGlassBuilder::target; + walkable->operator()(t->row, t->col) = prim_glass_builder::target; } return result; } -TokiMap ySlice2TokiMap(const Eigen::Tensor &raw) { +template +TokiMap impl_ySlice2TokiMap(const Eigen::Tensor &raw) noexcept { assert(raw.dimension(2) == 1); TokiMap result(raw.dimension(0), raw.dimension(1)); result.setZero(); for (int i = 0; i < raw.size(); i++) - if (raw(i) > 1) - result(i) = PrimGlassBuilder::target; + if (raw(i) > 1) result(i) = prim_glass_builder::target; return result; } + +TokiMap ySlice2TokiMap_u16(const Eigen::Tensor &xzy, + std::span start_xzy, + std::span extension_xzy) noexcept { + // assert(raw.dimension(2) == 1); + assert(extension_xzy[2] == 1); + + TokiMap result; + + result.setZero(extension_xzy[0], extension_xzy[1]); + for (int x = 0; x < result.rows(); x++) { + for (int z = 0; z < result.cols(); z++) { + if (xzy(x + start_xzy[0], z + start_xzy[1], start_xzy[2]) > 1) + result(x, z) = prim_glass_builder::target; + } + } + return result; +} \ No newline at end of file diff --git a/SlopeCraftL/PrimGlassBuilder.h b/SlopeCraftL/prim_glass_builder.h similarity index 54% rename from SlopeCraftL/PrimGlassBuilder.h rename to SlopeCraftL/prim_glass_builder.h index 3cac912f..2956e4cc 100644 --- a/SlopeCraftL/PrimGlassBuilder.h +++ b/SlopeCraftL/prim_glass_builder.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,12 +22,6 @@ This file is part of SlopeCraft. #ifndef PRIMGLASSBUILDER_H #define PRIMGLASSBUILDER_H -#include "object_pool.hpp" - -#include "SCLDefines.h" - -#include "Colors.h" -#include "WaterItem.h" #include #include #include @@ -35,6 +29,10 @@ This file is part of SlopeCraft. #include #include #include +#include + +#include "SCLDefines.h" +#include "water_item.h" // using namespace Eigen; @@ -47,73 +45,70 @@ typedef TokiMap glassMap; typedef TokiMap walkableMap; class edge { -public: - edge(); - edge(uint32_t begIdx, uint32_t endIdx); - // edge(uint32_t begIdx,uint32_t endIdx); - // edge(TokiPos,TokiPos); - // edge(uint16_t r1,uint16_t c1,uint16_t r2,uint16_t c2); - uint32_t begIdx; - uint32_t endIdx; - int lengthSquare; - - static const std::vector *vertexes; - - TokiPos beg() const; - TokiPos end() const; - bool connectWith(TokiPos) const; - void drawEdge(glassMap &, bool drawHead = false) const; + public: + explicit edge() = delete; + edge(uint32_t begIdx, uint32_t endIdx, std::span vertices); + const uint32_t begIdx; + const uint32_t endIdx; + const int lengthSquare; + + rc_pos beg(std::span vertices) const; + rc_pos end(std::span vertices) const; + // bool connectWith(rc_pos) const; + // void drawEdge(glassMap &, bool drawHead = false) const; }; -class pairedEdge : public std::pair { -public: +class pairedEdge : public std::pair { + public: pairedEdge(); - pairedEdge(TokiPos, TokiPos); - pairedEdge(uint16_t r1, uint16_t c1, uint16_t r2, uint16_t c2); - pairedEdge(const edge &); + pairedEdge(rc_pos, rc_pos); + pairedEdge(uint32_t r1, uint32_t c1, uint32_t r2, uint32_t c2); + pairedEdge(const edge &, std::span vertices); + int lengthSquare; - bool connectWith(TokiPos) const; + bool connectWith(rc_pos) const; void drawEdge(glassMap &, bool drawHead = false) const; }; -TokiMap ySlice2TokiMap(const Eigen::Tensor &); +//[[deprecated]] TokiMap ySlice2TokiMap( +// const Eigen::Tensor &) noexcept; +//[[deprecated]] TokiMap ySlice2TokiMap_u16( +// const Eigen::Tensor &) noexcept; +TokiMap ySlice2TokiMap_u16(const Eigen::Tensor &xzy, + std::span start_xzy, + std::span extension_xzy) noexcept; glassMap connectBetweenLayers(const TokiMap &, const TokiMap &, walkableMap *walkable); -// 返回值是架构在相对较高的一层上的,walkable是各层俯视图叠加 - -class PrimGlassBuilder { -public: - PrimGlassBuilder(); +// 返回值是架构在相对较高的一层上的,walkable 是各层俯视图叠加 - template friend class tf::ObjectPool; - void *_object_pool_block; +class prim_glass_builder { + public: + prim_glass_builder(); static const uint32_t unitL = 32; static const uint32_t reportRate = 50; enum blockType { air = 0, glass = 1, target = 127 }; glassMap makeBridge(const TokiMap &_targetMap, walkableMap *walkable = nullptr); - void **windPtr; - void (**progressRangeSetPtr)(void *, int, int, int); - void (**progressAddPtr)(void *, int); - void (**keepAwakePtr)(void *); -private: - std::vector targetPoints; + SlopeCraft::ui_callbacks ui; + SlopeCraft::progress_callbacks progress_bar; + + private: + std::vector targetPoints; std::list edges; std::vector tree; void addEdgesToGraph(); void runPrim(); glassMap make4SingleMap(const TokiMap &_targetMap, walkableMap *walkable); - static pairedEdge connectSingleMaps(const PrimGlassBuilder *map1, - TokiPos offset1, - const PrimGlassBuilder *map2, - TokiPos offset2); + static pairedEdge connectSingleMaps(const prim_glass_builder &map1, + rc_pos offset1, + const prim_glass_builder &map2, + rc_pos offset2); }; -inline tf::ObjectPool pgb; EImage TokiMap2EImage(const TokiMap &); -#endif // PRIMGLASSBUILDER_H +#endif // PRIMGLASSBUILDER_H diff --git a/SlopeCraftL/simpleBlock.cpp b/SlopeCraftL/simpleBlock.cpp deleted file mode 100644 index 1456589b..00000000 --- a/SlopeCraftL/simpleBlock.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "simpleBlock.h" - -simpleBlock::simpleBlock() { - id = ""; - version = 0; - idOld = ""; - needGlass = false; - doGlow = false; - endermanPickable = false; - burnable = false; - - image.resize(16, 16); - // wallUseable=true; -} - -bool simpleBlock::dealBlockId(const std::string &id, std::string &netBlockId, - stringList *proName, stringList *proVal) { - proName->clear(); - proVal->clear(); - if (id.back() != ']') { - netBlockId = id; - return false; - } - short ReadBeg = id.find('['); - // BlockId.indexOf('['); - short ReadEnd = id.find(']'); - // BlockId.indexOf(']'); - - if (ReadBeg <= 0 || ReadEnd <= 0 || ReadEnd <= ReadBeg) { - std::cerr << "方块 Id 格式出现错误:" << id; - return false; - } - - short ProIndex[2] = {-1, -1}, ProValIndex[2] = {-1, -1}; - - netBlockId = id.substr(0, ReadBeg); - // BlockId.mid(0,ReadBeg).toLower(); - - for (short read = ReadBeg; read <= ReadEnd; read++) { - switch (id.at(read)) { - case '[': // 代表找到了一个新的属性 - ProIndex[0] = read + 1; - continue; - - case '=': // 识别出了属性名,寻找属性值 - ProIndex[1] = read - 1; - ProValIndex[0] = read + 1; - continue; - - case ',': // 代表结束了一个属性,并找到了下一个属性 - ProValIndex[1] = read - 1; - proName->push_back( - id.substr(ProIndex[0], ProIndex[1] - ProIndex[0] + 1)); - // BlockId.mid(ProIndex[0],ProIndex[1]-ProIndex[0]+1).toLower()); - proVal->push_back( - id.substr(ProValIndex[0], ProValIndex[1] - ProValIndex[0] + 1)); - // BlockId.mid(ProValIndex[0],ProValIndex[1]-ProValIndex[0]+1).toLower()); - ProIndex[0] = -1; - ProIndex[1] = -1; - ProValIndex[0] = -1; - ProValIndex[1] = -1; - - ProIndex[0] = read + 1; - continue; - case ']': - ProValIndex[1] = read - 1; - proName->push_back( - id.substr(ProIndex[0], ProIndex[1] - ProIndex[0] + 1)); - // BlockId.mid(ProIndex[0],ProIndex[1]-ProIndex[0]+1).toLower()); - proVal->push_back( - id.substr(ProValIndex[0], ProValIndex[1] - ProValIndex[0] + 1)); - // BlockId.mid(ProValIndex[0],ProValIndex[1]-ProValIndex[0]+1).toLower()); - continue; - } - } - // qDebug()<back()<<'='<back(); - return true; -} - -size_t BlockList::get_blocks(AbstractBlock **dst, uint8_t *dst_basecolor, - size_t capacity_in_elements) noexcept { - if (dst == nullptr || dst_basecolor == nullptr || capacity_in_elements <= 0) { - return 0; - } - - size_t counter = 0; - for (auto &ptr : this->m_blocks) { - dst[counter] = ptr.first; - dst_basecolor[counter] = ptr.second; - counter++; - if (counter >= capacity_in_elements) { - return counter; - } - } - return counter; -} - -size_t BlockList::get_blocks(const AbstractBlock **dst, uint8_t *dst_basecolor, - size_t capacity_in_elements) const noexcept { - if (dst == nullptr || dst_basecolor == nullptr || capacity_in_elements <= 0) { - return 0; - } - - size_t counter = 0; - for (auto &ptr : this->m_blocks) { - dst[counter] = ptr.first; - dst_basecolor[counter] = ptr.second; - counter++; - if (counter >= capacity_in_elements) { - return counter; - } - } - return counter; -} - -void BlockList::clear() noexcept { - for (auto &ptr : this->m_blocks) { - delete ptr.first; - } - - this->m_blocks.clear(); -} - -BlockList::~BlockList() { this->clear(); } \ No newline at end of file diff --git a/SlopeCraftL/string_deliver.h b/SlopeCraftL/string_deliver.h new file mode 100644 index 00000000..71047837 --- /dev/null +++ b/SlopeCraftL/string_deliver.h @@ -0,0 +1,27 @@ +#ifndef SLOPECRAFT_SLOPECRAFTL_WRITESTRINGDELIVER_H +#define SLOPECRAFT_SLOPECRAFTL_WRITESTRINGDELIVER_H +#include "SlopeCraftL.h" +#include + +namespace SlopeCraft { +inline void write_to_sd(string_deliver* s, std::string_view sv) noexcept { + if (s == nullptr) { + return; + } + if (!s->is_valid()) { + return; + } + + const size_t move_bytes = std::min(sv.size(), s->capacity); + memcpy(s->data, sv.data(), move_bytes); + s->is_complete = move_bytes < sv.size(); + + if (!s->is_complete) { + s->data[s->capacity - 1] = '\0'; + } + + s->size = move_bytes; +} +}; // namespace SlopeCraft + +#endif // SLOPECRAFT_SLOPECRAFTL_WRITESTRINGDELIVER_H diff --git a/SlopeCraftL/structure_3D.cpp b/SlopeCraftL/structure_3D.cpp new file mode 100644 index 00000000..e047bda1 --- /dev/null +++ b/SlopeCraftL/structure_3D.cpp @@ -0,0 +1,504 @@ +// +// Created by joseph on 4/17/24. +// +#include +#include +#include +#include +#include +#include +#include +#include + +#include "structure_3D.h" +#include "color_table.h" +#include "lossy_compressor.h" +#include "prim_glass_builder.h" +#include "FlatDiagram.h" + +std::optional structure_3D_impl::create( + const color_table_impl &table, const converted_image_impl &cvted, + const build_options &option) noexcept { + if (option.max_allowed_height < 14) { + option.ui.report_error( + errorFlag::MAX_ALLOWED_HEIGHT_LESS_THAN_14, + std::format("Max allowed height should be >= 14, but found {}", + option.max_allowed_height) + .c_str()); + return std::nullopt; + } + structure_3D_impl ret; + // set up basic infos + { + ret.schem.set_MC_major_version_number(table.mc_version_); + ret.schem.set_MC_version_number( + MCDataVersion::suggested_version(table.mc_version_)); + auto id = table.block_id_list(true); + ret.schem.set_block_id(id); + } + + build_options fixed_opt = option; + if (table.is_flat() || !table.is_vanilla()) { + fixed_opt.compress_method = compressSettings::noCompress; + fixed_opt.glass_method = glassBridgeSettings::noBridge; + } + fixed_opt.ui.report_working_status(workStatus::buidingHeighMap); + fixed_opt.main_progressbar.set_range(0, 10 * cvted.size(), 0); + { + std::unordered_map water_list; + fixed_opt.main_progressbar.add(cvted.size()); + } + + Eigen::ArrayXXi map_color, base_color, high_map, low_map; + std::unordered_map water_list; + { + auto opt = cvted.height_info(fixed_opt); + if (!opt) { + return std::nullopt; + } + map_color = std::move(opt.value().map_color); + base_color = std::move(opt.value().base); + high_map = std::move(opt.value().high_map); + low_map = std::move(opt.value().low_map); + water_list = std::move(opt.value().water_list); + } + assert((high_map >= low_map).all()); + assert(low_map.minCoeff() == 0); + + // std::cout << base_color << std::endl; + + try { + ret.schem.resize(2 + cvted.cols(), high_map.maxCoeff() + 1, + 2 + cvted.rows()); + ret.schem.set_zero(); + } catch (const std::bad_alloc &e) { + const std::array shape{ + 2 + cvted.cols(), static_cast(high_map.maxCoeff() + 1), + 2 + cvted.rows()}; + const uint64_t bytes_required = shape[0] * shape[1] * shape[2]; + option.ui.report_error( + errorFlag::MEMORY_ALLOCATE_FAILED, + std::format("Failed to allocate memory for this structure, " + "required {} GiB. The exception says: \"{}\"", + double(bytes_required) / (uint64_t{1} << 30), e.what()) + .c_str()); + return std::nullopt; + } + // make 3D + { + // base_color(r+1,c)<->High(r+1,c)<->Build(c+1,High(r+1,c),r+1) + // 为了区分玻璃与空气,张量中存储的是 Base+1.所以元素为 1 对应着玻璃,0 + // 对应空气 + + // 水柱周围的玻璃 + for (auto it = water_list.begin(); it != water_list.end(); it++) { + const int x = it->first.col + 1; + const int z = it->first.row; + const int y = it->second.high_y; + const int yLow = it->second.low_y; + ret.schem(x, y + 1, z) = 0 + 1; // 柱顶玻璃 + for (int yDynamic = yLow; yDynamic <= y; yDynamic++) { + ret.schem(x - 1, yDynamic, z - 0) = 1; + ret.schem(x + 1, yDynamic, z + 0) = 1; + ret.schem(x + 0, yDynamic, z - 1) = 1; + ret.schem(x + 0, yDynamic, z + 1) = 1; + } + if (yLow >= 1) { + ret.schem(x, yLow - 1, z) = 1; + } // 柱底玻璃 + } + + fixed_opt.main_progressbar.add(cvted.size()); + + // std::println("{} rows, {} cols", cvted.rows(), cvted.cols()); + // Common blocks + for (int64_t r = -1; r < int64_t(cvted.rows()); r++) { + for (int64_t c = 0; c < int64_t(cvted.cols()); c++) { + // std::println("r = {}, c = {}", r, c); + const int cur_base_color = base_color(r + 1, c); + if (cur_base_color == 12 || cur_base_color == 0) { + // water or air + continue; + } + const int x = c + 1; + const int y = low_map(r + 1, c); + const int z = r + 1; + if (y >= 1) { + auto &blk = table.blocks[base_color(r + 1, c)]; + if (blk.needGlass) { + ret.schem(x, y - 1, z) = 0 + 1; + } + if (blk.needStone[table.mc_version_]) { + ret.schem(x, y - 1, z) = 11 + 1; + } + } + const bool fire_proof = + fixed_opt.fire_proof && table.blocks[base_color(r + 1, c)].burnable; + const bool enderman_proof = + fixed_opt.enderman_proof && + table.blocks[base_color(r + 1, c)].endermanPickable; + if (fire_proof || enderman_proof) { + if (y >= 1 && ret.schem(x, y - 1, z) == 0) + ret.schem(x, y - 1, z) = 0 + 1; + if (x >= 1 && ret.schem(x - 1, y, z) == 0) + ret.schem(x - 1, y, z) = 0 + 1; + if (z >= 1 && ret.schem(x, y, z - 1) == 0) + ret.schem(x, y, z - 1) = 0 + 1; + if (y + 1 < ret.schem.y_range() && ret.schem(x, y + 1, z) == 0) + ret.schem(x, y + 1, z) = 0 + 1; + if (x + 1 < ret.schem.x_range() && ret.schem(x + 1, y, z) == 0) + ret.schem(x + 1, y, z) = 0 + 1; + if (z + 1 < ret.schem.z_range() && ret.schem(x, y, z + 1) == 0) + ret.schem(x, y, z + 1) = 0 + 1; + } + + ret.schem(x, y, z) = cur_base_color + 1; + } + fixed_opt.main_progressbar.add(cvted.cols()); + } + + fixed_opt.main_progressbar.add(cvted.size()); + + for (auto it = water_list.cbegin(); it != water_list.cend(); ++it) { + const int x = it->first.col + 1; + const int z = it->first.row; + const int y = it->second.high_y; + const int yLow = it->second.low_y; + for (int yDynamic = yLow; yDynamic <= y; yDynamic++) { + ret.schem(x, yDynamic, z) = 13; + } + } + } + fixed_opt.main_progressbar.set_range(0, 9 * cvted.size(), 8 * cvted.size()); + // build bridges + if (table.map_type() == mapTypes::Slope && + fixed_opt.glass_method == glassBridgeSettings::withBridge) { + fixed_opt.ui.report_working_status(workStatus::constructingBridges); + + fixed_opt.sub_progressbar.set_range(0, 100, 0); + const int step = cvted.size() / ret.schem.y_range(); + + prim_glass_builder glass_builder; + glass_builder.ui = fixed_opt.ui; + glass_builder.progress_bar = fixed_opt.sub_progressbar; + fixed_opt.ui.keep_awake(); + for (uint32_t y = 0; y < ret.schem.y_range(); y++) { + fixed_opt.sub_progressbar.add(step); + if (y % (fixed_opt.bridge_interval + 1) == 0) { + std::array start, extension; // x,z,y + start[0] = 0; + start[1] = 0; + start[2] = y; + extension[0] = ret.schem.x_range(); + extension[1] = ret.schem.z_range(); + extension[2] = 1; + TokiMap targetMap = + ySlice2TokiMap_u16(ret.schem.tensor(), start, extension); + glassMap glass; + // cerr << "Construct glass bridge at y=" << y << endl; + glass = glass_builder.makeBridge(targetMap); + for (int r = 0; r < glass.rows(); r++) + for (int c = 0; c < glass.cols(); c++) + if (ret.schem(r, y, c) == prim_glass_builder::air && + glass(r, c) == prim_glass_builder::glass) + ret.schem(r, y, c) = prim_glass_builder::glass; + } else { + continue; + } + } + fixed_opt.ui.keep_awake(); + fixed_opt.sub_progressbar.set_range(0, 100, 100); + } + + if (fixed_opt.connect_mushrooms) { + ret.schem.process_mushroom_states(); + } + + { + const auto shrink_result = ret.schem.remove_unused_ids(); + if (not shrink_result) { + fixed_opt.ui.report_error(SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_BLOCKS, + shrink_result.error().c_str()); + return std::nullopt; + } + } + + fixed_opt.main_progressbar.set_range(0, 9 * cvted.size(), 9 * cvted.size()); + fixed_opt.ui.report_working_status(workStatus::none); + + ret.map_color = map_color.cast(); + return ret; +} + +bool structure_3D_impl::export_litematica( + const char *filename, + const SlopeCraft::litematic_options &option) const noexcept { + option.ui.report_working_status(workStatus::writingMetaInfo); + option.progressbar.set_range(0, 100 + this->schem.size(), 0); + libSchem::litematic_info info{}; + info.litename_utf8 = option.litename_utf8; + info.regionname_utf8 = option.region_name_utf8; + + { + auto res = this->schem.export_litematic(filename, info); + + if (not res) { + option.ui.report_error(res.error().first, res.error().second.c_str()); + return false; + } + } + option.ui.report_working_status(workStatus::none); + option.progressbar.set_range(0, 100, 100); + return true; +} + +bool structure_3D_impl::export_vanilla_structure( + const char *filename, + const SlopeCraft::vanilla_structure_options &option) const noexcept { + option.ui.report_working_status(workStatus::writingMetaInfo); + option.progressbar.set_range(0, 100 + schem.size(), 0); + + auto res = schem.export_structure(filename, option.is_air_structure_void); + if (not res) { + option.ui.report_error(res.error().first, res.error().second.c_str()); + return false; + } + + option.progressbar.set_range(0, 100, 100); + option.ui.report_working_status(workStatus::none); + return true; +} + +bool structure_3D_impl::export_WE_schem( + const char *filename, + const SlopeCraft::WE_schem_options &option) const noexcept { + option.progressbar.set_range(0, 100, 0); + + libSchem::WorldEditSchem_info info; + + info.schem_name_utf8 = "GeneratedBySlopeCraftL"; + memcpy(info.offset.data(), option.offset, sizeof(info.offset)); + memcpy(info.WE_offset.data(), option.we_offset, sizeof(info.WE_offset)); + + info.required_mods_utf8.resize(option.num_required_mods); + + for (int idx = 0; idx < option.num_required_mods; idx++) { + info.required_mods_utf8[idx] = option.required_mods_name_utf8[idx]; + } + + option.progressbar.set_range(0, 100, 5); + + auto res = schem.export_WESchem(filename, info); + if (not res) { + option.ui.report_error(res.error().first, res.error().second.c_str()); + return false; + } + + option.progressbar.set_range(0, 100, 100); + return true; +} + +bool structure_3D_impl::export_flat_diagram( + const char *filename, const SlopeCraft::color_table &table_, + const SlopeCraft::flag_diagram_options &option) const noexcept { + const auto &table = dynamic_cast(table_); + if (table.map_type() != SCL_mapTypes::Flat) { + option.ui.report_error( + SCL_errorFlag::EXPORT_FLAT_DIAGRAM_ON_WRONG_MAP_TYPE, + std::format( + "We can only export flat diagram for flat maps, but found {}", + magic_enum::enum_name(table.map_type())) + .c_str()); + return false; + } + const libFlatDiagram::fd_option fdopt{ + .row_start = 0, + .row_end = this->schem.z_range(), + .cols = this->schem.x_range(), + .split_line_row_margin = option.split_line_row_margin, + .split_line_col_margin = option.split_line_col_margin, + .png_compress_level = option.png_compress_level, + .png_compress_memory_level = option.png_compress_memory_level, + }; + + std::vector> img_list_rmj; + img_list_rmj.reserve(this->schem.palette_size()); + + color_table_searching_index block_indexer; + { + auto indexer_opt = + dynamic_cast(table_).build_indexer(); + if (not indexer_opt) { + option.ui.report_error( + errorFlag::EXPORT_FLAT_DIAGRAM_FAILURE, + std::format("SlopeCraftL internal error. {}", indexer_opt.error()) + .c_str()); + return false; + } + block_indexer = std::move(indexer_opt.value()); + } + for (size_t pblkid = 0; pblkid < this->schem.palette_size(); pblkid++) { + if (pblkid == 0) { // air + img_list_rmj.emplace_back(); + img_list_rmj[0].setZero(); + continue; + } + std::string_view id = this->schem.palette()[pblkid]; + const mc_block *blkp = block_indexer.find(id); + // const mc_block *blkp = table.find_block_for_index(pblkid - 1, id); + if (blkp == nullptr) { + std::string blkid_full; + blkid_full.reserve(64 * 2048); + for (const auto &blk : table.blocks) { + blkid_full += blk.id; + blkid_full.push_back('\n'); + } + option.ui.report_error( + errorFlag::EXPORT_FLAT_DIAGRAM_FAILURE, + std::format("SlopeCraftL internal error. Failed to find block image " + "for \"{}\". " + "In the 3d structure, the corresponding block idx is " + "{}.\nThe whole " + "block palette is as below: {}", + id, pblkid, blkid_full) + .c_str()); + return false; + } + + img_list_rmj.emplace_back(blkp->image); + } + + auto block_at_callback = [this, &img_list_rmj]( + int64_t r, + int64_t c) -> libFlatDiagram::block_img_ref_t { + if (r < 0 or c < 0 or r >= this->schem.z_range() or + c >= this->schem.x_range()) { // out of range + return libFlatDiagram::block_img_ref_t{img_list_rmj.at(0).data()}; + } + + const int ele = this->schem(c, 0, r); + assert(ele >= 0 and ele < ptrdiff_t(this->schem.palette_size())); + + return libFlatDiagram::block_img_ref_t{img_list_rmj.at(ele).data()}; + }; + + std::array, 4> txt{ + std::make_pair( + "Title", "Flat diagram generated by SlopeCraftL."), + std::make_pair("Software", "SlopeCraftL"), + std::make_pair( + "Description", + "This image is a flat diagram created by SlopeCraftL, which is is " + "a subproject of SlopeCraft, developed by TokiNoBug."), + std::make_pair( + "Comment", + "SlopeCraft is a free software published " + "under GPLv3 license. You can find " + "its repository at https://github.com/SlopeCraft/SlopeCraft")}; + + auto err = libFlatDiagram::export_flat_diagram(filename, fdopt, + block_at_callback, txt); + if (!err.empty()) { + option.ui.report_error(errorFlag::EXPORT_FLAT_DIAGRAM_FAILURE, err.c_str()); + return false; + } + return true; +} + +namespace cereal { +template +void save(archive &ar, const Eigen::ArrayXX &mat) { + ar(mat.rows(), mat.cols()); + ar(cereal::binary_data(mat.data(), mat.size())); +} + +template +void load(archive &ar, Eigen::ArrayXX &mat) { + Eigen::Index rows{0}, cols{0}; + ar(rows, cols); + if (rows < 0 || cols < 0) { + throw std::runtime_error{ + std::format("Found negative shape when deserializing " + "Eigen::ArrayXX, {} rows and {} cols", + rows, cols)}; + } + mat.resize(rows, cols); + ar(cereal::binary_data(mat.data(), mat.size() * sizeof(uint8_t))); +} +} // namespace cereal + +std::string structure_3D_impl::save_cache( + const std::filesystem::path &filename) const noexcept { + try { + std::filesystem::create_directories(filename.parent_path()); + boost::iostreams::filtering_ostream ofs{}; + ofs.set_auto_close(true); + { + boost::iostreams::zstd_params params; + // ZSTD_defaultCLevel() doesn't exist below zstd 1.5 +#if ZSTD_VERSION_MINOR >= 5 + params.level = uint32_t(ZSTD_defaultCLevel()); +#else + params.level = uint32_t(ZSTD_CLEVEL_DEFAULT); +#endif + ofs.push(boost::iostreams::zstd_compressor{params}); + ofs.push( + boost::iostreams::file_sink{filename.string(), std::ios::binary}); + } + + { + cereal::BinaryOutputArchive boa{ofs}; + boa(*this); + } + + } catch (const std::exception &e) { + return std::format("Caught exception: {}", e.what()); + } + + return {}; +} + +tl::expected structure_3D_impl::load_cache( + const std::filesystem::path &filename) noexcept { + structure_3D_impl ret; + try { + boost::iostreams::filtering_istream ifs; + ifs.set_auto_close(true); + ifs.push(boost::iostreams::zstd_decompressor{}); + ifs.push( + boost::iostreams::file_source{filename.string(), std::ios::binary}); + { + cereal::BinaryInputArchive bia{ifs}; + bia(ret); + } + } catch (const std::exception &e) { + return tl::make_unexpected(std::format("Caught exception: {}", e.what())); + } + + return ret; +} + +uint64_t structure_3D_impl::block_count() const noexcept { + std::vector LUT_is_air; + LUT_is_air.reserve(this->schem.palette_size()); + for (auto &id : this->schem.palette()) { + if (id == "air" || id == "minecraft:air") { + LUT_is_air.emplace_back(1); + } else { + LUT_is_air.emplace_back(0); + } + } + + uint64_t counter = 0; + for (int64_t i = 0; i < this->schem.size(); i++) { + const auto cur_blk_id = this->schem(i); + if (cur_blk_id >= LUT_is_air.size()) [[unlikely]] { + counter++; + continue; + } + if (!LUT_is_air[cur_blk_id]) { + counter++; + } + } + return counter; +} \ No newline at end of file diff --git a/SlopeCraftL/structure_3D.h b/SlopeCraftL/structure_3D.h new file mode 100644 index 00000000..672c108d --- /dev/null +++ b/SlopeCraftL/structure_3D.h @@ -0,0 +1,70 @@ +// +// Created by joseph on 4/17/24. +// + +#ifndef SLOPECRAFT_STRUCTURE_3D_H +#define SLOPECRAFT_STRUCTURE_3D_H + +#include "SlopeCraftL.h" +#include "converted_image.h" +#include "Schem/Schem.h" +#include "water_item.h" + +class structure_3D_impl : public structure_3D { + private: + public: + libSchem::Schem schem; + Eigen::ArrayXX + map_color; // map color may be modified by lossy + // compression,so we store the modified one + + size_t shape_x() const noexcept final { return this->schem.x_range(); } + size_t shape_y() const noexcept final { return this->schem.y_range(); } + size_t shape_z() const noexcept final { return this->schem.z_range(); } + size_t palette_length() const noexcept final { + return this->schem.palette_size(); + } + void get_palette(const char **buffer_block_id) const noexcept final { + for (size_t i = 0; i < this->palette_length(); i++) { + buffer_block_id[i] = this->schem.palette()[i].c_str(); + } + } + + static std::optional create( + const color_table_impl &, const converted_image_impl &cvted, + const build_options &option) noexcept; + + bool export_litematica(const char *filename, + const litematic_options &option) const noexcept final; + bool export_vanilla_structure( + const char *filename, + const vanilla_structure_options &option) const noexcept final; + bool export_WE_schem(const char *filename, + const WE_schem_options &option) const noexcept final; + + bool export_flat_diagram( + const char *filename, const color_table &table, + const flag_diagram_options &option) const noexcept final; + + [[nodiscard]] std::string save_cache( + const std::filesystem::path &file) const noexcept; + + [[nodiscard]] static tl::expected load_cache( + const std::filesystem::path &file) noexcept; + + uint64_t block_count() const noexcept final; + + template + void load(archive &ar) { + ar(this->map_color); + ar(this->schem); + }; + + template + void save(archive &ar) const { + ar(this->map_color); + ar(this->schem); + } +}; + +#endif // SLOPECRAFT_STRUCTURE_3D_H diff --git a/SlopeCraftL/tests/load_scl_blocklist.cpp b/SlopeCraftL/tests/load_scl_blocklist.cpp new file mode 100644 index 00000000..7f5f1bd2 --- /dev/null +++ b/SlopeCraftL/tests/load_scl_blocklist.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +int main(int argc, char** argv) { + if (argc != 2) { + printf("Must provide 1 arguments\n"); + return __LINE__; + } + + const char* archive_path = argv[1]; + printf("Try parsing %s\n", archive_path); + SlopeCraft::block_list_interface* blocklist{nullptr}; + { + std::string errmsg; + errmsg.resize(8192); + SlopeCraft::string_deliver err_sd{errmsg.data(), errmsg.size()}; + + std::string warnings; + warnings.resize(8192); + SlopeCraft::string_deliver warn_sd{warnings.data(), warnings.size()}; + SlopeCraft::block_list_create_info option{ + SC_VERSION_U64, + &warn_sd, + &err_sd, + }; + blocklist = SlopeCraft::SCL_create_block_list(archive_path, option); + errmsg.resize(err_sd.size); + warnings.resize(warn_sd.size); + + if (!warnings.empty()) { + printf("Warnings: %s\n", warnings.data()); + } + + if (!errmsg.empty()) { + printf("Error: %s\n", errmsg.data()); + return __LINE__; + } + } + + printf("%s loaded successfully\n", archive_path); + SlopeCraft::SCL_destroy_block_list(blocklist); + + return 0; + // if (archive.getE) return 0; +} diff --git a/SlopeCraftL/WaterItem.h b/SlopeCraftL/water_item.h similarity index 50% rename from SlopeCraftL/WaterItem.h rename to SlopeCraftL/water_item.h index 6ee945da..4bbe1ecd 100644 --- a/SlopeCraftL/WaterItem.h +++ b/SlopeCraftL/water_item.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,25 +23,36 @@ This file is part of SlopeCraft. #ifndef WATERITEM_H #define WATERITEM_H -#include - -using TokiPos = uint32_t; -// typedef unsigned int TokiPos; -// typedef unsigned int waterItem; -using waterItem = uint32_t; -extern const TokiPos nullPos; -extern const waterItem nullWater; -extern const short WaterColumnSize[3]; -TokiPos TokiRC(int row, int col); // 前16bit存储row,后16bit存储col -short TokiRow(TokiPos); -short TokiCol(TokiPos); - -constexpr waterItem (*TokiWater)(int, int) = TokiRC; -constexpr short (*waterHigh)(waterItem) = TokiRow; -constexpr short (*waterLow)(waterItem) = TokiCol; -/* -waterItem TokiWater(short high,short low);//前两字节存储high,后两字节存储low -short waterHigh(waterItem); -short waterLow(waterItem);*/ - -#endif // WATERITEM_H +#include +#include + +struct rc_pos { + int32_t row; + int32_t col; + + [[nodiscard]] inline bool operator==(rc_pos b) const noexcept { + return (this->row == b.row) && (this->col == b.col); + } +}; + +template <> +struct std::hash { + size_t operator()(rc_pos pos) const noexcept { + return std::hash{}(reinterpret_cast(pos)); + } +}; + +struct water_y_range { + int high_y; + int low_y; +}; +[[nodiscard]] inline bool operator==(water_y_range a, + water_y_range b) noexcept { + return (a.high_y == b.high_y) && (a.low_y == b.low_y); +} + +// constexpr water_y_range nullWater = +// water_y_range{.high_y = INT_MIN, .low_y = INT_MIN}; +constexpr int32_t WATER_COLUMN_SIZE[3] = {11, 6, 1}; + +#endif // WATERITEM_H diff --git a/SlopeCraftMain/AiCvterParameterDialog.cpp b/SlopeCraftMain/AiCvterParameterDialog.cpp deleted file mode 100644 index 8a6044d0..00000000 --- a/SlopeCraftMain/AiCvterParameterDialog.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "AiCvterParameterDialog.h" -#include "ui_AiCvterParameterDialog.h" - -#include "MainWindow.h" - -using namespace SlopeCraft; -AiCvterParameterDialog::AiCvterParameterDialog(QWidget *parent) - : QDialog(parent), ui(new Ui::AiCvterParameterDialog) { - ui->setupUi(this); - - MainWindow *wind = qobject_cast(parent); - Kernel *k = wind->kernelPtr(); - const AiCvterOpt *src = k->aiCvterOpt(); - - ui->crossoverProb->setValue(SCL_getCrossoverProb(src)); - ui->mutateProb->setValue(SCL_getMutationProb(src)); - ui->maxGeneration->setValue(SCL_getMaxGeneration(src)); - ui->popSize->setValue(SCL_getPopSize(src)); - - connect(ui->maxGeneration, &QSpinBox::valueChanged, this, - &AiCvterParameterDialog::updateMaxFailTimes); - connect(ui->maxFailTimes, &QSpinBox::valueChanged, this, - &AiCvterParameterDialog::updateMaxFailTimes); - connect(ui->enableFailTimes, &QCheckBox::clicked, this, - &AiCvterParameterDialog::updateMaxFailTimes); - - updateMaxFailTimes(); - ui->maxFailTimes->setValue(SCL_getMaxFailTimes(src)); - - ui->enableFailTimes->setChecked(SCL_getMaxFailTimes(src) < - SCL_getMaxGeneration(src)); -} - -AiCvterParameterDialog::~AiCvterParameterDialog() { delete ui; } - -void AiCvterParameterDialog::updateMaxFailTimes() { - ui->maxFailTimes->setMaximum(ui->maxGeneration->value()); - - ui->maxFailTimes->setEnabled(ui->enableFailTimes->isChecked()); - - if (!ui->enableFailTimes->isChecked()) { - ui->maxFailTimes->setValue(ui->maxGeneration->value()); - } -} - -void AiCvterParameterDialog::closeEvent(QCloseEvent *event) { - QDialog::closeEvent(event); - - deleteLater(); -} - -void AiCvterParameterDialog::on_buttonBox_accepted() { - AiCvterOpt *opt = SCL_createAiCvterOpt(); - SCL_setCrossoverProb(opt, ui->crossoverProb->value()); - SCL_setMaxFailTimes(opt, ui->maxFailTimes->value()); - SCL_setMaxGeneration(opt, ui->maxGeneration->value()); - SCL_setMutationProb(opt, ui->mutateProb->value()); - SCL_setPopSize(opt, ui->popSize->value()); - - MainWindow *wind = qobject_cast(parent()); - Kernel *k = wind->kernelPtr(); - - k->setAiCvterOpt(opt); -} - -void AiCvterParameterDialog::on_buttonBox_clicked(QAbstractButton *) { - deleteLater(); -} diff --git a/SlopeCraftMain/BatchUi.cpp b/SlopeCraftMain/BatchUi.cpp deleted file mode 100644 index f9990c27..00000000 --- a/SlopeCraftMain/BatchUi.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "BatchUi.h" -#include "ui_BatchUi.h" - -#include "MainWindow.h" - -BatchUi::BatchUi(QWidget *parent) : QMainWindow(parent), ui(new Ui::BatchUi) { - ui->setupUi(this); - this->setAttribute(Qt::WA_QuitOnClose, false); - - // ui->setTypeLite->setChecked(false); - - connect(ui->setTypeData, &QRadioButton::clicked, this, - &BatchUi::onTaskTypeChanged); - connect(ui->setTypeLite, &QRadioButton::clicked, this, - &BatchUi::onTaskTypeChanged); - connect(ui->setTypeStructure, &QRadioButton::clicked, this, - &BatchUi::onTaskTypeChanged); - connect(ui->setDataFolder, &QLineEdit::textEdited, ui->setDataFolder, - &QLineEdit::textChanged); - connect(ui->setDataFolder, &QLineEdit::textChanged, this, - &BatchUi::checkExecutable); -} - -void BatchUi::closeEvent(QCloseEvent *event) { - QMainWindow::closeEvent(event); - delete this; -} - -BatchUi::~BatchUi() { delete ui; } - -void BatchUi::setTasks(const QStringList &fileNames) { - - taskBoxes.reserve(fileNames.size()); - - for (ushort idx = 0; idx < fileNames.size(); idx++) { - TaskBox *box = new TaskBox(this); - ui->scrollLayout->addWidget(box); - taskBoxes.emplace_back(box); - box->setTask(fileNames[idx]); - box->setMapBegSeqReadOnly(true); - - connect(box, &TaskBox::erase, this, &BatchUi::erased); - connect(box, &TaskBox::seqNumChanged, this, &BatchUi::onBoxSeqNumChanged); - } - qDebug("setTasks完毕"); - taskBoxes.front()->setMapBegSeq(0); - taskBoxes.front()->setMapBegSeqReadOnly(false); - onBoxSeqNumChanged(); - - emit ui->setTypeLite->clicked(); -} - -auto BatchUi::ptr2It(TaskBox *widgetPtr) const { - auto target = taskBoxes.begin(); - for (; target != taskBoxes.end(); ++target) { - if (*target == widgetPtr) { - return target; - } - } - return taskBoxes.end(); -} - -void BatchUi::erased(TaskBox *widgetPtr) { - ui->scrollLayout->removeWidget(widgetPtr); - - auto temp = ptr2It(widgetPtr); - taskBoxes.erase(temp); - widgetPtr->deleteLater(); - for (auto i : taskBoxes) { - i->setMapBegSeqReadOnly(true); - } - taskBoxes.front()->setMapBegSeqReadOnly(false); - onBoxSeqNumChanged(); -} - -void BatchUi::onBoxSeqNumChanged() { - uint32_t curBeg = taskBoxes.front()->begSeqNum(); - taskBoxes.front()->setMapBegSeq(curBeg); - for (size_t idx = 1; idx < taskBoxes.size(); idx++) { - taskBoxes[idx]->setMapBegSeq(curBeg + taskBoxes[idx - 1]->mapSize()); - curBeg += taskBoxes[idx - 1]->mapSize(); - } -} - -void BatchUi::onTaskTypeChanged() { - qDebug("void BatchUi::onTaskTypeChanged"); - if (ui->setTypeData->isChecked()) - TaskBox::taskType = TaskType::Data; - if (ui->setTypeLite->isChecked()) - TaskBox::taskType = TaskType::Litematica; - if (ui->setTypeStructure->isChecked()) - TaskBox::taskType = TaskType::Structure; - - ui->setDataFolder->setEnabled(TaskBox::taskType == TaskType::Data); - ui->BtnBrowseDataFolder->setEnabled(TaskBox::taskType == TaskType::Data); - ui->labelDenoteDstFolder->setEnabled(TaskBox::taskType == TaskType::Data); - - for (auto i : taskBoxes) { - i->updateTaskType(); - } - - checkExecutable(); -} - -void BatchUi::on_BtnBrowseDataFolder_clicked() { - QString path = QFileDialog::getExistingDirectory(this, tr("选择输出文件夹"), - ui->setDataFolder->text()); - if (path.isEmpty()) - return; - path.replace("\\\\", "/"); - path.replace('\\', '/'); - ui->setDataFolder->setText(path); -} - -MainWindow *BatchUi::wind() const { - return qobject_cast(parent()); -} - -SlopeCraft::Kernel *BatchUi::kernel() const { return wind()->kernelPtr(); } - -void BatchUi::checkExecutable() { - bool executable = true; - ui->LabelShowInfo->setText(""); - if (TaskBox::taskType == TaskType::Data) { - QString dstFolder = ui->setDataFolder->text(); - if (dstFolder.isEmpty() || !QDir(dstFolder).exists()) { - ui->LabelShowInfo->setText(tr("目标文件夹不可用")); - executable = false; - } - } else { - if (!kernel()->isVanilla()) { - ui->LabelShowInfo->setText(tr("主窗体中选择了纯文件地图画,冲突")); - executable = false; - } - } - - ui->LabelShowInfo->setText(tr("就绪")); - ui->BtnExecute->setEnabled(executable); -} - -void BatchUi::eraseAllTasks() { - for (auto widgetPtr : taskBoxes) { - - ui->scrollLayout->removeWidget(widgetPtr); - widgetPtr->deleteLater(); - } - taskBoxes.clear(); -} - -void BatchUi::on_BtnExecute_clicked() { - MainWindow::isBatchOperating = true; - for (size_t idx = 0; idx < taskBoxes.size(); idx++) { - TaskBox *curTask = taskBoxes[idx]; - if (curTask->mapSize() <= 0) { - continue; - } - QString prefix = tr("批量处理中:") + QString::number(idx + 1) + " / " + - QString::number(taskBoxes.size()) + '\n'; - ui->LabelShowInfo->setText(prefix); - - wind()->turnToPage(1); - wind()->preprocessImage(curTask->rawImgPath()); - wind()->kernelSetType(); - wind()->kernelSetImg(); - wind()->turnToPage(4); - - ui->LabelShowInfo->setText(prefix + tr("正在转化为地图画")); - wind()->on_Convert_clicked(); - - if (TaskBox::taskType != TaskType::Data) { - wind()->turnToPage(5); - ui->LabelShowInfo->setText(prefix + tr("正在构建三维结构")); - wind()->on_Build4Lite_clicked(); - ui->LabelShowInfo->setText(prefix + tr("正在导出三维结构")); - wind()->onExportLiteclicked(curTask->liteName()); - } else { - wind()->turnToPage(7); - ui->LabelShowInfo->setText(prefix + tr("正在导出地图文件")); - wind()->ui->InputDataIndex->setText( - QString::number(curTask->begSeqNum())); - wind()->onExportDataclicked(ui->setDataFolder->text()); - // export as data - } - } - eraseAllTasks(); - - ui->LabelShowInfo->setText(tr("批量处理完成")); - wind()->turnToPage(8); - qDebug("finished"); - MainWindow::isBatchOperating = false; -} diff --git a/SlopeCraftMain/BatchUi.h b/SlopeCraftMain/BatchUi.h deleted file mode 100644 index fa15b4de..00000000 --- a/SlopeCraftMain/BatchUi.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef BATCHUI_H -#define BATCHUI_H - -#include -#include -#include - -#include "TaskBox.h" -#include "ui_BatchUi.h" - -namespace Ui { -class BatchUi; -} - -class MainWindow; - -namespace SlopeCraft { -class Kernel; -} - -class BatchUi : public QMainWindow { - Q_OBJECT - -public: - explicit BatchUi(QWidget *parent); - ~BatchUi(); - void setTasks(const QStringList &); - -signals: - -protected: - void closeEvent(QCloseEvent *) override; - -private slots: - void erased(TaskBox *); - void onBoxSeqNumChanged(); - void onTaskTypeChanged(); - - void on_BtnExecute_clicked(); - - void on_BtnBrowseDataFolder_clicked(); - - void checkExecutable(); - - void eraseAllTasks(); - -private: - Ui::BatchUi *ui; - std::vector taskBoxes; - - auto ptr2It(TaskBox *widgetPtr) const; - MainWindow *wind() const; - SlopeCraft::Kernel *kernel() const; -}; - -#endif // BATCHUI_H diff --git a/SlopeCraftMain/BatchUi.ui b/SlopeCraftMain/BatchUi.ui deleted file mode 100644 index 9648cfcd..00000000 --- a/SlopeCraftMain/BatchUi.ui +++ /dev/null @@ -1,173 +0,0 @@ - - - BatchUi - - - - 0 - 0 - 640 - 480 - - - - - 640 - 0 - - - - SlopeCraft批量操作 - - - - - 0 - 0 - - - - - - - true - - - - - 0 - 0 - 620 - 353 - - - - - 0 - - - 0 - - - 0 - - - - - - - - - QFrame::StyledPanel - - - QFrame::Plain - - - - - - true - - - - - - - false - - - 开始执行 - - - - - - - - - 导出格式 - - - - - - - - 0 - 0 - - - - Litematica投影文件 - - - true - - - - - - - - 0 - 0 - - - - 原版结构方块文件 - - - - - - - - 0 - 0 - - - - 地图文件 - - - - - - - - - - - 地图文件输出位置 - - - - - - - false - - - false - - - - - - - false - - - 浏览 - - - - - - - - - - - diff --git a/SlopeCraftMain/CMakeLists.txt b/SlopeCraftMain/CMakeLists.txt deleted file mode 100644 index 9097e73c..00000000 --- a/SlopeCraftMain/CMakeLists.txt +++ /dev/null @@ -1,124 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(SlopeCraft_Main VERSION ${SlopeCraft_version} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) - -include_directories(${SlopeCraft_Eigen3_include_dir} - ${CMAKE_SOURCE_DIR}/SlopeCraftL - ${CMAKE_SOURCE_DIR}/utilities/BlockListManager) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets LinguistTools Network Concurrent REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets LinguistTools Network Concurrent REQUIRED) - -set(SlopeCraftMain_windows_rc_file) - -if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - message("Configuring on Windows. Adding rc file to " ${PROJECT_NAME}) - configure_file(others/SlopeCraft.rc.in others/SlopeCraft.rc) - set(SlopeCraftMain_windows_rc_file ${CMAKE_CURRENT_BINARY_DIR}/others/SlopeCraft.rc) -endif() - -set(SlopeCraftMain_header_files - AiCvterParameterDialog.h - BatchUi.h - TaskBox.h - MainWindow.h - previewwind.h - tpstrategywind.h -) - -set(SlopeCraftMain_source_files - - AiCvterParameterDialog.cpp - BatchUi.cpp - TaskBox.cpp - main.cpp - MainWindow.cpp - previewwind.cpp - tpstrategywind.cpp -) - -set(SlopeCraftMain_ui_files - - AiCvterParameterDialog.ui - BatchUi.ui - mainwindow.ui - previewwind.ui - TaskBox.ui - tpstrategywind.ui) - -set(SlopeCraftMain_ts_files - - others/SlopeCraft_en_US.ts) - -if(${SlopeCraft_update_ts_files}) - execute_process( - COMMAND ${SlopeCraft_Qt_lupdate_executable} ${SlopeCraftMain_header_files} ${SlopeCraftMain_source_files} ${SlopeCraftMain_ui_files} "-ts" ${SlopeCraftMain_ts_files} ${SlopeCraft_ts_flags} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) -endif() - -set(SlopeCraftMain_qrc_files - others/BlockTextures.qrc - others/Pics.qrc - - # others/SlopeCraft_translation.qrc -) - -set(SlopeCraftMain_project_sources - ${SlopeCraftMain_header_files} - ${SlopeCraftMain_source_files} - - ${SlopeCraftMain_ts_files} - ${SlopeCraftMain_ui_files} - ${SlopeCraftMain_qrc_files} - ${SlopeCraftMain_windows_rc_file} -) - -qt_add_executable(SlopeCraft - MANUAL_FINALIZATION - ${SlopeCraftMain_project_sources}) - -# execute_process(COMMAND "pause") -# qt_create_translation(SlopeCraftMain_qm_files ${CMAKE_CURRENT_SOURCE_DIR} ${SlopeCraftMain_ts_files}) -qt_add_lrelease(SlopeCraft TS_FILES ${SlopeCraftMain_ts_files} - QM_FILES_OUTPUT_VARIABLE SC_qm_files) - -qt_add_resources(SlopeCraft "translations" - PREFIX "/i18n" - BASE ${CMAKE_CURRENT_BINARY_DIR} - FILES ${SC_qm_files}) - -# qt6_add_resources(SlopeCraft "translation" -# PREFIX "/i18n" -# FILES -# ${SlopeCraftMain_qm_files} -# ${CMAKE_SOURCE_DIR}/CMakeLists.txt) -target_link_libraries(SlopeCraft - PRIVATE - BlockListManager - SlopeCraftL - Qt${QT_VERSION_MAJOR}::Widgets - Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Concurrent - VersionDialog -) - -set_target_properties(SlopeCraft PROPERTIES - VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_ICON_FILE SlopeCraftIconNew.icns - MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.ToKiNoBug.SlopeCraft" - MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} - MACOSX_BUNDLE TRUE - WIN32_EXECUTABLE TRUE -) - -qt_finalize_executable(SlopeCraft) - -include(install.cmake) \ No newline at end of file diff --git a/SlopeCraftMain/MainWindow.cpp b/SlopeCraftMain/MainWindow.cpp deleted file mode 100644 index 35a9066b..00000000 --- a/SlopeCraftMain/MainWindow.cpp +++ /dev/null @@ -1,1984 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include -#include -#include -#include -#include - -#include "MainWindow.h" - -#include - -bool MainWindow::isBatchOperating = false; - -blockListPreset MainWindow::preset_vanilla; -blockListPreset MainWindow::preset_elegant; -blockListPreset MainWindow::preset_cheap; -blockListPreset MainWindow::preset_shiny; - -inline uint32_t inverseColor(uint32_t raw) noexcept { - const uint32_t ASeg = raw & (0xFF000000); - const uint32_t R = 255 - ((raw & 0x00FF0000) >> 16); - const uint32_t G = 255 - ((raw & 0x0000FF00) >> 8); - const uint32_t B = 255 - (raw & 0x000000FF); - - return ASeg | (R << 16) | (G << 8) | (B); -} - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), - ui(new Ui::MainWindow), - kernel(SlopeCraft::SCL_createKernel()) { - ui->setupUi(this); - - ui->label_show_version->setText( - ui->label_show_version->text().arg(QStringLiteral("v") + SC_VERSION_STR)); - - // qDebug("成功 setupUi"); - Collected = false; - - // qDebug("成功创建内核"); - kernel->setWindPtr(this); - kernel->setKeepAwake(keepAwake); - kernel->setProgressRangeSet(progressRangeSet); - kernel->setProgressAdd(progressAdd); - kernel->setAlgoProgressRangeSet(algoProgressRangeSet); - kernel->setAlgoProgressAdd(algoProgressAdd); - kernel->setReportError(showError); - kernel->setReportWorkingStatue(showWorkingStatue); - - proTracker = nullptr; - - ProductDir = ""; - - Manager = new BlockListManager( - (QHBoxLayout *)ui->scrollAreaWidgetContents->layout()); - - // qDebug("成功创建方块列表管理者"); - connect(Manager, &BlockListManager::switchToCustom, this, - &MainWindow::ChangeToCustom); - connect(Manager, &BlockListManager::blockListChanged, this, - &MainWindow::onBlockListChanged); - // connect(Kernel,SIGNAL(convertProgressSetRange(int,int,int))); - - ui->maxHeight->setValue(255); - - connect(ui->progressStart, &QPushButton::clicked, this, - &MainWindow::turnToPage0); - connect(ui->progressImPic, &QPushButton::clicked, this, - &MainWindow::turnToPage1); - connect(ui->progressType, &QPushButton::clicked, this, - &MainWindow::turnToPage2); - connect(ui->progressBL, &QPushButton::clicked, this, - &MainWindow::turnToPage3); - connect(ui->progressAdjPic, &QPushButton::clicked, this, - &MainWindow::turnToPage4); - connect(ui->progressExLite, &QPushButton::clicked, this, - &MainWindow::turnToPage5); - connect(ui->progressExStructure, &QPushButton::clicked, this, - &MainWindow::turnToPage5); - connect(ui->progressExWESchem, &QPushButton::clicked, this, - &MainWindow::turnToPage5); - connect(ui->progressExFlatDiagram, &QPushButton::clicked, this, - &MainWindow::turnToPage5); - // connect(ui->menuExMcF,&QAction::trigger,this,&MainWindow::turnToPage6); - connect(ui->progressExData, &QPushButton::clicked, this, - &MainWindow::turnToPage7); - - connect(ui->progressAbout, &QPushButton::clicked, this, - &MainWindow::turnToPage8); - connect(ui->actionAboutSlopeCraft, &QAction::triggered, this, - &MainWindow::turnToPage8); - connect(ui->actionChinese, &QAction::triggered, this, &MainWindow::turnCh); - connect(ui->actionEnglish, &QAction::triggered, this, &MainWindow::turnEn); - connect(ui->progressChinese, &QPushButton::clicked, this, - &MainWindow::turnCh); - connect(ui->progressEnglish, &QPushButton::clicked, this, - &MainWindow::turnEn); - connect(ui->actionToki, &QAction::triggered, this, &MainWindow::contactG); - connect(ui->actionDoki, &QAction::triggered, this, &MainWindow::contactB); - connect(ui->progressG, &QPushButton::clicked, this, &MainWindow::contactG); - connect(ui->progressB, &QPushButton::clicked, this, &MainWindow::contactB); - connect(ui->progressCheckUpdates, &QPushButton::clicked, this, - &MainWindow::checkVersion); - - connect(ui->contact, &QPushButton::clicked, this, &MainWindow::contactB); - connect(ui->contact, &QPushButton::clicked, this, &MainWindow::contactG); - connect(ui->actionReportBugs, &QAction::triggered, this, - &MainWindow::on_reportBugs_clicked); - connect(ui->actionCheckUpdates, &QAction::triggered, this, - &MainWindow::checkVersion); - - connect(ui->actionTestBlockList, &QAction::triggered, this, - &MainWindow::testBlockList); - connect(ui->actionSetBuildParameters, &QAction::triggered, this, - &MainWindow::turnToPage5); - connect(ui->action_export_avaliable_color_list, &QAction::triggered, this, - &MainWindow::exportAvailableColors); - - qDebug("成功 connect 所有的菜单"); - - connect(ui->NextPage, &QPushButton::clicked, this, &MainWindow::turnToPage2); - connect(ui->NextPage2, &QPushButton::clicked, this, &MainWindow::turnToPage3); - connect(ui->NextPage3, &QPushButton::clicked, this, &MainWindow::turnToPage4); - connect(ui->ExLite, &QPushButton::clicked, this, &MainWindow::turnToPage5); - connect(ui->ExStructure, &QPushButton::clicked, this, - &MainWindow::turnToPage5); - connect(ui->ExWESchem, &QPushButton::clicked, this, &MainWindow::turnToPage5); - connect(ui->ExportFlatDiagram, &QPushButton::clicked, this, - &MainWindow::turnToPage5); - - connect(ui->ExData, &QPushButton::clicked, this, &MainWindow::turnToPage7); - connect(ui->FinishExLite, &QPushButton::clicked, this, - &MainWindow::turnToPage8); - connect(ui->FinshExData, &QPushButton::clicked, this, - &MainWindow::turnToPage8); - connect(ui->Exit, &QPushButton::clicked, this, &MainWindow::close); - qDebug("成功 connect 所有的翻页按钮"); - - connect(ui->isGame12, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame13, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame14, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame15, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame16, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame17, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame18, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - connect(ui->isGame19, &QRadioButton::toggled, this, - &MainWindow::onGameVerClicked); - - connect(ui->isMapCreative, &QRadioButton::toggled, this, - &MainWindow::onMapTypeClicked); - connect(ui->isMapSurvival, &QRadioButton::toggled, this, - &MainWindow::onMapTypeClicked); - /* - connect(ui->isMapWall,&QRadioButton::toggled, - this,&MainWindow::onMapTypeClicked);*/ - connect(ui->isMapFlat, &QRadioButton::toggled, this, - &MainWindow::onMapTypeClicked); - - connect(ui->isBLCreative, &QRadioButton::clicked, this, - &MainWindow::onPresetsClicked); - connect(ui->isBLSurvivalCheaper, &QRadioButton::clicked, this, - &MainWindow::onPresetsClicked); - connect(ui->isBLSurvivalBetter, &QRadioButton::clicked, this, - &MainWindow::onPresetsClicked); - connect(ui->isBLGlowing, &QRadioButton::clicked, this, - &MainWindow::onPresetsClicked); - - connect(ui->isColorSpaceRGBOld, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->isColorSpaceRGB, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->isColorSpaceHSV, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->isColorSpaceLab94, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->isColorSpaceLab00, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->isColorSpaceXYZ, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->isColorSpaceAi, &QRadioButton::clicked, this, - &MainWindow::onAlgoClicked); - connect(ui->AllowDither, &QCheckBox::clicked, this, - &MainWindow::onAlgoClicked); - - connect(ui->actionStart, &QAction::triggered, ui->progressStart, - &QPushButton::clicked); - connect(ui->actionImportImage, &QAction::triggered, ui->progressImPic, - &QPushButton::clicked); - connect(ui->actionMapType, &QAction::triggered, ui->progressType, - &QPushButton::clicked); - connect(ui->actionBlockList, &QAction::triggered, ui->progressBL, - &QPushButton::clicked); - connect(ui->actionConvert, &QAction::triggered, ui->progressAdjPic, - &QPushButton::clicked); - connect(ui->actionExportLite, &QAction::triggered, ui->progressExLite, - &QPushButton::clicked); - connect(ui->actionExportNBT, &QAction::triggered, ui->progressExStructure, - &QPushButton::clicked); - connect(ui->actionExportWESchem, &QAction::triggered, ui->progressExWESchem, - &QPushButton::clicked); - connect(ui->actionExportData, &QAction::triggered, ui->progressExData, - &QPushButton::clicked); - - connect(ui->actionFinish, &QAction::triggered, ui->progressAbout, - &QPushButton::clicked); - connect(ui->actionSavePreset, &QAction::triggered, this, - &MainWindow::onActionSavePreset); - connect(ui->actionLoadPreset, &QAction::triggered, this, - &MainWindow::onActionLoadPreset); - connect(ui->actionAiCvterOption, &QAction::triggered, this, - &MainWindow::onActionAiCvterParameters); - - turnToPage(0); -} - -MainWindow::~MainWindow() { - SlopeCraft::SCL_destroyKernel(this->kernel); - delete Manager; - delete ui; -} - -void MainWindow::showPreview() { - if (kernel->queryStep() < SlopeCraft::step::builded) return; - - PreviewWind *preWind = new PreviewWind(this); - preWind->Src.resize(64); - preWind->BlockCount.resize(64); - - preWind->Src = Manager->getQRadioButtonList(); - - int totalNum = 0; - kernel->getBlockCounts(&totalNum, preWind->BlockCount.data()); - - qDebug() << "去重前有:" << preWind->Src.size() << "个元素"; - auto iS = preWind->Src.begin(); - for (auto ib = preWind->BlockCount.begin(); - ib != preWind->BlockCount.end();) { - if (*iS == nullptr) { - ib = preWind->BlockCount.erase(ib); - iS = preWind->Src.erase(iS); - } - if (*ib > 0) { // if the block is used, keep it. otherwise erase it. - ib++; - iS++; - continue; - } - ib = preWind->BlockCount.erase(ib); - iS = preWind->Src.erase(iS); - } - - kernel->get3DSize(&preWind->size[0], &preWind->size[1], &preWind->size[2]); - - qDebug() << "去重后有:" << preWind->Src.size() << "个元素"; - // preWind->Water=Blocks[12][0]; - preWind->Water = Manager->getQRadioButtonList()[12]; - // preWind->Src[1]=Blocks[1][0];preWind->BlockCount[1]=1919810; - EImage tempE; - tempE.resize(kernel->getImageRows(), kernel->getImageCols()); - int a, b; - kernel->getConvertedImage(&a, &b, tempE.data()); - - QImage temp = EImage2QImage(tempE); - - preWind->ShowMaterialList(); - - preWind->showConvertedImage(temp); - - preWind->show(); -} - -void MainWindow::keepAwake(void *) { QCoreApplication::processEvents(); } - -void MainWindow::loadBlockList() { - if (!Manager->setupFixedBlockList("./Blocks/FixedBlocks.json", - "./Blocks/FixedBlocks")) { - QMessageBox::critical(this, tr("SlopeCraft 无法加载必需方块列表"), - tr("SlopeCraft 必须退出")); - exit(1); - } - - if (!Manager->setupCustomBlockList("./Blocks/CustomBlocks.json", - "./Blocks/CustomBlocks")) { - auto ret = QMessageBox::critical( - this, tr("SlopeCraft 无法加载可选方块列表"), tr("此错误可以忽略"), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, - QMessageBox::StandardButton::Close}); - - if (ret == QMessageBox::StandardButton::Close) { - exit(1); - } - } - - QRgb colors[64]; - kernel->getBaseColorInARGB32(colors); - Manager->setLabelColors(colors); - // applyPre(BLBetter); - Manager->loadInternalPreset(preset_elegant); -} - -void MainWindow::InitializeAll() { - ui->LeftScroll->verticalScrollBar()->setStyleSheet( - "QScrollBar{width: 7px;margin: 0px 0 0px 0;background-color: rgba(255, " - "255, 255, 64);color: rgba(255, 255, 255, 128);}"); - static bool needInitialize = true; - if (needInitialize) { - needInitialize = false; - } - - try { - MainWindow::preset_vanilla = - load_preset("./Blocks/Presets/vanilla.sc_preset_json"); - MainWindow::preset_cheap = - load_preset("./Blocks/Presets/cheap.sc_preset_json"); - MainWindow::preset_elegant = - load_preset("./Blocks/Presets/elegant.sc_preset_json"); - MainWindow::preset_shiny = - load_preset("./Blocks/Presets/shiny.sc_preset_json"); - } catch (std::exception &e) { - QMessageBox::critical(this, tr("加载默认预设失败"), - tr("一个或多个内置的预设不能被解析。SlopeCraft " - "可能已经损坏,请重新安装。\n具体报错信息:\n%1") - .arg(e.what())); - exit(1); - } - - if (!Collected) { - loadBlockList(); - // qDebug("方块列表加载完毕"); - Manager->setVersion((unsigned char)SlopeCraft::gameVersion::MC17); - onPresetsClicked(); - Collected = true; - } -} - -void MainWindow::contactG() { - QDesktopServices::openUrl(QUrl("https://github.com/SlopeCraft/SlopeCraft")); -} - -void MainWindow::contactB() { - QDesktopServices::openUrl(QUrl("https://space.bilibili.com/351429231")); -} - -#ifndef tpSDestroyer -#define tpSDestroyer -tpS::~tpS() {} -#endif - -void MainWindow::turnToPage(int page) { - page %= 9; - QString newtitle = "SlopeCraft "; - - newtitle += SlopeCraft::SCL_getSCLVersion(); -#ifdef WIN32 - newtitle += " Copyright © 2021-2023 TokiNoBug "; // windowsf -#elif defined(_MAC) || defined(__APPLE__) - newtitle += - " Copyright © 2021-2023 TokiNoBug,AbrasiveBoar, Cubik65536 "; // macOs -#else - newtitle += " Copyright © 2021-2023 TokiNoBug "; // unknown platform -#endif - - switch (page) { - case 0: - newtitle += "Step 0 / 6"; - newtitle += " "; - newtitle += tr("开始"); - ui->stackedWidget->setCurrentIndex(page); - break; - - case 1: - newtitle += "Step 1 / 6"; - newtitle += " "; - newtitle += tr("导入图片"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 2: - newtitle += "Step 2 / 6"; - newtitle += " "; - newtitle += tr("设置地图画类型"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 3: - newtitle += "Step 3 / 6"; - newtitle += " "; - newtitle += tr("设置方块列表"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 4: - newtitle += "Step 4 / 6"; - newtitle += " "; - newtitle += tr("调整颜色"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 5: - newtitle += "Step 5 / 6"; - newtitle += " "; - newtitle += tr("导出为投影文件"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 6: - newtitle += "Step 5 / 6"; - newtitle += " "; - newtitle += tr("导出为 mcfunction"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 7: - newtitle += "Step 5 / 6"; - newtitle += " "; - newtitle += tr("导出为地图文件"); - ui->stackedWidget->setCurrentIndex(page); - break; - case 8: - newtitle += "Step 6 / 6"; - newtitle += " "; - newtitle += tr("结束"); - ui->stackedWidget->setCurrentIndex(page); - break; - default: - qDebug("尝试翻页错误"); - break; - } - this->setWindowTitle(newtitle); - updateEnables(); - return; -} - -void MainWindow::turnToPage0() { - turnToPage(0); - return; -} - -void MainWindow::turnToPage1() { - turnToPage(1); - return; -} - -void MainWindow::turnToPage2() { - turnToPage(2); - return; -} - -void MainWindow::turnToPage3() { - turnToPage(3); - return; -} - -void MainWindow::turnToPage4() { - turnToPage(4); - return; -} - -void MainWindow::turnToPage5() { - turnToPage(5); - return; -} - -void MainWindow::turnToPage6() { - turnToPage(6); - return; -} - -void MainWindow::turnToPage7() { - turnToPage(7); - return; -} - -void MainWindow::turnToPage8() { - turnToPage(8); - return; -} - -void MainWindow::updateEnables() { - bool temp = true; - ui->StartWithFlat->setEnabled(temp); - ui->StartWithNotVanilla->setEnabled(temp); - ui->StartWithNotVanilla->setEnabled(temp); - - temp = kernel->queryStep() >= SlopeCraft::step::wait4Image; - ui->actionTestBlockList->setEnabled(temp); - ui->action_export_avaliable_color_list->setEnabled(temp); - - temp = kernel->queryStep() >= SlopeCraft::step::convertionReady; - ui->Convert->setEnabled(temp); - ui->ShowRaw->setEnabled(temp); - // ui->Convert->setEnabled(temp); - - // //////////////// - temp = kernel->queryStep() >= SlopeCraft::step::converted; - ui->ShowAdjed->setEnabled(temp); - // ui->menuExport->setEnabled(temp); - // ui->ExData->setEnabled(temp); - ui->ExportData->setEnabled(temp); - // ui->actionExportData->setEnabled(temp); - // ui->progressEx->setEnabled(temp); - // ui->progressExData->setEnabled(temp); - - // //////////////// - temp = (!ui->isMapCreative->isChecked()) && - kernel->queryStep() >= SlopeCraft::step::converted; - ui->Build4Lite->setEnabled(temp); - // ui->actionExportLite->setEnabled(temp); - // ui->actionExportNBT->setEnabled(temp); - // ui->actionExportWESchem->setEnabled(temp); - // ui->ExLite->setEnabled(temp); - // ui->ExStructure->setEnabled(temp); - // ui->ExWESchem->setEnabled(temp); - // ui->progressExLite->setEnabled(temp); - // ui->progressExStructure->setEnabled(temp); - // ui->progressExFlatDiagram->setEnabled(temp); - // ui->progressExWESchem->setEnabled(temp); - - // //////////////// - temp = kernel->queryStep() >= SlopeCraft::step::builded; - ui->ExportLite->setEnabled(temp); - ui->ManualPreview->setEnabled(temp); - ui->ExportFlatDiagram->setEnabled(temp && (kernel->isFlat())); -} - -void MainWindow::on_StartWithSlope_clicked() { - ui->isMapSurvival->setChecked(true); - turnToPage(1); - onBlockListChanged(); -} - -void MainWindow::on_StartWithFlat_clicked() { - ui->isMapFlat->setChecked(true); - onBlockListChanged(); - turnToPage(1); -} - -void MainWindow::on_StartWithNotVanilla_clicked() { - ui->isMapCreative->setChecked(true); - onBlockListChanged(); - turnToPage(1); -} -/* -void MainWindow::on_StartWithWall_clicked() { - ui->isMapWall->setChecked(true); - onBlockListChanged(); - turnToPage(1); -} -*/ - -void MainWindow::preprocessImage(const QString &Path) { - if (!rawPic.load(Path)) { - QMessageBox::information(this, tr("打开图片失败"), - tr("要不试试换一张图片吧!")); - return; - } - bool needSearch = rawPic.hasAlphaChannel(); - rawPic = rawPic.convertToFormat(QImage::Format_ARGB32); - bool OriginHasTp = false; - if (needSearch) { - QRgb *CL = nullptr; - for (short r = 0; r < rawPic.height(); r++) { - CL = (QRgb *)rawPic.scanLine(r); - for (short c = 0; c < rawPic.width(); c++) { - if (qAlpha(CL[c]) < 255) { - r = rawPic.height() + 1; - OriginHasTp = true; - break; - } - } - } - } - - // ui->ShowRawPic->setPixmap(QPixmap::fromImage(rawPic)); - - ui->IntroPicInfo->setText(tr("图片尺寸:") + - QString::number(rawPic.height()) + "×" + - QString::number(rawPic.width()) + tr("像素")); - if (OriginHasTp) { - preProcess(Strategy.pTpS, Strategy.hTpS, Strategy.BGC); - ui->IntroPicInfo->setText( - ui->IntroPicInfo->text() + "\n" + - tr("图片中存在透明/" - "半透明像素,已处理,您可以点击“设置”重新选择处理透明/" - "半透明像素的方式。\n重新设置处理方式后,需要重新导入一次。")); - } else { - rawPic = rawPic.copy(); - } - ui->ShowRawPic->setPixmap(QPixmap::fromImage(rawPic)); - ui->ShowPic->setPixmap(QPixmap::fromImage(rawPic)); - - kernel->decreaseStep(SlopeCraft::step::nothing); - this->kernelSetType(); - this->kernelSetImg(); - this->updateEnables(); -} - -void MainWindow::on_ImportPic_clicked() { - QStringList userSelected = QFileDialog::getOpenFileNames( - this, tr("选择图片"), this->prevOpenedDir, - tr("图片 (*.png *.bmp *.jpg *.tif *.GIF )")); - - if (userSelected.isEmpty()) return; - - this->prevOpenedDir = QFileInfo(userSelected.front()).filePath(); - - if (userSelected.size() == 1) { - QString Path = userSelected.front(); - - if (Path.isEmpty()) { - return; - } - preprocessImage(Path); - - return; - } else { - auto bo = new BatchUi(this); - // qDebug("开始创建 BatchUi"); - bo->setTasks(userSelected); - bo->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); - bo->show(); - /* - connect(this,&MainWindow::mapTypeChanged, - batchOperator,&BatchUi::taskTypeUpdated); - */ - // qDebug("Mainwindow setTasks 完毕"); - return; - } -} - -void MainWindow::on_ImportSettings_clicked() { - auto transSubWind = new tpStrategyWind(this); - connect(transSubWind, &tpStrategyWind::Confirm, this, - &MainWindow::ReceiveTPS); - transSubWind->setVal(Strategy); - - transSubWind->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); - transSubWind->show(); -} - -void MainWindow::ReceiveTPS(tpS t) { - this->Strategy = t; - qDebug("接收成功"); - qDebug() << "pTpS=" << t.pTpS << ". hTpS=" << t.hTpS; -} - -QRgb ComposeColor(const QRgb front, const QRgb back) { - QRgb red = - (qRed(front) * qAlpha(front) + qRed(back) * (255 - qAlpha(front))) / 255; - QRgb green = - (qGreen(front) * qAlpha(front) + qGreen(back) * (255 - qAlpha(front))) / - 255; - QRgb blue = - (qBlue(front) * qAlpha(front) + qBlue(back) * (255 - qAlpha(front))) / - 255; - return qRgb(red, green, blue); -} - -inline void MainWindow::preProcess(char pureTpStrategy, char halfTpStrategy, - QRgb BGC) { - qDebug("调用了 preProcess"); - // 透明像素处理策略:B->替换为背景色;A->空气;W->暂缓,等待处理 - // 半透明像素处理策略:B->替换为背景色;C->与背景色叠加;R->保留颜色;W->暂缓,等待处理 - qDebug("Cpoied"); - bool hasTotalTrans = false; - if (pureTpStrategy != 'W' && halfTpStrategy != 'W') { - QRgb *CL = nullptr; - for (int r = 0; r < rawPic.height(); r++) { - CL = (QRgb *)rawPic.scanLine(r); - for (int c = 0; c < rawPic.width(); c++) { - if (qAlpha(CL[c]) >= 255) continue; - if (qAlpha(CL[c]) == 0) switch (pureTpStrategy) { - case 'B': - CL[c] = BGC; - continue; - case 'A': - if (!hasTotalTrans) { - qDebug() << "发现纯透明像素"; - hasTotalTrans = true; - } - CL[c] = qRgba(0, 0, 0, 0); - continue; - } - - // qDebug("neeeee"); - switch (halfTpStrategy) { - case 'B': - CL[c] = BGC; - break; - case 'C': - CL[c] = ComposeColor(CL[c], BGC); - break; - case 'R': - CL[c] = qRgb(qRed(CL[c]), qGreen(CL[c]), qBlue(CL[c])); - } - } - } - } -} - -void MainWindow::onGameVerClicked() { - if (ui->isGame12->isChecked()) { - Manager->setVersion(12); - } - if (ui->isGame13->isChecked()) { - Manager->setVersion(13); - } - if (ui->isGame14->isChecked()) { - Manager->setVersion(14); - } - if (ui->isGame15->isChecked()) { - Manager->setVersion(15); - } - if (ui->isGame16->isChecked()) { - Manager->setVersion(16); - } - if (ui->isGame17->isChecked()) { - Manager->setVersion(17); - } - if (ui->isGame18->isChecked()) { - Manager->setVersion(18); - } - if (ui->isGame19->isChecked()) { - Manager->setVersion(19); - } - kernel->decreaseStep(SlopeCraft::step::nothing); - onBlockListChanged(); - - this->kernelSetType(); - if (!this->rawPic.isNull()) { - kernelSetImg(); - } - this->updateEnables(); -} - -void MainWindow::onMapTypeClicked() { - if (ui->isMapCreative->isChecked()) { - Manager->setEnabled(12, true); - } - if (ui->isMapFlat->isChecked()) { - Manager->setEnabled(12, true); - } - if (ui->isMapSurvival->isChecked()) { - Manager->setEnabled(12, false); - } - /* - if(ui->isMapWall->isChecked()) { - Manager->setEnabled(12,false); - }*/ - kernel->decreaseStep(SlopeCraft::step::nothing); - onBlockListChanged(); - - this->kernelSetType(); - if (!this->rawPic.isNull()) { - kernelSetImg(); - } - this->updateEnables(); -} - -void MainWindow::ChangeToCustom() { - ui->isBLCustom->setChecked(true); - kernel->decreaseStep(SlopeCraft::step::nothing); - updateEnables(); -} - -void MainWindow::onPresetsClicked() { - if (ui->isBLCreative->isChecked()) { - Manager->loadInternalPreset(preset_vanilla); - } - if (ui->isBLSurvivalCheaper->isChecked()) { - Manager->loadInternalPreset(preset_cheap); - } - if (ui->isBLSurvivalBetter->isChecked()) { - Manager->loadInternalPreset(preset_elegant); - } - if (ui->isBLGlowing->isChecked()) { - Manager->loadInternalPreset(preset_shiny); - } - - if (ui->isMapSurvival->isChecked()) Manager->setEnabled(12, false); - - kernel->decreaseStep(SlopeCraft::step::nothing); - - this->kernelSetType(); - if (!this->rawPic.isNull()) { - this->kernelSetImg(); - } - - updateEnables(); -} - -void MainWindow::onAlgoClicked() { - static SlopeCraft::convertAlgo lastChoice = - SlopeCraft::convertAlgo::RGB_Better; - static bool lastDither = false; - - SlopeCraft::convertAlgo now; - const bool nowDither = ui->AllowDither->isChecked(); - if (ui->isColorSpaceRGBOld->isChecked()) now = SlopeCraft::convertAlgo::RGB; - if (ui->isColorSpaceRGB->isChecked()) - now = SlopeCraft::convertAlgo::RGB_Better; - if (ui->isColorSpaceHSV->isChecked()) now = SlopeCraft::convertAlgo::HSV; - if (ui->isColorSpaceLab94->isChecked()) now = SlopeCraft::convertAlgo::Lab94; - if (ui->isColorSpaceLab00->isChecked()) now = SlopeCraft::convertAlgo::Lab00; - if (ui->isColorSpaceXYZ->isChecked()) now = SlopeCraft::convertAlgo::XYZ; - if (ui->isColorSpaceAi->isChecked()) now = SlopeCraft::convertAlgo::gaCvter; - - if (lastChoice == SlopeCraft::convertAlgo::gaCvter) kernelSetImg(); - - if ((lastChoice != now) || (lastDither != nowDither)) - kernel->decreaseStep(SlopeCraft::step::convertionReady); - - updateEnables(); - lastChoice = now; - lastDither = nowDither; -} - -void MainWindow::kernelSetType() { - SlopeCraft::mapTypes type = SlopeCraft::mapTypes::Slope; - { - if (ui->isMapCreative->isChecked()) type = SlopeCraft::mapTypes::FileOnly; - if (ui->isMapFlat->isChecked()) type = SlopeCraft::mapTypes::Flat; - if (ui->isMapSurvival->isChecked()) type = SlopeCraft::mapTypes::Slope; - /* - if(ui->isMapWall->isChecked()) - type=SlopeCraft::mapTypes::Wall;*/ - } - - SlopeCraft::gameVersion ver = SlopeCraft::gameVersion::MC19; - { - if (ui->isGame12->isChecked()) ver = SlopeCraft::gameVersion::MC12; - if (ui->isGame13->isChecked()) ver = SlopeCraft::gameVersion::MC13; - if (ui->isGame14->isChecked()) ver = SlopeCraft::gameVersion::MC14; - if (ui->isGame15->isChecked()) ver = SlopeCraft::gameVersion::MC15; - if (ui->isGame16->isChecked()) ver = SlopeCraft::gameVersion::MC16; - if (ui->isGame17->isChecked()) ver = SlopeCraft::gameVersion::MC17; - if (ui->isGame18->isChecked()) ver = SlopeCraft::gameVersion::MC18; - if (ui->isGame19->isChecked()) ver = SlopeCraft::gameVersion::MC19; - } - - bool allowedBaseColor[64]; - Manager->getEnableList(allowedBaseColor); - - std::array palette; - // const AbstractBlock * palette[64]; - Manager->getSimpleBlockList(palette.data()); - - kernel->setType(type, ver, allowedBaseColor, palette.data()); - const uint8_t *allowedMap; - int colorN = 0; - - SlopeCraft::SCL_getColorMapPtrs(nullptr, nullptr, nullptr, &allowedMap, - &colorN); - /* - cout<<"\n\nAllowedMap="; - for(int i=0;iisVanilla(); - emit mapTypeChanged(); -} - -void MainWindow::kernelSetImg() { - EImage rawImg = QImage2EImage(rawPic); - kernel->setRawImage(rawImg.data(), rawImg.rows(), rawImg.cols()); - // this->updateEnables(); -} - -EImage QImage2EImage(const QImage &qi) { - EImage ei; - if (qi.isNull() || qi.height() <= 0 || qi.width() <= 0) { - ei.setZero(0, 0); - return ei; - } - ei.setZero(qi.height(), qi.width()); - const QRgb *CL = nullptr; - for (int r = 0; r < ei.rows(); r++) { - CL = (const QRgb *)qi.scanLine(r); - for (int c = 0; c < ei.cols(); c++) ei(r, c) = CL[c]; - } - return ei; -} - -QImage EImage2QImage(const EImage &ei, ushort scale) { - QImage qi(ei.cols() * scale, ei.rows() * scale, - QImage::Format::Format_ARGB32); - QRgb *CL = nullptr; - for (int r = 0; r < qi.height(); r++) { - CL = (QRgb *)qi.scanLine(r); - for (int c = 0; c < qi.width(); c++) CL[c] = ei(r / scale, c / scale); - } - return qi; -} - -void MainWindow::progressRangeSet(void *p, int min, int max, int val) { - MainWindow *wind = (MainWindow *)p; - // 设置进度条的取值范围和值 - if (wind->proTracker == nullptr) { - qDebug("错误!proTracker==nullptr"); - return; - } - wind->proTracker->setRange(min, max); - wind->proTracker->setValue(val); -} - -void MainWindow::progressAdd(void *p, int deltaVal) { - MainWindow *wind = (MainWindow *)p; - if (wind->proTracker == nullptr) { - qDebug("错误!proTracker==nullptr"); - return; - } - wind->proTracker->setValue(deltaVal + wind->proTracker->value()); -} - -void MainWindow::algoProgressRangeSet(void *p, int min, int max, int val) { - MainWindow *wind = (MainWindow *)p; - wind->ui->algoBar->setRange(min, max); - wind->ui->algoBar->setValue(val); -} - -void MainWindow::algoProgressAdd(void *p, int deltaVal) { - MainWindow *wind = (MainWindow *)p; - wind->ui->algoBar->setValue(wind->ui->algoBar->value() + deltaVal); -} - -void MainWindow::on_Convert_clicked() { - if (kernel->queryStep() < SlopeCraft::step::wait4Image) { - qDebug("setType again"); - onBlockListChanged(); - if (kernel->queryStep() < SlopeCraft::step::wait4Image) return; - } - - if (kernel->queryStep() < SlopeCraft::step::convertionReady) { - qDebug("setImage again"); - kernelSetImg(); - if (kernel->queryStep() < SlopeCraft::step::convertionReady) return; - } - - SlopeCraft::convertAlgo now = SlopeCraft::convertAlgo::RGB; - bool nowDither = ui->AllowDither->isChecked(); - { - if (ui->isColorSpaceRGBOld->isChecked()) now = SlopeCraft::convertAlgo::RGB; - if (ui->isColorSpaceRGB->isChecked()) - now = SlopeCraft::convertAlgo::RGB_Better; - if (ui->isColorSpaceHSV->isChecked()) now = SlopeCraft::convertAlgo::HSV; - if (ui->isColorSpaceLab94->isChecked()) - now = SlopeCraft::convertAlgo::Lab94; - if (ui->isColorSpaceLab00->isChecked()) - now = SlopeCraft::convertAlgo::Lab00; - if (ui->isColorSpaceXYZ->isChecked()) now = SlopeCraft::convertAlgo::XYZ; - if (ui->isColorSpaceAi->isChecked()) now = SlopeCraft::convertAlgo::gaCvter; - } - - proTracker = ui->ShowProgressABbar; - - kernel->decreaseStep(SlopeCraft::step::convertionReady); - updateEnables(); - - bool temp = false; - ui->Convert->setEnabled(temp); // 防止用户在繁忙时重复操作 - ui->isColorSpaceHSV->setEnabled(temp); - ui->isColorSpaceRGB->setEnabled(temp); - ui->isColorSpaceLab94->setEnabled(temp); - ui->isColorSpaceLab00->setEnabled(temp); - ui->isColorSpaceXYZ->setEnabled(temp); - ui->isColorSpaceRGBOld->setEnabled(temp); - ui->isColorSpaceAi->setEnabled(temp); - ui->AllowDither->setEnabled(temp); - - std::clock_t startTime = std::clock(); - qDebug("Start to convert"); - kernel->convert(now, nowDither); - - qDebug() << "Convertion finished in " - << double(std::clock() - startTime) * 1000.0 / CLOCKS_PER_SEC - << " miliseconds."; - proTracker = nullptr; - - temp = true; - ui->Convert->setEnabled(temp); // 恢复锁定 - ui->isColorSpaceHSV->setEnabled(temp); - ui->isColorSpaceRGB->setEnabled(temp); - ui->isColorSpaceLab94->setEnabled(temp); - ui->isColorSpaceLab00->setEnabled(temp); - ui->isColorSpaceXYZ->setEnabled(temp); - ui->isColorSpaceRGBOld->setEnabled(temp); - ui->isColorSpaceAi->setEnabled(temp); - ui->AllowDither->setEnabled(temp); - on_ShowAdjed_clicked(); - updateEnables(); -} - -void MainWindow::on_ShowRaw_clicked() { - ui->ShowPic->setPixmap(QPixmap::fromImage(rawPic)); -} - -void MainWindow::on_ShowAdjed_clicked() { - EImage ei(kernel->getImageRows(), kernel->getImageCols()); - // int a, b; - kernel->getConvertedImage(nullptr, nullptr, ei.data()); - ui->ShowPic->setPixmap(QPixmap::fromImage(EImage2QImage(ei))); -} - -void MainWindow::on_ExData_clicked() { - int mapRows = ceil(kernel->getImageRows() / 128.0); - int mapCols = ceil(kernel->getImageCols() / 128.0); - int mapCounts = mapRows * mapCols; - ui->ShowDataRows->setText(QString::number(mapRows)); - ui->ShowDataCols->setText(QString::number(mapCols)); - ui->ShowDataCounts->setText(QString::number(mapCounts)); - ui->InputDataIndex->setText("0"); -} - -void MainWindow::on_ExportFlatDiagram_clicked() { - const QString path = - QFileDialog::getSaveFileName(this, "保存为平面示意图", "", "*.png"); - - if (path.isEmpty()) return; - - ui->ExportFlatDiagram->setEnabled(false); - ui->progressExFlatDiagram->setEnabled(false); - ui->ExportLite->setEnabled(false); - ui->progressExLite->setEnabled(false); - - this->proTracker = ui->ShowProgressExLite; - // ui->ShowProgressExLite->setValue(0); - - constexpr int charHeight = 14; - constexpr int charWidth = 10; - constexpr int charSpace = 1; - constexpr int leftSpace = 2; - constexpr int topSpace = 2; - - /* - here [] means an blank pixel - left border : [][]char[]char[]...char[]Blocks - - top border : - [] - [] - char - [] - char - [] - ... - char - [] - Blocks - */ - - const int buildRows = kernel->getZRange(); - const int buildCols = kernel->getXRange(); - - /// pixel rows and cols of single block - constexpr int blockRowsCols = 16; - - const int numberRowsDigits = std::ceil(std::log10(1e-2 + buildRows)); - const int numberColsDigits = std::ceil(std::log10(1e-2 + buildCols)); - - const int leftBorderWidth = - leftSpace + numberColsDigits * (charWidth + charSpace); - const int topBorderWidth = - topSpace + numberRowsDigits * (charHeight + charSpace); - - /// the height of exported image - const int diagramRows = blockRowsCols * buildRows + topBorderWidth; - /// the width of exported image - const int diagramCols = blockRowsCols * buildCols + leftBorderWidth; - - // progress bar range: 0~ rows + cols + pixNum + rows + cols + pixNum/10 - const int progressMax = buildRows + buildCols + buildRows * buildCols + - buildRows + buildCols + buildRows * buildCols / 10; - // ui->ShowProgressExLite->setRange(0,progressMax); - progressRangeSet(this, 0, progressMax, 0); - - using blockEImg_t = - Eigen::Array; - std::vector blockImgs16; - blockImgs16.reserve(64); - - // fill blockImgs16 with images of each block - { - auto tokiBlockList = Manager->getTokiBlockList(); - for (auto tokiBlock : tokiBlockList) { - if (tokiBlock == nullptr) { - break; - } - QImage tempImg = tokiBlock->getTarget() - ->icon() - .pixmap(blockRowsCols, blockRowsCols) - .toImage() - .convertToFormat(QImage::Format_ARGB32); - Eigen::Map map( - reinterpret_cast(tempImg.scanLine(0))); - SlopeCraft::SCL_preprocessImage(map.data(), map.size()); - blockImgs16.emplace_back(map); - } - } - - using charEImg_t = - Eigen::Array; - - std::vector numberImgs(10); - // fill numberImgs with images of each number - { - QImage tempImg = QImage(":/new/Pic/MCStyledNumbers.png") - .convertToFormat(QImage::Format_ARGB32); - Eigen::Map< - Eigen::Array> - mappedTempImg(reinterpret_cast(tempImg.scanLine(0)), - tempImg.height(), tempImg.width()); - for (int idx = 0; idx < 10; idx++) { - numberImgs[idx] = - mappedTempImg.block(0, idx * charWidth); - } - } - // cout<<"Size of blockImgs16 = "< - EDiagram; - - EDiagram.setConstant(diagramRows, diagramCols, 0xFFFFFFFFU); - - // write row numbers - for (int row = 0; row < buildRows; row++) { - const std::string str = std::to_string(row); - - const int rowOffset = - topBorderWidth + blockRowsCols * row + (blockRowsCols - charHeight) / 2; - // cout<<"\n\nrowOffset = "<= (int)str.size()) break; - - // the reverse idx - const char curChar = str[str.size() - 1 - invIdx]; - const int curCharIdx = curChar - '0'; - - const int colOffset = - leftBorderWidth - (invIdx + 1) * (charWidth + charSpace); - - // cout<<"colOffset = "<(rowOffset, colOffset) = - numberImgs[curCharIdx]; - } - } - - progressAdd(this, buildRows); - keepAwake(this); - // cout<= (int)str.size()) break; - const int curCharIdx = str[str.size() - 1 - invIdx] - '0'; - - const int rowOffset = - topBorderWidth - (invIdx + 1) * (charHeight + charSpace); - EDiagram.block(rowOffset, colOffset) = - numberImgs[curCharIdx]; - } - } - - progressAdd(this, buildCols); - keepAwake(this); - - const Eigen::TensorMap> build( - kernel->getBuild(), kernel->getXRange(), kernel->getHeight(), - kernel->getZRange()); - - static constexpr int reportInterval = 512; - - // fill diagram with blocks - for (int bRow = 0; bRow < buildRows; bRow++) { - constexpr int yPos = 0; - const int rowOffset = topBorderWidth + blockRowsCols * bRow; - const int zPos = bRow; - for (int bCol = 0; bCol < buildCols; bCol++) { - const int xPos = bCol; - const int eleIdx = &build(xPos, yPos, zPos) - build.data(); - - if (eleIdx % reportInterval == 0) { - progressAdd(this, reportInterval); - keepAwake(this); - } - - if (build(xPos, yPos, zPos) <= 0) continue; - - const int blockIdx = build(xPos, yPos, zPos) - 1; - - const int colOffset = leftBorderWidth + blockRowsCols * bCol; - - EDiagram.block(rowOffset, colOffset) = - blockImgs16[blockIdx]; - } - } - - // draw horizontal lines - for (int lineRows = 1; lineRows < buildRows; lineRows += 16) { - const int row = topBorderWidth + blockRowsCols * lineRows; - for (int col = leftBorderWidth; col < diagramCols; col++) { - EDiagram(row, col) = inverseColor(EDiagram(row, col)); - } - } - progressAdd(this, buildRows); - - // draw vertical lines - for (int lineCols = 1; lineCols < buildCols; lineCols += 16) { - const int col = leftBorderWidth + blockRowsCols * lineCols; - for (int row = topBorderWidth; row < diagramRows; row++) { - // if((row-topBorderWidth)%(blockRowsCols*16)==0) continue; - - EDiagram(row, col) = inverseColor(EDiagram(row, col)); - } - } - progressAdd(this, buildCols); - keepAwake(this); - - QImage(reinterpret_cast(EDiagram.data()), diagramCols, diagramRows, - QImage::Format_ARGB32) - .save(path); - - progressRangeSet(this, 0, progressMax, progressMax); - keepAwake(this); - - this->proTracker = nullptr; - - ui->FinishExLite->setEnabled(true); - - this->ProductDir = path; - - ProductDir = ProductDir.replace('\\', '/'); - ProductDir = ProductDir.left(ProductDir.lastIndexOf('/')); - - ui->seeExported->setEnabled(true); - - updateEnables(); -} - -// Page5 - -void MainWindow::on_Build4Lite_clicked() { - bool naturalCompress = ui->AllowNaturalOpti->isChecked(); - bool forcedCompress = ui->AllowForcedOpti->isChecked(); - SlopeCraft::compressSettings cS; - if (naturalCompress) { - if (forcedCompress) - cS = SlopeCraft::compressSettings::Both; - else - cS = SlopeCraft::compressSettings::NaturalOnly; - } else { - if (forcedCompress) - cS = SlopeCraft::compressSettings::ForcedOnly; - else - cS = SlopeCraft::compressSettings::noCompress; - } - - bool allowBridge = ui->allowGlassBridge->isChecked(); - SlopeCraft::glassBridgeSettings gBS = - allowBridge ? SlopeCraft::glassBridgeSettings::withBridge - : SlopeCraft::glassBridgeSettings::noBridge; - - kernel->decreaseStep(SlopeCraft::step::converted); - ui->ExportLite->setEnabled(false); - ui->FinishExLite->setEnabled(false); - ui->ManualPreview->setEnabled(false); - - proTracker = ui->ShowProgressExLite; - qDebug() << "ui->maxHeight->value()=" << ui->maxHeight->value(); - kernel->build( - cS, ui->maxHeight->value(), gBS, ui->glassBridgeInterval->value(), - ui->allowAntiFire->isChecked(), ui->allowAntiEnderman->isChecked()); - - int size3D[3], total; - - kernel->get3DSize(&size3D[0], &size3D[1], &size3D[2]); - total = kernel->getBlockCounts(); - ui->ShowLiteBlocks->setText(QString::number(total)); - ui->ShowLiteXYZ->setText(QString::fromStdString( - "X:" + std::to_string(size3D[0]) + " × Y:" + std::to_string(size3D[1]) + - " × Z:" + std::to_string(size3D[2]))); - proTracker = nullptr; - updateEnables(); - if (!isBatchOperating) showPreview(); -} - -void MainWindow::on_ManualPreview_clicked() { showPreview(); } - -void MainWindow::on_ExportLite_clicked() { onExportLiteclicked(""); } - -void MainWindow::onExportLiteclicked(QString path) { - std::string FileName; - if (path.isEmpty()) { - QStringList suffixes({tr("投影文件 (*.litematic)"), - tr("结构方块文件 (*.nbt)"), - tr("WorldEdit 原理图 (*.schem)")}); - - const int first_format_idx = ui->tabExport3DInfo->currentIndex(); - - QString suffix = suffixes[first_format_idx]; - - for (int idx = 0; idx < suffixes.size(); idx++) { - if (idx != first_format_idx) { - suffix += ";;"; - suffix += suffixes[idx]; - } - } - - FileName = QFileDialog::getSaveFileName(this, tr("导出为投影/结构方块文件"), - "", suffix) - .toLocal8Bit() - .data(); - } else { - FileName = path.toLocal8Bit().data(); - } - // std::string unCompressed; - char failed_file_name[512] = ""; - if (FileName.empty()) return; - const bool putLitematic = - (FileName.substr(FileName.length() - strlen(".litematic")) == - ".litematic"); - const bool putStructure = - (FileName.substr(FileName.length() - strlen(".nbt")) == ".nbt"); - const bool putWESchem = - (FileName.substr(FileName.length() - strlen(".schem")) == ".schem"); - - if (!putLitematic && !putStructure && !putWESchem) { - qDebug("Invalid save file name : "); - qDebug() << FileName.data(); - return; - } - // qDebug("开始导出投影"); - // cout << FileName << endl; - - ui->FinishExLite->setEnabled(false); - ui->seeExported->setEnabled(false); - ui->Build4Lite->setEnabled(false); - - this->proTracker = ui->ShowProgressExLite; - - if (putStructure) - kernel->exportAsStructure(FileName.data(), failed_file_name); - else if (putLitematic) - kernel->exportAsLitematic( - FileName.data(), ui->InputLiteName->text().toUtf8().data(), - (ui->InputRegionName->text() + tr("(xz 坐标=-65±128×整数)")) - .toUtf8() - .data(), - failed_file_name); - - else { - int offset[3] = {0, 0, 0}, weOffset[3] = {0, 0, 0}; - QString dependModsListString = ui->schem_required_mods->toPlainText(); - QStringList modList = dependModsListString.split('\n'); - - std::vector stdStrList(modList.size()); - std::vector charPtrs; - for (int idx = 0; idx < int(stdStrList.size()); idx++) { - stdStrList[idx] = modList[idx].toUtf8().data(); - charPtrs.emplace_back(stdStrList[idx].data()); - } - - const std::array offsetSrc( - {ui->schem_offsetX, ui->schem_offsetY, ui->schem_offsetZ}); - const std::array weOffsetSrc( - {ui->schem_weOffsetX, ui->schem_weOffsetY, ui->schem_weOffsetZ}); - - for (int d = 0; d < 3; d++) { - bool ok = true; - int result = offsetSrc[d]->text().toInt(&ok); - if (ok) offset[d] = result; - - result = weOffsetSrc[d]->text().toInt(&ok); - if (ok) weOffset[d] = result; - } - - kernel->exportAsWESchem(FileName.data(), offset, weOffset, - ui->schem_name->text().toUtf8().data(), - charPtrs.data(), charPtrs.size(), failed_file_name); - } - - if (std::strlen(failed_file_name) <= 0) { - // success - // qDebug("压缩成功"); - ProductDir = QString::fromLocal8Bit(FileName.data()); - ProductDir = ProductDir.replace('\\', '/'); - ProductDir = ProductDir.left(ProductDir.lastIndexOf('/')); - - qDebug() << "ProductDir=" << ProductDir; - - } else { - qDebug("Failed to export."); - QMessageBox::warning( - this, tr("投影文件导出失败"), - tr("这可能是汉字编码错误造成的。请检查路径中是否有汉字") + '\n' + - tr("错误信息:") + '\n' + failed_file_name); - return; - }; - - ui->FinishExLite->setEnabled(true); - ui->seeExported->setEnabled(true); - ui->Build4Lite->setEnabled(true); - - updateEnables(); - this->proTracker = nullptr; - // success - // qDebug("导出为投影成功"); - return; -} - -void MainWindow::on_InputDataIndex_textChanged() { - bool isIndexValid = false; - const int indexStart = ui->InputDataIndex->toPlainText().toInt(&isIndexValid); - isIndexValid = isIndexValid && (indexStart >= 0); - if (isIndexValid) { - if (ceil(kernel->getImageRows() / 128.0f) == 1 && - ceil(kernel->getImageCols() / 128.0f) == 1) - ui->ShowDataFileName->setText("map_" + QString::number(indexStart) + - ".dat"); - else - ui->ShowDataFileName->setText( - "map_" + QString::number(indexStart) + ".dat" + "~" + "map_" + - QString::number(indexStart + - ceil(kernel->getImageRows() / 128.0f) * - ceil(kernel->getImageRows() / 128.0f) - - 1) + - ".dat"); - ui->ExportData->setEnabled(true); - return; - } - - ui->ShowDataFileName->setText( - tr("你输入的起始序号不可用,请输入大于等于 0 的整数!")); - ui->ExportData->setEnabled(false); - return; -} - -void MainWindow::on_ExportData_clicked() { onExportDataclicked(""); } - -void MainWindow::onExportDataclicked(QString path) { - bool isIndexValid = false; - const int indexStart = ui->InputDataIndex->toPlainText().toInt(&isIndexValid); - isIndexValid = isIndexValid && (indexStart >= 0); - if (!isIndexValid) { - QMessageBox::information(this, tr("你输入的起始序号不可用"), - tr("请输入大于等于 0 的整数!")); - return; - } - QString FolderPath; - - if (path.isEmpty()) - FolderPath = (QFileDialog::getExistingDirectory( - this, tr("请选择导出的文件夹"), this->prevOpenedDir)); - else - FolderPath = path; - - if (FolderPath.isEmpty()) { - QMessageBox::information(this, tr("你选择的文件夹不存在!"), - tr("你可以选择存档中的 data 文件夹")); - return; - } - - this->prevOpenedDir = FolderPath; - - ui->InputDataIndex->setEnabled(false); - ui->ExportData->setEnabled(false); - ui->FinshExData->setEnabled(false); - ui->ExportData->setText(tr("请稍等")); - - proTracker = ui->ShowProgressExData; - - FolderPath = FolderPath.replace('\\', '/'); - ProductDir = FolderPath; - const uint32_t fileNum = ceil(kernel->getImageRows() / 128.0f) * - ceil(kernel->getImageRows() / 128.0f); - std::vector unCompressedBuffers(fileNum); - for (auto &i : unCompressedBuffers) { - i = new char[512]; - } - // int fileCount=0; - kernel->exportAsData(FolderPath.toLocal8Bit().data(), indexStart, - //&fileCount,unCompressedBuffers.data() - nullptr, nullptr); - // qDebug("导出地图文件成功"); - - ui->InputDataIndex->setEnabled(true); - ui->ExportData->setEnabled(true); - ui->FinshExData->setEnabled(true); - ui->ExportData->setText(tr("导出")); - proTracker = nullptr; - updateEnables(); -} - -void MainWindow::turnCh() { switchLan(Language::ZH); } - -void MainWindow::turnEn() { switchLan(Language::EN); } - -void MainWindow::switchLan(Language lang) { - emit Manager->translate(lang); - - if (lang == Language::EN) { - QString msg{""}; - - if (!this->trans_SC.load(":/i18n/SlopeCraft_en_US.qm")) { - msg += QStringLiteral("Failed to load translation file \"%1\"\n") - .arg(":/i18n/SlopeCraft_en_US.qm"); - } - if (!this->trans_BLM.load(":/i18n/BlockListManager_en_US.qm")) { - msg += QStringLiteral("Failed to load translation file \"%1\"\n") - .arg(":/i18n/BlockListManager_en_US.qm"); - } - if (!this->trans_VD.load(":/i18n/VersionDialog_en_US.qm")) { - msg += QStringLiteral("Failed to load translation file \"%1\"\n") - .arg(":/i18n/VersionDialog_en_US.qm"); - } - - if (!msg.isEmpty()) { - QMessageBox::warning(this, "Failed to load translation.", msg); - return; - } - - qApp->installTranslator(&this->trans_SC); - qApp->installTranslator(&this->trans_BLM); - qApp->installTranslator(&this->trans_VD); - ui->retranslateUi(this); - // qDebug("Changed language to English"); - } else { - qApp->removeTranslator(&this->trans_SC); - qApp->removeTranslator(&this->trans_BLM); - qApp->removeTranslator(&this->trans_VD); - ui->retranslateUi(this); - // qDebug("Changed language to Chinese"); - } - return; -} - -void MainWindow::on_allowGlassBridge_stateChanged(int arg1) { - ui->glassBridgeInterval->setEnabled(arg1); -} - -void MainWindow::showError(void *p, SlopeCraft::errorFlag error, - const char *msg) { - MainWindow *wind = (MainWindow *)p; - QString title, text; - bool isFatal = false; - - const QString detail = - (msg == nullptr) ? ("") - : (tr("\n具体信息:") + QString::fromStdString(msg)); - - switch (error) { - case SlopeCraft::errorFlag::NO_ERROR_OCCUR: - return; - case SlopeCraft::errorFlag::UNKNOWN_MAJOR_GAME_VERSION: - title = tr("未知游戏版本"); - text = detail; - break; - case SlopeCraft::errorFlag::EXPORT_SCHEM_BLOCK_PALETTE_OVERFLOW: - title = tr("导出原理图失败"); - text = tr("方块种类超出上限。") + detail; - break; - case SlopeCraft::errorFlag::EXPORT_SCHEM_WRONG_EXTENSION: - title = tr("导出原理图失败"); - text = tr("错误的文件扩展名") + detail; - break; - case SlopeCraft::errorFlag::EXPORT_SCHEM_HAS_INVALID_BLOCKS: - title = tr("导出原理图失败"); - text = tr("三维结构中存在错误的方块") + detail; - break; - case SlopeCraft::errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE: - title = tr("导出原理图失败"); - text = tr("无法创建/打开文件") + detail; - break; - case SlopeCraft::errorFlag::EXPORT_SCHEM_MC12_NOT_SUPPORTED: - title = tr("导出 WorldEdit 原理图失败"); - text = tr("不支持导出 1.12 " - "WorldEdit 原理图(.schematic 格式),仅支持.schem 格式") + - detail; - break; - case SlopeCraft::errorFlag::EXPORT_SCHEM_STRUCTURE_REQUIRES_AIR: - title = tr("导出原版结构方块文件失败"); - text = tr("导出时指定不使用结构空位表示空气,但方块列表中不包含空气。") + - detail; - break; - - case SlopeCraft::errorFlag::EMPTY_RAW_IMAGE: - title = tr("转化原图为地图画时出错"); - text = tr("原图为空!你可能没有导入原图!"); - break; - case SlopeCraft::errorFlag::DEPTH_3_IN_VANILLA_MAP: - title = tr("构建高度矩阵时出现错误"); - text = tr( - "原版地图画不允许出现第三个阴影(不存在的几何关系不可能生存实装!)\n" - "请检查你的地图画类型,纯文件地图画不可以导出为投影!"); - break; - case SlopeCraft::errorFlag::HASTY_MANIPULATION: - title = tr("跳步操作"); - text = tr("SlopeCraft 不允许你跳步操作,请按照左侧竖边栏的顺序操作!"); - break; - case SlopeCraft::errorFlag::LOSSYCOMPRESS_FAILED: - title = tr("有损压缩失败"); - text = tr( - "在构建高度矩阵时,有损压缩失败,没能将地图画压缩到目标高度。 \ - 这可能是因为地图画行数过大。 \ - 尝试启用无损压缩,或者提高最大允许高度"); - break; - case SlopeCraft::errorFlag::MAX_ALLOWED_HEIGHT_LESS_THAN_14: - title = tr("最大允许高度太小了"); - text = tr("有损压缩的最大允许不要低于 14,否则很容易压缩失败"); - break; - case SlopeCraft::errorFlag::USEABLE_COLOR_TOO_FEW: - title = tr("允许使用的颜色过少"); - text = tr("你应该勾选启用尽可能多的基色,颜色太少是不行的!"); - break; - case SlopeCraft::errorFlag::FAILED_TO_COMPRESS: - title = tr("导出时 Gzip 压缩文件失败"); - text = tr("这可能是因为路径中含有中文字符!"); - break; - case SlopeCraft::errorFlag::FAILED_TO_REMOVE: - title = tr("删除临时文件失败"); - text = tr("这可能是因为路径中含有中文字符!"); - break; - } - if (isFatal) - QMessageBox::warning(wind, title, text + detail, - QMessageBox::StandardButton::Ok, - QMessageBox::StandardButton::NoButton); - else { - QMessageBox::critical(wind, title, text + detail, - QMessageBox::StandardButton::Close); - emit wind->ui->Exit->clicked(); - } - wind->updateEnables(); - return; -} - -void MainWindow::showWorkingStatue(void *p, SlopeCraft::workStatues statue) { - MainWindow *wind = (MainWindow *)p; - QString title = wind->windowTitle(); - const char spacer[] = " | "; - if (title.contains(spacer)) { - title = title.left(title.lastIndexOf(spacer)); - } - - if (statue != SlopeCraft::workStatues::none) title += spacer; - - switch (statue) { - case SlopeCraft::workStatues::none: - break; - case SlopeCraft::workStatues::buidingHeighMap: - title += tr("正在构建高度矩阵"); - break; - case SlopeCraft::workStatues::building3D: - title += tr("正在构建三维结构"); - break; - case SlopeCraft::workStatues::collectingColors: - title += tr("正在收集整张图片的颜色"); - break; - case SlopeCraft::workStatues::compressing: - title += tr("正在压缩立体地图画"); - break; - case SlopeCraft::workStatues::constructingBridges: - title += tr("正在为立体地图画搭桥"); - break; - case SlopeCraft::workStatues::converting: - title += tr("正在匹配颜色"); - break; - case SlopeCraft::workStatues::dithering: - title += tr("正在使用抖动仿色"); - break; - case SlopeCraft::workStatues::flippingToWall: - title += tr("正在将平板地图画变为墙面地图画"); - break; - case SlopeCraft::workStatues::writing3D: - title += tr("正在写入三维结构"); - break; - case SlopeCraft::workStatues::writingBlockPalette: - title += tr("正在写入方块列表"); - break; - case SlopeCraft::workStatues::writingMapDataFiles: - title += tr("正在写入地图数据文件"); - break; - case SlopeCraft::workStatues::writingMetaInfo: - title += tr("正在写入基础信息"); - break; - } - - wind->setWindowTitle(title); - return; -} - -void MainWindow::on_seeExported_clicked() { - if (ProductDir.isEmpty()) { - return; - } - qDebug() << "ProductDir=" << ProductDir; - QDesktopServices::openUrl(QUrl::fromLocalFile(ProductDir)); -} - -void MainWindow::on_AllowForcedOpti_stateChanged(int arg1) { - ui->maxHeight->setEnabled(arg1); -} - -void MainWindow::on_reportBugs_clicked() { - QUrl url("https://github.com/SlopeCraft/SlopeCraft/issues/new/choose"); - QDesktopServices::openUrl(url); -} - -void MainWindow::checkVersion() { - // QtConcurrent::run(grabVersion,this); - grabVersion(false); - return; -} - -void MainWindow::grabVersion(bool isAuto) { - VersionDialog::start_network_request( - this, "SlopeCraft", - QUrl("https://api.github.com/repos/SlopeCraft/SlopeCraft/releases"), - networkManager(), !isAuto); -} - -void MainWindow::onBlockListChanged() { - // qDebug("onBlockListChanged"); - if (kernel->queryStep() < SlopeCraft::step::nothing) { - return; - } - - this->kernelSetType(); - if (!this->rawPic.isNull()) { - this->kernelSetImg(); - } - this->updateEnables(); - - ushort colorCount = kernel->getColorCount(); - ui->IntroColorCount->setText(tr("可用") + QString::number(colorCount) + - tr("种颜色")); -} - -void MainWindow::closeEvent(QCloseEvent *event) { - emit closed(); - qDebug("closed Signal emitted"); - QMainWindow::closeEvent(event); - exit(0); -} - -void MainWindow::keyPressEvent(QKeyEvent *event) { - switch (event->key()) { - case Qt::Key::Key_F5: { - QString destName = QFileDialog::getSaveFileName( - this, tr("保存截屏"), "", - tr("图片 (*.jpg *.jpeg *.tif *.bmp *.png)")); - - if (destName.isEmpty()) { - break; - } - - QPixmap pix = this->grab(); - pix.save(destName); - break; - } - default: - break; - } - - QMainWindow::keyPressEvent(event); -} - -void MainWindow::on_ExImage_clicked() { - QPixmap image = ui->ShowPic->pixmap(); - - if (image.isNull()) { - return; - } - - QString savePath = QFileDialog::getSaveFileName(this, tr("保存当前显示图片"), - "./", tr("图片 (*.png)")); - - if (savePath.isEmpty()) { - return; - } - - image.save(savePath); -} - -void MainWindow::selectBlockByString(const std::string &key) { - std::vector tbcs; - Manager->getTokiBaseColors(&tbcs); - - for (uint8_t baseColor = 0; baseColor < tbcs.size(); baseColor++) { - std::vector tbs; - tbcs[baseColor]->getTokiBlockList(tbs); - for (uint16_t idx = 0; idx < tbs.size(); idx++) { - std::string curBlockId = tbs[idx]->getSimpleBlock()->getId(); - if (curBlockId.find(key) != std::string::npos) { - Manager->setSelected(baseColor, idx); - continue; - } - } - } -} - -void MainWindow::onActionSavePreset() { - QString dst = QFileDialog::getSaveFileName( - this, tr("保存预设"), "", - QStringLiteral("*") + MainWindow::sc_preset_extension); - if (dst.isEmpty()) return; - - auto preset = this->Manager->currentPreset(); - - QString content = serialize_preset(preset); - - QFile ofile(dst); - if (!ofile.open(QFile::OpenMode::enum_type::WriteOnly)) { - QMessageBox::warning(this, tr("保存预设文件失败"), - tr("无法创建文件\"%1\"").arg(dst)); - return; - } - - ofile.write(content.toUtf8()); - ofile.close(); -} - -void MainWindow::onActionLoadPreset() { - QString src = QFileDialog::getOpenFileName( - this, tr("选择预设文件"), this->prevOpenedDir, - QStringLiteral("*") + MainWindow::sc_preset_extension); - if (src.isEmpty()) return; - - this->prevOpenedDir = QFileInfo(src).filePath(); - QString err; - auto preset = load_preset(src, err); - if (!err.isEmpty()) { - QMessageBox::warning(this, tr("加载预设失败"), err); - return; - } - - this->Manager->loadPreset(preset); -} - -void MainWindow::onActionAiCvterParameters() { - auto acpDialog = new AiCvterParameterDialog(this); - acpDialog->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); - acpDialog->show(); -} - -void MainWindow::on_FirstConcrete_clicked() { selectBlockByString("concrete"); } - -void MainWindow::on_FirstWool_clicked() { selectBlockByString("wool"); } - -void MainWindow::on_FirstStainedGlass_clicked() { - selectBlockByString("stained_glass"); -} - -void MainWindow::testBlockList() { - std::vector ptr_buffer; - std::vector base_buffer; - - const int buffSize = Manager->getBlockNum(); - ptr_buffer.resize(buffSize + 1); - base_buffer.resize(buffSize + 1); - - Manager->getBlockPtrs(ptr_buffer.data(), base_buffer.data()); - - QString targetName = QFileDialog::getSaveFileName( - this, tr("测试方块列表的结构文件"), "", "*.nbt"); - if (targetName.isEmpty()) { - return; - } - - char failed_file_name[512] = ""; - kernel->makeTests(ptr_buffer.data(), base_buffer.data(), - targetName.toLocal8Bit().data(), failed_file_name); - if (std::string_view(failed_file_name).empty()) { - // cerr<<"Success"<tabExport3DInfo->setCurrentIndex(0); -} - -void MainWindow::on_ExStructure_clicked() { - ui->tabExport3DInfo->setCurrentIndex(1); -} - -void MainWindow::on_ExWESchem_clicked() { - ui->tabExport3DInfo->setCurrentIndex(2); -} - -void MainWindow::exportAvailableColors() { - constexpr int basecolors_per_row = 4; - constexpr int basecolors_per_col = 16; - - static_assert(basecolors_per_row * basecolors_per_col == 64); - - constexpr int row_pixels = basecolors_per_row * 4; - constexpr int col_pixels = basecolors_per_col * 1; - - static_assert(row_pixels * col_pixels == 256); - - const QString dest_file = - QFileDialog::getSaveFileName(this, tr("保存颜色表"), "", "*.png"); - - if (dest_file.isEmpty()) { - return; - } - - QImage img(row_pixels, col_pixels, QImage::Format::Format_ARGB32); - - img.fill(0x00FFFFFFU); - - if (img.isNull()) { - std::cout << "File " << __FILE__ << ", Line " << __LINE__ - << ", the image is null, memory allocation failed" << endl; - return; - } - - Eigen::Map< - Eigen::Array> - map(reinterpret_cast(img.scanLine(0)), row_pixels, - col_pixels); - - uint32_t argb_colors[256]; - uint8_t map_colors[256]; - const int available_colors = kernel->getColorCount(); - kernel->getAvailableColors(argb_colors, map_colors); - - for (int cidx = 0; cidx < available_colors; cidx++) { - /* - const int basecolor = (map_colors[cidx] / 4); - const int shade = (map_colors[cidx] % 4); - const int pixel_row = basecolor / basecolors_per_col; - const int pixel_col = (basecolor % basecolors_per_col) * 4 + shade; - */ - map(map_colors[cidx]) = argb_colors[cidx]; - } - - if (img.save(dest_file)) { - this->ProductDir = dest_file; - } - return; -} - -QNetworkAccessManager &MainWindow::networkManager() noexcept { - static QNetworkAccessManager manager; - return manager; -} \ No newline at end of file diff --git a/SlopeCraftMain/MainWindow.h b/SlopeCraftMain/MainWindow.h deleted file mode 100644 index 8718740b..00000000 --- a/SlopeCraftMain/MainWindow.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -// #define dispDerivative -// #define putDitheredImg -// #define putMapData -// #define putBlockList -#include "ui_mainwindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -// #define SLOPECRAFTL_WITH_AICVETR -#include "SlopeCraftL.h" - -#include "AiCvterParameterDialog.h" -#include "BatchUi.h" -#include "BlockListManager.h" -#include "previewwind.h" -#include "tpstrategywind.h" - -#include - -class MainWindow; - -using std::cout, std::endl, std::cerr; - -#include -#include - -// class ColorSet; - -QT_BEGIN_NAMESPACE -namespace Ui { -class MainWindow; -} -QT_END_NAMESPACE - -class tpStrategyWind; - -#ifndef TPS__ -#define TPS__ -class tpS { - public: - tpS(char _pTpS = 'B', char _hTpS = 'C', QRgb _BGC = qRgb(220, 220, 220)) { - pTpS = _pTpS; - hTpS = _hTpS; - BGC = _BGC; - } - ~tpS(); - char pTpS; - char hTpS; - QRgb BGC; -}; -#endif - -class MainWindow : public QMainWindow { - Q_OBJECT - friend class BatchUi; - - public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - - // 初始化方块列表用 - void InitializeAll(); - - static constexpr char sc_preset_extension[] = ".sc_preset_json"; - - signals: - void mapTypeChanged(); - - void closed(); - - protected: - void closeEvent(QCloseEvent *) override; - void keyPressEvent(QKeyEvent *) override; - - public: - inline void preProcess(char pureTpStrategy = 'B', char halfTpStrategy = 'C', - QRgb BGC = qRgb(220, 220, 220)); - - inline auto kernelPtr() const { return kernel; } - - static QNetworkAccessManager &networkManager() noexcept; - - public slots: - void ReceiveTPS(tpS); - // 透明像素处理策略:B->替换为背景色;A->空气;W->暂缓,等待处理 - // 半透明像素处理策略:B->替换为背景色;C->与背景色叠加;R->保留颜色;W->暂缓,等待处理 - - void showPreview(); - // 语言槽 - void turnCh(); - void turnEn(); - - void checkVersion(); - - void grabVersion(bool isAuto = true); - - private slots: - - // void when_network_finished(QNetworkReply *reply, bool is_manually); - - void contactG(); - void contactB(); - // 翻页的自定义槽 - void turnToPage0(); - void turnToPage1(); - void turnToPage2(); - void turnToPage3(); - void turnToPage4(); - void turnToPage5(); - void turnToPage6(); - void turnToPage7(); - void turnToPage8(); - - // for Page0 - void on_StartWithSlope_clicked(); - void on_StartWithFlat_clicked(); - void on_StartWithNotVanilla_clicked(); - - // for Page1 - void on_ImportPic_clicked(); - void on_ImportSettings_clicked(); - - // for Page2 - void onGameVerClicked(); - void onMapTypeClicked(); - - // for Page3 - // 应用预设方块列表的自定义槽 - void ChangeToCustom(); - void onPresetsClicked(); - void onBlockListChanged(); - - void onActionSavePreset(); - void onActionLoadPreset(); - - void exportAvailableColors(); - - // for Page4 - void onAlgoClicked(); - void on_Convert_clicked(); - void on_ShowRaw_clicked(); - void on_ShowAdjed_clicked(); - void on_ExData_clicked(); - void on_ExportFlatDiagram_clicked(); - // void on_ExFlatDiagram_clicked(); - - void onActionAiCvterParameters(); - - void on_ExLite_clicked(); - - void on_ExStructure_clicked(); - - void on_ExWESchem_clicked(); - - // for Page5 - void on_Build4Lite_clicked(); - void on_ManualPreview_clicked(); - void on_ExportLite_clicked(); - void onExportLiteclicked(QString); - void on_allowGlassBridge_stateChanged(int arg1); - - // for Page7 - void on_InputDataIndex_textChanged(); - void on_ExportData_clicked(); - void onExportDataclicked(QString); - - void on_seeExported_clicked(); - - void on_AllowForcedOpti_stateChanged(int arg1); - - void on_reportBugs_clicked(); - - // void on_StartWithWall_clicked(); - - void on_ExImage_clicked(); - - void on_FirstConcrete_clicked(); - - void on_FirstWool_clicked(); - - void on_FirstStainedGlass_clicked(); - - void testBlockList(); - - private: - Ui::MainWindow *ui; - - SlopeCraft::Kernel *const kernel; - BlockListManager *Manager; - - QImage rawPic; - - static bool isBatchOperating; - - static blockListPreset preset_vanilla; - static blockListPreset preset_elegant; - static blockListPreset preset_cheap; - static blockListPreset preset_shiny; - - tpS Strategy; - - QString prevOpenedDir{"./"}; - QString ProductDir; - QTranslator trans_SC; - QTranslator trans_BLM; - QTranslator trans_VD; - bool Collected; - QProgressBar *proTracker; - - // void applyPre(ushort*BL); - - private: - void loadBlockList(); - void turnToPage(int); - void updateEnables(); - void preprocessImage(const QString &); - void switchLan(Language); - void kernelSetType(); - void kernelSetImg(); - - void selectBlockByString(const std::string &); - - static void progressRangeSet(void *, int min, int max, - int val); // 设置进度条的取值范围和值 - static void progressAdd(void *, int deltaVal); - static void keepAwake(void *); - - static void showError(void *, SlopeCraft::errorFlag, const char *errInfo); - static void showWorkingStatue(void *, SlopeCraft::workStatues); - - static void algoProgressRangeSet(void *, int min, int max, int val); - static void algoProgressAdd(void *, int deltaVal); -}; - -using EImage = Eigen::ArrayXX; - -EImage QImage2EImage(const QImage &); -QImage EImage2QImage(const EImage &, ushort scale = 1); -#endif // MAINWINDOW_H diff --git a/SlopeCraftMain/TaskBox.cpp b/SlopeCraftMain/TaskBox.cpp deleted file mode 100644 index b337c601..00000000 --- a/SlopeCraftMain/TaskBox.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TaskBox.h" - -TaskType TaskBox::taskType = TaskType::Litematica; - -TaskBox::TaskBox(QWidget *parent) : QWidget(parent), ui(new Ui::TaskBox) { - ui->setupUi(this); - mapCols = 0; - mapRows = 0; -} - -TaskBox::~TaskBox() { delete ui; } - -void TaskBox::on_browseImage_clicked() { - QString imgPath = QFileDialog::getOpenFileName( - this, tr("选择图片"), "./", tr("图片(*.png *.bmp *.jpg *.tif *.GIF )")); - if (imgPath.isEmpty()) - return; - setTask(imgPath); -} diff --git a/SlopeCraftMain/TaskBox.h b/SlopeCraftMain/TaskBox.h deleted file mode 100644 index efb28c86..00000000 --- a/SlopeCraftMain/TaskBox.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef TASKBOX_H -#define TASKBOX_H - -#include "ui_TaskBox.h" -#include -#include - -namespace Ui { -class TaskBox; -} - -class BatchUi; - -enum TaskType { Litematica, Structure, Data }; - -class TaskBox : public QWidget { - Q_OBJECT - // friend class BatchUi; -public: - explicit TaskBox(QWidget *parent = nullptr); - ~TaskBox(); - - static TaskType taskType; - - inline int32_t mapSize() const { return mapRows * mapCols; } - - inline void updateTaskType() { - ui->liteName->setText(rawImgPath() + liteSuffix()); - ui->DenoteLiteName->setEnabled(taskType != TaskType::Data); - ui->liteName->setEnabled(taskType != TaskType::Data); - ui->setMapBegSeq->setEnabled(taskType == TaskType::Data); - } - - void setTask(const QString &rawImg) { - QImage img; - int32_t prevMapSize = mapSize(); - if (!img.load(rawImg)) { - mapRows = 0; - mapCols = 0; - ui->preView->setPixmap(QPixmap()); - ui->preView->setText(tr("图片格式损坏")); - } - mapRows = std::ceil(img.height() / 128.0); - mapCols = std::ceil(img.width() / 128.0); - ui->preView->setPixmap(QPixmap::fromImage(img.scaledToWidth(128))); - ui->preView->setText(""); - ui->imageName->setText(rawImg); - - updateTaskType(); - - if (prevMapSize != mapSize() && taskType == Data) { - emit seqNumChanged(this); - } - } - - inline QString rawImgPath() const { return ui->imageName->text(); } - - inline QString liteName() const { return ui->liteName->text(); } - - inline int32_t begSeqNum() const { return ui->setMapBegSeq->value(); } - - inline static const char *liteSuffix() { - switch (taskType) { - case TaskType::Litematica: - return ".litematic"; - case TaskType::Structure: - return ".nbt"; - default: - return ".ERROR_TASK_TYPE"; - } - } - - inline void setMapBegSeqReadOnly(bool mbsro) { - ui->setMapBegSeq->setReadOnly(mbsro); - } - - inline void setMapBegSeq(int32_t mbs) { - ui->setMapBegSeq->setValue(mbs); - ui->setMapBegSeq->setSuffix(" ~ " + QString::number(mbs + mapSize() - 1)); - } - -signals: - void erase(TaskBox *); - void seqNumChanged(TaskBox *); - -private slots: - void on_imageName_editingFinished() { setTask(ui->imageName->text()); } - - void on_setMapBegSeq_valueChanged(int) { - if (!ui->setMapBegSeq->isReadOnly()) { - emit seqNumChanged(this); - } - } - - void on_BtnErase_clicked() { emit erase(this); } - - void on_browseImage_clicked(); - -private: - Ui::TaskBox *ui; - int16_t mapRows; - int16_t mapCols; -}; - -#endif // TASKBOX_H diff --git a/SlopeCraftMain/TaskBox.ui b/SlopeCraftMain/TaskBox.ui deleted file mode 100644 index c9b114cc..00000000 --- a/SlopeCraftMain/TaskBox.ui +++ /dev/null @@ -1,149 +0,0 @@ - - - TaskBox - - - - 0 - 0 - 500 - 132 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 6 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 浏览 - - - - - - - - 123 - 123 - - - - true - - - QFrame::StyledPanel - - - - - - true - - - - - - - - - - - 0 - 0 - - - - 投影/结构文件名 - - - - - - - 0 - - - - - × - - - Qt::NoArrow - - - - - - - - - true - - - ~ - - - 占用的地图序号: - - - 65535 - - - - - - - - 0 - 0 - - - - - 0 - 22 - - - - FileName - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - diff --git a/SlopeCraftMain/install.cmake b/SlopeCraftMain/install.cmake deleted file mode 100644 index 98b152c5..00000000 --- a/SlopeCraftMain/install.cmake +++ /dev/null @@ -1,140 +0,0 @@ - -file(GLOB SlopeCraft_install_jsons - "${CMAKE_SOURCE_DIR}/Blocks/*.json" - "${CMAKE_SOURCE_DIR}/Blocks/*.md") - -file(GLOB SlopeCraft_install_png_fixedblocks - "${CMAKE_SOURCE_DIR}/Blocks/FixedBlocks/*.png") - -file(GLOB SlopeCraft_install_png_customblocks - "${CMAKE_SOURCE_DIR}/Blocks/CustomBlocks/*.png") - -file(GLOB SlopeCraft_install_presets - "${CMAKE_CURRENT_SOURCE_DIR}/others/*.sc_preset_json") - -set(AppName SlopeCraft) -configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) - -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") - # Install for macOS - # Install app - install(TARGETS SlopeCraft - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX} - ) - - # Install icons - file(GLOB SlopeCraft_Icon - ${CMAKE_SOURCE_DIR}/SlopeCraftMain/others/SlopeCraftIconNew.icns) - install(FILES ${SlopeCraft_Icon} - DESTINATION ${CMAKE_INSTALL_PREFIX}/SlopeCraft.app/Contents/Resources) - - # Install FixedBlocks.json, CustomBlocks.json and README.md - install(FILES ${SlopeCraft_install_jsons} - DESTINATION ${CMAKE_INSTALL_PREFIX}/SlopeCraft.app/Contents/MacOS/Blocks) - file(COPY ${SlopeCraft_install_jsons} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.app/Contents/MacOS/Blocks) - - # Install all png files of fixedblocks - install(FILES ${SlopeCraft_install_png_fixedblocks} - DESTINATION ${CMAKE_INSTALL_PREFIX}/SlopeCraft.app/Contents/MacOS/Blocks/FixedBlocks) - file(COPY ${SlopeCraft_install_png_fixedblocks} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.app/Contents/MacOS/Blocks/FixedBlocks) - - # Install all png files of customblocks - install(FILES ${SlopeCraft_install_png_customblocks} - DESTINATION ${CMAKE_INSTALL_PREFIX}/SlopeCraft.app/Contents/MacOS/Blocks/CustomBlocks) - file(COPY ${SlopeCraft_install_png_customblocks} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.app/Contents/MacOS/Blocks/CustomBlocks) - - # Install presets - install(FILES ${SlopeCraft_install_presets} - DESTINATION ${CMAKE_INSTALL_PREFIX}/SlopeCraft.app/Contents/MacOS/Blocks/Presets) - file(COPY ${SlopeCraft_install_presets} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/SlopeCraft.app/Contents/MacOS/Blocks/Presets) - return() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Windows") - # Install app - install(TARGETS SlopeCraft - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - ) - - # Run windeployqt at build time - add_custom_target(Windeployqt-SlopeCraft ALL - COMMAND ${SlopeCraft_Qt_windeployqt_executable} SlopeCraft.exe - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS SlopeCraft) - - # Run windeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) - - # Install FixedBlocks.json, CustomBlocks.json and README.md - install(FILES ${SlopeCraft_install_jsons} - DESTINATION ${CMAKE_INSTALL_PREFIX}/Blocks) - file(COPY ${SlopeCraft_install_jsons} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks) - - # Install all png files of fixedblocks - install(FILES ${SlopeCraft_install_png_fixedblocks} - DESTINATION ${CMAKE_INSTALL_PREFIX}/Blocks/FixedBlocks) - file(COPY ${SlopeCraft_install_png_fixedblocks} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/FixedBlocks) - - # Install all png files of customblocks - install(FILES ${SlopeCraft_install_png_customblocks} - DESTINATION ${CMAKE_INSTALL_PREFIX}/Blocks/CustomBlocks) - file(COPY ${SlopeCraft_install_png_customblocks} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/CustomBlocks) - - # Install presets - install(FILES ${SlopeCraft_install_presets} - DESTINATION ${CMAKE_INSTALL_PREFIX}/Blocks/Presets) - file(COPY ${SlopeCraft_install_presets} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/Presets) - - return() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - # set_target_properties(SlopeCraft PROPERTIES INSTALL_RPATH "../lib") - # Install for Linux - # Install app - install(TARGETS SlopeCraft - RUNTIME DESTINATION bin - ) - - # Install FixedBlocks.json, CustomBlocks.json and README.md - install(FILES ${SlopeCraft_install_jsons} - DESTINATION bin/Blocks) - file(COPY ${SlopeCraft_install_jsons} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks) - - # Install all png files of fixedblocks - install(FILES ${SlopeCraft_install_png_fixedblocks} - DESTINATION bin/Blocks/FixedBlocks) - file(COPY ${SlopeCraft_install_png_fixedblocks} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/FixedBlocks) - - # Install all png files of customblocks - install(FILES ${SlopeCraft_install_png_customblocks} - DESTINATION bin/Blocks/CustomBlocks) - file(COPY ${SlopeCraft_install_png_customblocks} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/CustomBlocks) - - file(GLOB SlopeCraft_Icon ${CMAKE_SOURCE_DIR}/SlopeCraftMain/others/SlopeCraftIconNew.png) - - install(FILES ${SlopeCraft_Icon} - DESTINATION bin/icons) - - # Install presets - install(FILES ${SlopeCraft_install_presets} - DESTINATION bin/Blocks/Presets) - file(COPY ${SlopeCraft_install_presets} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Blocks/Presets) - - return() -endif() - -message(WARNING "No rule to install SlopeCraft and images, because the system is not Windows, linux or MacOS.") diff --git a/SlopeCraftMain/main.cpp b/SlopeCraftMain/main.cpp deleted file mode 100644 index c3d7c35b..00000000 --- a/SlopeCraftMain/main.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright © 2021-2022 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/ToKiNoBug - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "MainWindow.h" - -#include -#include -#include - -int main(int argc, char *argv[]) { - QApplication a(argc, argv); - - const QStringList uiLanguages = QLocale::system().uiLanguages(); - - // qDebug() << uiLanguages; - - // qDebug() << "当前运行路径:" << QCoreApplication::applicationDirPath(); - // QString DirPath=QCoreApplication::applicationDirPath()+'/'; - QDir::setCurrent(QCoreApplication::applicationDirPath()); - - const QStringList ZHLang = {"zh-CN", "zh", "zh-Hans-CN"}; - - bool isZH = false; - for (auto it = ZHLang.cbegin(); it != ZHLang.cend(); it++) { - if (uiLanguages.contains(*it)) { - isZH = true; - break; - } - } - - MainWindow w; - w.show(); - - if (isZH) - w.turnCh(); - else - w.turnEn(); - - w.InitializeAll(); - - if (isZH) - w.turnCh(); - else - w.turnEn(); - - w.grabVersion(true); - - return a.exec(); -} diff --git a/SlopeCraftMain/mainwindow.ui b/SlopeCraftMain/mainwindow.ui deleted file mode 100644 index f9e3115c..00000000 --- a/SlopeCraftMain/mainwindow.ui +++ /dev/null @@ -1,4058 +0,0 @@ - - - TokiNoBug - MainWindow - - - - 0 - 0 - 960 - 540 - - - - - 0 - 0 - - - - - 960 - 540 - - - - - 16777215 - 16777215 - - - - - - - SlopeCraft v3.10.1 Copyright © 2021-2022 TokiNoBug - - - - others/SlopeCraftIconNew.icoothers/SlopeCraftIconNew.ico - - - - - - - 128 - 128 - - - - true - - - QTabWidget::Rounded - - - true - - - - - 0 - 0 - - - - QWidget#centralwidget{border-image: url(":/new/Pic/BG4.png");} - - - - QLayout::SetNoConstraint - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - #LeftScroll ,#LeftScrollArea{ - background-color: rgba(255, 255, 255, 0); -} - -QPushButton:enabled{ - border:2px; - border-radius:10px; - padding:2px 4px; - background-color: rgba(255, 255, 255,128); - color: rgb(0, 0, 0); - font: 9pt "微软雅黑"; -} - -QPushButton:!enabled{ -border:2px; -border-radius:10px; -padding:2px 4px; -background-color: rgba(191, 191,191,128); -color: rgb(64, 64, 64); -font: 9pt "微软雅黑"; -} - -QPushButton:hover{ -border:2px; -border-radius:10px; -padding:2px 4px; -background-color: rgba(255, 255, 255,191); -color: rgb(0, 0, 0); -font: 9pt "微软雅黑"; -} - -QPushButton:pressed{ -border:2px; -border-radius:10px; -padding:2px 4px; -background-color: rgba(255, 255, 255,223); -border-style: inset; -border-color: rgb(235, 235, 235); -color: rgb(0, 0, 0); -font: 9pt "微软雅黑"; -} - -/**/ - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - Qt::AlignCenter - - - - - 0 - 0 - 152 - 757 - - - - - 0 - 0 - - - - - 0 - 0 - - - - false - - - - - - - 9 - - - 5 - - - 5 - - - 9 - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:700; color:#ffffff;">地图画</span></p></body></html> - - - - - - - - 0 - 0 - - - - - 100 - 30 - - - - 开始 - - - - :/new/Textures/Slope/block/grass_block.png:/new/Textures/Slope/block/grass_block.png - - - - - - - - 100 - 30 - - - - 导入图片 - - - - :/new/Textures/Slope/items/book.png:/new/Textures/Slope/items/book.png - - - - - - - true - - - - 100 - 30 - - - - - - - 地图画类型 - - - - :/new/Textures/Slope/items/map.png:/new/Textures/Slope/items/map.png - - - - - - - - 100 - 30 - - - - 方块列表 - - - - :/new/Textures/Slope/block/waxed_cut_copper.png:/new/Textures/Slope/block/waxed_cut_copper.png - - - - - - - - 100 - 30 - - - - 调整颜色 - - - - :/new/Textures/Slope/items/amethyst_shard.png:/new/Textures/Slope/items/amethyst_shard.png - - - - - - - - 100 - 30 - - - - 导出为 - - - - :/new/Textures/Slope/items/iron_pickaxe.png:/new/Textures/Slope/items/iron_pickaxe.png - - - - - - - 0 - - - - - - 0 - 0 - - - - - 90 - 25 - - - - 平面示意图 - - - - - - - - 0 - 0 - - - - - 90 - 25 - - - - 地图文件 - - - - - - - - 0 - 0 - - - - - 90 - 25 - - - - 投影文件 - - - - - - - - 0 - 0 - - - - - 90 - 25 - - - - 结构文件 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 90 - 25 - - - - WE原理图 - - - - - - - - - - 100 - 30 - - - - 完成 - - - - :/new/Textures/Slope/items/diamond.png:/new/Textures/Slope/items/diamond.png - - - - - - - <html><head/><body><p><br/><span style=" font-weight:700; color:#ffffff;">语言/Languages</span></p></body></html> - - - - - - - - 0 - 30 - - - - 简体中文 - - - - - - - - 0 - 30 - - - - English - - - - - - - <html><head/><body><p><br/><span style=" font-weight:700; color:#ffffff;">联系作者</span></p></body></html> - - - - - - - - 0 - 30 - - - - GitHub - - - - - - - - 0 - 30 - - - - BiliBili - - - - - - - - 0 - 30 - - - - 检查更新 - - - - - - - - 0 - 30 - - - - 反馈Bug - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 0 - - - - - 30 - 0 - - - - - - - /*QWidget#PageEnd{border-image: url(:/new/Pic/BG4.png);}*/ - -QWidget#PageStart,#PageEnd{ - background-color: rgba(255, 255, 255, 0); -} - - -QWidget#PageImPic,#PageType,#PageBL,#PageAdjPic,#PageExLite,#PageExMcF,#PageExDat{ -background-color:#f0f0f0; -border:2px solid gray -} - -QPushButton:enabled{ -border:2px; -border-radius:10px; -border-style:groove; -border-color: rgb(136, 136, 136); -color: rgb(0, 0, 0); -padding:2px 4px; -background-color: rgb(225, 225, 225); -} - -QPushButton:hover{ -border:2px; -border-radius:10px; -border-style:solid; -border-color: rgb(136, 136, 136); -color: rgb(0, 0, 0); -padding:2px 4px; -background-color: rgb(241, 241, 241); -} - -QPushButton:!enabled{ -border:2px; -border-radius:10px; -border-style:groove; -border-color: rgb(136, 136, 136); -color: rgb(80, 80, 80); -padding:2px 4px; -background-color: rgb(193, 193, 193); -} - -QCommandLinkButton:enabled{ -color: rgb(0, 0, 0); -border:0px; -background-color: rgba(255, 255, 255,128); -font: 11pt "微软雅黑"; -} - -QCommandLinkButton:hover{ -color: rgb(0, 0, 0); -border:0px; -background-color: rgba(255, 255, 255,191); -font: 11pt "微软雅黑"; -} -QCommandLinkButton:pressed{ -color: rgb(0, 0, 0); -border:0px; -background-color: rgba(255, 255, 255,223); -font: 11pt "微软雅黑"; -} - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - - - - - - 30 - - - - - - 0 - 0 - - - - - 20 - false - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:700; color:#ffff7f;">Copyright © 2021-2022 TokiNoBug</span></p></body></html> - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - - - 0 - 0 - - - - - MV Boli - 26 - false - - - - <html><head/><body><p><span style=" font-size:40pt; color:#ffffff;">SlopeCraft</span><span style=" color:#ffffff;"> %1</span></p></body></html> - - - Qt::AlignBottom|Qt::AlignHCenter - - - - - - - - - 0 - - - 0 - - - - - QLayout::SetDefaultConstraint - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 195 - - - - QFrame::StyledPanel - - - 2 - - - - - - :/new/Pic/cover_Flat.png - - - true - - - - - - - - 0 - 40 - - - - - 微软雅黑 - 11 - false - false - - - - 创建平板地图画 - - - - - - - 2 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">传统的地图画样式</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 195 - - - - - 260 - 195 - - - - QFrame::StyledPanel - - - 2 - - - - - - :/new/Pic/cover_3D.png - - - true - - - - - - - - 微软雅黑 - 11 - false - false - - - - - - - - - - - - - 创建立体地图画 - - - - - - - 2 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">原版生存的最高画质</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 195 - - - - false - - - QFrame::StyledPanel - - - 2 - - - - - - :/new/Pic/cover_Data.png - - - true - - - - - - - true - - - - 0 - 40 - - - - - 微软雅黑 - 11 - false - false - - - - 创建纯文件地图画 - - - - - - - 2 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">地图画质的极限!但需要修改存档。</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - - - - - 0 - 0 - - - - - 65536 - 65536 - - - - false - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 440 - 440 - - - - #ShowRawPic{background-image: url(:/new/Pic/Transparent.png);} - - - QFrame::StyledPanel - - - - - - :/new/Pic/BG2.png - - - true - - - Qt::AlignCenter - - - - - - - - - - 0 - 0 - - - - - 100 - 30 - - - - - - - 透明度相关设置 - - - 设置 - - - - - - - - 0 - 0 - - - - - - - - - - true - - - - - - - true - - - - 0 - 0 - - - - - 100 - 30 - - - - - 100 - 30 - - - - - 5 - 2 - - - - - 10 - - - - 下一步 - - - - - - - - 0 - 0 - - - - - - - <html><head/><body><p>第一步,点击“导入图片“按钮,选择你要制作为地图画的图片。</p><p>图片的长宽(以像素为单位)最好是128的整倍数。</p><p>如果要生存实装,那么图片不要太大,我推荐128×128或者256×256。</p></body></html> - - - Qt::AutoText - - - true - - - - - - - - 0 - 0 - - - - - 100 - 30 - - - - - 5 - 2 - - - - - 9 - - - - Qt::LeftToRight - - - false - - - 导入图片 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - - - backgroundcolor: rgb(255, 255, 255); - - - <html><head/><body><p align="justify">第二步,选择好你要什么样的地图画。</p><p align="justify">请选择好地图画对应的游戏版本,以及地图画的类型。</p><p align="justify">立体地图画如右图,是我们的核心功能;</p><p align="justify">平板地图画颜色相对少,但更容易建造;</p><p align="justify">纯文件地图画拥有最丰富的的颜色,但不可建造。</p></body></html> - - - true - - - - - - - - - true - - - - 0 - 0 - - - - - 100 - 30 - - - - - 100 - 30 - - - - - 10 - - - - 下一步 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - 0 - 0 - - - - - 9 - - - - 游戏版本 - - - - - - - 9 - - - - 1.15 - - - false - - - - - - - - 9 - - - - 1.16 - - - false - - - - - - - true - - - - - - 1.17 - - - true - - - - - - - - - - 1.13 - - - - - - - - - - 1.12 - - - - - - - - - - 1.14 - - - - - - - 1.18 - - - - - - - 1.19 - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - - - - true - - - - - - - - 0 - 0 - - - - - - - :/new/Pic/BG3.png - - - true - - - - - - - - 0 - 0 - - - - - 10 - - - - 地图画类型 - - - - - - - 9 - - - - 立体地图画 - - - true - - - - - - - - 9 - - - - 平板地图画 - - - - - - - - 9 - - - - 纯文件地图画 - - - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - 0 - - - - - - 10 - - - - 预设方块列表 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - 9 - - - - 适合创造 - - - Vanilla - - - - - - - - 9 - - - - 适合生存早期 - - - Cheap - - - - - - - - 9 - - - - 适合生存后期 - - - Elegant - - - true - - - - - - - - 9 - - - - 适合光影 - - - Shiny - - - - - - - - 9 - - - - 自定义 - - - Custom - - - - - - - - - - - 0 - 0 - - - - - - - - - - true - - - - - - - 6 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 100 - 30 - - - - - 100 - 30 - - - - - 9 - - - - 下一步 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - 0 - - - - - 0 - - - - - - 0 - 0 - - - - - 120 - 0 - - - - - - - <html><head/><body><p align="justify">第三步,给每一种颜色设置对应的方块,我们有四种预设。</p><p align="justify">你也可以在右侧的滚动条里自定义。每一列方块对应一种颜色,每种颜色只能选择一种方块。你也可以勾掉启用,禁用任何一种颜色。</p></body></html> - - - true - - - - - - - - 0 - 25 - - - - - 9 - - - - 优先混凝土 - - - - - - - - 0 - 25 - - - - - 9 - - - - 优先羊毛 - - - - - - - - 0 - 25 - - - - - 9 - - - - 优先染色玻璃 - - - - - - - - - true - - - QFrame::Box - - - 2 - - - Qt::ScrollBarAlwaysOn - - - true - - - - - 0 - 0 - 78 - 22 - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 1 - - - - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 0 - 15 - - - - Qt::DefaultContextMenu - - - - - - - - - false - - - - - - 1 - - - 0 - - - Qt::AlignCenter - - - %p% - - - - - - - 0 - - - - - - 0 - 0 - - - - - 100 - 40 - - - - - 32767 - 16777215 - - - - - 9 - - - - 转换为地图画 - - - false - - - - - - - - 0 - 0 - - - - - 80 - 40 - - - - - 150 - 1000 - - - - - - - 保存当前图片 - - - - - - - - 0 - 0 - - - - - 100 - 20 - - - - - 150 - 16777215 - - - - - 9 - - - - 显示地图画 - - - - - - - true - - - - 0 - 0 - - - - - 80 - 30 - - - - - 16777215 - 16777215 - - - - 导出为 -原版结构文件 - - - - - - - - 0 - 0 - - - - - 100 - 20 - - - - - 150 - 16777215 - - - - - 9 - - - - 显示原图 - - - - - - - true - - - - 0 - 0 - - - - - 80 - 30 - - - - - 150 - 32767 - - - - - - - 导出为 -地图文件 - - - - - - - true - - - - 0 - 0 - - - - - 80 - 30 - - - - - 150 - 16777215 - - - - 导出为 -WorldEdit原理图 - - - - - - - true - - - - 0 - 0 - - - - - 80 - 30 - - - - - 150 - 16777215 - - - - - 9 - - - - - - - 导出为 -Litematica投影 - - - - - - - - 0 - 0 - - - - - 0 - 100 - - - - 第四步,调整颜色。地图支持的颜色有限,你的图片必须也只能由地图允许的颜色组成。 -六种算法对应不同的调整颜色的方式,你可以挨个试一试。如果开启“抖动仿色”,SlopeCraft会尝试用相近的颜色混合,更好的贴近原图。 -调整颜色可能会比较慢,下面的进度条指示了进度。 - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 150 - 300 - - - - - 9 - - - - 算法 - - - - 2 - - - 4 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 9 - - - - RGB欧式距离 - - - RGB - - - - - - - - 0 - 0 - - - - - - - 更好的RGB色差公式 - - - RGB+ - - - true - - - - - - - - 0 - 0 - - - - - 9 - - - - 不是简单的欧式距离 - - - HSV - - - - - - - - 0 - 0 - - - - - 9 - - - - Lab1994公式 - - - Lab94 - - - false - - - - - - - - 0 - 0 - - - - - - - Lab2000公式 - - - Lab00 - - - - - - - - 0 - 0 - - - - - 9 - - - - XYZ欧式距离 - - - XYZ - - - - - - - - 0 - 0 - - - - 遗传算法 - - - GACvter - - - - - - - - 0 - 0 - - - - - - - 用分辨率换颜色 - - - 抖动仿色 - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 440 - 440 - - - - true - - - QFrame::StyledPanel - - - - - - :/new/Pic/BG2.png - - - true - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - 0 - - - 12 - - - - - QFrame::NoFrame - - - 其他选项 - - - - - - - 搭桥 - - - - - - - 方便建造,耗费玻璃 - - - 允许搭桥 - - - - - - - - - - 降低总高度,破坏连续性 - - - - - - - - - 无损压缩 - - - true - - - - - - - Qt::Horizontal - - - - - - - 用玻璃包围防火方块 - - - 防火 - - - - - - - - - - QFrame::NoFrame - - - 压缩高度 - - - - - - - true - - - 用玻璃包围可偷方块 - - - 防末影人 - - - false - - - - - - - true - - - - - - 降低总高度,微调颜色 - - - 智能有损压缩 - - - true - - - - - - - - 0 - 0 - - - - - - - <html><head/><body><p align="justify">最后一步,将立体地图画导出为投影文件,这很方便生存实装,是不是?</p><p align="justify">左侧的三个栏是投影文件的一些属性,空着不填也完全没问题。</p><p align="justify">如果您启用了“允许压缩高度”,SlopeCraft会在构建三维结构时尝试依据地图绘制的规律压缩立体地图画的高度,这可以有效避免超过限高(但未必能成功)。</p><p align="justify">下面的两个栏显示了立体地图画的信息,以供参考。</p><p align="justify">这个过程可能会很慢,请关注进度条</p></body></html> - - - true - - - - - - - - 0 - 30 - - - - - - - QFrame::StyledPanel - - - - - - - - - - Qt::Horizontal - - - - - - - - - - 投影尺寸 - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - - - - QFrame::StyledPanel - - - - - - - - - - true - - - - - - QAbstractSpinBox::UpDownArrows - - - - - - QAbstractSpinBox::CorrectToNearestValue - - - 最大允许高度: - - - 14 - - - 32767 - - - 255 - - - - - - - false - - - - - - 搭桥间隔: - - - 4 - - - - - - - - - - 总方块数 - - - - - - - - - - - - - 0 - - - - Litematica - - - - - - - 9 - - - - 投影名称 - - - - - - - Pixel Paint by SlopeCraft - - - - - - - - 9 - - - - 投影区域名称 - - - - - - - Pixel Paint by SlopeCraft - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Structure - - - - - - 节省大量存储空间 - - - - - - - - - 用结构空位代替 - - - true - - - - - - - 空气处理方式 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - WorldEdit - - - - - - 0 - - - - - - - WEOffset(X,Y,Z) - - - - - - - 0 - - - - - - - 0 - - - - - - - 0 - - - - - - - offset(X,Y,Z) - - - - - - - 0 - - - - - - - 原理图名称 - - - - - - - 0 - - - - - - - 依赖mod名称 - - - - - - - - - - Pixel Paint by SlopeCraft - - - - - - - - - - - - - - - false - - - - 0 - 0 - - - - - 100 - 20 - - - - - 10 - - - - 结束 - - - - - - - - - - 0 - 0 - - - - 1 - - - 0 - - - Qt::AlignCenter - - - false - - - Qt::Vertical - - - false - - - %p% - - - - - - - 0 - - - Qt::Vertical - - - - - - - - - false - - - - 0 - 0 - - - - - 100 - 30 - - - - 导出平面示意图 - - - - - - - false - - - - 100 - 30 - - - - - - - 导出三维结构 - - - - - - - false - - - - 0 - 30 - - - - 预览 - - - - - - - false - - - - 0 - 30 - - - - - - - 构建三维结构 - - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - 0 - - - 12 - - - - - 地图画起点坐标 - - - - - - - 地图画尺寸 - - - - - - - Y - - - - - - - X - - - - - - - 总命令数 - - - - - - - Z - - - - - - - - 0 - 30 - - - - QFrame::StyledPanel - - - - - - - - - - - 200 - 30 - - - - QFrame::StyledPanel - - - - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - 1 - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - -64 - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - -64 - - - - - - - - - 0 - - - - - - 100 - 20 - - - - - 10 - - - - 完成 - - - - - - - 0 - - - - - 0 - - - -1 - - - Qt::Vertical - - - - - - - - - - 100 - 30 - - - - - 0 - 0 - - - - - 10 - - - - 导出 - - - - - - - - 0 - 30 - - - - - - - 构建三维结构 - - - - - - - - - - 0 - 0 - - - - - - - <html><head/><body><p align="justify">最后一步,导出为mcfunction!</p><p align="justify">如果你不想导出为投影文件,也可以使用mc自带的数据包。</p><p align="justify">导出的文件是一个mc中的“函数”,是数据包的一部分。</p><p align="justify">你可以在单机存档中使用/reload命令来加载它,用/function命令调用它。</p><p align="justify">如果地图画很大,超过了一个mcfunction的命令数,那么会将全部的命令分散在多个mcfuntion中。</p><p align="justify">在导出前,你需要指定地图画的起点坐标,它是xyz负方向的方块的坐标。</p><p align="justify">(如果不知道要放在哪里,请使用默认值-64,1,-64)</p></body></html> - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 30 - - - - QFrame::StyledPanel - - - 1 - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - QFrame::StyledPanel - - - 1 - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - - 16777215 - 60 - - - - - - - - - - - - - 0 - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> -p, li { white-space: pre-wrap; } -hr { height: 1px; border-width: 0; } -li.unchecked::marker { content: "\2610"; } -li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:6px; margin-bottom:6px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Microsoft YaHei UI'; font-size:9pt;">0</span></p></body></html> - - - - - - - - - - - 0 - 30 - - - - QFrame::StyledPanel - - - map_0.dat - - - Qt::AlignCenter - - - - - - - - 0 - 30 - - - - QFrame::StyledPanel - - - QFrame::Plain - - - 1 - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - <html><head/><body><p align="justify">最后一步,导出为地图文件。</p><p align="justify">地图文件的文件名形如map_3.dat,是minecraft用于存储地图内容的文件。其中3表示地图文件的序号,地图的序号从0开始。</p><p align="justify">地图文件存储于存档中的data文件夹下,你需要一定的权限才能修改它们。如果你既不是单机游戏,又不是服主,那你应该选择导出为投影,而不是地图文件。</p><p align="justify">每张地图只能显示128×128的画面,大图片可能会有多个地图,对应多个地图文件。它们会占用连续的一段序号。</p><p align="justify">例如,你可以使用/give @s filled_map{map:3}来获得map_3.dat的地图。</p><p align="justify">1.12版请使用/give @p filled_map 1 3</p><p align="justify">这种方法适合以最高画质展示大图片,但不适合服务器。</p></body></html> - - - true - - - - - - - 0 - - - - - false - - - - 100 - 20 - - - - - 10 - - - - 完成 - - - - - - - 0 - - - - - 1 - - - 0 - - - Qt::Vertical - - - %p% - - - - - - - - - false - - - - 100 - 30 - - - - - 0 - 0 - - - - - 10 - - - - 导出 - - - - - - - - - 导出的文件名 - - - - - - - - 0 - 0 - - - - 地图文件起始序号 - - - - - - - 地图画行数 - - - - - - - 地图画列数 - - - - - - - 地图文件数量 - - - - - - - - - - - - 0 - 30 - - - - - - - 查看导出的文件 - - - - - - - true - - - - 0 - 30 - - - - - - - 联系作者 - - - - - - - - 0 - 30 - - - - - - - 退出 - - - - - - - - - - background-color: rgba(0, 0, 0, 48); - - - <html><head/><body><p align="center"><span style=" font-size:16pt; font-weight:700; color:#ffffff;">地图画生成完毕!</span><br/></p><p align="center"><span style=" font-size:11pt; font-weight:700; color:#ffffff;">关于SlopeCraft</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">感谢你使用SlopeCraft,我是开发者TokiNoBug。SlopeCraft是由我开发的一款立体地图画生成器,主要用于在minecraft中制造可以生存实装的立体地图画(但同样支持传统的平板地图画)。立体地图画的优势在于拥有更高的“画质”,此处不再详述。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">你正在使用的是SlopeCraft的第三代版本,在开发时使用了Qt,zlib和eigen,对上述库的开发者表示感谢。也感谢Mojang,整个软件就是为minecraft而设计的。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">AbrasiveBoar902(PopChrono)为本软件的设计和优化贡献了不少力量,并编写了基于SlopeCraftL核心的全新界面;Cubik65536和67au为本软件在MacOS和Linux的适配做出了贡献。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">你可以访问:</span><a href="https://github.com/ToKiNoBug/SlopeCraft/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">SlopeCraft的github</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://github.com/ToKiNoBug/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">我的github主页</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://space.bilibili.com/351429231"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">我的bilibili</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">本软件遵循GPL-3.0及以后版本(GPL-3.0 or later)协议开放源代码。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Copyright © 2021-2022 TokiNoBug</span></p></body></html> - - - true - - - 20 - - - - - - - - - - - - - 0 - 0 - 960 - 27 - - - - - 关于 - - - - 联系作者 - - - - - - - - - - - - - - - - - - - Languages - - - - - - - 地图画 - - - - 导出为 - - - - - - - - - - - - - - - - - 高级 - - - - 批量操作 - - - - - - 方块列表预设 - - - - - - - - - - - - - - - - - - - 转到页面 - - - - - 转到页面 - - - - - 转到页面 - - - - - 转到页面 - - - - - 转到页面 - - - - - 转到页面 - - - - - 转到页面 - - - - - false - - - 导出为投影文件 - - - 导出为投影文件 - - - 导出为投影文件 - - - - - false - - - 导出为mcfunction - - - 导出为mcfunction - - - - - false - - - true - - - 导出为地图文件 - - - - - false - - - 关于SlopeCraft - - - - - 简体中文 - - - - - English - - - - - GitHub - - - - - BiliBili - - - - - 反馈Bug - - - - - false - - - 检查更新 - - - 检查更新 - - - 检查更新 - - - - - 开始 - - - - - 导入图片 - - - - - 地图画类型 - - - - - 方块列表 - - - - - 调整颜色 - - - - - 投影文件 - - - - - 结构文件 - - - - - 地图文件 - - - - - 完成 - - - - - false - - - 测试方块列表 - - - - - 设置导出参数 - - - - - 保存预设 - - - - - 加载预设 - - - - - GACvter参数 - - - GACvter参数 - - - GACvter参数 - - - - - 输出当前颜色表 - - - - - WE原理图 - - - - - isMapSurvival - isMapFlat - isGame16 - isBLSurvivalCheaper - ImportPic - isBLSurvivalBetter - isBLGlowing - isBLCustom - NextPage3 - FirstConcrete - FirstWool - FirstStainedGlass - scrollArea - isColorSpaceRGBOld - isColorSpaceHSV - isColorSpaceLab94 - isColorSpaceXYZ - FinishExLite - ExportLite - NWPosY - NWPosX - NWPosZ - FinishMcF - _ExportMcF - isBLCreative - NextPage - NextPage2 - - - - - - - diff --git a/SlopeCraftMain/others/BG3.png b/SlopeCraftMain/others/BG3.png deleted file mode 100644 index fdab448a..00000000 Binary files a/SlopeCraftMain/others/BG3.png and /dev/null differ diff --git a/SlopeCraftMain/others/BG4.png b/SlopeCraftMain/others/BG4.png deleted file mode 100644 index 04702abf..00000000 Binary files a/SlopeCraftMain/others/BG4.png and /dev/null differ diff --git a/SlopeCraftMain/others/BlockTextures.qrc b/SlopeCraftMain/others/BlockTextures.qrc deleted file mode 100644 index a20bfdbe..00000000 --- a/SlopeCraftMain/others/BlockTextures.qrc +++ /dev/null @@ -1,11 +0,0 @@ - - - Slope/items/map.png - Slope/items/amethyst_shard.png - Slope/items/book.png - Slope/items/iron_pickaxe.png - Slope/items/diamond.png - Slope/block/grass_block.png - Slope/block/waxed_cut_copper.png - - diff --git a/SlopeCraftMain/others/Pics.qrc b/SlopeCraftMain/others/Pics.qrc deleted file mode 100644 index 19d6db90..00000000 --- a/SlopeCraftMain/others/Pics.qrc +++ /dev/null @@ -1,11 +0,0 @@ - - - Transparent.png - BG3.png - MCStyledNumbers.png - BG4.png - cover_3D.png - cover_Data.png - cover_Flat.png - - diff --git a/SlopeCraftMain/others/Slope/block/grass_block.png b/SlopeCraftMain/others/Slope/block/grass_block.png deleted file mode 100644 index 3aaebc55..00000000 Binary files a/SlopeCraftMain/others/Slope/block/grass_block.png and /dev/null differ diff --git a/SlopeCraftMain/others/Slope/block/waxed_cut_copper.png b/SlopeCraftMain/others/Slope/block/waxed_cut_copper.png deleted file mode 100644 index 022dab56..00000000 Binary files a/SlopeCraftMain/others/Slope/block/waxed_cut_copper.png and /dev/null differ diff --git a/SlopeCraftMain/others/Slope/items/amethyst_shard.png b/SlopeCraftMain/others/Slope/items/amethyst_shard.png deleted file mode 100644 index 4dce7915..00000000 Binary files a/SlopeCraftMain/others/Slope/items/amethyst_shard.png and /dev/null differ diff --git a/SlopeCraftMain/others/Slope/items/book.png b/SlopeCraftMain/others/Slope/items/book.png deleted file mode 100644 index 68abee66..00000000 Binary files a/SlopeCraftMain/others/Slope/items/book.png and /dev/null differ diff --git a/SlopeCraftMain/others/Slope/items/diamond.png b/SlopeCraftMain/others/Slope/items/diamond.png deleted file mode 100644 index 8ce901e9..00000000 Binary files a/SlopeCraftMain/others/Slope/items/diamond.png and /dev/null differ diff --git a/SlopeCraftMain/others/Slope/items/iron_pickaxe.png b/SlopeCraftMain/others/Slope/items/iron_pickaxe.png deleted file mode 100644 index ac75a010..00000000 Binary files a/SlopeCraftMain/others/Slope/items/iron_pickaxe.png and /dev/null differ diff --git a/SlopeCraftMain/others/Slope/items/map.png b/SlopeCraftMain/others/Slope/items/map.png deleted file mode 100644 index 45019a54..00000000 Binary files a/SlopeCraftMain/others/Slope/items/map.png and /dev/null differ diff --git a/SlopeCraftMain/others/SlopeCraft_en_US.ts b/SlopeCraftMain/others/SlopeCraft_en_US.ts deleted file mode 100644 index 6b9c79f2..00000000 --- a/SlopeCraftMain/others/SlopeCraft_en_US.ts +++ /dev/null @@ -1,1445 +0,0 @@ - - - - - AiCvterParameterDialog - - - Ai转化器参数 - Set AiConverter parameters - - - - 允许提前收敛 - Enable fail times - - - - 最大提前收敛代数: - Max fail times : - - - - 交叉概率: - Crossover prob : - - - - 种群数量: - Population size : - - - - 最大进化代数: - Max generation : - - - - 变异概率: - Mutation prob : - - - - BatchUi - - - SlopeCraft批量操作 - SlopeCraft batch operation - - - - 开始执行 - Start - - - - 导出格式 - Export format - - - - Litematica投影文件 - Litematica - - - - 原版结构方块文件 - Vanilla structure - - - - 地图文件 - Map data files - - - - 地图文件输出位置 - Map data files directory - - - - 浏览 - Browse - - - - 选择输出文件夹 - Select a directory - - - - 目标文件夹不可用 - Invalid directory - - - - 主窗体中选择了纯文件地图画,冲突 - Confilction : you assigned file-only maps in main window - - - - 就绪 - Ready - - - - 批量处理中: - Batch operation working ...... - - - - 正在转化为地图画 - Converting image to map - - - - 正在构建三维结构 - Building 3D structure - - - - 正在导出三维结构 - Exporting 3D structure - - - - 正在导出地图文件 - Exporting map data files - - - - 批量处理完成 - All tasks finished - - - - MainWindow - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:700; color:#ffffff;">地图画</span></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:700; color:#ffffff;">Map Arts</span></p></body></html> - - - - - - 开始 - Start - - - - - - - 导入图片 - Import image - - - - - - 地图画类型 - Type of map - - - - - 方块列表 - Block List - - - - - - 调整颜色 - Convert to Map - - - - - 导出为 - Export - - - - - 投影文件 - Litematic - - - - - 地图文件 - Map data files - - - - - 结构文件 - Structure - - - - - - 完成 - Finish - - - - <html><head/><body><p><br/><span style=" font-weight:700; color:#ffffff;">联系作者</span></p></body></html> - <html><head/><body><p><br/><span style=" font-weight:700; color:#ffffff;">Contact Us</span></p></body></html> - - - - - 反馈Bug - Report bugs - - - - 创建平板地图画 - Create Flat Map - - - - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">传统的地图画样式</span></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Traditional 2D map pixel art</span></p></body></html> - - - - 创建立体地图画 - Create 3D Map - - - - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">原版生存的最高画质</span></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Best quality in vanilla survival</span></p></body></html> - - - - 创建纯文件地图画 - Create File-only Map - - - - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">地图画质的极限!但需要修改存档。</span></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Highest quality, but requires to edit saves.</span></p></body></html> - - - - 设置 - Settings - - - - - - 下一步 - Next - - - - <html><head/><body><p>第一步,点击“导入图片“按钮,选择你要制作为地图画的图片。</p><p>图片的长宽(以像素为单位)最好是128的整倍数。</p><p>如果要生存实装,那么图片不要太大,我推荐128×128或者256×256。</p></body></html> - <html><head/><body><p>Step 1,click the load Image button, choose the image you want to make into map.</p><p>The size of image should be multiple of 128 pixels.</p><p></p><p></p><p>Provided that you want to build it, don't choose a large image. 128×128 is enough.</p></body></html> - - - - <html><head/><body><p align="justify">第二步,选择好你要什么样的地图画。</p><p align="justify">请选择好地图画对应的游戏版本,以及地图画的类型。</p><p align="justify">立体地图画如右图,是我们的核心功能;</p><p align="justify">平板地图画颜色相对少,但更容易建造;</p><p align="justify">纯文件地图画拥有最丰富的的颜色,但不可建造。</p></body></html> - <html><head/><body><p align="justify">Step 2</p><p align="justify">Set the type and gameversion of your map.</p><p align="justify">3D means 3D map paintings built in survival.</p><p align="justify">Flat is like a floor which is easiest to build.</p><p align="justify">File-only Maps has most colors but can't be built.</p></body></html> - - - - 游戏版本 - Game Version - - - - 1.18 - - - - - 1.19 - - - - - 立体地图画 - 3D Map - - - - 平板地图画 - Flat Map - - - - 纯文件地图画 - File-only Map - - - - 预设方块列表 - Presets - - - - <html><head/><body><p align="justify">第三步,给每一种颜色设置对应的方块,我们有四种预设。</p><p align="justify">你也可以在右侧的滚动条里自定义。每一列方块对应一种颜色,每种颜色只能选择一种方块。你也可以勾掉启用,禁用任何一种颜色。</p></body></html> - <html><head/><body><p align="justify">Step 3</p><p align="justify">Select a block for each colors. </p><p align="justify">Here we have 4 presets above, You can also set your custom block list in the scroll area.</p></body></html> - - - - 优先混凝土 - Use Concrete - - - - 优先羊毛 - Use Wool - - - - 优先染色玻璃 - Use Glass - - - - 第四步,调整颜色。地图支持的颜色有限,你的图片必须也只能由地图允许的颜色组成。 -六种算法对应不同的调整颜色的方式,你可以挨个试一试。如果开启“抖动仿色”,SlopeCraft会尝试用相近的颜色混合,更好的贴近原图。 -调整颜色可能会比较慢,下面的进度条指示了进度。 - Step 4, Convert image to map. Your map should only consist of map colors. -6 Algorithms correspond to different method to adjust pixel colors, you can try each. If "Dithering" is turned on,SlopeCraft will try to fit image better through mixing similar colors. -It might be laggy, the progressbar shows the progress. - - - - 导出为 -地图文件 - Export -map files - - - - 显示原图 - Show image - - - - 显示地图画 - Show Map - - - - 转换为地图画 - Convert to map - - - - 算法 - Algothrims - - - - 抖动仿色 - Dithering - - - - 无损压缩 - Lossless Compress - - - - 最大允许高度: - Max height: - - - - 智能有损压缩 - Intelligent Lossy Compress - - - - 投影名称 - Litematic name - - - - 总方块数 - Total count - - - - 压缩高度 - Compressing - - - - 防火 - Fire Proof - - - - 层 - layers - - - - 搭桥间隔: - Construct interval: - - - - 投影尺寸 - Size - - - - 允许搭桥 - Construct Glass Bridge - - - - 防末影人 - Enderman Proof - - - - 投影区域名称 - Region name - - - - 其他选项 - Others - - - - 搭桥 - Glass Bridge - - - - <html><head/><body><p align="justify">最后一步,将立体地图画导出为投影文件,这很方便生存实装,是不是?</p><p align="justify">左侧的三个栏是投影文件的一些属性,空着不填也完全没问题。</p><p align="justify">如果您启用了“允许压缩高度”,SlopeCraft会在构建三维结构时尝试依据地图绘制的规律压缩立体地图画的高度,这可以有效避免超过限高(但未必能成功)。</p><p align="justify">下面的两个栏显示了立体地图画的信息,以供参考。</p><p align="justify">这个过程可能会很慢,请关注进度条</p></body></html> - <html><head/><body><p align="justify"> - The last step is to make your map into a litematic file, to make it easier to be built.</p><p align="justify">3 blanks on the left side are some properties of your litematic file. </p><p align="justify">If you turn on "Compress height(lossless)", SlopeCraft will try to compress the height of 3d map according to rule of maps, in order to prevent it from surpassing 256(not always effective on all kinds of images).</p><p align="justify">Two bars below exhibit some information of your map. </p><p align="justify">This process might be quite slow, see the progress bar.</p></body></html> - - - - - 结束 - Finish - - - - - 导出 - Export - - - - 预览 - Preview - - - - 平面示意图 - Flat diagram - - - - 透明度相关设置 - About transparency - - - - 适合创造 - Suitable for creative mod - - - - 适合生存早期 - Suitable for survival mod(undeveloped) - - - - 适合生存后期 - - - - - 适合光影 - Suitable for shaders - - - - 自定义 - Custom - - - - RGB欧式距离 - RGB Euclidean Distance - - - - 更好的RGB色差公式 - A better RGB color difference formula - - - - 不是简单的欧式距离 - Not simple Euclidean distance - - - - Lab1994公式 - The Lab1994 color difference formula - - - - Lab2000公式 - The Lab2000 color difference formula - - - - XYZ欧式距离 - XYZ Euclidean Distance - - - - 遗传算法 - Genetic algorithm - - - - GACvter - GACvter - - - - 用分辨率换颜色 - Trade colors with resolution - - - - 方便建造,耗费玻璃 - Easy to build but cost many glass blocks - - - - 降低总高度,破坏连续性 - Lower the total height and break the continousness - - - - 用玻璃包围防火方块 - Cover burnable blocks with glass - - - - 用玻璃包围可偷方块 - Cover stealable blocks with glasses - - - - 降低总高度,微调颜色 - Lower the total height and modify colors slightly - - - - 节省大量存储空间 - Save disk space greatly - - - - 用结构空位代替 - Replace with structure void - - - - 空气处理方式 - Air processing method - - - - 原理图名称 - Name of schematic - - - - 依赖mod名称 - Dependent mods - - - - 导出平面示意图 - Export flat diagram - - - - 导出三维结构 - Export 3D - - - - - - GACvter参数 - GACvter parameters - - - - 构建三维结构 - Build 3D - - - - - WE原理图 - WE Schem - - - - 保存当前图片 - Save current Image - - - - 导出为 -原版结构文件 - Export - Vanilla structure - - - - 导出为 -WorldEdit原理图 - Export -WE Schem - - - - 导出为 -Litematica投影 - Export -Litematic - - - - <html><head/><body><p align="justify">最后一步,导出为地图文件。</p><p align="justify">地图文件的文件名形如map_3.dat,是minecraft用于存储地图内容的文件。其中3表示地图文件的序号,地图的序号从0开始。</p><p align="justify">地图文件存储于存档中的data文件夹下,你需要一定的权限才能修改它们。如果你既不是单机游戏,又不是服主,那你应该选择导出为投影,而不是地图文件。</p><p align="justify">每张地图只能显示128×128的画面,大图片可能会有多个地图,对应多个地图文件。它们会占用连续的一段序号。</p><p align="justify">例如,你可以使用/give @s filled_map{map:3}来获得map_3.dat的地图。</p><p align="justify">1.12版请使用/give @p filled_map 1 3</p><p align="justify">这种方法适合以最高画质展示大图片,但不适合服务器。</p></body></html> - <html><head/><body><p align="justify">The last step is to generate map data files.</p><p align="justify">The name of map data files is like<span style=" font-weight:700;"> map_3.dat</span>, it's used to storage content of maps. In it's file name, 3 refers to the sequence number of map data files. Sequence number starts with 0.</p><p align="justify">Located in<span style=" font-weight:700;"> data</span> folder in your saves, map data files can only be manipulated with enough authority. If you are neither single-player nor op in a server, you should export it as litematic instead of map data files.</p><p align="justify">Each map can only show a 128×128 frame, a large image results to multiple map data files. Thus, a continuous series of sequence numbers will be occupied.</p><p align="justify">For ex, run <span style=" font-weight:700;">/give @s filled_map{map:3}</span> will give you the map of <span style=" font-weight:700;">map_3.dat </span><p align="justify">1.12Command: /give @p filled_map 1 3</p><span style=" font-style:italic;">.</span></p><p align="justify">This is suitable for large images, but not for servers.</p></body></html> - - - - 导出的文件名 - Map data files - - - - 地图文件起始序号 - Beginning -sequence number - - - - 地图画行数 - Map rows - - - - 地图画列数 - Map cols - - - - 地图文件数量 - Map counts - - - - 查看导出的文件 - See exported file - - - - - 联系作者 - Contact with me - - - - 退出 - Exit - - - - <html><head/><body><p align="center"><span style=" font-size:16pt; font-weight:700; color:#ffffff;">地图画生成完毕!</span><br/></p><p align="center"><span style=" font-size:11pt; font-weight:700; color:#ffffff;">关于SlopeCraft</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">感谢你使用SlopeCraft,我是开发者TokiNoBug。SlopeCraft是由我开发的一款立体地图画生成器,主要用于在minecraft中制造可以生存实装的立体地图画(但同样支持传统的平板地图画)。立体地图画的优势在于拥有更高的“画质”,此处不再详述。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">你正在使用的是SlopeCraft的第三代版本,在开发时使用了Qt,zlib和eigen,对上述库的开发者表示感谢。也感谢Mojang,整个软件就是为minecraft而设计的。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">AbrasiveBoar902(PopChrono)为本软件的设计和优化贡献了不少力量,并编写了基于SlopeCraftL核心的全新界面;Cubik65536和67au为本软件在MacOS和Linux的适配做出了贡献。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">你可以访问:</span><a href="https://github.com/ToKiNoBug/SlopeCraft/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">SlopeCraft的github</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://github.com/ToKiNoBug/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">我的github主页</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://space.bilibili.com/351429231"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">我的bilibili</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">本软件遵循GPL-3.0及以后版本(GPL-3.0 or later)协议开放源代码。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Copyright © 2021-2022 TokiNoBug</span></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:16pt; font-weight:700; color:#ffffff;">地图画生成完毕!</span><br/></p><p align="center"><span style=" font-size:11pt; font-weight:700; color:#ffffff;">关于SlopeCraft</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">感谢你使用SlopeCraft,我是开发者TokiNoBug。SlopeCraft是由我开发的一款立体地图画生成器,主要用于在minecraft中制造可以生存实装的立体地图画(但同样支持传统的平板地图画)。立体地图画的优势在于拥有更高的“画质”,此处不再详述。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">你正在使用的是SlopeCraft的第三代版本,在开发时使用了Qt,zlib和eigen,对上述库的开发者表示感谢。也感谢Mojang,整个软件就是为minecraft而设计的。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">你可以访问:</span><a href="https://github.com/ToKiNoBug/SlopeCraft/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">SlopeCraft的github</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://github.com/ToKiNoBug/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">我的github主页</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://space.bilibili.com/351429231"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">我的bilibili</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">本软件遵循GPL-3.0及以后版本(GPL-3.0 or later)协议开放源代码。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Copyright © 2021 TokiNoBug</span></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:16pt; font-weight:700; color:#ffffff;">Your map is finished!</span><br/></p><p align="center"><span style=" font-size:11pt; font-weight:700; color:#ffffff;">About SlopeCraft</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Thank you for using SlopeCraft, I'm TokiNoBug, the developer. SlopeCraft is a minecraft map generator, supporting many kinds of maps.</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">You are currently using the 3rd generation of SlopeCraft, based on Qt, Eigen and zlib. I'm grateful to developers of thest libs. Also thanks Mojang, the whole software is developed for minecraft.</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">AbrasiveBoar902(PopChrono) made great effort to the designing and optimization, and also developed a brand-new interface based on SlopeCraft's kernel. Besides, Cubik65536 and 67au made contribution to the adaption on MacOS and Linux platforms respectively.</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Contact with me:</span><a href="https://github.com/ToKiNoBug/SlopeCraft/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">SlopeCraft on github</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://github.com/ToKiNoBug/"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">My homepage on GitHub</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">,</span><a href="https://space.bilibili.com/351429231"><span style=" font-weight:700; text-decoration: underline; color:#ffffff;">bilibili</span></a><span style=" font-size:10pt; font-weight:700; color:#ffffff;">。</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">This program is released under license GPL-3.0 or later.</span></p><p align="justify"><span style=" font-size:10pt; font-weight:700; color:#ffffff;">Copyright © 2021-2022 TokiNoBug</span></p></body></html> - - - - 关于 - About - - - - 地图画 - Map - - - - 高级 - Advanced - - - - 批量操作 - Batch operation - - - - 方块列表预设 - Blocklist preset - - - - - - - - - - 转到页面 - Goto this page - - - - - - - 导出为投影文件 - Export as litematic - - - - - 导出为mcfunction - Export as mcfunction - - - - - 导出为地图文件 - Export as map files - - - - 关于SlopeCraft - About SlopeCraft - - - - 测试方块列表 - Test block list - - - - 设置导出参数 - Set exporting parameters - - - - - 保存预设 - Save preset - - - - 加载预设 - Load preset - - - - 输出当前颜色表 - Export current colorset - - - - - - - 检查更新 - Check updates - - - - 设置地图画类型 - Set map type - - - - 设置方块列表 - Set BlockList - - - - 选择图片 - Select Image - - - - 打开图片失败 - Failed to read image - - - - SlopeCraft 无法加载必需方块列表 - SlopeCraft failed to load fixed block list - - - - SlopeCraft 必须退出 - SlopeCraft must exit - - - - SlopeCraft 无法加载可选方块列表 - SlopeCraft failed to load custom block list - - - - 此错误可以忽略 - This error can be ignored - - - - 加载默认预设失败 - Failed to load default presets - - - - 一个或多个内置的预设不能被解析。SlopeCraft 可能已经损坏,请重新安装。 -具体报错信息: -%1 - One or more internal presets failed to be parsed. SlopeCraft may have been damaged, please reinstall it. -Detail information: -%1 - - - - 要不试试换一张图片吧! - Why not try another one? - - - - 图片尺寸: - Image size: - - - - 像素 - Pixels - - - - 图片中存在透明/半透明像素,已处理,您可以点击“设置”重新选择处理透明/半透明像素的方式。 -重新设置处理方式后,需要重新导入一次。 - There's transparent pixels in your image, SlopeCraft has processed it. You can click "Settings" to reset the strategy of processing transparent pixels. -After you reset the strategy, load the image again. - - - - 导出为投影/结构方块文件 - Export as litematic or structure - - - - 投影文件导出失败 - Failed to export as litematic. - - - - 这可能是汉字编码错误造成的。请检查路径中是否有汉字 - There might be sth wrong with characters' encoding, check if the filepath includes non-English characters. - - - - 错误信息: - Error details : - - - - 你输入的起始序号不可用 - Invalid sequence number. - - - - 请选择导出的文件夹 - Please select a folder for exporting. - - - - 你选择的文件夹不存在! - Folder dosen't exist! - - - - 请稍等 - Please wait - - - - -具体信息: - Detail: - - - - 未知游戏版本 - Unknown game version - - - - - - - 导出原理图失败 - Failed to export. - - - - 方块种类超出上限。 - The types of blocks overflow. - - - - 错误的文件扩展名 - Invalid file extension name - - - - 三维结构中存在错误的方块 - Wrong block in 3D structure. - - - - 无法创建/打开文件 - Failed to create/open file - - - - 导出原版结构方块文件失败 - Failed to export vanilla structure file. - - - - 导出时指定不使用结构空位表示空气,但方块列表中不包含空气。 - When exporting as structure, you assigned not to replace air with structure void, but there is no air in the block palette. - - - - 转化原图为地图画时出错 - Error occurred when converting image to map - - - - 原图为空!你可能没有导入原图! - Your source image is empty! You might have forgot to import source image! - - - - 构建高度矩阵时出现错误 - Error occurred when making height martix - - - - 原版地图画不允许出现第三个阴影(不存在的几何关系不可能生存实装!) -请检查你的地图画类型,纯文件地图画不可以导出为投影! - The third shadow shouldn't apper in vanilla map! -Check your map type, remember that file-only map can't be exported as litematic! - - - - 跳步操作 - You skipped a step - - - - 有损压缩失败 - Inteligent lossy compression failed - - - - 最大允许高度太小了 - The maxmium allowed height is too low - - - - 允许使用的颜色过少 - Too few colors allowed! - - - - 你应该勾选启用尽可能多的基色,颜色太少是不行的! - You should enable as much base colors as possible. - - - - - 这可能是因为路径中含有中文字符! - This might because of non-English characters in filename of path. - - - - 删除临时文件失败 - Failed to remove temporary file - - - - 在构建高度矩阵时,有损压缩失败,没能将地图画压缩到目标高度。 这可能是因为地图画行数过大。 尝试启用无损压缩,或者提高最大允许高度 - Lossy compression failed to compress the maximun height down to max allowed hehight when computing height map. This may be caused by a too large image. Try enabling lossless compression, or enlarge max allowed height. - - - - 导出为 mcfunction - Export as mcfunction - - - - 图片 (*.png *.bmp *.jpg *.tif *.GIF ) - Image (*.png *.bmp *.jpg *.tif *.GIF ) - - - - 投影文件 (*.litematic) - Litematic file (*.litematic) - - - - 结构方块文件 (*.nbt) - Structure file(*.nbt) - - - - WorldEdit 原理图 (*.schem) - WorldEdit schematic (*.schem) - - - - (xz 坐标=-65±128×整数) - (xz coordinate = -65±128×<any integer>) - - - - 你输入的起始序号不可用,请输入大于等于 0 的整数! - The initial sequence number is invalid, expected non-negative number! - - - - 请输入大于等于 0 的整数! - Please input a non negative number! - - - - 你可以选择存档中的 data 文件夹 - You can select the "data" dir in the saves of MC. - - - - 导出 WorldEdit 原理图失败 - Failed to export WorldEdit schem - - - - 不支持导出 1.12 WorldEdit 原理图(.schematic 格式),仅支持.schem 格式 - Exporting as WE schematic is not supported in MC 12(*.schematic), only *.schem is supported - - - - SlopeCraft 不允许你跳步操作,请按照左侧竖边栏的顺序操作! - Skipping any step is not allowed by SlopeCraft - - - - 有损压缩的最大允许不要低于 14,否则很容易压缩失败 - The maximum height of lossy compression should not be less than 14 - - - - 导出时 Gzip 压缩文件失败 - Failed to compress a exported file - - - - 正在构建高度矩阵 - Making height matrix - - - - 正在构建三维结构 - Building 3D structure - - - - 正在收集整张图片的颜色 - Collecting colors of the whole image - - - - 正在压缩立体地图画 - Compressing 3D map pixel arts - - - - 正在为立体地图画搭桥 - Constructing glass bridge - - - - 正在匹配颜色 - Matching colors - - - - 正在使用抖动仿色 - Dithering - - - - 正在将平板地图画变为墙面地图画 - Converting flat map art to wall map art - - - - 正在写入三维结构 - Writing 3D structure - - - - 正在写入方块列表 - Writing block palette - - - - 正在写入地图数据文件 - Writing map data - - - - 正在写入基础信息 - Writing meta data - - - - 可用 - - - - - 种颜色 - colors available. - - - - 保存截屏 - Save screenshot - - - - 图片 (*.jpg *.jpeg *.tif *.bmp *.png) - Image (*.jpg *.jpeg *.tif *.bmp *.png) - - - - 保存当前显示图片 - Save current image - - - - 图片 (*.png) - Image (*.png) - - - - 保存预设文件失败 - Failed to save preset file - - - - 无法创建文件"%1" - Failed to create file "%1" - - - - 选择预设文件 - Select a preset file - - - - 加载预设失败 - Failed to load preset - - - - 测试方块列表的结构文件 - Svae structure file for blocklist testing - - - - 测试方块列表失败 - Failed to test the block list. - - - - 具体信息: - Detailed information: - - - - 保存颜色表 - Save palette - - - - PreviewWind - - - 预览投影文件 - Preview Schematic - - - - 材料表 - Material List - - - - - 方块 - Blocks - - - - 投影尺寸 - Size - - - - - 数量 - Count - - - - 总方块数 - Total count - - - - 总体积 - Volume - - - - 切换单位 - Switch Unit - - - - 预览地图画 - Preview map - - - - 如果使用了有损压缩,上一步生成的地图画可能会被轻微的修改 - The converted image might be slightly modified if lossy compression is enabled. - - - - QObject - - - 个 - block(s) - - - - 盒 - SB - - - - 组 - stacks - - - - TaskBox - - - Form - - - - - 浏览 - Browse - - - - 投影/结构文件名 - Schematic name - - - - × - - - - - ~ - - - - - 占用的地图序号: - Map sequence number : - - - - FileName - - - - - 选择图片 - Select Image - - - - 图片(*.png *.bmp *.jpg *.tif *.GIF ) - Images(*.png *.bmp *.jpg *.tif *.GIF ) - - - - 图片格式损坏 - Damaged image - - - - tpStrategyWind - - - 透明像素处理策略 - Transparent pixels processing strategy - - - - 确定 - OK - - - - 取消 - Cancel - - - - 恢复默认设定 - Restore to default - - - - 地图画中几乎不能使用透明像素。立体地图画和平板地图画都不能实现纯透明像素,纯文件地图画虽然理论上支持透明像素,但只会透出地图/物品展示框的背景色。因此有必要对透明/半透明像素进行处理。 -”替换为背景色“会将像素替换为背景色;”替换为空气“将像素替换为空气。 -”与背景色叠加“会按照像素的透明度与背景色叠加;“保留颜色”会忽视半透明像素的透明度,直接使用它们的颜色。 -完成设置后,需重新导入图片。 - Transparency is hardly supported in map paintings. 3d maps and flat maps cannot implement transparent pixels. Although transparent pixels do exist in file-only maps, they just let the background of map/item go through. Thus it's necessary to deal with full/semi trasnparent pixels. -"Replace with background" replace pixels with background color, while "Replace with air" with air. -"Compose with backgroundcolor" compose pixel's color with background color accroding to alpha value, and "Reserve color" simply ignore the alpha value, using its RGB directly. -You should load the image again after you confirmed these settings. - - - - 纯透明像素 - Full transparent - - - - 替换为空气 - Replace with air - - - - - 替换为背景色 - Replace with background - - - - 背景色 - Background color - - - - 高级灰(#DCDCDC) - Silver gray(#DCDCDC) - - - - 自定义 - Custom - - - - 纯白(#FFFFFF) - Pure white(#FFFFFF) - - - - 半透明像素 - Semi-transparent - - - - 保留颜色 - Reserve color - - - - 与背景色叠加 - Compose with backgroundcolor - - - diff --git a/SlopeCraftMain/others/Transparent.png b/SlopeCraftMain/others/Transparent.png deleted file mode 100644 index 1b4bdb10..00000000 Binary files a/SlopeCraftMain/others/Transparent.png and /dev/null differ diff --git a/SlopeCraftMain/others/cover_3D.png b/SlopeCraftMain/others/cover_3D.png deleted file mode 100644 index cf463405..00000000 Binary files a/SlopeCraftMain/others/cover_3D.png and /dev/null differ diff --git a/SlopeCraftMain/others/cover_Data.png b/SlopeCraftMain/others/cover_Data.png deleted file mode 100644 index a05472a9..00000000 Binary files a/SlopeCraftMain/others/cover_Data.png and /dev/null differ diff --git a/SlopeCraftMain/others/cover_Flat.png b/SlopeCraftMain/others/cover_Flat.png deleted file mode 100644 index 863e8636..00000000 Binary files a/SlopeCraftMain/others/cover_Flat.png and /dev/null differ diff --git a/SlopeCraftMain/previewwind.cpp b/SlopeCraftMain/previewwind.cpp deleted file mode 100644 index 31af361f..00000000 --- a/SlopeCraftMain/previewwind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "previewwind.h" -#include "ui_previewwind.h" - -PreviewWind::PreviewWind(QWidget *parent) - : QMainWindow(parent), ui(new Ui::PreviewWind) { - ui->setupUi(this); - this->setAttribute(Qt::WA_QuitOnClose, false); -} - -PreviewWind::~PreviewWind() { delete ui; } - -QString blockCount2string(int count, int setCount) { - if (count > 0 && count <= (setCount - 1)) - return QString::number(count) + QObject::tr("个"); - if (count >= (setCount * 27)) - return QString::number(count / (setCount * 27)) + QObject::tr("盒") + - blockCount2string(count % (setCount * 27)); - if (count >= setCount && count < (setCount * 27)) - return QString::number(count / setCount) + QObject::tr("组") + - blockCount2string(count % setCount); - return ""; -} - -void PreviewWind::ShowMaterialList() { - TotalBlockCount = 0; - - for (auto i = BlockCount.begin(); i != BlockCount.end(); i++) { - TotalBlockCount += *i; - } - - ui->Size->setText(QString::number(size[0]) + "×" + QString::number(size[1]) + - "×" + QString::number(size[2])); - - ui->Volume->setText(QString::number(size[0] * size[1] * size[2])); - - ui->BlockCount->setText(QString::number(TotalBlockCount)); - - auto area = ui->MaterialArea; - QLabel *iconShower, *idShower, *countShower; - QLabel *Spacer; - int rows = area->columnCount(), colOffset = 0; - - for (int i = 0; i < (signed)Src.size(); i++) { - area->addWidget(iconShower = new QLabel("Test1"), rows, colOffset); - area->addWidget(idShower = new QLabel("Test2"), rows, colOffset); - area->addWidget(countShower = new QLabel("Test3"), rows, colOffset + 1); - - auto icon = Src[i]->icon(); - auto iconsize = Src[i]->iconSize(); - auto pixmap = icon.pixmap(iconsize); - iconShower->setPixmap(pixmap); - - iconShower->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - idShower->setText(" " + Src[i]->text()); - countShower->setText(QString::number(BlockCount[i])); - countShower->setAlignment(Qt::AlignHCenter); - CountLabel.push_back(countShower); - colOffset = 2 * (!colOffset); - rows += i % 2; - } - - area->addWidget(Spacer = new QLabel(""), area->rowCount(), 0); - Spacer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); -} - -void PreviewWind::on_SwitchUnit_clicked(bool checked) { - if (checked) - for (int i = 0; i < (signed)BlockCount.size(); i++) { - if (Src[i] == Water) { - CountLabel[i]->setText(blockCount2string(BlockCount[i], 1)); - continue; - } - CountLabel[i]->setText(blockCount2string(BlockCount[i])); - } - else { - for (int i = 0; i < (signed)BlockCount.size(); i++) { - CountLabel[i]->setText(QString::number(BlockCount[i])); - } - } -} - -void PreviewWind::showConvertedImage(const QImage &img) { - ui->showImg->setPixmap(QPixmap::fromImage(img)); -} diff --git a/SlopeCraftMain/previewwind.h b/SlopeCraftMain/previewwind.h deleted file mode 100644 index 4b84988b..00000000 --- a/SlopeCraftMain/previewwind.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef PREVIEWWIND_H -#define PREVIEWWIND_H - -#include -#include -#include -#include -#include -#include - -namespace Ui { -class PreviewWind; -} - -class PreviewWind : public QMainWindow { - Q_OBJECT - - public: - explicit PreviewWind(QWidget *parent = nullptr); - - std::vector Src; - std::vector BlockCount; - std::vector CountLabel; - const QRadioButton *Water; - int size[3]; - int TotalBlockCount; - void ShowMaterialList(); - void showConvertedImage(const QImage &); - ~PreviewWind(); - - private slots: - void on_SwitchUnit_clicked(bool checked); - - private: - Ui::PreviewWind *ui; -}; - -QString blockCount2string(int, int = 64); -#endif // PREVIEWWIND_H diff --git a/SlopeCraftMain/previewwind.ui b/SlopeCraftMain/previewwind.ui deleted file mode 100644 index a5a2d24a..00000000 --- a/SlopeCraftMain/previewwind.ui +++ /dev/null @@ -1,544 +0,0 @@ - - - PreviewWind - - - - 0 - 0 - 590 - 482 - - - - 预览投影文件 - - - - - 500 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 460 - 460 - - - - 1 - - - - 材料表 - - - - 10 - - - - - 方块 - - - Qt::AlignCenter - - - - - - - - - - QFrame::StyledPanel - - - - - - - - - - - 0 - 0 - - - - 投影尺寸 - - - Qt::AlignCenter - - - - - - - /*background-color: rgba(255, 255, 255,0);*/ - - - QFrame::NoFrame - - - QFrame::Plain - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 568 - 335 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 5 - - - 5 - - - 10 - - - - - - - - - - - 数量 - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 0 - 24 - - - - 总方块数 - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - 总体积 - - - Qt::AlignCenter - - - - - - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 120 - 215 - - - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 240 - 240 - 240 - - - - - - - - - 120 - 120 - 120 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 120 - 215 - - - - - - - - false - - - 方块 - - - false - - - Qt::AlignCenter - - - - - - - 数量 - - - Qt::AlignCenter - - - - - - - - - - QFrame::StyledPanel - - - - - - - - - - QFrame::Plain - - - Qt::Horizontal - - - - - - - - 0 - 24 - - - - - - - QFrame::StyledPanel - - - - - - - - - - - 0 - 0 - - - - 切换单位 - - - - - - - - true - - - 预览地图画 - - - - - - true - - - - 0 - 0 - - - - - 414 - 414 - - - - true - - - QFrame::StyledPanel - - - - - - true - - - - - - - true - - - - 0 - 0 - - - - - 57 - 10 - - - - - 140 - 16777215 - - - - 如果使用了有损压缩,上一步生成的地图画可能会被轻微的修改 - - - true - - - - - - - - - - - - - diff --git a/SlopeCraftMain/tpstrategywind.cpp b/SlopeCraftMain/tpstrategywind.cpp deleted file mode 100644 index 552590a2..00000000 --- a/SlopeCraftMain/tpstrategywind.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef TpStrategyWind_CPP -#define TpStrategyWind_CPP -#include "tpstrategywind.h" -#include "MainWindow.h" -#include "ui_tpstrategywind.h" -#include -#include -tpStrategyWind::tpStrategyWind(QWidget *parent) - : QMainWindow(parent), ui(new Ui::tpStrategyWind) { - ui->setupUi(this); - BackGroundColor = qRgb(220, 220, 220); - this->setAttribute(Qt::WA_QuitOnClose, false); -} - -void tpStrategyWind::setVal(tpS t) { - if (t.pTpS == 'B') - ui->isPureB->setChecked(true); - else - ui->isPureA->setChecked(true); - - if (t.hTpS == 'B') - ui->isHB->setChecked(true); - else if (t.hTpS == 'C') - ui->isHS->setChecked(true); - else - ui->isHR->setChecked(true); - - QPalette temp; - QColor Temp(qRed(t.BGC), qGreen(t.BGC), qBlue(t.BGC)); - temp.setColor(QPalette::Window, Temp); - ui->ShowBGCCustom->setPalette(temp); - ui->ShowBGCCustom->setAutoFillBackground(true); -} - -void tpStrategyWind::closeEvent(QCloseEvent *event) { - emit destroyed(); - delete this; - event->accept(); -} - -tpStrategyWind::~tpStrategyWind() { - qDebug("子窗口析构"); - delete ui; -} - -void tpStrategyWind::on_Confirm_clicked() { - grabTpSInfo(); - emit Confirm(tpS(pTpS, hTpS, BackGroundColor)); - qDebug("传值完成"); - close(); -} - -void tpStrategyWind::grabTpSInfo() { - pTpS = (ui->isPureB->isChecked()) ? 'B' : 'A'; - - hTpS = (ui->isHB->isChecked()) ? 'B' : ((ui->isHR->isChecked()) ? 'R' : 'C'); - if (ui->isBGCWhite->isChecked()) - BackGroundColor = qRgb(255, 255, 255); - else if (ui->isBGCGray->isChecked()) - BackGroundColor = qRgb(220, 220, 220); - qDebug("抓取完成"); -} - -void tpStrategyWind::on_isBGCCustom_clicked() { - QColor Temp = QColorDialog::getColor(); - if (!Temp.isValid()) return; - BackGroundColor = qRgb(Temp.red(), Temp.green(), Temp.blue()); - QPalette temp; - temp.setColor(QPalette::Window, Temp); - ui->ShowBGCCustom->setPalette(temp); - ui->ShowBGCCustom->setAutoFillBackground(true); -} - -void tpStrategyWind::on_Reset_clicked() { - ui->isPureB->setChecked(true); - ui->isHS->setChecked(true); - ui->isBGCGray->setChecked(true); -} - -#endif diff --git a/SlopeCraftMain/tpstrategywind.h b/SlopeCraftMain/tpstrategywind.h deleted file mode 100644 index 554f72f6..00000000 --- a/SlopeCraftMain/tpstrategywind.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef TPSTRATEGYWIND_H -#define TPSTRATEGYWIND_H - -#include - -namespace Ui { -class tpStrategyWind; -} -class MainWindow; - -#ifndef TPS__ -#define TPS__ -class tpS { -public: - tpS(char _pTpS = 'B', char _hTpS = 'C', QRgb _BGC = qRgb(220, 220, 220)) { - pTpS = _pTpS; - hTpS = _hTpS; - BGC = _BGC; - } - ~tpS(); - char pTpS; - char hTpS; - QRgb BGC; -}; -#endif - -class tpStrategyWind : public QMainWindow { - Q_OBJECT - -public: - explicit tpStrategyWind(QWidget *parent = nullptr); - ~tpStrategyWind(); - MainWindow *parent; - char pTpS; - char hTpS; - QRgb BackGroundColor; - void setVal(tpS); - -protected: - void closeEvent(QCloseEvent *event) override; - -private: - Ui::tpStrategyWind *ui; -signals: - void Confirm(tpS); -public slots: - void grabTpSInfo(); -private slots: - void on_Confirm_clicked(); - void on_isBGCCustom_clicked(); - void on_Reset_clicked(); -}; - -#endif // TPSTRATEGYWIND_H diff --git a/SlopeCraftMain/tpstrategywind.ui b/SlopeCraftMain/tpstrategywind.ui deleted file mode 100644 index a6257ce3..00000000 --- a/SlopeCraftMain/tpstrategywind.ui +++ /dev/null @@ -1,263 +0,0 @@ - - - tpStrategyWind - - - - 0 - 0 - 520 - 324 - - - - - 520 - 324 - - - - 透明像素处理策略 - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 3 - - - 0 - - - - - 12 - - - 6 - - - 6 - - - 0 - - - - - 确定 - - - - - - - 取消 - - - - - - - 恢复默认设定 - - - - - - - - - - 0 - 0 - - - - 地图画中几乎不能使用透明像素。立体地图画和平板地图画都不能实现纯透明像素,纯文件地图画虽然理论上支持透明像素,但只会透出地图/物品展示框的背景色。因此有必要对透明/半透明像素进行处理。 -”替换为背景色“会将像素替换为背景色;”替换为空气“将像素替换为空气。 -”与背景色叠加“会按照像素的透明度与背景色叠加;“保留颜色”会忽视半透明像素的透明度,直接使用它们的颜色。 -完成设置后,需重新导入图片。 - - - true - - - 4 - - - - - - - 纯透明像素 - - - - - - 替换为空气 - - - - - - - 替换为背景色 - - - true - - - - - - - - - - 背景色 - - - - - - 高级灰(#DCDCDC) - - - true - - - - - - - - 20 - 20 - - - - - 20 - 16777215 - - - - background-color: rgb(255, 255, 255); - - - QFrame::StyledPanel - - - - - - - - - - 自定义 - - - - - - - QFrame::StyledPanel - - - - - - - - - - background-color: rgb(220, 220, 220); - - - QFrame::StyledPanel - - - - - - - - - - 纯白(#FFFFFF) - - - - - - - - - - 半透明像素 - - - - - - 保留颜色 - - - - - - - 与背景色叠加 - - - true - - - - - - - 替换为背景色 - - - - - - - - - - - - - Cancel - clicked() - tpStrategyWind - close() - - - 259 - 309 - - - 259 - 161 - - - - - diff --git a/VisualCraft/BiomeBrowser.cpp b/VisualCraft/BiomeBrowser.cpp index f3bbc11b..fb4b7614 100644 --- a/VisualCraft/BiomeBrowser.cpp +++ b/VisualCraft/BiomeBrowser.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ This file is part of SlopeCraft. #include #include #include -#include +#include BiomeBrowser::BiomeBrowser(QWidget *parent) : QWidget(parent), ui(new Ui::BiomeBrowser) { diff --git a/VisualCraft/BiomeBrowser.h b/VisualCraft/BiomeBrowser.h index da7eb952..49620178 100644 --- a/VisualCraft/BiomeBrowser.h +++ b/VisualCraft/BiomeBrowser.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -30,17 +30,17 @@ class BiomeBrowser; class BiomeBrowser : public QWidget { Q_OBJECT -private: + private: Ui::BiomeBrowser *ui; -public: + public: BiomeBrowser(QWidget *parent); ~BiomeBrowser(); -private: + private: VCL_biome_t biome_selected() const noexcept; bool is_grass_selected() const noexcept; -private slots: + private slots: void when_biome_changed() noexcept; void refresh_colormap() noexcept; }; \ No newline at end of file diff --git a/VisualCraft/BlockBrowser.cpp b/VisualCraft/BlockBrowser.cpp index 073494cc..366a79f1 100644 --- a/VisualCraft/BlockBrowser.cpp +++ b/VisualCraft/BlockBrowser.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -27,13 +27,13 @@ This file is part of SlopeCraft. #include #include #include -#include +#include BlockBrowser::BlockBrowser(QWidget *parent) : QWidget(parent), ui(new Ui::BlockBrowser) { ui->setupUi(this); - this->fecth_content(); + this->fetch_content(); connect(this->ui->combobox_select_blk, &QComboBox::currentIndexChanged, this, &BlockBrowser::update_display); @@ -52,8 +52,8 @@ const VCWind *BlockBrowser::parent() const noexcept { return dynamic_cast(QWidget::parentWidget()); } -void BlockBrowser::fecth_content() noexcept { - // fecth content for avaliable blocks +void BlockBrowser::fetch_content() noexcept { + // fetch content for available blocks this->ui->combobox_select_blk->clear(); this->ui->combobox_select_face->clear(); @@ -102,7 +102,7 @@ void BlockBrowser::fecth_content() noexcept { QVariant::fromValue((void *)blk)); } { - this->ui->tw_version->setRowCount(19 - 12 + 1); + this->ui->tw_version->setRowCount(20 - 12 + 1); this->ui->tw_version->setColumnCount(2); // if (false) for (int r = 0; r < this->ui->tw_version->rowCount(); r++) { @@ -141,7 +141,6 @@ void BlockBrowser::fecth_content() noexcept { } void BlockBrowser::update_display() noexcept { - if (this->ui->combobox_select_blk->currentIndex() < 0) { return; } @@ -266,7 +265,7 @@ void BlockBrowser::on_combobox_select_blk_all_currentIndexChanged( this->ui->tb_blockid_all->setText(VCL_get_block_id(blk)); - for (int v = 12; v <= 19; v++) { + for (int v = 12; v <= 20; v++) { const int r = v - 12; QTableWidgetItem *qtwi = this->ui->tw_version->item(r, 1); assert(qtwi != nullptr); diff --git a/VisualCraft/BlockBrowser.h b/VisualCraft/BlockBrowser.h index 3217ef7b..b5126710 100644 --- a/VisualCraft/BlockBrowser.h +++ b/VisualCraft/BlockBrowser.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -37,20 +37,20 @@ class VCWind; class BlockBrowser : public QWidget { Q_OBJECT -private: + private: Ui::BlockBrowser *ui; - void fecth_content() noexcept; + void fetch_content() noexcept; -private slots: - // manuually connected + private slots: + // manually connected void update_display() noexcept; // auto connected void on_pb_save_current_image_clicked() noexcept; void on_combobox_select_blk_all_currentIndexChanged(int idx) noexcept; -public: + public: explicit BlockBrowser(QWidget *parent); ~BlockBrowser(); @@ -58,4 +58,4 @@ private slots: const VCWind *parent() const noexcept; }; -#endif // SLOPECRAFT_VISUALCRAFT_BLOCKBROWSER_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_BLOCKBROWSER_H \ No newline at end of file diff --git a/VisualCraft/BlockSelector.cpp b/VisualCraft/BlockSelector.cpp index 193288ae..295457ce 100644 --- a/VisualCraft/BlockSelector.cpp +++ b/VisualCraft/BlockSelector.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -89,9 +89,8 @@ void BlockSelector::when_criteria_changed() noexcept { BlockSelector::tr("匹配到%1个方块").arg(count)); } -std::function -BlockSelector::match_functor() const noexcept { - +std::function BlockSelector::match_functor() + const noexcept { bs_criteria cr; for (auto bsc : this->criterias) { diff --git a/VisualCraft/BlockSelector.h b/VisualCraft/BlockSelector.h index 955db838..13729ae9 100644 --- a/VisualCraft/BlockSelector.h +++ b/VisualCraft/BlockSelector.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -33,10 +33,10 @@ class BlockSelectorCriteria; namespace Ui { class BlockSelector; class BlockSelectorCriteria; -} // namespace Ui +} // namespace Ui class bs_criteria { -public: + public: struct alignas(4) statement { bool logic_is_and{true}; VCL_block_attribute_t key; @@ -50,21 +50,21 @@ class bs_criteria { class BlockSelector : public QWidget { Q_OBJECT -private: + private: Ui::BlockSelector *ui; std::vector criterias; -public: + public: BlockSelector(QWidget *parent); ~BlockSelector(); -private: + private: void update_criteria_roles() noexcept; std::function match_functor() const noexcept; -private slots: + private slots: void emplace_back() noexcept; void remove_one(BlockSelectorCriteria *) noexcept; @@ -77,16 +77,16 @@ private slots: class BlockSelectorCriteria : public QWidget { Q_OBJECT -private: + private: Ui::BlockSelectorCriteria *ui; -signals: + signals: void options_changed(); void append(); void remove(BlockSelectorCriteria *); -public: + public: BlockSelectorCriteria(QWidget *parent); ~BlockSelectorCriteria(); @@ -94,10 +94,10 @@ class BlockSelectorCriteria : public QWidget { void update_criteria(bs_criteria &cr) const noexcept; -private slots: + private slots: // auto connected void on_tb_append_clicked() noexcept; void on_tb_remove_clicked() noexcept; }; -#endif // SLOPECRAFT_VISUALCRAFT_BLOCKSELECTOR_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_BLOCKSELECTOR_H \ No newline at end of file diff --git a/VisualCraft/BlockSelectorCriteria.cpp b/VisualCraft/BlockSelectorCriteria.cpp index dc06b64d..8a972b0c 100644 --- a/VisualCraft/BlockSelectorCriteria.cpp +++ b/VisualCraft/BlockSelectorCriteria.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ This file is part of SlopeCraft. #include "BlockSelector.h" #include "ui_BlockSelectorCriteria.h" -#include +#include BlockSelectorCriteria::BlockSelectorCriteria(QWidget *parent) : QWidget(parent), ui(new Ui::BlockSelectorCriteria) { diff --git a/VisualCraft/CMakeLists.txt b/VisualCraft/CMakeLists.txt index 9175553d..42ec82b6 100644 --- a/VisualCraft/CMakeLists.txt +++ b/VisualCraft/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.20) project(VisualCraft_Main VERSION ${SlopeCraft_version} LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -7,18 +7,18 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) -find_package(Qt6 COMPONENTS Widgets LinguistTools Network Concurrent REQUIRED) +find_package(Qt6 COMPONENTS Widgets LinguistTools Network REQUIRED) find_package(magic_enum REQUIRED) find_package(OpenMP REQUIRED) -if(${WIN32}) +if (${WIN32}) configure_file(others/VisualCraft.rc.in others/VisualCraft.rc) set(VisualCraft_win_files ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraft.rc) -else() +else () set(VisualCraft_win_files) -endif() +endif () set(VisualCraft_header_files VCWind.h @@ -73,15 +73,11 @@ set(VisualCraft_project_sources ${VisualCraft_header_files} ${VisualCraft_source_files} ${VisualCraft_ui_files} - ${VisualCraft_ts_files} + + # ${VisualCraft_ts_files} ${VisualCraft_win_files} ) -if(${SlopeCraft_update_ts_files}) - execute_process(COMMAND ${SlopeCraft_Qt_lupdate_executable} ${VisualCraft_header_files} ${VisualCraft_source_files} ${VisualCraft_ui_files} -ts others/VisualCraft_en_US.ts ${SlopeCraft_ts_flags} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND_ERROR_IS_FATAL ANY) -endif() - qt_add_executable(VisualCraft MANUAL_FINALIZATION ${VisualCraft_project_sources}) @@ -90,9 +86,8 @@ target_compile_features(VisualCraft PRIVATE cxx_std_20) target_link_libraries(VisualCraft PRIVATE VisualCraftL - Qt${QT_VERSION_MAJOR}::Widgets - Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Concurrent + Qt6::Widgets + Qt6::Network magic_enum::magic_enum OpenMP::OpenMP_CXX VersionDialog @@ -103,7 +98,7 @@ target_include_directories(VisualCraft PRIVATE ${SlopeCraft_Nlohmann_json_includ set_target_properties(VisualCraft PROPERTIES VERSION ${PROJECT_VERSION} - # MACOSX_BUNDLE_ICON_FILE SlopeCraftIconNew.icns + MACOSX_BUNDLE_ICON_FILE VisualCraft.icns MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.ToKiNoBug.SlopeCraft" MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} @@ -112,9 +107,14 @@ set_target_properties(VisualCraft PROPERTIES ) # translation +qt_add_lupdate(VisualCraft + TS_FILES ${VisualCraft_ts_files} + SOURCES ${VisualCraft_project_sources} + OPTIONS ${SC_lupdate_flags} +) qt_add_lrelease(VisualCraft TS_FILES ${VisualCraft_ts_files} QM_FILES_OUTPUT_VARIABLE VC_qm_files) -qt_add_resources(VisualCraft "translations" +qt_add_resources(VisualCraft "VC_translations" PREFIX "/i18n" BASE ${CMAKE_CURRENT_BINARY_DIR} FILES ${VC_qm_files} @@ -122,8 +122,15 @@ qt_add_resources(VisualCraft "translations" qt_finalize_executable(VisualCraft) -include(${CMAKE_SOURCE_DIR}/cmake/configure_vanilla_zips.cmake) - configure_file(vc-config-to-bin-dir.json.in vc-config.json) +if (${WIN32}) + add_custom_target(SC_create_symlink_VC + COMMAND mklink VisualCraftL.dll "..\\VisualCraftL\\VisualCraftL.dll" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS VisualCraftL + COMMENT "Create symlink to VisualCraftL.dll for VisualCraft.exe") + add_dependencies(SC_create_all_symlinks SC_create_symlink_VC) +endif () + include(install.cmake) \ No newline at end of file diff --git a/VisualCraft/CallbackFunctions.cpp b/VisualCraft/CallbackFunctions.cpp index 614f22a7..f096fb22 100644 --- a/VisualCraft/CallbackFunctions.cpp +++ b/VisualCraft/CallbackFunctions.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ This file is part of SlopeCraft. #include "CallbackFunctions.h" #include #include +#include QWidget *VC_callback::wind{nullptr}; @@ -31,37 +32,47 @@ void VC_callback::callback_receive_report(VCL_report_type_t type, bool flush) noexcept { static std::stringstream ss_warning; - switch (type) { - case VCL_report_type_t::information: { - QMessageBox::information(wind, "Information", QString::fromLocal8Bit(msg)); - return; + QWidget *wind_ptr{nullptr}; + if (wind != nullptr && QThread::currentThread() == wind->thread()) { + wind_ptr = wind; } - case VCL_report_type_t::warning: { - ss_warning << msg; - if (flush) { - std::string res; - ss_warning >> res; + switch (type) { + case VCL_report_type_t::information: { + QMessageBox::information(wind_ptr, "Information", + QString::fromLocal8Bit(msg)); + return; + } + case VCL_report_type_t::warning: { + if (msg != nullptr) { + ss_warning << msg; + } + + if (flush) { + std::string res; + ss_warning >> res; - if (res.empty()) { - return; + if (res.empty()) { + return; + } + QMessageBox::warning(wind_ptr, "Warning", QString::fromLocal8Bit(res)); } - QMessageBox::warning(wind, "Warning", QString::fromLocal8Bit(res)); + return; } - return; - } - case VCL_report_type_t::error: { - auto ret = QMessageBox::critical( - wind, "Fatal error", QString::fromLocal8Bit(msg), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}, - QMessageBox::StandardButton::Close); + case VCL_report_type_t::error: { + QString text = QString::fromLocal8Bit(msg); + + auto ret = QMessageBox::critical( + wind_ptr, "Fatal error", text, + QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, + QMessageBox::StandardButton::Ignore}, + QMessageBox::StandardButton::Close); - if (ret == QMessageBox::StandardButton::Close) { - exit(1919810); + if (ret == QMessageBox::StandardButton::Close) { + exit(1919810); + } + return; } - return; - } } abort(); } \ No newline at end of file diff --git a/VisualCraft/CallbackFunctions.h b/VisualCraft/CallbackFunctions.h index 3a959a04..f57ee691 100644 --- a/VisualCraft/CallbackFunctions.h +++ b/VisualCraft/CallbackFunctions.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -31,6 +31,6 @@ void callback_receive_report(VCL_report_type_t, const char *msg, bool flush) noexcept; extern QWidget *wind; -} // namespace VC_callback +} // namespace VC_callback -#endif // SLOPECRAFT_VISUALCRAFT_CALLBACKFUNCTIONS_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_CALLBACKFUNCTIONS_H \ No newline at end of file diff --git a/VisualCraft/ColorBrowser.cpp b/VisualCraft/ColorBrowser.cpp index 5ac454ba..3cc5d49e 100644 --- a/VisualCraft/ColorBrowser.cpp +++ b/VisualCraft/ColorBrowser.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -62,7 +62,7 @@ void compose_blocks(QImage &dst, const QImage &src, int idx, const int rows = src.height(); const int col_begin = idx * (rows + margin); - const int col_end = col_begin + rows; + [[maybe_unused]] const int col_end = col_begin + rows; assert(dst.width() >= col_end); @@ -131,8 +131,7 @@ void ColorBrowser::setup_table(const uint16_t *const color_id_list, color_id_list[idx], pair.first.data(), &pair.second); if (num <= 0) { - - const auto ret = QMessageBox::warning( + const auto ret[[maybe_unused]] = QMessageBox::warning( this, ColorBrowser::tr("获取颜色表失败"), ColorBrowser::tr( "在尝试获取第%1个颜色(color_id = " @@ -141,19 +140,12 @@ void ColorBrowser::setup_table(const uint16_t *const color_id_list, .arg(idx) .arg(color_id_list[idx]) .arg(num), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, - QMessageBox::StandardButton::Close}, + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}, QMessageBox::StandardButton::Ignore); - if (ret == QMessageBox::StandardButton::Close) { - abort(); - return; - } else { - // ignore the error pair.first.resize(0); continue; - } } pair.first.resize(num); @@ -171,20 +163,13 @@ void ColorBrowser::setup_table(const uint16_t *const color_id_list, VCL_model *const model = VCL_get_block_model(pair.first, VCL_get_resource_pack()); if (model == nullptr) { - - const auto ret = QMessageBox::warning( + const auto ret[[maybe_unused]] = QMessageBox::warning( this, ColorBrowser::tr("计算投影图像失败"), ColorBrowser::tr("在尝试获取方块 \"%1\" 的方块模型时出现错误。") .arg(QString::fromUtf8(VCL_get_block_id(pair.first))), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, - QMessageBox::StandardButton::Close}, + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}, QMessageBox::StandardButton::Ignore); - if (ret == QMessageBox::StandardButton::Close) { - abort(); - return; - } - pair.second = std::move(proj); continue; } @@ -194,21 +179,14 @@ void ColorBrowser::setup_table(const uint16_t *const color_id_list, reinterpret_cast(proj.scanLine(0)), proj.sizeInBytes()); if (!ok) { - const auto ret = QMessageBox::warning( this, ColorBrowser::tr("计算投影图像失败"), ColorBrowser::tr( "成功获取到方块 \"%1\" 的方块模型,但计算投影图像失败。") .arg(QString::fromUtf8(VCL_get_block_id(pair.first))), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, - QMessageBox::StandardButton::Close}, + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}, QMessageBox::StandardButton::Ignore); - if (ret == QMessageBox::StandardButton::Close) { - abort(); - return; - } - pair.second = std::move(proj); VCL_destroy_block_model(model); continue; diff --git a/VisualCraft/ColorBrowser.h b/VisualCraft/ColorBrowser.h index 701151c6..c6e0f8ab 100644 --- a/VisualCraft/ColorBrowser.h +++ b/VisualCraft/ColorBrowser.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -34,14 +34,14 @@ class ColorBrowser; class ColorBrowser : public QWidget { Q_OBJECT -private: + private: Ui::ColorBrowser *ui; // private_class_setup_chart *thread{nullptr}; void setup_table(const uint16_t *const color_id_list, const size_t color_count) noexcept; -public: + public: explicit ColorBrowser(QWidget *parent); ~ColorBrowser(); @@ -51,4 +51,4 @@ class ColorBrowser : public QWidget { // void setup_table_threaded() noexcept; }; -#endif // SLOPECRAFT_VISUALCRAFT_COLORBROWSER_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_COLORBROWSER_H \ No newline at end of file diff --git a/VisualCraft/VCWind.cpp b/VisualCraft/VCWind.cpp index ebada0fe..bf0ba3c6 100644 --- a/VisualCraft/VCWind.cpp +++ b/VisualCraft/VCWind.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -27,8 +27,8 @@ This file is part of SlopeCraft. #include #include #include -#include - +#include +#include #include "VC_block_class.h" #include "ui_VCWind.h" @@ -90,7 +90,8 @@ void VCWind::callback_progress_range_add(void *__w, int delta) noexcept { // utilitiy functions void VCWind::append_default_to_rp_or_bsl(QListWidget *qlw, bool is_rp) noexcept { - const QString txt = is_rp ? VCWind::tr("原版资源包") : VCWind::tr("原版json"); + const QString txt = + is_rp ? VCWind::tr("原版资源包") : VCWind::tr("原版 json"); QListWidgetItem *qlwi = new QListWidgetItem(txt); qlwi->setData(Qt::UserRole, true); @@ -135,6 +136,12 @@ SCL_gameVersion VCWind::current_selected_version() const noexcept { if (ui->rdb_version_19->isChecked()) { return SCL_gameVersion::MC19; } + if (ui->rdb_version_20->isChecked()) { + return SCL_gameVersion::MC20; + } + if (ui->rdb_version_21->isChecked()) { + return SCL_gameVersion::MC21; + } abort(); } @@ -209,12 +216,21 @@ QByteArray VCWind::checksum_basic_colorset_option( hash.addData(json.toUtf8()); } - hash.addData(QByteArrayView((const char *)&opt.face, sizeof(opt.face))); - hash.addData(QByteArrayView((const char *)&opt.version, sizeof(opt.version))); - hash.addData(QByteArrayView((const char *)&opt.layers, sizeof(opt.layers))); - hash.addData(QByteArrayView((const char *)&opt.biome, sizeof(opt.biome))); - hash.addData(QByteArrayView((const char *)&opt.is_leaves_transparent, - sizeof(opt.is_leaves_transparent))); +#if QT_VERSION > 0x060300 +#define VC_PMACRO_QBAV_CLASS QByteArrayView +#else +#define VC_PMACRO_QBAV_CLASS QByteArray +#endif + + hash.addData(VC_PMACRO_QBAV_CLASS{(const char *)&opt.face, sizeof(opt.face)}); + hash.addData( + VC_PMACRO_QBAV_CLASS{(const char *)&opt.version, sizeof(opt.version)}); + hash.addData( + VC_PMACRO_QBAV_CLASS{(const char *)&opt.layers, sizeof(opt.layers)}); + hash.addData( + VC_PMACRO_QBAV_CLASS{(const char *)&opt.biome, sizeof(opt.biome)}); + hash.addData(VC_PMACRO_QBAV_CLASS{(const char *)&opt.is_leaves_transparent, + sizeof(opt.is_leaves_transparent)}); return hash.result(); } @@ -226,12 +242,33 @@ VCL_resource_pack *VCWind::create_resource_pack( rpfiles_qba.reserve(opt.zips.size()); std::vector rpfiles_charp; rpfiles_charp.reserve(opt.zips.size()); + + std::vector rp_contents; + std::vector rp_buffer_references; + for (auto &qstr : opt.zips) { rpfiles_qba.emplace_back(qstr.toUtf8()); rpfiles_charp.emplace_back(rpfiles_qba.back().data()); + QFile file{qstr}; + if (not file.open(QIODevice::OpenModeFlag::ReadOnly | + QIODevice::OpenModeFlag::ExistingOnly)) { + QMessageBox::critical(nullptr, tr("无法读取文件 %1").arg(qstr), + file.errorString()); + return nullptr; + } + rp_contents.emplace_back(file.readAll()); + const auto &content = rp_contents.back(); + rp_buffer_references.emplace_back(content.data(), content.size()); + file.close(); } + assert(rpfiles_qba.size() == rpfiles_charp.size()); + assert(rpfiles_charp.size() == rp_contents.size()); + assert(rp_contents.size() == rp_buffer_references.size()); - return VCL_create_resource_pack(rpfiles_charp.size(), rpfiles_charp.data()); + return VCL_create_resource_pack_from_buffers( + rp_contents.size(), rp_buffer_references.data(), rpfiles_charp.data()); + // return VCL_create_resource_pack(rpfiles_charp.size(), + // rpfiles_charp.data()); } // utilitiy functions @@ -286,7 +323,7 @@ void VCWind::on_pb_remove_rp_clicked() noexcept { void VCWind::on_pb_add_bsl_clicked() noexcept { static QString prev_dir{""}; QStringList jsons = QFileDialog::getOpenFileNames( - this, VCWind::tr("选择方块id json文件"), prev_dir, "*.json"); + this, VCWind::tr("选择方块 id json 文件"), prev_dir, "*.json"); if (jsons.size() <= 0) { return; } @@ -433,15 +470,18 @@ void VCWind::setup_block_widgets() noexcept { } bool VCWind::is_basical_colorset_changed() const noexcept { - static QByteArray hash_prev{""}; + // static QByteArray hash_prev{""}; auto curr_opt = this->current_basic_option(); QByteArray curr_hash = this->checksum_basic_colorset_option(curr_opt); - const bool ret = hash_prev != curr_hash; - hash_prev = curr_hash; + const bool ret = this->basical_option_hash_prev != curr_hash; return ret; } +void VCWind::update_hash_basic(const basic_colorset_option &opt) noexcept { + this->basical_option_hash_prev = this->checksum_basic_colorset_option(opt); +} + void VCWind::setup_basical_colorset() noexcept { if (VCL_is_basic_colorset_ok() && !this->is_basical_colorset_changed()) { return; @@ -467,7 +507,7 @@ void VCWind::setup_basical_colorset() noexcept { VCL_block_state_list *bsl = VCWind::create_block_state_list(current_option); if (bsl == nullptr) { QMessageBox::critical( - this, VCWind::tr("方块状态列表json解析失败"), + this, VCWind::tr("方块状态列表 json 解析失败"), VCWind::tr("在此窗口之前弹出的错误信息非常重要,请将它汇报给开发者。"), QMessageBox::StandardButtons{QMessageBox::StandardButton::Close}); VCL_destroy_resource_pack(rp); @@ -485,9 +525,12 @@ void VCWind::setup_basical_colorset() noexcept { const bool success = VCL_set_resource_move(&rp, &bsl, option); + VCL_destroy_block_state_list(bsl); + VCL_destroy_resource_pack(rp); + if (!success) { const auto ret = QMessageBox::critical( - this, VCWind::tr("资源包/方块状态列表json解析失败"), + this, VCWind::tr("资源包/方块状态列表 json 解析失败"), VCWind::tr("部分方块的投影图像计算失败,或者总颜色数量超过上限(65534)" "。尝试移除解析失败的资源包/方块列表,或者减小最大层数。"), QMessageBox::StandardButtons{QMessageBox::StandardButton::Close}); @@ -497,6 +540,7 @@ void VCWind::setup_basical_colorset() noexcept { } } + this->update_hash_basic(current_option); this->setup_block_widgets(); this->ui->ac_browse_basic_colors->setEnabled(true); } @@ -515,19 +559,24 @@ QByteArray VCWind::checksum_allowed_colorset_option( bool VCWind::is_allowed_colorset_changed( allowed_colorset_option *opt) const noexcept { - static QByteArray prev_hash{""}; + // static QByteArray prev_hash{""}; this->selected_blocks(&opt->blocks); QByteArray cur_hash = VCWind::checksum_allowed_colorset_option(*opt); - const bool ret = prev_hash != cur_hash; + const bool ret = this->allowed_option_hash_prev != cur_hash; - prev_hash = cur_hash; + // prev_hash = cur_hash; return ret; } +void VCWind::update_hash_allowed(const allowed_colorset_option &opt) noexcept { + this->allowed_option_hash_prev = this->checksum_allowed_colorset_option(opt); +} + void VCWind::setup_allowed_colorset() noexcept { this->setup_basical_colorset(); + this->ui->ac_export_test_schem->setEnabled(true); allowed_colorset_option cur_option; if (VCL_is_allowed_colorset_ok() && @@ -558,10 +607,10 @@ void VCWind::setup_allowed_colorset() noexcept { } } + this->update_hash_allowed(cur_option); this->ui->gb_convert_algo->setTitle( VCWind::tr("调色算法 (共%1种颜色)") .arg(VCL_get_allowed_colors(nullptr, 0))); - this->clear_convert_cache(); this->ui->ac_browse_allowed_colors->setEnabled(true); } @@ -601,8 +650,7 @@ void VCWind::on_tb_add_images_clicked() noexcept { const auto ret = QMessageBox::warning( this, VCWind::tr("读取图片失败"), VCWind::tr("无法读取图片%" - "1。图片可能是不支持的格式,或者已经损坏" - "。图像过大也可能导致此错误。") + "1。图片可能是不支持的格式,或者已经损坏。") .arg(filename), QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, QMessageBox::StandardButton::Close}, @@ -688,10 +736,21 @@ SCL_convertAlgo VCWind::current_selected_algo() const noexcept { } void VCWind::show_image(decltype(image_cache)::iterator it) noexcept { - this->ui->label_raw_image->setPixmap(QPixmap::fromImage(it->second.first)); + const int margin_pixels = 8; + const auto margin = QMargins{0, 0, margin_pixels, margin_pixels}; + this->ui->label_raw_image->setPixmap( + QPixmap::fromImage(it->second.first) + .scaled(this->ui->label_raw_image->size().shrunkBy(margin), + Qt::KeepAspectRatio, Qt::FastTransformation)); if (!it->second.second.isNull()) { - this->ui->lable_converted->setPixmap(QPixmap::fromImage(it->second.second)); + auto raw_pixmap = QPixmap::fromImage(it->second.second); + const auto size = this->ui->lable_converted->size().shrunkBy(margin); + qDebug() << "size = " << size; + // for (int i = 0; i < 1; i++) { + this->ui->lable_converted->setPixmap( + raw_pixmap.scaled(size, Qt::KeepAspectRatio, Qt::FastTransformation)); + // } return; } @@ -701,14 +760,16 @@ void VCWind::show_image(decltype(image_cache)::iterator it) noexcept { const bool ok = this->kernel->convert(this->current_selected_algo(), this->ui->cb_algo_dither->isChecked()); if (!ok) { - assert(false); + it->second.second = QImage{}; + this->ui->lable_converted->setPixmap(QPixmap{}); return; } - int64_t rows, cols; + int64_t rows = 0, cols = 0; this->kernel->converted_image(nullptr, &rows, &cols, true); if (rows <= 0 || cols <= 0) { - assert(false); + it->second.second = QImage{}; + this->ui->lable_converted->setPixmap(QPixmap{}); return; } @@ -716,10 +777,11 @@ void VCWind::show_image(decltype(image_cache)::iterator it) noexcept { memset(img.scanLine(0), 0xFF, img.sizeInBytes()); this->kernel->converted_image(reinterpret_cast(img.scanLine(0)), - nullptr, nullptr, true); + nullptr, nullptr, false); it->second.second = img; - this->ui->lable_converted->setPixmap(QPixmap::fromImage(img)); + const auto size = this->ui->lable_converted->size().shrunkBy(margin); + this->ui->lable_converted->setPixmap(QPixmap::fromImage(img).scaled(size, Qt::KeepAspectRatio, Qt::FastTransformation)); } void VCWind::on_lw_image_files_itemClicked(QListWidgetItem *item) noexcept { @@ -758,13 +820,6 @@ void VCWind::clear_convert_cache() noexcept { } } -void VCWind::on_cb_show_raw_size_stateChanged(int state) noexcept { - bool autoscale = (state == 0); - - this->ui->label_raw_image->setScaledContents(autoscale); - this->ui->lable_converted->setScaledContents(autoscale); -} - void VCWind::on_cb_show_raw_stateChanged(int state) noexcept { if (state) { this->ui->label_raw_image->show(); diff --git a/VisualCraft/VCWind.h b/VisualCraft/VCWind.h index f88b1951..09a9fd8a 100644 --- a/VisualCraft/VCWind.h +++ b/VisualCraft/VCWind.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -78,10 +78,13 @@ class VCWind : public QMainWindow { // for all pages Ui::VCWind *ui; VCL_Kernel *const kernel{nullptr}; + // for page 0 // VCL_resource_pack *rp{nullptr}; // VCL_block_state_list *bsl{nullptr}; // bool is_basical_colorset_changed{true}; + QByteArray basical_option_hash_prev{""}; + QByteArray allowed_option_hash_prev{""}; // for page 1 std::map map_VC_block_class{}; @@ -129,6 +132,9 @@ class VCWind : public QMainWindow { [[nodiscard]] static VCL_block_state_list *create_block_state_list( const basic_colorset_option &opt) noexcept; + void update_hash_basic(const basic_colorset_option &opt) noexcept; + void update_hash_allowed(const allowed_colorset_option &opt) noexcept; + // receive current selected version from ui SCL_gameVersion current_selected_version() const noexcept; VCL_face_t current_selected_face() const noexcept; @@ -186,6 +192,9 @@ class VCWind : public QMainWindow { void flush_export_tabel() noexcept; + [[nodiscard]] std::unordered_map + id_blockclass_map() noexcept; + static constexpr int export_col_filename = 0; static constexpr int export_col_imagesize = 1; static constexpr int export_col_lite = 2; @@ -212,11 +221,13 @@ class VCWind : public QMainWindow { // for all pages ------------------------------------------ void on_ac_flush_warnings_triggered() noexcept; + void on_ac_export_test_schem_triggered() noexcept; // auto connected void on_tabWidget_main_currentChanged(int page) noexcept; // auto connected + void on_ac_tutorial_triggered() noexcept; void on_ac_about_VisualCraft_triggered() noexcept; void on_ac_contact_bilibili_triggered() noexcept; void on_ac_contact_github_repo_triggered() noexcept; @@ -260,8 +271,12 @@ class VCWind : public QMainWindow { // auto connected void on_pb_custom_select_clicked() noexcept; + void on_pb_load_preset_clicked() noexcept; + void on_pb_save_preset_clicked() noexcept; + // manually connected void setup_block_widgets() noexcept; + // void when_allowed_colorset_changed() noexcept; // for page 2 ------------------------------------------ @@ -270,7 +285,6 @@ class VCWind : public QMainWindow { void on_tb_remove_images_clicked() noexcept; // auto connected - void on_cb_show_raw_size_stateChanged(int state) noexcept; void on_cb_show_raw_stateChanged(int state) noexcept; void on_cb_show_converted_stateChanged(int state) noexcept; void on_lw_image_files_itemClicked(QListWidgetItem *item) noexcept; diff --git a/VisualCraft/VCWind.ui b/VisualCraft/VCWind.ui index 85481bf8..8e74358f 100644 --- a/VisualCraft/VCWind.ui +++ b/VisualCraft/VCWind.ui @@ -3,14 +3,14 @@ VCWind - Qt::NonModal + Qt::WindowModality::NonModal 0 0 1057 - 476 + 490 @@ -29,7 +29,7 @@ 0 - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTop|Qt::AlignmentFlag::AlignTrailing
@@ -43,16 +43,16 @@ - QTabWidget::North + QTabWidget::TabPosition::North - QTabWidget::Rounded + QTabWidget::TabShape::Rounded 0 - Qt::ElideNone + Qt::TextElideMode::ElideNone @@ -62,7 +62,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -78,17 +78,17 @@ 游戏版本 - - + + - 1.15 + 1.18 - - + + - 1.12 + 1.15 @@ -99,6 +99,23 @@ + + + + 1.17 + + + + + + + 1.19 + + + false + + + @@ -106,34 +123,34 @@ - - + + - 1.14 + 1.12 - - + + - 1.17 + 1.14 - - + + - 1.19 + 1.20 true - - + + - 1.18 + 1.21 @@ -188,10 +205,10 @@ true - QAbstractItemView::InternalMove + QAbstractItemView::DragDropMode::InternalMove - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection @@ -200,13 +217,13 @@ - QListView::Snap + QListView::Movement::Snap - QListView::Batched + QListView::LayoutMode::Batched - QListView::ListMode + QListView::ViewMode::ListMode true @@ -245,19 +262,19 @@ - QAbstractItemView::InternalMove + QAbstractItemView::DragDropMode::InternalMove - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection - QListView::Snap + QListView::Movement::Snap - QListView::Batched + QListView::LayoutMode::Batched - QListView::ListMode + QListView::ViewMode::ListMode true @@ -300,7 +317,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -330,33 +347,6 @@ 方块列表 - - - - 全部方块 - - - - - - true - - - - - 0 - 0 - 861 - 273 - - - - - - - - - @@ -401,7 +391,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -428,17 +418,67 @@ + + + + 预设 + + + + + + 加载预设 + + + + + + + 保存当前预设 + + + + + + + + + + 全部方块 + + + + + + true + + + + + 0 + 0 + 881 + 305 + + + + + + + + +
选择图片 - + - Qt::Vertical + Qt::Orientation::Vertical @@ -446,27 +486,17 @@ - 0 - 0 + 1 + 1 - QFrame::StyledPanel + QFrame::Shape::StyledPanel - true - - - - - - - 显示原始尺寸 - - false @@ -493,6 +523,12 @@ + + + 0 + 0 + + 抖动 @@ -500,6 +536,12 @@ + + + 0 + 0 + + HSV @@ -507,6 +549,12 @@ + + + 0 + 0 + + RGB @@ -517,6 +565,12 @@ + + + 0 + 0 + + Lab00(CIEDE00) @@ -524,6 +578,12 @@ + + + 0 + 0 + + RGB+ @@ -531,6 +591,12 @@ + + + 0 + 0 + + Lab94(CIEDE94) @@ -538,6 +604,12 @@ + + + 0 + 0 + + XYZ @@ -549,22 +621,22 @@ - + 0 - 0 + 1 true - QAbstractItemView::DropOnly + QAbstractItemView::DragDropMode::DropOnly - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection - QListView::Static + QListView::Movement::Static false @@ -578,18 +650,18 @@ - 0 - 0 + 1 + 1 - QFrame::StyledPanel + QFrame::Shape::StyledPanel - true + false @@ -633,7 +705,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -809,7 +881,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -981,7 +1053,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1072,10 +1144,10 @@ - QAbstractItemView::SingleSelection + QAbstractItemView::SelectionMode::SingleSelection - QAbstractItemView::SelectItems + QAbstractItemView::SelectionBehavior::SelectItems true @@ -1102,7 +1174,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1161,7 +1233,7 @@ 0 0 1057 - 27 + 33 @@ -1205,6 +1277,7 @@ + @@ -1322,6 +1395,11 @@ 刷新警告信息 + + + 使用教程 + + diff --git a/VisualCraft/VCWind_export.cpp b/VisualCraft/VCWind_export.cpp index 842359ac..9d78d6cd 100644 --- a/VisualCraft/VCWind_export.cpp +++ b/VisualCraft/VCWind_export.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -90,13 +90,12 @@ void VCWind::flush_export_tabel() noexcept { } void VCWind::on_pb_select_export_dir_clicked() noexcept { - if (this->ui->combobox_export_type->currentIndex() < 0) { QMessageBox::warning( this, VCWind::tr("错误操作"), VCWind::tr( - "设置任何一种导出类型的输出位置时,都需要在左侧的combo " - "box中选择一个导出类型。请先选择一种导出类型,再设置导出位置。"), + "设置任何一种导出类型的输出位置时,都需要在左侧的 combo " + "box 中选择一个导出类型。请先选择一种导出类型,再设置导出位置。"), QMessageBox::StandardButtons{QMessageBox::StandardButton::Ok}); return; } @@ -111,30 +110,30 @@ void VCWind::on_pb_select_export_dir_clicked() noexcept { bool strip_image_extension = true; int dest_col = -1; switch (this->ui->combobox_export_type->currentIndex()) { - case 0: - suffix = ".litematic"; - dest_col = VCWind::export_col_lite; - break; - case 1: - suffix = ".nbt"; - dest_col = VCWind::export_col_structure; - break; - case 2: - suffix = ".schem"; - dest_col = VCWind::export_col_schem; - break; - case 3: - suffix = ""; - dest_col = VCWind::export_col_converted; - strip_image_extension = false; - break; - case 4: - suffix = ".png"; - dest_col = VCWind::export_col_flagdiagram; - break; - default: - abort(); - return; + case 0: + suffix = ".litematic"; + dest_col = VCWind::export_col_lite; + break; + case 1: + suffix = ".nbt"; + dest_col = VCWind::export_col_structure; + break; + case 2: + suffix = ".schem"; + dest_col = VCWind::export_col_schem; + break; + case 3: + suffix = ""; + dest_col = VCWind::export_col_converted; + strip_image_extension = false; + break; + case 4: + suffix = ".png"; + dest_col = VCWind::export_col_flagdiagram; + break; + default: + abort(); + return; } const int c = dest_col; @@ -173,19 +172,15 @@ bool VCWind::export_lite(const QString &lite_dest, this->ui->pte_lite_regionname->toPlainText().toUtf8().data()); if (!success) { const auto ret = QMessageBox::critical( - this, VCWind::tr("导出litematica失败"), - VCWind::tr("VisualCraftL不能为图像\"%1\"生成投影文件\"%2\"。") + this, VCWind::tr("导出 litematica 失败"), + VCWind::tr("VisualCraftL 不能为图像\"%1\"生成投影文件\"%2\"。") .arg(image_filename) .arg(lite_dest), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}, - QMessageBox::StandardButton::Close); - if (ret == QMessageBox::StandardButton::Ignore) { - return false; - } else { - abort(); - return false; - } + QMessageBox::StandardButtons{ + QMessageBox::StandardButton::Ignore + }, + QMessageBox::StandardButton::Ignore); + return false; } return true; } @@ -198,18 +193,14 @@ bool VCWind::export_structure(const QString &nbt_dest, if (!success) { const auto ret = QMessageBox::critical( this, VCWind::tr("导出原版结构方块文件失败"), - VCWind::tr("VisualCraftL不能为图像\"%1\"生成结构方块文件\"%2\"。") + VCWind::tr("VisualCraftL 不能为图像\"%1\"生成结构方块文件\"%2\"。") .arg(image_filename) .arg(nbt_dest), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}, - QMessageBox::StandardButton::Close); - if (ret == QMessageBox::StandardButton::Ignore) { - return false; - } else { - abort(); - return false; - } + QMessageBox::StandardButtons{ + QMessageBox::StandardButton::Ignore + }, + QMessageBox::StandardButton::Ignore); + return false; } return true; } @@ -238,20 +229,17 @@ bool VCWind::export_schem(const QString &schem_dest, this->ui->pte_weschem_name->toPlainText().toUtf8().data(), mods_charp.data(), mods_charp.size()); if (!success) { - const auto ret = QMessageBox::critical( - this, VCWind::tr("导出World Edit原理图失败"), - VCWind::tr("VisualCraftL不能为图像\"%1\"生成World Edit原理图\"%2\"。") - .arg(image_filename) - .arg(schem_dest), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}, - QMessageBox::StandardButton::Close); - if (ret == QMessageBox::StandardButton::Ignore) { - return false; - } else { - abort(); + const auto ret[[maybe_unused]] = QMessageBox::critical( + this, VCWind::tr("导出 World Edit 原理图失败"), + VCWind::tr( + "VisualCraftL 不能为图像\"%1\"生成 World Edit 原理图\"%2\"。") + .arg(image_filename) + .arg(schem_dest), + QMessageBox::StandardButtons{ + QMessageBox::StandardButton::Ignore + }, + QMessageBox::StandardButton::Ignore); return false; - } } return true; } @@ -260,39 +248,28 @@ bool VCWind::export_converted(const QString &converted_image_dest_path, const QImage &new_img) noexcept { bool success = new_img.save(converted_image_dest_path); if (!success) { - const auto ret = QMessageBox::critical( - this, VCWind::tr("保存转化后图像失败"), - VCWind::tr("QImage未能生成\"%1\"。").arg(converted_image_dest_path), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}, - QMessageBox::StandardButton::Close); - if (ret == QMessageBox::StandardButton::Ignore) { - return false; - } else { - abort(); + const auto ret [[maybe_unused]] = QMessageBox::critical( + this, VCWind::tr("保存转化后图像失败"), + VCWind::tr("QImage 未能生成\"%1\"。").arg(converted_image_dest_path), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}, + QMessageBox::StandardButton::Ignore); return false; - } } return true; } bool VCWind::export_flatdiagram(const QString &diagram_dest) noexcept { const QStringList qsl = diagram_dest.split(';'); - if (qsl.size() != VCL_get_max_block_layers()) { - const auto ret = QMessageBox::critical( - this, VCWind::tr("平面示意图输入错误"), - VCWind::tr("应输入%1个以\";\"分隔的文件名,但实际上输入了%" - "2个。\n您输入的%2个文件名是:\n%3") - .arg(VCL_get_max_block_layers()) - .arg(qsl.size()) - .arg(diagram_dest), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}); - if (ret == QMessageBox::StandardButton::Ignore) { + if (qsl.size() not_eq VCL_get_max_block_layers()) { + const auto ret [[maybe_unused]] = QMessageBox::critical( + this, VCWind::tr("平面示意图输入错误"), + VCWind::tr("应输入%1个以\";\"分隔的文件名,但实际上输入了%" + "2 个。\n您输入的%2个文件名是:\n%3") + .arg(VCL_get_max_block_layers()) + .arg(qsl.size()) + .arg(diagram_dest), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}); return false; - } - abort(); - return false; } VCL_Kernel::flag_diagram_option option; @@ -320,19 +297,14 @@ bool VCWind::export_flatdiagram(const QString &diagram_dest) noexcept { qsl[l].toLocal8Bit().data(), option, l); if (!ok) { - const auto ret = QMessageBox::critical( - this, VCWind::tr("导出平面示意图失败"), - VCWind::tr("尝试为原图生成第%1个平面示意图(%2)时出现了错误。") - .arg(l) - .arg(qsl[l]), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}); - - if (ret == QMessageBox::StandardButton::Ignore) { + const auto ret [[maybe_unused]] = QMessageBox::critical( + this, VCWind::tr("导出平面示意图失败"), + VCWind::tr("尝试为原图生成第%1个平面示意图(%2)时出现了错误。") + .arg(l) + .arg(qsl[l]), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}); + return false; - } - abort(); - return true; } } @@ -351,8 +323,8 @@ void VCWind::on_pb_execute_clicked() noexcept { if (it == this->image_cache.end()) { QMessageBox::critical( this, VCWind::tr("致命逻辑错误"), - VCWind::tr("导出页码表格中的图片\"%1\"不能在this->image_" - "cache中找到对应的缓存。请将这个错误反馈给软件开发者。") + VCWind::tr("导出页码表格中的图片\"%1\"不能在 this->image_" + "cache 中找到对应的缓存。请将这个错误反馈给软件开发者。") .arg(image_filename)); abort(); } @@ -397,14 +369,14 @@ void VCWind::on_pb_execute_clicked() noexcept { ->setText( QStringLiteral("%i %").arg(100.0f * task_finished / task_num)); - QImage new_img(it->second.first.height(), it->second.first.width(), + QImage new_img(it->second.first.width(), it->second.first.height(), QImage::Format_ARGB32); this->kernel->converted_image((uint32_t *)new_img.scanLine(0), nullptr, - nullptr, true); + nullptr, false); it->second.second = new_img; - if (!converted_image_dest_path.isEmpty()) { - if (!this->export_converted(converted_image_dest_path, new_img)) { + if (not converted_image_dest_path.isEmpty()) { + if (not this->export_converted(converted_image_dest_path, new_img)) { continue; } } @@ -413,8 +385,8 @@ void VCWind::on_pb_execute_clicked() noexcept { ->setText( QStringLiteral("%1 %").arg(100.0f * task_finished / task_num)); - if (!diagram_dest.isEmpty()) { - if (!this->export_flatdiagram(diagram_dest)) { + if (not diagram_dest.isEmpty()) { + if (not this->export_flatdiagram(diagram_dest)) { continue; } task_finished++; @@ -431,19 +403,13 @@ void VCWind::on_pb_execute_clicked() noexcept { success = this->kernel->build(); if (!success) { - const auto ret = QMessageBox::critical( - this, VCWind::tr("构建三维结构失败"), - VCWind::tr("VisualCraftL不能为图像\"%1\"构建三维结构。") + const auto ret[[maybe_unused]] = QMessageBox::critical( + this, VCWind::tr("构建三维结构失败"), + VCWind::tr("VisualCraftL 不能为图像\"%1\"构建三维结构。") .arg(image_filename), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}, - QMessageBox::StandardButton::Close); - if (ret == QMessageBox::StandardButton::Ignore) { + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}, + QMessageBox::StandardButton::Ignore); continue; - } else { - abort(); - return; - } } task_finished++; this->ui->tw_build->item(r, VCWind::export_col_progress) @@ -486,4 +452,20 @@ void VCWind::on_pb_execute_clicked() noexcept { for (auto &pair : this->image_cache) { this->setup_image(pair.second.first); } +} + +void VCWind::on_ac_export_test_schem_triggered() noexcept { + static QString prev_dir{""}; + QString file = QFileDialog::getSaveFileName( + this, tr("保存测试投影"), prev_dir, "*.litematic;;*.nbt;;*.schem"); + if (file.isEmpty()) { + return; + } + prev_dir = QFileInfo{file}.dir().absolutePath(); + + bool ok = VCL_export_test_litematic(file.toLocal8Bit().data()); + if (!ok) { + QMessageBox::warning(this, tr("无法输出测试投影"), + tr("详细错误信息在之前的窗口中")); + } } \ No newline at end of file diff --git a/VisualCraft/VCWind_gpu.cpp b/VisualCraft/VCWind_gpu.cpp index 20375888..5aff1419 100644 --- a/VisualCraft/VCWind_gpu.cpp +++ b/VisualCraft/VCWind_gpu.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,55 +20,111 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include #include -#include #include #include +#include +#include #include "VCWind.h" #include "ui_VCWind.h" -std::string get_cpu_name(bool &error) noexcept { - int buffer[5]; - uint8_t *const buffer_cptr = reinterpret_cast(buffer); - constexpr uint32_t input[3] = {0x80000002, 0x80000003, 0x80000004}; +QString get_cpu_name(bool &error) noexcept { + QProcess proc{0}; + error = true; + +#ifdef WIN32 + const char *command = "wmic cpu get name"; +#elif defined(__linux__) + const char *command = "sh -c \"cat /proc/cpuinfo | grep name\""; +#elif defined(__APPLE__) + const char *command = "sysctl machdep.cpu.brand_string"; +#else +#warning Unknown OS + const char *command = nullptr; + return {}; +#endif - char str[1024] = ""; + proc.startCommand(command); + if (!proc.waitForStarted()) { + return {}; + } + if (!proc.waitForFinished()) { + return {}; + } - error = false; - for (auto i : input) { - memset(buffer, 0, sizeof(buffer)); - try { - __cpuid(i, buffer[0], buffer[1], buffer[2], buffer[3]); - } catch (std::exception &e) { - strcpy(str, "Instruction cpuid failed. Detail : "); - strcpy(str, e.what()); - error = true; - return str; - } - if constexpr (false) { - for (size_t o = 0; o < 4 * sizeof(uint32_t); o++) { - std::cout << (int)buffer_cptr[o] << ", "; - } - std::cout << std::endl; - } + QString output = QString::fromUtf8(proc.readAllStandardOutput()); - strcat(str, reinterpret_cast(buffer_cptr)); +#ifdef WIN32 + output = output.remove(QChar{'\r'}); + auto splitted = output.split('\n'); + if (splitted.size() < 2) { + return {}; + } + + if (splitted[0].remove(QChar{' '}) != QStringLiteral("Name")) { + return {}; } - return str; + + const auto cpu_name = splitted[1]; + +#elif defined(__linux__) + + output.remove('\t'); + + auto spilitted = output.split('\n'); + if (spilitted.size() <= 0) { + return {}; + } + const auto line0 = spilitted[0]; + if (!line0.contains(':')) { + return {}; + } + auto line0_splitted = line0.split(':'); + if (line0_splitted.size() != 2) { + return {}; + } + if (line0_splitted[0].remove(' ') != "modelname") { + return {}; + } + const auto cpu_name = line0_splitted[1]; + +#elif defined(__APPLE__) + output.remove('\n'); + auto split_out = output.split(':'); + if (split_out.size() < 2) { + return {}; + } + if (split_out[0] != "machdep.cpu.brand_string") { + return {}; + } + auto cpu_name = split_out[1]; + cpu_name.remove('\t'); + while (cpu_name.front() == ' ') { + cpu_name.removeFirst(); + } + while (cpu_name.back() == ' ') { + cpu_name.removeLast(); + } +#else +#warning "Unknown OS" + QString cpu_name{}; +#endif + error = false; + return cpu_name; } void VCWind::refresh_gpu_info() noexcept { - this->ui->sb_threads->setValue(std::thread::hardware_concurrency()); + this->ui->sb_threads->setValue((int)std::thread::hardware_concurrency()); this->ui->tw_gpu->clear(); this->ui->combobox_select_device->clear(); { bool error = false; - QString text("CPU : " + QString::fromUtf8(get_cpu_name(error))); + QString text("CPU : " + get_cpu_name(error)); + // QString text("CPU : " +); QTreeWidgetItem *cpu = new QTreeWidgetItem; cpu->setText(0, text); if (error) { @@ -93,7 +149,7 @@ void VCWind::refresh_gpu_info() noexcept { QString platname{}; if (plat == nullptr) { platname = - VCWind::tr("无法获取platform信息. 请检查驱动. OpenCL错误码: %1.") + VCWind::tr("无法获取 platform 信息。请检查驱动。OpenCL 错误码:%1.") .arg(errcode); } else { platname = QString::fromLocal8Bit(VCL_get_platform_name(plat)); @@ -116,7 +172,7 @@ void VCWind::refresh_gpu_info() noexcept { QString devicename{}; if (dev == nullptr) { devicename = - VCWind::tr("无法获取device信息. 请检查驱动. OpenCL错误码: %1") + VCWind::tr("无法获取 device 信息。请检查驱动。OpenCL 错误码:%1") .arg(errcode); } else { devicename = QString::fromLocal8Bit(VCL_get_device_name(dev)); @@ -127,7 +183,9 @@ void VCWind::refresh_gpu_info() noexcept { twi->addChild(twi_device); - this->ui->combobox_select_device->addItem(devicename, QPoint(pid, did)); + this->ui->combobox_select_device->addItem( + QStringLiteral("%1 / %2").arg(platname, devicename), + QPoint(pid, did)); VCL_release_device(dev); } @@ -171,14 +229,20 @@ void VCWind::on_combobox_select_device_currentIndexChanged(int idx) noexcept { const QString err = this->update_gpu_device(current_choice); - if (!err.isEmpty()) { - auto ret = QMessageBox::critical( - this, VCWind::tr("设置计算设备失败"), err, - QMessageBox::StandardButtons{QMessageBox::StandardButton::Close, - QMessageBox::StandardButton::Ignore}); - if (ret == QMessageBox::StandardButton::Close) { - abort(); - } + if (err.isEmpty()) { + return; + } + QMessageBox msg_box{this}; + msg_box.setStandardButtons(QMessageBox::StandardButtons{ + QMessageBox::StandardButton::Close, QMessageBox::StandardButton::Ignore}); + msg_box.setWindowTitle(VCWind::tr("设置计算设备失败")); + msg_box.setText( + tr("这不是一个致命错误,您可以选择其他的显卡,或者只使用 CPU " + "计算。点击 Ignore 将忽略这个错误,点击 Close 将关闭 VisualCraft")); + msg_box.setDetailedText(err); + auto ret = msg_box.exec(); + if (ret == QMessageBox::StandardButton::Close) { + exit(1); } } @@ -197,22 +261,33 @@ QString VCWind::update_gpu_device(QPoint current_choice) noexcept { VCL_GPU_Platform *plat = VCL_get_platform(current_choice.x()); if (plat == nullptr) { - return VCWind::tr("创建GPU平台失败,平台序号为%1,设备序号为%2") + return VCWind::tr("创建 GPU 平台失败,平台序号为%1,设备序号为%2") .arg(current_choice.x()) .arg(current_choice.y()); } VCL_GPU_Device *dev = VCL_get_device(plat, current_choice.y()); if (dev == nullptr) { - return VCWind::tr("创建GPU设备失败,平台序号为%1,设备序号为%2") + return VCWind::tr("创建 GPU 设备失败,平台序号为%1,设备序号为%2") .arg(current_choice.x()) .arg(current_choice.y()); } - - if (!this->kernel->set_gpu_resource(plat, dev)) { - return VCWind::tr("设置GPU设备失败。,平台序号为%1,设备序号为%2") - .arg(current_choice.x()) - .arg(current_choice.y()); + { + std::string error_msg; + error_msg.resize(4096); + VCL_Kernel::gpu_options gpu_options; + VCL_string_deliver s{ + .data = error_msg.data(), .size = 0, .capacity = error_msg.size()}; + gpu_options.error_message = &s; + if (!this->kernel->set_gpu_resource(plat, dev, gpu_options)) { + error_msg.resize(s.size); + return VCWind::tr( + "设置 GPU " + "设备失败。平台序号为%1,设备序号为%2,详细错误信息:\n%3") + .arg(current_choice.x()) + .arg(current_choice.y()) + .arg(error_msg.data()); + } } VCL_release_device(dev); diff --git a/VisualCraft/VCWind_select.cpp b/VisualCraft/VCWind_select.cpp index 5fdbaf21..a8378e99 100644 --- a/VisualCraft/VCWind_select.cpp +++ b/VisualCraft/VCWind_select.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,10 +23,11 @@ This file is part of SlopeCraft. #include "VCWind.h" #include "VC_block_class.h" #include "ui_VCWind.h" +#include +#include void VCWind::apply_selection( std::function selector) noexcept { - for (auto &blk_class : this->map_VC_block_class) { for (auto &blk_pair : blk_class.second->blocks_vector()) { selector(blk_pair.first, blk_pair.second); @@ -107,4 +108,118 @@ void VCWind::on_pb_invselect_blockwise_clicked() noexcept { this->apply_selection([](const VCL_block *, QCheckBox *cb) { cb->setChecked(!cb->isChecked()); }); +} + +[[nodiscard]] std::unordered_map +VCWind::id_blockclass_map() noexcept { + std::unordered_map ret; + for (auto &blk_class_pair : this->map_VC_block_class) { + for (auto &blk : blk_class_pair.second->blocks_vector()) { + ret.emplace(VCL_get_block_id(blk.first), blk.second); + } + } + + return ret; +} + +void VCWind::on_pb_load_preset_clicked() noexcept { + static QString prev_dir{""}; + QString preset_file = QFileDialog::getOpenFileName(this, tr("选择预设文件"), + prev_dir, "*.vc_preset"); + if (preset_file.isEmpty()) { + return; + } + prev_dir = QFileInfo{preset_file}.dir().absolutePath(); + + std::string err; + err.resize(8192); + VCL_string_deliver vsd{err.data(), err.size(), err.size()}; + auto preset = VCL_load_preset(preset_file.toLocal8Bit().data(), &vsd); + err.resize(vsd.size); + + if (preset == nullptr) { + QMessageBox::warning( + this, tr("加载预设失败"), + tr("无法解析预设文件,详细信息:\n%1").arg(err.data()), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ok}); + return; + } + + for (auto &cls : this->map_VC_block_class) { + const bool enable = VCL_preset_contains_class(preset, cls.first); + cls.second->chbox_enabled()->setChecked(enable); + } + // select ids + { + std::vector ids; + ids.resize(VCL_preset_num_ids(preset)); + { + const size_t num = VCL_preset_get_ids(preset, ids.data(), ids.size()); + ids.resize(num); + } + + auto id_blk_mapping = this->id_blockclass_map(); + for (auto &pair : id_blk_mapping) { + pair.second->setChecked(false); + } + for (auto id : ids) { + auto it = id_blk_mapping.find(id); + if (it == id_blk_mapping.end()) { + auto ret = QMessageBox::warning( + this, tr("无法加载预设"), + tr("预设中包含%1,但可用方块中没有 id 相同的方块。点击 Ignore " + "以跳过这个方块,点击 Close 终止此次操作。"), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}, + QMessageBox::StandardButton::Close); + if (ret == QMessageBox::StandardButton::Close) { + VCL_destroy_preset(preset); + return; + } + continue; + } + it->second->setChecked(true); + } + } + + VCL_destroy_preset(preset); +} + +void VCWind::on_pb_save_preset_clicked() noexcept { + static QString prev_dir{""}; + QString preset_file = QFileDialog::getSaveFileName(this, tr("选择预设文件"), + prev_dir, "*.vc_preset"); + if (preset_file.isEmpty()) { + return; + } + prev_dir = QFileInfo{preset_file}.dir().absolutePath(); + + VCL_preset *preset = VCL_create_preset(); + + for (const auto &it : this->map_VC_block_class) { + if (it.second->chbox_enabled()->isChecked()) { + VCL_preset_emplace_class(preset, it.first); + } + for (const auto &jt : it.second->blocks_vector()) { + if (jt.second->isChecked()) { + VCL_preset_emplace_id(preset, VCL_get_block_id(jt.first)); + } + } + } + + std::string err; + err.resize(8192); + VCL_string_deliver vsd{err.data(), err.size(), err.size()}; + const bool ok = + VCL_save_preset(preset, preset_file.toLocal8Bit().data(), &vsd); + err.resize(vsd.size); + + if (!ok) { + QMessageBox::warning( + this, tr("保存预设文件失败"), + tr("无法保存预设文件,详细信息:\n%1").arg(err.c_str())); + VCL_destroy_preset(preset); + return; + } + + VCL_destroy_preset(preset); } \ No newline at end of file diff --git a/VisualCraft/VCWind_subwindows.cpp b/VisualCraft/VCWind_subwindows.cpp index fa813561..f8a07e0a 100644 --- a/VisualCraft/VCWind_subwindows.cpp +++ b/VisualCraft/VCWind_subwindows.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -47,6 +47,10 @@ void VCWind::on_ac_browse_block_triggered() noexcept { bb->show(); } +void VCWind::on_ac_tutorial_triggered() noexcept { + QDesktopServices::openUrl(QUrl{"https://slopecraft.readthedocs.io/"}); +} + void VCWind::on_ac_about_VisualCraft_triggered() noexcept { QMessageBox::information( this, VCWind::tr("关于 VisualCraft"), diff --git a/VisualCraft/VC_block_class.cpp b/VisualCraft/VC_block_class.cpp index 01af0b2d..fb429964 100644 --- a/VisualCraft/VC_block_class.cpp +++ b/VisualCraft/VC_block_class.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify diff --git a/VisualCraft/VC_block_class.h b/VisualCraft/VC_block_class.h index 5029340b..3c8a70a9 100644 --- a/VisualCraft/VC_block_class.h +++ b/VisualCraft/VC_block_class.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -36,11 +36,11 @@ class QCheckBox; class VC_block_class : public QGroupBox { Q_OBJECT -private: + private: Ui::VC_block_class *ui; std::vector> blocks; -public: + public: explicit VC_block_class(QWidget *parent); ~VC_block_class(); @@ -54,12 +54,12 @@ class VC_block_class : public QGroupBox { QCheckBox *chbox_enabled() noexcept; -private: + private: void erase_blocks() noexcept; void set_state_for_all(bool checked) noexcept; -private slots: + private slots: }; -#endif // SLOPECRAFT_VISUALCRAFT_VC_BLOCK_CLASS_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_VC_BLOCK_CLASS_H \ No newline at end of file diff --git a/VisualCraft/install.cmake b/VisualCraft/install.cmake index b11eef85..96f5a60f 100644 --- a/VisualCraft/install.cmake +++ b/VisualCraft/install.cmake @@ -1,47 +1,86 @@ set(AppName VisualCraft) -configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") install(TARGETS VisualCraft - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . ) install(FILES vc-config.json - DESTINATION ${CMAKE_INSTALL_PREFIX}) + DESTINATION .) - # Run windeployqt at build time - add_custom_target(Windeployqt-VisualCraft ALL - COMMAND ${SlopeCraft_Qt_windeployqt_executable} VisualCraft.exe - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS VisualCraft) + QD_add_deployqt(VisualCraft + BUILD_MODE + FLAGS ${SlopeCraft_windeployqt_flags_build}) + QD_add_deployqt(VisualCraft + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_windeployqt_flags_install}) + DLLD_add_deploy(VisualCraft + BUILD_MODE + INSTALL_MODE INSTALL_DESTINATION . + IGNORE VisualCraftL.dll libVisualCraftL.dll) - # Run windeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") install(TARGETS VisualCraft + EXPORT SlopeCraftTargets RUNTIME DESTINATION bin ) install(FILES vc-config.json DESTINATION bin) + + install(FILES others/Vc_256.png + DESTINATION share/pixmaps + RENAME com.github.SlopeCraft.VisualCraft.png) + install(FILES others/VisualCraft.desktop + DESTINATION share/applications) + + # Install platforms and imageformats plugins + include(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake) + return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include(${CMAKE_SOURCE_DIR}/VisualCraftL/setup_zip_names.cmake) install(TARGETS VisualCraft - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX}) + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . + BUNDLE DESTINATION .) + + # Install icons + file(GLOB SlopeCraft_Icon + ${CMAKE_SOURCE_DIR}/VisualCraft/others/VisualCraft.icns) + install(FILES ${SlopeCraft_Icon} + DESTINATION VisualCraft.app/Contents/Resources) # Install config json file, VisualCraft will try to find it by ./vc-config.json install(FILES vc-config.json - DESTINATION ${CMAKE_INSTALL_PREFIX}/VisualCraft.app/Contents/MacOS) + DESTINATION VisualCraft.app/Contents/MacOS) + + # Install zips. In vccl-config.json or vc-config.json, they are referred like ./Blocks_VCL/Vanilla_1_19_3.zip + install(FILES ${VCL_app_files} + DESTINATION VisualCraft.app/Contents/MacOS/Blocks_VCL + ) + + install(TARGETS VisualCraftL + # EXPORT SlopeCraftTargets + RUNTIME DESTINATION VisualCraft.app/Contents/Frameworks + LIBRARY DESTINATION VisualCraft.app/Contents/Frameworks) + + QD_add_deployqt(VisualCraft + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_macdeployqt_flags_install}) - # Run macdeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) + DylibD_add_deploy(VisualCraft + INSTALL_DESTINATION . + RPATH_POLICY REPLACE) + RCS_add_codesign(VisualCraft + INSTALL_DESTINATION .) return() -endif() \ No newline at end of file +endif () \ No newline at end of file diff --git a/VisualCraft/main.cpp b/VisualCraft/main.cpp index a0e3c54d..69cafe95 100644 --- a/VisualCraft/main.cpp +++ b/VisualCraft/main.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,12 +20,14 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include +#include +#include #include #include +#include #include #include -#include +#include #include "CallbackFunctions.h" #include "VCWind.h" @@ -39,6 +41,7 @@ bool parse_config_json(QString &err) noexcept; int main(int argc, char **argv) { QApplication qapp(argc, argv); QDir::setCurrent(QCoreApplication::applicationDirPath()); + QImageReader::setAllocationLimit(INT32_MAX); ::is_language_ZH = QLocale::system().uiLanguages().contains("zh"); @@ -109,7 +112,7 @@ int main(int argc, char **argv) { VCL_set_report_callback(VC_callback::callback_receive_report); wind.setWindowTitle( - QStringLiteral("VisualCraft v%1 Copyright © 2021-2023 TokiNoBug") + QStringLiteral("VisualCraft v%1 Copyright © 2021-2026 TokiNoBug") .arg(SC_VERSION_STR)); wind.show(); diff --git a/VisualCraft/others/VisualCraft.desktop b/VisualCraft/others/VisualCraft.desktop new file mode 100644 index 00000000..9abde249 --- /dev/null +++ b/VisualCraft/others/VisualCraft.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=VisualCraft +Comment=Visual Pixel Art Generator for Minecraft +Exec=VisualCraft +Icon=com.github.SlopeCraft.VisualCraft.png +Type=Application +Keywords=SlopeCraft; \ No newline at end of file diff --git a/VisualCraft/others/VisualCraft.icns b/VisualCraft/others/VisualCraft.icns new file mode 100644 index 00000000..f13b8a2f Binary files /dev/null and b/VisualCraft/others/VisualCraft.icns differ diff --git a/VisualCraft/others/VisualCraft.rc.in b/VisualCraft/others/VisualCraft.rc.in index 6bdd831c..87c66194 100644 --- a/VisualCraft/others/VisualCraft.rc.in +++ b/VisualCraft/others/VisualCraft.rc.in @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -44,7 +44,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "CompanyName", "\0" VALUE "FileDescription", "Minecraft Pixel Art Tool\0" VALUE "FileVersion", "@PROJECT_VERSION@.0\0" - VALUE "LegalCopyright", "Copyright TokiNoBug 2021-2023\0" + VALUE "LegalCopyright", "Copyright TokiNoBug 2021-2026\0" VALUE "OriginalFilename", "VisualCraft.exe\0" VALUE "ProductName", "VisualCraft\0" VALUE "ProductVersion", "@PROJECT_VERSION@.0\0" diff --git a/VisualCraft/others/VisualCraft_en_US.ts b/VisualCraft/others/VisualCraft_en_US.ts index 1004a1c6..c3596a71 100644 --- a/VisualCraft/others/VisualCraft_en_US.ts +++ b/VisualCraft/others/VisualCraft_en_US.ts @@ -72,7 +72,7 @@ If you select a block in "Avaliable blocks" page, the other page will
- + 保存当前图片 Save current image @@ -136,7 +136,7 @@ If you select a block in "Avaliable blocks" page, the other page will - + 方块类别: Block class: @@ -146,12 +146,12 @@ If you select a block in "Avaliable blocks" page, the other page will Impossible error - + 保存图片失败 Failed to save image. - + 不知道怎么回事,反正就是没存上。 I don't know why. @@ -212,28 +212,28 @@ If you select a block in "Avaliable blocks" page, the other page will block %1 - + 获取颜色表失败 Failed to retrive colorset - + 在尝试获取第%1个颜色(color_id = %2)时出现错误。函数VCL_get_basic_color_composition返回值为%3,正常情况下应当返回正数。 An error occurred when trying to get color %1 (color_id = %2). Function VCL_get_basic_color_composition returned %3, but a positive number is expected. - - + + 计算投影图像失败 Failed to compute projection image - + 在尝试获取方块 "%1" 的方块模型时出现错误。 An error occurred when trying to get block model for block "%1" (color_id = %2) - + 成功获取到方块 "%1" 的方块模型,但计算投影图像失败。 Managed to get a block model for block '%1", but subsequent computation of projection image failed. @@ -256,533 +256,553 @@ If you select a block in "Avaliable blocks" page, the other page will MC version - + 最大厚度: Max layers: - + 资源包 Resource packs - - + + 添加 Add - - + + 删除 Remove - + 方块id文件(json) Block state list files (json) - + 像素画方向 Pixel art direction - + 侧面(侧视) Side - + 上面(俯视) Up - + 下面(仰视) Down - + 树叶透明 Transparent leaves - + 方块列表 Blocks - - + + 全部方块 All blocks - + 快捷键 Hot keys - + 全部启用 Select all - + 全部禁用 Deselect all - + 高级匹配 Advanced - + 禁用罕见方块 Disable rare blocks - + 禁用不可再生 Disable non-reproducible - + 全部反选 Select reversely blockwise - + 按类反选 Select reversely classwise - - 选择图片 - Select images + + 预设 + Presets + + + + 加载预设 + Load preset - - 显示原始尺寸 - Original size + + 保存当前预设 + Save current preset + + + + 选择图片 + Select images - + 调色算法 Convertion algorithm - + 抖动 Dithering - + 显示原图 Display original - + 显示转化后图像 Display converted - - + + 导出 Export - + 图像 Image - + 大小(行,列) Size (rows, cols) - + 生成litematica Litematica file - + 生成结构方块文件 Structure file - + 生成WE原理图 WE schem file - + 生成转化后图像 Converted image - + 生成平面示意图 Flat diagram - + 进度 Progress - + 导出类型 Export type - + litematic投影 Litematica - + 原版结构方块文件 Vanilla structure - + WorldEdit原理图 WorldEdit schematic - + 转化后图像 Converted image - + 平面示意图 Flat diagram - + 开始执行 Start - + 设置导出位置 Set export directory - + 平面示意图(*.png) Flat diagram(*.png) - + png压缩级别: png compress level: - - + + 方块 blocks - - + + 间距: margin: - + 垂直分隔线 Vertical split line - + 水平分隔线 Horizontal split line - + png压缩内存级别: png compress memory level: - + 联系开发者 Contact developer - + 关于VisualCraft About VisualCraft - + 反馈bug Report bugs - - + + Github仓库 Github repository - + bilibili ? bilibili - + 生成测试投影 Generate testing litematic - + 查看所有颜色 Browse all colors - + 查看可用颜色 Browse avaliable colors - + 检查更新 Check updates - + 刷新警告信息 Flush warnings - + + 使用教程 + Tutorial + + + 原版结构方块文件(*.nbt) Vanilla structure(*.nbt) - + 用结构空位表示空气 Use structure void as air - + Litematica投影(*.litematic) Litematica file (*.litematic) - + 导出选项 Export options - + 区域名称 Region name - + 投影名称(非文件名) Schematic name(not file name) - + WorldEdit原理图(*.schem) (仅1.13+) WorldEdit schematic(*schem) (1.13+ only) - + 名称(非文件名) Name (not file name) - + 需要的mod名(一行一个,默认为空) Required mods (one mod in one line) - + 显卡设置 GPU settings - + 计算设置 Computation settings - + CPU线程数: CPU threads: - + 选择计算设备 Select compute device - + 测试 Test - + 查看 Browse - + 资源 Resources - + 关于 About - + 方块 Blocks - + 加载资源 Load resources - + 设置可用的方块 Set avaliable blocks - + 生物群系 Biomes - + VisualCraftL 动态库版本不匹配 The version of VisualCraftL shared lib mismatch - + 界面程序编译时使用的 VisualCraftL 版本为%1,而 VisualCraftL 动态库的版本为%2。通常这是因为动态库版本过低。 The version of VisualCraftL at compile-time is %1, while that at runtime is %2. Usually it is because the version of VisualCraftL shared lib is too old. - + 加载配置文件失败。 Failed to load configuration file. - + 无法加载配置文件"./vc-config.json"。 %1 Failed to load configuration file "./vc-config.json'. %1 - + 原版资源包 Vanilla - - 原版json - Default - - - + 选择资源包 Select resource pack - - 选择方块id json文件 - Select block state list json file - - - + 资源包解析失败 Failed to parse resource pack - - + + 在此窗口之前弹出的错误信息非常重要,请将它汇报给开发者。 Error messages shown before this window is really important. Report it to the deveploer. - - 方块状态列表json解析失败 - Failed to parse block state list json + + 原版 json + Vanilla - - 资源包/方块状态列表json解析失败 - Failed to parse resource packs or bsl jsons. + + 无法读取文件 %1 + Unable to load file %1 - + + 选择方块 id json 文件 + Select block id json file + + + + 方块状态列表 json 解析失败 + Failed to parse block state json list + + + + 资源包/方块状态列表 json 解析失败 + The resource pack or block list json failed to be parsed + + + 部分方块的投影图像计算失败,或者总颜色数量超过上限(65534)。尝试移除解析失败的资源包/方块列表,或者减小最大层数。 Failed to compute projection image for some blocks, or the total color amout exceeds the limit of 65534. You can try removing resource packs or block state list jsons that failed to be parsed, or reduce the max layers. - + 设置可用方块失败 Failed to set avaliable blocks - + 可能是总颜色数量超过上限(65536),尝试移除解析失败的资源包/方块列表,或者减小最大层数。 Probably the count of colors exceeds the upper bound(65536). Try removing some resource packs or jsons, or reduce the maximum layers. - + 调色算法 (共%1种颜色) Convert algorithms(%1 colors avaliable) - + 选择图片(可多选) Select images - + 读取图片失败 Failed to load image. - - 无法读取图片%1。图片可能是不支持的格式,或者已经损坏。图像过大也可能导致此错误。 - Failed to load image %1 . The image may be of unsupported format, or has been screwed up. A tooooo large image may also cause this error. + + 无法读取图片%1。图片可能是不支持的格式,或者已经损坏。 + Failed to read image %1 . Unsupported format or broken image. - + 设置图片失败 Failed to set image - + 这个错误不应该发生的,可能是你点儿背。 This error is considered to be impossible. - + 关于 VisualCraft About VisualCraft - + VisualCraft 是一款全新的 Minecraft 像素画生成器,由 MC 玩家 TokiNoBug 开发,是 SlopeCraft 的子项目。与其他类似的软件不同,VisualCraft 旨在跟进最新的 MC 版本 (1.12~最新版)、支持最多的 MC 特性,提供最强大的功能。 @@ -791,7 +811,7 @@ If you select a block in "Avaliable blocks" page, the other page will - + 目前 VisualCraft 能够解析许多第三方资源包,也允许自定义增加加新方块。与传统的思路不同,VisualCraft 以方块模型的方式来解析资源包,尽量贴近 Minecraft 的方式,因此支持各种自定义的方块模型。 @@ -800,7 +820,7 @@ If you select a block in "Avaliable blocks" page, the other page will - + 在导出方面,VisualCraft 支持 Litematica mod 的投影 (*.litematic)、WorldEdit 原理图 (*.shem)(仅 1.13+可用)、原版结构方块文件 (*.nbt)、平面示意图 (*.png) 等方式。 @@ -809,7 +829,7 @@ If you select a block in "Avaliable blocks" page, the other page will - + VisualCraft 支持用各种透明方块互相叠加,产生更多的颜色。软件最多支持不超过 65534 种颜色,受此限制,像素画的层数不超过3 层。 @@ -818,44 +838,51 @@ If you select a block in "Avaliable blocks" page, the other page will - + 由于颜色数量很多,VisualCraft 使用了显卡加速。目前支持的 API 有 OpenCL。现在正在使用的 API 是%1 The great amount of colors makes GPU accleration necessary. Now OpenCL is supported, and %1 is what currently being used. - + 可用的计算设备(CPU + %1可用的显卡) Avaliable compute devices (CPU + %1-avaliable-GPUs) - - 无法获取platform信息. 请检查驱动. OpenCL错误码: %1. - Failed to get platform information. Please check GPU driver(s). OpenCL error code : %1. + + 无法获取 platform 信息。请检查驱动。OpenCL 错误码:%1. + Failed to retrieve platform infomation, please check your deiver. OpenCL error code: %1 - - 无法获取device信息. 请检查驱动. OpenCL错误码: %1 - Failed to get device infomation. Please check the driver(s). OpenCL error code : %1 + + 无法获取 device 信息。请检查驱动。OpenCL 错误码:%1 + Failed to retrieve device infomation, please check your deiver. OpenCL error code: %1 - - 设置计算设备失败 - Failed to set compute device + + 这不是一个致命错误,您可以选择其他的显卡,或者只使用 CPU 计算。点击 Ignore 将忽略这个错误,点击 Close 将关闭 VisualCraft + This is not a fatal error, you can use other GPUs or your CPU instead. Click Ignore will ignore this error, or click Close to close VisualCraft - - 创建GPU平台失败,平台序号为%1,设备序号为%2 - Failed to create GPU platform, the index of platform is %1 and index of device is %2 + + 创建 GPU 平台失败,平台序号为%1,设备序号为%2 + Failed to create GPU platform, Platform index = %1, device index = %2 - - 创建GPU设备失败,平台序号为%1,设备序号为%2 - Failed to create GPU device, the index of platform is %1 and index of device is %2 + + 创建 GPU 设备失败,平台序号为%1,设备序号为%2 + Failed to create GPU device, Platform index = %1, device index = %2 + + + + 设置 GPU 设备失败。平台序号为%1,设备序号为%2,详细错误信息: +%3 + Failed to set GPU device. Platform index = %1, device index = %2, detailed information: +%3 - - 设置GPU设备失败。,平台序号为%1,设备序号为%2 - Failed to set GPU device, the index of platform is %1 and index of device is %2 + + 设置计算设备失败 + Failed to set compute device @@ -868,29 +895,84 @@ If you select a block in "Avaliable blocks" page, the other page will %1 rows, %2 cols - + 错误操作 Invalid operation - - 设置任何一种导出类型的输出位置时,都需要在左侧的combo box中选择一个导出类型。请先选择一种导出类型,再设置导出位置。 + + 设置任何一种导出类型的输出位置时,都需要在左侧的 combo box 中选择一个导出类型。请先选择一种导出类型,再设置导出位置。 When you set the output directory for any type of export, you should select the type of export with the combobox in the left. Please select a type first. - - 平面示意图输入错误 - Invalid input for flatdiagram + + 导出 litematica 失败 + Failed to export as litematic + + + + VisualCraftL 不能为图像"%1"生成投影文件"%2"。 + VisualCraft failed to generate litematic file "%2" for image "%1". + + + + VisualCraftL 不能为图像"%1"生成结构方块文件"%2"。 + VisualCraft failed to generate structure file "%2" for image "%1". + + + + 导出 World Edit 原理图失败 + Failed to export as WE schematic + + + + VisualCraftL 不能为图像"%1"生成 World Edit 原理图"%2"。 + VisualCraft failed to generate WE schematic file "%2" for image "%1". + + + + QImage 未能生成"%1"。 + QImage failed to generate "%1". - 应输入%1个以";"分隔的文件名,但实际上输入了%2个。 + 应输入%1个以";"分隔的文件名,但实际上输入了%2 个。 您输入的%2个文件名是: %3 You should input %1 filenames separeted by ";", but %2 is(are) given infact. The %2 filename(s) you give is: %3 + + + 导出页码表格中的图片"%1"不能在 this->image_cache 中找到对应的缓存。请将这个错误反馈给软件开发者。 + Cache of image named "%1" in the export table cannot be found in this->image_cache. Please send this as a feedback to the developer. + + + + VisualCraftL 不能为图像"%1"构建三维结构。 + VisualCraft failed to build 3D structure for image "%1". + + + + 保存测试投影 + Save test schematic + + + + 无法输出测试投影 + Failed to export test schematic + + + + 详细错误信息在之前的窗口中 + The detail information is displayed in previous windows + + + + 平面示意图输入错误 + Invalid input for flatdiagram + 导出平面示意图失败 @@ -906,60 +988,60 @@ The %2 filename(s) you give is: 致命逻辑错误 Impossible error - - - 导出页码表格中的图片"%1"不能在this->image_cache中找到对应的缓存。请将这个错误反馈给软件开发者。 - Cache of image named "%1" in the export table cannot be found in this->image_cache. Please send this as a feedback to the developer. - 保存转化后图像失败 Failed to save the converted image - - - QImage未能生成"%1"。 - QImage failed to generate "%1". - 构建三维结构失败 Failed to build 3D structure - - VisualCraftL不能为图像"%1"构建三维结构。 - VisualCraft failed to build 3D structure for image "%1". + + 导出原版结构方块文件失败 + Failed to export as vanilla structure - - 导出litematica失败 - Failed to export as litematic + + + 选择预设文件 + Select preset file - - VisualCraftL不能为图像"%1"生成投影文件"%2"。 - VisualCraft failed to generate litematic file "%2" for image "%1". + + 加载预设失败 + Failed to load preset - - 导出原版结构方块文件失败 - Failed to export as vanilla structure + + 无法解析预设文件,详细信息: +%1 + Failed to parse preset file, detail: +%1 - - VisualCraftL不能为图像"%1"生成结构方块文件"%2"。 - VisualCraft failed to generate structure file "%2" for image "%1". + + 无法加载预设 + Failed to load preset - - 导出World Edit原理图失败 - Failed to export as WE schematic + + 预设中包含%1,但可用方块中没有 id 相同的方块。点击 Ignore 以跳过这个方块,点击 Close 终止此次操作。 + Preset contains %1, but it is not in available blocks. Click Ignore to skip this block, or clock Close to stop this operation. - - VisualCraftL不能为图像"%1"生成World Edit原理图"%2"。 - VisualCraft failed to generate WE schematic file "%2" for image "%1". + + 保存预设文件失败 + Failed to save preset file + + + + 无法保存预设文件,详细信息: +%1 + Failed to save preset file, detail: +%1 diff --git a/VisualCraft/vc-config-to-bin-dir.json.in b/VisualCraft/vc-config-to-bin-dir.json.in index 90b7ed3c..0221ff4a 100644 --- a/VisualCraft/vc-config-to-bin-dir.json.in +++ b/VisualCraft/vc-config-to-bin-dir.json.in @@ -31,6 +31,14 @@ [ 19, "@VCL_resource_19@" + ], + [ + 20, + "@VCL_resource_20@" + ], + [ + 21, + "@VCL_resource_21@" ] ], "default_block_state_list": [ diff --git a/VisualCraft/vc-config.json b/VisualCraft/vc-config.json index 585685b0..1317190c 100644 --- a/VisualCraft/vc-config.json +++ b/VisualCraft/vc-config.json @@ -1,39 +1,47 @@ { - "default_resource_pack_zip": [ - [ - 12, - "./Blocks_VCL/Vanilla_1_12_2.zip" - ], - [ - 13, - "./Blocks_VCL/Vanilla_1_13_2.zip" - ], - [ - 14, - "./Blocks_VCL/Vanilla_1_14_4.zip" - ], - [ - 15, - "./Blocks_VCL/Vanilla_1_15_2.zip" - ], - [ - 16, - "./Blocks_VCL/Vanilla_1_16_5.zip" - ], - [ - 17, - "./Blocks_VCL/Vanilla_1_17_1.zip" - ], - [ - 18, - "./Blocks_VCL/Vanilla_1_18_2.zip" - ], - [ - 19, - "./Blocks_VCL/Vanilla_1_19_3.zip" - ] - ], - "default_block_state_list": [ - "./Blocks_VCL/VCL_blocks_fixed.json" + "default_resource_pack_zip": [ + [ + 12, + "./Blocks_VCL/Vanilla_1_12_2.zip" + ], + [ + 13, + "./Blocks_VCL/Vanilla_1_13_2.zip" + ], + [ + 14, + "./Blocks_VCL/Vanilla_1_14_4.zip" + ], + [ + 15, + "./Blocks_VCL/Vanilla_1_15_2.zip" + ], + [ + 16, + "./Blocks_VCL/Vanilla_1_16_5.zip" + ], + [ + 17, + "./Blocks_VCL/Vanilla_1_17_1.zip" + ], + [ + 18, + "./Blocks_VCL/Vanilla_1_18_2.zip" + ], + [ + 19, + "./Blocks_VCL/Vanilla_1_19_3.zip" + ], + [ + 20, + "./Blocks_VCL/Vanilla_1_20_6.zip" + ], + [ + 21, + "./Blocks_VCL/Vanilla_1_21_11.zip" ] + ], + "default_block_state_list": [ + "./Blocks_VCL/VCL_blocks_fixed.json" + ] } \ No newline at end of file diff --git a/VisualCraftL/BlockStateList.cpp b/VisualCraftL/BlockStateList.cpp index 404f14a1..43f2a696 100644 --- a/VisualCraftL/BlockStateList.cpp +++ b/VisualCraftL/BlockStateList.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -27,7 +27,7 @@ This file is part of SlopeCraft. #include "ParseResourcePack.h" #include "VCL_internal.h" -#include +#include VCL_block::VCL_block() { this->initialize_attributes(); } @@ -97,48 +97,44 @@ version_set parse_version_set(const nlohmann::json &jo, return {}; } -#define VCL_PRIVATE_MACRO_PARSE_ATTRIBUTE(key_str, key_enum) \ - if (jo.contains(#key_str)) { \ - if (!jo.at(#key_str).is_boolean()) { \ - *ok = false; \ - return {}; \ - } \ - \ - ret.set_attribute(VCL_block::attribute::key_enum, jo.at(#key_str)); \ +#define VCL_PRIVATE_MACRO_PARSE_ATTRIBUTE(key_str, key_enum) \ + if (jo.contains(#key_str)) { \ + if (!jo.at(#key_str).is_boolean()) { \ + return std::nullopt; \ + } \ + \ + ret.set_attribute(VCL_block::attribute::key_enum, jo.at(#key_str)); \ } -VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { +std::optional parse_block(const nlohmann::json &jo) { if (!jo.contains("version")) { - *ok = false; - return {}; + return std::nullopt; } VCL_block ret; - ret.version_info = parse_version_set(jo.at("version"), ok); + bool ok = false; + ret.version_info = parse_version_set(jo.at("version"), &ok); - if (!ok) { - *ok = false; - return {}; + if (not ok) { + return std::nullopt; } ret.set_transparency(false); if (!jo.contains("class") || !jo.at("class").is_string()) { - *ok = false; - return {}; + return std::nullopt; } else { const std::string &str = jo.at("class"); - ret.block_class = string_to_block_class(str, ok); + ret.block_class = string_to_block_class(str, &ok); - if (!*ok) { - return {}; + if (not ok) { + return std::nullopt; } } if (jo.contains("id_replace_list")) { if (!jo.at("id_replace_list").is_array()) { - *ok = false; - return {}; + return std::nullopt; } const nlohmann::json &ja = jo.at("id_replace_list"); @@ -146,13 +142,11 @@ VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { for (size_t i = 0; i < ja.size(); i++) { const nlohmann::json &jaa = ja.at(i); if (!jaa.is_array() || jaa.size() != 2) { - *ok = false; - return {}; + return std::nullopt; } if (!jaa[0].is_number_integer() || !jaa[1].is_string()) { - *ok = false; - return {}; + return std::nullopt; } int val = jaa[0]; @@ -167,8 +161,7 @@ VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { if (jo.at("nameZH").is_string()) { ret.name_ZH = jo.at("nameZH"); } else { - *ok = false; - return {}; + return std::nullopt; } } @@ -176,8 +169,7 @@ VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { if (jo.at("nameEN").is_string()) { ret.name_EN = jo.at("nameEN"); } else { - *ok = false; - return {}; + return std::nullopt; } } @@ -185,8 +177,7 @@ VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { if (jo.at("transparent").is_boolean()) { ret.set_transparency(jo.at("transparent")); } else { - *ok = false; - return {}; + return std::nullopt; } } @@ -197,23 +188,20 @@ VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { for (size_t i = 0; i < ja.size(); i++) { if (!ja.at(i).is_string()) { - *ok = false; - return {}; + return std::nullopt; } bool _ok = true; - const VCL_face_t f = - string_to_face_idx(ja.at(i).get(), &_ok); - if (!_ok) { - *ok = false; - return {}; + const std::optional f = + string_to_face_idx(ja.at(i).get()); + if (not f) { + return std::nullopt; } - ret.set_face_avaliablity(f, true); + ret.set_face_avaliablity(f.value(), true); } } else { - *ok = false; - return {}; + return std::nullopt; } } @@ -227,8 +215,6 @@ VCL_block parse_block(const nlohmann::json &jo, bool *const ok) { VCL_PRIVATE_MACRO_PARSE_ATTRIBUTE(reproducible, reproducible); VCL_PRIVATE_MACRO_PARSE_ATTRIBUTE(rare, rare); - *ok = true; - return ret; } @@ -245,25 +231,23 @@ bool VCL_block_state_list::add(std::string_view filename) noexcept { ifs.close(); } catch (std::exception &e) { std::string msg = - fmt::format("Failed to parse {}, detail : {}", filename, e.what()); + std::format("Failed to parse {}, detail : {}", filename, e.what()); ::VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } - bool ok = true; - for (const auto &pair : jo.items()) { - VCL_block vb = parse_block(pair.value(), &ok); + auto vb = parse_block(pair.value()); - if (!ok) { - std::string msg = fmt::format( + if (not vb) { + std::string msg = std::format( "Failed to parse {}, : invalid value for block state {} : {}", - filename, pair.key(), pair.value()); + filename, pair.key().c_str(), to_string(pair.value())); ::VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } - auto it = this->states.emplace(pair.key(), std::move(vb)); + auto it = this->states.emplace(pair.key(), std::move(vb.value())); // This statement requires that VCL_block_state_list is a friend class of // VCL_block @@ -319,7 +303,6 @@ void VCL_block_state_list::update_foliages( VCL_block_class_t string_to_block_class(std::string_view str, bool *ok) noexcept { - auto ret = magic_enum::enum_cast(str); if (ok != nullptr) { *ok = ret.has_value(); diff --git a/VisualCraftL/BlockStateList.h b/VisualCraftL/BlockStateList.h index f01b050f..bb59f6fa 100644 --- a/VisualCraftL/BlockStateList.h +++ b/VisualCraftL/BlockStateList.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -28,70 +28,18 @@ This file is part of SlopeCraft. #include #include #include - +#include #include +#include "version_set.hpp" #include "VisualCraftL.h" -constexpr inline size_t major_version_to_idx(SCL_gameVersion v) noexcept { - switch (v) { - case SCL_gameVersion::FUTURE: - return 31; - - default: - return size_t(v); - } -} - -class version_set { -private: - std::bitset<32> set{0}; - -public: - version_set() = default; - - version_set(uint32_t val) : set(val) {} - - static version_set all() noexcept { - version_set ret(~uint32_t(0)); - return ret; - } - - inline bool match(SCL_gameVersion v) const noexcept { - return set[major_version_to_idx(v)]; - } - - inline auto operator[](SCL_gameVersion v) noexcept { - return set[major_version_to_idx(v)]; - } - - inline auto operator[](SCL_gameVersion v) const noexcept { - return set[major_version_to_idx(v)]; - } - - inline uint64_t to_u32() const noexcept { return set.to_ulong(); } - - inline bool operator==(const version_set &vs) const noexcept { - return this->to_u32() == vs.to_u32(); - } - - SCL_gameVersion introduced_version() const noexcept { - for (size_t i = 0; i < 32; i++) { - if (this->set[i]) { - return SCL_gameVersion(i); - } - } - return SCL_gameVersion::FUTURE; - } -}; - class VCL_block_state_list; class VCL_block { - friend class VCL_block_state_list; -public: + public: VCL_block(); VCL_block(const std::string *full_id_ptr); @@ -181,7 +129,6 @@ class VCL_block { } inline const std::string &id_for_schem(SCL_gameVersion v) const noexcept { - for (const auto &pair : this->id_replace_list) { if (pair.first == v) { return pair.second; @@ -190,22 +137,22 @@ class VCL_block { return *this->full_id_p; } -private: + private: void initialize_attributes() noexcept; // members -public: + public: version_set version_info; -private: + private: std::bitset<32> attributes; -public: + public: VCL_block_class_t block_class{VCL_block_class_t::others}; -private: + private: const std::string *full_id_p{nullptr}; -public: + public: Eigen::Array project_image_on_exposed_face{0, 0}; std::string name_ZH{""}; @@ -214,15 +161,16 @@ class VCL_block { }; class VCL_block_state_list { -private: + private: std::unordered_map states; -public: + public: + using is_allowed_callback_t = std::function; bool add(std::string_view filename) noexcept; - void - available_block_states(SCL_gameVersion v, VCL_face_t f, - std::vector *const str_list) noexcept; + void available_block_states( + SCL_gameVersion v, VCL_face_t f, + std::vector *const str_list) noexcept; void avaliable_block_states_by_transparency( SCL_gameVersion v, VCL_face_t f, @@ -248,4 +196,4 @@ class VCL_block_state_list { VCL_block_class_t string_to_block_class(std::string_view str, bool *ok = nullptr) noexcept; -#endif // SLOPECRAFT_VISUALCRAFT_BLOCKSTATELIST_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_BLOCKSTATELIST_H \ No newline at end of file diff --git a/VisualCraftL/CMakeLists.txt b/VisualCraftL/CMakeLists.txt index 58426f5e..5a81a831 100644 --- a/VisualCraftL/CMakeLists.txt +++ b/VisualCraftL/CMakeLists.txt @@ -1,29 +1,27 @@ cmake_minimum_required(VERSION 3.20) project(VisualCraftL VERSION ${SlopeCraft_version} LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 20) - set(VCL_enable_internal_test OFF) include(config_versions.cmake) -if(CMAKE_SYSTEM_NAME STREQUAL "Windows") +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") file(STRINGS others/VisualCraftL.def.in VCL_def_funs) set(VCL_counter 0) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraftL.def "") - foreach(VCL_fun_name ${VCL_def_funs}) + foreach (VCL_fun_name ${VCL_def_funs}) file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraftL.def ${VCL_fun_name}) - if(VCL_counter GREATER 0) + if (VCL_counter GREATER 0) file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraftL.def " @" ${VCL_counter} "\n") - else() + else () file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraftL.def "\n") - endif() + endif () math(EXPR VCL_counter "${VCL_counter} +1") - endforeach(VCL_fun_name ${VCL_def_funs}) + endforeach (VCL_fun_name ${VCL_def_funs}) unset(VCL_def_funs) unset(VCL_counter) @@ -32,9 +30,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraftL.def ${CMAKE_CURRENT_BINARY_DIR}/others/VisualCraftL.rc) -else() +else () set(VCL_win_sources) -endif() +endif () # find_package(OpenCL) set(VCL_source_files @@ -42,9 +40,8 @@ set(VCL_source_files VisualCraftL.h VisualCraftL.cpp - textures_need_to_override.h - textures_need_to_override.cpp - + # textures_need_to_override.h + # textures_need_to_override.cpp TokiVC.h TokiVC.cpp TokiVC_flagdiagram.cpp @@ -77,85 +74,79 @@ add_library(VisualCraftL_static STATIC ${VCL_source_files}) find_package(ZLIB 1.2.11 REQUIRED) -find_package(PNG 1.6.37 REQUIRED) -find_package(libzip 1.9.2 REQUIRED) - -include(${CMAKE_SOURCE_DIR}/cmake/configure_fmtlib.cmake) -find_package(fmt 9.1.0 REQUIRED) +find_package(libzip 1.7.0 REQUIRED) set(VCL_include_dirs ${SlopeCraft_Nlohmann_json_include_dir} - ${SlopeCraft_Eigen3_include_dir} ${SlopeCraft_HeuristicFlow_include_dir} ${CMAKE_SOURCE_DIR}) target_include_directories(VisualCraftL PRIVATE ${VCL_include_dirs}) target_include_directories(VisualCraftL_static PUBLIC ${VCL_include_dirs}) target_include_directories(VisualCraftL PUBLIC - ${CMAKE_SOURCE_DIR}/utilities) -target_include_directories(VisualCraftL_static PUBLIC - ${CMAKE_SOURCE_DIR}/utilities) - -target_include_directories(VisualCraftL PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_BINARY_DIR}/utilities) + $ + $ + $) target_include_directories(VisualCraftL_static PUBLIC + ${CMAKE_SOURCE_DIR}/utilities ${CMAKE_CURRENT_SOURCE_DIR}) set(VCL_link_libs ColorManip + version_set libzip::zip ZLIB::ZLIB PNG::PNG ${VCL_libzip_additions} Schem - fmt::fmt - ProcessBlockId) + ProcessBlockId + FlatDiagram +) -target_link_libraries(VisualCraftL PRIVATE ${VCL_link_libs}) +target_link_libraries(VisualCraftL PRIVATE $) target_link_libraries(VisualCraftL_static PUBLIC ${VCL_link_libs}) # message(STATUS "ret = " ${ret}) -target_compile_definitions(VisualCraftL PRIVATE "-DVISUALCRAFTL_BUILD") -target_compile_definitions(VisualCraftL_static PRIVATE "-DVISUALCRAFTL_BUILD") +target_compile_definitions(VisualCraftL PRIVATE VISUALCRAFTL_BUILD) +target_compile_definitions(VisualCraftL_static PRIVATE VISUALCRAFTL_BUILD) -target_compile_features(VisualCraftL PRIVATE cxx_std_20) -target_compile_features(VisualCraftL_static PUBLIC cxx_std_20) +target_compile_features(VisualCraftL PRIVATE cxx_std_23) +target_compile_features(VisualCraftL_static PUBLIC cxx_std_23) target_compile_options(VisualCraftL PRIVATE ${SlopeCraft_vectorize_flags}) target_compile_options(VisualCraftL_static PRIVATE ${SlopeCraft_vectorize_flags}) # target_compile_options(VisualCraftL PRIVATE "-std=c++20") -if(NOT ${MSVC}) +if (NOT ${MSVC}) target_compile_options(VisualCraftL PUBLIC "-flto") # target_compile_options(VisualCraftL PUBLIC "-Wall") -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") # if the system is windows, remove the "lib" prefix. set_target_properties(VisualCraftL PROPERTIES PREFIX "") -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") target_compile_options(VisualCraftL PUBLIC "-fvisibility=hidden" ) get_target_property(VCL_libzip_type libzip::zip TYPE) get_target_property(VCL_libzip_location libzip::zip LOCATION) - if(VCL_libzip_type STREQUAL "SHARED_LIBRARY") + if (VCL_libzip_type STREQUAL "SHARED_LIBRARY") get_filename_component(VCL_libzip_dir ${VCL_libzip_location} DIRECTORY) message(STATUS "VCL_libzip_dir = " ${VCL_libzip_dir}) # target_link_directories(VisualCraftL PUBLIC ${VCL_libzip_dir}) - target_link_libraries(VisualCraftL PUBLIC libzip::zip) + target_link_libraries(VisualCraftL PUBLIC $) target_link_libraries(VisualCraftL_static PUBLIC libzip::zip) - endif() + endif () # target_link_options(VisualCraftL PRIVATE -rpath=${VCL_libzip_dir}) -endif() +endif () set_target_properties(VisualCraftL PROPERTIES VERSION ${PROJECT_VERSION} diff --git a/VisualCraftL/DirectionHandler.hpp b/VisualCraftL/DirectionHandler.hpp index 2e5c97c0..06478ede 100644 --- a/VisualCraftL/DirectionHandler.hpp +++ b/VisualCraftL/DirectionHandler.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -26,28 +26,29 @@ This file is part of SlopeCraft. #include "VisualCraftL.h" #include -template class dir_handler { -private: +template +class dir_handler { + private: std::array size_xyz{0, 0, 0}; VCL_face_t map_face; -public: + public: dir_handler() = delete; dir_handler(VCL_face_t __face, int_t rows, int_t cols, int_t depth) : map_face(__face) { switch (this->map_face) { - case VCL_face_t::face_north: - case VCL_face_t::face_south: - this->size_xyz = {cols, rows, depth}; - return; - case VCL_face_t::face_east: - case VCL_face_t::face_west: - this->size_xyz = {depth, rows, cols}; - return; - case VCL_face_t::face_up: - case VCL_face_t::face_down: - this->size_xyz = {cols, depth, rows}; - return; + case VCL_face_t::face_north: + case VCL_face_t::face_south: + this->size_xyz = {cols, rows, depth}; + return; + case VCL_face_t::face_east: + case VCL_face_t::face_west: + this->size_xyz = {depth, rows, cols}; + return; + case VCL_face_t::face_up: + case VCL_face_t::face_down: + this->size_xyz = {cols, depth, rows}; + return; } abort(); } @@ -76,34 +77,34 @@ return this->coord_when_vertical(r, c, depth); return this->coord_when_no_rot(r, c, depth); } -private: + private: std::array coord_when_no_rot(int_t r, int_t c, int_t depth) const noexcept { switch (this->map_face) { - case VCL_face_t::face_east: - return {this->size_xyz[idx_x] - depth - 1, this->size_xyz[idx_y] - r - 1, - this->size_xyz[idx_z] - c - 1}; + case VCL_face_t::face_east: + return {this->size_xyz[idx_x] - depth - 1, + this->size_xyz[idx_y] - r - 1, this->size_xyz[idx_z] - c - 1}; - case VCL_face_t::face_west: - return {depth, this->size_xyz[idx_y] - r - 1, c}; + case VCL_face_t::face_west: + return {depth, this->size_xyz[idx_y] - r - 1, c}; - case VCL_face_t::face_north: - return {this->size_xyz[idx_x] - c - 1, this->size_xyz[idx_y] - r - 1, - depth}; + case VCL_face_t::face_north: + return {this->size_xyz[idx_x] - c - 1, this->size_xyz[idx_y] - r - 1, + depth}; - case VCL_face_t::face_south: - return {c, this->size_xyz[idx_y] - r - 1, - this->size_xyz[idx_z] - depth - 1}; + case VCL_face_t::face_south: + return {c, this->size_xyz[idx_y] - r - 1, + this->size_xyz[idx_z] - depth - 1}; - case VCL_face_t::face_up: - return {c, this->size_xyz[idx_y] - depth - 1, r}; + case VCL_face_t::face_up: + return {c, this->size_xyz[idx_y] - depth - 1, r}; - case VCL_face_t::face_down: - return {c, depth, this->size_xyz[idx_z] - r - 1}; + case VCL_face_t::face_down: + return {c, depth, this->size_xyz[idx_z] - r - 1}; } abort(); return {}; } }; -#endif // SLOPECRAFT_VISUALCRAFTL_DIRECTION_HANDLER_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFTL_DIRECTION_HANDLER_H \ No newline at end of file diff --git a/VisualCraftL/ParseResourcePack.h b/VisualCraftL/ParseResourcePack.h index 218c4192..ee65c2d9 100644 --- a/VisualCraftL/ParseResourcePack.h +++ b/VisualCraftL/ParseResourcePack.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,13 +23,14 @@ This file is part of SlopeCraft. #ifndef SLOPECRAFT_VISUALCRAFTL_PARSERESOURCEPACK_H #define SLOPECRAFT_VISUALCRAFTL_PARSERESOURCEPACK_H -#include - -#include #include #include #include #include +#include + +#include +#include #include "Resource_tree.h" #include "VisualCraftL.h" @@ -427,8 +428,8 @@ class model { } // namespace block_model -std::array compute_mean_color( - const block_model::EImgRowMajor_t &img, bool *const ok = nullptr) noexcept; +std::optional> compute_mean_color( + const block_model::EImgRowMajor_t &img) noexcept; bool compose_image_background_half_transparent( block_model::EImgRowMajor_t &frontend_and_dest, @@ -558,8 +559,8 @@ bool parse_block_state( } // namespace resource_json -block_model::face_idx string_to_face_idx(std::string_view str, - bool *const _ok) noexcept; +std::optional string_to_face_idx( + std::string_view str) noexcept; const char *face_idx_to_string(block_model::face_idx) noexcept; struct model_with_rotation { @@ -614,17 +615,14 @@ class VCL_resource_pack { bool add_textures(const zipped_folder &resourece_pack_root, const bool on_conflict_replace_old = true) noexcept; - bool add_block_models(const zipped_folder &resourece_pack_root, + bool add_block_models(const zipped_folder &resource_pack_root, const bool on_conflict_replace_old = true) noexcept; bool add_block_states(const zipped_folder &resourece_pack_root, const bool on_conflict_replace_old = true) noexcept; - const Eigen::Array - *find_texture(std::string_view path, bool override_only) const noexcept; - - [[deprecated]] bool override_textures( - VCL_biome_t biome, bool replace_transparent_with_black) noexcept; + const Eigen::Array * + find_texture(std::string_view path, bool override_only) const noexcept; bool override_required_textures(VCL_biome_t biome, bool replace_transparent_with_black, diff --git a/VisualCraftL/ParseResourcePack_blocks.cpp b/VisualCraftL/ParseResourcePack_blocks.cpp index 479b27a5..ef6f97a3 100644 --- a/VisualCraftL/ParseResourcePack_blocks.cpp +++ b/VisualCraftL/ParseResourcePack_blocks.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -28,20 +28,20 @@ using Array3f = ::Eigen::Array3f; ray_t::ray_t(const face_idx f) { switch (f) { - case face_idx::face_down: - case face_idx::face_up: - abc = {0, 1, 0}; - break; - - case face_idx::face_north: - case face_idx::face_south: - abc = {0, 0, 1}; - break; - - case face_idx::face_east: - case face_idx::face_west: - abc = {1, 0, 0}; - break; + case face_idx::face_down: + case face_idx::face_up: + abc = {0, 1, 0}; + break; + + case face_idx::face_north: + case face_idx::face_south: + abc = {0, 0, 1}; + break; + + case face_idx::face_east: + case face_idx::face_west: + abc = {1, 0, 0}; + break; } } @@ -53,43 +53,43 @@ plane_t element::plane(face_idx fi) const noexcept { Array3f pos_max = this->_to.max(this->_from); switch (fi) { - case face_y_pos: - point = {0.5, pos_max[1], 0.5}; - break; - case face_y_neg: - point = {0.5, pos_min[1], 0.5}; - break; - - case face_x_pos: - point = {pos_max[0], 0.5, 0.5}; - break; - case face_x_neg: - point = {pos_min[0], 0.5, 0.5}; - break; - - case face_z_pos: - point = {0.5, 0.5, pos_max[2]}; - break; - case face_z_neg: - point = {0.5, 0.5, pos_min[2]}; - break; + case face_y_pos: + point = {0.5, pos_max[1], 0.5}; + break; + case face_y_neg: + point = {0.5, pos_min[1], 0.5}; + break; + + case face_x_pos: + point = {pos_max[0], 0.5, 0.5}; + break; + case face_x_neg: + point = {pos_min[0], 0.5, 0.5}; + break; + + case face_z_pos: + point = {0.5, 0.5, pos_max[2]}; + break; + case face_z_neg: + point = {0.5, 0.5, pos_min[2]}; + break; } switch (fi) { - case face_y_pos: - case face_y_neg: - norm = {0, 1, 0}; - break; - - case face_x_pos: - case face_x_neg: - norm = {1, 0, 0}; - break; - - case face_z_pos: - case face_z_neg: - norm = {0, 0, 1}; - break; + case face_y_pos: + case face_y_neg: + norm = {0, 1, 0}; + break; + + case face_x_pos: + case face_x_neg: + norm = {1, 0, 0}; + break; + + case face_z_pos: + case face_z_neg: + norm = {0, 0, 1}; + break; } return plane_t(norm, point); @@ -126,8 +126,7 @@ Array3f crossover_point(const plane_t &plane, const ray_t &ray) noexcept { void element::intersect_points( const face_idx f, const ray_t &ray, std::vector *const dest) const noexcept { - if (dest == nullptr) - return; + if (dest == nullptr) return; // const Array3f &start_point = ray.x0y0z0; /* switch (f) { @@ -154,31 +153,30 @@ void element::intersect_points( } */ - if (this->face(f).is_hidden) - return; + if (this->face(f).is_hidden) return; switch (f) { - case face_x_neg: - case face_x_pos: - if (this->y_range_abs() * this->z_range_abs() < 1e-4f) { - // VCL_report(VCL_report_type_t::information, "yz range skip"); - return; - } - break; - case face_y_neg: - case face_y_pos: - if (this->x_range_abs() * this->z_range_abs() < 1e-4f) { - // VCL_report(VCL_report_type_t::information, "xz range skip"); - return; - } - break; - case face_z_neg: - case face_z_pos: - if (this->x_range_abs() * this->y_range_abs() < 1e-4f) { - // VCL_report(VCL_report_type_t::information, "xy range skip"); - return; - } - break; + case face_x_neg: + case face_x_pos: + if (this->y_range_abs() * this->z_range_abs() < 1e-4f) { + // VCL_report(VCL_report_type_t::information, "yz range skip"); + return; + } + break; + case face_y_neg: + case face_y_pos: + if (this->x_range_abs() * this->z_range_abs() < 1e-4f) { + // VCL_report(VCL_report_type_t::information, "xz range skip"); + return; + } + break; + case face_z_neg: + case face_z_pos: + if (this->x_range_abs() * this->y_range_abs() < 1e-4f) { + // VCL_report(VCL_report_type_t::information, "xy range skip"); + return; + } + break; } intersect_point intersect{1e9f, {0, 0}, nullptr}; @@ -186,8 +184,7 @@ void element::intersect_points( coordinate = crossover_point(this->plane(f), ray); // printf("\nelement::intersect_points : coordinate = [%f, %f, // %f]",coordinate[0], coordinate[1], coordinate[2]); - if (!this->is_not_outside(coordinate)) - return; + if (!this->is_not_outside(coordinate)) return; intersect.face_ptr = &this->face(f); @@ -205,73 +202,73 @@ void element::intersect_points( // #warning compute uv here switch (f) { - case face_idx::face_up: - uv_start = {min_pos[0], max_pos[1], min_pos[2]}; - // uv_end=max_pos; - - // here u <-> x+ - intersect.uv[0] = (coordinate[0] - uv_start[0]) / this->x_range_abs(); - // here v<-> z+ - intersect.uv[1] = (coordinate[2] - uv_start[2]) / this->z_range_abs(); - /* - printf("\nelement::intersect_points : face = up, uv_start = [%f, %f, %f], - " "uv = [%f, %f] ", uv_start[0], uv_start[1], uv_start[2], - intersect.uv[0], intersect.uv[1]); - - */ - - break; - - case face_idx::face_down: - uv_start = {max_pos[0], min_pos[1], max_pos[2]}; - { - Array3f uv_end = {min_pos[0], min_pos[1], min_pos[2]}; - // here u <-> x+ - intersect.uv[0] = (coordinate[0] - uv_end[0]) / this->x_range_abs(); - // here v <-> z- - intersect.uv[1] = (uv_start[2] - coordinate[2]) / this->z_range_abs(); - } - break; - - case face_idx::face_east: - // uv_start=max_pos; - uv_start = {max_pos[0], max_pos[1], max_pos[2]}; - // uv_end = {max_pos[0], min_pos[1], min_pos[2]}; - // here u <-> z- - intersect.uv[0] = (uv_start[2] - coordinate[2]) / this->z_range_abs(); - // here v <-> y- - intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); - break; - - case face_idx::face_west: - uv_start = {min_pos[0], max_pos[1], min_pos[2]}; - // uv_end = {min_pos[0], min_pos[1], max_pos[2]}; - - // here u <-> z+ - intersect.uv[0] = (coordinate[2] - uv_start[2]) / this->z_range_abs(); - // here v <-> y- - intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); - break; - - case face_idx::face_south: - uv_start = {min_pos[0], max_pos[1], max_pos[2]}; - // uv_end = {max_pos[0], min_pos[1], max_pos[2]}; - - // here u <-> x+ - intersect.uv[0] = (coordinate[0] - uv_start[0]) / this->x_range_abs(); - // here v <-> y- - intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); - break; - - case face_idx::face_north: - uv_start = {max_pos[0], max_pos[0], min_pos[0]}; - // uv_end = min_pos; - - // here u <-> x- - intersect.uv[0] = (uv_start[0] - coordinate[0]) / this->x_range_abs(); - // here v <-> y- - intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); - break; + case face_idx::face_up: + uv_start = {min_pos[0], max_pos[1], min_pos[2]}; + // uv_end=max_pos; + + // here u <-> x+ + intersect.uv[0] = (coordinate[0] - uv_start[0]) / this->x_range_abs(); + // here v<-> z+ + intersect.uv[1] = (coordinate[2] - uv_start[2]) / this->z_range_abs(); + /* + printf("\nelement::intersect_points : face = up, uv_start = [%f, %f, %f], + " "uv = [%f, %f] ", uv_start[0], uv_start[1], uv_start[2], + intersect.uv[0], intersect.uv[1]); + + */ + + break; + + case face_idx::face_down: + uv_start = {max_pos[0], min_pos[1], max_pos[2]}; + { + Array3f uv_end = {min_pos[0], min_pos[1], min_pos[2]}; + // here u <-> x+ + intersect.uv[0] = (coordinate[0] - uv_end[0]) / this->x_range_abs(); + // here v <-> z- + intersect.uv[1] = (uv_start[2] - coordinate[2]) / this->z_range_abs(); + } + break; + + case face_idx::face_east: + // uv_start=max_pos; + uv_start = {max_pos[0], max_pos[1], max_pos[2]}; + // uv_end = {max_pos[0], min_pos[1], min_pos[2]}; + // here u <-> z- + intersect.uv[0] = (uv_start[2] - coordinate[2]) / this->z_range_abs(); + // here v <-> y- + intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); + break; + + case face_idx::face_west: + uv_start = {min_pos[0], max_pos[1], min_pos[2]}; + // uv_end = {min_pos[0], min_pos[1], max_pos[2]}; + + // here u <-> z+ + intersect.uv[0] = (coordinate[2] - uv_start[2]) / this->z_range_abs(); + // here v <-> y- + intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); + break; + + case face_idx::face_south: + uv_start = {min_pos[0], max_pos[1], max_pos[2]}; + // uv_end = {max_pos[0], min_pos[1], max_pos[2]}; + + // here u <-> x+ + intersect.uv[0] = (coordinate[0] - uv_start[0]) / this->x_range_abs(); + // here v <-> y- + intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); + break; + + case face_idx::face_north: + uv_start = {max_pos[0], max_pos[0], min_pos[0]}; + // uv_end = min_pos; + + // here u <-> x- + intersect.uv[0] = (uv_start[0] - coordinate[0]) / this->x_range_abs(); + // here v <-> y- + intersect.uv[1] = (uv_start[1] - coordinate[1]) / this->y_range_abs(); + break; } for (auto &uv : intersect.uv) { @@ -279,25 +276,25 @@ void element::intersect_points( } switch (intersect.face_ptr->rot) { - case face_rot::face_rot_0: - break; - case face_rot::face_rot_90: { - float temp_u = intersect.uv[0]; - intersect.uv[0] = intersect.uv[1]; - intersect.uv[1] = 1 - temp_u; - } break; - - case face_rot::face_rot_180: - intersect.uv[0] = 1 - intersect.uv[0]; - intersect.uv[1] = 1 - intersect.uv[1]; - break; - - case face_rot::face_rot_270: { - float temp_u = intersect.uv[0]; - intersect.uv[0] = 1 - intersect.uv[1]; - intersect.uv[1] = temp_u; - break; - } + case face_rot::face_rot_0: + break; + case face_rot::face_rot_90: { + float temp_u = intersect.uv[0]; + intersect.uv[0] = intersect.uv[1]; + intersect.uv[1] = 1 - temp_u; + } break; + + case face_rot::face_rot_180: + intersect.uv[0] = 1 - intersect.uv[0]; + intersect.uv[1] = 1 - intersect.uv[1]; + break; + + case face_rot::face_rot_270: { + float temp_u = intersect.uv[0]; + intersect.uv[0] = 1 - intersect.uv[1]; + intersect.uv[1] = temp_u; + break; + } } intersect.distance = (coordinate - ray.x0y0z0).square().sum(); @@ -328,29 +325,29 @@ void model::projection_image(face_idx fidx, // set the origin point of a ray switch (fidx) { - case face_idx::face_up: - ray.x0y0z0 = {c + 0.5f, 128.0f, r + 0.5f}; - break; - case face_idx::face_down: - ray.x0y0z0 = {c + 0.5f, -128.0f, 15.5f - r}; - break; - - case face_idx::face_east: - // r->y-, c->z-,x=128 - ray.x0y0z0 = {128.0f, 15.5f - r, 15.5f - c}; - break; - case face_idx::face_west: - // r->y-, c->z+, x=-128 - ray.x0y0z0 = {-128.0f, 15.5f - r, c + 0.5f}; - break; - case face_idx::face_south: - // r->y-, c->x+, z=128 - ray.x0y0z0 = {c + 0.5f, 15.5f - r, 128.0f}; - break; - case face_idx::face_north: - // r->y-, c->x-, z=-128 - ray.x0y0z0 = {15.5f - c, 15.5f - r, -128.0f}; - break; + case face_idx::face_up: + ray.x0y0z0 = {c + 0.5f, 128.0f, r + 0.5f}; + break; + case face_idx::face_down: + ray.x0y0z0 = {c + 0.5f, -128.0f, 15.5f - r}; + break; + + case face_idx::face_east: + // r->y-, c->z-,x=128 + ray.x0y0z0 = {128.0f, 15.5f - r, 15.5f - c}; + break; + case face_idx::face_west: + // r->y-, c->z+, x=-128 + ray.x0y0z0 = {-128.0f, 15.5f - r, c + 0.5f}; + break; + case face_idx::face_south: + // r->y-, c->x+, z=128 + ray.x0y0z0 = {c + 0.5f, 15.5f - r, 128.0f}; + break; + case face_idx::face_north: + // r->y-, c->x-, z=-128 + ray.x0y0z0 = {15.5f - c, 15.5f - r, -128.0f}; + break; } for (const element &ele : this->elements) { @@ -364,7 +361,7 @@ void model::projection_image(face_idx fidx, } if constexpr (false) { std::string msg = - fmt::format("num of intersects : {}", intersects.size()); + std::format("num of intersects : {}", intersects.size()); VCL_report(VCL_report_type_t::information, msg.c_str()); } @@ -386,8 +383,7 @@ void model::projection_image(face_idx fidx, // printf("\n0x%08X + 0x%08X -> ", color, ip.color()); color = ComposeColor_background_half_transparent(color, ip.color()); // printf("0x%08X; ", color); - if (getA(color) >= 255) - break; + if (getA(color) >= 255) break; } // printf(";"); @@ -421,20 +417,20 @@ Eigen::Array3f block_model::rotate_x(const Eigen::Array3f &pos, Eigen::Array3f diff_after = diff_before; switch (x_rot) { - case face_rot::face_rot_0: - return pos; - case face_rot::face_rot_90: - diff_after[y_idx] = diff_before[z_idx]; - diff_after[z_idx] = -diff_before[y_idx]; - break; - case face_rot::face_rot_180: - diff_after[y_idx] = -diff_before[y_idx]; - diff_after[z_idx] = -diff_before[z_idx]; - break; - case face_rot::face_rot_270: - diff_after[y_idx] = -diff_before[z_idx]; - diff_after[z_idx] = diff_before[y_idx]; - break; + case face_rot::face_rot_0: + return pos; + case face_rot::face_rot_90: + diff_after[y_idx] = diff_before[z_idx]; + diff_after[z_idx] = -diff_before[y_idx]; + break; + case face_rot::face_rot_180: + diff_after[y_idx] = -diff_before[y_idx]; + diff_after[z_idx] = -diff_before[z_idx]; + break; + case face_rot::face_rot_270: + diff_after[y_idx] = -diff_before[z_idx]; + diff_after[z_idx] = diff_before[y_idx]; + break; } return diff_after + center; } @@ -447,21 +443,21 @@ Eigen::Array3f block_model::rotate_y(const Eigen::Array3f &pos, Eigen::Array3f diff_after = diff_before; switch (y_rot) { - case face_rot::face_rot_0: - return pos; - - case face_rot::face_rot_90: - diff_after[x_idx] = -diff_before[z_idx]; - diff_after[z_idx] = diff_before[x_idx]; - break; - case face_rot::face_rot_180: - diff_after[x_idx] = -diff_before[x_idx]; - diff_after[z_idx] = -diff_before[z_idx]; - break; - case face_rot::face_rot_270: - diff_after[x_idx] = diff_before[z_idx]; - diff_after[z_idx] = -diff_before[x_idx]; - break; + case face_rot::face_rot_0: + return pos; + + case face_rot::face_rot_90: + diff_after[x_idx] = -diff_before[z_idx]; + diff_after[z_idx] = diff_before[x_idx]; + break; + case face_rot::face_rot_180: + diff_after[x_idx] = -diff_before[x_idx]; + diff_after[z_idx] = -diff_before[z_idx]; + break; + case face_rot::face_rot_270: + diff_after[x_idx] = diff_before[z_idx]; + diff_after[z_idx] = -diff_before[x_idx]; + break; } return diff_after + center; @@ -472,7 +468,7 @@ element block_model::element::rotate(face_rot x_rot, element ret; if constexpr (false) { std::string msg = - fmt::format("this->_from = [{}, {}, {}], this->_to = [{}, {}, {}]", + std::format("this->_from = [{}, {}, {}], this->_to = [{}, {}, {}]", this->_from[0], this->_from[1], this->_from[2], this->_to[0], this->_to[1], this->_to[2]); @@ -492,7 +488,7 @@ element block_model::element::rotate(face_rot x_rot, } if constexpr (false) { - std::string msg = fmt::format("f = [{}, {}, {}], t = [{}, {}, {}]", f[0], + std::string msg = std::format("f = [{}, {}, {}], t = [{}, {}, {}]", f[0], f[1], f[2], t[0], t[1], t[2]); VCL_report(VCL_report_type_t::information, msg.c_str()); } diff --git a/VisualCraftL/ParseResourcePack_json.cpp b/VisualCraftL/ParseResourcePack_json.cpp index 0dbe2f9e..4962f9c3 100644 --- a/VisualCraftL/ParseResourcePack_json.cpp +++ b/VisualCraftL/ParseResourcePack_json.cpp @@ -1,1279 +1,1268 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include -#include -#include -#include - -#include -#include "ParseResourcePack.h" -#include "VCL_internal.h" - -using namespace resource_json; - -using njson = nlohmann::json; - -size_t resource_json::state_list::num_1() const noexcept { - size_t counter = 0; - for (const auto &s : *this) { - if (s.value.empty()) { - counter++; - } - } - return counter; -} - -bool resource_json::state_list::euqals( - const state_list &another) const noexcept { - if (this->size() <= 0) return true; - if (this->size() != another.size()) return false; - int match_num = 0; - for (const state &sa : *this) { - for (const state &sb : another) { - if ((sa.key == sb.key) && (sa.value == sb.value)) { - match_num++; - } - } - } - - if (match_num < int(this->size())) { - return false; - } else { - return true; - } -} - -bool resource_json::state_list::contains( - const state_list &another) const noexcept { - if (another.size() > this->size()) { - return false; - } - - for (const state &s_json : another) { - bool is_current_state_matched = false; - for (const state &s_block : *this) { - if (s_json.key != s_block.key) { - continue; - } - - if (s_block.value == s_json.value) { - is_current_state_matched = true; - break; - } - } - if (!is_current_state_matched) { - return false; - } - } - - return true; -} - -bool resource_json::criteria_list_and::match( - const state_list &sl) const noexcept { - const auto &cl = *this; - int match_num = 0; - for (const criteria &c : cl) { - std::string_view key = c.key; - - const char *value = nullptr; - for (const state &s : sl) { - if (s.key == key) { - value = s.value.data(); - break; - } - } - // if value is not set, it is not considered as match - if (value == nullptr) { - break; - } - - if (c.match(value)) { - match_num++; - } - } - - if (match_num < int(cl.size())) { - return false; - } - return true; -} - -bool resource_json::match_criteria_list(const criteria_list_and &cl, - const state_list &sl) noexcept { - return cl.match(sl); -} - -bool resource_json::multipart_pair::match(const state_list &sl) const noexcept { - const resource_json::criteria *when = - std::get_if(&this->criteria_variant); - if (when != nullptr) { - std::string_view key = when->key; - const char *slvalue = nullptr; - for (const state &s : sl) { - if (s.key == key) { - slvalue = s.value.data(); - break; - } - } - // if sl don't have a value for the key of criteria, it is considered as - // mismatch - if (slvalue == nullptr) { - return false; - } - - return when->match(slvalue); - } - - if (std::get_if(&this->criteria_variant) != nullptr) { - return true; - } - - const auto &when_or = std::get(this->criteria_variant); - - size_t counter = 0; - for (const criteria_list_and &cl : when_or.components) { - if (cl.match(sl)) { - counter++; - } - } - - if (when_or.is_or) { - return counter > 0; - } else { - return counter >= when_or.components.size(); - } - - return false; -} - -model_pass_t block_states_variant::block_model_name( - const state_list &sl_blk) const noexcept { - model_pass_t res; - res.model_name = nullptr; - for (const auto &pair : this->LUT) { - if (sl_blk.contains(pair.first)) { - res = model_pass_t(pair.second); - return res; - } - } - - return res; -} - -void block_states_variant::sort() noexcept { - std::sort(LUT.begin(), LUT.end(), - [](const std::pair &a, - const std::pair &b) -> bool { - const size_t a_1 = a.first.num_1(); - const size_t b_1 = b.first.num_1(); - if (a_1 != b_1) { - return a_1 < b_1; - } - return a.first.size() > b.first.size(); - }); -} - -std::vector block_state_multipart::block_model_names( - const state_list &sl) const noexcept { - std::vector res; - - for (const multipart_pair &pair : this->pairs) { - if (pair.match(sl)) { - for (const auto &ms : pair.apply_blockmodel) { - res.emplace_back(model_pass_t(ms)); - } - } - // res.emplace_back(model_pass_t(pair.apply_blockmodel)); - } - - return res; -} - -struct parse_bs_buffer { - std::vector> attributes; -}; - -bool parse_block_state_variant(const njson::object_t &obj, - block_states_variant *const dest_variant); - -bool parse_block_state_multipart(const njson::object_t &obj, - block_state_multipart *const dest_variant); - -bool resource_json::parse_block_state( - const char *const json_str_beg, const char *const json_str_end, - std::variant *dest, - bool *const is_dest_variant) noexcept { - njson::object_t obj; - try { - obj = njson::parse(json_str_beg, json_str_end); - } catch (...) { - std::string msg = "nlohmann json failed to parse json string : "; - msg.append(json_str_beg, json_str_end); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - const bool has_variant = - obj.contains("variants") && obj.at("variants").is_object(); - const bool has_multipart = - obj.contains("multipart") && obj.at("multipart").is_array(); - - if (has_variant == has_multipart) { - std::string msg = fmt::format( - "Function parse_block_state failed to parse json : " - "has_variant = {}, has_multipart = {}.", - has_variant, has_multipart); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - if (has_variant) { - if (is_dest_variant != nullptr) *is_dest_variant = true; - - block_states_variant variant; - const bool ok = parse_block_state_variant(obj, &variant); - *dest = std::move(variant); - return ok; - } - - if (has_multipart) { - // parsing multipart is not supported yet. - if (is_dest_variant != nullptr) *is_dest_variant = false; - - block_state_multipart multipart; - const bool ok = parse_block_state_multipart(obj, &multipart); - *dest = std::move(multipart); - return ok; - // return parse_block_state_multipart(obj, dest_multipart); - } - // unreachable - return false; -} - -bool parse_block_state_list(std::string_view str, state_list *const sl, - parse_bs_buffer &buffer) noexcept { - sl->clear(); - if (str.size() <= 1) return true; - - if (str == "normal") { - return true; - } - - if (str == "all") { - return true; - } - - if (str == "map") { - return true; - } - - if (!blkid::process_state_list({str.data(), str.data() + str.size()}, - &buffer.attributes, nullptr)) { - std::string msg = fmt::format( - " Function parse_block_state_list failed to parse block state " - "list : {}", - str); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - sl->reserve(buffer.attributes.size()); - - for (const auto &pair : buffer.attributes) { - state strpair; - strpair.key.assign(pair.first.begin(), pair.first.end()); - strpair.value.assign(pair.second.begin(), pair.second.end()); - - sl->emplace_back(strpair); - } - - return true; -} - -bool parse_block_state_list(std::string_view str, - state_list *const sl) noexcept { - parse_bs_buffer buffer; - - return parse_block_state_list(str, sl, buffer); -} - -model_store_t json_to_model(const njson &obj) noexcept { - model_store_t res; - - res.model_name = obj.at("model"); - - if (obj.contains("x") && obj.at("x").is_number()) { - const int val = obj.at("x"); - - if (!block_model::is_0_90_180_270(val)) { - std::string msg; - msg = fmt::format( - "Invalid x rotation value : {}. Invalid values : 0, 90, 180, 270.", - val); - VCL_report(VCL_report_type_t::error, msg.c_str()); - return {}; - } - - res.x = block_model::int_to_face_rot(val); - } - - if (obj.contains("y") && obj.at("y").is_number()) { - const int val = obj.at("y"); - if (!block_model::is_0_90_180_270(val)) { - std::string msg; - msg = fmt::format( - "Invalid y rotation value : {}. Invalid values : 0, 90, 180, 270.", - val); - VCL_report(VCL_report_type_t::error, msg.c_str()); - return {}; - } - res.y = block_model::int_to_face_rot(val); - } - - if (obj.contains("uvlock") && obj.at("uvlock").is_boolean()) { - res.uvlock = obj.at("uvlock"); - } - - return res; -} - -bool parse_block_state_variant(const njson::object_t &obj, - block_states_variant *const dest) { - const njson &variants = obj.at("variants"); - - dest->LUT.clear(); - dest->LUT.reserve(variants.size()); - - for (auto pair : variants.items()) { - if (!pair.value().is_structured()) { - std::string msg = fmt::format( - "Function parse_block_state_variant failed to parse json : " - "value for key \"{}\" is not an object or array.", - pair.key()); - - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - if (pair.value().is_array() && pair.value().size() <= 0) { - std::string msg = fmt::format( - "Function parse_block_state_variant failed to parse json : " - "value for key \"{}\" is an empty array.", - pair.key().data()); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - const njson &obj = - (pair.value().is_object()) ? (pair.value()) : (pair.value().at(0)); - - if ((!obj.contains("model")) || (!obj.at("model").is_string())) { - std::string msg = fmt::format( - "Function parse_block_state_variant failed to parse json : no " - "valid value for key \"model\""); - - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - std::pair p; - - parse_bs_buffer buffer; - - if (!parse_block_state_list(pair.key(), &p.first, buffer)) { - std::string msg = - fmt::format("Failed to parse block state list : {}", pair.key()); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - p.second = json_to_model(obj); - - dest->LUT.emplace_back(p); - } - - dest->sort(); - - return true; -} - -void parse_single_criteria_split(std::string_view key, std::string_view values, - criteria *const cr) noexcept { - cr->key = key; - cr->values.clear(); - - size_t current_value_beg_idx = 0; - - for (size_t idx = 0;; idx++) { - if (values.size() <= idx || values[idx] == '\0' || values[idx] == '|') { - cr->values.emplace_back( - values.substr(current_value_beg_idx, idx - current_value_beg_idx)); - current_value_beg_idx = idx + 1; - } - - if (values.size() <= idx || values[idx] == '\0') { - break; - } - } -} - -model_store_t parse_single_apply(const njson &single_obj) noexcept(false) { - model_store_t ms; - - ms.model_name = single_obj.at("model"); - if (single_obj.contains("x")) { - ms.x = block_model::int_to_face_rot(single_obj.at("x")); - } - if (single_obj.contains("y")) { - ms.y = block_model::int_to_face_rot(single_obj.at("y")); - } - if (single_obj.contains("uvlock")) { - ms.uvlock = single_obj.at("uvlock"); - } - - return ms; -} - -std::vector parse_multipart_apply(const njson &apply) noexcept( - false) { - std::vector ret; - - if (apply.is_object()) { - ret.emplace_back(parse_single_apply(apply)); - return ret; - } - - if (apply.is_array()) { - for (size_t i = 0; i < apply.size(); i++) { - ret.emplace_back(parse_single_apply(apply.at(i))); - } - return ret; - } - throw std::runtime_error("Invalid value for \"apply\" in a multipart."); - return {}; -} - -std::variant -parse_multipart_when(const njson &when) noexcept(false) { - const bool is_or = when.contains("OR"); - const bool is_and = when.contains("AND"); - if (is_or || is_and) { - const njson &list_or_and = (is_or) ? (when.at("OR")) : (when.at("AND")); - criteria_list_or_and when_or_and; - - when_or_and.components.reserve(list_or_and.size()); - when_or_and.is_or = is_or; - - for (size_t idx = 0; idx < list_or_and.size(); idx++) { - criteria_list_and and_list; - - for (auto it = list_or_and[idx].begin(); it != list_or_and[idx].end(); - ++it) { - criteria cr; - if (it.value().is_boolean()) { - cr.key = it.key(); - cr.values.emplace_back((it.value()) ? ("true") : ("false")); - - } else { - parse_single_criteria_split(it.key(), it.value().get(), - &cr); - } - // const std::string &v_str = ; - and_list.emplace_back(std::move(cr)); - } - - when_or_and.components.emplace_back(std::move(and_list)); - } - - return when_or_and; - } - - if (when.size() == 1) { - criteria cr; - - auto it = when.begin(); - - if (it.value().is_boolean()) { - cr.key = it.key(); - cr.values.emplace_back((it.value()) ? ("true") : ("false")); - } else { - parse_single_criteria_split(it.key(), it.value().get(), &cr); - } - - return cr; - } - criteria_list_and and_list; - - for (auto it = when.begin(); it != when.end(); ++it) { - criteria cr; - - if (it.value().is_boolean()) { - cr.key = it.key(); - cr.values.emplace_back((it.value()) ? ("true") : ("false")); - } else { - parse_single_criteria_split(it.key(), it.value().get(), &cr); - } - and_list.emplace_back(std::move(cr)); - } - - criteria_list_or_and when_or; - when_or.components.emplace_back(std::move(and_list)); - - return when_or; -} - -bool parse_block_state_multipart(const njson::object_t &obj, - block_state_multipart *const dest) { - const njson &multiparts = obj.at("multipart"); - - if (!multiparts.is_array()) { - std::string msg = fmt::format("Fatal error : multipart must be an array."); - - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - dest->pairs.clear(); - - for (size_t i = 0; i < multiparts.size(); i++) { - const njson &part = multiparts[i]; - - multipart_pair mpp; - - // parse apply - try { - const njson &apply = part.at("apply"); - mpp.apply_blockmodel = parse_multipart_apply(apply); - } catch (const std::exception &err) { - std::string msg = fmt::format( - "An error occurred when parsing the value of apply. Details : {}", - err.what()); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - // parse when - if (!part.contains("when")) { - mpp.criteria_variant = criteria_all_pass(); - dest->pairs.emplace_back(std::move(mpp)); - continue; - } - - try { - const njson &when = part.at("when"); - - mpp.criteria_variant = parse_multipart_when(when); - - } catch (const std::exception &err) { - std::string msg = fmt::format( - "\nFatal error : failed to parse \"when\" for a multipart blockstate " - "file. Details : {}\n", - err.what()); - return false; - } - - dest->pairs.emplace_back(std::move(mpp)); - /* - if (!part.is_object()) { - printf("\nFatal error : multipart must an array of objects.\n"); - return false; - } - - if (!part.contains("apply") || !part.contains("when")) { - printf("\nFatal error : element in multipart must contains \"apply\" - and " - "\"when\"\n"); - return false; - } - - if (!apply.contains("model") || !apply.at("model").is_string()) { - printf("\nFatal error : multipart should apply a model.\n"); - return false; - } - */ - } - - return true; -} - -struct face_json_temp { - std::string texture{""}; - std::array uv{0, 0, 16, 16}; - block_model::face_idx cullface_face; - bool have_cullface{false}; - bool is_hidden{true}; ///< note that by default, is_hidden is true. -}; - -struct element_json_temp { - std::array from; - std::array to; - std::array faces; -}; - -struct block_model_json_temp { - std::string parent{""}; - std::map textures; - std::vector elements; - bool is_inherited{false}; -}; - -block_model::face_idx string_to_face_idx(std::string_view str, - bool *const _ok) noexcept { - block_model::face_idx res = block_model::face_idx::face_down; - bool ok = false; - if (str == "up") { - res = block_model::face_idx::face_up; - ok = true; - } - if (str == "down") { - res = block_model::face_idx::face_down; - ok = true; - } - if (str == "bottom") { - res = block_model::face_idx::face_down; - ok = true; - } - if (str == "north") { - res = block_model::face_idx::face_north; - ok = true; - } - if (str == "south") { - res = block_model::face_idx::face_south; - ok = true; - } - if (str == "east") { - res = block_model::face_idx::face_east; - ok = true; - } - if (str == "west") { - res = block_model::face_idx::face_west; - ok = true; - } - - if (_ok != nullptr) { - *_ok = ok; - } - return res; -} - -const char *face_idx_to_string(block_model::face_idx f) noexcept { - switch (f) { - case block_model::face_idx::face_up: - return "up"; - case block_model::face_idx::face_down: - return "down"; - case block_model::face_idx::face_north: - return "north"; - case block_model::face_idx::face_south: - return "south"; - case block_model::face_idx::face_east: - return "east"; - case block_model::face_idx::face_west: - return "west"; - } - - return nullptr; -} - -bool parse_single_model_json(const char *const json_beg, - const char *const json_end, - block_model_json_temp *const dest) { - dest->textures.clear(); - dest->elements.clear(); - // disable exceptions, and ignore comments. - njson obj = njson::parse(json_beg, json_end, nullptr, false, true); - if (obj.is_null()) { - // this may be unsafe but just keep it currently. - std::string msg = "Failed to parse block model json : "; - msg.append(json_beg, json_end); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - if (obj.contains("parent") && obj.at("parent").is_string()) { - std::string p_str = obj.at("parent"); - if (p_str.starts_with("minecraft:")) { - dest->parent = p_str.substr(sizeof("minecraft:") / sizeof(char) - 1); - } else { - dest->parent = p_str; - } - } - - if (obj.contains("textures") && obj.at("textures").is_object()) { - const njson &textures = obj.at("textures"); - // dest->textures.reserve(textures.size()); - for (auto temp : textures.items()) { - if (!temp.value().is_string()) { - continue; - } - - auto it = dest->textures.emplace(temp.key(), temp.value()); - - if (it.first->second.starts_with("block/") || - it.first->second.starts_with("blocks/")) { - it.first->second = "minecraft:" + it.first->second; - } - } - } - // finished textures - - if (obj.contains("elements") && obj.at("elements").is_array()) { - const njson::array_t &elearr = obj.at("elements"); - - dest->elements.reserve(obj.size()); - for (const auto &e : elearr) { - if (!e.is_object()) { - return false; - } - - element_json_temp ele; - if (!e.contains("from") || !e.at("from").is_array()) { - ::VCL_report(VCL_report_type_t::error, - "\"from\" doesn't exist, or is not an array."); - return false; - } - // from - { - const njson::array_t &arr_from = e.at("from"); - if (arr_from.size() != 3 || !arr_from.front().is_number()) { - ::VCL_report(VCL_report_type_t::error, "size of \"from\" is not 3"); - return false; - } - - for (int idx = 0; idx < 3; idx++) { - if (!arr_from[idx].is_number()) { - ::VCL_report( - VCL_report_type_t::error, - "one or more element in array \"from\" is not number."); - return false; - } - ele.from[idx] = arr_from[idx]; - } - } - if (!e.contains("to") || !e.at("to").is_array()) { - ::VCL_report(VCL_report_type_t::error, - "\"to\" doesn't exist, or is not an array."); - return false; - } - // to - { - const njson::array_t &arr_to = e.at("to"); - if (arr_to.size() != 3) { - ::VCL_report(VCL_report_type_t::error, "size of \"to\" is not 3."); - return false; - } - - for (int idx = 0; idx < 3; idx++) { - if (!arr_to[idx].is_number()) { - ::VCL_report(VCL_report_type_t::error, - "one or more element in array \"to\" is not number."); - return false; - } - ele.to[idx] = arr_to[idx]; - } - } - - // faces - { - if (!e.contains("faces") || !e.at("faces").is_object()) { - ::VCL_report(VCL_report_type_t::error, - "\"faces\" doesn't exist, or is not an object."); - return false; - } - - const njson &faces = e.at("faces"); - for (auto temp : faces.items()) { - // if the face is not object, skip current face. - if (!temp.value().is_object()) continue; - face_json_temp f; - block_model::face_idx fidx; - { - bool _ok; - fidx = string_to_face_idx(temp.key(), &_ok); - if (!_ok) { - std::string msg = fmt::format( - "Error while parsing block model json : invalid key {} " - "doesn't refer to any face.", - temp.key()); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - } - - const njson &curface = temp.value(); - - if (!curface.contains("texture") || - !curface.at("texture").is_string()) { - ::VCL_report( - VCL_report_type_t::error, - "Error while parsing block model json : face do not have " - "texture."); - return false; - } - - f.texture = curface.at("texture"); - if (f.texture.starts_with("block/")) { - f.texture = ("minecraft:") + f.texture; - } - // finished texture - - // cullface - { - std::string cullface_temp(""); - if (curface.contains("cullface") && - curface.at("cullface").is_string()) { - cullface_temp = curface.at("cullface"); - } - - if (!cullface_temp.empty()) { - bool ok; - const block_model::face_idx cullface_fidx = - string_to_face_idx(cullface_temp, &ok); - - if (!ok) { - std::string msg = fmt::format("Invalid value for cullface : {}", - cullface_temp); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - f.cullface_face = cullface_fidx; - f.have_cullface = true; - } - } - // finished cullface - - // uv - if (curface.contains("uv") && curface.at("uv").is_array()) { - const njson::array_t &uvarr = curface.at("uv"); - - if (uvarr.size() != 4) { - ::VCL_report(VCL_report_type_t::error, - "Invalid value for uv array : the size must be 4."); - return false; - } - - for (int idx = 0; idx < 4; idx++) { - if (!uvarr.at(idx).is_number()) { - ::VCL_report(VCL_report_type_t::error, - "Invalid value for uv array : the value must be " - "numbers."); - return false; - } - f.uv[idx] = uvarr[idx]; - } - } - // finished uv - - f.is_hidden = false; - // finished is_hidden - - // write in this face - ele.faces[int(fidx)] = f; - } - } - // finished all faces - - dest->elements.emplace_back(ele); - } - } - - return true; -} - -const char *dereference_texture_name( - std::map::iterator it, - std::map &text) noexcept { - if (it == text.end()) { - return nullptr; - } - - if (!it->second.starts_with('#')) { - return it->second.data(); - } - - // here it->second must be a # reference. - - auto next_it = text.find(it->second.data() + 1); - - // This line is added as a patch, to fix error when parsing 1.19.3 data packs. - // I'm not sure whether models that triggered this can be parsed correctly, it - // is only introduced to prevent endless recursion, so that errors can be - // reported - if (next_it == it) { - // found a self-reference value - return nullptr; - } - - const char *const ret = dereference_texture_name(next_it, text); - - if (ret != nullptr) { - // found a non-reference value - it->second = ret; - return ret; - } else { - // it->second is the the farest reference and no further link - return it->second.data(); - } -} - -void dereference_texture_name( - std::map &text) noexcept { - for (auto it = text.begin(); it != text.end(); ++it) { - if (!it->second.starts_with('#')) continue; - dereference_texture_name(it, text); - } -} - -void dereference_model(block_model_json_temp &model) { - // dereference_texture_name(model.textures); - - for (auto &ele : model.elements) { - for (auto &face : ele.faces) { - if (face.is_hidden) continue; - if (face.texture.starts_with('#')) { - auto it = model.textures.find(face.texture.data() + 1); - - if (it == model.textures.end()) { - continue; - } - face.texture = it->second; - } - } - // finished current face - } - // finished current element -} - -bool model_json_inherit_new(block_model_json_temp &child, - block_model_json_temp &parent, const bool) { - if (child.parent.empty()) { - ::VCL_report(VCL_report_type_t::error, "child has no parent."); - return false; - } - - parent.is_inherited = true; - - // child.textures.reserve(child.textures.size() + parent.textures.size()); - // merge textures - for (const auto &pt : parent.textures) { - if (!child.textures.contains(pt.first)) { - child.textures.emplace(pt.first, pt.second); - } - } - - dereference_texture_name(child.textures); - - // if child have a parent, and child doesn't define its own element, child - // inherit parent's elements. - if (child.elements.size() <= 0) { - child.elements = parent.elements; - } - - dereference_model(child); - - // parent - child.parent = parent.parent; - return true; -} - -bool inherit_recrusively(std::string_view childname, - block_model_json_temp &child, - std::unordered_map - &temp_models) noexcept { - if (child.parent.empty()) return true; - - // #warning This function is not finished yet. I hope to inherit from the - // root, which measn to find the root and inherit from root to leaf - - // find parent till the root - auto it = temp_models.find(child.parent); - - if (it == temp_models.end()) { - std::string msg = fmt::format( - "Failed to inherit. Undefined reference to model {}, " - "required by {}.", - child.parent.data(), childname.data()); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - if (!it->second.parent.empty()) { - // find root - const bool success = - inherit_recrusively(it->first, it->second, temp_models); - if (!success) { - return false; - } - } - /* - printf("\ninhering : parent : %s, child : %s,\n", child.parent.data(), - childname.data()); - */ - const bool success = model_json_inherit_new(child, it->second, false); - - // dereference_texture_name(child.textures); - - if (!success) { - std::string msg = fmt::format("Failed to inherit. Child : {}, parent : {}.", - childname.data(), child.parent.data()); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - return true; -} - -bool resource_pack::add_block_models( - const zipped_folder &resourece_pack_root, - const bool on_conflict_replace_old) noexcept { - const std::unordered_map *files = nullptr; - // find assets/minecraft/models/block - { - const zipped_folder *temp = resourece_pack_root.subfolder("assets"); - if (temp == nullptr) return false; - temp = temp->subfolder("minecraft"); - if (temp == nullptr) return false; - temp = temp->subfolder("models"); - if (temp == nullptr) return false; - temp = temp->subfolder("block"); - if (temp == nullptr) return false; - - files = &temp->files; - } - - // the name of model is : block/ - std::unordered_map temp_models; - - temp_models.reserve(files->size()); - - std::array buffer; - - for (const auto &file : *files) { - if (!file.first.ends_with(".json")) continue; - buffer.fill('\0'); - std::strcpy(buffer.data(), "block/"); - { - const int end = file.first.find_last_of('.'); - char *const dest = buffer.data() + std::strlen(buffer.data()); - for (int idx = 0; idx < end; idx++) { - dest[idx] = file.first[idx]; - } - } - - block_model_json_temp bmjt; - - const bool ok = parse_single_model_json( - (const char *)file.second.data(), - (const char *)file.second.data() + file.second.file_size(), &bmjt); - - if (!ok) { - std::string msg = fmt::format( - "Failed to parse assets/minecraft/models/block/{}.", file.first); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - temp_models.emplace(buffer.data(), bmjt); - } - // parsed all jsons - /* - printf("Loaded %i model jsons.\n", int(temp_models.size())); - - for (const auto &file : temp_models) { - printf("%s, ", file.first.data()); - } - printf("\n\n"); - - */ - - // inherit - for (auto &model : temp_models) { - const bool ok = inherit_recrusively(model.first, model.second, temp_models); - if (!ok) { - model.second.parent = "INVALID"; - std::string msg = fmt::format( - "Failed to inherit model {}. This model will be " - "skipped, but it may cause further errors.", - model.first); - ::VCL_report(VCL_report_type_t::warning, msg.c_str()); - // #warning following line should be commented. - // return false; - continue; - } - - dereference_texture_name(model.second.textures); - dereference_model(model.second); - } - // remove invalid - for (auto it = temp_models.begin(); it != temp_models.end();) { - if (it->second.parent == "INVALID") { - it = temp_models.erase(it); - continue; - } - - ++it; - } - - // convert temp models to block_models - this->block_models.reserve(this->block_models.size() + temp_models.size()); - for (auto &tmodel : temp_models) { - if (this->block_models.contains(tmodel.first) && !on_conflict_replace_old) { - continue; - } - - block_model::model md; - bool skip_this_model = false; - - md.elements.reserve(tmodel.second.elements.size()); - for (auto &tele : tmodel.second.elements) { - if (skip_this_model) break; - block_model::element ele; - - // ele._from = tele.from; - for (int idx = 0; idx < 3; idx++) { - ele._from[idx] = tele.from[idx]; - ele._to[idx] = tele.to[idx]; - } - - for (uint8_t faceidx = 0; faceidx < 6; faceidx++) { - if (skip_this_model) break; - auto &tface = tele.faces[faceidx]; - ele.faces[faceidx].is_hidden = tface.is_hidden; - if (tface.is_hidden) { - ele.faces[faceidx].texture = nullptr; - continue; - } - ele.faces[faceidx].uv_start[0] = tface.uv[0]; - ele.faces[faceidx].uv_start[1] = tface.uv[1]; - ele.faces[faceidx].uv_end[0] = tface.uv[2]; - ele.faces[faceidx].uv_end[1] = tface.uv[3]; - - auto imgptr = this->find_texture(tface.texture, false); - - if (imgptr == nullptr) { - if (tface.texture.starts_with('#') && tmodel.second.is_inherited) { - // This model is considered to be abstract - skip_this_model = true; - continue; - } - std::string msg = fmt::format( - "Undefined reference to texture {}, required by " - "model {} but no such image.\nThe textures are : \n", - tface.texture, tmodel.first); - for (const auto &pair : tmodel.second.textures) { - msg.push_back('{'); - std::string temp = - fmt::format("{}, {}\n", pair.first.data(), pair.second.data()); - msg.append(temp); - msg.push_back('}'); - } - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - - // if managed to find, go on - } - ele.faces[faceidx].texture = imgptr; - } - // finished all faces - - md.elements.emplace_back(ele); - } - // finished current model - if (!skip_this_model) { - this->block_models.emplace(tmodel.first, std::move(md)); - } - } - - for (const auto &pair : this->block_models) { - for (const auto &ele : pair.second.elements) { - for (const auto &face : ele.faces) { - if (!face.is_hidden && face.texture == nullptr) { - std::string msg = fmt::format( - "Found an error while examining all block models : " - "face.texture==nullptr in model {}", - pair.first); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - } - } - } - - return true; -} - -bool resource_pack::add_block_states( - const zipped_folder &resourece_pack_root, - const bool on_conflict_replace_old) noexcept { - const std::unordered_map *files = nullptr; - { - const zipped_folder *temp = resourece_pack_root.subfolder("assets"); - if (temp == nullptr) { - return false; - } - temp = temp->subfolder("minecraft"); - if (temp == nullptr) { - return false; - } - temp = temp->subfolder("blockstates"); - if (temp == nullptr) { - return false; - } - files = &temp->files; - } - - this->block_states.reserve(this->block_states.size() + files->size()); - - for (const auto &file : *files) { - if (this->block_states.contains(file.first) && !on_conflict_replace_old) { - continue; - } - std::variant - bs; - bool is_dest_variant; - - const bool success = parse_block_state( - (const char *)file.second.data(), - (const char *)file.second.data() + file.second.file_size(), &bs, - &is_dest_variant); - - if (!success) { - std::string msg = fmt::format( - "Failed to parse block state json file " - "assets/minecraft/blockstates/{}. This will be " - "skipped but may cause further errors.\n", - file.first); - - ::VCL_report(VCL_report_type_t::warning, msg.c_str()); - continue; - } - - const int substrlen = file.first.find_last_of('.'); - this->block_states.emplace(file.first.substr(0, substrlen), std::move(bs)); - } - - return true; -} +/* + Copyright © 2021-2026 TokiNoBug +This file is part of SlopeCraft. + + SlopeCraft is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SlopeCraft is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SlopeCraft. If not, see . + + Contact with me: + github:https://github.com/SlopeCraft/SlopeCraft + bilibili:https://space.bilibili.com/351429231 +*/ + +#include +#include +#include +#include + +#include +#include "ParseResourcePack.h" +#include "VCL_internal.h" + +using namespace resource_json; + +using njson = nlohmann::json; + +size_t resource_json::state_list::num_1() const noexcept { + size_t counter = 0; + for (const auto &s : *this) { + if (s.value.empty()) { + counter++; + } + } + return counter; +} + +bool resource_json::state_list::euqals( + const state_list &another) const noexcept { + if (this->size() <= 0) return true; + if (this->size() != another.size()) return false; + int match_num = 0; + for (const state &sa : *this) { + for (const state &sb : another) { + if ((sa.key == sb.key) && (sa.value == sb.value)) { + match_num++; + } + } + } + + if (match_num < int(this->size())) { + return false; + } else { + return true; + } +} + +bool resource_json::state_list::contains( + const state_list &another) const noexcept { + if (another.size() > this->size()) { + return false; + } + + for (const state &s_json : another) { + bool is_current_state_matched = false; + for (const state &s_block : *this) { + if (s_json.key != s_block.key) { + continue; + } + + if (s_block.value == s_json.value) { + is_current_state_matched = true; + break; + } + } + if (!is_current_state_matched) { + return false; + } + } + + return true; +} + +bool resource_json::criteria_list_and::match( + const state_list &sl) const noexcept { + const auto &cl = *this; + int match_num = 0; + for (const criteria &c : cl) { + std::string_view key = c.key; + + const char *value = nullptr; + for (const state &s : sl) { + if (s.key == key) { + value = s.value.data(); + break; + } + } + // if value is not set, it is not considered as match + if (value == nullptr) { + break; + } + + if (c.match(value)) { + match_num++; + } + } + + if (match_num < int(cl.size())) { + return false; + } + return true; +} + +bool resource_json::match_criteria_list(const criteria_list_and &cl, + const state_list &sl) noexcept { + return cl.match(sl); +} + +bool resource_json::multipart_pair::match(const state_list &sl) const noexcept { + const resource_json::criteria *when = + std::get_if(&this->criteria_variant); + if (when != nullptr) { + std::string_view key = when->key; + const char *slvalue = nullptr; + for (const state &s : sl) { + if (s.key == key) { + slvalue = s.value.data(); + break; + } + } + // if sl don't have a value for the key of criteria, it is considered as + // mismatch + if (slvalue == nullptr) { + return false; + } + + return when->match(slvalue); + } + + if (std::get_if(&this->criteria_variant) != nullptr) { + return true; + } + + const auto &when_or = std::get(this->criteria_variant); + + size_t counter = 0; + for (const criteria_list_and &cl : when_or.components) { + if (cl.match(sl)) { + counter++; + } + } + + if (when_or.is_or) { + return counter > 0; + } else { + return counter >= when_or.components.size(); + } +} + +model_pass_t block_states_variant::block_model_name( + const state_list &sl_blk) const noexcept { + model_pass_t res; + res.model_name = nullptr; + for (const auto &pair : this->LUT) { + if (sl_blk.contains(pair.first)) { + res = model_pass_t(pair.second); + return res; + } + } + + return res; +} + +void block_states_variant::sort() noexcept { + std::sort(LUT.begin(), LUT.end(), + [](const std::pair &a, + const std::pair &b) -> bool { + const size_t a_1 = a.first.num_1(); + const size_t b_1 = b.first.num_1(); + if (a_1 != b_1) { + return a_1 < b_1; + } + return a.first.size() > b.first.size(); + }); +} + +std::vector block_state_multipart::block_model_names( + const state_list &sl) const noexcept { + std::vector res; + + for (const multipart_pair &pair : this->pairs) { + if (pair.match(sl)) { + for (const auto &ms : pair.apply_blockmodel) { + res.emplace_back(model_pass_t(ms)); + } + } + // res.emplace_back(model_pass_t(pair.apply_blockmodel)); + } + + return res; +} + +struct parse_bs_buffer { + std::vector> attributes; +}; + +bool parse_block_state_variant(const njson::object_t &obj, + block_states_variant *const dest_variant); + +bool parse_block_state_multipart(const njson::object_t &obj, + block_state_multipart *const dest_variant); + +bool resource_json::parse_block_state( + const char *const json_str_beg, const char *const json_str_end, + std::variant *dest, + bool *const is_dest_variant) noexcept { + njson::object_t obj; + try { + obj = njson::parse(json_str_beg, json_str_end); + } catch (...) { + std::string msg = "nlohmann json failed to parse json string : "; + msg.append(json_str_beg, json_str_end); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + const bool has_variant = + obj.contains("variants") && obj.at("variants").is_object(); + const bool has_multipart = + obj.contains("multipart") && obj.at("multipart").is_array(); + + if (has_variant == has_multipart) { + std::string msg = std::format( + "Function parse_block_state failed to parse json : " + "has_variant = {}, has_multipart = {}.", + has_variant, has_multipart); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + if (has_variant) { + if (is_dest_variant != nullptr) *is_dest_variant = true; + + block_states_variant variant; + const bool ok = parse_block_state_variant(obj, &variant); + *dest = std::move(variant); + return ok; + } + + if (has_multipart) { + // parsing multipart is not supported yet. + if (is_dest_variant != nullptr) *is_dest_variant = false; + + block_state_multipart multipart; + const bool ok = parse_block_state_multipart(obj, &multipart); + *dest = std::move(multipart); + return ok; + // return parse_block_state_multipart(obj, dest_multipart); + } + // unreachable + return false; +} + +bool parse_block_state_list(std::string_view str, state_list *const sl, + parse_bs_buffer &buffer) noexcept { + sl->clear(); + if (str.size() <= 1) return true; + + if (str == "normal") { + return true; + } + + if (str == "all") { + return true; + } + + if (str == "map") { + return true; + } + + if (!blkid::process_state_list({str.data(), str.data() + str.size()}, + &buffer.attributes, nullptr)) { + std::string msg = std::format( + " Function parse_block_state_list failed to parse block state " + "list : {}", + str); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + sl->reserve(buffer.attributes.size()); + + for (const auto &pair : buffer.attributes) { + state strpair; + strpair.key.assign(pair.first.begin(), pair.first.end()); + strpair.value.assign(pair.second.begin(), pair.second.end()); + + sl->emplace_back(strpair); + } + + return true; +} + +bool parse_block_state_list(std::string_view str, + state_list *const sl) noexcept { + parse_bs_buffer buffer; + + return parse_block_state_list(str, sl, buffer); +} + +model_store_t json_to_model(const njson &obj) noexcept { + model_store_t res; + + res.model_name = obj.at("model"); + + if (obj.contains("x") && obj.at("x").is_number()) { + const int val = obj.at("x"); + + if (!block_model::is_0_90_180_270(val)) { + std::string msg; + msg = std::format( + "Invalid x rotation value : {}. Invalid values : 0, 90, 180, 270.", + val); + VCL_report(VCL_report_type_t::error, msg.c_str()); + return {}; + } + + res.x = block_model::int_to_face_rot(val); + } + + if (obj.contains("y") && obj.at("y").is_number()) { + const int val = obj.at("y"); + if (!block_model::is_0_90_180_270(val)) { + std::string msg; + msg = std::format( + "Invalid y rotation value : {}. Invalid values : 0, 90, 180, 270.", + val); + VCL_report(VCL_report_type_t::error, msg.c_str()); + return {}; + } + res.y = block_model::int_to_face_rot(val); + } + + if (obj.contains("uvlock") && obj.at("uvlock").is_boolean()) { + res.uvlock = obj.at("uvlock"); + } + + return res; +} + +bool parse_block_state_variant(const njson::object_t &obj, + block_states_variant *const dest) { + const njson &variants = obj.at("variants"); + + dest->LUT.clear(); + dest->LUT.reserve(variants.size()); + + for (auto pair : variants.items()) { + if (!pair.value().is_structured()) { + std::string msg = std::format( + "Function parse_block_state_variant failed to parse json : " + "value for key \"{}\" is not an object or array.", + pair.key()); + + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + if (pair.value().is_array() && pair.value().size() <= 0) { + std::string msg = std::format( + "Function parse_block_state_variant failed to parse json : " + "value for key \"{}\" is an empty array.", + pair.key().data()); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + const njson &obj = + (pair.value().is_object()) ? (pair.value()) : (pair.value().at(0)); + + if ((!obj.contains("model")) || (!obj.at("model").is_string())) { + std::string msg = std::format( + "Function parse_block_state_variant failed to parse json : no " + "valid value for key \"model\""); + + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + std::pair p; + + parse_bs_buffer buffer; + + if (!parse_block_state_list(pair.key(), &p.first, buffer)) { + std::string msg = + std::format("Failed to parse block state list : {}", pair.key()); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + p.second = json_to_model(obj); + + dest->LUT.emplace_back(p); + } + + dest->sort(); + + return true; +} + +void parse_single_criteria_split(std::string_view key, std::string_view values, + criteria *const cr) noexcept { + cr->key = key; + cr->values.clear(); + + size_t current_value_beg_idx = 0; + + for (size_t idx = 0;; idx++) { + if (values.size() <= idx || values[idx] == '\0' || values[idx] == '|') { + cr->values.emplace_back( + values.substr(current_value_beg_idx, idx - current_value_beg_idx)); + current_value_beg_idx = idx + 1; + } + + if (values.size() <= idx || values[idx] == '\0') { + break; + } + } +} + +model_store_t parse_single_apply(const njson &single_obj) noexcept(false) { + model_store_t ms; + + ms.model_name = single_obj.at("model"); + if (single_obj.contains("x")) { + ms.x = block_model::int_to_face_rot(single_obj.at("x")); + } + if (single_obj.contains("y")) { + ms.y = block_model::int_to_face_rot(single_obj.at("y")); + } + if (single_obj.contains("uvlock")) { + ms.uvlock = single_obj.at("uvlock"); + } + + return ms; +} + +std::vector parse_multipart_apply(const njson &apply) noexcept( + false) { + std::vector ret; + + if (apply.is_object()) { + ret.emplace_back(parse_single_apply(apply)); + return ret; + } + + if (apply.is_array()) { + for (size_t i = 0; i < apply.size(); i++) { + ret.emplace_back(parse_single_apply(apply.at(i))); + } + return ret; + } + throw std::runtime_error("Invalid value for \"apply\" in a multipart."); +} + +std::variant +parse_multipart_when(const njson &when) noexcept(false) { + const bool is_or = when.contains("OR"); + const bool is_and = when.contains("AND"); + if (is_or || is_and) { + const njson &list_or_and = (is_or) ? (when.at("OR")) : (when.at("AND")); + criteria_list_or_and when_or_and; + + when_or_and.components.reserve(list_or_and.size()); + when_or_and.is_or = is_or; + + for (size_t idx = 0; idx < list_or_and.size(); idx++) { + criteria_list_and and_list; + + for (auto it = list_or_and[idx].begin(); it != list_or_and[idx].end(); + ++it) { + criteria cr; + if (it.value().is_boolean()) { + cr.key = it.key(); + cr.values.emplace_back((it.value()) ? ("true") : ("false")); + + } else { + parse_single_criteria_split(it.key(), it.value().get(), + &cr); + } + // const std::string &v_str = ; + and_list.emplace_back(std::move(cr)); + } + + when_or_and.components.emplace_back(std::move(and_list)); + } + + return when_or_and; + } + + if (when.size() == 1) { + criteria cr; + + auto it = when.begin(); + + if (it.value().is_boolean()) { + cr.key = it.key(); + cr.values.emplace_back((it.value()) ? ("true") : ("false")); + } else { + parse_single_criteria_split(it.key(), it.value().get(), &cr); + } + + return cr; + } + criteria_list_and and_list; + + for (auto it = when.begin(); it != when.end(); ++it) { + criteria cr; + + if (it.value().is_boolean()) { + cr.key = it.key(); + cr.values.emplace_back((it.value()) ? ("true") : ("false")); + } else { + parse_single_criteria_split(it.key(), it.value().get(), &cr); + } + and_list.emplace_back(std::move(cr)); + } + + criteria_list_or_and when_or; + when_or.components.emplace_back(std::move(and_list)); + + return when_or; +} + +bool parse_block_state_multipart(const njson::object_t &obj, + block_state_multipart *const dest) { + const njson &multiparts = obj.at("multipart"); + + if (!multiparts.is_array()) { + std::string msg = std::format("Fatal error : multipart must be an array."); + + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + dest->pairs.clear(); + + for (size_t i = 0; i < multiparts.size(); i++) { + const njson &part = multiparts[i]; + + multipart_pair mpp; + + // parse apply + try { + const njson &apply = part.at("apply"); + mpp.apply_blockmodel = parse_multipart_apply(apply); + } catch (const std::exception &err) { + std::string msg = std::format( + "An error occurred when parsing the value of apply. Details : {}", + err.what()); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + // parse when + if (!part.contains("when")) { + mpp.criteria_variant = criteria_all_pass(); + dest->pairs.emplace_back(std::move(mpp)); + continue; + } + + try { + const njson &when = part.at("when"); + + mpp.criteria_variant = parse_multipart_when(when); + + } catch (const std::exception &err) { + std::string msg = std::format( + "\nFatal error : failed to parse \"when\" for a multipart blockstate " + "file. Details : {}\n", + err.what()); + return false; + } + + dest->pairs.emplace_back(std::move(mpp)); + /* + if (!part.is_object()) { + printf("\nFatal error : multipart must an array of objects.\n"); + return false; + } + + if (!part.contains("apply") || !part.contains("when")) { + printf("\nFatal error : element in multipart must contains \"apply\" + and " + "\"when\"\n"); + return false; + } + + if (!apply.contains("model") || !apply.at("model").is_string()) { + printf("\nFatal error : multipart should apply a model.\n"); + return false; + } + */ + } + + return true; +} + +struct face_json_temp { + std::string texture{""}; + std::array uv{0, 0, 16, 16}; + block_model::face_idx cullface_face; + bool have_cullface{false}; + bool is_hidden{true}; ///< note that by default, is_hidden is true. +}; + +struct element_json_temp { + std::array from; + std::array to; + std::array faces; +}; + +struct block_model_json_temp { + std::string parent{""}; + std::map textures; + std::vector elements; + bool is_inherited{false}; +}; + +std::optional string_to_face_idx( + std::string_view str) noexcept { + if (str == "up") { + return block_model::face_idx::face_up; + } + if (str == "down") { + return block_model::face_idx::face_down; + } + if (str == "bottom") { + return block_model::face_idx::face_down; + } + if (str == "north") { + return block_model::face_idx::face_north; + } + if (str == "south") { + return block_model::face_idx::face_south; + } + if (str == "east") { + return block_model::face_idx::face_east; + } + if (str == "west") { + return block_model::face_idx::face_west; + } + + return std::nullopt; +} + +const char *face_idx_to_string(block_model::face_idx f) noexcept { + switch (f) { + case block_model::face_idx::face_up: + return "up"; + case block_model::face_idx::face_down: + return "down"; + case block_model::face_idx::face_north: + return "north"; + case block_model::face_idx::face_south: + return "south"; + case block_model::face_idx::face_east: + return "east"; + case block_model::face_idx::face_west: + return "west"; + } + + return nullptr; +} + +bool parse_single_model_json(const char *const json_beg, + const char *const json_end, + block_model_json_temp *const dest) { + dest->textures.clear(); + dest->elements.clear(); + // disable exceptions, and ignore comments. + njson obj = njson::parse(json_beg, json_end, nullptr, false, true); + if (obj.is_null()) { + // this may be unsafe but just keep it currently. + std::string msg = "Failed to parse block model json : "; + msg.append(json_beg, json_end); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + if (obj.contains("parent") && obj.at("parent").is_string()) { + std::string p_str = obj.at("parent"); + if (p_str.starts_with("minecraft:")) { + dest->parent = p_str.substr(sizeof("minecraft:") / sizeof(char) - 1); + } else { + dest->parent = p_str; + } + } + + if (obj.contains("textures") && obj.at("textures").is_object()) { + const njson &textures = obj.at("textures"); + // dest->textures.reserve(textures.size()); + for (auto temp : textures.items()) { + if (!temp.value().is_string()) { + continue; + } + + auto it = dest->textures.emplace(temp.key(), temp.value()); + + if (it.first->second.starts_with("block/") || + it.first->second.starts_with("blocks/")) { + it.first->second = "minecraft:" + it.first->second; + } + } + } + // finished textures + + if (obj.contains("elements") && obj.at("elements").is_array()) { + const njson::array_t &elearr = obj.at("elements"); + + dest->elements.reserve(obj.size()); + for (const auto &e : elearr) { + if (!e.is_object()) { + return false; + } + + element_json_temp ele; + if (!e.contains("from") || !e.at("from").is_array()) { + ::VCL_report(VCL_report_type_t::error, + "\"from\" doesn't exist, or is not an array."); + return false; + } + // from + { + const njson::array_t &arr_from = e.at("from"); + if (arr_from.size() != 3 || !arr_from.front().is_number()) { + ::VCL_report(VCL_report_type_t::error, "size of \"from\" is not 3"); + return false; + } + + for (int idx = 0; idx < 3; idx++) { + if (!arr_from[idx].is_number()) { + ::VCL_report( + VCL_report_type_t::error, + "one or more element in array \"from\" is not number."); + return false; + } + ele.from[idx] = arr_from[idx]; + } + } + if (!e.contains("to") || !e.at("to").is_array()) { + ::VCL_report(VCL_report_type_t::error, + "\"to\" doesn't exist, or is not an array."); + return false; + } + // to + { + const njson::array_t &arr_to = e.at("to"); + if (arr_to.size() != 3) { + ::VCL_report(VCL_report_type_t::error, "size of \"to\" is not 3."); + return false; + } + + for (int idx = 0; idx < 3; idx++) { + if (!arr_to[idx].is_number()) { + ::VCL_report(VCL_report_type_t::error, + "one or more element in array \"to\" is not number."); + return false; + } + ele.to[idx] = arr_to[idx]; + } + } + + // faces + { + if (!e.contains("faces") || !e.at("faces").is_object()) { + ::VCL_report(VCL_report_type_t::error, + "\"faces\" doesn't exist, or is not an object."); + return false; + } + + const njson &faces = e.at("faces"); + for (auto temp : faces.items()) { + // if the face is not object, skip current face. + if (!temp.value().is_object()) continue; + face_json_temp f; + block_model::face_idx fidx; + { + auto fidx_opt = string_to_face_idx(temp.key()); + if (not fidx_opt) { + std::string msg = std::format( + "Error while parsing block model json : invalid key {} " + "doesn't refer to any face.", + temp.key()); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + fidx = fidx_opt.value(); + } + + const njson &curface = temp.value(); + + if (!curface.contains("texture") || + !curface.at("texture").is_string()) { + ::VCL_report( + VCL_report_type_t::error, + "Error while parsing block model json : face do not have " + "texture."); + return false; + } + + f.texture = curface.at("texture"); + if (f.texture.starts_with("block/")) { + f.texture = ("minecraft:") + f.texture; + } + // finished texture + + // cullface + { + std::string cullface_temp(""); + if (curface.contains("cullface") && + curface.at("cullface").is_string()) { + cullface_temp = curface.at("cullface"); + } + + if (!cullface_temp.empty()) { + auto cullface_fidx = string_to_face_idx(cullface_temp); + + if (not cullface_fidx) { + std::string msg = std::format("Invalid value for cullface : {}", + cullface_temp); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + f.cullface_face = cullface_fidx.value(); + f.have_cullface = true; + } + } + // finished cullface + + // uv + if (curface.contains("uv") && curface.at("uv").is_array()) { + const njson::array_t &uvarr = curface.at("uv"); + + if (uvarr.size() != 4) { + ::VCL_report(VCL_report_type_t::error, + "Invalid value for uv array : the size must be 4."); + return false; + } + + for (int idx = 0; idx < 4; idx++) { + if (!uvarr.at(idx).is_number()) { + ::VCL_report(VCL_report_type_t::error, + "Invalid value for uv array : the value must be " + "numbers."); + return false; + } + f.uv[idx] = uvarr[idx]; + } + } + // finished uv + + f.is_hidden = false; + // finished is_hidden + + // write in this face + ele.faces[int(fidx)] = f; + } + } + // finished all faces + + dest->elements.emplace_back(ele); + } + } + + return true; +} + +const char *dereference_texture_name( + std::map::iterator it, + std::map &text) noexcept { + if (it == text.end()) { + return nullptr; + } + + if (!it->second.starts_with('#')) { + return it->second.data(); + } + + // here it->second must be a # reference. + + auto next_it = text.find(it->second.data() + 1); + + // This line is added as a patch, to fix error when parsing 1.19.3 data packs. + // I'm not sure whether models that triggered this can be parsed correctly, it + // is only introduced to prevent endless recursion, so that errors can be + // reported + if (next_it == it) { + // found a self-reference value + return nullptr; + } + + const char *const ret = dereference_texture_name(next_it, text); + + if (ret != nullptr) { + // found a non-reference value + it->second = ret; + return ret; + } else { + // it->second is the the farest reference and no further link + return it->second.data(); + } +} + +void dereference_texture_name( + std::map &text) noexcept { + for (auto it = text.begin(); it != text.end(); ++it) { + if (!it->second.starts_with('#')) continue; + dereference_texture_name(it, text); + } +} + +void dereference_model(block_model_json_temp &model) { + // dereference_texture_name(model.textures); + + for (auto &ele : model.elements) { + for (auto &face : ele.faces) { + if (face.is_hidden) continue; + if (face.texture.starts_with('#')) { + auto it = model.textures.find(face.texture.data() + 1); + + if (it == model.textures.end()) { + continue; + } + face.texture = it->second; + } + } + // finished current face + } + // finished current element +} + +bool model_json_inherit_new(block_model_json_temp &child, + block_model_json_temp &parent, const bool) { + if (child.parent.empty()) { + ::VCL_report(VCL_report_type_t::error, "child has no parent."); + return false; + } + + parent.is_inherited = true; + + // child.textures.reserve(child.textures.size() + parent.textures.size()); + // merge textures + for (const auto &pt : parent.textures) { + if (!child.textures.contains(pt.first)) { + child.textures.emplace(pt.first, pt.second); + } + } + + dereference_texture_name(child.textures); + + // if child have a parent, and child doesn't define its own element, child + // inherit parent's elements. + if (child.elements.size() <= 0) { + child.elements = parent.elements; + } + + dereference_model(child); + + // parent + child.parent = parent.parent; + return true; +} + +bool inherit_recrusively(std::string_view childname, + block_model_json_temp &child, + std::unordered_map + &temp_models) noexcept { + if (child.parent.empty()) return true; + + // #warning This function is not finished yet. I hope to inherit from the + // root, which measn to find the root and inherit from root to leaf + + // find parent till the root + auto it = temp_models.find(child.parent); + + if (it == temp_models.end()) { + std::string msg = std::format( + "Failed to inherit. Undefined reference to model {}, " + "required by {}.", + child.parent.data(), childname.data()); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + if (!it->second.parent.empty()) { + // find root + const bool success = + inherit_recrusively(it->first, it->second, temp_models); + if (!success) { + return false; + } + } + /* + printf("\ninhering : parent : %s, child : %s,\n", child.parent.data(), + childname.data()); + */ + const bool success = model_json_inherit_new(child, it->second, false); + + // dereference_texture_name(child.textures); + + if (!success) { + std::string msg = std::format("Failed to inherit. Child : {}, parent : {}.", + childname.data(), child.parent.data()); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + return true; +} + +bool resource_pack::add_block_models( + const zipped_folder &resource_pack_root, + const bool on_conflict_replace_old) noexcept { + const std::unordered_map *files; + // find assets/minecraft/models/block + { + const zipped_folder *temp = resource_pack_root.subfolder("assets"); + if (temp == nullptr) return false; + temp = temp->subfolder("minecraft"); + if (temp == nullptr) return false; + temp = temp->subfolder("models"); + if (temp == nullptr) return false; + temp = temp->subfolder("block"); + if (temp == nullptr) return false; + + files = &temp->files; + } + + // the name of model is : block/ + std::unordered_map temp_models; + + temp_models.reserve(files->size()); + + std::array buffer; + + for (const auto &file : *files) { + if (!file.first.ends_with(".json")) continue; + buffer.fill('\0'); + std::strcpy(buffer.data(), "block/"); + { + const int end = file.first.find_last_of('.'); + char *const dest = buffer.data() + std::strlen(buffer.data()); + for (int idx = 0; idx < end; idx++) { + dest[idx] = file.first[idx]; + } + } + + block_model_json_temp bmjt; + + const bool ok = parse_single_model_json( + (const char *)file.second.data(), + (const char *)file.second.data() + file.second.file_size(), &bmjt); + + if (!ok) { + std::string msg = std::format( + "Failed to parse assets/minecraft/models/block/{}.", file.first); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + + temp_models.emplace(buffer.data(), bmjt); + } + // parsed all jsons + /* + printf("Loaded %i model jsons.\n", int(temp_models.size())); + + for (const auto &file : temp_models) { + printf("%s, ", file.first.data()); + } + printf("\n\n"); + + */ + + // inherit + for (auto &model : temp_models) { + const bool ok = inherit_recrusively(model.first, model.second, temp_models); + if (!ok) { + model.second.parent = "INVALID"; + std::string msg = std::format( + "Failed to inherit model {}. This model will be " + "skipped, but it may cause further errors.", + model.first); + ::VCL_report(VCL_report_type_t::warning, msg.c_str()); + // #warning following line should be commented. + // return false; + continue; + } + + dereference_texture_name(model.second.textures); + dereference_model(model.second); + } + // remove invalid + for (auto it = temp_models.begin(); it != temp_models.end();) { + if (it->second.parent == "INVALID") { + it = temp_models.erase(it); + continue; + } + + ++it; + } + + // convert temp models to block_models + this->block_models.reserve(this->block_models.size() + temp_models.size()); + for (auto &tmodel : temp_models) { + if (this->block_models.contains(tmodel.first) && !on_conflict_replace_old) { + continue; + } + + block_model::model md; + bool skip_this_model = false; + + md.elements.reserve(tmodel.second.elements.size()); + for (auto &tele : tmodel.second.elements) { + if (skip_this_model) break; + block_model::element ele; + + // ele._from = tele.from; + for (int idx = 0; idx < 3; idx++) { + ele._from[idx] = tele.from[idx]; + ele._to[idx] = tele.to[idx]; + } + + for (uint8_t faceidx = 0; faceidx < 6; faceidx++) { + if (skip_this_model) break; + auto &tface = tele.faces[faceidx]; + ele.faces[faceidx].is_hidden = tface.is_hidden; + if (tface.is_hidden) { + ele.faces[faceidx].texture = nullptr; + continue; + } + ele.faces[faceidx].uv_start[0] = tface.uv[0]; + ele.faces[faceidx].uv_start[1] = tface.uv[1]; + ele.faces[faceidx].uv_end[0] = tface.uv[2]; + ele.faces[faceidx].uv_end[1] = tface.uv[3]; + + // try to find the image in texture/block + auto imgptr = this->find_texture(tface.texture, false); + if (imgptr == nullptr) { // try to resolve the name of this texture + auto it = tmodel.second.textures.find(tface.texture); + if (it not_eq tmodel.second.textures.end()) { + imgptr = this->find_texture(it->second, false); + } + } + + if (imgptr == nullptr) { + if (tface.texture.starts_with('#') && tmodel.second.is_inherited) { + // This model is considered to be abstract + skip_this_model = true; + continue; + } + std::string msg = std::format( + "Undefined reference to texture \"{}\", required by " + "model {} but no such image.\nThe textures are : \n", + tface.texture, tmodel.first); + for (const auto &pair : tmodel.second.textures) { + msg.push_back('{'); + std::string temp = + std::format("{}, {}\n", pair.first.data(), pair.second.data()); + msg.append(temp); + msg.push_back('}'); + } + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + + // if managed to find, go on + } + ele.faces[faceidx].texture = imgptr; + } + // finished all faces + + md.elements.emplace_back(ele); + } + // finished current model + if (!skip_this_model) { + this->block_models.emplace(tmodel.first, std::move(md)); + } + } + + for (const auto &pair : this->block_models) { + for (const auto &ele : pair.second.elements) { + for (const auto &face : ele.faces) { + if (!face.is_hidden && face.texture == nullptr) { + std::string msg = std::format( + "Found an error while examining all block models : " + "face.texture==nullptr in model {}", + pair.first); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + } + } + } + + return true; +} + +bool resource_pack::add_block_states( + const zipped_folder &resourece_pack_root, + const bool on_conflict_replace_old) noexcept { + const std::unordered_map *files = nullptr; + { + const zipped_folder *temp = resourece_pack_root.subfolder("assets"); + if (temp == nullptr) { + return false; + } + temp = temp->subfolder("minecraft"); + if (temp == nullptr) { + return false; + } + temp = temp->subfolder("blockstates"); + if (temp == nullptr) { + return false; + } + files = &temp->files; + } + + this->block_states.reserve(this->block_states.size() + files->size()); + + for (const auto &file : *files) { + if (this->block_states.contains(file.first) && !on_conflict_replace_old) { + continue; + } + std::variant + bs; + bool is_dest_variant; + + const bool success = parse_block_state( + (const char *)file.second.data(), + (const char *)file.second.data() + file.second.file_size(), &bs, + &is_dest_variant); + + if (!success) { + std::string msg = std::format( + "Failed to parse block state json file " + "assets/minecraft/blockstates/{}. This will be " + "skipped but may cause further errors.\n", + file.first); + + ::VCL_report(VCL_report_type_t::warning, msg.c_str()); + continue; + } + + const int substrlen = file.first.find_last_of('.'); + this->block_states.emplace(file.first.substr(0, substrlen), std::move(bs)); + } + + return true; +} diff --git a/VisualCraftL/ParseResourcePack_png.cpp b/VisualCraftL/ParseResourcePack_png.cpp index 51d02e25..f8450507 100644 --- a/VisualCraftL/ParseResourcePack_png.cpp +++ b/VisualCraftL/ParseResourcePack_png.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -99,49 +99,48 @@ bool parse_png( if (bit_depth > 8) { png_set_strip_16(png); } - if (bit_depth < 8) - png_set_expand(png); + if (bit_depth < 8) png_set_expand(png); switch (color_type) { - case PNG_COLOR_TYPE_GRAY: // fixed - png_set_gray_to_rgb(png); - add_alpha = true; - // cout << "PNG_COLOR_TYPE_GRAY"; - break; - case PNG_COLOR_TYPE_PALETTE: // fixed - - png_set_palette_to_rgb(png); - png_set_bgr(png); - { - int num_trans = 0; - png_get_tRNS(png, info, NULL, &num_trans, NULL); - if (num_trans <= 0) { - add_alpha = true; + case PNG_COLOR_TYPE_GRAY: // fixed + png_set_gray_to_rgb(png); + add_alpha = true; + // cout << "PNG_COLOR_TYPE_GRAY"; + break; + case PNG_COLOR_TYPE_PALETTE: // fixed + + png_set_palette_to_rgb(png); + png_set_bgr(png); + { + int num_trans = 0; + png_get_tRNS(png, info, NULL, &num_trans, NULL); + if (num_trans <= 0) { + add_alpha = true; + } + // cout << "num_trans = " << num_trans << endl; } - // cout << "num_trans = " << num_trans << endl; - } - // cout << "PNG_COLOR_TYPE_PALETTE"; - break; - case PNG_COLOR_TYPE_RGB: // fixed - png_set_bgr(png); - add_alpha = true; - // cout << "PNG_COLOR_TYPE_RGB"; - break; - case PNG_COLOR_TYPE_RGB_ALPHA: // fixed - png_set_bgr(png); - // cout << "PNG_COLOR_TYPE_RGB_ALPHA"; - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: // fixed - png_set_gray_to_rgb(png); - // png_set_swap_alpha(png); - // cout << "PNG_COLOR_TYPE_GRAY_ALPHA"; - break; - default: - png_destroy_read_struct(&png, &info, &info_end); - std::string msg = fmt::format("Unknown color type {}", color_type); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; + // cout << "PNG_COLOR_TYPE_PALETTE"; + break; + case PNG_COLOR_TYPE_RGB: // fixed + png_set_bgr(png); + add_alpha = true; + // cout << "PNG_COLOR_TYPE_RGB"; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: // fixed + png_set_bgr(png); + // cout << "PNG_COLOR_TYPE_RGB_ALPHA"; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: // fixed + png_set_gray_to_rgb(png); + // png_set_swap_alpha(png); + // cout << "PNG_COLOR_TYPE_GRAY_ALPHA"; + break; + default: + png_destroy_read_struct(&png, &info, &info_end); + std::string msg = std::format("Unknown color type {}", color_type); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; } // cout << ")\n"; // #warning here @@ -157,7 +156,7 @@ bool parse_png( png_read_image(png, row_ptrs.data()); - if (add_alpha) { // add alpha manually + if (add_alpha) { // add alpha manually for (int r = 0; r < int(height); r++) { uint8_t *const data = reinterpret_cast(&(*img)(r, 0)); for (int pixel_idx = img->cols() - 1; pixel_idx > 0; pixel_idx--) { @@ -185,8 +184,7 @@ resize_image_nearest(const Eigen::Array result(0, 0); - if (rows <= 0 || cols <= 0 || src.size() <= 0) - return result; + if (rows <= 0 || cols <= 0 || src.size() <= 0) return result; result.resize(rows, cols); result.setZero(); @@ -211,8 +209,7 @@ resize_image_nearest( int rows, int cols) noexcept { Eigen::Array result(0, 0); - if (rows <= 0 || cols <= 0 || src_block.size() <= 0) - return result; + if (rows <= 0 || cols <= 0 || src_block.size() <= 0) return result; result.resize(rows, cols); result.setZero(); @@ -253,7 +250,6 @@ bool resource_pack::add_colormaps(const zipped_folder &rpr) noexcept { bool resource_pack::add_textures(const zipped_folder &rpr, const bool conflict_conver_old) noexcept { - const zipped_folder *const assets = rpr.subfolder("assets"); if (assets == nullptr) { ::VCL_report(VCL_report_type_t::error, @@ -312,8 +308,7 @@ bool resource_pack::add_textures_direct( std::array buffer; for (const auto &file : pngs) { - if (!file.first.ends_with(".png")) - continue; + if (!file.first.ends_with(".png")) continue; const bool is_dynamic = pngs.contains(file.first + ".mcmeta"); @@ -350,17 +345,17 @@ bool resource_pack::add_textures_direct( const bool success = parse_png(file.second.data(), file.second.file_size(), &img); if (!success || img.size() <= 0) { - std::string msg = - fmt::format("Failed to parse png file {} in {}. Png parsing will " - "continue but this warning may cause further errors.", - file.first, buffer.data()); + std::string msg = std::format( + "Failed to parse png file {} in {}. Png parsing will " + "continue but this warning may cause further errors.", + file.first, buffer.data()); ::VCL_report(VCL_report_type_t::warning, msg.c_str()); continue; } if (is_dynamic) { if (img.rows() % img.cols() != 0) { - std::string msg = fmt::format( + std::string msg = std::format( "Failed to process dynamic png file {} in {}. Image " "has {} rows and {} cols, which is not of integer ratio. Png " "parsing will continue but this warning may cause further " @@ -387,7 +382,7 @@ process_dynamic_texture(const Eigen::Arrayfiles.contains(filename.data())) { - std::string msg = - fmt::format("Failed to find \"assets/minecraft/textures/colormap/{}\". " - "File doesn\'t exist.", - filename); + std::string msg = std::format( + "Failed to find \"assets/minecraft/textures/colormap/{}\". " + "File doesn\'t exist.", + filename); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -446,7 +441,7 @@ bool resource_pack::add_colormap(const zipped_folder &resourece_pack_root, const bool success = parse_png(png.data(), png.file_size(), &img); if (!success) { - std::string msg = fmt::format( + std::string msg = std::format( "Failed to parse \"assets/minecraft/textures/colormap/{}\". " "Not a valid png file.", filename); @@ -455,10 +450,10 @@ bool resource_pack::add_colormap(const zipped_folder &resourece_pack_root, } if (img.rows() != 256 || img.cols() != 256) { - std::string msg = - fmt::format("Failed to parse assets/minecraft/textures/colormap/{}. " - "The rows({}) and cols({}) mismatch with (256,256).", - filename, img.rows(), img.cols()); + std::string msg = std::format( + "Failed to parse assets/minecraft/textures/colormap/{}. " + "The rows({}) and cols({}) mismatch with (256,256).", + filename, img.rows(), img.cols()); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -488,15 +483,10 @@ resource_pack::find_texture(std::string_view path, return nullptr; } -std::array -compute_mean_color(const block_model::EImgRowMajor_t &img, - bool *const ok) noexcept { +std::optional> compute_mean_color( + const block_model::EImgRowMajor_t &img) noexcept { if (img.size() <= 0) { - if (ok != nullptr) { - *ok = false; - } - - return {0, 0, 0}; + return std::nullopt; } std::array val{0, 0, 0}; @@ -515,11 +505,6 @@ compute_mean_color(const block_model::EImgRowMajor_t &img, val[i] /= img.size(); ret[i] = val[i] & 0xFF; } - - if (ok != nullptr) { - *ok = true; - } - return ret; } @@ -544,19 +529,16 @@ bool compose_image_background_half_transparent( return true; } -std::array -compose_image_and_mean(const block_model::EImgRowMajor_t &front, - const block_model::EImgRowMajor_t &back, - bool *const ok) noexcept { +std::array compose_image_and_mean( + const block_model::EImgRowMajor_t &front, + const block_model::EImgRowMajor_t &back, bool *const ok) noexcept { if (front.rows() != back.rows() || front.cols() != back.cols()) { - if (ok != nullptr) - *ok = false; + if (ok != nullptr) *ok = false; return {}; } if (front.size() <= 0) { - if (ok != nullptr) - *ok = false; + if (ok != nullptr) *ok = false; return {}; } diff --git a/VisualCraftL/ResourcePack.cpp b/VisualCraftL/ResourcePack.cpp index ab67bae1..30c60c2a 100644 --- a/VisualCraftL/ResourcePack.cpp +++ b/VisualCraftL/ResourcePack.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -26,7 +26,6 @@ This file is part of SlopeCraft. #include bool VCL_resource_pack::copy(const VCL_resource_pack &src) noexcept { - this->textures_original = src.textures_original; this->textures_override = src.textures_override; this->block_states = src.block_states; @@ -67,7 +66,6 @@ bool VCL_resource_pack::filter_model_textures( const std::unordered_map &filter, bool is_missing_error) noexcept { - for (auto &pair : this->block_models) { for (auto &ele : pair.second.elements) { for (auto &face : ele.faces) { @@ -76,10 +74,10 @@ bool VCL_resource_pack::filter_model_textures( face.texture = it->second; } else { if (is_missing_error) { - std::string msg = - fmt::format("Failed to filter image pointer {}. Missing in the " - "filter and is_missing_error is set to true.", - (const void *)face.texture); + std::string msg = std::format( + "Failed to filter image pointer {}. Missing in the " + "filter and is_missing_error is set to true.", + (const void *)face.texture); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -140,8 +138,7 @@ bool process_block_state_list_in_id( cur_statement_beg = cur + 1; } - if (cur >= end) - break; + if (cur >= end) break; } return true; @@ -151,12 +148,9 @@ bool resource_json::process_full_id(std::string_view full_id, std::string *namespace_name, std::string *pure_id, state_list *states) noexcept { - if (namespace_name != nullptr) - *namespace_name = ""; - if (pure_id != nullptr) - pure_id->clear(); - if (states != nullptr) - states->clear(); + if (namespace_name != nullptr) *namespace_name = ""; + if (pure_id != nullptr) pure_id->clear(); + if (states != nullptr) states->clear(); if (full_id.find_first_of(':') != full_id.find_last_of(':')) { return false; @@ -177,11 +171,9 @@ bool resource_json::process_full_id(std::string_view full_id, const bool have_left_bracket = (idx_left_bracket != full_id.npos); const bool have_right_bracket = (idx_right_bracket != full_id.npos); - if (have_left_bracket != have_right_bracket) - return false; + if (have_left_bracket != have_right_bracket) return false; - if (idx_colon != full_id.npos && idx_colon > idx_left_bracket) - return false; + if (idx_colon != full_id.npos && idx_colon > idx_left_bracket) return false; if (have_left_bracket && (idx_left_bracket >= idx_right_bracket)) { return false; @@ -219,13 +211,12 @@ bool resource_json::process_full_id(std::string_view full_id, std::variant VCL_resource_pack::find_model(const std::string &block_state_str, buffer_t &buffer) const noexcept { - if (!resource_json::process_full_id(block_state_str, nullptr, &buffer.pure_id, &buffer.state_list)) { - std::string msg = - fmt::format("invalid full block id that can not be parsed to a list " - "of block states : \"{}\"", - block_state_str.c_str()); + std::string msg = std::format( + "invalid full block id that can not be parsed to a list " + "of block states : \"{}\"", + block_state_str.c_str()); VCL_report(VCL_report_type_t::error, msg.c_str()); return model_with_rotation{nullptr}; @@ -236,7 +227,7 @@ VCL_resource_pack::find_model(const std::string &block_state_str, std::string msg = "statelist = ["; for (const auto &i : buffer.state_list) { - std::string temp = fmt::format("{}={},", i.key.c_str(), i.value.c_str()); + std::string temp = std::format("{}={},", i.key.c_str(), i.value.c_str()); msg.append(temp); } msg.append("]\n"); @@ -254,10 +245,10 @@ VCL_resource_pack::find_model(const std::string &block_state_str, if (buffer.pure_id == "air") { return block_model::model{}; } - std::string msg = - fmt::format("Undefined reference to block state whose pure block id " - "is : \"{}\" and full block id is : \"{}\"\n", - buffer.pure_id, block_state_str); + std::string msg = std::format( + "Undefined reference to block state whose pure block id " + "is : \"{}\" and full block id is : \"{}\"\n", + buffer.pure_id, block_state_str); VCL_report(VCL_report_type_t::error, msg.c_str()); return model_with_rotation{nullptr}; } @@ -270,10 +261,10 @@ VCL_resource_pack::find_model(const std::string &block_state_str, // face_exposed = block_model::invrotate(face_exposed, model.x, model.y); if (model.model_name == nullptr) { - std::string msg = - fmt::format("No block model for full id : \"{}\", this is usually " - "because block states mismatch.", - block_state_str); + std::string msg = std::format( + "No block model for full id : \"{}\", this is usually " + "because block states mismatch.", + block_state_str); VCL_report(VCL_report_type_t::error, msg.c_str()); return model_with_rotation{nullptr}; } @@ -293,7 +284,7 @@ VCL_resource_pack::find_model(const std::string &block_state_str, } if (it_model == this->block_models.end()) { - std::string msg = fmt::format( + std::string msg = std::format( "Failed to find block model for full id : \"{}\". Detail : " "undefined reference to model named \"{}\".\n", block_state_str.c_str(), model.model_name); @@ -309,23 +300,22 @@ VCL_resource_pack::find_model(const std::string &block_state_str, const auto models = multipart.block_model_names(buffer.state_list); for (const auto &md : models) { - if constexpr (false) { std::string msg = - fmt::format("x_rot = {}, y_rot = {}", int(md.x), int(md.y)); + std::format("x_rot = {}, y_rot = {}", int(md.x), int(md.y)); VCL_report(VCL_report_type_t::information, msg.c_str()); } if (md.model_name == nullptr) { std::string msg = - fmt::format("File = {}, line = {}\n", __FILE__, __LINE__); + std::format("File = {}, line = {}\n", __FILE__, __LINE__); VCL_report(VCL_report_type_t::error, msg.c_str()); return model_with_rotation{nullptr}; } } if (models.size() <= 0) { - std::string msg = fmt::format("File = {}, line = {}\n", __FILE__, __LINE__); + std::string msg = std::format("File = {}, line = {}\n", __FILE__, __LINE__); VCL_report(VCL_report_type_t::error, msg.c_str()); return model_with_rotation{nullptr}; } @@ -348,7 +338,7 @@ VCL_resource_pack::find_model(const std::string &block_state_str, it_model = this->block_models.find("block/" + buffer.pure_id); } if (it_model == this->block_models.end()) { - std::string msg = fmt::format( + std::string msg = std::format( "Failed to find block model for full id : \"{}\". Detail : " "undefined reference to model named \"{}\".\n", block_state_str.c_str(), models[mdidx].model_name); @@ -357,7 +347,7 @@ VCL_resource_pack::find_model(const std::string &block_state_str, } if constexpr (false) { std::string msg = - fmt::format("merging back model : {}", models[mdidx].model_name); + std::format("merging back model : {}", models[mdidx].model_name); VCL_report(VCL_report_type_t::information, msg.c_str()); } @@ -371,17 +361,16 @@ VCL_resource_pack::find_model(const std::string &block_state_str, bool VCL_resource_pack::compute_projection( const std::string &block_state_str, VCL_face_t face_exposed, block_model::EImgRowMajor_t *const img, buffer_t &buffer) const noexcept { - std::variant ret = this->find_model(block_state_str, buffer); if (ret.index() == 0) { auto model = std::get<0>(ret); if (model.model_ptr == nullptr) { - std::string msg = - fmt::format("failed to find a block model for full id :\"{}\", " - "function find_model returned nullptr\n", - block_state_str.c_str()); + std::string msg = std::format( + "failed to find a block model for full id :\"{}\", " + "function find_model returned nullptr\n", + block_state_str.c_str()); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -406,7 +395,7 @@ bool VCL_resource_pack::override_texture( auto it = this->textures_original.find(path_in_original.data()); if (it == this->textures_original.end()) { - std::string msg = fmt::format( + std::string msg = std::format( "Failed to override texture \"{0}\" with given color {1:#x}, the " "original texture \"{0}\" doesn't exist.", path_in_original, standard_color); @@ -417,12 +406,12 @@ bool VCL_resource_pack::override_texture( const auto &img_original = it->second; if (img_original.rows() != 16 || img_original.cols() != 16) { - std::string msg = - fmt::format("Failed to override texture \"{0}\" with given " - "color {1:#x}, the image size of " - "original texture \"{0}\" is {2} * {3} instead of 16 * 16", - path_in_original, standard_color, img_original.rows(), - img_original.cols()); + std::string msg = std::format( + "Failed to override texture \"{0}\" with given " + "color {1:#x}, the image size of " + "original texture \"{0}\" is {2} * {3} instead of 16 * 16", + path_in_original, standard_color, img_original.rows(), + img_original.cols()); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -475,8 +464,8 @@ bool VCL_resource_pack::override_texture( return true; } -std::array -VCL_resource_pack::locate_color_rc(VCL_biome_info info) const noexcept { +std::array VCL_resource_pack::locate_color_rc( + VCL_biome_info info) const noexcept { assert(info.downfall == info.downfall); assert(info.temperature == info.temperature); const float t_adj = std::clamp(info.temperature, 0.0f, 1.0f); @@ -496,7 +485,6 @@ VCL_resource_pack::locate_color_rc(VCL_biome_info info) const noexcept { uint32_t VCL_resource_pack::standard_color(VCL_biome_info info, bool is_foliage) const noexcept { - const auto rc = this->locate_color_rc(info); const int r = rc[0]; @@ -557,50 +545,11 @@ uint32_t VCL_resource_pack::standard_color( if (biome == VCL_biome_t::cherry_grove) { return 0xFF'90'81'4d; } - - return default_result; -} - -#include "textures_need_to_override.h" - -bool VCL_resource_pack::override_textures( - VCL_biome_t biome, bool replace_transparent_with_black) noexcept { - const std::string_view *grass_ids = nullptr; - size_t grass_num = 0; - const std::string_view *foliage_ids = nullptr; - size_t foliage_num = 0; - if (this->is_MC12) { - grass_ids = VCL_12_grass_texture_names; - grass_num = VCL_12_grass_texture_name_size; - foliage_ids = VCL_12_foliage_texture_names; - foliage_num = VCL_12_foliage_texture_name_size; - } else { - grass_ids = VCL_latest_grass_texture_names; - grass_num = VCL_latest_grass_texture_name_size; - foliage_ids = VCL_latest_foliage_texture_names; - foliage_num = VCL_latest_foliage_texture_name_size; + if (biome == VCL_biome_t::pale_garden) { + return 0xFF'87'8d'76; } - for (size_t gid = 0; gid < grass_num; gid++) { - const bool ok = this->override_texture( - grass_ids[gid], this->standard_color(biome, grass_ids[gid]), - replace_transparent_with_black); - if (!ok) { - return false; - } - } - - for (size_t fid = 0; fid < foliage_num; fid++) { - - const bool ok = this->override_texture( - foliage_ids[fid], this->standard_color(biome, foliage_ids[fid]), - replace_transparent_with_black); - if (!ok) { - return false; - } - } - - return this->update_block_model_textures(); + return default_result; } bool VCL_resource_pack::update_block_model_textures() noexcept { @@ -612,12 +561,12 @@ bool VCL_resource_pack::update_block_model_textures() noexcept { for (auto &pair : this->textures_override) { auto it = this->textures_original.find(pair.first); if (it == this->textures_original.end()) { - std::string msg = - fmt::format("Internal logical error when invoking function " - "\"VCL_resource_pack::update_block_model_textures\" : " - "texture \"{}\" is overrided, but failed to find the " - "image with same id in this->texture_original.", - pair.first); + std::string msg = std::format( + "Internal logical error when invoking function " + "\"VCL_resource_pack::update_block_model_textures\" : " + "texture \"{}\" is overrided, but failed to find the " + "image with same id in this->texture_original.", + pair.first); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -655,17 +604,17 @@ bool VCL_resource_pack::override_required_textures( } if (is_grass == is_foliage) { - - std::string msg = fmt::format("Failed to override texture for block {} " - "because it is both grass and foliage.", - blk.full_id_ptr()->c_str()); + std::string msg = std::format( + "Failed to override texture for block {} " + "because it is both grass and foliage.", + blk.full_id_ptr()->c_str()); return false; } auto model = this->find_model(*blk.full_id_ptr(), buffer); if (model.index() == 0 && std::get<0>(model).model_ptr == nullptr) { - std::string msg = fmt::format( + std::string msg = std::format( "Failed to override texture for block {} because model is not found.", blk.full_id_ptr()->c_str()); VCL_report(VCL_report_type_t::error, msg.c_str()); @@ -682,7 +631,6 @@ bool VCL_resource_pack::override_required_textures( for (const auto &element : md->elements) { for (size_t idx = 0; idx < 6; idx++) { - const auto &face = element.faces[idx]; if (is_grass && (idx != (size_t)VCL_face_t::face_up)) { @@ -702,7 +650,6 @@ bool VCL_resource_pack::override_required_textures( } for (auto &pair : textures_used) { - for (const auto &j : this->textures_original) { if (&j.second == pair.first) { pair.second.name = j.first.c_str(); @@ -710,16 +657,16 @@ bool VCL_resource_pack::override_required_textures( } if (pair.second.name == nullptr) { - std::string msg = - fmt::format("Failed to override texture at address {}, because this " - "image cannot be found in this->textures_original.", - (const void *)(pair.first)); + std::string msg = std::format( + "Failed to override texture at address {}, because this " + "image cannot be found in this->textures_original.", + (const void *)(pair.first)); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } if (pair.second.is_foliage && pair.second.is_grass) { - std::string msg = fmt::format( + std::string msg = std::format( "Texture \"{}\" will is used both as grass and as foliage.", pair.second.name); VCL_report(VCL_report_type_t::warning, msg.c_str()); @@ -733,7 +680,7 @@ bool VCL_resource_pack::override_required_textures( replace_transparent_with_black); if (!success) { std::string msg = - fmt::format("Failed to override texture named {}", pair.second.name); + std::format("Failed to override texture named {}", pair.second.name); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } diff --git a/VisualCraftL/Resource_tree.cpp b/VisualCraftL/Resource_tree.cpp index 78b2a682..c758e197 100644 --- a/VisualCraftL/Resource_tree.cpp +++ b/VisualCraftL/Resource_tree.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,9 +22,10 @@ This file is part of SlopeCraft. #include "Resource_tree.h" -#include - +#include #include +#include +#include #include "VCL_internal.h" @@ -49,48 +50,11 @@ auto split_by_slash(std::string_view str) noexcept { return result; } -zipped_folder zipped_folder::from_zip(std::string_view zipname, - bool *const ok) noexcept { +std::optional zipped_folder::from_zip(std::string_view zipname, + zip_t *zip) noexcept { zipped_folder result; - if (true) { - std::filesystem::path path = (const char8_t *)(zipname).data(); - if (zipname.empty()) { - if (ok) - *ok = false; - std::string msg = - fmt::format("The filename \"{}\" of zip is empty.", zipname); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return result; - } - - if (!std::filesystem::is_regular_file(path)) { - if (ok) - *ok = false; - std::string msg = fmt::format( - "The filename \"{}\" does not refer to a regular file.", zipname); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return result; - } - - if (path.extension() != ".zip") { - if (ok) - *ok = false; - std::string msg = fmt::format( - "The filename \"{}\" extension name is not .zip", zipname); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return result; - } - } - int errorcode; - zip_t *const zip = zip_open(zipname.data(), ZIP_RDONLY, &errorcode); - - if (zip == NULL) { - if (ok) - *ok = false; - std::string msg = fmt::format( - "Failed to open zip file : {}, error code = {}", zipname, errorcode); - ::VCL_report(VCL_report_type_t::error, msg.c_str()); - return result; + if (zip == nullptr) { + return std::nullopt; } const int64_t entry_num = zip_get_num_entries(zip, ZIP_FL_UNCHANGED); @@ -124,9 +88,7 @@ zipped_folder zipped_folder::from_zip(std::string_view zipname, destfile->__data.resize(stat.size); zip_file_t *const zfile = zip_fopen_index(zip, entry_idx, ZIP_FL_UNCHANGED); if (zfile == NULL) { - if (ok) - *ok = false; - std::string msg = fmt::format( + std::string msg = std::format( "Failed to open file in zip. index : {}, file name : {}\n", entry_idx, ::zip_get_name(zip, entry_idx, ZIP_FL_ENC_GUESS)); ::VCL_report(VCL_report_type_t::error, msg.c_str()); @@ -136,12 +98,77 @@ zipped_folder zipped_folder::from_zip(std::string_view zipname, zip_fread(zfile, destfile->__data.data(), stat.size); } - if (ok) - *ok = true; - return result; } +std::optional zipped_folder::from_zip( + std::string_view zipname, + const std::span zip_content) noexcept { + zip_error_t err; + zip_source_t *source = zip_source_buffer_create( + zip_content.data(), zip_content.size_bytes(), 0, &err); + if (source == nullptr) { + ::VCL_report(VCL_report_type_t::error, + std::format("{} may be a broken zip: {}", zipname, + zip_error_strerror(&err)) + .c_str()); + return std::nullopt; + } + + zip_t *archive = zip_open_from_source(source, ZIP_RDONLY, &err); + if (archive == nullptr) { + ::VCL_report(VCL_report_type_t::error, + std::format("{} may be a broken zip: {}", zipname, + zip_error_strerror(&err)) + .c_str()); + zip_source_free(source); + return std::nullopt; + } + auto content = from_zip(zipname, archive); + zip_close(archive); + return std::move(content); +} + +std::optional zipped_folder::from_zip( + std::string_view zipname) noexcept { + if (true) { + std::filesystem::path path = (const char8_t *)(zipname).data(); + if (zipname.empty()) { + std::string msg = + std::format("The filename \"{}\" of zip is empty.", zipname); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return std::nullopt; + } + + if (!std::filesystem::is_regular_file(path)) { + std::string msg = std::format( + "The filename \"{}\" does not refer to a regular file.", zipname); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return std::nullopt; + } + + if (path.extension() != ".zip") { + std::string msg = std::format( + "The filename \"{}\" extension name is not .zip", zipname); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return std::nullopt; + } + } + int errorcode; + zip_t *const zip = zip_open(zipname.data(), ZIP_RDONLY, &errorcode); + + if (zip == NULL) { + std::string msg = std::format( + "Failed to open zip file : {}, error code = {}", zipname, errorcode); + ::VCL_report(VCL_report_type_t::error, msg.c_str()); + return std::nullopt; + } + + auto content = from_zip(zipname, zip); + zip_close(zip); + return std::move(content); +} + void zipped_folder::merge_from_base(const zipped_folder &source_base) noexcept { for (const auto &it : source_base.files) { auto find = this->files.find(it.first); diff --git a/VisualCraftL/Resource_tree.h b/VisualCraftL/Resource_tree.h index de15f294..2cf8952e 100644 --- a/VisualCraftL/Resource_tree.h +++ b/VisualCraftL/Resource_tree.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -27,17 +27,20 @@ This file is part of SlopeCraft. #include #include #include - -#include +#include +#include +#include +// forward declaration for zip and zip_t defined in zip.h +struct zip; class zipped_file; class zipped_folder; class zipped_file { -private: + private: std::vector __data; -public: + public: friend class zipped_folder; inline int64_t file_size() const noexcept { return __data.size(); } @@ -49,7 +52,7 @@ class zipped_file { }; class zipped_folder { -public: + public: std::unordered_map subfolders; std::unordered_map files; @@ -70,8 +73,8 @@ class zipped_folder { } } - inline const zipped_folder * - subfolder(std::string_view fdname) const noexcept { + inline const zipped_folder *subfolder( + std::string_view fdname) const noexcept { auto it = subfolders.find(std::string(fdname)); if (it == subfolders.end()) { return nullptr; @@ -86,8 +89,15 @@ class zipped_folder { void merge_from_base(zipped_folder &&source_base) noexcept; - static zipped_folder from_zip(std::string_view zipname, - bool *const ok = nullptr) noexcept; + static std::optional from_zip(std::string_view zipname, + zip *archive) noexcept; + + static std::optional from_zip( + std::string_view zipname) noexcept; + + static std::optional from_zip( + std::string_view zipname, + const std::span zip_content) noexcept; }; -#endif // SLOPECRAFT_VISUALCRAFTL_RESOURCE_TREE_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFTL_RESOURCE_TREE_H \ No newline at end of file diff --git a/VisualCraftL/TokiVC.cpp b/VisualCraftL/TokiVC.cpp index df901a54..7dc080ee 100644 --- a/VisualCraftL/TokiVC.cpp +++ b/VisualCraftL/TokiVC.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,10 +23,8 @@ This file is part of SlopeCraft. #include "TokiVC.h" #include "VCL_internal.h" -#include #include #include -#include #include #include @@ -35,31 +33,6 @@ libImageCvt::template ImageCvter::basic_colorset_t libImageCvt::template ImageCvter::allowed_colorset_t TokiVC::colorset_allowed; -// bind static members for template classes -template <> -const libImageCvt::template ImageCvter::basic_colorset_t - &libImageCvt::template ImageCvter::basic_colorset = - TokiVC::colorset_basic; - -template <> -const libImageCvt::template ImageCvter::allowed_colorset_t - &libImageCvt::template ImageCvter::allowed_colorset = - TokiVC::colorset_allowed; - -template <> -const libImageCvt::template ImageCvter::basic_colorset_t - *const newTokiColor< - false, libImageCvt::template ImageCvter::basic_colorset_t, - libImageCvt::template ImageCvter::allowed_colorset_t>::Basic = - &TokiVC::colorset_basic; - -template <> -const libImageCvt::template ImageCvter::allowed_colorset_t - *const newTokiColor< - false, libImageCvt::template ImageCvter::basic_colorset_t, - libImageCvt::template ImageCvter::allowed_colorset_t>::Allowed = - &TokiVC::colorset_allowed; - // global variables that VCL uses VCL_resource_pack TokiVC::pack; VCL_block_state_list TokiVC::bsl; @@ -80,9 +53,9 @@ bool is_basic_color_set_ready = false; bool is_allowed_color_set_ready = false; std::set TokiVC_register; -} // namespace TokiVC_internal +} // namespace TokiVC_internal -TokiVC::TokiVC() { +TokiVC::TokiVC() : img_cvter{colorset_basic, colorset_allowed} { TokiVC_internal::global_lock.lock(); if (TokiVC_internal::is_basic_color_set_ready) { this->_step = VCL_Kernel_step::VCL_wait_for_image; @@ -128,7 +101,7 @@ bool add_projection_image_for_bsl(const std::vector &bs_list, resource_pack::buffer_t &buff) noexcept { for (VCL_block *blkp : bs_list) { if (blkp->full_id_ptr() == nullptr) { - std::string msg = fmt::format( + std::string msg = std::format( "\nError : a VCL_block do not have full_id. The block names are : " "{}, {}\n", blkp->name_ZH, blkp->name_EN); @@ -136,18 +109,18 @@ bool add_projection_image_for_bsl(const std::vector &bs_list, return false; } - if constexpr (false) { - std::string msg = - fmt::format("Computing projection image for full id \"{}\"\n", - blkp->full_id_ptr()->c_str()); - VCL_report(VCL_report_type_t::information, msg.c_str()); - } + // { + // std::string msg = + // std::format("Computing projection image for full id \"{}\"\n", + // blkp->full_id_ptr()->c_str()); + // VCL_report(VCL_report_type_t::information, msg.c_str()); + // } block_model::EImgRowMajor_t *img = &blkp->project_image_on_exposed_face; if (!TokiVC::pack.compute_projection(*blkp->full_id_ptr(), TokiVC::exposed_face, img, buff)) { - std::string msg = fmt::format("failed to compute projection for {}.\n", + std::string msg = std::format("failed to compute projection for {}.\n", blkp->full_id_ptr()->c_str()); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; @@ -163,13 +136,14 @@ bool add_color_non_transparent( const std::vector &bs_nontransparent, mutlihash_color_blocks &map_color_blocks) noexcept { for (VCL_block *blkp : bs_nontransparent) { - bool ok = true; - auto ret = compute_mean_color(blkp->project_image_on_exposed_face, &ok); - if (!ok) { + auto ret = compute_mean_color(blkp->project_image_on_exposed_face); + if (not ret) { return false; } + auto mean_color = ret.value(); - map_color_blocks.emplace(ARGB32(ret[0], ret[1], ret[2]), blkp); + map_color_blocks.emplace( + ARGB32(mean_color[0], mean_color[1], mean_color[2]), blkp); // temp_rgb_rowmajor.emplace_back(ret); // LUT_bcitb.emplace_back(blkp); @@ -218,7 +192,7 @@ bool add_color_trans_to_trans_recurs( mutlihash_color_blocks &map_color_blocks) noexcept { if (allowed_depth <= 0) { std::string msg = - fmt::format("Invalid value for allowed_depth : {}\n", allowed_depth); + std::format("Invalid value for allowed_depth : {}\n", allowed_depth); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -235,19 +209,18 @@ bool add_color_trans_to_trans_recurs( min_alpha = std::min(min_alpha, getA(front(i))); } - // if multiple transparent block composed a non transparent image, then the + // if multiple transparent block composed a non-transparent image, then the // recursion terminate. if (min_alpha >= 255) { - bool ok = true; - std::array mean = compute_mean_color(front, &ok); + auto mean_opt = compute_mean_color(front); - if (!ok) { + if (not mean_opt) { VCL_report(VCL_report_type_t::error, "Function add_color_trans_to_trans_recurs failed to " "compute mean color.\n"); return false; } - + auto &mean = mean_opt.value(); map_color_blocks.emplace(ARGB32(mean[0], mean[1], mean[2]), accumulate_blocks); // LUT_bcitb.emplace_back(accumulate_blocks); @@ -342,7 +315,6 @@ bool compare_blocks_variant( const std::variant> &a, const std::variant> &b) noexcept { - if (blocks_count(a) != blocks_count(b)) { return blocks_count(a) < blocks_count(b); } @@ -364,7 +336,6 @@ void convert_blocks_and_colors_from_hash_vector( std::vector> &colors_temp, std::vector>> &LUT_bcitb) noexcept { - std::vector selected_variants; selected_variants.reserve(src.size()); @@ -411,15 +382,15 @@ void convert_blocks_and_colors_from_hash_vector( bool TokiVC::set_resource_no_lock() noexcept { switch (TokiVC::version) { - case SCL_gameVersion::ANCIENT: - case SCL_gameVersion::FUTURE: { - std::string msg = - fmt::format("Invalid MC version : {}\n", int(TokiVC::version)); - VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - default: - break; + case SCL_gameVersion::ANCIENT: + case SCL_gameVersion::FUTURE: { + std::string msg = + std::format("Invalid MC version : {}\n", int(TokiVC::version)); + VCL_report(VCL_report_type_t::error, msg.c_str()); + return false; + } + default: + break; } TokiVC::pack.set_is_MC12(TokiVC::version == SCL_gameVersion::MC12); @@ -480,11 +451,11 @@ bool TokiVC::set_resource_no_lock() noexcept { return false; } - if constexpr (false) { - std::string msg = fmt::format("Size of map_color_blocks = {}\n", - map_color_blocks.size()); - VCL_report(VCL_report_type_t::information, msg.c_str()); - } + // { + // std::string msg = std::format("Size of map_color_blocks = {}\n", + // map_color_blocks.size()); + // VCL_report(VCL_report_type_t::information, msg.c_str()); + // } for (int layers = 2; layers <= max_block_layers; layers++) { if (!add_color_trans_to_trans_start_recurse( @@ -495,11 +466,11 @@ bool TokiVC::set_resource_no_lock() noexcept { } } - if constexpr (false) { - std::string msg = fmt::format("Size of map_color_blocks = {}\n", - map_color_blocks.size()); - VCL_report(VCL_report_type_t::information, msg.c_str()); - } + // { + // std::string msg = std::format("Size of map_color_blocks = {}\n", + // map_color_blocks.size()); + // VCL_report(VCL_report_type_t::information, msg.c_str()); + // } std::vector> colors_temp; TokiVC::LUT_basic_color_idx_to_blocks.clear(); @@ -508,7 +479,7 @@ bool TokiVC::set_resource_no_lock() noexcept { map_color_blocks, colors_temp, TokiVC::LUT_basic_color_idx_to_blocks); if (colors_temp.size() != TokiVC::LUT_basic_color_idx_to_blocks.size()) { - std::string msg = fmt::format( + std::string msg = std::format( "\nImpossible error : " "colors_temp.size() (aka {}) " "!=TokiVC::LUT_basic_color_idx_to_blocks.size() (aka {})\n", @@ -518,7 +489,7 @@ bool TokiVC::set_resource_no_lock() noexcept { } if (colors_temp.size() >= UINT16_MAX - 1) { - std::string msg = fmt::format( + std::string msg = std::format( "\nError : too much colors. Num of colors should not exceed {}, " "but it is {} now.\n", UINT16_MAX - 1, colors_temp.size()); @@ -551,26 +522,45 @@ bool TokiVC::set_resource_no_lock() noexcept { return true; } -bool TokiVC::set_allowed_no_lock(const VCL_block *const *const blocks_allowed, - size_t num_block_allowed) noexcept { +bool is_color_allowed( + const std::variant> + &variant, + const std::unordered_map + &blks_allowed) noexcept { + if (variant.index() == 0) { + return blks_allowed.contains(std::get<0>(variant)); + } else { + const auto &blocks = std::get<1>(variant); + for (const VCL_block *blkp : blocks) { + if (!blks_allowed.contains(blkp)) { + return false; + // here continue only skips one for loop! + } + } + return true; + } +} + +bool TokiVC::set_allowed_no_lock( + std::span blocks_ptr_allowed) noexcept { if (!TokiVC_internal::is_basic_color_set_ready) { - VCL_report( - VCL_report_type_t::error, - "You can not set the allowed blocks before basic color set is ready."); + VCL_report(VCL_report_type_t::error, + "You can not set the allowed blocks before basic color set is " + "ready."); return false; } TokiVC::blocks_allowed.clear(); - TokiVC::blocks_allowed.reserve(num_block_allowed); + TokiVC::blocks_allowed.reserve(blocks_ptr_allowed.size()); - for (size_t i = 0; i < num_block_allowed; i++) { - if (blocks_allowed[i] == nullptr || - blocks_allowed[i]->full_id_ptr() == nullptr) { + for (size_t i = 0; i < blocks_ptr_allowed.size(); i++) { + if (blocks_ptr_allowed[i] == nullptr || + blocks_ptr_allowed[i]->full_id_ptr() == nullptr) { VCL_report(VCL_report_type_t::error, "Invalid VCL_block pointer."); return false; } - TokiVC::blocks_allowed.emplace(blocks_allowed[i], 0xFFFF); + TokiVC::blocks_allowed.emplace(blocks_ptr_allowed[i], 0xFFFF); } { @@ -589,7 +579,7 @@ bool TokiVC::set_allowed_no_lock(const VCL_block *const *const blocks_allowed, if (counter_air != 1) { std::string msg = - fmt::format("Types of air block is {}, expected 1.", counter_air); + std::format("Types of air block is {}, but expected 1.", counter_air); VCL_report(VCL_report_type_t::error, msg.c_str()); return false; } @@ -597,35 +587,27 @@ bool TokiVC::set_allowed_no_lock(const VCL_block *const *const blocks_allowed, std::vector allowed_list; allowed_list.resize(TokiVC::LUT_basic_color_idx_to_blocks.size()); - memset(allowed_list.data(), 0, allowed_list.size()); + std::fill(allowed_list.begin(), allowed_list.end(), 0); - if constexpr (false) { - std::string msg = fmt::format("TokiVC::colorset_basic.color_count() = {}.", - TokiVC::colorset_basic.color_count()); - VCL_report(VCL_report_type_t::information, msg.c_str()); - } + // { + // std::string msg = std::format("TokiVC::colorset_basic.color_count() = + // {} + // .", + // TokiVC::colorset_basic.color_count()); + // VCL_report(VCL_report_type_t::information, msg.c_str()); + // } for (size_t idx = 0; idx < TokiVC::LUT_basic_color_idx_to_blocks.size(); idx++) { const auto &variant = LUT_basic_color_idx_to_blocks[idx]; - // allowed_list[idx] = false; - if (variant.index() == 0) { - if (!TokiVC::blocks_allowed.contains(std::get<0>(variant))) { - continue; - } - } else { - for (const VCL_block *blkp : std::get<1>(variant)) { - if (!TokiVC::blocks_allowed.contains(blkp)) { - continue; - } - } + if (is_color_allowed(variant, TokiVC::blocks_allowed)) { + allowed_list[idx] = 1; } - - allowed_list[idx] = true; } + if (!TokiVC::colorset_allowed.apply_allowed( TokiVC::colorset_basic, - reinterpret_cast(allowed_list.data()))) { + reinterpret_cast(allowed_list.data()))) { VCL_report(VCL_report_type_t::error, "Function \"TokiVC::colorset_allowed.apply_allowed\" failed."); return false; @@ -708,7 +690,7 @@ bool TokiVC::convert(::SCL_convertAlgo algo, bool dither) noexcept { } if (!this->img_cvter.convert_image(algo, dither, this->imgcvter_prefer_gpu)) { std::string msg = - fmt::format("Failed to convert. detail : {}, error code = {}", + std::format("Failed to convert. detail : {}, error code = {}", this->img_cvter.gpu_resource()->error_detail_v(), this->img_cvter.gpu_resource()->error_code_v()); VCL_report(VCL_report_type_t::error, msg.c_str()); @@ -728,5 +710,28 @@ void TokiVC::converted_image(uint32_t *dest, int64_t *rows, int64_t *cols, // constexpr size_t sz = sizeof(decltype(this->img_cvter)::TokiColor_t); - this->img_cvter.converted_image(dest, rows, cols, !write_dest_row_major); + this->img_cvter.converted_image(dest, rows, cols, write_dest_row_major); } + +bool TokiVC::set_gpu_resource(const VCL_GPU_Platform *p, + const VCL_GPU_Device *d, + const gpu_options &option) noexcept { + if (this->img_cvter.have_gpu_resource()) { + gpu_wrapper::gpu_interface::destroy(this->img_cvter.gpu_resource()); + } + auto platp = static_cast(p->pw); + auto devp = static_cast(d->dw); + + std::pair err; + auto gi = gpu_wrapper::gpu_interface::create(platp, devp, err); + if (gi == nullptr || !gi->ok_v()) { + err.second = std::format("{}, error code = {}", err.second, err.first); + write_to_string_deliver(err.second, option.error_message); + return false; + } else { + write_to_string_deliver("", option.error_message); + } + + this->img_cvter.set_gpu_resource(gi); + return this->img_cvter.gpu_resource()->ok_v(); +} \ No newline at end of file diff --git a/VisualCraftL/TokiVC.h b/VisualCraftL/TokiVC.h index dcd4c134..9562e779 100644 --- a/VisualCraftL/TokiVC.h +++ b/VisualCraftL/TokiVC.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -37,19 +37,19 @@ This file is part of SlopeCraft. #include class VCL_GPU_Platform { -public: + public: ~VCL_GPU_Platform() { gpu_wrapper::platform_wrapper::destroy(this->pw); } gpu_wrapper::platform_wrapper *pw{nullptr}; }; class VCL_GPU_Device { -public: + public: ~VCL_GPU_Device() { gpu_wrapper::device_wrapper::destroy(this->dw); } gpu_wrapper::device_wrapper *dw{nullptr}; }; class TokiVC : public VCL_Kernel { -public: + public: TokiVC(); virtual ~TokiVC(); void set_ui(void *uiptr, void (*progressRangeSet)(void *, int, int, int), @@ -61,21 +61,12 @@ class TokiVC : public VCL_Kernel { bool set_gpu_resource(const VCL_GPU_Platform *p, const VCL_GPU_Device *d) noexcept override { - if (this->img_cvter.have_gpu_resource()) { - gpu_wrapper::gpu_interface::destroy(this->img_cvter.gpu_resource()); - } - auto platp = static_cast(p->pw); - auto devp = static_cast(d->dw); - - auto gi = gpu_wrapper::gpu_interface::create(platp, devp); - if (gi == nullptr || !gi->ok_v()) { - return false; - } - - this->img_cvter.set_gpu_resource(gi); - return this->img_cvter.gpu_resource()->ok_v(); + return this->set_gpu_resource(p, d, {}); } + bool set_gpu_resource(const VCL_GPU_Platform *p, const VCL_GPU_Device *d, + const gpu_options &option) noexcept override; + bool prefer_gpu() const noexcept override { return this->imgcvter_prefer_gpu; } @@ -114,9 +105,10 @@ class TokiVC : public VCL_Kernel { void converted_image(uint32_t *dest, int64_t *rows, int64_t *cols, bool write_dest_row_major) const noexcept override; - void flag_diagram(uint32_t *image_u8c3_rowmajor, const flag_diagram_option &, - int layer_idx, int64_t *rows_required_dest, - int64_t *cols_required_dest) const noexcept override; + // void flag_diagram(uint32_t *image_u8c3_rowmajor, const flag_diagram_option + // &, + // int layer_idx, int64_t *rows_required_dest, + // int64_t *cols_required_dest) const noexcept override; bool export_flag_diagram(const char *png_filename, const flag_diagram_option &, int layer_idx) const noexcept override; @@ -138,7 +130,7 @@ class TokiVC : public VCL_Kernel { const char *const *const utf8_requiredMods = nullptr, const int requiredModsCount = 0) const noexcept override; -public: + public: static libImageCvt::template ImageCvter::basic_colorset_t colorset_basic; static libImageCvt::template ImageCvter::allowed_colorset_t @@ -153,8 +145,8 @@ class TokiVC : public VCL_Kernel { static VCL_biome_t biome; static bool set_resource_no_lock() noexcept; - static bool set_allowed_no_lock(const VCL_block *const *const blocks_allowed, - size_t num_block_allowed) noexcept; + static bool set_allowed_no_lock( + std::span blocks_ptr_allowed) noexcept; static bool export_test_litematic_no_lock(const char *filename) noexcept; @@ -162,14 +154,14 @@ class TokiVC : public VCL_Kernel { return TokiVC::LUT_basic_color_idx_to_blocks; } -private: + private: static std::vector< std::variant>> LUT_basic_color_idx_to_blocks; static std::unordered_map blocks_allowed; -private: + private: VCL_Kernel_step _step{VCL_Kernel_step::VCL_wait_for_resource}; bool imgcvter_prefer_gpu{false}; @@ -187,6 +179,6 @@ namespace TokiVC_internal { extern std::shared_mutex global_lock; extern bool is_basic_color_set_ready; extern bool is_allowed_color_set_ready; -} // namespace TokiVC_internal +} // namespace TokiVC_internal -#endif // SLOPECRAFT_VISUALCRAFTL_TOKIVC_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFTL_TOKIVC_H \ No newline at end of file diff --git a/VisualCraftL/TokiVC_build.cpp b/VisualCraftL/TokiVC_build.cpp index 6b7202dc..a4993785 100644 --- a/VisualCraftL/TokiVC_build.cpp +++ b/VisualCraftL/TokiVC_build.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -49,12 +49,9 @@ int64_t TokiVC::xyz_size(int64_t *x, int64_t *y, int64_t *z) const noexcept { return -1; } - if (x != nullptr) - *x = this->schem.x_range(); - if (y != nullptr) - *y = this->schem.y_range(); - if (z != nullptr) - *z = this->schem.z_range(); + if (x != nullptr) *x = this->schem.x_range(); + if (y != nullptr) *y = this->schem.y_range(); + if (z != nullptr) *z = this->schem.z_range(); return this->schem.size(); } @@ -80,12 +77,15 @@ bool TokiVC::build() noexcept { std::atomic_bool ret = true; const Eigen::ArrayXX color_id_mat = this->img_cvter.color_id(); + // #warning resume omp here #pragma omp parallel for schedule(static) for (int64_t r = 0; r < this->img_cvter.rows(); r++) { for (int64_t c = 0; c < this->img_cvter.cols(); c++) { + auto color_id = color_id_mat(r, c); + if (color_id >= 0xFFFF) + continue; // full transparent pixels, use air instead - const auto &variant = - TokiVC::LUT_basic_color_idx_to_blocks[color_id_mat(r, c)]; + const auto &variant = TokiVC::LUT_basic_color_idx_to_blocks[color_id]; const VCL_block *const *blockpp = nullptr; size_t depth_current = 0; @@ -106,7 +106,7 @@ bool TokiVC::build() noexcept { for (size_t di = 0; di < 3; di++) { if (coord[di] < 0 || coord[di] >= range[di]) { std::string msg = - fmt::format("coordiante out of range : {}, {}, {}.\n", coord[0], + std::format("coordinate out of range : {}, {}, {}.\n", coord[0], coord[1], coord[2]); VCL_report(VCL_report_type_t::error, msg.c_str()); } @@ -116,10 +116,10 @@ bool TokiVC::build() noexcept { auto it = TokiVC::blocks_allowed.find(blkp); if (it == TokiVC::blocks_allowed.end()) { - std::string msg = - fmt::format("Failed to find VCL_block at address {} named {} in " - "allowed blocks. This is an internal error.", - (const void *)blkp, blkp->full_id_ptr()->c_str()); + std::string msg = std::format( + "Failed to find VCL_block at address {} named {} in " + "allowed blocks. This is an internal error.", + (const void *)blkp, blkp->full_id_ptr()->c_str()); VCL_report(VCL_report_type_t::error, msg.c_str()); ret = false; } @@ -143,7 +143,6 @@ bool TokiVC::build() noexcept { bool TokiVC::export_litematic(const char *localEncoding_filename, const char *utf8_litename, const char *utf8_regionname) const noexcept { - std::shared_lock lkgd(TokiVC_internal::global_lock); if (this->_step < VCL_Kernel_step::VCL_built) { @@ -171,10 +170,10 @@ bool TokiVC::export_litematic(const char *localEncoding_filename, &flag, &detail); if (!ok) { - std::string err = - fmt::format("VisualCraftL failed to export a litematic. Error " - "number(SCSL_errorFlag) = {}, detail : {}", - int(flag), detail); + std::string err = std::format( + "VisualCraftL failed to export a litematic. Error " + "number(SCSL_errorFlag) = {}, detail : {}", + int(flag), detail); VCL_report(VCL_report_type_t::error, err.c_str()); return false; } @@ -183,7 +182,6 @@ bool TokiVC::export_litematic(const char *localEncoding_filename, bool TokiVC::export_structure(const char *localEncoding_TargetName, bool is_air_structure_void) const noexcept { - std::shared_lock lkgd(TokiVC_internal::global_lock); if (this->_step < VCL_Kernel_step::VCL_built) { @@ -198,10 +196,10 @@ bool TokiVC::export_structure(const char *localEncoding_TargetName, localEncoding_TargetName, is_air_structure_void, &flag, &detail); if (!ok) { - std::string err = - fmt::format("VisualCraftL failed to export a structure. Error " - "number(SCSL_errorFlag) = {}, detail : {}", - int(flag), detail); + std::string err = std::format( + "VisualCraftL failed to export a structure. Error " + "number(SCSL_errorFlag) = {}, detail : {}", + int(flag), detail); VCL_report(VCL_report_type_t::error, err.c_str()); return false; } @@ -239,10 +237,10 @@ bool TokiVC::export_WESchem(const char *localEncoding_fileName, const bool ok = this->schem.export_WESchem(localEncoding_fileName, info, &flag, &detail); if (!ok) { - std::string err = - fmt::format("VisualCraftL failed to export a WorldEdit schem. Error " - "number(SCSL_errorFlag) = {}, detail : {}", - int(flag), detail); + std::string err = std::format( + "VisualCraftL failed to export a WorldEdit schem. Error " + "number(SCSL_errorFlag) = {}, detail : {}", + int(flag), detail); VCL_report(VCL_report_type_t::error, err.c_str()); return false; } diff --git a/VisualCraftL/TokiVC_export_test.cpp b/VisualCraftL/TokiVC_export_test.cpp index db5d2f2e..10f9b2d1 100644 --- a/VisualCraftL/TokiVC_export_test.cpp +++ b/VisualCraftL/TokiVC_export_test.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,15 +20,15 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ +#include +#include #include "TokiVC.h" #include "VCL_internal.h" #include "VisualCraftL.h" -#include -#include -const VCL_block * -find_first_mark_block(const std::unordered_map - &blocks_allowed) noexcept { +const VCL_block *find_first_mark_block( + const std::unordered_map + &blocks_allowed) noexcept { const VCL_block *p = nullptr; for (const auto &pair : blocks_allowed) { if (pair.first->is_air()) { @@ -46,7 +46,6 @@ find_first_mark_block(const std::unordered_map } bool TokiVC::export_test_litematic_no_lock(const char *filename) noexcept { - if (!TokiVC_internal::is_basic_color_set_ready || !TokiVC_internal::is_allowed_color_set_ready) { VCL_report( @@ -138,10 +137,19 @@ bool TokiVC::export_test_litematic_no_lock(const char *filename) noexcept { { libSchem::litematic_info info; info.litename_utf8 = - fmt::format("Testing litematic for 1.{}", int(TokiVC::version)); + std::format("Testing litematic for 1.{}", int(TokiVC::version)); info.author_utf8 = "VisualCraftL"; info.destricption_utf8 = "This litematic is generated by VisualCraft."; - return schem.export_litematic(filename, info); + auto res = schem.export_litematic(filename, info); + if (not res) { + auto &err = res.error(); + auto msg = + std::format("Failed to export {} because {}, detail: {}\n", filename, + magic_enum::enum_name(err.first), err.second); + VCL_report(VCL_report_type_t::warning, msg.c_str(), true); + return false; + } + return true; } } diff --git a/VisualCraftL/TokiVC_flagdiagram.cpp b/VisualCraftL/TokiVC_flagdiagram.cpp index da02557d..f8b396d2 100644 --- a/VisualCraftL/TokiVC_flagdiagram.cpp +++ b/VisualCraftL/TokiVC_flagdiagram.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,8 +23,10 @@ This file is part of SlopeCraft. #include "TokiVC.h" #include "VCL_internal.h" #include "VisualCraftL.h" -#include -#include + +#ifdef min +#undef min +#endif constexpr uint32_t reverse_color(uint32_t ARGB_src) noexcept { return ARGB32(255 - getR(ARGB_src), 255 - getG(ARGB_src), @@ -55,112 +57,7 @@ void ARGB_to_AGBR(uint32_t *ptr, size_t num_pixels) noexcept { } } -void TokiVC::draw_flag_diagram_to_memory(uint32_t *image_u8c3_rowmajor, - const flag_diagram_option &opt, - int layer_idx) const noexcept { - Eigen::Map map( - image_u8c3_rowmajor, (opt.row_end - opt.row_start) * 16, - this->cols() * 16); - - memset(image_u8c3_rowmajor, 0, - (opt.row_end - opt.row_start) * 16 * this->img_cvter.cols() * 16 * - sizeof(uint32_t)); - - /*s constexpr int size_of_u32_per_vec = 32 / sizeof(uint32_t); - - const bool is_dst_aligned = - (reinterpret_cast(image_u8c3_rowmajor) % 32) == 0; - - */ - // copy block images - for (int64_t r = opt.row_start; r < opt.row_end; r++) { - const int r_pixel_beg = (r - opt.row_start) * 16; - for (int64_t c = 0; c < this->cols(); c++) { - const int c_pixel_beg = c * 16; - const uint16_t current_color_idx = this->img_cvter.color_id(r, c); - - const auto &variant = - TokiVC::LUT_basic_color_idx_to_blocks[current_color_idx]; - const VCL_block *blkp = nullptr; - if (variant.index() == 0) { - if (layer_idx == 0) { - blkp = std::get<0>(variant); - } - } else { - const auto &vec = std::get<1>(variant); - - if (layer_idx < (int)vec.size()) { - blkp = vec[layer_idx]; - } - } - - if (blkp == nullptr) { - map.block<16, 16>(r_pixel_beg, c_pixel_beg).fill(0x00'FF'FF'FF); - continue; - } - /* - const bool is_src_aligned = - (reinterpret_cast( - blkp->project_image_on_exposed_face.data()) % - 32) == 0; - const bool is_aligned = is_dst_aligned && is_src_aligned; - */ - - map.block<16, 16>(r_pixel_beg, c_pixel_beg) = - blkp->project_image_on_exposed_face; - } - } - - for (int64_t br = opt.row_start; br < opt.row_end; br++) { - if ((opt.split_line_row_margin > 0) && - (br % opt.split_line_row_margin == 0)) { - const int64_t pr = (br - opt.row_start) * 16; - - reverse_color(&map(pr, 0), map.cols()); - } - } - - for (int64_t bc = 0; bc < this->img_cvter.cols(); bc++) { - if ((opt.split_line_col_margin > 0) && - (bc % opt.split_line_col_margin == 0)) { - const int64_t pc = bc * 16; - - for (int64_t pr = 0; pr < map.rows(); pr++) { - map(pr, pc) = reverse_color(map(pr, pc)); - } - } - } -} - -void TokiVC::flag_diagram(uint32_t *image_u8c3_rowmajor, - const flag_diagram_option &opt, int layer_idx, - int64_t *rows_required_dest, - int64_t *cols_required_dest) const noexcept { - if (this->_step < ::VCL_Kernel_step::VCL_wait_for_build) { - VCL_report(VCL_report_type_t::error, - "Trying to export flag diagram without image converted."); - return; - } - - std::shared_lock lkgd(TokiVC_internal::global_lock); - - if (rows_required_dest != nullptr) { - *rows_required_dest = (opt.row_end - opt.row_start) * 16; - } - - if (cols_required_dest != nullptr) { - *cols_required_dest = this->img_cvter.cols() * 16; - } - - if (image_u8c3_rowmajor == nullptr) { - return; - } - - this->draw_flag_diagram_to_memory(image_u8c3_rowmajor, opt, layer_idx); -} - -#include -#include +#include bool TokiVC::export_flag_diagram(const char *png_filename, const flag_diagram_option &opt, @@ -172,111 +69,61 @@ bool TokiVC::export_flag_diagram(const char *png_filename, } std::shared_lock lkgd(TokiVC_internal::global_lock); + std::array, 4> txt{ + std::make_pair( + "Title", "Flat diagram generated by VisualCraftL."), + std::make_pair("Software", "VisualCraftL"), + std::make_pair( + "Description", + "This image is a flat diagram created by VisualCraftL which is is a " + "subproject of SlopeCraft, developed by TokiNoBug."), + std::make_pair( + "Comment", + "SlopeCraft is a free software published " + "under GPLv3 license. You can find " + "its repository at https://github.com/SlopeCraft/SlopeCraft")}; this->img_cvter.ui.rangeSet(0, this->img_cvter.rows(), 0); + const libFlatDiagram::fd_option fdopt{ + .row_start = opt.row_start, + .row_end = opt.row_end, + .cols = this->cols(), + .split_line_row_margin = opt.split_line_row_margin, + .split_line_col_margin = opt.split_line_col_margin, + .png_compress_level = opt.png_compress_level, + .png_compress_memory_level = opt.png_compress_memory_level, + }; + Eigen::Array empty_image; + empty_image.fill(0x00'FF'FF'FF); + auto get_block_image = [&](int64_t r, + int64_t c) -> libFlatDiagram::block_img_ref_t { + const uint16_t current_color_idx = this->img_cvter.color_id(r, c); + + const auto &variant = + TokiVC::LUT_basic_color_idx_to_blocks[current_color_idx]; + const VCL_block *blkp = nullptr; + if (variant.index() == 0) { + if (layer_idx == 0) { + blkp = std::get<0>(variant); + } + } else { + const auto &vec = std::get<1>(variant); - const int64_t rows_capacity_by_blocks = 64; - - block_model::EImgRowMajor_t buffer(rows_capacity_by_blocks * 16, - this->img_cvter.cols() * 16); - - FILE *fp = fopen(png_filename, "wb"); - - if (fp == nullptr) { - std::string msg = - fmt::format("fopen failed to create png file {}.", png_filename); - VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - png_struct *png = - png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (png == nullptr) { - fclose(fp); - std::string msg = fmt::format( - "fopen failed to create png struct for png file {}.", png_filename); - VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - png_info *png_info = png_create_info_struct(png); - if (png_info == nullptr) { - png_destroy_write_struct(&png, &png_info); - fclose(fp); - std::string msg = - fmt::format("fopen failed to create png info struct for png file {}.", - png_filename); - VCL_report(VCL_report_type_t::error, msg.c_str()); - return false; - } - - png_init_io(png, fp); - - png_set_compression_level(png, opt.png_compress_level); - - png_set_compression_mem_level(png, opt.png_compress_memory_level); - - // png_set_text_compression_level(png, 8); - - png_set_IHDR(png, png_info, this->img_cvter.cols() * 16, - 16 * (opt.row_end - opt.row_start), 8, PNG_COLOR_TYPE_RGB_ALPHA, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT); - png_write_info(png, png_info); - - { - std::array, 4> txt{ - std::make_pair( - "Title", "Flat diagram generated by VisualCraftL."), - std::make_pair("Software", "VisualCraftL"), - std::make_pair( - "Description", - "This image is a flat diagram created by VisualCraftL, which is is " - "a subproject of SlopeCraft, developed by TokiNoBug."), - std::make_pair( - "Comment", - "VisualCraftL is a free software published " - "under GPLv3 license. You can find " - "its repository at https://github.com/ToKiNoBug/SlopeCraft")}; - - std::array png_txts; - - for (size_t i = 0; i < txt.size(); i++) { - png_txts[i].compression = -1; - png_txts[i].key = txt[i].first.data(); - png_txts[i].text = txt[i].second.data(); - // png.txts[i].key = + if (layer_idx < (int)vec.size()) { + blkp = vec[layer_idx]; + } } + if (c == 0) + this->img_cvter.ui.rangeSet(0, this->img_cvter.rows(), r - opt.row_start); - png_set_text(png, png_info, png_txts.data(), png_txts.size()); - } - - for (int64_t ridx = opt.row_start; ridx < opt.row_end; - ridx += rows_capacity_by_blocks) { - const int64_t rows_this_time = - std::min(opt.row_end - ridx, rows_capacity_by_blocks); - memset(buffer.data(), 0xFF, buffer.size() * sizeof(uint32_t)); - this->draw_flag_diagram_to_memory( - buffer.data(), - {opt.lib_version, ridx, ridx + rows_this_time, - opt.split_line_row_margin, opt.split_line_col_margin}, - layer_idx); - - ARGB_to_AGBR(buffer.data(), - rows_this_time * 16 * this->img_cvter.cols() * 16); - - for (int64_t pix_r = 0; pix_r < rows_this_time * 16; pix_r++) { - png_write_row(png, reinterpret_cast(&buffer(pix_r, 0))); + if (blkp == nullptr) { + return libFlatDiagram::block_img_ref_t{empty_image.data()}; } - - this->img_cvter.ui.rangeSet(0, this->img_cvter.rows(), - ridx - opt.row_start); - } - - png_write_end(png, png_info); - - png_destroy_write_struct(&png, &png_info); - fclose(fp); + return libFlatDiagram::block_img_ref_t{ + blkp->project_image_on_exposed_face.data()}; + }; + const auto err_string = libFlatDiagram::export_flat_diagram( + png_filename, fdopt, get_block_image, txt); this->img_cvter.ui.rangeSet(0, this->img_cvter.rows(), this->img_cvter.rows()); diff --git a/VisualCraftL/VCL_blocks_fixed.json b/VisualCraftL/VCL_blocks_fixed.json index 7a54a2a9..1b338655 100644 --- a/VisualCraftL/VCL_blocks_fixed.json +++ b/VisualCraftL/VCL_blocks_fixed.json @@ -1,3582 +1,4368 @@ { - "minecraft:air": { - "class": "others", - "nameEN": "Air", - "nameZH": "空气", - "version": "all", - "is_air": true - }, - "minecraft:brick_block": { - "class": "clay", - "nameEN": "Bricks (1.12)", - "nameZH": "砖块 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:bricks": { - "class": "clay", - "nameEN": "Bricks", - "nameZH": "砖块", - "version": 13 - }, - "minecraft:clay": { - "class": "clay", - "endermanPickable": true, - "nameEN": "Clay block", - "nameZH": "黏土块", - "version": "all", - "reproducible": false - }, - "minecraft:black_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=black]" - ] - ], - "nameEN": "Black concrete", - "nameZH": "黑色混凝土", - "version": "all" - }, - "minecraft:blue_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=blue]" - ] - ], - "nameEN": "Blue concrete", - "nameZH": "蓝色混凝土", - "version": "all" - }, - "minecraft:brown_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=brown]" - ] - ], - "nameEN": "Brown concrete", - "nameZH": "棕色混凝土", - "version": "all" - }, - "minecraft:cyan_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=cyan]" - ] - ], - "nameEN": "Cyan concrete", - "nameZH": "青色混凝土", - "version": "all" - }, - "minecraft:gray_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=gray]" - ] - ], - "nameEN": "Gray concrete", - "nameZH": "灰色混凝土", - "version": "all" - }, - "minecraft:green_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=green]" - ] - ], - "nameEN": "Green concrete", - "nameZH": "绿色混凝土", - "version": "all" - }, - "minecraft:light_blue_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=light_blue]" - ] - ], - "nameEN": "Light blue concrete", - "nameZH": "浅蓝混凝土", - "version": "all" - }, - "minecraft:light_gray_concrete": { - "background": true, - "class": "concrete", - "nameEN": "Light gray concrete", - "nameZH": "浅灰混凝土", - "version": 13 - }, - "minecraft:lime_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=lime]" - ] - ], - "nameEN": "Lime concrete", - "nameZH": "浅绿混凝土", - "version": "all" - }, - "minecraft:magenta_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=magenta]" - ] - ], - "nameEN": "Magenta concrete", - "nameZH": "品红色混凝土", - "version": "all" - }, - "minecraft:orange_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=orange]" - ] - ], - "nameEN": "Orange concrete", - "nameZH": "橙色混凝土", - "version": "all" - }, - "minecraft:pink_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=pink]" - ] - ], - "nameEN": "Pink concrete", - "nameZH": "粉色混凝土", - "version": "all" - }, - "minecraft:purple_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=purple]" - ] - ], - "nameEN": "Purple concrete", - "nameZH": "紫色混凝土", - "version": "all" - }, - "minecraft:red_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=red]" - ] - ], - "nameEN": "Red concrete", - "nameZH": "红色混凝土", - "version": "all" - }, - "minecraft:silver_concrete": { - "background": true, - "class": "concrete", - "nameEN": "Silver concrete (1.12)", - "nameZH": "银色混凝土 (1.12)", - "version": [ - 12 - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=silver]" - ] - ] - }, - "minecraft:white_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=white]" - ] - ], - "nameEN": "White concrete", - "nameZH": "白色混凝土", - "version": "all" - }, - "minecraft:yellow_concrete": { - "background": true, - "class": "concrete", - "id_replace_list": [ - [ - 12, - "minecraft:concrete[color=yellow]" - ] - ], - "nameEN": "Yellow concrete", - "nameZH": "黄色混凝土", - "version": "all" - }, - "minecraft:hay_block[axis=y]": { - "class": "crafted", - "nameEN": "Hay block", - "nameZH": "干草块", - "version": "all" - }, - "minecraft:coarse_dirt": { - "class": "desert", - "endermanPickable": true, - "nameEN": "Coarse dirt", - "nameZH": "砂土", - "version": 13 - }, - "minecraft:red_sandstone[type=smooth_red_sandstone]": { - "class": "desert", - "nameEN": "Smooth red sandstone", - "nameZH": "平滑红砂岩 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:sandstone[type=smooth_sandstone]": { - "class": "desert", - "nameEN": "Smooth sandstone", - "nameZH": "平滑砂岩 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:smooth_red_sandstone": { - "class": "desert", - "nameEN": "Smooth red sandstone", - "nameZH": "平滑红砂岩", - "version": 13 - }, - "minecraft:smooth_sandstone": { - "class": "desert", - "nameEN": "Smooth sandstone", - "nameZH": "平滑砂岩", - "version": 13 - }, - "minecraft:black_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=black]" - ] - ], - "nameEN": "Blakc glass", - "nameZH": "黑色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:blue_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=blue]" - ] - ], - "nameEN": "Blue glass", - "nameZH": "蓝色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:brown_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=brown]" - ] - ], - "nameEN": "Brown glass", - "nameZH": "棕色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:cyan_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=cyan]" - ] - ], - "nameEN": "Cyan glass", - "nameZH": "青色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:gray_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=gray]" - ] - ], - "nameEN": "Gray glass", - "nameZH": "灰色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:green_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=green]" - ] - ], - "nameEN": "Green glass", - "nameZH": "绿色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:light_blue_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=light]" - ] - ], - "nameEN": "Light blue glass", - "nameZH": "浅蓝染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:light_gray_stained_glass": { - "class": "glass", - "nameEN": "Light gray glass", - "nameZH": "浅灰染色玻璃", - "transparent": true, - "version": 13 - }, - "minecraft:lime_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=lime]" - ] - ], - "nameEN": "Lime glass", - "nameZH": "浅绿染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:magenta_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=magenta]" - ] - ], - "nameEN": "Magenta glass", - "nameZH": "品红染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:orange_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=orange]" - ] - ], - "nameEN": "Orange glass", - "nameZH": "橙色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:pink_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=pink]" - ] - ], - "nameEN": "Pink glass", - "nameZH": "粉色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:purple_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=purple]" - ] - ], - "nameEN": "Purple glass", - "nameZH": "紫色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:red_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=red]" - ] - ], - "nameEN": "Red glass", - "nameZH": "红色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:silver_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=silver]" - ] - ], - "nameEN": "Light gray glass (1.12)", - "nameZH": "浅灰染色玻璃 (1.12)", - "transparent": true, - "version": [ - 12 - ] - }, - "minecraft:tinted_glass": { - "class": "glass", - "nameEN": "Tinted glass", - "nameZH": "遮光玻璃", - "transparent": true, - "version": 17 - }, - "minecraft:white_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=white]" - ] - ], - "nameEN": "White glass", - "nameZH": "白色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:yellow_stained_glass": { - "class": "glass", - "id_replace_list": [ - [ - 12, - "minecraft:stained_glass[color=yellow]" - ] - ], - "nameEN": "yellow glass", - "nameZH": "黄色染色玻璃", - "transparent": true, - "version": "all" - }, - "minecraft:acacia_leaves[distance=7,persistent=true]": { - "burnable": true, - "class": "leaves", - "id_replace_list": [ - [ - 12, - "minecraft:leaves2[variant=acacia,check_decay=false,decayable=false]" - ] - ], - "nameEN": "Acacia leaves", - "nameZH": "金合欢树叶", - "version": "all", - "is_foliage": true - }, - "minecraft:birch_leaves[distance=7,persistent=true]": { - "burnable": true, - "class": "leaves", - "id_replace_list": [ - [ - 12, - "minecraft:leaves[variant=birch,check_decay=false,decayable=false]" - ] - ], - "nameEN": "Brich leaves", - "nameZH": "白桦树叶", - "version": "all", - "is_foliage": true - }, - "minecraft:dark_oak_leaves[distance=7,persistent=true]": { - "burnable": true, - "class": "leaves", - "id_replace_list": [ - [ - 12, - "minecraft:leaves2[variant=dark_oak,check_decay=false,decayable=false]" - ] - ], - "nameEN": "Dark oat leaves", - "nameZH": "黑橡树树叶", - "version": "all", - "is_foliage": true - }, - "minecraft:jungle_leaves[distance=7,persistent=true]": { - "burnable": true, - "class": "leaves", - "id_replace_list": [ - [ - 12, - "minecraft:leaves[variant=jungle,check_decay=false,decayable=false]" - ] - ], - "nameEN": "Jungle leaves", - "nameZH": "丛林木树叶", - "version": "all", - "is_foliage": true - }, - "minecraft:oak_leaves[distance=7,persistent=true]": { - "burnable": true, - "class": "leaves", - "id_replace_list": [ - [ - 12, - "minecraft:leaves[variant=oak,check_decay=false,decayable=false]" - ] - ], - "nameEN": "Oak leaves", - "nameZH": "橡树树叶", - "version": "all", - "is_foliage": true - }, - "minecraft:spruce_leaves[distance=7,persistent=true]": { - "burnable": true, - "class": "leaves", - "id_replace_list": [ - [ - 12, - "minecraft:leaves[variant=spruce,check_decay=false,decayable=false]" - ] - ], - "nameEN": "Spurce leaves", - "nameZH": "云杉树叶", - "version": "all", - "is_foliage": true - }, - "minecraft:blue_ice": { - "class": "natural", - "nameEN": "Blue ice", - "nameZH": "蓝冰", - "version": 13 - }, - "minecraft:dirt": { - "class": "natural", - "endermanPickable": true, - "nameEN": "Dirt", - "nameZH": "泥土", - "version": 13 - }, - "minecraft:dirt[variant=coarse_dirt,snowy=false]": { - "class": "natural", - "endermanPickable": true, - "nameEN": "Coarse dirt (1.12)", - "nameZH": "砂土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:dirt[variant=dirt,snowy=false]": { - "class": "natural", - "endermanPickable": true, - "nameEN": "Dirt (1.12)", - "nameZH": "泥土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:dirt[variant=podzol,snowy=false]": { - "class": "natural", - "endermanPickable": true, - "nameEN": "Podzol (1.12)", - "nameZH": "灰化土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:grass[snowy=false]": { - "class": "natural", - "endermanPickable": true, - "faces": [ - "up" - ], - "nameEN": "Grass block (1.12)", - "nameZH": "草方块 (1.12)", - "version": [ - 12 - ], - "is_grass": true - }, - "minecraft:grass_block[snowy=false]": { - "class": "natural", - "endermanPickable": true, - "faces": [ - "up" - ], - "nameEN": "Grass block", - "nameZH": "草方块", - "version": 13, - "is_grass": true - }, - "minecraft:honeycomb_block": { - "class": "natural", - "nameEN": "Honey comb block", - "nameZH": "蜜脾块", - "version": 15 - }, - "minecraft:ice": { - "class": "natural", - "nameEN": "Ice", - "nameZH": "冰", - "transparent": true, - "version": "all" - }, - "minecraft:mud": { - "class": "natural", - "nameEN": "Mud", - "nameZH": "泥巴", - "version": 19 - }, - "minecraft:mud_bricks": { - "class": "natural", - "nameEN": "Mud bricks", - "nameZH": "泥砖", - "version": 19 - }, - "minecraft:mycelium[snowy=false]": { - "class": "natural", - "faces": [ - "up" - ], - "nameEN": "Mycelium", - "nameZH": "菌丝", - "version": "all" - }, - "minecraft:ochre_froglight[axis=y]": { - "class": "natural", - "isGlowing": true, - "nameEN": "Ochre Froglight", - "nameZH": "黄色蛙鸣灯", - "version": 19, - "rare": true - }, - "minecraft:packed_ice": { - "class": "natural", - "nameEN": "Packed ice", - "nameZH": "浮冰", - "version": "all" - }, - "minecraft:packed_mud": { - "class": "natural", - "nameEN": "Packed mud", - "nameZH": "泥坯", - "version": 19 - }, - "minecraft:pearlescent_froglight[axis=y]": { - "class": "natural", - "isGlowing": true, - "nameEN": "Verdant Froglight", - "nameZH": "紫色蛙鸣灯", - "version": 19, - "rare": true - }, - "minecraft:podzol[snowy=false]": { - "class": "natural", - "endermanPickable": true, - "nameEN": "Podzol", - "nameZH": "灰化土", - "version": 13 - }, - "minecraft:pumpkin": { - "class": "natural", - "endermanPickable": true, - "nameEN": "Pumpkin", - "nameZH": "南瓜", - "version": 13 - }, - "minecraft:pumpkin[facing=east]": { - "class": "natural", - "endermanPickable": true, - "faces": [ - "east" - ], - "id_replace_list": [ - [ - 12, - "minecraft:pumpkin[variant=east]" - ] - ], - "nameEN": "Pumpkin(east) (1.12)", - "nameZH": "南瓜(东) (1.12)", - "version": [ - 12 - ] - }, - "minecraft:pumpkin[facing=north]": { - "class": "natural", - "endermanPickable": true, - "id_replace_list": [ - [ - 12, - "minecraft:pumpkin[variant=north]" - ] - ], - "nameEN": "Pumpkin(north) (1.12)", - "nameZH": "南瓜(北) (1.12)", - "version": [ - 12 - ] - }, - "minecraft:pumpkin[facing=south]": { - "class": "natural", - "endermanPickable": true, - "faces": [ - "south", - "north" - ], - "id_replace_list": [ - [ - 12, - "minecraft:pumpkin[variant=south]" - ] - ], - "nameEN": "Pumpkin(south) (1.12)", - "nameZH": "南瓜(南) (1.12)", - "version": [ - 12 - ] - }, - "minecraft:pumpkin[facing=west]": { - "class": "natural", - "endermanPickable": true, - "faces": [ - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:pumpkin[variant=west]" - ] - ], - "nameEN": "Pumpkin(west) (1.12)", - "nameZH": "南瓜(西) (1.12)", - "version": [ - 12 - ] - }, - "minecraft:shroomlight": { - "class": "natural", - "isGlowing": true, - "nameEN": "Shroomlight", - "nameZH": "菌光体", - "version": 16 - }, - "minecraft:snow": { - "class": "natural", - "nameEN": "Snow (1.12)", - "nameZH": "雪块 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:snow_block": { - "class": "natural", - "nameEN": "Snow", - "nameZH": "雪块", - "version": 13 - }, - "minecraft:verdant_froglight[axis=y]": { - "class": "natural", - "isGlowing": true, - "nameEN": "Verdant Froglight", - "nameZH": "绿色蛙鸣灯", - "version": 19, - "rare": true - }, - "minecraft:crimson_nylium": { - "class": "nether", - "endermanPickable": true, - "faces": [ - "up" - ], - "nameEN": "Crimson nylium", - "nameZH": "绯红菌岩", - "version": 16 - }, - "minecraft:glowstone": { - "class": "nether", - "isGlowing": true, - "nameEN": "Glowstone", - "nameZH": "萤石", - "version": "all" - }, - "minecraft:magma": { - "class": "nether", - "nameEN": "Magma block (1.12)", - "nameZH": "岩浆块 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:magma_block": { - "class": "nether", - "nameEN": "Magma block", - "nameZH": "岩浆块", - "version": 13 - }, - "minecraft:nether_brick": { - "class": "nether", - "nameEN": "Nether brick (1.12)", - "nameZH": "地狱砖块 (1.12)", - "version": [ - 12 - ], - "reproducible": false - }, - "minecraft:nether_bricks": { - "class": "nether", - "nameEN": "Nether brick", - "nameZH": "地狱砖块", - "version": 13, - "reproducible": false - }, - "minecraft:nether_wart_block": { - "class": "nether", - "nameEN": "Nether wart block", - "nameZH": "地狱疣块", - "version": "all" - }, - "minecraft:netherrack": { - "class": "nether", - "nameEN": "Netherrack", - "nameZH": "地狱岩", - "version": "all", - "reproducible": false - }, - "minecraft:soul_sand": { - "class": "nether", - "nameEN": "Soul sand", - "nameZH": "灵魂沙", - "version": "all", - "reproducible": false - }, - "minecraft:soul_soil": { - "class": "nether", - "nameEN": "Soul soil", - "nameZH": "灵魂土", - "version": 16, - "reproducible": false - }, - "minecraft:warped_nylium": { - "class": "nether", - "endermanPickable": true, - "faces": [ - "up" - ], - "nameEN": "Warped nylium", - "nameZH": "扭曲菌岩", - "version": 16 - }, - "minecraft:warped_wart_block": { - "class": "nether", - "nameEN": "Warped wart block", - "nameZH": "扭曲疣块", - "version": 16 - }, - "minecraft:dark_prismarine": { - "class": "ocean", - "nameEN": "Dark prismarine", - "nameZH": "暗海晶石", - "version": 13 - }, - "minecraft:dried_kelp_block": { - "class": "ocean", - "nameEN": "Kelp block", - "nameZH": "海带块", - "version": 13 - }, - "minecraft:prismarine": { - "class": "ocean", - "nameEN": "Prismarine", - "nameZH": "海晶石", - "version": 13 - }, - "minecraft:prismarine[variant=dark_prismarine]": { - "class": "ocean", - "nameEN": "Dark prismarine (1.12)", - "nameZH": "暗海晶石 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:prismarine[variant=prismarine]": { - "class": "ocean", - "nameEN": "Prismarine (1.12)", - "nameZH": "海晶石 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:prismarine[variant=prismarine_bricks]": { - "class": "ocean", - "nameEN": "Prismarine bricks (1.12)", - "nameZH": "海晶石砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:prismarine_bricks": { - "class": "ocean", - "nameEN": "Prismarine bricks", - "nameZH": "海晶石砖", - "version": 13 - }, - "minecraft:sea_lantern": { - "class": "ocean", - "nameEN": "Sea lantern", - "nameZH": "海晶灯", - "version": "all" - }, - "minecraft:amethyst_block": { - "class": "ore", - "nameEN": "Amethyst block", - "nameZH": "紫水晶块", - "version": 17, - "rare": true - }, - "minecraft:coal_block": { - "burnable": true, - "class": "ore", - "nameEN": "Coal block", - "nameZH": "煤块", - "version": "all" - }, - "minecraft:diamond_block": { - "class": "ore", - "nameEN": "Diamond block", - "nameZH": "钻石块", - "version": "all", - "reproducible": false, - "rare": true - }, - "minecraft:emerald_block": { - "class": "ore", - "nameEN": "Emerald block", - "nameZH": "绿宝石块", - "version": "all", - "rare": true - }, - "minecraft:gold_block": { - "class": "ore", - "nameEN": "Gold block", - "nameZH": "金块", - "version": "all", - "rare": true - }, - "minecraft:iron_block": { - "class": "ore", - "nameEN": "Iron block", - "nameZH": "铁块", - "version": "all", - "rare": true - }, - "minecraft:lapis_block": { - "class": "ore", - "nameEN": "Lapis block", - "nameZH": "青金石块", - "version": "all", - "reproducible": false, - "rare": true - }, - "minecraft:raw_copper_block": { - "class": "ore", - "nameEN": "Raw copper block", - "nameZH": "生铜块", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:raw_gold_block": { - "class": "ore", - "nameEN": "Raw gold block", - "nameZH": "粗金块", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:raw_iron_block": { - "class": "ore", - "nameEN": "Raw iron block", - "nameZH": "生铁块", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:waxed_copper_block": { - "class": "ore", - "nameEN": "Waxed copper", - "nameZH": "涂蜡铜块", - "version": 17, - "rare": true - }, - "minecraft:waxed_cut_copper": { - "class": "ore", - "nameEN": "Waxed cut copper", - "nameZH": "涂蜡切制铜块", - "version": 17, - "rare": true - }, - "minecraft:waxed_exposed_copper": { - "class": "ore", - "nameEN": "Waxed exposed copper", - "nameZH": "涂蜡斑驳铜块", - "version": 17, - "rare": true - }, - "minecraft:waxed_exposed_cut_copper": { - "class": "ore", - "nameEN": "Waxed exposed cut copper", - "nameZH": "涂蜡切制斑驳铜块", - "version": 17, - "rare": true - }, - "minecraft:waxed_oxidized_copper": { - "class": "ore", - "nameEN": "Waxed oxided copper", - "nameZH": "涂蜡氧化铜块", - "version": 17, - "rare": true - }, - "minecraft:waxed_oxidized_cut_copper": { - "class": "ore", - "nameEN": "Waxed oxided cut copper", - "nameZH": "涂蜡切制氧化铜块", - "version": 17, - "rare": true - }, - "minecraft:waxed_weathered_copper": { - "class": "ore", - "nameEN": "Waxed weathered copper", - "nameZH": "涂蜡生锈铜块", - "version": 17, - "rare": true - }, - "minecraft:gilded_blackstone": { - "class": "ore", - "nameEN": "Glided blackstone", - "nameZH": "镶金黑石", - "version": 16, - "reproducible": false, - "rare": true - }, - "minecraft:coal_ore": { - "class": "ore", - "nameEN": "Coal ore", - "nameZH": "煤矿", - "version": "all", - "reproducible": false - }, - "minecraft:deepslate_coal_ore": { - "class": "ore", - "nameEN": "Deepslate coal ore", - "nameZH": "深板岩煤矿", - "version": 17, - "reproducible": false - }, - "minecraft:copper_ore": { - "class": "ore", - "nameEN": "Copper ore", - "nameZH": "铜矿", - "version": 17, - "reproducible": false - }, - "minecraft:deepslate_copper_ore": { - "class": "ore", - "nameEN": "Deepslate copper ore", - "nameZH": "深板岩铜矿", - "version": 17, - "reproducible": false - }, - "minecraft:diamond_ore": { - "class": "ore", - "nameEN": "Diamond ore", - "nameZH": "钻石矿", - "version": "all", - "reproducible": false, - "rare": true - }, - "minecraft:deepslate_diamond_ore": { - "class": "ore", - "nameEN": "Deepslate diamond ore", - "nameZH": "深板岩钻石矿", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:emerald_ore": { - "class": "ore", - "nameEN": "Emerald ore", - "nameZH": "绿宝石矿", - "version": "all", - "reproducible": false, - "rare": true - }, - "minecraft:deepslate_emerald_ore": { - "class": "ore", - "nameEN": "Deepslate emerald ore", - "nameZH": "深板岩绿宝石矿", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:gold_ore": { - "class": "ore", - "nameEN": "Gold ore", - "nameZH": "金矿", - "version": "all", - "reproducible": false - }, - "minecraft:deepslate_gold_ore": { - "class": "ore", - "nameEN": "Deepslate gold ore", - "nameZH": "深板岩金矿", - "version": 17, - "reproducible": false - }, - "minecraft:iron_ore": { - "class": "ore", - "nameEN": "Iron ore", - "nameZH": "铁矿", - "version": "all", - "reproducible": false - }, - "minecraft:deepslate_iron_ore": { - "class": "ore", - "nameEN": "Deepslate iron ore", - "nameZH": "深板岩铁矿", - "version": 17, - "reproducible": false - }, - "minecraft:lapis_ore": { - "class": "ore", - "nameEN": "Lapis ore", - "nameZH": "青金石矿", - "version": "all", - "reproducible": false - }, - "minecraft:deepslate_lapis_ore": { - "class": "ore", - "nameEN": "Deepslate lapis ore", - "nameZH": "深板岩青金石矿", - "version": 17, - "reproducible": false - }, - "minecraft:redstone_ore": { - "class": "ore", - "nameEN": "Redstone ore (1.12&1.16+)", - "nameZH": "红石矿 (1.12&1.16+)", - "version": [ - 12, - 16, - 17, - 18, - 19 - ], - "reproducible": false - }, - "minecraft:redstone_ore[lit=false]": { - "class": "ore", - "nameEN": "Redstone ore (1.13~1.15)", - "nameZH": "红石矿 (1.13~1.15)", - "version": [ - 13, - 14, - 15 - ], - "reproducible": false - }, - "minecraft:deepslate_redstone_ore": { - "class": "ore", - "nameEN": "Deepslate redstone ore", - "nameZH": "深板岩红石矿", - "version": 17, - "reproducible": false - }, - "minecraft:nether_gold_ore": { - "class": "ore", - "nameEN": "Nether gold ore", - "nameZH": "下界金矿", - "version": 16, - "reproducible": false - }, - "minecraft:nether_quartz_ore": { - "class": "ore", - "nameEN": "Nether quartz ore", - "nameZH": "下界石英矿", - "version": 13, - "reproducible": false - }, - "minecraft:quartz_ore": { - "class": "ore", - "nameEN": "Nether quartz ore (1.12)", - "nameZH": "下界石英矿 (1.12)", - "version": [ - 12 - ], - "reproducible": false - }, - "minecraft:ancient_debris": { - "class": "ore", - "nameEN": "Ancient debris", - "nameZH": "远古残骸", - "version": 16, - "reproducible": false, - "rare": true - }, - "minecraft:sculk": { - "class": "others", - "nameEN": "Sculk block", - "nameZH": "潜声方块", - "version": 19, - "rare": true - }, - "minecraft:sculk_catalyst[bloom=false]": { - "class": "others", - "nameEN": "Sculk catalyst", - "nameZH": "潜声催化剂", - "version": 19, - "rare": true - }, - "minecraft:acacia_planks": { - "burnable": true, - "class": "planks", - "id_replace_list": [ - [ - 12, - "minecraft:planks[variant=acacia]" - ] - ], - "nameEN": "Acacia plank", - "nameZH": "金合欢木板", - "version": "all" - }, - "minecraft:birch_planks": { - "burnable": true, - "class": "planks", - "id_replace_list": [ - [ - 12, - "minecraft:planks[variant=birch]" - ] - ], - "nameEN": "Birch plank", - "nameZH": "白桦木板", - "version": "all" - }, - "minecraft:crimson_planks": { - "burnable": true, - "class": "planks", - "nameEN": "Crimson plank", - "nameZH": "绯红木板", - "version": 16 - }, - "minecraft:dark_oak_planks": { - "burnable": true, - "class": "planks", - "id_replace_list": [ - [ - 12, - "minecraft:planks[variant=dark_oak]" - ] - ], - "nameEN": "Dark oak plank", - "nameZH": "黑橡木木板", - "version": "all" - }, - "minecraft:jungle_planks": { - "burnable": true, - "class": "planks", - "id_replace_list": [ - [ - 12, - "minecraft:planks[variant=jungle]" - ] - ], - "nameEN": "Jungle plank", - "nameZH": "丛林木板", - "version": "all" - }, - "minecraft:mangrove_planks": { - "burnable": true, - "class": "planks", - "nameEN": "Mangrove planks", - "nameZH": "红树木板", - "version": 19 - }, - "minecraft:oak_planks": { - "burnable": true, - "class": "planks", - "id_replace_list": [ - [ - 12, - "minecraft:planks[variant=oak]" - ] - ], - "nameEN": "Oak plank", - "nameZH": "橡木木板", - "version": "all" - }, - "minecraft:spruce_planks": { - "burnable": true, - "class": "planks", - "id_replace_list": [ - [ - 12, - "minecraft:planks[variant=spruce]" - ] - ], - "nameEN": "Spruce plank", - "nameZH": "云杉木板", - "version": "all" - }, - "minecraft:warped_planks": { - "burnable": true, - "class": "planks", - "nameEN": "Warped plank", - "nameZH": "扭曲木板", - "version": 16 - }, - "minecraft:honey_block": { - "class": "redstone", - "nameEN": "Honey block", - "nameZH": "蜜块", - "version": 15 - }, - "minecraft:redstone_block": { - "class": "redstone", - "nameEN": "Redstone block", - "nameZH": "红石块", - "version": "all" - }, - "minecraft:slime": { - "class": "redstone", - "nameEN": "Slime block (1.12)", - "nameZH": "粘液块 (1.12)", - "transparent": true, - "version": [ - 12 - ] - }, - "minecraft:slime_block": { - "class": "redstone", - "nameEN": "Slime block", - "nameZH": "粘液块", - "transparent": true, - "version": 13 - }, - "minecraft:target[power=0]": { - "class": "redstone", - "nameEN": "Target", - "nameZH": "靶子", - "version": 16 - }, - "minecraft:tnt[explode=false]": { - "burnable": true, - "class": "redstone", - "endermanPickable": true, - "nameEN": "TNT (1.12)", - "nameZH": "TNT (1.12)", - "version": [ - 12 - ] - }, - "minecraft:tnt[unstable=false]": { - "burnable": true, - "class": "redstone", - "endermanPickable": true, - "nameEN": "TNT", - "nameZH": "TNT", - "version": 13 - }, - "minecraft:acacia_slab[half=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:wooden_slab[half=top,variant=acacia]" - ] - ], - "nameEN": "Acacia slab (1.12)", - "nameZH": "金合欢木上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:acacia_slab[type=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Acacia slab", - "nameZH": "金合欢木上半砖", - "version": 13 - }, - "minecraft:birch_slab[half=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:wooden_slab[half=top,variant=birch]" - ] - ], - "nameEN": "Birch slab (1.12)", - "nameZH": "白桦木上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:birch_slab[type=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Birch slab", - "nameZH": "白桦木上半砖", - "version": 13 - }, - "minecraft:brick_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=brick]" - ] - ], - "nameEN": "Brick slab (1.12)", - "nameZH": "砖块上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:brick_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Brick slab", - "nameZH": "砖块上半砖", - "version": 13 - }, - "minecraft:cobblestone_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=cobblestone]" - ] - ], - "nameEN": "Cobblestone slab (1.12)", - "nameZH": "圆石上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:cobblestone_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Cobblestone slab", - "nameZH": "圆石上半砖", - "version": 13 - }, - "minecraft:dark_oak_slab[half=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:wooden_slab[half=top,variant=dark_oak]" - ] - ], - "nameEN": "Dark oak slab (1.12)", - "nameZH": "黑橡木上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:dark_oak_slab[type=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Dark oak slab", - "nameZH": "黑橡木上半砖", - "version": 13 - }, - "minecraft:dark_prismarine_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Dark prismarine slab", - "nameZH": "暗海晶石上半砖", - "version": 13 - }, - "minecraft:jungle_slab[half=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:wooden_slab[half=top,variant=jungle]" - ] - ], - "nameEN": "Jungle slab (1.12)", - "nameZH": "丛林木上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:jungle_slab[type=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Jungle slab", - "nameZH": "丛林木上半砖", - "version": 13 - }, - "minecraft:nether_brick_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=nether_brick]" - ] - ], - "nameEN": "Nether brick slab (1.12)", - "nameZH": "地狱砖块上半砖 (1.12)", - "version": [ - 12 - ], - "reproducible": false - }, - "minecraft:nether_brick_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Nether brick slab", - "nameZH": "地狱砖块上半砖", - "version": 13, - "reproducible": false - }, - "minecraft:oak_slab[half=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:wooden_slab[half=top,variant=oak]" - ] - ], - "nameEN": "Oak slab (1.12)", - "nameZH": "橡木上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:oak_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Oak slab", - "nameZH": "橡木上半砖", - "version": 13 - }, - "minecraft:prismarine_brick_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Prismarine brick slab", - "nameZH": "海晶石砖上半砖", - "version": 13 - }, - "minecraft:prismarine_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Prismarine slab", - "nameZH": "海晶石上半砖", - "version": 13 - }, - "minecraft:purpur_slab[half=top,variant=default]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Purpur slab (1.12)", - "nameZH": "紫珀上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:purpur_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Purpur slab", - "nameZH": "紫珀上半砖", - "version": 13 - }, - "minecraft:quartz_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=quartz]" - ] - ], - "nameEN": "Quartz slab (1.12)", - "nameZH": "石英上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:quartz_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Quartz slab", - "nameZH": "石英上半砖", - "version": 13 - }, - "minecraft:red_sandstone_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab2[half=top,variant=red_sandstone]" - ] - ], - "nameEN": "Red sandstone slab (1.12)", - "nameZH": "红砂岩上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:red_sandstone_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Red sandstone slab", - "nameZH": "红砂岩上半砖", - "version": 13 - }, - "minecraft:sandstone_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=sandstone]" - ] - ], - "nameEN": "Sandstone slab (1.12)", - "nameZH": "砂岩上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:sandstone_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Sandstone slab", - "nameZH": "砂岩上半砖", - "version": 13 - }, - "minecraft:spruce_slab[half=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:wooden_slab[half=top,variant=spruce]" - ] - ], - "nameEN": "Spruce slab (1.12)", - "nameZH": "云杉木上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:spruce_slab[type=top]": { - "burnable": true, - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Spruce slab", - "nameZH": "云杉木上半砖", - "version": 13 - }, - "minecraft:stone_brick_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=stone_brick]" - ] - ], - "nameEN": "Stonebrick slab (1.12)", - "nameZH": "石砖上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:stone_brick_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Stonebrick slab", - "nameZH": "石砖上半砖", - "version": 13 - }, - "minecraft:stone_slab[half=top]": { - "class": "slab", - "faces": [ - "up" - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone_slab[half=top,variant=stone]" - ] - ], - "nameEN": "Stone slab (1.12)", - "nameZH": "石头上半砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:stone_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Stone slab", - "nameZH": "石头上半砖", - "version": 13 - }, - "minecraft:waxed_cut_copper_slab[type=top]": { - "class": "slab", - "faces": [ - "up" - ], - "nameEN": "Waxed cut copper slab", - "nameZH": "涂蜡切制铜上半砖", - "version": 17, - "rare": true - }, - "minecraft:tuff": { - "class": "stone", - "nameEN": "Tuff", - "nameZH": "凝灰岩", - "version": 17, - "reproducible": false - }, - "minecraft:calcite": { - "class": "stone", - "nameEN": "Calcite", - "nameZH": "方解石", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:chiseled_deepslate": { - "class": "stone", - "nameEN": "Chiseled deepslate", - "nameZH": "錾制深板岩", - "version": 17, - "reproducible": false - }, - "minecraft:cobbled_deepslate": { - "class": "stone", - "nameEN": "Cobbled deepslate", - "nameZH": "深板岩圆石", - "version": 17, - "reproducible": false - }, - "minecraft:cobblestone": { - "class": "stone", - "nameEN": "Cobblestone", - "nameZH": "圆石", - "version": "all" - }, - "minecraft:mossy_cobblestone": { - "class": "stone", - "nameEN": "Mossy cobblestone", - "nameZH": "苔石", - "version": "all" - }, - "minecraft:deepslate[axis=y]": { - "class": "stone", - "nameEN": "Deepslate", - "nameZH": "深板岩", - "version": 17, - "reproducible": false - }, - "minecraft:deepslate_bricks": { - "class": "stone", - "nameEN": "Deepslate bricks", - "nameZH": "深板岩砖", - "version": 17, - "reproducible": false - }, - "minecraft:deepslate_tiles": { - "class": "stone", - "nameEN": "Deepslate tiles", - "nameZH": "深板岩瓦", - "version": 17, - "reproducible": false - }, - "minecraft:dripstone_block": { - "class": "stone", - "nameEN": "DripStone Block", - "nameZH": "滴水石块", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:obsidian": { - "class": "stone", - "nameEN": "Obsidian", - "nameZH": "黑曜石", - "version": "all" - }, - "minecraft:crying_obsidian": { - "class": "stone", - "nameEN": "Crying obsidian", - "nameZH": "哭泣的黑曜石", - "version": 16, - "rare": true - }, - "minecraft:polished_basalt[axis=y]": { - "class": "stone", - "nameEN": "Polished basalt", - "nameZH": "磨制玄武岩", - "version": 16, - "reproducible": false - }, - "minecraft:polished_blackstone": { - "class": "stone", - "nameEN": "Polished blackstone", - "nameZH": "磨制黑石", - "version": 16, - "reproducible": false - }, - "minecraft:polished_deepslate": { - "class": "stone", - "nameEN": "Polished deepslate", - "nameZH": "磨制深板岩", - "version": 17, - "reproducible": false - }, - "minecraft:polished_diorite": { - "class": "stone", - "nameEN": "Polished diorite", - "nameZH": "磨制闪长岩", - "version": 13, - "reproducible": false - }, - "minecraft:polished_granite": { - "class": "stone", - "nameEN": "Polished granite", - "nameZH": "磨制花岗岩", - "version": 13, - "reproducible": false - }, - "minecraft:quartz_block": { - "class": "ore", - "nameEN": "Quartz block", - "nameZH": "石英块", - "version": 13 - }, - "minecraft:quartz_block[variant=default]": { - "class": "ore", - "nameEN": "Quartz block (1.12)", - "nameZH": "石英块 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:smooth_stone": { - "class": "stone", - "nameEN": "Smooth stone", - "nameZH": "平滑石", - "version": 13 - }, - "minecraft:stone": { - "class": "stone", - "nameEN": "Stone", - "nameZH": "石头", - "version": 13 - }, - "minecraft:andesite": { - "class": "stone", - "nameEN": "Andesite", - "nameZH": "安山岩", - "version": "all", - "id_replace_list": [ - [ - 12, - "minecraft:stone[variant=andesite]" - ] - ], - "reproducible": false - }, - "minecraft:diorite": { - "class": "stone", - "nameEN": "Diorite", - "nameZH": "闪长岩", - "version": "all", - "id_replace_list": [ - [ - 12, - "minecraft:stone[variant=diorite]" - ] - ], - "reproducible": false - }, - "minecraft:granite": { - "class": "stone", - "nameEN": "Granite", - "nameZH": "花岗岩", - "version": "all", - "id_replace_list": [ - [ - 12, - "minecraft:stone[variant=granite]" - ] - ], - "reproducible": false - }, - "minecraft:smooth_andesite": { - "class": "stone", - "nameEN": "Polished andesite (1.12)", - "nameZH": "磨制安山岩 (1.12)", - "version": [ - 12 - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone[variant=smooth_andesite]" - ] - ], - "reproducible": false - }, - "minecraft:smooth_diorite": { - "class": "stone", - "nameEN": "Polished diorite (1.12)", - "nameZH": "磨制闪长岩 (1.12)", - "version": [ - 12 - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone[variant=smooth_diorite]" - ] - ], - "reproducible": false - }, - "minecraft:smooth_granite": { - "class": "stone", - "nameEN": "Polished granite (1.12)", - "nameZH": "磨制花岗岩 (1.12)", - "version": [ - 12 - ], - "id_replace_list": [ - [ - 12, - "minecraft:stone[variant=smooth_granite]" - ] - ], - "reproducible": false - }, - "minecraft:stone[variant=stone]": { - "class": "stone", - "nameEN": "Stone (1.12)", - "nameZH": "石头 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:stone_bricks": { - "class": "stone", - "nameEN": "Stone brick", - "nameZH": "石砖", - "version": 13 - }, - "minecraft:stonebrick[variant=stonebrick]": { - "class": "stone", - "nameEN": "Stone brick (1.12)", - "nameZH": "石砖 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:black_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=black]" - ] - ], - "nameEN": "Black hardened clay (1.12)", - "nameZH": "黑色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:black_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Black terracotta", - "nameZH": "黑色陶瓦", - "version": 13 - }, - "minecraft:blue_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=blue]" - ] - ], - "nameEN": "Blue hardened clay (1.12)", - "nameZH": "蓝色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:blue_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Blue terracotta", - "nameZH": "蓝色陶瓦", - "version": 13 - }, - "minecraft:brown_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=brown]" - ] - ], - "nameEN": "Brown hardened clay (1.12)", - "nameZH": "棕色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:brown_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Brown terracotta", - "nameZH": "棕色陶瓦", - "version": 13 - }, - "minecraft:cyan_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=cyan]" - ] - ], - "nameEN": "Cyan hardened clay (1.12)", - "nameZH": "青色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:cyan_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Cyan terracotta", - "nameZH": "青色陶瓦", - "version": 13 - }, - "minecraft:gray_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=gray]" - ] - ], - "nameEN": "Gray hardened clay (1.12)", - "nameZH": "灰色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:gray_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Gray terracotta", - "nameZH": "灰色陶瓦", - "version": 13 - }, - "minecraft:green_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=green]" - ] - ], - "nameEN": "Green hardened clay (1.12)", - "nameZH": "绿色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:green_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Green terracotta", - "nameZH": "绿色陶瓦", - "version": 13 - }, - "minecraft:hardened_clay": { - "background": true, - "class": "terracotta", - "nameEN": "Hardened clay (1.12)", - "nameZH": "硬化粘土 (1.12)", - "version": [ - 12 - ], - "reproducible": false - }, - "minecraft:light_blue_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=light_blue]" - ] - ], - "nameEN": "Light blue hardened clay (1.12)", - "nameZH": "浅蓝硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:light_blue_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Light blue terracotta", - "nameZH": "浅蓝陶瓦", - "version": 13 - }, - "minecraft:light_gray_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Light gray terracotta", - "nameZH": "浅灰陶瓦", - "version": 13 - }, - "minecraft:lime_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=lime]" - ] - ], - "nameEN": "Lime hardened clay (1.12)", - "nameZH": "浅绿硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:lime_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Lime terracotta", - "nameZH": "浅绿陶瓦", - "version": 13 - }, - "minecraft:magenta_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=magenta]" - ] - ], - "nameEN": "Magenta hardened clay (1.12)", - "nameZH": "品红硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:magenta_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Magenta terracotta", - "nameZH": "品红陶瓦", - "version": 13 - }, - "minecraft:orange_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=orange]" - ] - ], - "nameEN": "Orange hardened clay (1.12)", - "nameZH": "橙色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:orange_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Orange terracotta", - "nameZH": "橙色陶瓦", - "version": 13 - }, - "minecraft:pink_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=pink]" - ] - ], - "nameEN": "Pink hardened clay (1.12)", - "nameZH": "粉色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:pink_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Pink terracotta", - "nameZH": "粉色陶瓦", - "version": 13 - }, - "minecraft:purple_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=purple]" - ] - ], - "nameEN": "Purple hardened clay (1.12)", - "nameZH": "紫色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:purple_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Purple terracotta", - "nameZH": "紫色陶瓦", - "version": 13 - }, - "minecraft:red_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=red]" - ] - ], - "nameEN": "Red hardened clay (1.12)", - "nameZH": "红色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:red_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Red terracotta", - "nameZH": "红色陶瓦", - "version": 13 - }, - "minecraft:silver_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=silver]" - ] - ], - "nameEN": "Light gray hardened clay (1.12)", - "nameZH": "浅灰硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "Terracotta", - "nameZH": "陶瓦", - "version": 13, - "reproducible": false - }, - "minecraft:white_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=white]" - ] - ], - "nameEN": "White hardened clay (1.12)", - "nameZH": "白色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:white_terracotta": { - "background": true, - "class": "terracotta", - "nameEN": "White terracotta", - "nameZH": "白色陶瓦", - "version": 13 - }, - "minecraft:yellow_stained_hardened_clay": { - "background": true, - "class": "terracotta", - "id_replace_list": [ - [ - 12, - "minecraft:stained_hardened_clay[color=yellow]" - ] - ], - "nameEN": "Yellow hardened clay (1.12)", - "nameZH": "黄色硬化粘土 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:yellow_terracotta": { - "background": true, - "burnable": true, - "class": "terracotta", - "nameEN": "Yellow terracotta", - "nameZH": "黄色陶瓦", - "version": 13 - }, - "minecraft:end_bricks": { - "class": "the_end", - "nameEN": "Endstone bricks (1.12)", - "nameZH": "末地石砖 (1.12)", - "version": [ - 12 - ], - "reproducible": false - }, - "minecraft:end_stone": { - "class": "the_end", - "nameEN": "End stone", - "nameZH": "末地石", - "version": "all", - "reproducible": false - }, - "minecraft:end_stone_bricks": { - "class": "the_end", - "nameEN": "Endstone bricks", - "nameZH": "末地石砖", - "version": 13, - "reproducible": false - }, - "minecraft:purpur_block": { - "class": "the_end", - "nameEN": "Purpur block", - "nameZH": "紫珀块", - "version": "all" - }, - "minecraft:acacia_log[axis=y]": { - "class": "wood", - "nameEN": "Acacia log (Y axis)", - "nameZH": "金合欢原木 (Y 轴)", - "version": "all", - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log2[variant=acacia,axis=y]" - ] - ] - }, - "minecraft:acacia_log[axis=x]": { - "class": "wood", - "nameEN": "Acacia log (X axis)", - "nameZH": "金合欢原木 (X 轴)", - "version": "all", - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log2[variant=acacia,axis=x]" - ] - ] - }, - "minecraft:acacia_log[axis=z]": { - "class": "wood", - "nameEN": "Acacia log (Z axis)", - "nameZH": "金合欢原木 (Z 轴)", - "version": "all", - "faces": [ - "north", - "south" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log2[variant=acacia,axis=z]" - ] - ] - }, - "minecraft:dark_oak_log[axis=y]": { - "class": "wood", - "nameEN": "Dark oak log (Y axis)", - "nameZH": "黑橡树原木 (Y 轴)", - "version": "all", - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log2[variant=dark_oak,axis=y]" - ] - ] - }, - "minecraft:dark_oak_log[axis=x]": { - "class": "wood", - "nameEN": "Dark oak log (X axis)", - "nameZH": "黑橡树原木 (X 轴)", - "version": "all", - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log2[variant=dark_oak,axis=x]" - ] - ] - }, - "minecraft:dark_oak_log[axis=z]": { - "class": "wood", - "nameEN": "Dark oak log (Z axis)", - "nameZH": "黑橡树原木 (Z 轴)", - "version": "all", - "faces": [ - "north", - "south" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log2[variant=dark_oak,axis=z]" - ] - ] - }, - "minecraft:jungle_log[axis=y]": { - "class": "wood", - "nameEN": "Jungle log (Y axis)", - "nameZH": "丛林木原木 (Y 轴)", - "version": "all", - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=jungle,axis=y]" - ] - ] - }, - "minecraft:jungle_log[axis=x]": { - "class": "wood", - "nameEN": "Jungle log (X axis)", - "nameZH": "丛林木原木 (X 轴)", - "version": "all", - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=jungle,axis=x]" - ] - ] - }, - "minecraft:jungle_log[axis=z]": { - "class": "wood", - "nameEN": "Jungle log (Z axis)", - "nameZH": "丛林木原木 (Z 轴)", - "version": "all", - "faces": [ - "north", - "south" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=jungle,axis=z]" - ] - ] - }, - "minecraft:birch_log[axis=y]": { - "class": "wood", - "nameEN": "Birch log (Y axis)", - "nameZH": "白桦原木 (Y 轴)", - "version": "all", - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=birch,axis=y]" - ] - ] - }, - "minecraft:birch_log[axis=x]": { - "class": "wood", - "nameEN": "Birch log (X axis)", - "nameZH": "白桦原木 (X 轴)", - "version": "all", - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=birch,axis=x]" - ] - ] - }, - "minecraft:birch_log[axis=z]": { - "class": "wood", - "nameEN": "Birch log (Z axis)", - "nameZH": "白桦原木 (Z 轴)", - "version": "all", - "faces": [ - "north", - "south" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=birch,axis=z]" - ] - ] - }, - "minecraft:spruce_log[axis=y]": { - "class": "wood", - "nameEN": "Spruce log (Y axis)", - "nameZH": "云杉原木 (Y 轴)", - "version": "all", - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=spruce,axis=y]" - ] - ] - }, - "minecraft:spruce_log[axis=x]": { - "class": "wood", - "nameEN": "Spruce log (X axis)", - "nameZH": "云杉原木 (X 轴)", - "version": "all", - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=spruce,axis=x]" - ] - ] - }, - "minecraft:spruce_log[axis=z]": { - "class": "wood", - "nameEN": "Spruce log (Z axis)", - "nameZH": "云杉原木 (Z 轴)", - "version": "all", - "faces": [ - "north", - "south" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=spruce,axis=z]" - ] - ] - }, - "minecraft:oak_log[axis=y]": { - "class": "wood", - "nameEN": "Oak log (Y axis)", - "nameZH": "橡木原木 (Y 轴)", - "version": "all", - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=oak,axis=y]" - ] - ] - }, - "minecraft:oak_log[axis=x]": { - "class": "wood", - "nameEN": "Oak log (X axis)", - "nameZH": "橡木原木 (X 轴)", - "version": "all", - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=oak,axis=x]" - ] - ] - }, - "minecraft:oak_log[axis=z]": { - "class": "wood", - "nameEN": "Oak log (Z axis)", - "nameZH": "橡木原木 (Z 轴)", - "version": "all", - "faces": [ - "north", - "south" - ], - "burnable": true, - "id_replace_list": [ - [ - 12, - "minecraft:log[variant=oak,axis=z]" - ] - ] - }, - "minecraft:mangrove_log[axis=y]": { - "class": "wood", - "nameEN": "Mangrove log (Y axis)", - "nameZH": "红树原木 (Y 轴)", - "version": 19, - "burnable": true - }, - "minecraft:mangrove_log[axis=x]": { - "class": "wood", - "nameEN": "Mangrove log (X axis)", - "nameZH": "红树原木 (X 轴)", - "version": 19, - "faces": [ - "east", - "west", - "up", - "down" - ], - "burnable": true - }, - "minecraft:mangrove_log[axis=z]": { - "class": "wood", - "nameEN": "Mangrove log (Z axis)", - "nameZH": "红树原木 (Z 轴)", - "version": 19, - "faces": [ - "north", - "south" - ], - "burnable": true - }, - "minecraft:crimson_hyphae[axis=y]": { - "burnable": true, - "class": "wood", - "nameEN": "Crimson hyphae", - "nameZH": "绯红树皮", - "version": 16 - }, - "minecraft:stripped_crimson_hyphae[axis=y]": { - "burnable": true, - "class": "wood", - "nameEN": "Stripped crimson hyphae", - "nameZH": "去皮绯红树皮", - "version": 16 - }, - "minecraft:stripped_crimson_stem[axis=y]": { - "burnable": true, - "class": "wood", - "nameEN": "Stripped crimson log", - "nameZH": "去皮绯红原木", - "version": 16 - }, - "minecraft:stripped_warped_hyphae[axis=y]": { - "burnable": true, - "class": "wood", - "nameEN": "Stripped warped hyphae", - "nameZH": "去皮扭曲树皮", - "version": 16 - }, - "minecraft:stripped_warped_stem[axis=y]": { - "burnable": true, - "class": "wood", - "nameEN": "Stripped warped log", - "nameZH": "去皮扭曲原木", - "version": 16 - }, - "minecraft:warped_hyphae[axis=y]": { - "burnable": true, - "class": "wood", - "nameEN": "Warped hyphae", - "nameZH": "扭曲树皮", - "version": 16 - }, - "minecraft:black_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=black]" - ] - ], - "nameEN": "Black wool", - "nameZH": "黑色羊毛", - "version": "all" - }, - "minecraft:blue_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=blue]" - ] - ], - "nameEN": "Blue wool", - "nameZH": "蓝色羊毛", - "version": "all" - }, - "minecraft:brown_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=brown]" - ] - ], - "nameEN": "Brown wool", - "nameZH": "棕色羊毛", - "version": "all" - }, - "minecraft:cyan_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=cyan]" - ] - ], - "nameEN": "Cyan wool", - "nameZH": "青色羊毛", - "version": "all" - }, - "minecraft:gray_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=gray]" - ] - ], - "nameEN": "Gray wool", - "nameZH": "灰色羊毛", - "version": "all" - }, - "minecraft:green_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=green]" - ] - ], - "nameEN": "Green wool", - "nameZH": "绿色羊毛", - "version": "all" - }, - "minecraft:light_blue_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=light_blue]" - ] - ], - "nameEN": "light blue wool", - "nameZH": "浅蓝羊毛", - "version": "all" - }, - "minecraft:light_gray_wool": { - "background": true, - "burnable": true, - "class": "wool", - "nameEN": "Light gray wool", - "nameZH": "浅灰羊毛", - "version": 13 - }, - "minecraft:lime_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=lime]" - ] - ], - "nameEN": "Lime wool", - "nameZH": "浅绿羊毛", - "version": "all" - }, - "minecraft:magenta_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=magenta]" - ] - ], - "nameEN": "Magenta wool", - "nameZH": "品红色羊毛", - "version": "all" - }, - "minecraft:orange_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=orange]" - ] - ], - "nameEN": "Orange wool", - "nameZH": "橙色羊毛", - "version": "all" - }, - "minecraft:pink_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=pink]" - ] - ], - "nameEN": "Pink wool", - "nameZH": "粉色羊毛", - "version": "all" - }, - "minecraft:purple_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=purple]" - ] - ], - "nameEN": "Purple wool", - "nameZH": "紫色羊毛", - "version": "all" - }, - "minecraft:red_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=red]" - ] - ], - "nameEN": "Red wool", - "nameZH": "红色羊毛", - "version": "all" - }, - "minecraft:silver_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=silver]" - ] - ], - "nameEN": "Light gray wool (1.12)", - "nameZH": "浅灰羊毛 (1.12)", - "version": [ - 12 - ] - }, - "minecraft:white_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=white]" - ] - ], - "nameEN": "White wool", - "nameZH": "白色羊毛", - "version": "all" - }, - "minecraft:yellow_wool": { - "background": true, - "burnable": true, - "class": "wool", - "id_replace_list": [ - [ - 12, - "minecraft:wool[color=yellow]" - ] - ], - "nameEN": "Yellow wool", - "nameZH": "黄色羊毛", - "version": "all" - }, - "minecraft:brown_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]": { - "nameEN": "Brown mushroom block", - "nameZH": "棕色蘑菇块", - "class": "mushroom", - "version": 13 - }, - "minecraft:brown_mushroom_block[variant=all_outside]": { - "nameEN": "Brown mushroom block (1.12)", - "nameZH": "棕色蘑菇块 (1.12)", - "class": "mushroom", - "version": [ - 12 - ] - }, - "minecraft:mushroom_stem[east=true,west=true,north=true,south=true,up=true,down=true]": { - "burnable": true, - "nameEN": "Mushroom stem", - "nameZH": "蘑菇柄", - "class": "mushroom", - "version": 13 - }, - "minecraft:red_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]": { - "burnable": true, - "nameEN": "Red mushroom block", - "nameZH": "红色蘑菇块", - "class": "mushroom", - "version": 13 - }, - "minecraft:red_mushroom_block[variant=all_outside]": { - "burnable": true, - "nameEN": "Red mushroom block (1.12)", - "nameZH": "红色蘑菇块 (1.12)", - "class": "mushroom", - "version": [ - 12 - ] - }, - "minecraft:white_glazed_terracotta[facing=north]": { - "nameEN": "White glazed terracotta", - "nameZH": "白色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:orange_glazed_terracotta[facing=north]": { - "nameEN": "Orange glazed terracotta", - "nameZH": "橙色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:magenta_glazed_terracotta[facing=north]": { - "nameEN": "Magenta glazed terracotta", - "nameZH": "品红色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:light_blue_glazed_terracotta[facing=north]": { - "nameEN": "Light blue glazed terracotta", - "nameZH": "浅蓝色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:yellow_glazed_terracotta[facing=north]": { - "nameEN": "Yellow glazed terracotta", - "nameZH": "黄色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:lime_glazed_terracotta[facing=north]": { - "nameEN": "Lime glazed terracotta", - "nameZH": "浅绿色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:pink_glazed_terracotta[facing=north]": { - "nameEN": "Pink glazed terracotta", - "nameZH": "粉色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:gray_glazed_terracotta[facing=north]": { - "nameEN": "Gray glazed terracotta", - "nameZH": "灰色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:light_gray_glazed_terracotta[facing=north]": { - "nameEN": "Light gray glazed terracotta", - "nameZH": "浅灰色带釉陶瓦", - "class": "glazed_terracotta", - "version": 13, - "background": true - }, - "minecraft:silver_glazed_terracotta[facing=north]": { - "nameEN": "Silver glazed hardened clay (1.12)", - "nameZH": "银色带釉硬化粘土 (1.12)", - "class": "glazed_terracotta", - "version": [ - 12 - ], - "background": true - }, - "minecraft:cyan_glazed_terracotta[facing=north]": { - "nameEN": "Cyan glazed terracotta", - "nameZH": "青色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:blue_glazed_terracotta[facing=north]": { - "nameEN": "Blue glazed terracotta", - "nameZH": "蓝色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:brown_glazed_terracotta[facing=north]": { - "nameEN": "Brown glazed terracotta", - "nameZH": "棕色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:red_glazed_terracotta[facing=north]": { - "nameEN": "Red glazed terracotta", - "nameZH": "红色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:black_glazed_terracotta[facing=north]": { - "nameEN": "Black glazed terracotta", - "nameZH": "黑色带釉陶瓦", - "class": "glazed_terracotta", - "version": "all", - "background": true - }, - "minecraft:white_concrete_powder": { - "nameEN": "White concrete powder", - "nameZH": "白色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=white]" - ] - ] - }, - "minecraft:orange_concrete_powder": { - "nameEN": "Orange concrete powder", - "nameZH": "橙色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=orange]" - ] - ] - }, - "minecraft:magenta_concrete_powder": { - "nameEN": "Magenta concrete powder", - "nameZH": "品红色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=magenta]" - ] - ] - }, - "minecraft:light_blue_concrete_powder": { - "nameEN": "Light blue concrete powder", - "nameZH": "浅蓝色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=light_blue]" - ] - ] - }, - "minecraft:yellow_concrete_powder": { - "nameEN": "Yellow concrete powder", - "nameZH": "黄色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=yellow]" - ] - ] - }, - "minecraft:lime_concrete_powder": { - "nameEN": "Lime concrete powder", - "nameZH": "浅绿色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=lime]" - ] - ] - }, - "minecraft:pink_concrete_powder": { - "nameEN": "Pink concrete powder", - "nameZH": "粉色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=pink]" - ] - ] - }, - "minecraft:gray_concrete_powder": { - "nameEN": "Gray concrete powder", - "nameZH": "灰色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=gray]" - ] - ] - }, - "minecraft:light_gray_concrete_powder": { - "nameEN": "Light gray concrete powder", - "nameZH": "浅灰色混凝土粉末", - "class": "concrete_powder", - "version": 13, - "faces": [ - "up", - "north", - "south", - "east", - "west" - ] - }, - "minecraft:silver_concrete_powder": { - "nameEN": "Silver concrete powder (1.12)", - "nameZH": "银色混凝土粉末 (1.12)", - "class": "concrete_powder", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "version": [ - 12 - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=silver]" - ] - ] - }, - "minecraft:cyan_concrete_powder": { - "nameEN": "Cyan concrete powder", - "nameZH": "青色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=cyan]" - ] - ] - }, - "minecraft:blue_concrete_powder": { - "nameEN": "Blue concrete powder", - "nameZH": "蓝色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=blue]" - ] - ] - }, - "minecraft:brown_concrete_powder": { - "nameEN": "Brown concrete powder", - "nameZH": "棕色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=brown]" - ] - ] - }, - "minecraft:red_concrete_powder": { - "nameEN": "Red concrete powder", - "nameZH": "红色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=red]" - ] - ] - }, - "minecraft:black_concrete_powder": { - "nameEN": "Black concrete powder", - "nameZH": "黑色混凝土粉末", - "class": "concrete_powder", - "version": "all", - "faces": [ - "up", - "north", - "south", - "east", - "west" - ], - "id_replace_list": [ - [ - 12, - "minecraft:concrete_powder[color=black]" - ] - ] - }, - "minecraft:bedrock": { - "nameEN": "Bedrock", - "nameZH": "基岩", - "class": "creative_only", - "version": "all", - "reproducible": false, - "rare": true - }, - "minecraft:budding_amethyst": { - "nameEN": "Budding amethyst", - "nameZH": "紫水晶母岩", - "class": "creative_only", - "version": 17, - "reproducible": false, - "rare": true - }, - "minecraft:reinforced_deepslate": { - "nameEN": "Reinforced deepslate", - "nameZH": "强化深板岩", - "class": "creative_only", - "version": 19, - "reproducible": false, - "rare": true - } - /*, - "minecraft:beacon": { - "nameEN": "Beacon", - "nameZH": "信标", - "class": "others", - "version": "all" - }*/ + "minecraft:air": { + "class": "others", + "nameEN": "Air", + "nameZH": "空气", + "version": "all", + "is_air": true + }, + "minecraft:brick_block": { + "class": "clay", + "nameEN": "Bricks (1.12)", + "nameZH": "砖块 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:bricks": { + "class": "clay", + "nameEN": "Bricks", + "nameZH": "砖块", + "version": 13 + }, + "minecraft:clay": { + "class": "clay", + "endermanPickable": true, + "nameEN": "Clay block", + "nameZH": "黏土块", + "version": "all" + }, + "minecraft:black_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=black]" + ] + ], + "nameEN": "Black concrete", + "nameZH": "黑色混凝土", + "version": "all" + }, + "minecraft:blue_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=blue]" + ] + ], + "nameEN": "Blue concrete", + "nameZH": "蓝色混凝土", + "version": "all" + }, + "minecraft:brown_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=brown]" + ] + ], + "nameEN": "Brown concrete", + "nameZH": "棕色混凝土", + "version": "all" + }, + "minecraft:cyan_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=cyan]" + ] + ], + "nameEN": "Cyan concrete", + "nameZH": "青色混凝土", + "version": "all" + }, + "minecraft:gray_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=gray]" + ] + ], + "nameEN": "Gray concrete", + "nameZH": "灰色混凝土", + "version": "all" + }, + "minecraft:green_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=green]" + ] + ], + "nameEN": "Green concrete", + "nameZH": "绿色混凝土", + "version": "all" + }, + "minecraft:light_blue_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=light_blue]" + ] + ], + "nameEN": "Light blue concrete", + "nameZH": "浅蓝混凝土", + "version": "all" + }, + "minecraft:light_gray_concrete": { + "background": true, + "class": "concrete", + "nameEN": "Light gray concrete", + "nameZH": "浅灰混凝土", + "version": 13 + }, + "minecraft:lime_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=lime]" + ] + ], + "nameEN": "Lime concrete", + "nameZH": "浅绿混凝土", + "version": "all" + }, + "minecraft:magenta_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=magenta]" + ] + ], + "nameEN": "Magenta concrete", + "nameZH": "品红色混凝土", + "version": "all" + }, + "minecraft:orange_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=orange]" + ] + ], + "nameEN": "Orange concrete", + "nameZH": "橙色混凝土", + "version": "all" + }, + "minecraft:pink_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=pink]" + ] + ], + "nameEN": "Pink concrete", + "nameZH": "粉色混凝土", + "version": "all" + }, + "minecraft:purple_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=purple]" + ] + ], + "nameEN": "Purple concrete", + "nameZH": "紫色混凝土", + "version": "all" + }, + "minecraft:red_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=red]" + ] + ], + "nameEN": "Red concrete", + "nameZH": "红色混凝土", + "version": "all" + }, + "minecraft:silver_concrete": { + "background": true, + "class": "concrete", + "nameEN": "Silver concrete (1.12)", + "nameZH": "银色混凝土 (1.12)", + "version": [ + 12 + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=silver]" + ] + ] + }, + "minecraft:white_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=white]" + ] + ], + "nameEN": "White concrete", + "nameZH": "白色混凝土", + "version": "all" + }, + "minecraft:yellow_concrete": { + "background": true, + "class": "concrete", + "id_replace_list": [ + [ + 12, + "minecraft:concrete[color=yellow]" + ] + ], + "nameEN": "Yellow concrete", + "nameZH": "黄色混凝土", + "version": "all" + }, + "minecraft:hay_block[axis=y]": { + "class": "crafted", + "nameEN": "Hay block", + "nameZH": "干草块", + "version": "all" + }, + "minecraft:crafting_table": { + "class": "crafted", + "nameEN": "Crafting table", + "nameZH": "工作台", + "version": "all", + "burnable": true + }, + "minecraft:cartography_table": { + "class": "crafted", + "nameEN": "Cartography table", + "nameZH": "制图台", + "version": 14, + "burnable": true + }, + "minecraft:fletching_table": { + "class": "crafted", + "nameEN": "Fletching table", + "nameZH": "制箭台", + "version": 14, + "burnable": true + }, + "minecraft:smithing_table": { + "class": "crafted", + "nameEN": "Smithing table", + "nameZH": "锻造台", + "version": 14, + "burnable": true + }, + "minecraft:lodestone": { + "class": "crafted", + "nameEN": "Lodestone", + "nameZH": "磁石", + "version": 16, + "rare": true + }, + "minecraft:composter[level=0]": { + "class": "crafted", + "nameEN": "Composter", + "nameZH": "堆肥桶", + "version": 14, + "burnable": true + }, + "minecraft:note_block[note=0,powered=false]": { + "class": "crafted", + "nameEN": "Note block", + "nameZH": "音符盒", + "version": 14, + "burnable": true + }, + "minecraft:loom[facing=north]": { + "class": "crafted", + "nameEN": "Loom(north)", + "nameZH": "织布机(向北)", + "version": 14, + "burnable": true + }, + "minecraft:loom[facing=east]": { + "class": "crafted", + "nameEN": "Loom(east)", + "nameZH": "织布机(向东)", + "version": 14, + "burnable": true, + "faces": [ + "east", + "north" + ] + }, + "minecraft:loom[facing=south]": { + "class": "crafted", + "nameEN": "Loom(south)", + "nameZH": "织布机(向南)", + "version": 14, + "burnable": true, + "faces": [ + "south" + ] + }, + "minecraft:loom[facing=west]": { + "class": "crafted", + "nameEN": "Loom(west)", + "nameZH": "织布机(向西)", + "version": 14, + "burnable": true, + "faces": [ + "west" + ] + }, + "minecraft:smoker[facing=north,lit=false]": { + "class": "crafted", + "nameEN": "Smoker(north)", + "nameZH": "烟熏炉(向北)", + "version": 14 + }, + "minecraft:smoker[facing=east,lit=false]": { + "class": "crafted", + "nameEN": "Smoker(east)", + "nameZH": "烟熏炉(向东)", + "version": 14, + "faces": [ + "east", + "north" + ] + }, + "minecraft:smoker[facing=south,lit=false]": { + "class": "crafted", + "nameEN": "Smoker(south)", + "nameZH": "烟熏炉(向南)", + "version": 14, + "faces": [ + "south" + ] + }, + "minecraft:smoker[facing=west,lit=false]": { + "class": "crafted", + "nameEN": "Smoker(west)", + "nameZH": "烟熏炉(向西)", + "version": 14, + "faces": [ + "west" + ] + }, + "minecraft:furnace[facing=north,lit=false]": { + "class": "crafted", + "nameEN": "Furnace(north)", + "nameZH": "熔炉(向北)", + "version": "all" + }, + "minecraft:furnace[facing=east,lit=false]": { + "class": "crafted", + "nameEN": "Furnace(east)", + "nameZH": "熔炉(向东)", + "version": "all", + "faces": [ + "east", + "north" + ] + }, + "minecraft:furnace[facing=south,lit=false]": { + "class": "crafted", + "nameEN": "Furnace(south)", + "nameZH": "熔炉(向南)", + "version": "all", + "faces": [ + "south" + ] + }, + "minecraft:furnace[facing=west,lit=false]": { + "class": "crafted", + "nameEN": "Furnace(west)", + "nameZH": "熔炉(向西)", + "version": "all", + "faces": [ + "west" + ] + }, + "minecraft:blast_furnace[facing=north,lit=false]": { + "class": "crafted", + "nameEN": "Blast furnace(north)", + "nameZH": "高炉(向北)", + "version": 14 + }, + "minecraft:blast_furnace[facing=east,lit=false]": { + "class": "crafted", + "nameEN": "Blast furnace(east)", + "nameZH": "高炉(向东)", + "version": 14, + "faces": [ + "east", + "north" + ] + }, + "minecraft:blast_furnace[facing=south,lit=false]": { + "class": "crafted", + "nameEN": "Blast furnace(south)", + "nameZH": "高炉(向南)", + "version": 14, + "faces": [ + "south" + ] + }, + "minecraft:blast_furnace[facing=west,lit=false]": { + "class": "crafted", + "nameEN": "Blast furnace(west)", + "nameZH": "高炉(向西)", + "version": 14, + "faces": [ + "west" + ] + }, + "minecraft:beehive[facing=north,honey_level=0]": { + "class": "crafted", + "nameEN": "Bee hive(north)", + "nameZH": "蜂箱(向北)", + "version": 15, + "burnable": true + }, + "minecraft:beehive[facing=east,honey_level=0]": { + "class": "crafted", + "nameEN": "Bee hive(east)", + "nameZH": "蜂箱(向东)", + "version": 15, + "burnable": true, + "faces": [ + "east", + "north" + ] + }, + "minecraft:beehive[facing=south,honey_level=0]": { + "class": "crafted", + "nameEN": "Bee hive(south)", + "nameZH": "蜂箱(向南)", + "version": 15, + "burnable": true, + "faces": [ + "south" + ] + }, + "minecraft:beehive[facing=west,honey_level=0]": { + "class": "crafted", + "nameEN": "Bee hive(west)", + "nameZH": "蜂箱(向西)", + "version": 15, + "burnable": true, + "faces": [ + "west" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=up_north]": { + "class": "crafted", + "nameEN": "Crafter(up north)", + "nameZH": "合成器(正面向上,顶面向北)", + "version": 21, + "faces": [ + "up", + "down", + "north", + "south" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=up_west]": { + "class": "crafted", + "nameEN": "Crafter(up west)", + "nameZH": "合成器(正面向上,顶面向西)", + "version": 21, + "faces": [ + "north", + "south", + "east", + "west" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=up_south]": { + "class": "crafted", + "nameEN": "Crafter(up south)", + "nameZH": "合成器(正面向上,顶面向南)", + "version": 21, + "faces": [ + "north", + "south" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=up_east]": { + "class": "crafted", + "nameEN": "Crafter(up east)", + "nameZH": "合成器(正面向上,顶面向东)", + "version": 21, + "faces": [ + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=south_up]": { + "class": "crafted", + "nameEN": "Crafter(south up)", + "nameZH": "合成器(正面向南,顶面向上)", + "version": 21, + "faces": [ + "up", + "down", + "north" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=east_up]": { + "class": "crafted", + "nameEN": "Crafter(east up)", + "nameZH": "合成器(正面向东,顶面向上)", + "version": 21, + "faces": [ + "east", + "west" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=north_up]": { + "class": "crafted", + "nameEN": "Crafter(north up)", + "nameZH": "合成器(正面向北,顶面向上)", + "version": 21, + "faces": [ + "south" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=west_up]": { + "class": "crafted", + "nameEN": "Crafter(west up)", + "nameZH": "合成器(正面向西,顶面向上)", + "version": 21, + "faces": [] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=down_south]": { + "class": "crafted", + "nameEN": "Crafter(down south)", + "nameZH": "合成器(正面向下,顶面向南)", + "version": 21, + "faces": [ + "up", + "down" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=down_east]": { + "class": "crafted", + "nameEN": "Crafter(down east)", + "nameZH": "合成器(正面向下,顶面向东)", + "version": 21, + "faces": [ + "east", + "west" + ] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=down_north]": { + "class": "crafted", + "nameEN": "Crafter(down north)", + "nameZH": "合成器(正面向下,顶面向北)", + "version": 21, + "faces": [] + }, + "minecraft:crafter[crafting=false,triggered=false,orientation=down_west]": { + "class": "crafted", + "nameEN": "Crafter(down west)", + "nameZH": "合成器(正面向下,顶面向西)", + "version": 21, + "faces": [] + }, + "minecraft:coarse_dirt": { + "class": "desert", + "endermanPickable": true, + "nameEN": "Coarse dirt", + "nameZH": "砂土", + "version": 13 + }, + "minecraft:red_sandstone[type=smooth_red_sandstone]": { + "class": "desert", + "nameEN": "Smooth red sandstone", + "nameZH": "平滑红砂岩 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:sandstone[type=smooth_sandstone]": { + "class": "desert", + "nameEN": "Smooth sandstone", + "nameZH": "平滑砂岩 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:smooth_red_sandstone": { + "class": "desert", + "nameEN": "Smooth red sandstone", + "nameZH": "平滑红砂岩", + "version": 13 + }, + "minecraft:smooth_sandstone": { + "class": "desert", + "nameEN": "Smooth sandstone", + "nameZH": "平滑砂岩", + "version": 13 + }, + "minecraft:black_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=black]" + ] + ], + "nameEN": "Black glass", + "nameZH": "黑色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:blue_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=blue]" + ] + ], + "nameEN": "Blue glass", + "nameZH": "蓝色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:brown_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=brown]" + ] + ], + "nameEN": "Brown glass", + "nameZH": "棕色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:cyan_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=cyan]" + ] + ], + "nameEN": "Cyan glass", + "nameZH": "青色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:gray_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=gray]" + ] + ], + "nameEN": "Gray glass", + "nameZH": "灰色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:green_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=green]" + ] + ], + "nameEN": "Green glass", + "nameZH": "绿色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:light_blue_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=light]" + ] + ], + "nameEN": "Light blue glass", + "nameZH": "浅蓝染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:light_gray_stained_glass": { + "class": "glass", + "nameEN": "Light gray glass", + "nameZH": "浅灰染色玻璃", + "transparent": true, + "version": 13 + }, + "minecraft:lime_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=lime]" + ] + ], + "nameEN": "Lime glass", + "nameZH": "浅绿染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:magenta_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=magenta]" + ] + ], + "nameEN": "Magenta glass", + "nameZH": "品红染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:orange_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=orange]" + ] + ], + "nameEN": "Orange glass", + "nameZH": "橙色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:pink_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=pink]" + ] + ], + "nameEN": "Pink glass", + "nameZH": "粉色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:purple_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=purple]" + ] + ], + "nameEN": "Purple glass", + "nameZH": "紫色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:red_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=red]" + ] + ], + "nameEN": "Red glass", + "nameZH": "红色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:silver_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=silver]" + ] + ], + "nameEN": "Light gray glass (1.12)", + "nameZH": "浅灰染色玻璃 (1.12)", + "transparent": true, + "version": [ + 12 + ] + }, + "minecraft:tinted_glass": { + "class": "glass", + "nameEN": "Tinted glass", + "nameZH": "遮光玻璃", + "transparent": true, + "version": 17 + }, + "minecraft:white_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=white]" + ] + ], + "nameEN": "White glass", + "nameZH": "白色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:yellow_stained_glass": { + "class": "glass", + "id_replace_list": [ + [ + 12, + "minecraft:stained_glass[color=yellow]" + ] + ], + "nameEN": "yellow glass", + "nameZH": "黄色染色玻璃", + "transparent": true, + "version": "all" + }, + "minecraft:acacia_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "id_replace_list": [ + [ + 12, + "minecraft:leaves2[variant=acacia,check_decay=false,decayable=false]" + ] + ], + "nameEN": "Acacia leaves", + "nameZH": "金合欢树叶", + "version": "all", + "is_foliage": true + }, + "minecraft:birch_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "id_replace_list": [ + [ + 12, + "minecraft:leaves[variant=birch,check_decay=false,decayable=false]" + ] + ], + "nameEN": "Birch leaves", + "nameZH": "白桦树叶", + "version": "all", + "is_foliage": true + }, + "minecraft:dark_oak_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "id_replace_list": [ + [ + 12, + "minecraft:leaves2[variant=dark_oak,check_decay=false,decayable=false]" + ] + ], + "nameEN": "Dark oat leaves", + "nameZH": "黑橡树树叶", + "version": "all", + "is_foliage": true + }, + "minecraft:jungle_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "id_replace_list": [ + [ + 12, + "minecraft:leaves[variant=jungle,check_decay=false,decayable=false]" + ] + ], + "nameEN": "Jungle leaves", + "nameZH": "丛林木树叶", + "version": "all", + "is_foliage": true + }, + "minecraft:oak_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "id_replace_list": [ + [ + 12, + "minecraft:leaves[variant=oak,check_decay=false,decayable=false]" + ] + ], + "nameEN": "Oak leaves", + "nameZH": "橡树树叶", + "version": "all", + "is_foliage": true + }, + "minecraft:spruce_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "id_replace_list": [ + [ + 12, + "minecraft:leaves[variant=spruce,check_decay=false,decayable=false]" + ] + ], + "nameEN": "Spurce leaves", + "nameZH": "云杉树叶", + "version": "all", + "is_foliage": true + }, + "minecraft:mangrove_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "nameEN": "Mangrove leaves", + "nameZH": "红树树叶", + "version": 19, + "is_foliage": true + }, + // Cherry, azalea(and flowering), pale leaves don't use coloring mechanism, so is_foliage is set to false + "minecraft:cherry_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "nameEN": "Cherry leaves", + "nameZH": "樱花树叶", + "version": 20, + "is_foliage": false + }, + "minecraft:azalea_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "nameEN": "Azalea leaves", + "nameZH": "杜鹃树叶", + "version": 17, + "is_foliage": false + }, + "minecraft:flowering_azalea_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "nameEN": "Flowering azalea leaves", + "nameZH": "盛开杜鹃树叶", + "version": 17, + "is_foliage": false + }, + "minecraft:pale_oak_leaves[distance=7,persistent=true]": { + "burnable": true, + "class": "leaves", + "nameEN": "Pale oak leaves", + "nameZH": "苍白树叶", + "version": 21, + "is_foliage": false + }, + "minecraft:blue_ice": { + "class": "natural", + "nameEN": "Blue ice", + "nameZH": "蓝冰", + "version": 13 + }, + "minecraft:dirt": { + "class": "natural", + "endermanPickable": true, + "nameEN": "Dirt", + "nameZH": "泥土", + "version": 13 + }, + "minecraft:dirt[variant=coarse_dirt,snowy=false]": { + "class": "natural", + "endermanPickable": true, + "nameEN": "Coarse dirt (1.12)", + "nameZH": "砂土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:dirt[variant=dirt,snowy=false]": { + "class": "natural", + "endermanPickable": true, + "nameEN": "Dirt (1.12)", + "nameZH": "泥土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:dirt[variant=podzol,snowy=false]": { + "class": "natural", + "endermanPickable": true, + "nameEN": "Podzol (1.12)", + "nameZH": "灰化土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:grass[snowy=false]": { + "class": "natural", + "endermanPickable": true, + "faces": [ + "up" + ], + "nameEN": "Grass block (1.12)", + "nameZH": "草方块 (1.12)", + "version": [ + 12 + ], + "is_grass": true + }, + "minecraft:grass_block[snowy=false]": { + "class": "natural", + "endermanPickable": true, + "faces": [ + "up" + ], + "nameEN": "Grass block", + "nameZH": "草方块", + "version": 13, + "is_grass": true + }, + "minecraft:honeycomb_block": { + "class": "natural", + "nameEN": "Honey comb block", + "nameZH": "蜜脾块", + "version": 15 + }, + "minecraft:ice": { + "class": "natural", + "nameEN": "Ice", + "nameZH": "冰", + "transparent": true, + "version": "all" + }, + "minecraft:mud": { + "class": "natural", + "nameEN": "Mud", + "nameZH": "泥巴", + "version": 19 + }, + "minecraft:mud_bricks": { + "class": "natural", + "nameEN": "Mud bricks", + "nameZH": "泥砖", + "version": 19 + }, + "minecraft:mycelium[snowy=false]": { + "class": "natural", + "faces": [ + "up" + ], + "nameEN": "Mycelium", + "nameZH": "菌丝", + "version": "all" + }, + "minecraft:ochre_froglight[axis=y]": { + "class": "natural", + "isGlowing": true, + "nameEN": "Ochre Froglight", + "nameZH": "黄色蛙鸣灯", + "version": 19, + "rare": true + }, + "minecraft:packed_ice": { + "class": "natural", + "nameEN": "Packed ice", + "nameZH": "浮冰", + "version": "all" + }, + "minecraft:packed_mud": { + "class": "natural", + "nameEN": "Packed mud", + "nameZH": "泥坯", + "version": 19 + }, + "minecraft:pearlescent_froglight[axis=y]": { + "class": "natural", + "isGlowing": true, + "nameEN": "Verdant Froglight", + "nameZH": "紫色蛙鸣灯", + "version": 19, + "rare": true + }, + "minecraft:podzol[snowy=false]": { + "class": "natural", + "endermanPickable": true, + "nameEN": "Podzol", + "nameZH": "灰化土", + "version": 13 + }, + "minecraft:pumpkin": { + "class": "natural", + "endermanPickable": true, + "nameEN": "Pumpkin", + "nameZH": "南瓜", + "version": 13 + }, + "minecraft:pumpkin[facing=east]": { + "class": "natural", + "endermanPickable": true, + "faces": [ + "east" + ], + "id_replace_list": [ + [ + 12, + "minecraft:pumpkin[variant=east]" + ] + ], + "nameEN": "Pumpkin(east) (1.12)", + "nameZH": "南瓜(东) (1.12)", + "version": [ + 12 + ] + }, + "minecraft:pumpkin[facing=north]": { + "class": "natural", + "endermanPickable": true, + "id_replace_list": [ + [ + 12, + "minecraft:pumpkin[variant=north]" + ] + ], + "nameEN": "Pumpkin(north) (1.12)", + "nameZH": "南瓜(北) (1.12)", + "version": [ + 12 + ] + }, + "minecraft:pumpkin[facing=south]": { + "class": "natural", + "endermanPickable": true, + "faces": [ + "south", + "north" + ], + "id_replace_list": [ + [ + 12, + "minecraft:pumpkin[variant=south]" + ] + ], + "nameEN": "Pumpkin(south) (1.12)", + "nameZH": "南瓜(南) (1.12)", + "version": [ + 12 + ] + }, + "minecraft:pumpkin[facing=west]": { + "class": "natural", + "endermanPickable": true, + "faces": [ + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:pumpkin[variant=west]" + ] + ], + "nameEN": "Pumpkin(west) (1.12)", + "nameZH": "南瓜(西) (1.12)", + "version": [ + 12 + ] + }, + "minecraft:shroomlight": { + "class": "natural", + "isGlowing": true, + "nameEN": "Shroomlight", + "nameZH": "菌光体", + "version": 16 + }, + "minecraft:snow": { + "class": "natural", + "nameEN": "Snow (1.12)", + "nameZH": "雪块 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:snow_block": { + "class": "natural", + "nameEN": "Snow", + "nameZH": "雪块", + "version": 13 + }, + "minecraft:verdant_froglight[axis=y]": { + "class": "natural", + "isGlowing": true, + "nameEN": "Verdant Froglight", + "nameZH": "绿色蛙鸣灯", + "version": 19, + "rare": true + }, + "minecraft:bee_nest[facing=north,honey_level=0]": { + "class": "natural", + "nameEN": "Bee nest(north)", + "nameZH": "蜂巢(向北)", + "version": 15, + "burnable": true, + "reproducible": false + }, + "minecraft:bee_nest[facing=east,honey_level=0]": { + "class": "natural", + "nameEN": "Bee nest(east)", + "nameZH": "蜂巢(向东)", + "version": 15, + "burnable": true, + "faces": [ + "east", + "north" + ], + "reproducible": false + }, + "minecraft:bee_nest[facing=south,honey_level=0]": { + "class": "natural", + "nameEN": "Bee nest(south)", + "nameZH": "蜂巢(向南)", + "version": 15, + "burnable": true, + "faces": [ + "south" + ], + "reproducible": false + }, + "minecraft:bee_nest[facing=west,honey_level=0]": { + "class": "natural", + "nameEN": "Bee nest(west)", + "nameZH": "蜂巢(向西)", + "version": 15, + "burnable": true, + "faces": [ + "west" + ], + "reproducible": false + }, + "minecraft:resin_block": { + "class": "natural", + "nameEN": "Resin block", + "nameZH": "树脂块", + "version": 21 + }, + "minecraft:resin_bricks": { + "class": "natural", + "nameEN": "Resin bricks", + "nameZH": "树脂砖块", + "version": 21 + }, + "minecraft:chiseled_resin_bricks": { + "class": "natural", + "nameEN": "Chiseled resin bricks", + "nameZH": "雕纹树脂砖块", + "version": 21 + }, + "minecraft:pale_moss_block": { + "class": "natural", + "nameEN": "Pale Moss Block", + "nameZH": "苍白苔藓块", + "version": 21, + "burnable": true + }, + // Currently VCl is not able to process these carpet files. Comment them out + // "minecraft:pale_moss_carpet": { + // "class": "natural", + // "nameEN": "Pale moss carpet", + // "nameZH": "苍白苔藓地毯", + // "version": 21, + // "burnable": true, + // "faces": [ + // "up" + // ] + // }, + "minecraft:crimson_nylium": { + "class": "nether", + "endermanPickable": true, + "faces": [ + "up" + ], + "nameEN": "Crimson nylium", + "nameZH": "绯红菌岩", + "version": 16 + }, + "minecraft:glowstone": { + "class": "nether", + "isGlowing": true, + "nameEN": "Glowstone", + "nameZH": "萤石", + "version": "all" + }, + "minecraft:magma": { + "class": "nether", + "nameEN": "Magma block (1.12)", + "nameZH": "岩浆块 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:magma_block": { + "class": "nether", + "nameEN": "Magma block", + "nameZH": "岩浆块", + "version": 13 + }, + "minecraft:nether_brick": { + "class": "nether", + "nameEN": "Nether brick (1.12)", + "nameZH": "地狱砖块 (1.12)", + "version": [ + 12 + ], + "reproducible": false + }, + "minecraft:nether_bricks": { + "class": "nether", + "nameEN": "Nether brick", + "nameZH": "地狱砖块", + "version": 13 + }, + "minecraft:nether_wart_block": { + "class": "nether", + "nameEN": "Nether wart block", + "nameZH": "地狱疣块", + "version": "all" + }, + "minecraft:netherrack": { + "class": "nether", + "nameEN": "Netherrack", + "nameZH": "地狱岩", + "version": "all", + "reproducible": false + }, + "minecraft:soul_sand": { + "class": "nether", + "nameEN": "Soul sand", + "nameZH": "灵魂沙", + "version": "all" + }, + "minecraft:soul_soil": { + "class": "nether", + "nameEN": "Soul soil", + "nameZH": "灵魂土", + "version": 16, + "reproducible": false + }, + "minecraft:warped_nylium": { + "class": "nether", + "endermanPickable": true, + "faces": [ + "up" + ], + "nameEN": "Warped nylium", + "nameZH": "扭曲菌岩", + "version": 16 + }, + "minecraft:warped_wart_block": { + "class": "nether", + "nameEN": "Warped wart block", + "nameZH": "扭曲疣块", + "version": 16 + }, + "minecraft:dark_prismarine": { + "class": "ocean", + "nameEN": "Dark prismarine", + "nameZH": "暗海晶石", + "version": 13 + }, + "minecraft:dried_kelp_block": { + "class": "ocean", + "nameEN": "Kelp block", + "nameZH": "海带块", + "version": 13 + }, + "minecraft:prismarine": { + "class": "ocean", + "nameEN": "Prismarine", + "nameZH": "海晶石", + "version": 13 + }, + "minecraft:prismarine[variant=dark_prismarine]": { + "class": "ocean", + "nameEN": "Dark prismarine (1.12)", + "nameZH": "暗海晶石 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:prismarine[variant=prismarine]": { + "class": "ocean", + "nameEN": "Prismarine (1.12)", + "nameZH": "海晶石 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:prismarine[variant=prismarine_bricks]": { + "class": "ocean", + "nameEN": "Prismarine bricks (1.12)", + "nameZH": "海晶石砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:prismarine_bricks": { + "class": "ocean", + "nameEN": "Prismarine bricks", + "nameZH": "海晶石砖", + "version": 13 + }, + "minecraft:sea_lantern": { + "class": "ocean", + "nameEN": "Sea lantern", + "nameZH": "海晶灯", + "version": "all" + }, + "minecraft:amethyst_block": { + "class": "ore", + "nameEN": "Amethyst block", + "nameZH": "紫水晶块", + "version": 17, + "rare": true + }, + "minecraft:coal_block": { + "burnable": true, + "class": "ore", + "nameEN": "Coal block", + "nameZH": "煤块", + "version": "all" + }, + "minecraft:diamond_block": { + "class": "ore", + "nameEN": "Diamond block", + "nameZH": "钻石块", + "version": "all", + "reproducible": false, + "rare": true + }, + "minecraft:emerald_block": { + "class": "ore", + "nameEN": "Emerald block", + "nameZH": "绿宝石块", + "version": "all", + "rare": true + }, + "minecraft:gold_block": { + "class": "ore", + "nameEN": "Gold block", + "nameZH": "金块", + "version": "all", + "rare": true + }, + "minecraft:iron_block": { + "class": "ore", + "nameEN": "Iron block", + "nameZH": "铁块", + "version": "all", + "rare": true + }, + "minecraft:lapis_block": { + "class": "ore", + "nameEN": "Lapis block", + "nameZH": "青金石块", + "version": "all", + "reproducible": false, + "rare": true + }, + "minecraft:raw_copper_block": { + "class": "ore", + "nameEN": "Raw copper block", + "nameZH": "生铜块", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:raw_gold_block": { + "class": "ore", + "nameEN": "Raw gold block", + "nameZH": "粗金块", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:raw_iron_block": { + "class": "ore", + "nameEN": "Raw iron block", + "nameZH": "生铁块", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:waxed_copper_block": { + "class": "ore", + "nameEN": "Waxed copper", + "nameZH": "涂蜡铜块", + "version": 17 + }, + "minecraft:waxed_cut_copper": { + "class": "ore", + "nameEN": "Waxed cut copper", + "nameZH": "涂蜡切制铜块", + "version": 17 + }, + "minecraft:waxed_exposed_copper": { + "class": "ore", + "nameEN": "Waxed exposed copper", + "nameZH": "涂蜡斑驳铜块", + "version": 17 + }, + "minecraft:waxed_exposed_cut_copper": { + "class": "ore", + "nameEN": "Waxed exposed cut copper", + "nameZH": "涂蜡切制斑驳铜块", + "version": 17 + }, + "minecraft:waxed_weathered_copper": { + "class": "ore", + "nameEN": "Waxed weathered copper", + "nameZH": "涂蜡生锈铜块", + "version": 17 + }, + "minecraft:waxed_weathered_cut_copper": { + "class": "ore", + "nameEN": "Waxed weathered cut copper", + "nameZH": "涂蜡切制生锈铜块", + "version": 17 + }, + "minecraft:waxed_oxidized_copper": { + "class": "ore", + "nameEN": "Waxed oxidized copper", + "nameZH": "涂蜡氧化铜块", + "version": 17 + }, + "minecraft:waxed_oxidized_cut_copper": { + "class": "ore", + "nameEN": "Waxed oxidized cut copper", + "nameZH": "涂蜡切制氧化铜块", + "version": 17 + }, + "minecraft:waxed_chiseled_copper": { + "class": "ore", + "nameEN": "Waxed chiseled copper", + "nameZH": "涂蜡雕纹铜块", + "version": 21 + }, + "minecraft:waxed_exposed_chiseled_copper": { + "class": "ore", + "nameEN": "Waxed exposed chiseled copper", + "nameZH": "涂蜡斑驳雕纹铜块", + "version": 21 + }, + "minecraft:waxed_weathered_chiseled_copper": { + "class": "ore", + "nameEN": "Waxed weathered chiseled copper", + "nameZH": "涂蜡生锈雕纹铜块", + "version": 21 + }, + "minecraft:waxed_oxidized_chiseled_copper": { + "class": "ore", + "nameEN": "Waxed oxidized chiseled copper", + "nameZH": "涂蜡氧化雕纹铜块", + "version": 21 + }, + "minecraft:waxed_copper_grate": { + "class": "ore", + "nameEN": "Waxed copper grate", + "nameZH": "涂蜡铜格栅", + "version": 21, + "transparent": true + }, + "minecraft:waxed_exposed_copper_grate": { + "class": "ore", + "nameEN": "Waxed exposed copper grate", + "nameZH": "涂蜡斑驳铜格栅", + "version": 21, + "transparent": true + }, + "minecraft:waxed_weathered_copper_grate": { + "class": "ore", + "nameEN": "Waxed weathered copper grate", + "nameZH": "涂蜡生锈铜格栅", + "version": 21, + "transparent": true + }, + "minecraft:waxed_oxidized_copper_grate": { + "class": "ore", + "nameEN": "Waxed oxidized copper grate", + "nameZH": "涂蜡氧化铜格栅", + "version": 21, + "transparent": true + }, + "minecraft:waxed_copper_bulb[lit=false,powered=false]": { + "class": "ore", + "nameEN": "Waxed copper bulb", + "nameZH": "涂蜡铜灯", + "version": 21 + }, + "minecraft:waxed_copper_bulb[lit=true,powered=false]": { + "class": "ore", + "nameEN": "Waxed copper bulb(lit)", + "nameZH": "涂蜡铜灯(点亮)", + "version": 21, + "isGlowing": true + }, + "minecraft:waxed_exposed_copper_bulb[lit=false,powered=false]": { + "class": "ore", + "nameEN": "Waxed exposed copper bulb", + "nameZH": "涂蜡斑驳铜灯", + "version": 21 + }, + "minecraft:waxed_exposed_copper_bulb[lit=true,powered=false]": { + "class": "ore", + "nameEN": "Waxed exposed copper bulb(lit)", + "nameZH": "涂蜡斑驳铜灯(点亮)", + "version": 21, + "isGlowing": true + }, + "minecraft:waxed_weathered_copper_bulb[lit=false,powered=false]": { + "class": "ore", + "nameEN": "Waxed weathered copper bulb", + "nameZH": "涂蜡生锈铜灯", + "version": 21 + }, + "minecraft:waxed_weathered_copper_bulb[lit=true,powered=false]": { + "class": "ore", + "nameEN": "Waxed weathered copper bulb(lit)", + "nameZH": "涂蜡生锈铜灯(点亮)", + "version": 21, + "isGlowing": true + }, + "minecraft:waxed_oxidized_copper_bulb[lit=false,powered=false]": { + "class": "ore", + "nameEN": "Waxed oxidized copper bulb", + "nameZH": "涂蜡氧化铜灯", + "version": 21 + }, + "minecraft:waxed_oxidized_copper_bulb[lit=true,powered=false]": { + "class": "ore", + "nameEN": "Waxed oxidized copper bulb(lit)", + "nameZH": "涂蜡氧化铜灯(点亮)", + "version": 21, + "isGlowing": true + }, + "minecraft:gilded_blackstone": { + "class": "ore", + "nameEN": "Glided blackstone", + "nameZH": "镶金黑石", + "version": 16, + "reproducible": false, + "rare": true + }, + "minecraft:coal_ore": { + "class": "ore", + "nameEN": "Coal ore", + "nameZH": "煤矿", + "version": "all", + "reproducible": false + }, + "minecraft:deepslate_coal_ore": { + "class": "ore", + "nameEN": "Deepslate coal ore", + "nameZH": "深板岩煤矿", + "version": 17, + "reproducible": false + }, + "minecraft:copper_ore": { + "class": "ore", + "nameEN": "Copper ore", + "nameZH": "铜矿", + "version": 17, + "reproducible": false + }, + "minecraft:deepslate_copper_ore": { + "class": "ore", + "nameEN": "Deepslate copper ore", + "nameZH": "深板岩铜矿", + "version": 17, + "reproducible": false + }, + "minecraft:diamond_ore": { + "class": "ore", + "nameEN": "Diamond ore", + "nameZH": "钻石矿", + "version": "all", + "reproducible": false, + "rare": true + }, + "minecraft:deepslate_diamond_ore": { + "class": "ore", + "nameEN": "Deepslate diamond ore", + "nameZH": "深板岩钻石矿", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:emerald_ore": { + "class": "ore", + "nameEN": "Emerald ore", + "nameZH": "绿宝石矿", + "version": "all", + "reproducible": false, + "rare": true + }, + "minecraft:deepslate_emerald_ore": { + "class": "ore", + "nameEN": "Deepslate emerald ore", + "nameZH": "深板岩绿宝石矿", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:gold_ore": { + "class": "ore", + "nameEN": "Gold ore", + "nameZH": "金矿", + "version": "all", + "reproducible": false + }, + "minecraft:deepslate_gold_ore": { + "class": "ore", + "nameEN": "Deepslate gold ore", + "nameZH": "深板岩金矿", + "version": 17, + "reproducible": false + }, + "minecraft:iron_ore": { + "class": "ore", + "nameEN": "Iron ore", + "nameZH": "铁矿", + "version": "all", + "reproducible": false + }, + "minecraft:deepslate_iron_ore": { + "class": "ore", + "nameEN": "Deepslate iron ore", + "nameZH": "深板岩铁矿", + "version": 17, + "reproducible": false + }, + "minecraft:lapis_ore": { + "class": "ore", + "nameEN": "Lapis ore", + "nameZH": "青金石矿", + "version": "all", + "reproducible": false + }, + "minecraft:deepslate_lapis_ore": { + "class": "ore", + "nameEN": "Deepslate lapis ore", + "nameZH": "深板岩青金石矿", + "version": 17, + "reproducible": false + }, + "minecraft:redstone_ore": { + "class": "ore", + "nameEN": "Redstone ore (1.12&1.16+)", + "nameZH": "红石矿 (1.12&1.16+)", + "version": [ + 12, + 16, + 17, + 18, + 19, + 20 + ], + "reproducible": false + }, + "minecraft:redstone_ore[lit=false]": { + "class": "ore", + "nameEN": "Redstone ore (1.13~1.15)", + "nameZH": "红石矿 (1.13~1.15)", + "version": [ + 13, + 14, + 15 + ], + "reproducible": false + }, + "minecraft:deepslate_redstone_ore": { + "class": "ore", + "nameEN": "Deepslate redstone ore", + "nameZH": "深板岩红石矿", + "version": 17, + "reproducible": false + }, + "minecraft:nether_gold_ore": { + "class": "ore", + "nameEN": "Nether gold ore", + "nameZH": "下界金矿", + "version": 16, + "reproducible": false + }, + "minecraft:nether_quartz_ore": { + "class": "ore", + "nameEN": "Nether quartz ore", + "nameZH": "下界石英矿", + "version": 13, + "reproducible": false + }, + "minecraft:quartz_ore": { + "class": "ore", + "nameEN": "Nether quartz ore (1.12)", + "nameZH": "下界石英矿 (1.12)", + "version": [ + 12 + ], + "reproducible": false + }, + "minecraft:ancient_debris": { + "class": "ore", + "nameEN": "Ancient debris", + "nameZH": "远古残骸", + "version": 16, + "reproducible": false, + "rare": true + }, + "minecraft:sculk": { + "class": "others", + "nameEN": "Sculk block", + "nameZH": "潜声方块", + "version": 19, + "rare": true + }, + "minecraft:sculk_catalyst[bloom=false]": { + "class": "others", + "nameEN": "Sculk catalyst", + "nameZH": "潜声催化剂", + "version": 19, + "rare": true + }, + "minecraft:acacia_planks": { + "burnable": true, + "class": "planks", + "id_replace_list": [ + [ + 12, + "minecraft:planks[variant=acacia]" + ] + ], + "nameEN": "Acacia plank", + "nameZH": "金合欢木板", + "version": "all" + }, + "minecraft:birch_planks": { + "burnable": true, + "class": "planks", + "id_replace_list": [ + [ + 12, + "minecraft:planks[variant=birch]" + ] + ], + "nameEN": "Birch plank", + "nameZH": "白桦木板", + "version": "all" + }, + "minecraft:crimson_planks": { + "burnable": true, + "class": "planks", + "nameEN": "Crimson plank", + "nameZH": "绯红木板", + "version": 16 + }, + "minecraft:dark_oak_planks": { + "burnable": true, + "class": "planks", + "id_replace_list": [ + [ + 12, + "minecraft:planks[variant=dark_oak]" + ] + ], + "nameEN": "Dark oak plank", + "nameZH": "黑橡木木板", + "version": "all" + }, + "minecraft:jungle_planks": { + "burnable": true, + "class": "planks", + "id_replace_list": [ + [ + 12, + "minecraft:planks[variant=jungle]" + ] + ], + "nameEN": "Jungle plank", + "nameZH": "丛林木板", + "version": "all" + }, + "minecraft:mangrove_planks": { + "burnable": true, + "class": "planks", + "nameEN": "Mangrove planks", + "nameZH": "红树木板", + "version": 19 + }, + "minecraft:oak_planks": { + "burnable": true, + "class": "planks", + "id_replace_list": [ + [ + 12, + "minecraft:planks[variant=oak]" + ] + ], + "nameEN": "Oak plank", + "nameZH": "橡木木板", + "version": "all" + }, + "minecraft:spruce_planks": { + "burnable": true, + "class": "planks", + "id_replace_list": [ + [ + 12, + "minecraft:planks[variant=spruce]" + ] + ], + "nameEN": "Spruce plank", + "nameZH": "云杉木板", + "version": "all" + }, + "minecraft:warped_planks": { + "burnable": true, + "class": "planks", + "nameEN": "Warped plank", + "nameZH": "扭曲木板", + "version": 16 + }, + "minecraft:pale_oak_planks": { + "burnable": true, + "class": "planks", + "nameEN": "Pale oak plank", + "nameZH": "苍白木板", + "version": 21 + }, + "minecraft:honey_block": { + "class": "redstone", + "nameEN": "Honey block", + "nameZH": "蜜块", + "version": 15 + }, + "minecraft:redstone_block": { + "class": "redstone", + "nameEN": "Redstone block", + "nameZH": "红石块", + "version": "all" + }, + "minecraft:slime": { + "class": "redstone", + "nameEN": "Slime block (1.12)", + "nameZH": "粘液块 (1.12)", + "transparent": true, + "version": [ + 12 + ] + }, + "minecraft:slime_block": { + "class": "redstone", + "nameEN": "Slime block", + "nameZH": "粘液块", + "transparent": true, + "version": 13 + }, + "minecraft:target[power=0]": { + "class": "redstone", + "nameEN": "Target", + "nameZH": "靶子", + "version": 16 + }, + "minecraft:tnt[explode=false]": { + "burnable": true, + "class": "redstone", + "endermanPickable": true, + "nameEN": "TNT (1.12)", + "nameZH": "TNT (1.12)", + "version": [ + 12 + ] + }, + "minecraft:tnt[unstable=false]": { + "burnable": true, + "class": "redstone", + "endermanPickable": true, + "nameEN": "TNT", + "nameZH": "TNT", + "version": 13 + }, + "minecraft:acacia_slab[half=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:wooden_slab[half=top,variant=acacia]" + ] + ], + "nameEN": "Acacia slab (1.12)", + "nameZH": "金合欢木上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:acacia_slab[type=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Acacia slab", + "nameZH": "金合欢木上半砖", + "version": 13 + }, + "minecraft:birch_slab[half=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:wooden_slab[half=top,variant=birch]" + ] + ], + "nameEN": "Birch slab (1.12)", + "nameZH": "白桦木上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:birch_slab[type=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Birch slab", + "nameZH": "白桦木上半砖", + "version": 13 + }, + "minecraft:brick_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=brick]" + ] + ], + "nameEN": "Brick slab (1.12)", + "nameZH": "砖块上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:brick_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Brick slab", + "nameZH": "砖块上半砖", + "version": 13 + }, + "minecraft:cobblestone_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=cobblestone]" + ] + ], + "nameEN": "Cobblestone slab (1.12)", + "nameZH": "圆石上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:cobblestone_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Cobblestone slab", + "nameZH": "圆石上半砖", + "version": 13 + }, + "minecraft:dark_oak_slab[half=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:wooden_slab[half=top,variant=dark_oak]" + ] + ], + "nameEN": "Dark oak slab (1.12)", + "nameZH": "黑橡木上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:dark_oak_slab[type=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Dark oak slab", + "nameZH": "黑橡木上半砖", + "version": 13 + }, + "minecraft:dark_prismarine_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Dark prismarine slab", + "nameZH": "暗海晶石上半砖", + "version": 13 + }, + "minecraft:jungle_slab[half=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:wooden_slab[half=top,variant=jungle]" + ] + ], + "nameEN": "Jungle slab (1.12)", + "nameZH": "丛林木上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:jungle_slab[type=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Jungle slab", + "nameZH": "丛林木上半砖", + "version": 13 + }, + "minecraft:nether_brick_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=nether_brick]" + ] + ], + "nameEN": "Nether brick slab (1.12)", + "nameZH": "地狱砖块上半砖 (1.12)", + "version": [ + 12 + ], + "reproducible": false + }, + "minecraft:nether_brick_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Nether brick slab", + "nameZH": "地狱砖块上半砖", + "version": 13 + }, + "minecraft:oak_slab[half=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:wooden_slab[half=top,variant=oak]" + ] + ], + "nameEN": "Oak slab (1.12)", + "nameZH": "橡木上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:oak_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Oak slab", + "nameZH": "橡木上半砖", + "version": 13 + }, + "minecraft:prismarine_brick_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Prismarine brick slab", + "nameZH": "海晶石砖上半砖", + "version": 13 + }, + "minecraft:prismarine_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Prismarine slab", + "nameZH": "海晶石上半砖", + "version": 13 + }, + "minecraft:purpur_slab[half=top,variant=default]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Purpur slab (1.12)", + "nameZH": "紫珀上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:purpur_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Purpur slab", + "nameZH": "紫珀上半砖", + "version": 13 + }, + "minecraft:quartz_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=quartz]" + ] + ], + "nameEN": "Quartz slab (1.12)", + "nameZH": "石英上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:quartz_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Quartz slab", + "nameZH": "石英上半砖", + "version": 13 + }, + "minecraft:red_sandstone_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab2[half=top,variant=red_sandstone]" + ] + ], + "nameEN": "Red sandstone slab (1.12)", + "nameZH": "红砂岩上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:red_sandstone_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Red sandstone slab", + "nameZH": "红砂岩上半砖", + "version": 13 + }, + "minecraft:sandstone_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=sandstone]" + ] + ], + "nameEN": "Sandstone slab (1.12)", + "nameZH": "砂岩上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:sandstone_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Sandstone slab", + "nameZH": "砂岩上半砖", + "version": 13 + }, + "minecraft:spruce_slab[half=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:wooden_slab[half=top,variant=spruce]" + ] + ], + "nameEN": "Spruce slab (1.12)", + "nameZH": "云杉木上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:spruce_slab[type=top]": { + "burnable": true, + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Spruce slab", + "nameZH": "云杉木上半砖", + "version": 13 + }, + "minecraft:stone_brick_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=stone_brick]" + ] + ], + "nameEN": "Stonebrick slab (1.12)", + "nameZH": "石砖上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:stone_brick_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Stonebrick slab", + "nameZH": "石砖上半砖", + "version": 13 + }, + "minecraft:stone_slab[half=top]": { + "class": "slab", + "faces": [ + "up" + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone_slab[half=top,variant=stone]" + ] + ], + "nameEN": "Stone slab (1.12)", + "nameZH": "石头上半砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:stone_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Stone slab", + "nameZH": "石头上半砖", + "version": 13 + }, + "minecraft:waxed_cut_copper_slab[type=top]": { + "class": "slab", + "faces": [ + "up" + ], + "nameEN": "Waxed cut copper slab", + "nameZH": "涂蜡切制铜上半砖", + "version": 17, + "rare": true + }, + "minecraft:tuff": { + "class": "stone", + "nameEN": "Tuff", + "nameZH": "凝灰岩", + "version": 17, + "reproducible": false + }, + "minecraft:tuff_bricks": { + "class": "stone", + "nameEN": "Tuff bricks", + "nameZH": "凝灰岩砖", + "version": 21 + }, + "minecraft:chiseled_tuff_bricks": { + "class": "stone", + "nameEN": "Chiseled tuff bricks", + "nameZH": "雕纹凝灰岩砖", + "version": 21 + }, + "minecraft:polished_tuff": { + "class": "stone", + "nameEN": "Polished tuff", + "nameZH": "磨制凝灰岩", + "version": 21 + }, + "minecraft:calcite": { + "class": "stone", + "nameEN": "Calcite", + "nameZH": "方解石", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:chiseled_deepslate": { + "class": "stone", + "nameEN": "Chiseled deepslate", + "nameZH": "錾制深板岩", + "version": 17, + "reproducible": false + }, + "minecraft:cobbled_deepslate": { + "class": "stone", + "nameEN": "Cobbled deepslate", + "nameZH": "深板岩圆石", + "version": 17, + "reproducible": false + }, + "minecraft:cobblestone": { + "class": "stone", + "nameEN": "Cobblestone", + "nameZH": "圆石", + "version": "all" + }, + "minecraft:mossy_cobblestone": { + "class": "stone", + "nameEN": "Mossy cobblestone", + "nameZH": "苔石", + "version": "all" + }, + "minecraft:deepslate[axis=y]": { + "class": "stone", + "nameEN": "Deepslate", + "nameZH": "深板岩", + "version": 17, + "reproducible": false + }, + "minecraft:deepslate_bricks": { + "class": "stone", + "nameEN": "Deepslate bricks", + "nameZH": "深板岩砖", + "version": 17, + "reproducible": false + }, + "minecraft:deepslate_tiles": { + "class": "stone", + "nameEN": "Deepslate tiles", + "nameZH": "深板岩瓦", + "version": 17, + "reproducible": false + }, + "minecraft:dripstone_block": { + "class": "stone", + "nameEN": "DripStone Block", + "nameZH": "滴水石块", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:obsidian": { + "class": "stone", + "nameEN": "Obsidian", + "nameZH": "黑曜石", + "version": "all" + }, + "minecraft:crying_obsidian": { + "class": "stone", + "nameEN": "Crying obsidian", + "nameZH": "哭泣的黑曜石", + "version": 16, + "rare": true + }, + "minecraft:polished_basalt[axis=y]": { + "class": "stone", + "nameEN": "Polished basalt", + "nameZH": "磨制玄武岩", + "version": 16, + "reproducible": false + }, + "minecraft:polished_blackstone": { + "class": "stone", + "nameEN": "Polished blackstone", + "nameZH": "磨制黑石", + "version": 16, + "reproducible": false + }, + "minecraft:polished_deepslate": { + "class": "stone", + "nameEN": "Polished deepslate", + "nameZH": "磨制深板岩", + "version": 17, + "reproducible": false + }, + "minecraft:polished_diorite": { + "class": "stone", + "nameEN": "Polished diorite", + "nameZH": "磨制闪长岩", + "version": 13 + }, + "minecraft:polished_granite": { + "class": "stone", + "nameEN": "Polished granite", + "nameZH": "磨制花岗岩", + "version": 13 + }, + "minecraft:polished_andesite": { + "class": "stone", + "nameEN": "Polished andesite", + "nameZH": "磨制安山岩", + "version": 13 + }, + "minecraft:quartz_block": { + "class": "ore", + "nameEN": "Quartz block", + "nameZH": "石英块", + "version": 13 + }, + "minecraft:quartz_block[variant=default]": { + "class": "ore", + "nameEN": "Quartz block (1.12)", + "nameZH": "石英块 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:smooth_stone": { + "class": "stone", + "nameEN": "Smooth stone", + "nameZH": "平滑石", + "version": 13 + }, + "minecraft:stone": { + "class": "stone", + "nameEN": "Stone", + "nameZH": "石头", + "version": 13 + }, + "minecraft:andesite": { + "class": "stone", + "nameEN": "Andesite", + "nameZH": "安山岩", + "version": "all", + "id_replace_list": [ + [ + 12, + "minecraft:stone[variant=andesite]" + ] + ], + "reproducible": false + }, + "minecraft:diorite": { + "class": "stone", + "nameEN": "Diorite", + "nameZH": "闪长岩", + "version": "all", + "id_replace_list": [ + [ + 12, + "minecraft:stone[variant=diorite]" + ] + ], + "reproducible": false + }, + "minecraft:granite": { + "class": "stone", + "nameEN": "Granite", + "nameZH": "花岗岩", + "version": "all", + "id_replace_list": [ + [ + 12, + "minecraft:stone[variant=granite]" + ] + ], + "reproducible": false + }, + "minecraft:smooth_andesite": { + "class": "stone", + "nameEN": "Polished andesite (1.12)", + "nameZH": "磨制安山岩 (1.12)", + "version": [ + 12 + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone[variant=smooth_andesite]" + ] + ], + "reproducible": false + }, + "minecraft:smooth_diorite": { + "class": "stone", + "nameEN": "Polished diorite (1.12)", + "nameZH": "磨制闪长岩 (1.12)", + "version": [ + 12 + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone[variant=smooth_diorite]" + ] + ], + "reproducible": false + }, + "minecraft:smooth_granite": { + "class": "stone", + "nameEN": "Polished granite (1.12)", + "nameZH": "磨制花岗岩 (1.12)", + "version": [ + 12 + ], + "id_replace_list": [ + [ + 12, + "minecraft:stone[variant=smooth_granite]" + ] + ], + "reproducible": false + }, + "minecraft:stone[variant=stone]": { + "class": "stone", + "nameEN": "Stone (1.12)", + "nameZH": "石头 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:stone_bricks": { + "class": "stone", + "nameEN": "Stone brick", + "nameZH": "石砖", + "version": 13 + }, + "minecraft:stonebrick[variant=stonebrick]": { + "class": "stone", + "nameEN": "Stone brick (1.12)", + "nameZH": "石砖 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:black_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=black]" + ] + ], + "nameEN": "Black hardened clay (1.12)", + "nameZH": "黑色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:black_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Black terracotta", + "nameZH": "黑色陶瓦", + "version": 13 + }, + "minecraft:blue_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=blue]" + ] + ], + "nameEN": "Blue hardened clay (1.12)", + "nameZH": "蓝色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:blue_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Blue terracotta", + "nameZH": "蓝色陶瓦", + "version": 13 + }, + "minecraft:brown_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=brown]" + ] + ], + "nameEN": "Brown hardened clay (1.12)", + "nameZH": "棕色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:brown_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Brown terracotta", + "nameZH": "棕色陶瓦", + "version": 13 + }, + "minecraft:cyan_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=cyan]" + ] + ], + "nameEN": "Cyan hardened clay (1.12)", + "nameZH": "青色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:cyan_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Cyan terracotta", + "nameZH": "青色陶瓦", + "version": 13 + }, + "minecraft:gray_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=gray]" + ] + ], + "nameEN": "Gray hardened clay (1.12)", + "nameZH": "灰色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:gray_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Gray terracotta", + "nameZH": "灰色陶瓦", + "version": 13 + }, + "minecraft:green_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=green]" + ] + ], + "nameEN": "Green hardened clay (1.12)", + "nameZH": "绿色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:green_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Green terracotta", + "nameZH": "绿色陶瓦", + "version": 13 + }, + "minecraft:hardened_clay": { + "background": true, + "class": "terracotta", + "nameEN": "Hardened clay (1.12)", + "nameZH": "硬化粘土 (1.12)", + "version": [ + 12 + ], + "reproducible": false + }, + "minecraft:light_blue_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=light_blue]" + ] + ], + "nameEN": "Light blue hardened clay (1.12)", + "nameZH": "浅蓝硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:light_blue_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Light blue terracotta", + "nameZH": "浅蓝陶瓦", + "version": 13 + }, + "minecraft:light_gray_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Light gray terracotta", + "nameZH": "浅灰陶瓦", + "version": 13 + }, + "minecraft:lime_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=lime]" + ] + ], + "nameEN": "Lime hardened clay (1.12)", + "nameZH": "浅绿硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:lime_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Lime terracotta", + "nameZH": "浅绿陶瓦", + "version": 13 + }, + "minecraft:magenta_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=magenta]" + ] + ], + "nameEN": "Magenta hardened clay (1.12)", + "nameZH": "品红硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:magenta_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Magenta terracotta", + "nameZH": "品红陶瓦", + "version": 13 + }, + "minecraft:orange_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=orange]" + ] + ], + "nameEN": "Orange hardened clay (1.12)", + "nameZH": "橙色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:orange_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Orange terracotta", + "nameZH": "橙色陶瓦", + "version": 13 + }, + "minecraft:pink_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=pink]" + ] + ], + "nameEN": "Pink hardened clay (1.12)", + "nameZH": "粉色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:pink_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Pink terracotta", + "nameZH": "粉色陶瓦", + "version": 13 + }, + "minecraft:purple_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=purple]" + ] + ], + "nameEN": "Purple hardened clay (1.12)", + "nameZH": "紫色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:purple_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Purple terracotta", + "nameZH": "紫色陶瓦", + "version": 13 + }, + "minecraft:red_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=red]" + ] + ], + "nameEN": "Red hardened clay (1.12)", + "nameZH": "红色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:red_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Red terracotta", + "nameZH": "红色陶瓦", + "version": 13 + }, + "minecraft:silver_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=silver]" + ] + ], + "nameEN": "Light gray hardened clay (1.12)", + "nameZH": "浅灰硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "Terracotta", + "nameZH": "陶瓦", + "version": 13, + "reproducible": false + }, + "minecraft:white_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=white]" + ] + ], + "nameEN": "White hardened clay (1.12)", + "nameZH": "白色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:white_terracotta": { + "background": true, + "class": "terracotta", + "nameEN": "White terracotta", + "nameZH": "白色陶瓦", + "version": 13 + }, + "minecraft:yellow_stained_hardened_clay": { + "background": true, + "class": "terracotta", + "id_replace_list": [ + [ + 12, + "minecraft:stained_hardened_clay[color=yellow]" + ] + ], + "nameEN": "Yellow hardened clay (1.12)", + "nameZH": "黄色硬化粘土 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:yellow_terracotta": { + "background": true, + "burnable": true, + "class": "terracotta", + "nameEN": "Yellow terracotta", + "nameZH": "黄色陶瓦", + "version": 13 + }, + "minecraft:end_bricks": { + "class": "the_end", + "nameEN": "Endstone bricks (1.12)", + "nameZH": "末地石砖 (1.12)", + "version": [ + 12 + ], + "reproducible": false + }, + "minecraft:end_stone": { + "class": "the_end", + "nameEN": "End stone", + "nameZH": "末地石", + "version": "all" + }, + "minecraft:end_stone_bricks": { + "class": "the_end", + "nameEN": "Endstone bricks", + "nameZH": "末地石砖", + "version": 13 + }, + "minecraft:purpur_block": { + "class": "the_end", + "nameEN": "Purpur block", + "nameZH": "紫珀块", + "version": "all" + }, + "minecraft:acacia_log[axis=y]": { + "class": "wood", + "nameEN": "Acacia log (Y axis)", + "nameZH": "金合欢原木 (Y 轴)", + "version": "all", + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log2[variant=acacia,axis=y]" + ] + ] + }, + "minecraft:acacia_log[axis=x]": { + "class": "wood", + "nameEN": "Acacia log (X axis)", + "nameZH": "金合欢原木 (X 轴)", + "version": "all", + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log2[variant=acacia,axis=x]" + ] + ] + }, + "minecraft:acacia_log[axis=z]": { + "class": "wood", + "nameEN": "Acacia log (Z axis)", + "nameZH": "金合欢原木 (Z 轴)", + "version": "all", + "faces": [ + "north", + "south" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log2[variant=acacia,axis=z]" + ] + ] + }, + "minecraft:dark_oak_log[axis=y]": { + "class": "wood", + "nameEN": "Dark oak log (Y axis)", + "nameZH": "黑橡树原木 (Y 轴)", + "version": "all", + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log2[variant=dark_oak,axis=y]" + ] + ] + }, + "minecraft:dark_oak_log[axis=x]": { + "class": "wood", + "nameEN": "Dark oak log (X axis)", + "nameZH": "黑橡树原木 (X 轴)", + "version": "all", + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log2[variant=dark_oak,axis=x]" + ] + ] + }, + "minecraft:dark_oak_log[axis=z]": { + "class": "wood", + "nameEN": "Dark oak log (Z axis)", + "nameZH": "黑橡树原木 (Z 轴)", + "version": "all", + "faces": [ + "north", + "south" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log2[variant=dark_oak,axis=z]" + ] + ] + }, + "minecraft:jungle_log[axis=y]": { + "class": "wood", + "nameEN": "Jungle log (Y axis)", + "nameZH": "丛林木原木 (Y 轴)", + "version": "all", + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=jungle,axis=y]" + ] + ] + }, + "minecraft:jungle_log[axis=x]": { + "class": "wood", + "nameEN": "Jungle log (X axis)", + "nameZH": "丛林木原木 (X 轴)", + "version": "all", + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=jungle,axis=x]" + ] + ] + }, + "minecraft:jungle_log[axis=z]": { + "class": "wood", + "nameEN": "Jungle log (Z axis)", + "nameZH": "丛林木原木 (Z 轴)", + "version": "all", + "faces": [ + "north", + "south" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=jungle,axis=z]" + ] + ] + }, + "minecraft:birch_log[axis=y]": { + "class": "wood", + "nameEN": "Birch log (Y axis)", + "nameZH": "白桦原木 (Y 轴)", + "version": "all", + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=birch,axis=y]" + ] + ] + }, + "minecraft:birch_log[axis=x]": { + "class": "wood", + "nameEN": "Birch log (X axis)", + "nameZH": "白桦原木 (X 轴)", + "version": "all", + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=birch,axis=x]" + ] + ] + }, + "minecraft:birch_log[axis=z]": { + "class": "wood", + "nameEN": "Birch log (Z axis)", + "nameZH": "白桦原木 (Z 轴)", + "version": "all", + "faces": [ + "north", + "south" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=birch,axis=z]" + ] + ] + }, + "minecraft:spruce_log[axis=y]": { + "class": "wood", + "nameEN": "Spruce log (Y axis)", + "nameZH": "云杉原木 (Y 轴)", + "version": "all", + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=spruce,axis=y]" + ] + ] + }, + "minecraft:spruce_log[axis=x]": { + "class": "wood", + "nameEN": "Spruce log (X axis)", + "nameZH": "云杉原木 (X 轴)", + "version": "all", + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=spruce,axis=x]" + ] + ] + }, + "minecraft:spruce_log[axis=z]": { + "class": "wood", + "nameEN": "Spruce log (Z axis)", + "nameZH": "云杉原木 (Z 轴)", + "version": "all", + "faces": [ + "north", + "south" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=spruce,axis=z]" + ] + ] + }, + "minecraft:oak_log[axis=y]": { + "class": "wood", + "nameEN": "Oak log (Y axis)", + "nameZH": "橡木原木 (Y 轴)", + "version": "all", + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=oak,axis=y]" + ] + ] + }, + "minecraft:oak_log[axis=x]": { + "class": "wood", + "nameEN": "Oak log (X axis)", + "nameZH": "橡木原木 (X 轴)", + "version": "all", + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=oak,axis=x]" + ] + ] + }, + "minecraft:oak_log[axis=z]": { + "class": "wood", + "nameEN": "Oak log (Z axis)", + "nameZH": "橡木原木 (Z 轴)", + "version": "all", + "faces": [ + "north", + "south" + ], + "burnable": true, + "id_replace_list": [ + [ + 12, + "minecraft:log[variant=oak,axis=z]" + ] + ] + }, + "minecraft:mangrove_log[axis=y]": { + "class": "wood", + "nameEN": "Mangrove log (Y axis)", + "nameZH": "红树原木 (Y 轴)", + "version": 19, + "burnable": true + }, + "minecraft:mangrove_log[axis=x]": { + "class": "wood", + "nameEN": "Mangrove log (X axis)", + "nameZH": "红树原木 (X 轴)", + "version": 19, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:mangrove_log[axis=z]": { + "class": "wood", + "nameEN": "Mangrove log (Z axis)", + "nameZH": "红树原木 (Z 轴)", + "version": 19, + "faces": [ + "north", + "south" + ], + "burnable": true + }, + "minecraft:crimson_hyphae[axis=y]": { + "burnable": true, + "class": "wood", + "nameEN": "Crimson hyphae", + "nameZH": "绯红树皮", + "version": 16 + }, + "minecraft:stripped_crimson_hyphae[axis=y]": { + "burnable": true, + "class": "wood", + "nameEN": "Stripped crimson hyphae", + "nameZH": "去皮绯红树皮", + "version": 16 + }, + "minecraft:stripped_crimson_stem[axis=y]": { + "burnable": true, + "class": "wood", + "nameEN": "Stripped crimson log", + "nameZH": "去皮绯红原木", + "version": 16 + }, + "minecraft:stripped_warped_hyphae[axis=y]": { + "burnable": true, + "class": "wood", + "nameEN": "Stripped warped hyphae", + "nameZH": "去皮扭曲树皮", + "version": 16 + }, + "minecraft:stripped_warped_stem[axis=y]": { + "burnable": true, + "class": "wood", + "nameEN": "Stripped warped log", + "nameZH": "去皮扭曲原木", + "version": 16 + }, + "minecraft:warped_hyphae[axis=y]": { + "burnable": true, + "class": "wood", + "nameEN": "Warped hyphae", + "nameZH": "扭曲树皮", + "version": 16 + }, + "minecraft:black_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=black]" + ] + ], + "nameEN": "Black wool", + "nameZH": "黑色羊毛", + "version": "all" + }, + "minecraft:blue_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=blue]" + ] + ], + "nameEN": "Blue wool", + "nameZH": "蓝色羊毛", + "version": "all" + }, + "minecraft:brown_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=brown]" + ] + ], + "nameEN": "Brown wool", + "nameZH": "棕色羊毛", + "version": "all" + }, + "minecraft:cyan_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=cyan]" + ] + ], + "nameEN": "Cyan wool", + "nameZH": "青色羊毛", + "version": "all" + }, + "minecraft:gray_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=gray]" + ] + ], + "nameEN": "Gray wool", + "nameZH": "灰色羊毛", + "version": "all" + }, + "minecraft:green_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=green]" + ] + ], + "nameEN": "Green wool", + "nameZH": "绿色羊毛", + "version": "all" + }, + "minecraft:light_blue_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=light_blue]" + ] + ], + "nameEN": "light blue wool", + "nameZH": "浅蓝羊毛", + "version": "all" + }, + "minecraft:light_gray_wool": { + "background": true, + "burnable": true, + "class": "wool", + "nameEN": "Light gray wool", + "nameZH": "浅灰羊毛", + "version": 13 + }, + "minecraft:lime_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=lime]" + ] + ], + "nameEN": "Lime wool", + "nameZH": "浅绿羊毛", + "version": "all" + }, + "minecraft:magenta_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=magenta]" + ] + ], + "nameEN": "Magenta wool", + "nameZH": "品红色羊毛", + "version": "all" + }, + "minecraft:orange_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=orange]" + ] + ], + "nameEN": "Orange wool", + "nameZH": "橙色羊毛", + "version": "all" + }, + "minecraft:pink_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=pink]" + ] + ], + "nameEN": "Pink wool", + "nameZH": "粉色羊毛", + "version": "all" + }, + "minecraft:purple_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=purple]" + ] + ], + "nameEN": "Purple wool", + "nameZH": "紫色羊毛", + "version": "all" + }, + "minecraft:red_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=red]" + ] + ], + "nameEN": "Red wool", + "nameZH": "红色羊毛", + "version": "all" + }, + "minecraft:silver_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=silver]" + ] + ], + "nameEN": "Light gray wool (1.12)", + "nameZH": "浅灰羊毛 (1.12)", + "version": [ + 12 + ] + }, + "minecraft:white_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=white]" + ] + ], + "nameEN": "White wool", + "nameZH": "白色羊毛", + "version": "all" + }, + "minecraft:yellow_wool": { + "background": true, + "burnable": true, + "class": "wool", + "id_replace_list": [ + [ + 12, + "minecraft:wool[color=yellow]" + ] + ], + "nameEN": "Yellow wool", + "nameZH": "黄色羊毛", + "version": "all" + }, + "minecraft:brown_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]": { + "nameEN": "Brown mushroom block", + "nameZH": "棕色蘑菇块", + "class": "mushroom", + "version": 13 + }, + "minecraft:brown_mushroom_block[variant=all_outside]": { + "nameEN": "Brown mushroom block (1.12)", + "nameZH": "棕色蘑菇块 (1.12)", + "class": "mushroom", + "version": [ + 12 + ] + }, + "minecraft:mushroom_stem[east=true,west=true,north=true,south=true,up=true,down=true]": { + "burnable": true, + "nameEN": "Mushroom stem", + "nameZH": "蘑菇柄", + "class": "mushroom", + "version": 13 + }, + "minecraft:red_mushroom_block[east=true,west=true,north=true,south=true,up=true,down=true]": { + "burnable": true, + "nameEN": "Red mushroom block", + "nameZH": "红色蘑菇块", + "class": "mushroom", + "version": 13 + }, + "minecraft:red_mushroom_block[variant=all_outside]": { + "burnable": true, + "nameEN": "Red mushroom block (1.12)", + "nameZH": "红色蘑菇块 (1.12)", + "class": "mushroom", + "version": [ + 12 + ] + }, + "minecraft:white_glazed_terracotta[facing=north]": { + "nameEN": "White glazed terracotta", + "nameZH": "白色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:orange_glazed_terracotta[facing=north]": { + "nameEN": "Orange glazed terracotta", + "nameZH": "橙色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:magenta_glazed_terracotta[facing=north]": { + "nameEN": "Magenta glazed terracotta", + "nameZH": "品红色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:light_blue_glazed_terracotta[facing=north]": { + "nameEN": "Light blue glazed terracotta", + "nameZH": "浅蓝色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:yellow_glazed_terracotta[facing=north]": { + "nameEN": "Yellow glazed terracotta", + "nameZH": "黄色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:lime_glazed_terracotta[facing=north]": { + "nameEN": "Lime glazed terracotta", + "nameZH": "浅绿色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:pink_glazed_terracotta[facing=north]": { + "nameEN": "Pink glazed terracotta", + "nameZH": "粉色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:gray_glazed_terracotta[facing=north]": { + "nameEN": "Gray glazed terracotta", + "nameZH": "灰色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:light_gray_glazed_terracotta[facing=north]": { + "nameEN": "Light gray glazed terracotta", + "nameZH": "浅灰色带釉陶瓦", + "class": "glazed_terracotta", + "version": 13, + "background": true + }, + "minecraft:silver_glazed_terracotta[facing=north]": { + "nameEN": "Silver glazed hardened clay (1.12)", + "nameZH": "银色带釉硬化粘土 (1.12)", + "class": "glazed_terracotta", + "version": [ + 12 + ], + "background": true + }, + "minecraft:cyan_glazed_terracotta[facing=north]": { + "nameEN": "Cyan glazed terracotta", + "nameZH": "青色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:blue_glazed_terracotta[facing=north]": { + "nameEN": "Blue glazed terracotta", + "nameZH": "蓝色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:brown_glazed_terracotta[facing=north]": { + "nameEN": "Brown glazed terracotta", + "nameZH": "棕色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:red_glazed_terracotta[facing=north]": { + "nameEN": "Red glazed terracotta", + "nameZH": "红色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:black_glazed_terracotta[facing=north]": { + "nameEN": "Black glazed terracotta", + "nameZH": "黑色带釉陶瓦", + "class": "glazed_terracotta", + "version": "all", + "background": true + }, + "minecraft:white_concrete_powder": { + "nameEN": "White concrete powder", + "nameZH": "白色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=white]" + ] + ] + }, + "minecraft:orange_concrete_powder": { + "nameEN": "Orange concrete powder", + "nameZH": "橙色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=orange]" + ] + ] + }, + "minecraft:magenta_concrete_powder": { + "nameEN": "Magenta concrete powder", + "nameZH": "品红色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=magenta]" + ] + ] + }, + "minecraft:light_blue_concrete_powder": { + "nameEN": "Light blue concrete powder", + "nameZH": "浅蓝色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=light_blue]" + ] + ] + }, + "minecraft:yellow_concrete_powder": { + "nameEN": "Yellow concrete powder", + "nameZH": "黄色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=yellow]" + ] + ] + }, + "minecraft:lime_concrete_powder": { + "nameEN": "Lime concrete powder", + "nameZH": "浅绿色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=lime]" + ] + ] + }, + "minecraft:pink_concrete_powder": { + "nameEN": "Pink concrete powder", + "nameZH": "粉色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=pink]" + ] + ] + }, + "minecraft:gray_concrete_powder": { + "nameEN": "Gray concrete powder", + "nameZH": "灰色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=gray]" + ] + ] + }, + "minecraft:light_gray_concrete_powder": { + "nameEN": "Light gray concrete powder", + "nameZH": "浅灰色混凝土粉末", + "class": "concrete_powder", + "version": 13, + "faces": [ + "up", + "north", + "south", + "east", + "west" + ] + }, + "minecraft:silver_concrete_powder": { + "nameEN": "Silver concrete powder (1.12)", + "nameZH": "银色混凝土粉末 (1.12)", + "class": "concrete_powder", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "version": [ + 12 + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=silver]" + ] + ] + }, + "minecraft:cyan_concrete_powder": { + "nameEN": "Cyan concrete powder", + "nameZH": "青色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=cyan]" + ] + ] + }, + "minecraft:blue_concrete_powder": { + "nameEN": "Blue concrete powder", + "nameZH": "蓝色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=blue]" + ] + ] + }, + "minecraft:brown_concrete_powder": { + "nameEN": "Brown concrete powder", + "nameZH": "棕色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=brown]" + ] + ] + }, + "minecraft:red_concrete_powder": { + "nameEN": "Red concrete powder", + "nameZH": "红色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=red]" + ] + ] + }, + "minecraft:black_concrete_powder": { + "nameEN": "Black concrete powder", + "nameZH": "黑色混凝土粉末", + "class": "concrete_powder", + "version": "all", + "faces": [ + "up", + "north", + "south", + "east", + "west" + ], + "id_replace_list": [ + [ + 12, + "minecraft:concrete_powder[color=black]" + ] + ] + }, + "minecraft:bedrock": { + "nameEN": "Bedrock", + "nameZH": "基岩", + "class": "creative_only", + "version": "all", + "reproducible": false, + "rare": true + }, + "minecraft:budding_amethyst": { + "nameEN": "Budding amethyst", + "nameZH": "紫水晶母岩", + "class": "creative_only", + "version": 17, + "reproducible": false, + "rare": true + }, + "minecraft:reinforced_deepslate": { + "nameEN": "Reinforced deepslate", + "nameZH": "强化深板岩", + "class": "creative_only", + "version": 19, + "reproducible": false, + "rare": true + }, + "minecraft:bamboo_planks": { + "nameEN": "Bamboo planks", + "nameZH": "竹板", + "class": "planks", + "version": 20, + "burnable": true + }, + "minecraft:cherry_planks": { + "nameEN": "Cherry planks", + "nameZH": "樱花木板", + "class": "planks", + "version": 20, + "burnable": true + }, + "minecraft:bamboo_mosaic": { + "nameEN": "Bamboo mosaic", + "nameZH": "竹马赛克", + "class": "planks", + "version": 20, + "burnable": true + }, + "minecraft:bamboo_block[axis=y]": { + "class": "wood", + "nameEN": "Bamboo block (Y axis)", + "nameZH": "竹块 (Y 轴)", + "version": 20, + "burnable": true + }, + "minecraft:bamboo_block[axis=x]": { + "class": "wood", + "nameEN": "Bamboo block (X axis)", + "nameZH": "竹块 (X 轴)", + "version": 20, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:bamboo_block[axis=z]": { + "class": "wood", + "nameEN": "Bamboo block (Z axis)", + "nameZH": "竹块 (Z 轴)", + "version": 20, + "faces": [ + "north", + "south" + ], + "burnable": true + }, + "minecraft:stripped_bamboo_block[axis=y]": { + "class": "wood", + "nameEN": "Bamboo block (Y axis)", + "nameZH": "去皮竹块 (Y 轴)", + "version": 20, + "burnable": true + }, + "minecraft:stripped_bamboo_block[axis=x]": { + "class": "wood", + "nameEN": "Bamboo block (X axis)", + "nameZH": "去皮竹块 (X 轴)", + "version": 20, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:stripped_bamboo_block[axis=z]": { + "class": "wood", + "nameEN": "Bamboo block (Z axis)", + "nameZH": "去皮竹块 (Z 轴)", + "version": 20, + "faces": [ + "north", + "south" + ], + "burnable": true + }, + "minecraft:cherry_log[axis=y]": { + "class": "wood", + "nameEN": "Cherry log (Y axis)", + "nameZH": "樱花原木 (Y 轴)", + "version": 20, + "burnable": true + }, + "minecraft:cherry_log[axis=x]": { + "class": "wood", + "nameEN": "Cherry log (X axis)", + "nameZH": "樱花原木 (X 轴)", + "version": 20, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:cherry_log[axis=z]": { + "class": "wood", + "nameEN": "Cherry log (Z axis)", + "nameZH": "樱花原木 (Z 轴)", + "version": 20, + "faces": [ + "north", + "south" + ], + "burnable": true + }, + "minecraft:stripped_cherry_log[axis=y]": { + "class": "wood", + "nameEN": "Cherry log (Y axis)", + "nameZH": "去皮樱花原木 (Y 轴)", + "version": 20, + "burnable": true + }, + "minecraft:stripped_cherry_log[axis=x]": { + "class": "wood", + "nameEN": "Cherry log (X axis)", + "nameZH": "去皮樱花原木 (X 轴)", + "version": 20, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:stripped_cherry_log[axis=z]": { + "class": "wood", + "nameEN": "Cherry log (Z axis)", + "nameZH": "去皮樱花原木 (Z 轴)", + "version": 20, + "faces": [ + "north", + "south" + ], + "burnable": true + }, + "minecraft:pale_oak_wood[axis=y]": { + "class": "wood", + "nameEN": "Pale oak log (Y axis)", + "nameZH": "苍白原木 (Y 轴)", + "version": 21, + "burnable": true + }, + "minecraft:pale_oak_wood[axis=x]": { + "class": "wood", + "nameEN": "Pale oak log (X axis)", + "nameZH": "苍白原木 (X 轴)", + "version": 21, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:pale_oak_wood[axis=z]": { + "class": "wood", + "nameEN": "Pale oak log (Z axis)", + "nameZH": "苍白原木 (Z 轴)", + "version": 21, + "faces": [ + "north", + "south" + ], + "burnable": true + }, + "minecraft:stripped_pale_oak_wood[axis=y]": { + "class": "wood", + "nameEN": "Pale oak log (Y axis)", + "nameZH": "去皮苍白原木 (Y 轴)", + "version": 21, + "burnable": true + }, + "minecraft:stripped_pale_oak_wood[axis=x]": { + "class": "wood", + "nameEN": "Pale oak log (X axis)", + "nameZH": "去皮苍白原木 (X 轴)", + "version": 21, + "faces": [ + "east", + "west", + "up", + "down" + ], + "burnable": true + }, + "minecraft:stripped_pale_oak_wood[axis=z]": { + "class": "wood", + "nameEN": "Pale oak log (Z axis)", + "nameZH": "去皮苍白原木 (Z 轴)", + "version": 21, + "faces": [ + "north", + "south" + ], + "burnable": true + } } \ No newline at end of file diff --git a/VisualCraftL/VCL_internal.h b/VisualCraftL/VCL_internal.h index 392e26a2..410012a0 100644 --- a/VisualCraftL/VCL_internal.h +++ b/VisualCraftL/VCL_internal.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,9 +24,11 @@ This file is part of SlopeCraft. #define SLOPECRAFT_VISUALCRAFTL_VCL_INTERNAL_H #include "VisualCraftL.h" -#include -// #include +#include void VCL_report(VCL_report_type_t, const char *, bool flush = false) noexcept; -#endif // SLOPECRAFT_VISUALCRAFTL_VCL_INTERNAL_H \ No newline at end of file +void write_to_string_deliver(std::string_view sv, + VCL_string_deliver *strp) noexcept; + +#endif // SLOPECRAFT_VISUALCRAFTL_VCL_INTERNAL_H \ No newline at end of file diff --git a/VisualCraftL/VisualCraftL.cpp b/VisualCraftL/VisualCraftL.cpp index 922326cd..6b266c6d 100644 --- a/VisualCraftL/VisualCraftL.cpp +++ b/VisualCraftL/VisualCraftL.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ This file is part of SlopeCraft. #include #include +#include #include "BlockStateList.h" #include "ParseResourcePack.h" @@ -43,36 +44,8 @@ VCL_EXPORT_FUN void VCL_destroy_kernel(VCL_Kernel *const ptr) { } } -VCL_EXPORT_FUN VCL_resource_pack * -VCL_create_resource_pack(const int zip_file_count, - const char *const *const zip_file_names) { - if (zip_file_count <= 0) { - return nullptr; - } - - bool ok = true; - zipped_folder zf = zipped_folder::from_zip(zip_file_names[0], &ok); - - if (!ok) { - std::string msg = fmt::format("Failed to parse {}\n", zip_file_names[0]); - - VCL_report(VCL_report_type_t::error, msg.c_str(), true); - return nullptr; - } - - for (int zfidx = 1; zfidx < zip_file_count; zfidx++) { - zipped_folder z = zipped_folder::from_zip(zip_file_names[zfidx], &ok); - - if (!ok) { - std::string msg = - fmt::format("Failed to parse {}\n", zip_file_names[zfidx]); - VCL_report(VCL_report_type_t::error, msg.c_str(), true); - return nullptr; - } - - zf.merge_from_base(std::move(z)); - } - +VCL_resource_pack *zip_folder_to_resource_pack( + const zipped_folder &zf) noexcept { VCL_resource_pack *const rp = new VCL_resource_pack; if (!rp->add_colormaps(zf)) { @@ -117,15 +90,90 @@ VCL_create_resource_pack(const int zip_file_count, return rp; } +VCL_EXPORT_FUN VCL_resource_pack *VCL_create_resource_pack( + const int zip_file_count, const char *const *const zip_file_names) { + if (zip_file_count <= 0) { + return nullptr; + } + auto parse_zip = [](const char *filename) noexcept { + std::optional zf_opt = zipped_folder::from_zip(filename); + + if (not zf_opt) { + std::string msg = std::format("Failed to parse {}\n", filename); + VCL_report(VCL_report_type_t::error, msg.c_str(), true); + } + return zf_opt; + }; + + zipped_folder zf; + { + std::optional zf_opt = parse_zip(zip_file_names[0]); + + if (not zf_opt) { + return nullptr; + } + zf = std::move(zf_opt.value()); + } + + for (int zfidx = 1; zfidx < zip_file_count; zfidx++) { + std::optional zf_opt = parse_zip(zip_file_names[0]); + if (not zf_opt) { + return nullptr; + } + + zf.merge_from_base(std::move(zf_opt.value())); + } + + return zip_folder_to_resource_pack(zf); +} + +[[nodiscard]] VCL_EXPORT_FUN VCL_resource_pack * +VCL_create_resource_pack_from_buffers(const size_t zip_count, + const VCL_read_only_buffer *file_contents, + const char *const *const zip_file_names) { + if (zip_count <= 0) { + return nullptr; + } + zipped_folder zf; + { + auto zf_opt = zipped_folder::from_zip( + zip_file_names[0], + {reinterpret_cast(file_contents[0].data), + file_contents[0].size}); + if (not zf_opt) { + VCL_report(VCL_report_type_t::error, + std::format("Failed to parse {}\n", zip_file_names[0]).c_str(), + true); + return nullptr; + } + zf = std::move(zf_opt.value()); + } + for (size_t idx = 1; idx < zip_count; idx++) { + auto zf_opt = zipped_folder::from_zip( + zip_file_names[idx], + {reinterpret_cast(file_contents[idx].data), + file_contents[idx].size}); + if (not zf_opt) { + VCL_report( + VCL_report_type_t::error, + std::format("Failed to parse {}\n", zip_file_names[idx]).c_str(), + true); + return nullptr; + } + zf.merge_from_base(std::move(zf_opt.value())); + } + + return zip_folder_to_resource_pack(zf); +} + VCL_EXPORT_FUN void VCL_destroy_resource_pack(VCL_resource_pack *const ptr) { if (ptr != nullptr) { delete ptr; } } -[[nodiscard]] VCL_EXPORT_FUN VCL_block_state_list * -VCL_create_block_state_list(const int file_count, - const char *const *const json_file_names) { +[[nodiscard]] VCL_EXPORT_FUN VCL_block_state_list *VCL_create_block_state_list( + const int file_count, const char *const *const json_file_names) { if (file_count <= 0 || json_file_names == nullptr) { return nullptr; } @@ -139,8 +187,8 @@ VCL_create_block_state_list(const int file_count, return bsl; } -VCL_EXPORT_FUN void -VCL_destroy_block_state_list(VCL_block_state_list *const ptr) { +VCL_EXPORT_FUN void VCL_destroy_block_state_list( + VCL_block_state_list *const ptr) { delete ptr; } @@ -267,7 +315,6 @@ VCL_EXPORT_FUN void VCL_display_resource_pack(const VCL_resource_pack *rp, VCL_EXPORT_FUN const uint32_t *VCL_get_colormap(const VCL_resource_pack *rp, bool is_foliage, int *rows, int *cols) { - if (rows != nullptr) { *rows = 256; } @@ -312,36 +359,34 @@ void VCL_display_block_state_list(const VCL_block_state_list *bsl) { VCL_report(VCL_report_type_t::information, msg.c_str(), true); } -VCL_EXPORT_FUN double -VCL_estimate_color_num(size_t num_layers, size_t num_foreground, - size_t num_background, - size_t num_nontransparent_non_background) { +VCL_EXPORT_FUN double VCL_estimate_color_num( + size_t num_layers, size_t num_foreground, size_t num_background, + size_t num_nontransparent_non_background) { double a = num_background; double b = num_foreground; double num_stacked = 0; switch (num_foreground) { - case 0: - num_stacked = a; - break; - case 1: - num_stacked = a + a * b; - break; - case 2: - num_stacked = a + (num_layers + 1) * b * a; - break; - default: - num_stacked = a + a * b * (std::pow(b - 1, num_layers + 1) - 1) / (b - 2); - break; + case 0: + num_stacked = a; + break; + case 1: + num_stacked = a + a * b; + break; + case 2: + num_stacked = a + (num_layers + 1) * b * a; + break; + default: + num_stacked = a + a * b * (std::pow(b - 1, num_layers + 1) - 1) / (b - 2); + break; } return num_stacked + num_nontransparent_non_background; } -VCL_EXPORT_FUN bool -VCL_set_resource_copy(const VCL_resource_pack *const rp, - const VCL_block_state_list *const bsl, - const VCL_set_resource_option &option) { +VCL_EXPORT_FUN bool VCL_set_resource_copy( + const VCL_resource_pack *const rp, const VCL_block_state_list *const bsl, + const VCL_set_resource_option &option) { if (rp == nullptr || bsl == nullptr) { return false; } @@ -368,10 +413,9 @@ VCL_set_resource_copy(const VCL_resource_pack *const rp, return ret; } -VCL_EXPORT_FUN bool -VCL_set_resource_move(VCL_resource_pack **rp_ptr, - VCL_block_state_list **bsl_ptr, - const VCL_set_resource_option &option) { +VCL_EXPORT_FUN bool VCL_set_resource_move( + VCL_resource_pack **rp_ptr, VCL_block_state_list **bsl_ptr, + const VCL_set_resource_option &option) { if (rp_ptr == nullptr || bsl_ptr == nullptr) { return false; } @@ -472,10 +516,9 @@ VCL_EXPORT_FUN size_t VCL_num_basic_colors() { return TokiVC::LUT_bcitb().size(); } -VCL_EXPORT_FUN int -VCL_get_basic_color_composition(size_t color_idx, - const VCL_block **const blocks_dest, - uint32_t *const color) { +VCL_EXPORT_FUN int VCL_get_basic_color_composition( + size_t color_idx, const VCL_block **const blocks_dest, + uint32_t *const color) { std::shared_lock lkgd(TokiVC_internal::global_lock); if (!TokiVC_internal::is_basic_color_set_ready) { @@ -514,12 +557,11 @@ VCL_get_basic_color_composition(size_t color_idx, return num_blocks; } -VCL_EXPORT_FUN bool -VCL_set_allowed_blocks(const VCL_block *const *const blocks_allowed, - size_t num_block_allowed) { +VCL_EXPORT_FUN bool VCL_set_allowed_blocks( + const VCL_block *const *const blocks_allowed, size_t num_block_allowed) { std::unique_lock lkgd(TokiVC_internal::global_lock); - return TokiVC::set_allowed_no_lock(blocks_allowed, num_block_allowed); + return TokiVC::set_allowed_no_lock({blocks_allowed, num_block_allowed}); } VCL_EXPORT_FUN void VCL_discard_allowed_blocks() { std::unique_lock lkgd(TokiVC_internal::global_lock); @@ -527,7 +569,6 @@ VCL_EXPORT_FUN void VCL_discard_allowed_blocks() { } VCL_EXPORT_FUN bool VCL_is_allowed_colorset_ok() { - std::shared_lock lkgd(TokiVC_internal::global_lock); if (!TokiVC_internal::is_basic_color_set_ready) { @@ -569,7 +610,7 @@ VCL_EXPORT_FUN size_t VCL_get_allowed_color_id( std::shared_lock lkgd(TokiVC_internal::global_lock); if (!TokiVC_internal::is_basic_color_set_ready || - !TokiVC_internal::is_basic_color_set_ready) { + !TokiVC_internal::is_allowed_color_set_ready) { return 0; } @@ -685,13 +726,13 @@ VCL_EXPORT_FUN size_t VCL_get_blocks_from_block_state_list_match_const( size_t write_counter = 0; const bool can_write = - (array_of_const_VCL_block != nullptr) && (array_capcity > 0); + (array_of_const_VCL_block not_eq nullptr) and (array_capcity > 0); for (const auto &pair : bsl->block_states()) { if (pair.second.match(version, f)) { available_block_counter++; - if (can_write && (write_counter < array_capcity)) { + if (can_write and (write_counter < array_capcity)) { array_of_const_VCL_block[write_counter] = &pair.second; write_counter++; } @@ -710,8 +751,7 @@ VCL_EXPORT_FUN bool VCL_is_block_enabled(const VCL_block *b) { } VCL_EXPORT_FUN void VCL_set_block_enabled(VCL_block *b, bool val) { - if (b == nullptr) - return; + if (b == nullptr) return; b->set_disabled(!val); } @@ -721,7 +761,11 @@ VCL_EXPORT_FUN const char *VCL_face_t_to_str(VCL_face_t f) { } VCL_EXPORT_FUN VCL_face_t VCL_str_to_face_t(const char *str, bool *ok) { - return string_to_face_idx(str, ok); + auto res = string_to_face_idx(str); + if (ok not_eq nullptr) { + *ok = res.has_value(); + } + return res.value_or(VCL_face_t::face_up); } VCL_EXPORT_FUN bool VCL_get_block_attribute(const VCL_block *b, @@ -795,7 +839,7 @@ VCL_EXPORT_FUN bool VCL_compare_block(const VCL_block *b1, } } - return std::less()(*b1->full_id_ptr(), *b2->full_id_ptr()); + return std::less()(*b1->full_id_ptr(), *b2->full_id_ptr()); } VCL_EXPORT_FUN VCL_block_class_t VCL_string_to_block_class(const char *str, @@ -803,9 +847,8 @@ VCL_EXPORT_FUN VCL_block_class_t VCL_string_to_block_class(const char *str, return string_to_block_class(str, ok); } -[[nodiscard]] VCL_EXPORT_FUN VCL_model * -VCL_get_block_model(const VCL_block *block, - const VCL_resource_pack *resource_pack) { +[[nodiscard]] VCL_EXPORT_FUN VCL_model *VCL_get_block_model( + const VCL_block *block, const VCL_resource_pack *resource_pack) { if (block->full_id_ptr() == nullptr) { return nullptr; } @@ -824,9 +867,8 @@ VCL_get_block_model(const VCL_block *block, return ret; } -[[nodiscard]] VCL_EXPORT_FUN VCL_model * -VCL_get_block_model_by_name(const VCL_resource_pack *rp, const char *name) { - +[[nodiscard]] VCL_EXPORT_FUN VCL_model *VCL_get_block_model_by_name( + const VCL_resource_pack *rp, const char *name) { auto result = rp->find_model(name); if (result.index() == 0) { @@ -869,7 +911,7 @@ VCL_EXPORT_FUN bool VCL_compute_projection_image(const VCL_model *md, return false; } - auto img = mdptr->projection_image(face); + [[maybe_unused]] auto img = mdptr->projection_image(face); Eigen::Map map(img_buffer_argb32, 16, 16); @@ -901,14 +943,14 @@ VCL_EXPORT_FUN void VCL_display_model(const VCL_model *md) { msg.append("elements :[\n"); for (const auto &ele : mdp->elements) { msg.append(" {\n"); - msg.append(fmt::format(" from : [{}, {}, {}]\n", ele._from[0], + msg.append(std::format(" from : [{}, {}, {}]\n", ele._from[0], ele._from[1], ele._from[2])); - msg.append(fmt::format(" to : [{}, {}, {}]\n", ele._to[0], ele._to[1], + msg.append(std::format(" to : [{}, {}, {}]\n", ele._to[0], ele._to[1], ele._to[2])); msg.append(" faces : [\n"); for (const block_model::face_t &face : ele.faces) { msg.append(" {"); - msg.append(fmt::format( + msg.append(std::format( "uv_start=[{},{}],uv_end=[{},{}],rot={},is_hidden={},texture={}", face.uv_start[0], face.uv_start[1], face.uv_end[0], face.uv_end[1], int(face.rot) * 10, face.is_hidden, (const void *)face.texture)); @@ -930,15 +972,15 @@ void default_report_callback(VCL_report_type_t type, const char *msg, bool) { const char *type_msg = nullptr; switch (type) { - case VCL_report_type_t::information: - type_msg = "Information : "; - break; - case VCL_report_type_t::warning: - type_msg = "Warning : "; - break; - case VCL_report_type_t::error: - type_msg = "Error : "; - break; + case VCL_report_type_t::information: + type_msg = "Information : "; + break; + case VCL_report_type_t::warning: + type_msg = "Warning : "; + break; + case VCL_report_type_t::error: + type_msg = "Error : "; + break; } printf("\n%s%s\n", type_msg, msg); @@ -1008,408 +1050,413 @@ static_assert(std::is_trivially_copyable_v && VCL_EXPORT_FUN VCL_biome_info VCL_get_biome_info(VCL_biome_t biome) { switch (biome) { - case VCL_biome_t::the_void: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::plains: - return VCL_biome_info{0.8, 0.4}; - case VCL_biome_t::sunflower_plains: - return VCL_biome_info{0.8, 0.4}; - case VCL_biome_t::snowy_plains: - return VCL_biome_info{0, 0.5}; - case VCL_biome_t::ice_spikes: - return VCL_biome_info{0, 0.5}; - case VCL_biome_t::desert: - return VCL_biome_info{2, 0}; - case VCL_biome_t::swamp: - return VCL_biome_info{0.8, 0.9}; - case VCL_biome_t::mangrove_swamp: - return VCL_biome_info{0.8, 0.9}; - case VCL_biome_t::forest: - return VCL_biome_info{0.7, 0.4}; - case VCL_biome_t::flower_forest: - return VCL_biome_info{0.7, 0.8}; - case VCL_biome_t::birch_forest: - return VCL_biome_info{0.6, 0.6}; - case VCL_biome_t::dark_forest: - return VCL_biome_info{0.7, 0.8}; - case VCL_biome_t::old_growth_birch_forest: - return VCL_biome_info{0.6, 0.6}; - case VCL_biome_t::old_growth_pine_taiga: - return VCL_biome_info{0.3, 0.8}; - case VCL_biome_t::old_growth_spruce_taiga: - return VCL_biome_info{0.25, 0.8}; - case VCL_biome_t::taiga: - return VCL_biome_info{0.25, 0.8}; - case VCL_biome_t::snowy_taiga: - return VCL_biome_info{-0.5, 0.4}; - case VCL_biome_t::savanna: - return VCL_biome_info{2, 0}; - case VCL_biome_t::savanna_plateau: - return VCL_biome_info{2, 0}; - case VCL_biome_t::windswept_hills: - return VCL_biome_info{0.2, 0.3}; - case VCL_biome_t::windswept_gravelly_hills: - return VCL_biome_info{0.2, 0.3}; - case VCL_biome_t::windswept_forest: - return VCL_biome_info{0.2, 0.3}; - case VCL_biome_t::windswept_savanna: - return VCL_biome_info{2, 0}; - case VCL_biome_t::jungle: - return VCL_biome_info{0.95, 0.9}; - case VCL_biome_t::sparse_jungle: - return VCL_biome_info{0.95, 0.8}; - case VCL_biome_t::bamboo_jungle: - return VCL_biome_info{0.95, 0.9}; - case VCL_biome_t::badlands: - return VCL_biome_info{2, 0}; - case VCL_biome_t::eroded_badlands: - return VCL_biome_info{2, 0}; - case VCL_biome_t::wooded_badlands: - return VCL_biome_info{2, 0}; - case VCL_biome_t::meadow: - return VCL_biome_info{0.5, 0.8}; - case VCL_biome_t::grove: - return VCL_biome_info{-0.2, 0.8}; - case VCL_biome_t::snowy_slopes: - return VCL_biome_info{-0.3, 0.9}; - case VCL_biome_t::frozen_peaks: - return VCL_biome_info{-0.7, 0.9}; - case VCL_biome_t::jagged_peaks: - return VCL_biome_info{-0.7, 0.9}; - case VCL_biome_t::stony_peaks: - return VCL_biome_info{1, 0.3}; - case VCL_biome_t::river: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::frozen_river: - return VCL_biome_info{0, 0.5}; - case VCL_biome_t::beach: - return VCL_biome_info{0.8, 0.4}; - case VCL_biome_t::snowy_beach: - return VCL_biome_info{0.05, 0.3}; - case VCL_biome_t::stony_shore: - return VCL_biome_info{0.2, 0.3}; - case VCL_biome_t::warm_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::lukewarm_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::deep_lukewarm_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::deep_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::cold_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::deep_cold_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::frozen_ocean: - return VCL_biome_info{0, 0.5}; - case VCL_biome_t::deep_frozen_ocean: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::mushroom_fields: - return VCL_biome_info{0.9, 1}; - case VCL_biome_t::dripstone_caves: - return VCL_biome_info{0.8, 0.4}; - case VCL_biome_t::lush_caves: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::deep_dark: - return VCL_biome_info{0.8, 0.4}; - case VCL_biome_t::nether_wastes: - return VCL_biome_info{2, 0}; - case VCL_biome_t::warped_forest: - return VCL_biome_info{2, 0}; - case VCL_biome_t::crimson_forest: - return VCL_biome_info{2, 0}; - case VCL_biome_t::soul_sand_valley: - return VCL_biome_info{2, 0}; - case VCL_biome_t::basalt_deltas: - return VCL_biome_info{2, 0}; - case VCL_biome_t::the_end: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::end_highlands: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::end_midlands: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::small_end_islands: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::end_barrens: - return VCL_biome_info{0.5, 0.5}; - case VCL_biome_t::cherry_grove: - return VCL_biome_info{0.5, 0.8}; + case VCL_biome_t::the_void: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::plains: + return VCL_biome_info{0.8, 0.4}; + case VCL_biome_t::sunflower_plains: + return VCL_biome_info{0.8, 0.4}; + case VCL_biome_t::snowy_plains: + return VCL_biome_info{0, 0.5}; + case VCL_biome_t::ice_spikes: + return VCL_biome_info{0, 0.5}; + case VCL_biome_t::desert: + return VCL_biome_info{2, 0}; + case VCL_biome_t::swamp: + return VCL_biome_info{0.8, 0.9}; + case VCL_biome_t::mangrove_swamp: + return VCL_biome_info{0.8, 0.9}; + case VCL_biome_t::forest: + return VCL_biome_info{0.7, 0.4}; + case VCL_biome_t::flower_forest: + return VCL_biome_info{0.7, 0.8}; + case VCL_biome_t::birch_forest: + return VCL_biome_info{0.6, 0.6}; + case VCL_biome_t::dark_forest: + return VCL_biome_info{0.7, 0.8}; + case VCL_biome_t::old_growth_birch_forest: + return VCL_biome_info{0.6, 0.6}; + case VCL_biome_t::old_growth_pine_taiga: + return VCL_biome_info{0.3, 0.8}; + case VCL_biome_t::old_growth_spruce_taiga: + return VCL_biome_info{0.25, 0.8}; + case VCL_biome_t::taiga: + return VCL_biome_info{0.25, 0.8}; + case VCL_biome_t::snowy_taiga: + return VCL_biome_info{-0.5, 0.4}; + case VCL_biome_t::savanna: + return VCL_biome_info{2, 0}; + case VCL_biome_t::savanna_plateau: + return VCL_biome_info{2, 0}; + case VCL_biome_t::windswept_hills: + return VCL_biome_info{0.2, 0.3}; + case VCL_biome_t::windswept_gravelly_hills: + return VCL_biome_info{0.2, 0.3}; + case VCL_biome_t::windswept_forest: + return VCL_biome_info{0.2, 0.3}; + case VCL_biome_t::windswept_savanna: + return VCL_biome_info{2, 0}; + case VCL_biome_t::jungle: + return VCL_biome_info{0.95, 0.9}; + case VCL_biome_t::sparse_jungle: + return VCL_biome_info{0.95, 0.8}; + case VCL_biome_t::bamboo_jungle: + return VCL_biome_info{0.95, 0.9}; + case VCL_biome_t::badlands: + return VCL_biome_info{2, 0}; + case VCL_biome_t::eroded_badlands: + return VCL_biome_info{2, 0}; + case VCL_biome_t::wooded_badlands: + return VCL_biome_info{2, 0}; + case VCL_biome_t::meadow: + return VCL_biome_info{0.5, 0.8}; + case VCL_biome_t::grove: + return VCL_biome_info{-0.2, 0.8}; + case VCL_biome_t::snowy_slopes: + return VCL_biome_info{-0.3, 0.9}; + case VCL_biome_t::frozen_peaks: + return VCL_biome_info{-0.7, 0.9}; + case VCL_biome_t::jagged_peaks: + return VCL_biome_info{-0.7, 0.9}; + case VCL_biome_t::stony_peaks: + return VCL_biome_info{1, 0.3}; + case VCL_biome_t::river: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::frozen_river: + return VCL_biome_info{0, 0.5}; + case VCL_biome_t::beach: + return VCL_biome_info{0.8, 0.4}; + case VCL_biome_t::snowy_beach: + return VCL_biome_info{0.05, 0.3}; + case VCL_biome_t::stony_shore: + return VCL_biome_info{0.2, 0.3}; + case VCL_biome_t::warm_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::lukewarm_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::deep_lukewarm_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::deep_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::cold_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::deep_cold_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::frozen_ocean: + return VCL_biome_info{0, 0.5}; + case VCL_biome_t::deep_frozen_ocean: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::mushroom_fields: + return VCL_biome_info{0.9, 1}; + case VCL_biome_t::dripstone_caves: + return VCL_biome_info{0.8, 0.4}; + case VCL_biome_t::lush_caves: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::deep_dark: + return VCL_biome_info{0.8, 0.4}; + case VCL_biome_t::nether_wastes: + return VCL_biome_info{2, 0}; + case VCL_biome_t::warped_forest: + return VCL_biome_info{2, 0}; + case VCL_biome_t::crimson_forest: + return VCL_biome_info{2, 0}; + case VCL_biome_t::soul_sand_valley: + return VCL_biome_info{2, 0}; + case VCL_biome_t::basalt_deltas: + return VCL_biome_info{2, 0}; + case VCL_biome_t::the_end: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::end_highlands: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::end_midlands: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::small_end_islands: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::end_barrens: + return VCL_biome_info{0.5, 0.5}; + case VCL_biome_t::cherry_grove: + return VCL_biome_info{0.5, 0.8}; + case VCL_biome_t::pale_garden: + return VCL_biome_info{0.7, 0.8}; } return VCL_biome_info{NAN, NAN}; } -#include +#include const char *VCL_biome_name_ZH(VCL_biome_t b) noexcept { - switch (b) { - case VCL_biome_t::the_void: - return "虚空"; - case VCL_biome_t::plains: - return "平原"; - case VCL_biome_t::sunflower_plains: - return "向日葵平原"; - case VCL_biome_t::snowy_plains: - return "雪原"; - case VCL_biome_t::ice_spikes: - return "冰刺之地"; - case VCL_biome_t::desert: - return "沙漠"; - case VCL_biome_t::swamp: - return "湿地"; - case VCL_biome_t::mangrove_swamp: - return "红树林湿地"; - case VCL_biome_t::forest: - return "森林"; - case VCL_biome_t::flower_forest: - return "繁华森林"; - case VCL_biome_t::birch_forest: - return "白桦森林"; - case VCL_biome_t::dark_forest: - return "黑森林"; - case VCL_biome_t::old_growth_birch_forest: - return "原始桦木森林"; - case VCL_biome_t::old_growth_pine_taiga: - return "原始松木针叶林"; - case VCL_biome_t::old_growth_spruce_taiga: - return "原始云杉针叶林"; - case VCL_biome_t::taiga: - return "针叶林"; - case VCL_biome_t::snowy_taiga: - return "积雪针叶林"; - case VCL_biome_t::savanna: - return "热带草原"; - case VCL_biome_t::savanna_plateau: - return "热带高原"; - case VCL_biome_t::windswept_hills: - return "风袭丘陵"; - case VCL_biome_t::windswept_gravelly_hills: - return "风袭沙砾丘陵"; - case VCL_biome_t::windswept_forest: - return "风袭森林"; - case VCL_biome_t::windswept_savanna: - return "风袭热带草原"; - case VCL_biome_t::jungle: - return "丛林"; - case VCL_biome_t::sparse_jungle: - return "稀疏丛林"; - case VCL_biome_t::bamboo_jungle: - return "竹林"; - case VCL_biome_t::badlands: - return "恶地 (粘土山)"; - case VCL_biome_t::eroded_badlands: - return "风蚀恶地"; - case VCL_biome_t::wooded_badlands: - return "疏林恶地"; - case VCL_biome_t::meadow: - return "草甸"; - case VCL_biome_t::grove: - return "雪林"; - case VCL_biome_t::snowy_slopes: - return "积雪山坡"; - case VCL_biome_t::frozen_peaks: - return "冰封山峰"; - case VCL_biome_t::jagged_peaks: - return "尖峭山峰"; - case VCL_biome_t::stony_peaks: - return "裸岩山峰"; - case VCL_biome_t::river: - return "河流"; - case VCL_biome_t::frozen_river: - return "冻河"; - case VCL_biome_t::beach: - return "沙滩"; - case VCL_biome_t::snowy_beach: - return "积雪沙滩"; - case VCL_biome_t::stony_shore: - return "石岸"; - case VCL_biome_t::warm_ocean: - return "暖水海洋"; - case VCL_biome_t::lukewarm_ocean: - return "温水海洋"; - case VCL_biome_t::deep_lukewarm_ocean: - return "温水深海"; - case VCL_biome_t::ocean: - return "海洋"; - case VCL_biome_t::deep_ocean: - return "深海"; - case VCL_biome_t::cold_ocean: - return "冷水海洋"; - case VCL_biome_t::deep_cold_ocean: - return "冷水深海"; - case VCL_biome_t::frozen_ocean: - return "冻洋"; - case VCL_biome_t::deep_frozen_ocean: - return "冰冻深海"; - case VCL_biome_t::mushroom_fields: - return "蘑菇岛"; - case VCL_biome_t::dripstone_caves: - return "溶洞"; - case VCL_biome_t::lush_caves: - return "繁茂洞穴"; - case VCL_biome_t::deep_dark: - return "Deep ♂ Dark ♂"; - case VCL_biome_t::nether_wastes: - return "下界荒地"; - case VCL_biome_t::warped_forest: - return "扭曲森林"; - case VCL_biome_t::crimson_forest: - return "绯红森林"; - case VCL_biome_t::soul_sand_valley: - return "灵魂沙峡谷"; - case VCL_biome_t::basalt_deltas: - return "玄武岩三角洲"; - case VCL_biome_t::the_end: - return "末地"; - case VCL_biome_t::end_highlands: - return "末地高原"; - case VCL_biome_t::end_midlands: - return "末地内陆"; - case VCL_biome_t::small_end_islands: - return "末地小型岛屿"; - case VCL_biome_t::end_barrens: - return "末地荒地"; - case VCL_biome_t::cherry_grove: - return "樱花树林 (1.20)"; - } - return "Unamed"; + case VCL_biome_t::the_void: + return "虚空"; + case VCL_biome_t::plains: + return "平原"; + case VCL_biome_t::sunflower_plains: + return "向日葵平原"; + case VCL_biome_t::snowy_plains: + return "雪原"; + case VCL_biome_t::ice_spikes: + return "冰刺之地"; + case VCL_biome_t::desert: + return "沙漠"; + case VCL_biome_t::swamp: + return "湿地"; + case VCL_biome_t::mangrove_swamp: + return "红树林湿地"; + case VCL_biome_t::forest: + return "森林"; + case VCL_biome_t::flower_forest: + return "繁华森林"; + case VCL_biome_t::birch_forest: + return "白桦森林"; + case VCL_biome_t::dark_forest: + return "黑森林"; + case VCL_biome_t::old_growth_birch_forest: + return "原始桦木森林"; + case VCL_biome_t::old_growth_pine_taiga: + return "原始松木针叶林"; + case VCL_biome_t::old_growth_spruce_taiga: + return "原始云杉针叶林"; + case VCL_biome_t::taiga: + return "针叶林"; + case VCL_biome_t::snowy_taiga: + return "积雪针叶林"; + case VCL_biome_t::savanna: + return "热带草原"; + case VCL_biome_t::savanna_plateau: + return "热带高原"; + case VCL_biome_t::windswept_hills: + return "风袭丘陵"; + case VCL_biome_t::windswept_gravelly_hills: + return "风袭沙砾丘陵"; + case VCL_biome_t::windswept_forest: + return "风袭森林"; + case VCL_biome_t::windswept_savanna: + return "风袭热带草原"; + case VCL_biome_t::jungle: + return "丛林"; + case VCL_biome_t::sparse_jungle: + return "稀疏丛林"; + case VCL_biome_t::bamboo_jungle: + return "竹林"; + case VCL_biome_t::badlands: + return "恶地 (粘土山)"; + case VCL_biome_t::eroded_badlands: + return "风蚀恶地"; + case VCL_biome_t::wooded_badlands: + return "疏林恶地"; + case VCL_biome_t::meadow: + return "草甸"; + case VCL_biome_t::grove: + return "雪林"; + case VCL_biome_t::snowy_slopes: + return "积雪山坡"; + case VCL_biome_t::frozen_peaks: + return "冰封山峰"; + case VCL_biome_t::jagged_peaks: + return "尖峭山峰"; + case VCL_biome_t::stony_peaks: + return "裸岩山峰"; + case VCL_biome_t::river: + return "河流"; + case VCL_biome_t::frozen_river: + return "冻河"; + case VCL_biome_t::beach: + return "沙滩"; + case VCL_biome_t::snowy_beach: + return "积雪沙滩"; + case VCL_biome_t::stony_shore: + return "石岸"; + case VCL_biome_t::warm_ocean: + return "暖水海洋"; + case VCL_biome_t::lukewarm_ocean: + return "温水海洋"; + case VCL_biome_t::deep_lukewarm_ocean: + return "温水深海"; + case VCL_biome_t::ocean: + return "海洋"; + case VCL_biome_t::deep_ocean: + return "深海"; + case VCL_biome_t::cold_ocean: + return "冷水海洋"; + case VCL_biome_t::deep_cold_ocean: + return "冷水深海"; + case VCL_biome_t::frozen_ocean: + return "冻洋"; + case VCL_biome_t::deep_frozen_ocean: + return "冰冻深海"; + case VCL_biome_t::mushroom_fields: + return "蘑菇岛"; + case VCL_biome_t::dripstone_caves: + return "溶洞"; + case VCL_biome_t::lush_caves: + return "繁茂洞穴"; + case VCL_biome_t::deep_dark: + return "Deep ♂ Dark ♂"; + case VCL_biome_t::nether_wastes: + return "下界荒地"; + case VCL_biome_t::warped_forest: + return "扭曲森林"; + case VCL_biome_t::crimson_forest: + return "绯红森林"; + case VCL_biome_t::soul_sand_valley: + return "灵魂沙峡谷"; + case VCL_biome_t::basalt_deltas: + return "玄武岩三角洲"; + case VCL_biome_t::the_end: + return "末地"; + case VCL_biome_t::end_highlands: + return "末地高原"; + case VCL_biome_t::end_midlands: + return "末地内陆"; + case VCL_biome_t::small_end_islands: + return "末地小型岛屿"; + case VCL_biome_t::end_barrens: + return "末地荒地"; + case VCL_biome_t::cherry_grove: + return "樱花树林"; + case VCL_biome_t::pale_garden: + return "苍白花园"; + } + return "未命名"; } const char *VCL_biome_name_EN(VCL_biome_t b) noexcept { switch (b) { - case VCL_biome_t::the_void: - return "The Void"; - case VCL_biome_t::plains: - return "Plains"; - case VCL_biome_t::sunflower_plains: - return "Sunflower Plains"; - case VCL_biome_t::snowy_plains: - return "Snowy Plains"; - case VCL_biome_t::ice_spikes: - return "Ice Spikes"; - case VCL_biome_t::desert: - return "Desert"; - case VCL_biome_t::swamp: - return "Swamp"; - case VCL_biome_t::mangrove_swamp: - return "Mangrove Swamp"; - case VCL_biome_t::forest: - return "Forest"; - case VCL_biome_t::flower_forest: - return "Flower Forest"; - case VCL_biome_t::birch_forest: - return "Birch Forest"; - case VCL_biome_t::dark_forest: - return "Dark Forest"; - case VCL_biome_t::old_growth_birch_forest: - return "Old Growth Birch Forest"; - case VCL_biome_t::old_growth_pine_taiga: - return "Old Growth Pine Taiga"; - case VCL_biome_t::old_growth_spruce_taiga: - return "Old Growth Spruce Taiga"; - case VCL_biome_t::taiga: - return "Taiga"; - case VCL_biome_t::snowy_taiga: - return "Snowy Taiga"; - case VCL_biome_t::savanna: - return "Savanna"; - case VCL_biome_t::savanna_plateau: - return "Savanna Plateau"; - case VCL_biome_t::windswept_hills: - return "Windswept Hills"; - case VCL_biome_t::windswept_gravelly_hills: - return "Windswept Gravelly Hills"; - case VCL_biome_t::windswept_forest: - return "Windswept Forest"; - case VCL_biome_t::windswept_savanna: - return "Windswept Savanna"; - case VCL_biome_t::jungle: - return "Jungle"; - case VCL_biome_t::sparse_jungle: - return "Sparse Jungle"; - case VCL_biome_t::bamboo_jungle: - return "Bamboo Jungle"; - case VCL_biome_t::badlands: - return "Badlands"; - case VCL_biome_t::eroded_badlands: - return "Eroded Badlands"; - case VCL_biome_t::wooded_badlands: - return "Wooded Badlands"; - case VCL_biome_t::meadow: - return "Meadow"; - case VCL_biome_t::grove: - return "Grove"; - case VCL_biome_t::snowy_slopes: - return "Snowy Slopes"; - case VCL_biome_t::frozen_peaks: - return "Frozen Peaks"; - case VCL_biome_t::jagged_peaks: - return "Jagged Peaks"; - case VCL_biome_t::stony_peaks: - return "Stony Peaks"; - case VCL_biome_t::river: - return "River"; - case VCL_biome_t::frozen_river: - return "Frozen River"; - case VCL_biome_t::beach: - return "Beach"; - case VCL_biome_t::snowy_beach: - return "Snowy Beach"; - case VCL_biome_t::stony_shore: - return "Stony Shore"; - case VCL_biome_t::warm_ocean: - return "Warm Ocean"; - case VCL_biome_t::lukewarm_ocean: - return "Lukewarm Ocean"; - case VCL_biome_t::deep_lukewarm_ocean: - return "Deep Lukewarm Ocean"; - case VCL_biome_t::ocean: - return "Ocean"; - case VCL_biome_t::deep_ocean: - return "Deep Ocean"; - case VCL_biome_t::cold_ocean: - return "Cold Ocean"; - case VCL_biome_t::deep_cold_ocean: - return "Deep Cold Ocean"; - case VCL_biome_t::frozen_ocean: - return "Frozen Ocean"; - case VCL_biome_t::deep_frozen_ocean: - return "Deep Frozen Ocean"; - case VCL_biome_t::mushroom_fields: - return "Mushroom Fields"; - case VCL_biome_t::dripstone_caves: - return "Dripstone Caves"; - case VCL_biome_t::lush_caves: - return "Lush Caves"; - case VCL_biome_t::deep_dark: - return "Deep Dark"; - case VCL_biome_t::nether_wastes: - return "Nether Wastes"; - case VCL_biome_t::warped_forest: - return "Warped Forest"; - case VCL_biome_t::crimson_forest: - return "Crimson Forest"; - case VCL_biome_t::soul_sand_valley: - return "Soul Sand Valley"; - case VCL_biome_t::basalt_deltas: - return "Basalt Deltas"; - case VCL_biome_t::the_end: - return "The End"; - case VCL_biome_t::end_highlands: - return "End Highlands"; - case VCL_biome_t::end_midlands: - return "End Midlands"; - case VCL_biome_t::small_end_islands: - return "Small End Islands"; - case VCL_biome_t::end_barrens: - return "End Barrens"; - case VCL_biome_t::cherry_grove: - return "Cherry Grove"; - } - - return "Unamed"; + case VCL_biome_t::the_void: + return "The Void"; + case VCL_biome_t::plains: + return "Plains"; + case VCL_biome_t::sunflower_plains: + return "Sunflower Plains"; + case VCL_biome_t::snowy_plains: + return "Snowy Plains"; + case VCL_biome_t::ice_spikes: + return "Ice Spikes"; + case VCL_biome_t::desert: + return "Desert"; + case VCL_biome_t::swamp: + return "Swamp"; + case VCL_biome_t::mangrove_swamp: + return "Mangrove Swamp"; + case VCL_biome_t::forest: + return "Forest"; + case VCL_biome_t::flower_forest: + return "Flower Forest"; + case VCL_biome_t::birch_forest: + return "Birch Forest"; + case VCL_biome_t::dark_forest: + return "Dark Forest"; + case VCL_biome_t::old_growth_birch_forest: + return "Old Growth Birch Forest"; + case VCL_biome_t::old_growth_pine_taiga: + return "Old Growth Pine Taiga"; + case VCL_biome_t::old_growth_spruce_taiga: + return "Old Growth Spruce Taiga"; + case VCL_biome_t::taiga: + return "Taiga"; + case VCL_biome_t::snowy_taiga: + return "Snowy Taiga"; + case VCL_biome_t::savanna: + return "Savanna"; + case VCL_biome_t::savanna_plateau: + return "Savanna Plateau"; + case VCL_biome_t::windswept_hills: + return "Windswept Hills"; + case VCL_biome_t::windswept_gravelly_hills: + return "Windswept Gravelly Hills"; + case VCL_biome_t::windswept_forest: + return "Windswept Forest"; + case VCL_biome_t::windswept_savanna: + return "Windswept Savanna"; + case VCL_biome_t::jungle: + return "Jungle"; + case VCL_biome_t::sparse_jungle: + return "Sparse Jungle"; + case VCL_biome_t::bamboo_jungle: + return "Bamboo Jungle"; + case VCL_biome_t::badlands: + return "Badlands"; + case VCL_biome_t::eroded_badlands: + return "Eroded Badlands"; + case VCL_biome_t::wooded_badlands: + return "Wooded Badlands"; + case VCL_biome_t::meadow: + return "Meadow"; + case VCL_biome_t::grove: + return "Grove"; + case VCL_biome_t::snowy_slopes: + return "Snowy Slopes"; + case VCL_biome_t::frozen_peaks: + return "Frozen Peaks"; + case VCL_biome_t::jagged_peaks: + return "Jagged Peaks"; + case VCL_biome_t::stony_peaks: + return "Stony Peaks"; + case VCL_biome_t::river: + return "River"; + case VCL_biome_t::frozen_river: + return "Frozen River"; + case VCL_biome_t::beach: + return "Beach"; + case VCL_biome_t::snowy_beach: + return "Snowy Beach"; + case VCL_biome_t::stony_shore: + return "Stony Shore"; + case VCL_biome_t::warm_ocean: + return "Warm Ocean"; + case VCL_biome_t::lukewarm_ocean: + return "Lukewarm Ocean"; + case VCL_biome_t::deep_lukewarm_ocean: + return "Deep Lukewarm Ocean"; + case VCL_biome_t::ocean: + return "Ocean"; + case VCL_biome_t::deep_ocean: + return "Deep Ocean"; + case VCL_biome_t::cold_ocean: + return "Cold Ocean"; + case VCL_biome_t::deep_cold_ocean: + return "Deep Cold Ocean"; + case VCL_biome_t::frozen_ocean: + return "Frozen Ocean"; + case VCL_biome_t::deep_frozen_ocean: + return "Deep Frozen Ocean"; + case VCL_biome_t::mushroom_fields: + return "Mushroom Fields"; + case VCL_biome_t::dripstone_caves: + return "Dripstone Caves"; + case VCL_biome_t::lush_caves: + return "Lush Caves"; + case VCL_biome_t::deep_dark: + return "Deep Dark"; + case VCL_biome_t::nether_wastes: + return "Nether Wastes"; + case VCL_biome_t::warped_forest: + return "Warped Forest"; + case VCL_biome_t::crimson_forest: + return "Crimson Forest"; + case VCL_biome_t::soul_sand_valley: + return "Soul Sand Valley"; + case VCL_biome_t::basalt_deltas: + return "Basalt Deltas"; + case VCL_biome_t::the_end: + return "The End"; + case VCL_biome_t::end_highlands: + return "End Highlands"; + case VCL_biome_t::end_midlands: + return "End Midlands"; + case VCL_biome_t::small_end_islands: + return "Small End Islands"; + case VCL_biome_t::end_barrens: + return "End Barrens"; + case VCL_biome_t::cherry_grove: + return "Cherry Grove"; + case VCL_biome_t::pale_garden: + return "Pale garden"; + } + + return "Unnamed"; } VCL_EXPORT_FUN const char *VCL_biome_name(VCL_biome_t biome, uint8_t is_ZH) { @@ -1428,4 +1475,158 @@ VCL_EXPORT_FUN uint32_t VCL_locate_colormap(const VCL_resource_pack *rp, } return rp->standard_color(info, !is_grass); +} + +class VCL_preset { + public: + std::unordered_set ids; + std::unordered_set classes; +}; + +void write_to_string_deliver(std::string_view sv, + VCL_string_deliver *strp) noexcept { + if (strp == nullptr) { + return; + } + if (strp->data == nullptr || strp->capacity <= 0) { + return; + } + + const size_t written_bytes = std::min(sv.size(), strp->capacity - 1); + memcpy(strp->data, sv.data(), written_bytes); + strp->size = written_bytes; + strp->data[strp->capacity - 1] = '\0'; +} + +#include +#include +using njson = nlohmann::json; + +VCL_EXPORT VCL_preset *VCL_create_preset() { return new VCL_preset{}; } +VCL_EXPORT VCL_preset *VCL_load_preset(const char *filename, + VCL_string_deliver *error) { + write_to_string_deliver("", error); + njson block_ids; + njson block_classes; + try { + std::ifstream ifs{filename}; + auto nj = njson::parse(ifs, nullptr, true, true); + block_ids = std::move(nj.at("block_ids")); + block_classes = std::move(nj.at("block_classes")); + } catch (std::exception &e) { + auto err = std::format("Exception occurred when parsing {}, detail: {}", + filename, e.what()); + write_to_string_deliver(err, error); + return nullptr; + } + + VCL_preset *p = new VCL_preset{}; + p->ids.reserve(block_ids.size()); + + for (auto &pair : block_ids.items()) { + p->ids.emplace(std::move(pair.key())); + } + for (auto &pair : block_classes.items()) { + auto cls = magic_enum::enum_cast(pair.key()); + if (!cls.has_value()) { + auto err = std::format( + "Invalid block class \"{}\" can not be converted to " + "VCL_block_class_t", + pair.key()); + write_to_string_deliver(err, error); + delete p; + return nullptr; + } + p->classes.emplace(cls.value()); + } + return p; +} + +VCL_EXPORT bool VCL_save_preset(const VCL_preset *p, const char *filename, + VCL_string_deliver *error) { + write_to_string_deliver("", error); + njson nj; + { + njson classes, ids; + for (const auto &id : p->ids) { + ids.emplace(id, njson::object_t{}); + } + for (const auto cls : p->classes) { + classes.emplace(magic_enum::enum_name(cls), njson::object_t{}); + } + nj.emplace("block_ids", std::move(ids)); + nj.emplace("block_classes", std::move(classes)); + } + + std::ofstream ofs{filename}; + if (!ofs) { + write_to_string_deliver(std::format("Failed to open/create {}", filename), + error); + return false; + } + ofs << nj.dump(2); + ofs.close(); + return true; +} + +VCL_EXPORT void VCL_destroy_preset(VCL_preset *p) { delete p; } + +VCL_EXPORT bool VCL_preset_contains_id(const VCL_preset *p, const char *id) { + return p->ids.contains(id); +} + +VCL_EXPORT void VCL_preset_emplace_id(VCL_preset *p, const char *id) { + p->ids.emplace(id); +} + +VCL_EXPORT bool VCL_preset_contains_class(const VCL_preset *p, + VCL_block_class_t cls) { + return p->classes.contains(cls); +} + +VCL_EXPORT void VCL_preset_emplace_class(VCL_preset *p, VCL_block_class_t cls) { + p->classes.emplace(cls); +} + +VCL_EXPORT void VCL_preset_clear(VCL_preset *p) { + p->ids.clear(); + p->classes.clear(); +} + +VCL_EXPORT size_t VCL_preset_num_ids(const VCL_preset *p) { + return p->ids.size(); +} + +VCL_EXPORT size_t VCL_preset_get_ids(const VCL_preset *p, const char **id_dest, + size_t capacity) { + size_t written_num{0}; + + for (const auto &pair : p->ids) { + if (capacity <= written_num) { + break; + } + + id_dest[written_num] = pair.c_str(); + written_num++; + } + return written_num; +} + +VCL_EXPORT size_t VCL_preset_num_classes(const VCL_preset *p) { + return p->classes.size(); +} + +VCL_EXPORT size_t VCL_preset_get_classes(const VCL_preset *p, + VCL_block_class_t *class_dest, + size_t capacity) { + size_t written_num{0}; + for (const auto cls : p->classes) { + if (capacity <= written_num) { + break; + } + + class_dest[written_num] = cls; + written_num++; + } + return written_num; } \ No newline at end of file diff --git a/VisualCraftL/VisualCraftL.h b/VisualCraftL/VisualCraftL.h index cade947b..d1cc2de3 100644 --- a/VisualCraftL/VisualCraftL.h +++ b/VisualCraftL/VisualCraftL.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -163,7 +163,8 @@ enum class VCL_biome_t : uint16_t { end_midlands = 60, small_end_islands = 61, end_barrens = 62, - cherry_grove = 63 + cherry_grove = 63, + pale_garden = 64, }; /** @@ -177,6 +178,17 @@ enum class VCL_upper_direction_t : uint8_t { }; */ +struct VCL_string_deliver { + char *data{nullptr}; + size_t size{0}; + size_t capacity{0}; +}; + +struct VCL_read_only_buffer { + const void *data{nullptr}; + size_t size{0}; +}; + class VCL_Kernel; class VCL_resource_pack; class VCL_block_state_list; @@ -187,7 +199,7 @@ class VCL_GPU_Platform; class VCL_GPU_Device; class VCL_Kernel { -public: + public: VCL_Kernel() = default; virtual ~VCL_Kernel() = default; @@ -217,9 +229,9 @@ class VCL_Kernel { virtual int64_t cols() const noexcept = 0; // virtual int64_t layers() const noexcept = 0; - virtual const uint32_t * - raw_image(int64_t *const rows, int64_t *const cols, - bool *const is_row_major) const noexcept = 0; + virtual const uint32_t *raw_image( + int64_t *const rows, int64_t *const cols, + bool *const is_row_major) const noexcept = 0; //////////////////////////////////////////////////////////////////////// virtual bool convert(::SCL_convertAlgo algo, @@ -233,16 +245,16 @@ class VCL_Kernel { // [row_start,row_end) * [o,col_count) will be written int64_t row_start; int64_t row_end; - int32_t split_line_row_margin; // 0 or negative number means no split lines - int32_t split_line_col_margin; // 0 or negative number means no split lines + int32_t split_line_row_margin; // 0 or negative number means no split lines + int32_t split_line_col_margin; // 0 or negative number means no split lines int png_compress_level{9}; int png_compress_memory_level{8}; }; - virtual void flag_diagram(uint32_t *image_u8c3_rowmajor, - const flag_diagram_option &, int layer_idx, - int64_t *rows_required_dest, - int64_t *cols_required_dest) const noexcept = 0; + // virtual void flag_diagram(uint32_t *image_u8c3_rowmajor, + // const flag_diagram_option &, int layer_idx, + // int64_t *rows_required_dest, + // int64_t *cols_required_dest) const noexcept = 0; virtual bool export_flag_diagram(const char *png_filename, const flag_diagram_option &, int layer_idx) const noexcept = 0; @@ -264,6 +276,14 @@ class VCL_Kernel { const int (&weOffset)[3] = {0, 0, 0}, const char *utf8_Name = "", const char *const *const utf8_requiredMods = nullptr, const int requiredModsCount = 0) const noexcept = 0; + + struct gpu_options { + const uint64_t lib_version{SC_VERSION_U64}; + VCL_string_deliver *error_message{nullptr}; + }; + virtual bool set_gpu_resource(const VCL_GPU_Platform *, + const VCL_GPU_Device *, + const gpu_options &option) noexcept = 0; /* /// export map into Structure files (*.NBT) virtual void exportAsWESchem( @@ -281,18 +301,21 @@ extern "C" { VCL_EXPORT_FUN void VCL_destroy_kernel(VCL_Kernel *const ptr); // create and destroy resource pack +[[nodiscard]] VCL_EXPORT_FUN VCL_resource_pack *VCL_create_resource_pack( + const int zip_file_count, const char *const *const zip_file_names); [[nodiscard]] VCL_EXPORT_FUN VCL_resource_pack * -VCL_create_resource_pack(const int zip_file_count, - const char *const *const zip_file_names); -VCL_EXPORT_FUN void VCL_destroy_resource_pack(VCL_resource_pack *const ptr); +VCL_create_resource_pack_from_buffers(const size_t zip_count, + const VCL_read_only_buffer *file_contents, + const char *const *const zip_file_names); +VCL_EXPORT_FUN +void VCL_destroy_resource_pack(VCL_resource_pack *const ptr); // create and destroy block state list -[[nodiscard]] VCL_EXPORT_FUN VCL_block_state_list * -VCL_create_block_state_list(const int file_count, - const char *const *const json_file_names); +[[nodiscard]] VCL_EXPORT_FUN VCL_block_state_list *VCL_create_block_state_list( + const int file_count, const char *const *const json_file_names); -VCL_EXPORT_FUN void -VCL_destroy_block_state_list(VCL_block_state_list *const ptr); +VCL_EXPORT_FUN void VCL_destroy_block_state_list( + VCL_block_state_list *const ptr); struct VCL_set_resource_option { const uint64_t lib_version{SC_VERSION_U64}; @@ -303,21 +326,18 @@ struct VCL_set_resource_option { bool is_render_quality_fast; }; -VCL_EXPORT_FUN double -VCL_estimate_color_num(size_t num_layers, size_t num_foreground, - size_t num_background, - size_t num_nontransparent_non_background); +VCL_EXPORT_FUN double VCL_estimate_color_num( + size_t num_layers, size_t num_foreground, size_t num_background, + size_t num_nontransparent_non_background); // set resource for kernel -VCL_EXPORT_FUN bool -VCL_set_resource_copy(const VCL_resource_pack *const rp, - const VCL_block_state_list *const bsl, - const VCL_set_resource_option &option); +VCL_EXPORT_FUN bool VCL_set_resource_copy( + const VCL_resource_pack *const rp, const VCL_block_state_list *const bsl, + const VCL_set_resource_option &option); -VCL_EXPORT_FUN bool -VCL_set_resource_move(VCL_resource_pack **rp_ptr, - VCL_block_state_list **bsl_ptr, - const VCL_set_resource_option &option); +VCL_EXPORT_FUN bool VCL_set_resource_move( + VCL_resource_pack **rp_ptr, VCL_block_state_list **bsl_ptr, + const VCL_set_resource_option &option); VCL_EXPORT_FUN void VCL_discard_resource(); @@ -332,15 +352,13 @@ VCL_EXPORT_FUN size_t VCL_num_basic_colors(); /** \returns the number of blocks of this color. */ -VCL_EXPORT_FUN int -VCL_get_basic_color_composition(size_t color_idx, - const VCL_block **const blocks_dest = nullptr, - uint32_t *const color_dest = nullptr); +VCL_EXPORT_FUN int VCL_get_basic_color_composition( + size_t color_idx, const VCL_block **const blocks_dest = nullptr, + uint32_t *const color_dest = nullptr); // set allowed blocks for kernel -VCL_EXPORT_FUN bool -VCL_set_allowed_blocks(const VCL_block *const *const blocks_allowed, - size_t num_block_allowed); +VCL_EXPORT_FUN bool VCL_set_allowed_blocks( + const VCL_block *const *const blocks_allowed, size_t num_block_allowed); VCL_EXPORT_FUN void VCL_discard_allowed_blocks(); VCL_EXPORT_FUN bool VCL_is_allowed_colorset_ok(); @@ -425,12 +443,11 @@ VCL_EXPORT_FUN bool VCL_compare_block(const VCL_block *b1, const VCL_block *b2); VCL_EXPORT_FUN VCL_block_class_t VCL_string_to_block_class(const char *str, bool *ok = nullptr); -[[nodiscard]] VCL_EXPORT_FUN VCL_model * -VCL_get_block_model(const VCL_block *block, - const VCL_resource_pack *resource_pack); +[[nodiscard]] VCL_EXPORT_FUN VCL_model *VCL_get_block_model( + const VCL_block *block, const VCL_resource_pack *resource_pack); -[[nodiscard]] VCL_EXPORT_FUN VCL_model * -VCL_get_block_model_by_name(const VCL_resource_pack *, const char *name); +[[nodiscard]] VCL_EXPORT_FUN VCL_model *VCL_get_block_model_by_name( + const VCL_resource_pack *, const char *name); VCL_EXPORT_FUN void VCL_destroy_block_model(VCL_model *); @@ -463,8 +480,8 @@ i = if other values, the function will return INT_MIN. */ VCL_EXPORT_FUN int VCL_version_component(int i); -VCL_EXPORT_FUN bool -VCL_is_version_ok(uint64_t version_at_caller_s_build_time = SC_VERSION_U64); +VCL_EXPORT_FUN bool VCL_is_version_ok( + uint64_t version_at_caller_s_build_time = SC_VERSION_U64); VCL_EXPORT_FUN bool VCL_have_gpu_api(); VCL_EXPORT_FUN const char *VCL_get_GPU_api_name(); @@ -476,9 +493,8 @@ VCL_EXPORT_FUN void VCL_release_platform(VCL_GPU_Platform *); VCL_EXPORT_FUN const char *VCL_get_platform_name(const VCL_GPU_Platform *); VCL_EXPORT_FUN size_t VCL_get_device_num(const VCL_GPU_Platform *); -[[nodiscard]] VCL_EXPORT_FUN VCL_GPU_Device * -VCL_get_device(const VCL_GPU_Platform *, size_t device_idx, - int *errorcode = nullptr); +[[nodiscard]] VCL_EXPORT_FUN VCL_GPU_Device *VCL_get_device( + const VCL_GPU_Platform *, size_t device_idx, int *errorcode = nullptr); VCL_EXPORT_FUN void VCL_release_device(VCL_GPU_Device *); VCL_EXPORT_FUN const char *VCL_get_device_name(const VCL_GPU_Device *); @@ -493,6 +509,28 @@ VCL_EXPORT_FUN const char *VCL_biome_name(VCL_biome_t, uint8_t is_ZH); VCL_EXPORT_FUN uint32_t VCL_locate_colormap(const VCL_resource_pack *, bool is_grass, VCL_biome_info info, int *row, int *col); + +class VCL_preset; + +VCL_EXPORT VCL_preset *VCL_create_preset(); +VCL_EXPORT VCL_preset *VCL_load_preset(const char *filename, + VCL_string_deliver *error); +VCL_EXPORT bool VCL_save_preset(const VCL_preset *, const char *filename, + VCL_string_deliver *error); +VCL_EXPORT void VCL_destroy_preset(VCL_preset *); +VCL_EXPORT bool VCL_preset_contains_id(const VCL_preset *, const char *id); +VCL_EXPORT void VCL_preset_emplace_id(VCL_preset *, const char *id); +VCL_EXPORT bool VCL_preset_contains_class(const VCL_preset *, + VCL_block_class_t); +VCL_EXPORT void VCL_preset_emplace_class(VCL_preset *, VCL_block_class_t); +VCL_EXPORT size_t VCL_preset_num_ids(const VCL_preset *p); +VCL_EXPORT size_t VCL_preset_get_ids(const VCL_preset *p, const char **id_dest, + size_t capacity); +VCL_EXPORT size_t VCL_preset_num_classes(const VCL_preset *p); +VCL_EXPORT size_t VCL_preset_get_classes(const VCL_preset *p, + VCL_block_class_t *class_dest, + size_t capacity); +VCL_EXPORT void VCL_preset_clear(VCL_preset *); } -#endif // SLOPECRAFT_VISUALCRAFT_VISUALCRAFT_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_VISUALCRAFT_H \ No newline at end of file diff --git a/VisualCraftL/VisualCraftL_global.h b/VisualCraftL/VisualCraftL_global.h index 55a611fc..07a39c04 100644 --- a/VisualCraftL/VisualCraftL_global.h +++ b/VisualCraftL/VisualCraftL_global.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -23,10 +23,15 @@ This file is part of SlopeCraft. #ifndef SLOPECRAFT_VISUALCRAFT_VISUALCRAFTL_GLOBAL_H #define SLOPECRAFT_VISUALCRAFT_VISUALCRAFTL_GLOBAL_H -// #ifdef VISUALCRAFTL_BUILD +#ifdef Q_DECL_EXPORT +#undef Q_DECL_EXPORT +#endif +#ifdef Q_DECL_IMPORT +#undef Q_DECL_IMPORT +#endif -#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || \ - defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || \ +#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || \ + defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || \ defined(__WIN32__) || defined(__NT__) #define Q_DECL_EXPORT __declspec(dllexport) #define Q_DECL_IMPORT __declspec(dllimport) @@ -42,7 +47,7 @@ This file is part of SlopeCraft. #endif #ifdef _WIN32 -#define VCL_EXPORT_FUN __stdcall VCL_EXPORT +#define VCL_EXPORT_FUN VCL_EXPORT #else #define VCL_EXPORT_FUN VCL_EXPORT #endif @@ -53,4 +58,4 @@ This file is part of SlopeCraft. #include "SC_version_buildtime.h" #endif -#endif // SLOPECRAFT_VISUALCRAFT_VISUALCRAFTL_GLOBAL_H \ No newline at end of file +#endif // SLOPECRAFT_VISUALCRAFT_VISUALCRAFTL_GLOBAL_H \ No newline at end of file diff --git a/VisualCraftL/add_test_executables.cmake b/VisualCraftL/add_test_executables.cmake index f2525dc5..34827d25 100644 --- a/VisualCraftL/add_test_executables.cmake +++ b/VisualCraftL/add_test_executables.cmake @@ -27,8 +27,7 @@ add_executable(test_VCL_project_image tests/test_VCL_project_image.cpp) target_link_libraries(test_VCL_project_image PRIVATE VisualCraftL PNG::PNG) target_include_directories(test_VCL_project_image PRIVATE ${cli11_include_dir} - ${SlopeCraft_Nlohmann_json_include_dir} - ${SlopeCraft_Eigen3_include_dir}) + ${SlopeCraft_Nlohmann_json_include_dir}) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_project_image) # test showing model @@ -36,8 +35,7 @@ add_executable(test_VCL_model tests/test_VCL_model.cpp) target_link_libraries(test_VCL_model PRIVATE VisualCraftL) target_include_directories(test_VCL_model PRIVATE ${cli11_include_dir} - ${SlopeCraft_Nlohmann_json_include_dir} - ${SlopeCraft_Eigen3_include_dir}) + ${SlopeCraft_Nlohmann_json_include_dir}) # internal test for rotation add_executable(itest_VCL_rotate tests/itest_VCL_rotate.cpp) @@ -60,6 +58,4 @@ add_executable(test_VCL_version tests/test_VCL_version.cpp) target_link_libraries(test_VCL_version PRIVATE VisualCraftL) target_compile_features(test_VCL_version PRIVATE cxx_std_17) - - add_test(NAME test_VCL_version COMMAND test_VCL_version WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) \ No newline at end of file diff --git a/VisualCraftL/generate_tests.cmake b/VisualCraftL/generate_tests.cmake index 5d849800..db90d73d 100644 --- a/VisualCraftL/generate_tests.cmake +++ b/VisualCraftL/generate_tests.cmake @@ -5,25 +5,25 @@ set(temp_testname_prefix test_block_list) set(list_faces "up" "down" "north" "south" "east" "west") -foreach(_layers RANGE 1 3 1) - foreach(_ver RANGE 12 19) - if(_ver EQUAL 12) +foreach (_layers RANGE 1 3 1) + foreach (_ver RANGE 12 20) + if (_ver EQUAL 12) set(zip_file ${VCL_resource_12}) - else() + else () set(zip_file ${VCL_resource_latest}) - endif() + endif () - foreach(_face ${list_faces}) + foreach (_face ${list_faces}) # message(STATUS ${temp_testname_prefix}_${_ver}_${_face}) set(test_name ${temp_testname_prefix}_${_ver}_${_face}_layer${_layers}) # message(STATUS ${test_name}) add_test(NAME ${test_name} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND test_VCL_blockstate ${CMAKE_CURRENT_SOURCE_DIR}/VCL_blocks_fixed.json ${zip_file} --version ${_ver} --face ${_face} --layers ${_layers}) - endforeach(_face ${list_faces}) - endforeach(_ver RANGE 12 19) -endforeach(_layers RANGE 1 3 1) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND test_VCL_blockstate ${CMAKE_CURRENT_SOURCE_DIR}/VCL_blocks_fixed.json ${zip_file} --version ${_ver} --face ${_face} --layers ${_layers}) + endforeach (_face ${list_faces}) + endforeach (_ver RANGE 12 20) +endforeach (_layers RANGE 1 3 1) # add_test(NAME test_block_list_01 # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/VisualCraftL/install.cmake b/VisualCraftL/install.cmake index ea5b8dff..52a2faa3 100644 --- a/VisualCraftL/install.cmake +++ b/VisualCraftL/install.cmake @@ -1,81 +1,50 @@ - - -set(VCL_app_files - ${CMAKE_CURRENT_SOURCE_DIR}/VCL_blocks_fixed.json) - -include(${CMAKE_SOURCE_DIR}/cmake/configure_vanilla_zips.cmake) - -foreach(mcver RANGE 12 19) - set(VCL_current_var_name VCL_resource_${mcver}) - - if(NOT DEFINED ${VCL_current_var_name}) - message(WARNING "${VCL_current_var_name} is not defined") - endif() - - list(APPEND VCL_app_files ${${VCL_current_var_name}}) -endforeach(mcver RANGE 12 19) - -unset(mcver) +include(setup_zip_names.cmake) set(VCL_include_headers VisualCraftL.h VisualCraftL_global.h) +install(FILES ${VCL_include_headers} + DESTINATION include/SlopeCraft) -if(${WIN32}) +if (${WIN32}) # install for app install(TARGETS VisualCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . - # LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX} + # LIBRARY DESTINATION . ) install(FILES ${VCL_app_files} - DESTINATION ${CMAKE_INSTALL_PREFIX}/Blocks_VCL) + DESTINATION Blocks_VCL) + + DLLD_add_deploy(VisualCraftL + INSTALL_MODE INSTALL_DESTINATION .) - # install for lib - install(TARGETS VisualCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/bin - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib) - install(TARGETS VisualCraftL_static - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/bin - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib) - install(FILES ${VCL_include_headers} - DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/include) return() -endif() +endif () -if(${LINUX}) +if (${LINUX}) install(TARGETS VisualCraftL + EXPORT SlopeCraftTargets RUNTIME DESTINATION bin LIBRARY DESTINATION lib) - install(TARGETS VisualCraftL_static - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib) + # install(TARGETS VisualCraftL_static + # EXPORT SlopeCraftTargets + # RUNTIME DESTINATION bin + # LIBRARY DESTINATION lib) install(FILES ${VCL_app_files} DESTINATION bin/Blocks_VCL) - install(FILES ${VCL_include_headers} - DESTINATION include) - - # install(TARGETS libzip::zip - # RUNTIME DESTINATION bin - # LIBRARY DESTINATION lib) return() -endif() +endif () -if(${APPLE}) +if (${APPLE}) install(TARGETS VisualCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}) - - # Install zips. In vccl-config.json or vc-config.json, they are refered like ./Blocks_VCL/Vanilla_1_19_3.zip - install(FILES ${VCL_app_files} - DESTINATION ${CMAKE_INSTALL_PREFIX}/Blocks_VCL) - - install(TARGETS VisualCraftL - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/bin - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/lib) + EXPORT SlopeCraftTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib) return() -endif() +endif () message(WARNING "No rule to install VisualCraftL") \ No newline at end of file diff --git a/VisualCraftL/others/VisualCraftL.def.in b/VisualCraftL/others/VisualCraftL.def.in index f688ed3c..58101a32 100644 --- a/VisualCraftL/others/VisualCraftL.def.in +++ b/VisualCraftL/others/VisualCraftL.def.in @@ -2,6 +2,7 @@ EXPORTS VCL_create_kernel VCL_destroy_kernel VCL_create_resource_pack +VCL_create_resource_pack_from_buffers VCL_destroy_resource_pack VCL_create_block_state_list VCL_destroy_block_state_list @@ -66,4 +67,18 @@ VCL_release_device VCL_get_device_name VCL_get_biome_info VCL_biome_name -VCL_locate_colormap \ No newline at end of file +VCL_locate_colormap +VCL_create_preset +VCL_load_preset +VCL_save_preset +VCL_destroy_preset +VCL_preset_contains_id +VCL_preset_emplace_id +VCL_preset_contains_class +VCL_preset_emplace_class +VCL_preset_num_ids +VCL_preset_get_ids +VCL_preset_num_classes +VCL_preset_get_classes +VCL_preset_clear + diff --git a/VisualCraftL/others/VisualCraftL.rc.in b/VisualCraftL/others/VisualCraftL.rc.in index 5dcc5bf4..dc788e2e 100644 --- a/VisualCraftL/others/VisualCraftL.rc.in +++ b/VisualCraftL/others/VisualCraftL.rc.in @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -42,7 +42,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "CompanyName", "\0" VALUE "FileDescription", "VisualCraft Library\0" VALUE "FileVersion", "@VisualCraftL_VERSION@.0\0" - VALUE "LegalCopyright", "TokiNoBug\0" + VALUE "LegalCopyright", "Copyright TokiNoBug 2021-2026\0" VALUE "OriginalFilename", "VisualCraftL.dll\0" VALUE "ProductName", "VisualCraftL\0" VALUE "ProductVersion", "@VisualCraftL_VERSION@.0\0" diff --git a/VisualCraftL/setup_zip_names.cmake b/VisualCraftL/setup_zip_names.cmake new file mode 100644 index 00000000..52155d77 --- /dev/null +++ b/VisualCraftL/setup_zip_names.cmake @@ -0,0 +1,14 @@ +set(VCL_app_files + ${CMAKE_SOURCE_DIR}/VisualCraftL/VCL_blocks_fixed.json) + +foreach (mcver RANGE 12 21) + set(VCL_current_var_name VCL_resource_${mcver}) + + if (NOT DEFINED ${VCL_current_var_name}) + message(WARNING "${VCL_current_var_name} is not defined") + endif () + + list(APPEND VCL_app_files ${${VCL_current_var_name}}) +endforeach (mcver RANGE 12 21) + +unset(mcver) \ No newline at end of file diff --git a/VisualCraftL/tests/SCL_json_cvt.cpp b/VisualCraftL/tests/SCL_json_cvt.cpp index 124927ce..66f34424 100644 --- a/VisualCraftL/tests/SCL_json_cvt.cpp +++ b/VisualCraftL/tests/SCL_json_cvt.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -29,7 +29,6 @@ using std::cout, std::endl; using njson = nlohmann::json; njson &get_scl_block_list(njson &scl) noexcept { - if (scl.contains("FixedBlocks") && scl.at("FixedBlocks").is_array()) { return scl.at("FixedBlocks"); } @@ -39,14 +38,12 @@ njson &get_scl_block_list(njson &scl) noexcept { void try_copy_trait(std::string_view trait_name, const njson &obj_scl, njson &obj_vcl) { - if (obj_scl.contains(trait_name)) { obj_vcl.emplace(trait_name, obj_scl.at(trait_name)); } } int main(int argc, char **argv) { - if (argc != 3) { cout << "Usage : SCL_json_cvt [SCL json file] [VCL json file]" << endl; return 1; diff --git a/VisualCraftL/tests/VCL_json_sort.cpp b/VisualCraftL/tests/VCL_json_sort.cpp index 7334d64f..6f0dc0de 100644 --- a/VisualCraftL/tests/VCL_json_sort.cpp +++ b/VisualCraftL/tests/VCL_json_sort.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -82,17 +82,17 @@ int main(int argc, char **argv) { } switch (block_class) { - case VCL_block_class_t::concrete: - // case VCL_block_class_t::concrete_powder: - case VCL_block_class_t::glazed_terracotta: - case VCL_block_class_t::wool: - case VCL_block_class_t::shulker_box: - case VCL_block_class_t::terracotta: - it.value()["background"] = true; - break; - - default: - break; + case VCL_block_class_t::concrete: + // case VCL_block_class_t::concrete_powder: + case VCL_block_class_t::glazed_terracotta: + case VCL_block_class_t::wool: + case VCL_block_class_t::shulker_box: + case VCL_block_class_t::terracotta: + it.value()["background"] = true; + break; + + default: + break; } } diff --git a/VisualCraftL/tests/itest_VCL_rotate.cpp b/VisualCraftL/tests/itest_VCL_rotate.cpp index 2aa44a71..38b45318 100644 --- a/VisualCraftL/tests/itest_VCL_rotate.cpp +++ b/VisualCraftL/tests/itest_VCL_rotate.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify diff --git a/VisualCraftL/tests/test_VCL_blockstate.cpp b/VisualCraftL/tests/test_VCL_blockstate.cpp index ad3e6a80..bc72f29b 100644 --- a/VisualCraftL/tests/test_VCL_blockstate.cpp +++ b/VisualCraftL/tests/test_VCL_blockstate.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -31,7 +31,6 @@ This file is part of SlopeCraft. using std::cout, std::endl; int main(int argc, char **argv) { - CLI::App app; std::vector input_files; @@ -45,7 +44,7 @@ int main(int argc, char **argv) { int __version; app.add_option("--version", __version, "MC version.") ->default_val(19) - ->check(CLI::Range(12, 19, "Avaliable versions.")); + ->check(CLI::Range(12, int(max_version), "Avaliable versions.")); int __layers; app.add_option("--layers", __layers, "Max layers") ->default_val(3) @@ -83,7 +82,6 @@ int main(int argc, char **argv) { } { - std::vector zip_filenames, json_filenames; for (const std::string &i : input_files) { @@ -140,7 +138,6 @@ int main(int argc, char **argv) { return 1; } } else { - if (!VCL_set_resource_copy(rp, bsl, option)) { cout << "Failed to set resource pack" << endl; VCL_destroy_block_state_list(bsl); @@ -169,7 +166,6 @@ int main(int argc, char **argv) { return 1; } if (!VCL_set_allowed_blocks(blocks.data(), blocks.size())) { - cout << "VCL_set_allowed_blocks failed." << endl; VCL_destroy_kernel(kernel); diff --git a/VisualCraftL/tests/test_VCL_model.cpp b/VisualCraftL/tests/test_VCL_model.cpp index 79e6fc8a..311dc925 100644 --- a/VisualCraftL/tests/test_VCL_model.cpp +++ b/VisualCraftL/tests/test_VCL_model.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -30,7 +30,6 @@ This file is part of SlopeCraft. using std::cout, std::endl; int main(int argc, char **argv) { - CLI::App app; std::vector input_files; @@ -53,7 +52,7 @@ int main(int argc, char **argv) { int __version; app.add_option("--version", __version, "MC version.") ->default_val(19) - ->check(CLI::Range(12, 19, "Avaliable versions.")); + ->check(CLI::Range(12, 20, "Avaliable versions.")); */ /* diff --git a/VisualCraftL/tests/test_VCL_project_image.cpp b/VisualCraftL/tests/test_VCL_project_image.cpp index 35d7978f..92c08821 100644 --- a/VisualCraftL/tests/test_VCL_project_image.cpp +++ b/VisualCraftL/tests/test_VCL_project_image.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -35,7 +35,6 @@ bool rewrite_png(const char *const filename, Eigen::RowMajor> &img) noexcept; int main(int argc, char **argv) { - CLI::App app; std::vector input_files; @@ -57,7 +56,7 @@ int main(int argc, char **argv) { int __version; app.add_option("--version", __version, "MC version.") ->default_val(19) - ->check(CLI::Range(12, 19, "Avaliable versions.")); + ->check(CLI::Range(12, int(max_version), "Avaliable versions.")); /* int __layers; app.add_option("--layers", __layers, "Max layers") diff --git a/VisualCraftL/tests/test_VCL_version.cpp b/VisualCraftL/tests/test_VCL_version.cpp index 8faa39b8..bd7f947c 100644 --- a/VisualCraftL/tests/test_VCL_version.cpp +++ b/VisualCraftL/tests/test_VCL_version.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -26,7 +26,6 @@ This file is part of SlopeCraft. #include int main() { - std::string_view ver_str_1 = VCL_version_string(); std::string ver_str_2; diff --git a/VisualCraftL/tests/test_block_class.cpp b/VisualCraftL/tests/test_block_class.cpp index 02654f52..a20dd187 100644 --- a/VisualCraftL/tests/test_block_class.cpp +++ b/VisualCraftL/tests/test_block_class.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -25,7 +25,6 @@ This file is part of SlopeCraft. #include int main(int, char **) { - const char *arr[] = {"wood", "planks", "leaves", diff --git a/VisualCraftL/textures_need_to_override.cpp b/VisualCraftL/textures_need_to_override.cpp deleted file mode 100644 index 8a11fb2e..00000000 --- a/VisualCraftL/textures_need_to_override.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "textures_need_to_override.h" - -const std::string_view VCL_12_grass_texture_names[] = { - "minecraft:blocks/double_plant_grass_bottom", - "minecraft:blocks/double_plant_grass_top", - "minecraft:blocks/grass_side_overlay", "minecraft:blocks/grass_top"}; - -const size_t VCL_12_grass_texture_name_size = - sizeof(VCL_12_grass_texture_names) / sizeof(std::string_view); - -const std::string_view VCL_12_foliage_texture_names[] = { - "minecraft:blocks/leaves_acacia", "minecraft:blocks/leaves_big_oak", - "minecraft:blocks/leaves_birch", "minecraft:blocks/leaves_jungle", - "minecraft:blocks/leaves_oak", "minecraft:blocks/leaves_spruce", - "minecraft:blocks/vine"}; - -const size_t VCL_12_foliage_texture_name_size = - sizeof(VCL_12_foliage_texture_names) / sizeof(std::string_view); - -const std::string_view VCL_latest_grass_texture_names[] = { - "minecraft:block/grass_block_top", - "minecraft:block/grass_block_side", - "minecraft:block/grass_block_side_overlay", - "minecraft:block/grass", - "minecraft:block/tall_grass_bottom", - "minecraft:block/tall_grass_top"}; - -const size_t VCL_latest_grass_texture_name_size = - sizeof(VCL_latest_grass_texture_names) / sizeof(std::string_view); - -const std::string_view VCL_latest_foliage_texture_names[] = { - "minecraft:block/acacia_leaves", "minecraft:block/birch_leaves", - "minecraft:block/dark_oak_leaves", "minecraft:block/jungle_leaves", - "minecraft:block/mangrove_leaves", "minecraft:block/oak_leaves", - "minecraft:block/spruce_leaves", "minecraft:block/vine"}; - -const size_t VCL_latest_foliage_texture_name_size = - sizeof(VCL_latest_foliage_texture_names) / sizeof(std::string_view); \ No newline at end of file diff --git a/VisualCraftL/textures_need_to_override.h b/VisualCraftL/textures_need_to_override.h deleted file mode 100644 index 4ec7ff35..00000000 --- a/VisualCraftL/textures_need_to_override.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef SLOPECRAFT_VISUALCRAFTL_TEXTURES_NEED_TO_OVERRIDE_H -#define SLOPECRAFT_VISUALCRAFTL_TEXTURES_NEED_TO_OVERRIDE_H - -#include - -extern const std::string_view VCL_12_grass_texture_names[]; -extern const size_t VCL_12_grass_texture_name_size; -extern const std::string_view VCL_12_foliage_texture_names[]; -extern const size_t VCL_12_foliage_texture_name_size; - -extern const std::string_view VCL_latest_grass_texture_names[]; -extern const size_t VCL_latest_grass_texture_name_size; -extern const std::string_view VCL_latest_foliage_texture_names[]; -extern const size_t VCL_latest_foliage_texture_name_size; - -struct VCL_texture_names_to_override { - VCL_texture_names_to_override() = delete; - VCL_texture_names_to_override(bool is_MC12) { - if (is_MC12) { - this->grass = VCL_12_grass_texture_names; - this->num_grass = VCL_12_grass_texture_name_size; - this->foliage = VCL_12_foliage_texture_names; - this->num_foliage = VCL_12_foliage_texture_name_size; - } else { - - this->grass = VCL_latest_grass_texture_names; - this->num_grass = VCL_latest_grass_texture_name_size; - this->foliage = VCL_latest_foliage_texture_names; - this->num_foliage = VCL_latest_foliage_texture_name_size; - } - } - const std::string_view *grass; - size_t num_grass; - const std::string_view *foliage; - size_t num_foliage; -}; - -#endif // SLOPECRAFT_VISUALCRAFTL_TEXTURES_NEED_TO_OVERRIDE_H \ No newline at end of file diff --git a/cmake/add_compiler_path_to_prefix.cmake b/cmake/add_compiler_path_to_prefix.cmake index fa8d0c05..6c4ff588 100644 --- a/cmake/add_compiler_path_to_prefix.cmake +++ b/cmake/add_compiler_path_to_prefix.cmake @@ -1,6 +1,6 @@ -if(NOT DEFINED CMAKE_CXX_COMPILER) +if (NOT DEFINED CMAKE_CXX_COMPILER) message(FATAL_ERROR "Cpp compiler not found : CMAKE_CXX_COMPILER is not defined.") -endif() +endif () # message("CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}") cmake_path(GET CMAKE_CXX_COMPILER PARENT_PATH temp) @@ -8,12 +8,13 @@ cmake_path(GET temp PARENT_PATH temp) list(FIND CMAKE_PREFIX_PATH temp out_temp) -if(${out_temp} LESS 0) - message(STATUS "Added the root directory of compiler to CMAKE_PREFIX_PATH") +if (NOT ${temp} IN_LIST CMAKE_PREFIX_PATH) + message(STATUS "Added the installation prefix of compiler to CMAKE_PREFIX_PATH") list(APPEND CMAKE_PREFIX_PATH ${temp}) - #set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};${temp}) + + # set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};${temp}) message("CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}") -endif() +endif () unset(temp) unset(out_temp) diff --git a/cmake/configure_cli11.cmake b/cmake/configure_cli11.cmake deleted file mode 100644 index d6e8c39e..00000000 --- a/cmake/configure_cli11.cmake +++ /dev/null @@ -1,33 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -if(DEFINED cli11_include_dir) - if(EXISTS ${cli11_include_dir}/CLI11.hpp) - message(STATUS "cli11 found at " ${cli11_include_dir}/CLI11.hpp) - return() - else() - message(WARNING "Assigned cli11_include_dir to be " ${cli11_include_dir} - " but failed to find CLI11.hpp") - unset(cli11_include_dir) - endif() -endif() - -if(EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp) - message(STATUS "cli11 found at " ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp) - set(cli11_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/cli11) - return() -endif() - -message(STATUS "Downloading cli11.hpp ......") - -file(DOWNLOAD - https://github.com/CLIUtils/CLI11/releases/download/v2.3.2/CLI11.hpp - ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp - SHOW_PROGRESS) - -if(NOT EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp) - message(ERROR "Failed to download cli11.") - return() -endif() - -message(STATUS "cli11 downloaded successfully.") -set(cli11_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/cli11) diff --git a/cmake/configure_fmtlib.cmake b/cmake/configure_fmtlib.cmake deleted file mode 100644 index f62a8536..00000000 --- a/cmake/configure_fmtlib.cmake +++ /dev/null @@ -1,29 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -include(FetchContent) - -set(SC_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - -set(CMAKE_CXX_FLAGS "-fPIC") - -FetchContent_Declare( - fmt - GIT_REPOSITORY https://github.com/fmtlib/fmt.git - GIT_TAG "9.1.0" - OVERRIDE_FIND_PACKAGE - - # QUIET false - # FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/3rdParty - # FETCHCONTENT_TRY_FIND_PACKAGE_MODE ALWAYS FIND_PACKAGE_ARGS find_fmt_args -) - -message(STATUS "Downaloding and building fmtlib......") - -FetchContent_MakeAvailable(fmt) - -set(CMAKE_CXX_FLAGS ${SC_CMAKE_CXX_FLAGS}) -unset(SC_CMAKE_CXX_FLAGS) - -# message(STATUS "fmt_POPULATED = " ) - -# message(STATUS "find_fmt_args = " ${find_fmt_args}) diff --git a/cmake/deploy_qt.cmake.in b/cmake/deploy_qt.cmake.in deleted file mode 100644 index 63c05e64..00000000 --- a/cmake/deploy_qt.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -if(${WIN32}) - message(STATUS "Running windeployqt for @AppName@ ...") - execute_process( - COMMAND "@SlopeCraft_Qt_windeployqt_executable@" "@AppName@.exe" - WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@" - OUTPUT_QUIET - COMMAND_ERROR_IS_FATAL ANY) - - return() -endif() - -if(${APPLE}) - message(STATUS "Running macdeployqt for @AppName@ ...") - execute_process( - COMMAND "@SlopeCraft_Qt_macdeployqt_executable@" "@AppName@.app" - WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@" - OUTPUT_QUIET - COMMAND_ERROR_IS_FATAL ANY) - - return() -endif() \ No newline at end of file diff --git a/cmake/find_Eigen3.cmake b/cmake/find_Eigen3.cmake deleted file mode 100644 index 1b79264e..00000000 --- a/cmake/find_Eigen3.cmake +++ /dev/null @@ -1,74 +0,0 @@ -set(SlopeCraft_Eigen3_found OFF) - -function (SlopeCraft_examine_Eigen3_include_dir) - if(NOT DEFINED SlopeCraft_Eigen3_include_dir) - set(SlopeCraft_Eigen3_found OFF) - - else() - # examine whether the input is valid - if(EXISTS ${SlopeCraft_Eigen3_include_dir}/Eigen/Dense) - # the assigned Eigen3_include_dir is valid. nothing to do. - message("The value of SlopeCraft_Eigen3_include_dir is valid. SlopeCraft will have access to Eigen3") - #set(SlopeCraft_download_Eigen340 OFF) - set(SlopeCraft_Eigen3_found ON) - - else() - message(WARNING - "The value of SlopeCraft_Eigen3_include_dir is invalid. Failed to find file : " - ${SlopeCraft_Eigen3_include_dir}/Eigen/Dense) - #set(SlopeCraft_download_Eigen340 ON) - set(SlopeCraft_Eigen3_found OFF) - endif() - endif() - -endfunction() - - -SlopeCraft_examine_Eigen3_include_dir() - - # try to find Eigen3 by find_package -if(NOT ${SlopeCraft_Eigen3_found}) - find_package(Eigen3 3.4) - - if(${EIGEN3_FOUND}) - message(STATUS "CMake have found an libEigen by find_package") - set(SlopeCraft_Eigen3_include_dir ${EIGEN3_INCLUDE_DIR}) - set(SlopeCraft_Eigen3_found ON) - endif() -endif() - - - -# try to find Eigen3 in the 3rdParty directory -if(NOT ${SlopeCraft_Eigen3_found}) - if(EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/eigen/Eigen/Dense) - message(STATUS "Found Eigen in 3rdParty/eigen") - set(SlopeCraft_Eigen3_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/eigen) - set(SlopeCraft_Eigen3_found ON) - endif() -endif() - - -if(NOT ${SlopeCraft_Eigen3_found}) - message(STATUS "Cloning Eigen...") - execute_process( - COMMAND git clone https://gitlab.com/libeigen/eigen.git - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/3rdParty - COMMAND_ERROR_IS_FATAL ANY - ECHO_ERROR_VARIABLE - ECHO_OUTPUT_VARIABLE - ) - - message(STATUS "Successfully cloned Eigen. Checking out to tag 3.4.0") - execute_process( - COMMAND git checkout 3.4.0 - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/3rdParty/eigen - COMMAND_ERROR_IS_FATAL ANY - ECHO_ERROR_VARIABLE - ECHO_OUTPUT_VARIABLE - ) - message(STATUS "Eigen v3.4.0 is ready") - - set(SlopeCraft_download_Eigen340 OFF) - set(SlopeCraft_Eigen3_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/eigen) -endif() \ No newline at end of file diff --git a/cmake/find_HeuristicFlow.cmake b/cmake/find_HeuristicFlow.cmake deleted file mode 100644 index b5903e06..00000000 --- a/cmake/find_HeuristicFlow.cmake +++ /dev/null @@ -1,53 +0,0 @@ -set(SlopeCraft_download_HeuristicFlow OFF) - -if(NOT DEFINED SlopeCraft_HeuristicFlow_include_dir) - # try to find Heu in the 3rdParty directory - if(EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/HeuristicFlow/HeuristicFlow/Genetic) - message(STATUS "Found HeuristicFlow in 3rdParty/HeusiticFlow") - set(SlopeCraft_HeuristicFlow_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/HeuristicFlow) - - else() - set(SlopeCraft_download_HeuristicFlow ON) - endif() - -else() - #examine whether the input is valid - if(EXISTS ${SlopeCraft_HeuristicFlow_include_dir}/HeuristicFlow/Genetic) - # is valid - message("The value of SlopeCraft_HeuristicFlow_include_dir is valid. SlopeCraft will have access to Heu") - - else() - message(WARNING - "The value of SlopeCraft_HeuristicFlow_include_dir is invalid. Failed to find file : " - ${SlopeCraft_HeuristicFlow_include_dir}/HeuristicFlow/Genetic) - set(SlopeCraft_download_HeuristicFlow ON) - endif() -endif() - - -# download -if(${SlopeCraft_download_HeuristicFlow}) - #if() - message(STATUS "Cloning HeuristicFlow...") - execute_process( - COMMAND git clone https://github.com/ToKiNoBug/HeuristicFlow.git - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/3rdParty - COMMAND_ERROR_IS_FATAL ANY - ECHO_ERROR_VARIABLE - ECHO_OUTPUT_VARIABLE - ) - - message(STATUS "Successfully cloned HeuristicFlow. Checking out to tag v1.6.2.2") - execute_process( - COMMAND git checkout v1.6.2.2 - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/3rdParty/HeuristicFlow - COMMAND_ERROR_IS_FATAL ANY - ECHO_ERROR_VARIABLE - ) - - message(STATUS "HeuristicFlow is ready") - - set(SlopeCraft_download_HeuristicFlow OFF) - set(SlopeCraft_HeuristicFlow_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/HeuristicFlow) - -endif() diff --git a/cmake/find_ResourceCreator.cmake b/cmake/find_ResourceCreator.cmake deleted file mode 100644 index 76d70ce0..00000000 --- a/cmake/find_ResourceCreator.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# https://github.com/isRyven/ResourceCreator.cmake.git -set(SlopeCraft_rc_creator_found OFF) - -if(EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/ResourceCreator.cmake/.git) - set(SlopeCraft_rc_creator_found TRUE) - return() -endif() - -message(STATUS "Cloning ResourceCreator.cmake...") -execute_process(COMMAND git clone "https://github.com/isRyven/ResourceCreator.cmake.git" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/3rdParty - COMMAND_ERROR_IS_FATAL ANY -) -set(SlopeCraft_rc_creator_found TRUE) \ No newline at end of file diff --git a/cmake/find_qt6.cmake b/cmake/find_qt6.cmake deleted file mode 100644 index d0125e4d..00000000 --- a/cmake/find_qt6.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# set(temp_is_SQrd_valid OFF) - -# if(DEFINED SlopeCraft_Qt_root_dir) -# if(EXISTS ${SlopeCraft_Qt_root_dir}/bin/qmake${CMAKE_EXECUTABLE_SUFFIX}) -# message(STATUS "The value of SlopeCraft_Qt_root_dir is valid.") -# set(temp_is_SQrd_valid ON) -# else() -# message(WARNING "The value of SlopeCraft_Qt_root_dir may be invalid, failed to find qmake in directory " ${SlopeCraft_Qt_root_dir}/bin) -# endif() -# endif() - -# if(${temp_is_SQrd_valid}) -# list(PREPEND CMAKE_PREFIX_PATH ${SlopeCraft_Qt_root_dir}) -# endif() - -message(STATUS "Searching for Qt6. CMAKE_PREFIX_PATH = " ${CMAKE_PREFIX_PATH}) -find_package(QT NAMES Qt6 COMPONENTS Widgets LinguistTools REQUIRED) - -if(${WIN32}) - find_program(SlopeCraft_Qt_windeployqt_executable windeployqt PATHS ${CMAKE_PREFIX_PATH}) -endif() - -if(${APPLE}) - find_program(SlopeCraft_Qt_macdeployqt_executable macdeployqt PATHS ${CMAKE_PREFIX_PATH}) -endif() diff --git a/cmake/install.cmake b/cmake/install.cmake new file mode 100644 index 00000000..ae41c6c3 --- /dev/null +++ b/cmake/install.cmake @@ -0,0 +1,87 @@ +set(SlopeCraft_readme_and_license_files + + LICENSE + README.md + README-en.md + license-translations/LICENSE-zh.md +) + +include(${CMAKE_SOURCE_DIR}/cmake/add_compiler_path_to_prefix.cmake) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${CMAKE_BINARY_DIR}/SlopeCraftConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) +install(FILES ${CMAKE_BINARY_DIR}/SlopeCraftConfigVersion.cmake + DESTINATION lib/cmake/SlopeCraft) + +install(EXPORT SlopeCraftTargets + FILE SlopeCraftTargets.cmake + NAMESPACE SlopeCraft:: + DESTINATION lib/cmake/SlopeCraft) + +if (${WIN32}) + #include(${CMAKE_SOURCE_DIR}/cmake/scan_deps_for_lib.cmake) + install(FILES ${SlopeCraft_readme_and_license_files} + DESTINATION .) + + # set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") + # + # function(SlopeCraft_install_dll dllname dest is_required) + # find_library(dll_file + # NAMES ${dllname} + # PATHS ${CMAKE_PREFIX_PATH} + # PATH_SUFFIXES bin + # NO_CMAKE_INSTALL_PREFIX + # NO_CACHE) + # + # if (NOT dll_file) + # find_file(dll_file + # NAMES "lib${dllname}.dll" + # PATHS ${CMAKE_PREFIX_PATH} + # PATH_SUFFIXES bin + # NO_CMAKE_INSTALL_PREFIX + # NO_CACHE) + # endif () + # + # if (NOT dll_file) + # if (${is_required}) + # message(FATAL_ERROR "Failed to find ${dllname} dll.") + # else () + # message(WARNING "Failed to find ${dllname} dll.") + # endif () + # return() + # endif () + # + # message(STATUS "Install ${dll_file} to ${dest}") + # install(FILES ${dll_file} + # DESTINATION ${dest}) + # endfunction(SlopeCraft_install_dll) + # + # if (${MSVC}) + # SlopeCraft_install_dll(omp . true) + # else () + # SlopeCraft_install_dll(gomp-1 . true) + # SlopeCraft_install_dll(stdc++-6 . true) + # SlopeCraft_install_dll(gcc_s_seh-1 . true) + # SlopeCraft_install_dll(winpthread-1 . true) + # endif () + + return() +endif () + +if (${LINUX}) + install(FILES ${SlopeCraft_readme_and_license_files} + DESTINATION .) + return() +endif () + +if (${APPLE}) + install(FILES ${SlopeCraft_readme_and_license_files} + DESTINATION .) + return() +endif () + +message(WARNING "No rule to install readme and license files. Unknown system name.") \ No newline at end of file diff --git a/cmake/install_plugins.cmake b/cmake/install_plugins.cmake new file mode 100644 index 00000000..b3f0cc16 --- /dev/null +++ b/cmake/install_plugins.cmake @@ -0,0 +1,9 @@ +if(NOT ${LINUX}) + message(WARNING "This script should be only called on Linux.") +endif() + + +install(FILES ${SlopeCraft_Qt_plugin_platform_files} + DESTINATION bin/platforms) +install(FILES ${SlopeCraft_Qt_plugin_imageformat_files} + DESTINATION bin/imageformats) \ No newline at end of file diff --git a/cmake/optional_deps/7z.cmake b/cmake/optional_deps/7z.cmake new file mode 100644 index 00000000..f989fcee --- /dev/null +++ b/cmake/optional_deps/7z.cmake @@ -0,0 +1,30 @@ +find_program(z7_exe NAMES 7z 7za 7zr QUIET + HINTS "C:/Program Files/7-Zip/7z.exe;/usr/bin/7z;" + DOC "Find 7z to archive SlopeCraft block lists. 7z is required because file(ARCHIVE_CREATE in cmake too stupid to be used" +) + +if (z7_exe) + return() +endif () + +message(STATUS "7z not found, downloading...") +if (${WIN32}) + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/3rdParty") + file(DOWNLOAD "https://7-zip.org/a/7z2404-extra.7z" + "${CMAKE_BINARY_DIR}/3rdParty/7z2404-extra.7z" + EXPECTED_HASH SHA512=459C9CEFB6587BD0836CAABC106265CFBABA57D1C00F9444F0FD9A6773E199080DDF02ABB7D8E2FD461C484F61D07E0363E1448ADEDADF6E274DAED96CA4A5E6) + file(ARCHIVE_EXTRACT INPUT "${CMAKE_BINARY_DIR}/3rdParty/7z2404-extra.7z" + DESTINATION "${CMAKE_BINARY_DIR}/3rdParty/7z" + PATTERNS 7za.dll 7za.exe 7zxa.dll + VERBOSE) + set(z7_exe "${CMAKE_BINARY_DIR}/3rdParty/7z/7za.exe") + if (NOT EXISTS ${z7_exe}) + message(FATAL_ERROR "Failed to extract 7z2404-extra.7z, ${z7_exe} doesn't exist") + endif () + + return() +endif () + +message(FATAL_ERROR "Install 7z with your package manager") + +#list(GET z7_exe 0 z7_exe) \ No newline at end of file diff --git a/cmake/optional_deps/kompute.cmake b/cmake/optional_deps/kompute.cmake new file mode 100644 index 00000000..bbc90070 --- /dev/null +++ b/cmake/optional_deps/kompute.cmake @@ -0,0 +1,16 @@ +find_package(Vulkan REQUIRED) + +set(KOMPUTE_OPT_USE_BUILT_IN_FMT OFF) +set(KOMPUTE_OPT_USE_BUILT_IN_GOOGLE_TEST OFF) +set(KOMPUTE_OPT_USE_BUILT_IN_VULKAN_HEADER OFF) +FetchContent_Declare(kompute + GIT_REPOSITORY https://github.com/SlopeCraft/kompute + GIT_TAG 37afc4f814eb941adb5c0c9a76fc0d8c4ebc6e53 + OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL +) + +message(STATUS "Downaloding SlopeCraft/kompute......") +FetchContent_MakeAvailable(kompute) + +find_package(kompute REQUIRED) \ No newline at end of file diff --git a/cmake/required_deps/Eigen3.cmake b/cmake/required_deps/Eigen3.cmake new file mode 100644 index 00000000..f0f81959 --- /dev/null +++ b/cmake/required_deps/Eigen3.cmake @@ -0,0 +1,20 @@ +set(SlopeCraft_Eigen3_found OFF) +find_package(Eigen3 3.4.0 QUIET) + +if (${Eigen3_FOUND}) + message(STATUS "EIGEN3_INCLUDE_DIR = ${EIGEN3_INCLUDE_DIR}") + return() +endif () + +include(FetchContent) + +FetchContent_Declare(Eigen3 + GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git + GIT_TAG 3.4.0 + OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL +) + +FetchContent_MakeAvailable(Eigen3) + +find_package(Eigen3 3.4.0 REQUIRED) \ No newline at end of file diff --git a/cmake/required_deps/HeuristicFlow.cmake b/cmake/required_deps/HeuristicFlow.cmake new file mode 100644 index 00000000..5b0fcd08 --- /dev/null +++ b/cmake/required_deps/HeuristicFlow.cmake @@ -0,0 +1,14 @@ + +include(FetchContent) + +FetchContent_Declare(Heu + GIT_REPOSITORY https://github.com/SlopeCraft/HeuristicFlow.git + GIT_TAG v1.6.4.3 + OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL +) + +message(STATUS "Downloading HeuristicFlow") +FetchContent_MakeAvailable(Heu) + +find_package(Heu 1.6.4 REQUIRED) \ No newline at end of file diff --git a/cmake/configure_images.cmake b/cmake/required_deps/VCL-test-images.cmake similarity index 100% rename from cmake/configure_images.cmake rename to cmake/required_deps/VCL-test-images.cmake diff --git a/cmake/required_deps/_DLLDeployer.cmake b/cmake/required_deps/_DLLDeployer.cmake new file mode 100644 index 00000000..3318de64 --- /dev/null +++ b/cmake/required_deps/_DLLDeployer.cmake @@ -0,0 +1,43 @@ +#function(SC_download url filename) +# if (EXISTS ${filename}) +# file(SIZE ${filename} size) +# if (${size} GREATER 0) +# return() +# endif () +# +# file(REMOVE ${filename}) +# endif () +# +# message(STATUS "Downloading ${url} ...") +# file(DOWNLOAD ${url} ${filename} +# SHOW_PROGRESS +# REQUIRED) +# +# file(SIZE ${filename} size) +# if (${size} LESS_EQUAL 0) +# message(FATAL_ERROR "Failed to download ${filename} from ${url}, downloaded file is empty") +# endif () +#endfunction(SC_download) + +set(DLLD_download_dir ${CMAKE_BINARY_DIR}/3rdParty/DLLDeployer) +set(DLLD_file ${DLLD_download_dir}/DLLDeployer.cmake) +set(QD_file ${DLLD_download_dir}/QtDeployer.cmake) +if (${WIN32}) + file(DOWNLOAD https://github.com/SlopeCraft/SharedLibDeployer/releases/download/v1.5.1/DLLDeployer.cmake + ${DLLD_file} + EXPECTED_HASH SHA512=255C1390363E74EE0538F9CC003EAA00B039B55CBB2E17E940EE5B365D4F8FADEBAFD0DF21339FC52A10AD61A1F82FB56E080FDC96B5A617F2EAEC6D8770645A + ) + # SC_download(https://github.com/ToKiNoBug/SharedLibDeployer/releases/download/v1.4.0/DLLDeployer.cmake + # ${DLLD_file}) + include(${DLLD_file}) +endif () + +if (WIN32 OR APPLE) + file(DOWNLOAD https://github.com/SlopeCraft/SharedLibDeployer/releases/download/v1.5.1/QtDeployer.cmake + ${QD_file} + EXPECTED_HASH SHA512=7A6E7FEE90DEBC0A4E6BDC4CB65D87DF453E8B24C6ED9E188C174BF64C526CA232FA63211CA661E4B26D06A141291AB0AB3CC499B64775B23FC0E622E0CEA0B4 + ) + # SC_download(https://github.com/SlopeCraft/DLLDeployer/releases/download/v1.3/QtDeployer.cmake + # ${QD_file}) + include(${QD_file}) +endif () diff --git a/cmake/required_deps/_DylibDeployer.cmake b/cmake/required_deps/_DylibDeployer.cmake new file mode 100644 index 00000000..1214d911 --- /dev/null +++ b/cmake/required_deps/_DylibDeployer.cmake @@ -0,0 +1,18 @@ +if (NOT APPLE) + return() +endif () + +set(DylibD_download_dir ${CMAKE_SOURCE_DIR}/3rdParty/DylibDeployer.cmake) +set(DylibD_file ${DylibD_download_dir}/DylibDeployer.cmake) +file(DOWNLOAD https://github.com/SlopeCraft/DylibDeployer.cmake/releases/download/v1.0.2/DylibDeployer.cmake + ${DylibD_file} + EXPECTED_HASH SHA512=22D8E28508C424EA724294B58A22559747E33D0529358582E5497D613074204D3584FACB4FBBC85ACC3DBDA5CA67F666C7BFEE2BBF2DC02129871E354B5BC25B +) +include(${DylibD_file}) + +set(Codesigner_file ${DylibD_download_dir}/Codesigner.cmake) +file(DOWNLOAD https://github.com/SlopeCraft/DylibDeployer.cmake/releases/download/v1.0.2/Codesigner.cmake + ${Codesigner_file} + EXPECTED_HASH SHA512=2095462FB94B5F7D25953EED7ECDBA89A412B31E8C48B31CA328445106498EC088C08F35CA0C4498D6D22C60D8A26605D506DAA29E1A3D276316C5FD7FFE015E +) +include(${Codesigner_file}) \ No newline at end of file diff --git a/cmake/required_deps/_ResourceCreator.cmake b/cmake/required_deps/_ResourceCreator.cmake new file mode 100644 index 00000000..e5f6d0a8 --- /dev/null +++ b/cmake/required_deps/_ResourceCreator.cmake @@ -0,0 +1,10 @@ + +message(STATUS "Downloading ResourceCreator.cmake...") +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/3rdParty/") +set(SC_RC_location "${CMAKE_BINARY_DIR}/3rdParty/ResourceCreator.cmake") +file(DOWNLOAD "https://github.com/SlopeCraft/ResourceCreator.cmake/releases/download/v0.0.0/ResourceCreator.cmake" + ${SC_RC_location} + EXPECTED_HASH SHA512=241799A7BCC3A0AF1BD32FFD350EEEF0751D4D86DA7F11CFA6BD8F979E150F1585E9F55CBBBBAAB5C59C7379F5F93C27BF8E414BB0A603DFC5D07136C37EC6FA +) + +include(${SC_RC_location}) \ No newline at end of file diff --git a/cmake/required_deps/boost.cmake b/cmake/required_deps/boost.cmake new file mode 100644 index 00000000..2539097f --- /dev/null +++ b/cmake/required_deps/boost.cmake @@ -0,0 +1,50 @@ +if (${WIN32}) + find_package(Boost + COMPONENTS iostreams + OPTIONAL_COMPONENTS multi_array + CONFIG + ) +else () + message(STATUS "Finding boost without multi_array to avoid a fucking stupid error in BoostConfig.cmake") + find_package(Boost + COMPONENTS iostreams + ) +endif () + +if (NOT TARGET Boost::iostreams) + message(FATAL_ERROR "Failed to find Boost::iostreams, install boost first") +endif () +if (NOT TARGET Boost::multi_array) + message(WARNING "find_package failed to find Boost::multi_array, trying to find its headers manually...") + set(hints) + foreach (prefix ${CMAKE_PREFIX_PATH}) + list(APPEND hints "${prefix}/include/boost/multi_array.hpp") + endforeach () + find_file(multi_array_hpp_location NAMES multi_array.hpp + HINTS ${hints} + PATH_SUFFIXES "include/boost" + ) + unset(hpp_count) + list(LENGTH multi_array_hpp_location hpp_count) + if ((NOT multi_array_hpp_location) OR (${hpp_count} LESS_EQUAL 0)) + message(FATAL_ERROR "Boost::multi_array is not installed or not accessible, + you should install it and add the installation prefix to CMAKE_PREFIX_PATH") + endif () + + if (${hpp_count} GREATER 1) + message(STATUS "Found multiple installation of \"include/boost/multi_array.hpp\": \n\"${multi_array_hpp_location}\", select the first.") + list(GET multi_array_hpp_location 0 multi_array_hpp_location) + else () + message(STATUS "Manually found Boost::multi_array at \"${multi_array_hpp_location}\"") + endif () + message(STATUS "Making fake target named Boost::multi_array to replace the missing one. \n + Сука, what's wrong with BoostConfig.cmake and FindBoost.cmake? I have to write moooore code to fix it on ubuntu and mac. I don't experience such bug with vcpkg+windows") + + cmake_path(GET multi_array_hpp_location PARENT_PATH boost_dir_boost) + cmake_path(GET boost_dir_boost PARENT_PATH boost_dir_include) + + add_library(boost_fake_target_multi_array INTERFACE) + target_include_directories(boost_fake_target_multi_array INTERFACE ${boost_dir_include}) + add_library(Boost::multi_array ALIAS boost_fake_target_multi_array) + +endif () \ No newline at end of file diff --git a/cmake/required_deps/cereal.cmake b/cmake/required_deps/cereal.cmake new file mode 100644 index 00000000..9256590e --- /dev/null +++ b/cmake/required_deps/cereal.cmake @@ -0,0 +1,23 @@ +set(SKIP_PERFORMANCE_COMPARISON ON) +set(BUILD_SANDBOX OFF) +set(BUILD_DOC OFF) +set(CEREAL_INSTALL OFF) + +find_package(cereal 1.3.2 QUIET) + +if (${cereal_FOUND}) + return() +endif () + +include(FetchContent) + +FetchContent_Declare(cereal + GIT_REPOSITORY https://github.com/USCiLab/cereal + GIT_TAG v1.3.2 + OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL +) + +message(STATUS "Configuring cereal ...") + +FetchContent_MakeAvailable(cereal) \ No newline at end of file diff --git a/cmake/required_deps/cli11.cmake b/cmake/required_deps/cli11.cmake new file mode 100644 index 00000000..91773429 --- /dev/null +++ b/cmake/required_deps/cli11.cmake @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.19) + +if (DEFINED cli11_include_dir) + if (EXISTS ${cli11_include_dir}/CLI11.hpp) + message(STATUS "cli11 found at " ${cli11_include_dir}/CLI11.hpp) + return() + else () + message(WARNING "Assigned cli11_include_dir to be " ${cli11_include_dir} + " but failed to find CLI11.hpp") + unset(cli11_include_dir) + endif () +endif () + +if (EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp) + message(STATUS "cli11 found at " ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp) + set(cli11_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/cli11) + return() +endif () + +message(STATUS "Downloading cli11.hpp ......") + +file(DOWNLOAD + https://github.com/CLIUtils/CLI11/releases/download/v2.4.1/CLI11.hpp + ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp + EXPECTED_HASH SHA512=7DB1F5B879DF99639ADA29AD313D5E344E1B0FD30A44116DA00A6CA53BEF4BA840684CBC64290CF365569095B0915D181039777FCE780BC60E20F624623B98B9 + SHOW_PROGRESS) + +if (NOT EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/cli11/CLI11.hpp) + message(ERROR "Failed to download cli11.") + return() +endif () + +message(STATUS "cli11 downloaded successfully.") +set(cli11_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/cli11) diff --git a/cmake/required_deps/libnbt++.cmake b/cmake/required_deps/libnbt++.cmake new file mode 100644 index 00000000..e9183953 --- /dev/null +++ b/cmake/required_deps/libnbt++.cmake @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.29) + +find_package(ZLIB REQUIRED) + +find_package(libnbt++ QUIET) + +if (libnbt++_FOUND) + message(STATUS "Found installed libnbt++") + return() +endif () + +message(STATUS "Failed to find libnbt++, cloning...") +set(NBT_BUILD_SHARED OFF CACHE BOOL "") +set(NBT_BUILD_TESTS OFF CACHE BOOL "") +FetchContent_Declare(libnbt++ + GIT_REPOSITORY https://github.com/PrismLauncher/libnbtplusplus + GIT_TAG 23b955121b8217c1c348a9ed2483167a6f3ff4ad #Merge pull request #3 from TheKodeToad/max-depth-attempt2 + OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL) +message(STATUS "Cloning libnbt++...") +FetchContent_MakeAvailable(libnbt++) +find_package(libnbt++ REQUIRED) + +target_link_libraries(nbt++ ZLIB::ZLIB) +target_compile_features(nbt++ PUBLIC cxx_std_20) +if (${LINUX}) + set_target_properties(nbt++ PROPERTIES + POSITION_INDEPENDENT_CODE ON + ) +endif () \ No newline at end of file diff --git a/cmake/required_deps/libpng.cmake b/cmake/required_deps/libpng.cmake new file mode 100644 index 00000000..46ae2f95 --- /dev/null +++ b/cmake/required_deps/libpng.cmake @@ -0,0 +1,6 @@ + +if(${APPLE}) + set(CMAKE_FIND_FRAMEWORK LAST) +endif() + +find_package(PNG 1.6 REQUIRED) \ No newline at end of file diff --git a/cmake/configure_libzip.cmake b/cmake/required_deps/libzip.cmake similarity index 100% rename from cmake/configure_libzip.cmake rename to cmake/required_deps/libzip.cmake diff --git a/cmake/configure_magic_enum.cmake b/cmake/required_deps/magic_enum.cmake similarity index 69% rename from cmake/configure_magic_enum.cmake rename to cmake/required_deps/magic_enum.cmake index a6c71ea4..52c3eb21 100644 --- a/cmake/configure_magic_enum.cmake +++ b/cmake/required_deps/magic_enum.cmake @@ -2,16 +2,23 @@ cmake_minimum_required(VERSION 3.14) +find_package(magic_enum QUIET) + +if (${magic_enum_FOUND}) + return() +endif () + include(FetchContent) FetchContent_Declare(magic_enum # URL https://github.com/Neargye/magic_enum/releases/download/v0.8.2/magic_enum.hpp GIT_REPOSITORY https://github.com/Neargye/magic_enum.git - GIT_TAG "v0.8.2" + GIT_TAG "v0.9.7" OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL ) -message(STATUS "Downaloding magic_enum......") +message(STATUS "Downloading magic_enum......") FetchContent_MakeAvailable(magic_enum) \ No newline at end of file diff --git a/cmake/find_nlohmann_json.cmake b/cmake/required_deps/nlohmann_json.cmake similarity index 56% rename from cmake/find_nlohmann_json.cmake rename to cmake/required_deps/nlohmann_json.cmake index 3f2feccd..f7612c93 100644 --- a/cmake/find_nlohmann_json.cmake +++ b/cmake/required_deps/nlohmann_json.cmake @@ -1,31 +1,32 @@ set(SlopeCraft_download_njson OFF) -if(NOT DEFINED SlopeCraft_Nlohmann_json_include_dir) - if(EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/nlohmann/json.hpp) +if (NOT DEFINED SlopeCraft_Nlohmann_json_include_dir) + if (EXISTS ${CMAKE_SOURCE_DIR}/3rdParty/nlohmann/json.hpp) set(SlopeCraft_Nlohmann_json_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/nlohmann) - else() + else () set(SlopeCraft_download_njson ON) - endif() -else() + endif () +else () # check if cmake can find the json file - if(EXISTS ${SlopeCraft_Nlohmann_json_include_dir}/json.hpp) - # nothing to do - else() - message(WARNING - "The original value of SlopeCraft_Nlohmann_json_include_dir is invalid: failed to find " + if (EXISTS ${SlopeCraft_Nlohmann_json_include_dir}/json.hpp) + # nothing to do + else () + message(WARNING + "The original value of SlopeCraft_Nlohmann_json_include_dir is invalid: failed to find " ${SlopeCraft_Nlohmann_json_include_dir}/json.hpp) set(SlopeCraft_download_njson ON) - endif() -endif() + endif () +endif () -if(${SlopeCraft_download_njson}) +if (${SlopeCraft_download_njson}) message(STATUS "Downloading nlohmann json...") - file(DOWNLOAD - "https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp" + file(DOWNLOAD + "https://github.com/nlohmann/json/releases/download/v3.11.3/json.hpp" ${CMAKE_SOURCE_DIR}/3rdParty/nlohmann/json.hpp + EXPECTED_HASH SHA512=DA77FA48CA883DACF5CE147B2354E9D957AD66EDF72A7103FF5A8611C5CDA77B64F1F0CA60491295574EE158CECCCFE7797CD36FAAC5B47E75687400AC60769D SHOW_PROGRESS) message(STATUS "nlohmann json downloaded") set(SlopeCraft_download_njson OFF) set(SlopeCraft_Nlohmann_json_include_dir ${CMAKE_SOURCE_DIR}/3rdParty/nlohmann) -endif() \ No newline at end of file +endif () \ No newline at end of file diff --git a/cmake/required_deps/qt6.cmake b/cmake/required_deps/qt6.cmake new file mode 100644 index 00000000..8f36ed1c --- /dev/null +++ b/cmake/required_deps/qt6.cmake @@ -0,0 +1,41 @@ +# set(temp_is_SQrd_valid OFF) + +# if(DEFINED SlopeCraft_Qt_root_dir) +# if(EXISTS ${SlopeCraft_Qt_root_dir}/bin/qmake${CMAKE_EXECUTABLE_SUFFIX}) +# message(STATUS "The value of SlopeCraft_Qt_root_dir is valid.") +# set(temp_is_SQrd_valid ON) +# else() +# message(WARNING "The value of SlopeCraft_Qt_root_dir may be invalid, failed to find qmake in directory " ${SlopeCraft_Qt_root_dir}/bin) +# endif() +# endif() + +# if(${temp_is_SQrd_valid}) +# list(PREPEND CMAKE_PREFIX_PATH ${SlopeCraft_Qt_root_dir}) +# endif() + +message(STATUS "Searching for Qt6. CMAKE_PREFIX_PATH = " ${CMAKE_PREFIX_PATH}) +find_package(Qt6 COMPONENTS Widgets LinguistTools REQUIRED) + +# find lupdate executable +if (${SlopeCraft_update_ts_files} AND (NOT DEFINED SlopeCraft_Qt_lupdate_executable)) + find_program(SlopeCraft_Qt_lupdate_executable name lupdate PATHS ${CMAKE_PREFIX_PATH} REQUIRED) + message(STATUS "Found lupdate at : " ${SlopeCraft_Qt_lupdate_executable}) + + if (${SlopeCraft_update_ts_no_obsolete}) + set(SlopeCraft_ts_flags) + else () + set(SlopeCraft_ts_flags "-no-obsolete") + endif () +endif () + +if (${LINUX}) + if (NOT EXISTS ${Qt6_DIR}) + message(WARNING "Qt6_DIR is not set. No way to find platform plugins") + endif () + + # message(STATUS "Qt6_DIR = ${Qt6_DIR}") + file(GLOB_RECURSE SlopeCraft_Qt_plugin_platform_files "${Qt6_DIR}/../../../plugins/platforms/*") + file(GLOB_RECURSE SlopeCraft_Qt_plugin_imageformat_files "${Qt6_DIR}/../../../plugins/imageformats/*") + + # message(STATUS "Qt platform plugin files: ${SlopeCraft_Qt_plugin_platform_files}") +endif () diff --git a/cmake/required_deps/tl-expected.cmake b/cmake/required_deps/tl-expected.cmake new file mode 100644 index 00000000..11326457 --- /dev/null +++ b/cmake/required_deps/tl-expected.cmake @@ -0,0 +1,14 @@ + find_package(tl-expected 1.1.0 QUIET) + if (NOT tl-expected_FOUND) + set(EXPECTED_BUILD_TESTS OFF) + include(FetchContent) + message(STATUS "Downloading tl-expected...") + FetchContent_Declare(tl-expected + GIT_REPOSITORY https://github.com/TartanLlama/expected.git + GIT_TAG v1.1.0 + OVERRIDE_FIND_PACKAGE + EXCLUDE_FROM_ALL + ) + FetchContent_MakeAvailable(tl-expected) + find_package(tl-expected CONFIG REQUIRED) + endif () \ No newline at end of file diff --git a/cmake/configure_vanilla_zips.cmake b/cmake/required_deps/vanilla_zips.cmake similarity index 58% rename from cmake/configure_vanilla_zips.cmake rename to cmake/required_deps/vanilla_zips.cmake index c193cde5..ff6dd728 100644 --- a/cmake/configure_vanilla_zips.cmake +++ b/cmake/required_deps/vanilla_zips.cmake @@ -6,11 +6,14 @@ set(VCL_zip_names "Vanilla_1_16_5.zip" "Vanilla_1_17_1.zip" "Vanilla_1_18_2.zip" - "Vanilla_1_19_3.zip") - + "Vanilla_1_19_3.zip" + "Vanilla_1_20_6.zip" + "Vanilla_1_21_11.zip" +) +#https://github.com/SlopeCraft/VisualCraft-binaries/releases/download/resource-packs/Vanilla_1_20_1.zip set(VCL_url_prefix "https://github.com/SlopeCraft/VisualCraft-binaries/releases/download/resource-packs/") -foreach(mcver RANGE 12 19) +foreach (mcver RANGE 12 21) math(EXPR VCL_resource_idx "${mcver} - 12") # message(STATUS "VCL_resource_idx = ${VCL_resource_idx}") @@ -18,20 +21,21 @@ foreach(mcver RANGE 12 19) set(VCL_resource_${mcver} ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name} CACHE FILEPATH "") - if(EXISTS ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name}) + if (EXISTS ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name}) # message(STATUS "Found resoruce pack for MC${mcver} (${VCL_current_zip_name}).") continue() - endif() + endif () message(STATUS "${VCL_current_zip_name} not found. Downloading...") + string(CONCAT VCL_current_download_url ${VCL_url_prefix} ${VCL_current_zip_name}) file(DOWNLOAD - ${VCL_url_prefix}${VCL_current_zip_name} - ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name} SHOW_PROGRESS) + ${VCL_current_download_url} + ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name}) - if(EXISTS ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name}) + if (EXISTS ${CMAKE_SOURCE_DIR}/binaries/${VCL_current_zip_name}) message(STATUS "Downloaded resoruce pack for MC${mcver}.") - else() - message(FATAL_ERROR "Failed to download resource pack for MC${mcver}.") - endif() -endforeach(mcver RANGE 12 19) \ No newline at end of file + else () + message(FATAL_ERROR "Failed to download resource pack for MC${mcver} from ${VCL_current_download_url}.") + endif () +endforeach (mcver RANGE 12 21) \ No newline at end of file diff --git a/cmake/required_deps/xsimd.cmake b/cmake/required_deps/xsimd.cmake new file mode 100644 index 00000000..325550c6 --- /dev/null +++ b/cmake/required_deps/xsimd.cmake @@ -0,0 +1,15 @@ +find_package(xsimd QUIET) +if (NOT ${xsimd_FOUND}) + message(FATAL_ERROR "xsimd is not installed. You may install it with vcpkg") +endif () + +#include(FetchContent) +# +#FetchContent_Declare(xsimd +# GIT_REPOSITORY https://github.com/xtensor-stack/xsimd.git +# GIT_TAG 11.1.0 +# OVERRIDE_FIND_PACKAGE) +# +#FetchContent_MakeAvailable(xsimd) +# +#find_package(xsimd REQUIRED) \ No newline at end of file diff --git a/cmake/required_deps/zstd.cmake b/cmake/required_deps/zstd.cmake new file mode 100644 index 00000000..125f0f20 --- /dev/null +++ b/cmake/required_deps/zstd.cmake @@ -0,0 +1,49 @@ + + +find_package(ZSTD QUIET) +if (NOT ZSTD_FOUND) + message(STATUS "Failed to find zstd with \"find_package\", try importing manually...") + find_library(zstd_lib_loc NAMES zstd REQUIRED) + message(STATUS "Found zstd manually at ${zstd_lib_loc}") + + cmake_path(GET zstd_lib_loc EXTENSION zstd_lib_ext) + message(STATUS "zstd_lib_ext: ${zstd_lib_ext}") + set(zstd_is_shared OFF) + set(shared_lib_extension ".dll;.so;.dylib") + foreach (shared_lib_ext ${shared_lib_extension}) + if (${zstd_lib_ext} MATCHES ${shared_lib_ext}) + set(zstd_is_shared ON) + break() + endif () + endforeach () + if (${zstd_is_shared}) + add_library(zstd_manually_imported SHARED IMPORTED) + else () + add_library(zstd_manually_imported STATIC IMPORTED) + endif () + set_target_properties(zstd_manually_imported PROPERTIES + IMPORTED_LOCATION ${zstd_lib_loc}) + find_file(zstd_header_loc NAMES zstd.h + HINTS "/usr/lib/include/zstd.h") + if (zstd_header_loc) + cmake_path(GET zstd_header_loc PARENT_PATH zstd_include_dir) + target_include_directories(zstd_manually_imported INTERFACE ${zstd_include_dir}) + endif () + # cmake_path(GET zstd_lib_loc PARENT_PATH zstd_install_dir) + # cmake_path(GET zstd_install_dir PARENT_PATH zstd_install_dir) + # target_include_directories(zstd_manually_imported INTERFACE "${zstd_install_dir}/include") +endif () + +set(SC_zstd_target_name "") +if (TARGET zstd::libzstd_shared) + set(SC_zstd_target_name "zstd::libzstd_shared") +elseif (TARGET zstd::zstd) + set(SC_zstd_target_name "zstd::zstd") +elseif (TARGET zstd::libzstd_static) + set(SC_zstd_target_name "zstd::libzstd_static") +elseif (TARGET zstd_manually_imported) + set(SC_zstd_target_name "zstd_manually_imported") +else () + message(FATAL_ERROR "No zstd library target imported.") +endif () +message(STATUS "Found zstd: ${SC_zstd_target_name}") \ No newline at end of file diff --git a/cmake/select_vectorize_flag.cmake b/cmake/select_vectorize_flag.cmake new file mode 100644 index 00000000..35f1c668 --- /dev/null +++ b/cmake/select_vectorize_flag.cmake @@ -0,0 +1,26 @@ + +include(CheckCXXCompilerFlag) +#add_definitions("-DSC_VECTORIZE_AVX2") + +message(STATUS "System processor arch = ${CMAKE_SYSTEM_PROCESSOR}") + +set(amd64_arch_names AMD64 x86_64) + +if (${CMAKE_SYSTEM_PROCESSOR} IN_LIST amd64_arch_names) + if (${MSVC}) + set(SlopeCraft_vectorize_flags "/arch:AVX2") + else () + set(SlopeCraft_vectorize_flags -mavx -mavx2 -mfma) + endif () +elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64") + set(SlopeCraft_vectorize_flags -mfpu=neon) + # if (${APPLE}) + # set(SlopeCraft_vectorize_flags -mcpu=apple-m1 -mfpu=neon) + # endif () +else () + message(WARNING "Unknown cpu arch \"${CMAKE_SYSTEM_PROCESSOR}\", using -march=native") +endif () + + +message(STATUS "Vectorize using " ${SlopeCraft_vectorize_flags}) +add_compile_options(${SlopeCraft_vectorize_flags}) \ No newline at end of file diff --git a/cmake/tool_chain_files/x86_64-mac-to-arm64-mac.cmake b/cmake/tool_chain_files/x86_64-mac-to-arm64-mac.cmake new file mode 100644 index 00000000..4609e0f9 --- /dev/null +++ b/cmake/tool_chain_files/x86_64-mac-to-arm64-mac.cmake @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.18) +include_guard(GLOBAL) + +set(CMAKE_CROSSCOMPILING ON) +set(CMAKE_SYSTEM_NAME "Darwin") +set(CMAKE_SYSTEM_PROCESSOR "arm64") +set(CMAKE_C_COMPILER_TARGET "arm64-apple-darwin") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-darwin") +set(CMAKE_OSX_ARCHITECTURES "arm64") +set(CMAKE_OSX_DEPLOYMENT_TARGET 12) + +set(TARGET_SYSROOT "") +set(CMAKE_SYSROOT ${TARGET_SYSROOT}) + +#set(ENV{PKG_CONFIG_PATH} $PKG_CONFIG_PATH:/usr/lib/aarch64-linux-gnu/pkgconfig) +#set(ENV{PKG_CONFIG_LIBDIR} /usr/lib/pkgconfig:/usr/share/pkgconfig/:${TARGET_SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig:${TARGET_SYSROOT}/usr/lib/pkgconfig) +#set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) + +# if you use other version of gcc and g++ than gcc/g++ 9, you must change the following variables +set(CMAKE_C_COMPILER /usr/local/opt/llvm/bin/clang) +set(CMAKE_CXX_COMPILER /usr/local/opt/llvm/bin/clang++) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${TARGET_SYSROOT}/usr/include") +set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") + +set(QT_COMPILER_FLAGS "-march=arm64") +set(QT_COMPILER_FLAGS_RELEASE "-O3 -pipe") +#set(QT_LINKER_FLAGS "-Wl,-O -Wl,--hash-style=gnu -Wl,--as-needed") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_BUILD_RPATH ${TARGET_SYSROOT}) + + +include(CMakeInitializeConfigs) + +function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING) + if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS") + set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}") + + foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO) + if (DEFINED QT_COMPILER_FLAGS_${config}) + set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}") + endif () + endforeach () + endif () + + + if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS") + foreach (config SHARED MODULE EXE) + set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}") + endforeach () + endif () + + _cmake_initialize_per_config_variable(${ARGV}) +endfunction() + +#set(XCB_PATH_VARIABLE ${TARGET_SYSROOT}) +# +#set(GL_INC_DIR ${TARGET_SYSROOT}/usr/include) +#set(GL_LIB_DIR ${TARGET_SYSROOT}:${TARGET_SYSROOT}/usr/lib/aarch64-linux-gnu/:${TARGET_SYSROOT}/usr:${TARGET_SYSROOT}/usr/lib) +# +#set(EGL_INCLUDE_DIR ${GL_INC_DIR}) +#set(EGL_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libEGL.so) +# +#set(OPENGL_INCLUDE_DIR ${GL_INC_DIR}) +#set(OPENGL_opengl_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libOpenGL.so) +# +#set(GLESv2_INCLUDE_DIR ${GL_INC_DIR}) +#set(GLESv2_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libGLESv2.so) +# +#set(gbm_INCLUDE_DIR ${GL_INC_DIR}) +#set(gbm_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libgbm.so) +# +#set(Libdrm_INCLUDE_DIR ${GL_INC_DIR}) +#set(Libdrm_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libdrm.so) +# +#set(XCB_XCB_INCLUDE_DIR ${GL_INC_DIR}) +#set(XCB_XCB_LIBRARY ${XCB_PATH_VARIABLE}/usr/lib/aarch64-linux-gnu/libxcb.so) \ No newline at end of file diff --git a/cpack/deb.cmake b/cpack/deb.cmake new file mode 100644 index 00000000..375367f4 --- /dev/null +++ b/cpack/deb.cmake @@ -0,0 +1,40 @@ +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "SlopeCraft group") +set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64) + +set(SlopeCraft_debian_opencl_deps) + +if (${SlopeCraft_GPU_API} STREQUAL "OpenCL") + set(SlopeCraft_debian_opencl_deps + ocl-icd-libopencl1 + ) +endif () +if (${SlopeCraft_GPU_API} STREQUAL "Vulkan") + set(SlopeCraft_debian_vulkan_deps + libvulkan1) +endif () + +set(CPACK_DEBIAN_PACKAGE_DEPENDS + "zlib1g" + "libzip-dev" # package name like lib*-dev are much more universal in different ubuntu releases + "libboost-iostreams-dev" + "libpng-dev" + "libqt6core6(>=6.2.4)" + "libqt6gui6(>=6.2.4)" + "libqt6widgets6(>=6.2.4)" + "libqt6network6(>=6.2.4)" + "libopengl0" + "libqt6opengl6(>=6.2.4)" + "libqt6openglwidgets6(>=6.2.4)" + "libqt6dbus6(>=6.2.4)" + "libglx0" + "qt6-qpa-plugins(>=6.2.4)" + ${SlopeCraft_debian_opencl_deps} + ${SlopeCraft_debian_vulkan_deps} +) + +list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS "," CPACK_DEBIAN_PACKAGE_DEPENDS) + +message(STATUS "CPACK_DEBIAN_PACKAGE_DEPENDS = ${CPACK_DEBIAN_PACKAGE_DEPENDS}") + +set(CPACK_DEBIAN_PACKAGE_DESCRIPTION + "Get your Minecraft pixel painting in multiple kinds of forms") \ No newline at end of file diff --git a/cpack/make-packs.cmake b/cpack/make-packs.cmake new file mode 100644 index 00000000..e5637104 --- /dev/null +++ b/cpack/make-packs.cmake @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.16) + +# Common attributes +set(CPACK_PACKAGE_NAME SlopeCraft) +set(CPACK_PACKAGE_VERSION ${SlopeCraft_version}) +set(CPACK_PACKAGE_VENDOR "SlopeCraft group") +set(CPACK_PACKAGE_CONTACT https://github.com/SlopeCraft) +set(CPACK_PACKAGE_HOMEPAGE_URL https://github.com/SlopeCraft) + +if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(CPACK_PACKAGE_NAME ${CPACK_PACKAGE_NAME}-debug) +endif () + +if (${APPLE}) + set(cpu_arch ${CMAKE_SYSTEM_PROCESSOR}) + if (${cpu_arch} STREQUAL "AMD64") + set(cpu_arch "x86_64") + endif () + + if (${cpu_arch} STREQUAL "x86_64") + set(CPACK_SYSTEM_NAME "macOS-Intel") + endif () + if (${cpu_arch} STREQUAL "arm64") + set(CPACK_SYSTEM_NAME "macOS-Silicon") + endif () +endif () + +include(${CMAKE_SOURCE_DIR}/cpack/deb.cmake) + +include(CPack) \ No newline at end of file diff --git a/docs/SlopeCraft_ba-style@nulla.top.png b/docs/SlopeCraft_ba-style@nulla.top.png new file mode 100644 index 00000000..8f824d3a Binary files /dev/null and b/docs/SlopeCraft_ba-style@nulla.top.png differ diff --git a/docs/TODO.md b/docs/TODO.md index 29da804a..4ba88c2a 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -3,10 +3,11 @@ - ~~继续改进抖动算法~~(感觉没什么可改的了) - ~~有损压缩立体地图画的高度~~(已经实现) - ~~边缘识别 - 填充算法,考虑用遗传算法修补边缘识别结果,确保边缘绝对完整。~~(已实现) - - ~~如果成功了,那也许是第一个使用了人工智能的地图画生成器,哈哈~~(已经实现,虽然不是这个功能) + - ~~如果成功了,那也许是第一个使用了人工智能的地图画生成器,哈哈~~(已经实现,虽然不是这个功能) - ~~允许玩家旋转平板地图画(特别是垂直方向的旋转)~~(已经实现,叫做墙面像素画) - ~~跟进 Minecraft 1.18~~ -- 跟进 Minecraft 1.20 +- ~~跟进 Minecraft 1.20~~ +- 跟进 Minecraft 1.22 - 调整颜色后,允许玩家精修像素的颜色 - ~~可以考虑一下对基岩版的支持~~(原理不同,支持不了) - 想办法将颜色调整模块分离出来,为基岩版编写新的 SlopeCraftL.dll,尽量复用核心代码和界面代码 @@ -15,15 +16,15 @@ - ~~自定义添加新的方块~~(已经实现) - ~~修整代码风格~~(已经实现) - ~~导出原版结构方块格式的文件~~(已经实现) - - 方便 forge 端 + - 方便 forge 端 - 导出为 WorldEdit 的 schem 文件 - - 同样方便 Forge 端 + - 同样方便 Forge 端 - ~~在可燃方块周围添加玻璃方块,防止着火~~(已经实现) - - ~~也许可以用类似的方式防止末影人偷方块~~ + - ~~也许可以用类似的方式防止末影人偷方块~~ - 预览 - - 三维结构预览 - - ~~平面预览,显示每个顶端方块的材质和高度~~(已经实现) - - 应当支持分层和导出为图片 + - 三维结构预览 + - ~~平面预览,显示每个顶端方块的材质和高度~~(已经实现) + - 应当支持分层和导出为图片 - ~~批量处理~~(已实现) - - ~~批量生成纯文件地图画~~ + - ~~批量生成纯文件地图画~~ - 使用更先进的图像分割算法,提高调整颜色的能力 diff --git a/imageCutter/CMakeLists.txt b/imageCutter/CMakeLists.txt index ab8c7eab..16ede52a 100644 --- a/imageCutter/CMakeLists.txt +++ b/imageCutter/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets LinguistTools REQUIRED) +find_package(Qt6 COMPONENTS Widgets LinguistTools REQUIRED) set(imageCutter_header_files CutterWind.h @@ -29,19 +29,13 @@ endif() set(imageCutter_ts_files imageCutter_en_US.ts) -if(${SlopeCraft_update_ts_files}) - # qt_add_lupdate(imageCutter TSFILES ${imageCutter_ts_files} CutterWind.ui SOURCES ${imageCutter_header_files} ${imageCutter_source_files}) - execute_process( - COMMAND ${SlopeCraft_Qt_lupdate_executable} CutterWind.ui ${imageCutter_header_files} ${imageCutter_source_files} "-ts" ${imageCutter_ts_files} ${SlopeCraft_ts_flags} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endif() - set(imageCutter_project_sources CutterWind.h CutterWind.cpp main.cpp CutterWind.ui - ${imageCutter_ts_files} + + # ${imageCutter_ts_files} ${imageCutter_windows_rc_files} ) @@ -50,6 +44,12 @@ qt_add_executable(imageCutter ${imageCutter_project_sources}) # translation +qt_add_lupdate(imageCutter + TS_FILES ${imageCutter_ts_files} + SOURCES ${imageCutter_project_sources} + OPTIONS ${SC_lupdate_flags} +) + qt_add_lrelease(imageCutter TS_FILES ${imageCutter_ts_files} QM_FILES_OUTPUT_VARIABLE imageCutter_qm_files) qt_add_resources(imageCutter "translations" @@ -60,7 +60,7 @@ qt_add_resources(imageCutter "translations" target_link_libraries(imageCutter PRIVATE - Qt${QT_VERSION_MAJOR}::Widgets) + Qt6::Widgets) set_target_properties(imageCutter PROPERTIES VERSION ${PROJECT_VERSION} diff --git a/imageCutter/CutterWind.cpp b/imageCutter/CutterWind.cpp index 146086e6..cb35730c 100644 --- a/imageCutter/CutterWind.cpp +++ b/imageCutter/CutterWind.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -52,8 +52,7 @@ CutterWind::~CutterWind() { delete ui; } void CutterWind::loadImg() { QString path = QFileDialog::getOpenFileName( this, tr("选择图片"), "", tr("图片(*.png *.bmp *.jpg *.tif)")); - if (path.isEmpty()) - return; + if (path.isEmpty()) return; img.load(path); img = img.convertToFormat(QImage::Format_ARGB32); @@ -87,14 +86,12 @@ void CutterWind::updateImg() const { void CutterWind::saveImg() { QString name = QFileDialog::getSaveFileName( this, tr("保存图片"), "", tr("图片(*.png *.bmp *.jpg *.tif)")); - if (name.isEmpty()) - return; + if (name.isEmpty()) return; img.save(name); } void CutterWind::resizeImg() { - Qt::AspectRatioMode arm = Qt::AspectRatioMode(ui->boxAspectRatioMode->currentData().toInt()); Qt::TransformationMode tm = @@ -112,8 +109,7 @@ void CutterWind::cutImg() { QString dir = QFileDialog::getExistingDirectory(this, tr("选择输出文件夹"), ""); - if (dir.isEmpty()) - return; + if (dir.isEmpty()) return; dir = dir.replace("\\\\", "/"); dir = dir.replace('\\', '/'); @@ -133,8 +129,7 @@ void CutterWind::cutImg() { for (int rOffset = 0; rOffset < 128; rOffset++) { const int imgR = rOffset + 128 * mapR; const uint32_t *src = nullptr; - if (imgR < imgRN) - src = (const uint32_t *)img.constScanLine(imgR); + if (imgR < imgRN) src = (const uint32_t *)img.constScanLine(imgR); uint32_t *dst = (uint32_t *)part.scanLine(rOffset); diff --git a/imageCutter/CutterWind.h b/imageCutter/CutterWind.h index 77c04835..b49f962c 100644 --- a/imageCutter/CutterWind.h +++ b/imageCutter/CutterWind.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -35,16 +35,16 @@ QT_END_NAMESPACE class CutterWind : public QMainWindow { Q_OBJECT -public: + public: CutterWind(QWidget *parent = nullptr); ~CutterWind(); -private slots: + private slots: void loadImg(); void saveImg(); void cutImg(); -private: + private: void updateImg() const; void resizeImg(); QImage img; @@ -52,4 +52,4 @@ private slots: QString rawFileSuffix; Ui::CutterWind *ui; }; -#endif // CUTTERWIND_H +#endif // CUTTERWIND_H diff --git a/imageCutter/imageCutter_en_US.ts b/imageCutter/imageCutter_en_US.ts index fa1243e5..8ff4e6df 100644 --- a/imageCutter/imageCutter_en_US.ts +++ b/imageCutter/imageCutter_en_US.ts @@ -10,7 +10,7 @@ - + cols @@ -21,7 +21,7 @@ - + 图片尺寸(方块): Image size (blocks) : @@ -62,7 +62,7 @@ - + 保存图片 Save image @@ -97,30 +97,30 @@ Select image - - - 图片(*.png *.bmp *.jpg *.tif) - Images (*.png *.bmp *.jpg *.tif) - - - + 打开图片失败 Failed to load image - + 图片格式损坏,或者图片过于巨大。 The image might be damaged, or it's too huge. - + 行 , rows, - + 选择输出文件夹 Select output directory + + + + 图片(*.png *.bmp *.jpg *.tif) + + diff --git a/imageCutter/install.cmake b/imageCutter/install.cmake index 35217fb3..34902f0a 100644 --- a/imageCutter/install.cmake +++ b/imageCutter/install.cmake @@ -1,47 +1,82 @@ set(AppName imageCutter) -configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) +#configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in +# ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake +# @ONLY) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") install(TARGETS imageCutter - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX} + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . ) + QD_add_deployqt(imageCutter + BUILD_MODE + FLAGS ${SlopeCraft_windeployqt_flags_build}) + QD_add_deployqt(imageCutter + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_windeployqt_flags_install}) + DLLD_add_deploy(imageCutter + BUILD_MODE + INSTALL_MODE INSTALL_DESTINATION .) # Run windeployqt at build time - add_custom_target(Windeployqt-imageCutter ALL - COMMAND ${SlopeCraft_Qt_windeployqt_executable} imageCutter.exe - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS imageCutter) + # add_custom_target(Windeployqt-imageCutter + # COMMAND ${SlopeCraft_Qt_windeployqt_executable} imageCutter.exe ${SlopeCraft_windeployqt_flags_build} + # COMMAND_EXPAND_LISTS + # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + # DEPENDS imageCutter) + # add_dependencies(SC_deploy_all Windeployqt-imageCutter) + # + # # Run windeployqt at install time + # install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) - # Run windeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") # et_target_properties(imageCutter PROPERTIES INSTALL_RPATH "../lib") install(TARGETS imageCutter + EXPORT SlopeCraftTargets RUNTIME DESTINATION bin BUNDLE DESTINATION lib ) + install(FILES others/imageCutterIconNew.png + DESTINATION share/pixmaps + RENAME com.github.SlopeCraft.imageCutter.png) + install(FILES others/imageCutter.desktop + DESTINATION share/applications) + + # Install platforms and imageformats plugins + include(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake) return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") install(TARGETS imageCutter - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX} + EXPORT SlopeCraftTargets + RUNTIME DESTINATION . + BUNDLE DESTINATION . ) # Install icon for macOS file(GLOB imageCutter_Icon ${CMAKE_SOURCE_DIR}/imageCutter/others/imageCutterIconNew.icns) install(FILES ${imageCutter_Icon} - DESTINATION ${CMAKE_INSTALL_PREFIX}/imageCutter.app/Contents/Resources) + DESTINATION imageCutter.app/Contents/Resources) + + QD_add_deployqt(imageCutter + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_macdeployqt_flags_install}) + + DylibD_add_deploy(imageCutter + INSTALL_DESTINATION . + RPATH_POLICY REPLACE) + + RCS_add_codesign(imageCutter + INSTALL_DESTINATION .) return() -endif() +endif () message(WARNING "No rule to install imageCutter, because the system is not Windows, linux or MacOS.") \ No newline at end of file diff --git a/imageCutter/main.cpp b/imageCutter/main.cpp index c57ed02a..3d3ff13d 100644 --- a/imageCutter/main.cpp +++ b/imageCutter/main.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -31,7 +31,6 @@ This file is part of SlopeCraft. #include int main(int argc, char *argv[]) { - QApplication a(argc, argv); QTranslator translator; diff --git a/imageCutter/others/imageCutter.desktop b/imageCutter/others/imageCutter.desktop new file mode 100644 index 00000000..bc4ce1c1 --- /dev/null +++ b/imageCutter/others/imageCutter.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=imageCutter +Comment=Image preprocessor for SlopeCraft +Exec=imageCutter +Icon=com.github.SlopeCraft.imageCutter.png +Type=Application +Keywords=SlopeCraft; \ No newline at end of file diff --git a/py/.gitignore b/py/.gitignore new file mode 100644 index 00000000..c1d3e321 --- /dev/null +++ b/py/.gitignore @@ -0,0 +1 @@ +*/__pycache__ \ No newline at end of file diff --git a/py/test1/preprocess.py b/py/test1/preprocess.py new file mode 100644 index 00000000..ff1be214 --- /dev/null +++ b/py/test1/preprocess.py @@ -0,0 +1,223 @@ +import copy +import io + +import cv2 +import os +import pathlib +import argparse +import numpy as np +import multiprocessing as mp +import zipfile +import time +import hashlib +import tomllib +import tomli_w +from typing import Any +import torch +import torch.utils.data + + +def cut_image(input_img: cv2.typing.MatLike, + row_size: int, + col_size: int) -> list[cv2.typing.MatLike]: + result = [] + rows = input_img.shape[0] + cols = input_img.shape[1] + rows = rows - rows % row_size + cols = cols - cols % col_size + + for r in range(0, rows, row_size): + for c in range(0, cols, col_size): + result.append(input_img[r:r + row_size, c:c + col_size]) + + return result + + +def process_dir(source_dir: str, archive_name: str, cutted_rows: int, cutted_cols: int) -> dict[str, Any]: + read_counter: int = 0 + write_counter: int = 0 + duplicate_counter: int = 0 + archive = zipfile.ZipFile(archive_name, mode='w', compression=zipfile.ZIP_STORED) + + hash_set = set() + + for filepath, _, filenames in os.walk(source_dir): + for filename in filenames: + abs_filename = os.path.join(filepath, filename) + + if not os.path.isfile(abs_filename): + raise RuntimeError(f"Non existing file {abs_filename}") + + if filename == ".nomedia": + continue + + img = cv2.imdecode(np.fromfile(abs_filename, dtype=np.uint8), cv2.IMREAD_COLOR) + if img is None: + print(f"Failed to read {abs_filename}, skip and continue.") + continue + + read_counter += 1 + + if read_counter % 100 == 0: + print(f"{read_counter} images read, {write_counter} images generated.") + + cut_imgs = cut_image(img, cutted_rows, cutted_cols) + + for cut_img in cut_imgs: + core_filename = f"{write_counter:014}.jpg" + ok, encoded = cv2.imencode(".jpg", cut_img) + if not ok: + raise RuntimeError(f"Failed to encode {core_filename}") + + encoded_bytes = encoded.tobytes() + img_hash = hashlib.sha1() + img_hash.update(encoded_bytes) + + img_hash = img_hash.hexdigest() + if img_hash in hash_set: + duplicate_counter += 1 + continue + + hash_set.add(img_hash) + write_counter += 1 + archive.writestr(core_filename, encoded_bytes) + + archive.close() + print(f"{read_counter} images read, {duplicate_counter} duplicated images removed., {write_counter} images " + f"generated") + + return {'original_images': read_counter, + 'generated_images': write_counter, + 'source_dir': source_dir, + 'rows': cutted_rows, + 'cols': cutted_cols} + + +def parse_shape_str(shape_str: str) -> tuple[int, int]: + split = shape_str.split('x') + if len(split) != 2: + raise RuntimeError(f"Invalid shape string {shape_str}") + + result = [0, 0] + for idx in range(0, 2): + temp = int(split[idx]) + if temp <= 0: + raise RuntimeError(f"Invalid shape: {temp}") + result[idx] = temp + + return result[0], result[1] + + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument("--source-dir", type=str, required=True, nargs='+') + parser.add_argument("--out-dir", type=str, default="preprocessed") + parser.add_argument("--shape", type=str, default="128x128", nargs='+') + + args = vars(parser.parse_args()) + + out_dir = args["out_dir"] + os.makedirs(out_dir, exist_ok=True) + + source_dirs: list[str] = args["source_dir"] + + shapes = [] + for shape_str in args["shape"]: + shapes.append(parse_shape_str(shape_str)) + + # metainfos = dict() + + for src_dir_name in source_dirs: + print(f"Processing images in {src_dir_name}...") + for shape in shapes: + print(f"Cutting with shape str = {shape}...") + cut_rows = shape[1] + cut_cols = shape[0] + + filename_stem = f"{pathlib.Path(src_dir_name).stem}-{cut_cols}x{cut_rows}" + + archive_name = os.path.join(out_dir, f"{filename_stem}.zip") + abs_time = time.time() + info = process_dir(src_dir_name, archive_name, cut_rows, cut_cols) + seconds = time.time() - abs_time + print(f"Finished in {seconds} seconds") + + tomli_w.dump(info, open(os.path.join(out_dir, f"{filename_stem}.toml"), "wb")) + + # metainfos[archive_name] = info + + # tomli_w.dump(metainfos, open(os.path.join(out_dir, "total_info.toml"), 'wb')) + + +class ZipDataset(torch.utils.data.Dataset): + config: dict[str, Any] + zip: zipfile.ZipFile + + def __init__(self, zip_file: str): + config_file = zip_file.removesuffix(".zip") + ".toml" + if not os.path.isfile(config_file): + raise RuntimeError(f"Expected {config_file} as the config file for {zip_file}, but doesn't exist.") + + self.config = tomllib.load(open(config_file, "rb")) + print(f"Loading {zip_file}...") + self.zip = zipfile.ZipFile(zip_file, mode='r') + + def __len__(self) -> int: + return len(self.zip.filelist) + + @staticmethod + def read_image_binary(zip_info: zipfile.ZipInfo) -> bytes: + return zip_obj.read(name=zip_info) + + def decode_image(self, zip_info: zipfile.ZipInfo) -> torch.Tensor: + encoded = self.read_image_binary(zip_info) + encoded_nparr = np.frombuffer(encoded, dtype=np.uint8) + image = cv2.imdecode(encoded_nparr, flags=cv2.IMREAD_COLOR) + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + image = np.array(image, dtype=np.float32) / 255 + + tensor = torch.from_numpy(image) + tensor = torch.permute(tensor, dims=[2, 0, 1]) + return tensor + + def __getitem__(self, item) -> torch.Tensor: + return self.decode_image(self.zip.filelist[item]) + + # def __iter__(self): + # for zip_info in self.zip.filelist: + # tensor=self.decode_image(zip_info) + # yield tensor + + +class MPZipDataset(ZipDataset): + major_pid: int + zip_dict: dict[int, zipfile.ZipFile] + + def __init__(self, zip_file: str, major_pid: int = os.getpid()): + super().__init__(zip_file) + self.major_pid = os.getpid() + self.zip_dict = {} + + def select_zip_obj(self) -> zipfile.ZipFile: + pid = os.getpid() + if pid == self.major_pid: + return self.zip + + if pid not in self.zip_dict: + print(f"Reloading {self.zip.filename} for process {pid}...") + # new_zip = zipfile.ZipFile(self.zip.filename, mode='r') + new_zip = self.zip + new_zip.fp = io.open(self.zip.filename,self.zip.fp.mode) + self.zip_dict[pid] = new_zip + + return self.zip_dict[pid] + + # @override + def read_image_binary(self, zip_info: zipfile.ZipInfo) -> bytes: + return self.select_zip_obj().read(zip_info) + + +if __name__ == "__main__": + main() diff --git a/py/test1/split_dataset.py b/py/test1/split_dataset.py new file mode 100644 index 00000000..e5196ee9 --- /dev/null +++ b/py/test1/split_dataset.py @@ -0,0 +1,112 @@ +import os +import pathlib +from typing import Any +import preprocess as pp +import zipfile +import argparse +import random +import tomli_w + + +def picked_count(size: int, ratio: float, max_count: int) -> int: + assert max_count != 0 + if max_count < 0: + return int(size * ratio) + return min(max_count, int(size * ratio)) + + +def export_subdataset(filename: str, + src: zipfile.ZipFile, + filelist: list[zipfile.ZipInfo], + metainfo: dict[str, Any]): + dst = zipfile.ZipFile(filename, 'w', compression=zipfile.ZIP_STORED) + for info in filelist: + content = src.read(info) + dst.writestr(info.filename, data=content) + dst.close() + + config_filename = filename.removesuffix(".zip") + ".toml" + tomli_w.dump(metainfo, open(config_filename, "wb")) + + +def split_dataset(source: str, + train_dir: str, test_dir: str, validate_dir: str, + test_ratio: float, test_max_count: int, + validate_ratio: float, validate_max_count: int): + src_ds = pp.ZipDataset(source) + src = src_ds.zip + + filelist = src.filelist.copy() + random.shuffle(filelist) + + test_count = picked_count(len(filelist), test_ratio, test_max_count) + validate_count = picked_count(len(filelist), validate_ratio, validate_max_count) + + counter: int = 0 + + metainfo: dict[str, Any] = src_ds.config + metainfo['parent'] = source + metainfo.pop('original_images') + + metainfo['generated_images'] = test_count + metainfo['usage'] = 'test' + export_subdataset(os.path.join(test_dir, os.path.basename(source)), + src=src, + filelist=filelist[counter:counter + test_count], + metainfo=metainfo + ) + counter += test_count + + metainfo['generated_images'] = validate_count + metainfo['usage'] = 'validate' + export_subdataset(os.path.join(validate_dir, os.path.basename(source)), + src=src, + filelist=filelist[counter:counter + validate_count], + metainfo=metainfo + ) + counter += validate_count + + metainfo['generated_images'] = len(src.filelist) - validate_count - test_count + metainfo['usage'] = 'train' + export_subdataset(os.path.join(train_dir, os.path.basename(source)), + src=src, + filelist=filelist[counter:len(src.filelist)], + metainfo=metainfo + ) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("source", type=str, nargs='+') + parser.add_argument("--out-dir", type=str) + parser.add_argument("--test-ratio", "--tr", type=float) + parser.add_argument("--test-max-count", "--tmc", type=int, required=False, default=-1) + parser.add_argument("--validate-ratio", "--vr", type=float) + parser.add_argument("--validate-max-count", "--vmc", type=int, required=False, default=-1) + args = parser.parse_args() + + out_dir = args.out_dir + train_dir = os.path.join(out_dir, "train") + test_dir = os.path.join(out_dir, "test") + validate_dir = os.path.join(out_dir, "validate") + + os.makedirs(train_dir, exist_ok=True) + os.makedirs(test_dir, exist_ok=True) + os.makedirs(validate_dir, exist_ok=True) + + for src in args.source: + print(f"Splitting {src}...") + split_dataset(src, + train_dir=train_dir, + test_dir=test_dir, + validate_dir=validate_dir, + + test_ratio=args.test_ratio, + test_max_count=args.test_max_count, + + validate_ratio=args.validate_ratio, + validate_max_count=args.validate_max_count) + + +if __name__ == "__main__": + main() diff --git a/py/test1/test_load.py b/py/test1/test_load.py new file mode 100644 index 00000000..8984ad12 --- /dev/null +++ b/py/test1/test_load.py @@ -0,0 +1,27 @@ +import preprocess as pp +import random +import matplotlib.pyplot as plt +import torch.utils.data +import os +import argparse + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input", type=str) + args = parser.parse_args() + + ds = pp.MPZipDataset(args.input) + dl = torch.utils.data.DataLoader(ds, batch_size=64, + shuffle=True, + pin_memory=True, + num_workers=0 if os.name == 'nt' else 8) + + for batch_index, img in enumerate(dl): + img = img.to('cuda') + pass + print(f"Batch {batch_index}") + + +if __name__ == "__main__": + main() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5ad08920..e981ff58 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,2 +1,2 @@ add_subdirectory(test_libSchem) -add_subdirectory(test_SlopeCraftL) \ No newline at end of file +#add_subdirectory(test_SlopeCraftL) \ No newline at end of file diff --git a/tests/test_SlopeCraftL/test_SlopeCraftL.cpp b/tests/test_SlopeCraftL/test_SlopeCraftL.cpp index 02fec93e..54bcd40c 100644 --- a/tests/test_SlopeCraftL/test_SlopeCraftL.cpp +++ b/tests/test_SlopeCraftL/test_SlopeCraftL.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify diff --git a/tests/test_libSchem/CMakeLists.txt b/tests/test_libSchem/CMakeLists.txt index e565ba64..c7640205 100644 --- a/tests/test_libSchem/CMakeLists.txt +++ b/tests/test_libSchem/CMakeLists.txt @@ -1,9 +1,11 @@ set(CMAKE_CXX_STANDARD 20) -include_directories(${SlopeCraft_Eigen3_include_dir}) include_directories(${CMAKE_SOURCE_DIR}/utilities) add_executable(test_libSchem - + test_libSchem.cpp) -target_link_libraries(test_libSchem PRIVATE Schem NBTWriter -lz) \ No newline at end of file +target_link_libraries(test_libSchem PRIVATE Schem NBTWriter -lz) + +add_test(NAME test_libSchem + COMMAND test_libSchem) \ No newline at end of file diff --git a/tests/test_libSchem/test_libSchem.cpp b/tests/test_libSchem/test_libSchem.cpp index 3f4a362e..060a6338 100644 --- a/tests/test_libSchem/test_libSchem.cpp +++ b/tests/test_libSchem/test_libSchem.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -38,12 +38,12 @@ const std::vector trash_id = generate_trash(); int main() { libSchem::Schem schem; - schem.set_MC_major_version_number(SCL_gameVersion::MC18); - schem.set_MC_version_number(MCDataVersion::MCDataVersion_t::Java_1_18_2); + schem.set_MC_major_version_number(SCL_gameVersion::MC21); + schem.set_MC_version_number(MCDataVersion::MCDataVersion_t::Java_1_21_1); schem.resize(12, 9, 12); - std::vector ids; //= {"minecraft:air", "minecraft:glass"}; + std::vector ids; //= {"minecraft:air", "minecraft:glass"}; ids.resize(1); for (auto &id : ids) { @@ -51,11 +51,10 @@ int main() { } ids[0] = "minecraft:air"; - ids.reserve(trash_id.size() + 1); - - while (ids.size() < 254) { - ids.emplace_back(trash_id[ids.size() - 1].data()); - } + // ids.reserve(trash_id.size() + 1); + // while (ids.size() < 254) { + // ids.emplace_back(trash_id[ids.size() - 1].data()); + // } /* for (const auto &id : trash_id) { ids.emplace_back(id.data()); @@ -85,6 +84,7 @@ int main() { if (has_error) { cout << "error idx = " << err_idx << endl; + return 1; } libSchem::litematic_info info; @@ -95,21 +95,21 @@ int main() { // SCL_errorFlag flag; std::string error_str; if (!schem.export_litematic("test12.litematic", info, nullptr, &error_str)) { - cout << "Failed to export file " - << "test12.litematic" << endl; + cout << "Failed to export file " << "test12.litematic" << endl; cout << "Error info = " << error_str << endl; + return 1; } if (!schem.export_structure("test12.nbt", true, nullptr, &error_str)) { - cout << "Failed to export file " - << "test12.nbt" << endl; + cout << "Failed to export file " << "test12.nbt" << endl; cout << "Error info = " << error_str << endl; + return 1; } if (!schem.export_WESchem("test12.schem", weinfo, nullptr, &error_str)) { - cout << "Failed to export file " - << "test12.schem" << endl; + cout << "Failed to export file " << "test12.schem" << endl; cout << "Error info = " << error_str << endl; + return 1; } return 0; diff --git a/utilities/AdaptiveLabel/AdaptiveLabel.cpp b/utilities/AdaptiveLabel/AdaptiveLabel.cpp new file mode 100644 index 00000000..3773969b --- /dev/null +++ b/utilities/AdaptiveLabel/AdaptiveLabel.cpp @@ -0,0 +1,41 @@ +#include "AdaptiveLabel.h" +#include +#include +#include + +AdaptiveLabel::AdaptiveLabel(QWidget* parent) : QLabel(parent) {} + +// AdaptiveLabel::~AdaptiveLabel() {} + +QPixmap AdaptiveLabel::pixmap() const noexcept { return this->real_image; } + +void AdaptiveLabel::setPixmap(const QPixmap& img) noexcept { + this->real_image = img; + + this->refresh_image(); +} + +void AdaptiveLabel::refresh_image() noexcept { + const auto raw_image_size = this->real_image.size(); + + if (raw_image_size.height() <= 0 || raw_image_size.width() <= 0) { + static_cast(this)->setPixmap(QPixmap{}); + return; + } + auto label_size = this->size(); + + label_size.setHeight(std::max(label_size.height() - 2, 1)); + label_size.setWidth(std::max(label_size.width() - 2, 1)); + + QPixmap scaled = + this->real_image.scaled(label_size, Qt::AspectRatioMode::KeepAspectRatio, + Qt::TransformationMode::SmoothTransformation); + + static_cast(this)->setPixmap(scaled); +} + +void AdaptiveLabel::resizeEvent(QResizeEvent* event) noexcept { + QLabel::resizeEvent(event); + + this->refresh_image(); +} \ No newline at end of file diff --git a/utilities/AdaptiveLabel/AdaptiveLabel.h b/utilities/AdaptiveLabel/AdaptiveLabel.h new file mode 100644 index 00000000..ed9d93b1 --- /dev/null +++ b/utilities/AdaptiveLabel/AdaptiveLabel.h @@ -0,0 +1,40 @@ +#ifndef SLOPECRAFT_UTILITIES_ADAPTIVELABEL_ADAPTIVELABEL_H +#define SLOPECRAFT_UTILITIES_ADAPTIVELABEL_ADAPTIVELABEL_H + +#include +#include +#include + +class AdaptiveLabel : public QLabel { + // Q_OBJECT + private: + QPixmap real_image; + + void refresh_image() noexcept; + + public: + explicit AdaptiveLabel(QWidget* parent); + ~AdaptiveLabel() = default; + + void setPixmap(const QPixmap&) noexcept; + + QPixmap pixmap() const noexcept; + + QLabel& base() noexcept { return *this; } + const QLabel& base() const noexcept { return *this; } + + void resizeEvent(QResizeEvent* event) noexcept override; + + // using QLabel::; + using QLabel::setAlignment; + using QLabel::setFrameShape; + using QLabel::setMinimumSize; + using QLabel::setObjectName; + using QLabel::setScaledContents; + using QLabel::setSizePolicy; + using QLabel::setStyleSheet; + using QLabel::setText; + using QLabel::sizePolicy; +}; + +#endif // SLOPECRAFT_UTILITIES_ADAPTIVELABEL_ADAPTIVELABEL_H \ No newline at end of file diff --git a/utilities/AdaptiveLabel/CMakeLists.txt b/utilities/AdaptiveLabel/CMakeLists.txt new file mode 100644 index 00000000..402eb878 --- /dev/null +++ b/utilities/AdaptiveLabel/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) + +find_package(Qt6 COMPONENTS Widgets REQUIRED) +add_library(AdaptiveLabel STATIC + AdaptiveLabel.h + AdaptiveLabel.cpp) + +target_link_libraries(AdaptiveLabel PUBLIC Qt6::Widgets) +target_include_directories(AdaptiveLabel INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/utilities/BlockListManager/BLM_preset.cpp b/utilities/BlockListManager/BLM_preset.cpp index 78c81d9a..366178d6 100644 --- a/utilities/BlockListManager/BLM_preset.cpp +++ b/utilities/BlockListManager/BLM_preset.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ This file is part of SlopeCraft. #include "BlockListManager.h" #include +#include using njson = nlohmann::json; diff --git a/utilities/BlockListManager/BaseColor.cpp b/utilities/BlockListManager/BaseColor.cpp new file mode 100644 index 00000000..12a207ca --- /dev/null +++ b/utilities/BlockListManager/BaseColor.cpp @@ -0,0 +1,259 @@ +#include "BaseColor.h" +#include "ui_BaseColorWidget.h" +#include "BlockListManager.h" + +BaseColorWidget::BaseColorWidget(QWidget* parent, uint8_t _basecolor) + : QGroupBox(parent), ui(new Ui::BaseColorWidget), basecolor(_basecolor) { + this->ui->setupUi(this); + + connect(this->ui->cb_enable, &QCheckBox::toggled, this, + &BaseColorWidget::changed); +} + +BaseColorWidget::~BaseColorWidget() {} + +void BaseColorWidget::set_color(uint32_t color) noexcept { + QPalette pl; + pl.setColor(QPalette::ColorRole::Text, Qt::black); + pl.setColor(QPalette::ColorRole::Window, + QColor(qRed(color), qGreen(color), qBlue(color), + 255 * bool(this->basecolor))); + + this->setPalette(pl); + + if (this->basecolor == 0) { + this->ui->cb_enable->setDisabled(true); + } +} + +constexpr int basecolor_cols = 3; +static_assert(basecolor_cols >= 1); + +void BaseColorWidget::add_block(SlopeCraft::mc_block_interface* ab) noexcept { + this->place_holders.clear(); + BlockWidget* bw = new BlockWidget(this, ab); + + const int idx = this->blocks.size(); + + this->blocks.emplace_back(bw); + constexpr int cols = basecolor_cols; + + const int col = idx % cols; + const int row = idx / cols; + + dynamic_cast(this->ui->layout_blocks)->addWidget(bw, row, col); + + connect(bw, &QRadioButton::toggled, this, &BaseColorWidget::changed); +} + +void BaseColorWidget::add_placeholders() noexcept { + if (this->blocks.size() <= 0) { + this->place_holders.clear(); + return; + } + + if (this->blocks.size() < basecolor_cols) { + this->place_holders.clear(); + for (int i = this->blocks.size(); i < basecolor_cols; i++) { + auto lb = new QLabel("", this); + // lb->setFrameShape(QLabel::Shape::Box); + dynamic_cast(this->ui->layout_blocks)->addWidget(lb, 0, i); + this->place_holders.emplace_back(lb); + } + return; + } + + if (this->blocks.size() % basecolor_cols == 0) { + this->place_holders.clear(); + } +} + +void BaseColorWidget::re_arrange_blocks() noexcept { + this->place_holders.clear(); + auto layout = dynamic_cast(this->ui->layout_blocks); + for (auto& bw : this->blocks) { + assert(layout->indexOf(bw) >= 0); + layout->removeWidget(bw); + } + + constexpr int cols = basecolor_cols; + for (size_t idx = 0; idx < this->blocks.size(); idx++) { + const int col = idx % cols; + const int row = idx / cols; + layout->addWidget(this->blocks[idx], row, col); + } + this->finish_blocks(); +} + +tl::expected BaseColorWidget::remove_blocks( + const std::function& + remove_this_block) noexcept { + size_t remove_counter = 0; + for (auto it = this->blocks.begin(); it not_eq this->blocks.end();) { + const bool remove_current = remove_this_block((*it)->attached_block()); + if (not remove_current) { + ++it; + continue; + } + if (this->blocks.size() <= 1) { + return tl::make_unexpected( + tr("无法删除方块 %1,基色 %2 只拥有 " + "%3个方块,若继续删除,则该基色将没有方块,SlopeCraft 可能崩溃。") + .arg((*it)->text()) + .arg(int(this->basecolor), this->blocks.size())); + } + + (*it)->deleteLater(); + it = this->blocks.erase(it); + remove_counter++; + } + this->re_arrange_blocks(); + return remove_counter; +} + +constexpr inline bool should_be_disabled( + int, SCL_gameVersion block_ver, int num_blocks, + SCL_gameVersion selected_ver) noexcept { + if (num_blocks <= 1) { + return true; + } + + if (block_ver > selected_ver) { + return true; + } + + return false; +} + +void BaseColorWidget::finish_blocks() noexcept { + this->add_placeholders(); + assert(this->blocks.size() > 0); + + this->select_block_direct(0); + + const SCL_gameVersion v = + reinterpret_cast(this->parent())->version(); + + for (int idx = 0; idx < (int)this->blocks.size(); idx++) { + this->blocks[idx]->setDisabled(should_be_disabled( + idx, (SCL_gameVersion)this->blocks[idx]->attached_block()->getVersion(), + this->blocks.size(), v)); + } +} + +void BaseColorWidget::select_block_direct(int idx) noexcept { + assert(idx >= 0); + + assert(idx < (int)this->blocks.size()); + /* + const SCL_gameVersion v = + reinterpret_cast(this->parent())->version(); + + assert(this->blocks[idx]->attachted_block()->getVersion() <= (int)v); + */ + + this->blocks[idx]->setChecked(true); +} + +void BaseColorWidget::select_block_soft(int idx) noexcept { + const int new_select = this->prefered_block_idx( + idx, dynamic_cast(this->parent())->version()); + + this->select_block_direct(new_select); +} + +void BaseColorWidget::when_version_updated(SCL_gameVersion v) noexcept { + const int new_select = this->prefered_block_idx(v); + + this->select_block_direct(new_select); + + for (int idx = 0; idx < int(this->blocks.size()); idx++) { + this->blocks[idx]->setDisabled(should_be_disabled( + idx, (SCL_gameVersion)this->blocks[idx]->attached_block()->getVersion(), + this->blocks.size(), v)); + } + if (this->basecolor == 0) { // basecolor 0 (air) must be selected + return; + } + const bool is_version_ok = + SlopeCraft::SCL_basecolor_version(this->basecolor) <= v; + this->ui->cb_enable->setChecked(is_version_ok); + this->ui->cb_enable->setEnabled(is_version_ok); +} + +int BaseColorWidget::selected_idx() const noexcept { + for (int idx = 0; idx < (int)this->blocks.size(); idx++) { + if (this->blocks[idx]->isChecked()) { + return idx; + } + } + assert(false); + return 0; +} + +int BaseColorWidget::prefered_block_idx(int checked_idx, + SCL_gameVersion ver) const noexcept { + std::vector scores; + scores.resize(this->blocks.size()); + assert(checked_idx < (int)scores.size()); + assert(checked_idx >= 0); + + for (int idx = 0; idx < int(scores.size()); idx++) { + int& score = scores[idx]; + score = 0; + + const SCL_gameVersion blk_ver = + (SCL_gameVersion)this->blocks[idx]->attached_block()->getVersion(); + + if (blk_ver <= ver) { + score = 100; + } else { + score = 50; + } + + if (idx == checked_idx) { + score += 1; + } + } + + int max_idx{0}; + + for (int idx = 0; idx < int(scores.size()); idx++) { + if (scores[idx] > scores[max_idx]) { + max_idx = idx; + } + } + + return max_idx; +} + +bool BaseColorWidget::is_enabled() const noexcept { + return this->ui->cb_enable->isChecked(); +} + +void BaseColorWidget::set_enabled(bool enabled) noexcept { + this->ui->cb_enable->setChecked(enabled); +} + +void BaseColorWidget::select_by_callback(const select_callback_t& fun) { + std::vector blks; + blks.reserve(this->blocks.size()); + for (auto bw : this->blocks) { + blks.emplace_back(bw->attached_block()); + } + + const int output = fun(blks); + + if (output < 0 || output >= (int)this->blocks.size()) { + return; + } + + this->select_block_soft(output); +} + +void BaseColorWidget::update_lang(SCL_language lang) noexcept { + this->ui->retranslateUi(this); + for (auto bw : this->blocks) { + bw->update_lang(lang); + } +} \ No newline at end of file diff --git a/utilities/BlockListManager/BaseColor.h b/utilities/BlockListManager/BaseColor.h new file mode 100644 index 00000000..5c0a0581 --- /dev/null +++ b/utilities/BlockListManager/BaseColor.h @@ -0,0 +1,82 @@ +#ifndef SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BASECOLOR_H +#define SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BASECOLOR_H + +#include +#include +#include +#include + +#include + +class BaseColorWidget; + +namespace Ui { +class BaseColorWidget; +} + +using select_callback_t = std::function& blks)>; + +class BaseColorWidget : public QGroupBox { + Q_OBJECT + private: + std::unique_ptr ui; + const uint8_t basecolor{255}; + std::vector blocks; + std::vector> place_holders; + + public: + explicit BaseColorWidget(QWidget* parent, uint8_t _basecolor); + ~BaseColorWidget(); + + void set_color(uint32_t argb32) noexcept; + + void add_block(SlopeCraft::mc_block_interface* ab) noexcept; + + // return the num of removed blocks + tl::expected remove_blocks( + const std::function& + remove_this_block) noexcept; + + void finish_blocks() noexcept; + + void select_block_soft(int idx) noexcept; + + void when_version_updated(SCL_gameVersion v) noexcept; + + int selected_idx() const noexcept; + + bool is_enabled() const noexcept; + + const SlopeCraft::mc_block_interface* selected_block() const noexcept { + return this->blocks[this->selected_idx()]->attached_block(); + } + + void update_lang(SCL_language lang) noexcept; + + void set_enabled(bool enabled) noexcept; + + inline auto& block_widgets() noexcept { return this->blocks; } + + inline const auto& block_widgets() const noexcept { return this->blocks; } + + void select_by_callback(const select_callback_t& fun); + + signals: + void changed(); + + private: + void add_placeholders() noexcept; + + void re_arrange_blocks() noexcept; + + void select_block_direct(int idx) noexcept; + + int prefered_block_idx(int checked_idx, SCL_gameVersion ver) const noexcept; + + int prefered_block_idx(SCL_gameVersion ver) const noexcept { + return this->prefered_block_idx(this->selected_idx(), ver); + } +}; + +#endif // SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BASECOLOR_H diff --git a/utilities/BlockListManager/BaseColorWidget.ui b/utilities/BlockListManager/BaseColorWidget.ui new file mode 100644 index 00000000..b1214e02 --- /dev/null +++ b/utilities/BlockListManager/BaseColorWidget.ui @@ -0,0 +1,56 @@ + + + BaseColorWidget + + + + 0 + 0 + 400 + 300 + + + + + + + + + + 启用 + + + true + + + + + + + + + + + 30 + 16777215 + + + + true + + + QFrame::Box + + + QFrame::Sunken + + + + + + + + + + + diff --git a/utilities/BlockListManager/Block.cpp b/utilities/BlockListManager/Block.cpp new file mode 100644 index 00000000..d30adbc1 --- /dev/null +++ b/utilities/BlockListManager/Block.cpp @@ -0,0 +1,21 @@ +#include "Block.h" + +BlockWidget::BlockWidget(QWidget* parent, SlopeCraft::mc_block_interface* _blk) + : QRadioButton(parent), block(_blk) { + this->setText(this->block->getNameZH()); + QImage img{this->block->imageCols(), this->block->imageRows(), + QImage::Format_ARGB32}; + this->block->getImage(reinterpret_cast(img.scanLine(0))); + + this->setIcon(QIcon{QPixmap::fromImage(img)}); +}; +void BlockWidget::update_lang(SCL_language lang) noexcept { + switch (lang) { + case SCL_language::Chinese: + this->setText(QString::fromUtf8(this->block->getNameZH())); + break; + case SCL_language::English: + this->setText(QString::fromUtf8(this->block->getNameEN())); + break; + } +} \ No newline at end of file diff --git a/utilities/BlockListManager/Block.h b/utilities/BlockListManager/Block.h new file mode 100644 index 00000000..2f0dda8a --- /dev/null +++ b/utilities/BlockListManager/Block.h @@ -0,0 +1,23 @@ +#ifndef SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BLOCK_H +#define SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BLOCK_H + +#include +#include + +#include + +class BlockWidget : public QRadioButton { + Q_OBJECT + private: + SlopeCraft::mc_block_interface* const block; + + public: + explicit BlockWidget(QWidget* parent, SlopeCraft::mc_block_interface* _blk); + ~BlockWidget() = default; + + auto attached_block() const noexcept { return this->block; } + + void update_lang(SCL_language lang) noexcept; +}; + +#endif // SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BLOCK_H diff --git a/utilities/BlockListManager/BlockListManager.cpp b/utilities/BlockListManager/BlockListManager.cpp index 18bb154a..0bc69f83 100644 --- a/utilities/BlockListManager/BlockListManager.cpp +++ b/utilities/BlockListManager/BlockListManager.cpp @@ -1,385 +1,351 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +#include +#include +#include +#include +#include "BlockListManager.h" - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +extern const std::string_view basecolor_names[64]; - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . +BlockListManager::BlockListManager(QWidget *parent) : QWidget(parent) {} - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ +BlockListManager::~BlockListManager() {} -#include "BlockListManager.h" -#include "TokiBaseColor.h" -#include "TokiBlock.h" -#include -#include -#include -#include - -BlockListManager::BlockListManager(QHBoxLayout *_area, QObject *parent) - : QObject(parent) { - isApplyingPreset = false; - area = _area; - qDebug() << ((area == nullptr) ? " 错误!_area 为空指针" : ""); - QGroupBox *qgb = nullptr; - QGridLayout *qgl = nullptr; - TokiBaseColor *tbc = nullptr; - tbcs.clear(); - // qDebug("方块列表管理者开始创建 QGroupBox"); - for (uchar baseColor = 0; baseColor < 64; baseColor++) { - if (baseColorNames[baseColor].isEmpty()) break; - qgb = new QGroupBox(baseColorNames[baseColor]); - // qDebug("create QGroupBox"); - qgl = new QGridLayout; - qgb->setLayout(qgl); - // qDebug("create Layout"); - tbc = new TokiBaseColor(baseColor, qgl); - // qDebug("create TokiBaseColor"); - area->addWidget(qgb); - // qDebug("add QGroupBox to layout"); - tbcs.push_back(tbc); - - connect(this, &BlockListManager::translate, tbc, &TokiBaseColor::translate); - connect(tbc, &TokiBaseColor::userClicked, this, - &BlockListManager::receiveClicked); - } - // qDebug("Manager 构造函数完毕"); -} +void BlockListManager::setup_basecolors() noexcept { + this->basecolor_widgets.clear(); + this->basecolor_widgets.reserve(64); + const int max_basecolor = SlopeCraft::SCL_maxBaseColor(); -BlockListManager::~BlockListManager() { - for (uchar i = 0; i < tbcs.size(); i++) delete tbcs[i]; -} + uint32_t bc_arr[64]; -void BlockListManager::setVersion(uchar _ver) { - if (_ver < 12 || _ver > SlopeCraft::SCL_maxAvailableVersion()) return; - TokiBaseColor::mcVer = _ver; - for (uchar i = 0; i < tbcs.size(); i++) tbcs[i]->versionCheck(); -} + SlopeCraft::SCL_get_base_color_ARGB32(bc_arr); -bool callback_load_image(const char *filename, uint32_t *dst_row_major) { - QImage img(QString::fromLocal8Bit(filename)); + for (int bc = 0; bc <= max_basecolor; bc++) { + std::unique_ptr bcw{new BaseColorWidget(this, bc)}; + this->layout()->addWidget(bcw.get()); + bcw->setTitle(QString::fromUtf8(basecolor_names[bc].data())); + bcw->set_color(bc_arr[bc]); - if (img.isNull()) { - return false; + connect(bcw.get(), &BaseColorWidget::changed, + [this]() { emit this->changed(); }); + this->basecolor_widgets.push_back(std::move(bcw)); } - - QImage another = img.convertedTo(QImage::Format_ARGB32).scaled(16, 16); - - memcpy(dst_row_major, another.scanLine(0), 16 * 16 * sizeof(uint32_t)); - return true; } -bool BlockListManager::setupFixedBlockList(const QString &filename, - const QString &imgdir) noexcept { - return this->impl_setupBlockList(filename, imgdir, this->BL_fixed); -} -bool BlockListManager::setupCustomBlockList(const QString &filename, - const QString &imgdir) noexcept { - return this->impl_setupBlockList(filename, imgdir, this->BL_custom); +uint64_t std::hash::operator()(const selection &s) const noexcept { + boost::uuids::detail::md5 hash; + for (auto &id : s.ids) { + hash.process_bytes(id.data(), id.size()); + } + + uint32_t dig[4]{}; + hash.get_digest(reinterpret_cast(dig)); + uint64_t fold = 0; + for (size_t i = 0; i < 2; i++) { + const uint64_t cur = + (uint64_t(dig[2 * i]) << 32) | (uint64_t(dig[2 * i + 1])); + fold ^= cur; + } + return fold; } -bool BlockListManager::impl_setupBlockList( - const QString &filename, const QString &dirname, - std::unique_ptr - &dst) noexcept { +// bool callback_load_image(const char *filename, uint32_t *dst_row_major) { +// QImage img(QString::fromLocal8Bit(filename)); +// +// if (img.isNull()) { +// return false; +// } +// +// QImage another = img.convertedTo(QImage::Format_ARGB32).scaled(16, 16); +// +// memcpy(dst_row_major, another.scanLine(0), 16 * 16 * sizeof(uint32_t)); +// return true; +// } + +std::unique_ptr +BlockListManager::impl_addblocklist(const QByteArray &file_content) noexcept { std::string errmsg; - errmsg.resize(4096); - SlopeCraft::blockListOption opt; - opt.errmsg = errmsg.data(); - opt.errmsg_capacity = errmsg.size(); - size_t msg_len{0}; - opt.errmsg_len_dest = &msg_len; - - auto img_dir_local8bit = dirname.toLocal8Bit(); - opt.image_dir = img_dir_local8bit.data(); - - opt.callback_load_image = callback_load_image; - - SlopeCraft::BlockListInterface *bli = - SlopeCraft::SCL_createBlockList(filename.toLocal8Bit().data(), opt); - - errmsg.resize(msg_len); - - if (!errmsg.empty()) { + errmsg.resize(8192); + auto sd_err = SlopeCraft::string_deliver::from_string(errmsg); + std::string warning; + warning.resize(8192); + auto sd_warn = SlopeCraft::string_deliver::from_string(warning); + SlopeCraft::block_list_create_info option{SC_VERSION_U64, &sd_warn, &sd_err}; + + SlopeCraft::block_list_interface *bli = + SlopeCraft::SCL_create_block_list_from_buffer( + file_content.data(), file_content.size(), option); + + errmsg.resize(sd_err.size); + warning.resize(sd_warn.size); + + if ((not errmsg.empty()) or (not warning.empty())) { + errmsg.append(QStringLiteral("\npwd: %1") + .arg(QFileInfo{"."}.absolutePath()) + .toLocal8Bit()); if (bli == nullptr) { QMessageBox::critical(dynamic_cast(this->parent()), tr("解析方块列表失败"), QString::fromUtf8(errmsg.data())); - return false; + return {nullptr}; } else { QMessageBox::warning(dynamic_cast(this->parent()), - tr("解析方块列表失败"), - QString::fromUtf8(errmsg.data())); + tr("解析方块列表成功,但出现警告"), + QString::fromUtf8(warning.data())); } } - dst.reset(bli); - std::vector blockps; - std::vector basecolors; - basecolors.resize(dst->size()); - blockps.resize(dst->size()); + std::vector blockps; + std::vector base_colors; + base_colors.resize(bli->size()); + blockps.resize(bli->size()); - dst->get_blocks(blockps.data(), basecolors.data(), blockps.size()); + const size_t size_2 [[maybe_unused]] = + bli->get_blocks(blockps.data(), base_colors.data(), blockps.size()); + assert(size_2 == base_colors.size() and base_colors.size() == blockps.size()); - for (size_t idx = 0; idx < dst->size(); idx++) { - this->tbcs[basecolors[idx]]->addTokiBlock(blockps[idx]); + for (size_t idx = 0; idx < bli->size(); idx++) { + this->basecolor_widgets[base_colors[idx]]->add_block(blockps[idx]); } - return true; -} - -void BlockListManager::setSelected(uchar baseColor, ushort blockSeq) { - isApplyingPreset = true; - tbcs[baseColor]->setSelected(blockSeq); - isApplyingPreset = false; -} -void BlockListManager::setEnabled(uchar baseColor, bool isEnable) { - isApplyingPreset = true; - tbcs[baseColor]->checkBox->setChecked(isEnable); - isApplyingPreset = false; + return std::unique_ptr{ + bli}; } -void BlockListManager::receiveClicked() const { - if (isApplyingPreset) return; - emit switchToCustom(); - emit blockListChanged(); -} - -void BlockListManager::setLabelColors(const QRgb *colors) { - for (uchar i = 0; i < tbcs.size(); i++) tbcs[i]->makeLabel(colors[i]); -} - -void BlockListManager::getEnableList(bool *dest) const { - for (uchar i = 0; i < tbcs.size(); i++) dest[i] = tbcs[i]->getEnabled(); -} - -void BlockListManager::getSimpleBlockList( - const SlopeCraft::AbstractBlock **SBL) const { - // qDebug("void BlockListManager::getSimpleBlockList(simpleBlock * SBL) - // const"); - - for (uchar i = 0; i < 64; i++) { - SBL[i] = nullptr; - } - for (uchar i = 0; i < tbcs.size(); i++) { - SBL[i] = (tbcs[i]->getTokiBlock()->getSimpleBlock()); +bool BlockListManager::add_blocklist(QString filename) noexcept { + const QString name = QFileInfo{filename}.fileName(); + for (auto &[bl_name, _] : this->blockslists) { + if (bl_name == name) { + QMessageBox::warning( + this, tr("无法加载方块列表"), + tr("名为 %1 的方块列表已经加载,不允许加载同名的方块列表。") + .arg(bl_name)); + return false; + } } -} -std::vector -BlockListManager::getSimpleBlockList() const { - std::vector SBL(64); - const SlopeCraft::AbstractBlock *p; - for (uchar i = 0; i < tbcs.size(); i++) { - p = tbcs[i]->getTokiBlock()->getSimpleBlock(); - SBL[i] = p; + // Test for multiple encodings + + QFile archive{filename}; + if (not archive.open(QIODevice::OpenModeFlag::ExistingOnly | + QIODevice::OpenModeFlag::ReadOnly)) { + QMessageBox::warning(this, tr("无法加载方块列表"), + tr("无法读取文件 %1 ,它可能被误删。").arg(filename)); + return false; } - return SBL; -} -std::vector BlockListManager::getTokiBlockList() const { - std::vector TBL(64); - for (uchar i = 0; i < 64; i++) { - if (i < tbcs.size()) - TBL[i] = tbcs[i]->getTokiBlock(); - else - TBL[i] = nullptr; + auto file_content = archive.readAll(); + std::unique_ptr tmp = + this->impl_addblocklist(file_content); + if (not tmp) { + return false; } - return TBL; + this->blockslists.emplace_back(name, std::move(tmp)); + return true; } -std::vector BlockListManager::getQRadioButtonList() - const { - std::vector TBL(64); - for (uchar i = 0; i < 64; i++) { - if (i < tbcs.size()) - TBL[i] = tbcs[i]->getTokiBlock()->getTarget(); - else - TBL[i] = nullptr; +void BlockListManager::finish_blocklist() noexcept { + for (auto &bcw : this->basecolor_widgets) { + bcw->finish_blocks(); } - return TBL; } -std::vector BlockListManager::toPreset() const { - std::vector TBL(64); - for (uchar i = 0; i < 64; i++) { - if (i < tbcs.size()) - TBL[i] = tbcs[i]->getSelected(); - else - TBL[i] = 0; +tl::expected BlockListManager::remove_blocklist( + QString blocklist_name) noexcept { + const SlopeCraft::block_list_interface *bl = nullptr; + auto it_removed = this->blockslists.end(); + { + QString loaded_names; + for (auto it = this->blockslists.begin(); it not_eq this->blockslists.end(); + ++it) { + auto &name = it->first; + loaded_names.append(name); + loaded_names.push_back(","); + if (name == blocklist_name) { + bl = it->second.get(); + it_removed = it; + break; + } + } + if (bl == nullptr) { + return tl::make_unexpected( + tr("无法删除方块列表 \"%1\",没有加载同名的方块列表。已加载:%2") + .arg(blocklist_name, loaded_names)); + } } - return TBL; -} + size_t removed_counter = 0; + for (auto &bcw : this->basecolor_widgets) { + auto res = bcw->remove_blocks( + [bl](const SlopeCraft::mc_block_interface *blk) -> bool { + return bl->contains(blk); + }); + if (not res) { + return tl::make_unexpected(std::move(res.error())); + } + removed_counter += res.value(); + } + this->blockslists.erase(it_removed); -bool isValidBlockInfo(const QJsonObject &json) { - return (json.contains("id") && json.contains("nameZH") && - json.contains("nameEN") && json.contains("baseColor")); + return removed_counter; } -void BlockListManager::getTokiBaseColors( - std::vector *dest) const { - dest->clear(); - dest->reserve(tbcs.size()); - for (const auto it : tbcs) { - dest->emplace_back(it); +void BlockListManager::when_version_updated() noexcept { + for (auto &bcw : this->basecolor_widgets) { + bcw->when_version_updated(this->callback_get_version()); } } -int BlockListManager::getBlockNum() const { - int result = 0; - for (auto it : tbcs) { - result += it->tbs.size(); +void BlockListManager::get_blocklist( + std::vector &enable_list, + std::vector &block_list) + const noexcept { + enable_list.resize(64); + block_list.resize(64); + + for (int bc = 0; bc < (int)this->basecolor_widgets.size(); bc++) { + enable_list[bc] = this->basecolor_widgets[bc]->is_enabled(); + block_list[bc] = this->basecolor_widgets[bc]->selected_block(); } - return result; -} -void BlockListManager::getBlockPtrs(const SlopeCraft::AbstractBlock **dest, - uint8_t *baseColor) const { - int idx = 0; - for (auto it : tbcs) { - for (auto jt : it->tbs) { - baseColor[idx] = it->baseColor; - dest[idx] = jt->getSimpleBlock(); - idx++; - } + for (int bc = (int)this->basecolor_widgets.size(); bc < 64; bc++) { + enable_list[bc] = 0; + block_list[bc] = nullptr; } - dest[idx] = nullptr; } -bool BlockListManager::loadPreset(const blockListPreset &preset) { - if (preset.values.size() != this->tbcs.size()) { +bool BlockListManager::loadPreset(const blockListPreset &preset) noexcept { + if (preset.values.size() != this->basecolor_widgets.size()) { QMessageBox::warning(dynamic_cast(this->parent()), tr("加载预设错误"), tr("预设文件包含的基色数量 (%1) 与实际情况 (%2) 不符") .arg(preset.values.size()) - .arg(this->tbcs.size())); + .arg(this->basecolor_widgets.size())); return false; } - std::vector blocks_arr; + for (int bc = 0; bc < (int)preset.values.size(); bc++) { + auto &bcw = this->basecolor_widgets[bc]; - for (size_t basecolor = 0; basecolor < this->tbcs.size(); basecolor++) { - auto tbc = this->tbcs[basecolor]; - const auto &pre = preset.values[basecolor]; + bcw->set_enabled(preset.values[bc].first); - this->setEnabled(basecolor, pre.first); - - // find block - int matched_block_idx = -1; - blocks_arr.clear(); - tbc->getTokiBlockList(blocks_arr); - for (int bidx = 0; bidx < (int)blocks_arr.size(); bidx++) { - const char *const blkid = blocks_arr[bidx]->getSimpleBlock()->getId(); - if (QString::fromUtf8(blkid) == pre.second) { - matched_block_idx = bidx; + auto &bws = bcw->block_widgets(); + int matched_idx = -1; + for (int idx = 0; idx < (int)bws.size(); idx++) { + if (QString::fromLatin1(bws[idx]->attached_block()->getId()) == + preset.values[bc].second) { + matched_idx = idx; break; } } - if (matched_block_idx < 0) { - QMessageBox::warning( + if (matched_idx < 0) { + auto ret = QMessageBox::warning( dynamic_cast(this->parent()), tr("加载预设错误"), tr("预设中为基色%1指定的方块 id 是\"%2\",没有找到这个方块 id") - .arg(basecolor) - .arg(pre.second)); - return false; + .arg(bc) + .arg(preset.values[bc].second), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore, + QMessageBox::StandardButton::Close}); + if (ret == QMessageBox::StandardButton::Close) { + exit(__LINE__); + // return false; + } + continue; } - this->setSelected(basecolor, matched_block_idx); + bcw->select_block_soft(matched_idx); } - emit blockListChanged(); + // emit this->changed(); return true; } -bool BlockListManager::loadInternalPreset( - const blockListPreset &preset) noexcept { - return this->loadPreset(preset); -} - -blockListPreset BlockListManager::currentPreset() const noexcept { +blockListPreset BlockListManager::to_preset() const noexcept { blockListPreset ret; - ret.values.resize(this->tbcs.size()); - for (size_t basecolor = 0; basecolor < this->tbcs.size(); basecolor++) { - ret.values[basecolor].first = this->tbcs[basecolor]->getEnabled(); + ret.values.resize(this->basecolor_widgets.size()); + for (size_t basecolor = 0; basecolor < this->basecolor_widgets.size(); + basecolor++) { + ret.values[basecolor].first = + this->basecolor_widgets[basecolor]->is_enabled(); ret.values[basecolor].second = QString::fromUtf8( - this->tbcs[basecolor]->getTokiBlock()->getSimpleBlock()->getId()); + this->basecolor_widgets[basecolor]->selected_block()->getId()); } return ret; } -const QString BlockListManager::baseColorNames[64] = {"00 None", - "01 Grass", - "02 Sand", - "03 Wool", - "04 Fire", - "05 Ice", - "06 Metal", - "07 Plant", - "08 Snow", - "09 Clay", - "10 Dirt", - "11 Stone", - "12 Water", - "13 Wood", - "14 Quartz", - "15 ColorOrange", - "16 ColorMagenta", - "17 ColorLightBlue", - "18 ColorYellow", - "19 ColorLime", - "20 ColorPink", - "21 ColorGray", - "22 ColorLightGray", - "23 ColorCyan", - "24 ColorPurple", - "25 ColorBlue", - "26 ColorBrown", - "27 ColorGreen", - "28 ColorRed", - "29 ColorBlack", - "30 Gold", - "31 Diamond", - "32 Lapis", - "33 Emerald", - "34 Podzol", - "35 Nether", - "36 TerracottaWhite", - "37 TerracottaOrange", - "38 TerracottaMagenta", - "39 TerracottaLightBlue", - "40 TerracottaYellow", - "41 TerracottaLime", - "42 TerracottaPink", - "43 TerracottaGray", - "44 TerracottaLightGray", - "45 TerracottaCyan", - "46 TerracottaPurple", - "47 TerracottaBlue", - "48 TerracottaBrown", - "49 TerracottaGreen", - "50 TerracottaRed", - "51 TerracottaBlack", - "52 CrimsonNylium", - "53 CrimsonStem", - "54 CrimsonHyphae", - "55 WarpedNylium", - "56 WarpedStem", - "57 WarpedHyphae", - "58 WarpedWartBlock", - "59 Deepslate", - "60 RawIron", - "61 GlowLichen", - "", - ""}; +selection BlockListManager::current_selection() const noexcept { + std::vector ret; + ret.reserve(64); + for (auto &bcw : this->basecolor_widgets) { + if (bcw->is_enabled()) { + ret.emplace_back(bcw->selected_block()->getId()); + } else { + ret.emplace_back(""); + } + } + return selection{ret}; +} + +const std::string_view basecolor_names[64] = {"00 None", + "01 Grass", + "02 Sand", + "03 Wool", + "04 Fire", + "05 Ice", + "06 Metal", + "07 Plant", + "08 Snow", + "09 Clay", + "10 Dirt", + "11 Stone", + "12 Water", + "13 Wood", + "14 Quartz", + "15 ColorOrange", + "16 ColorMagenta", + "17 ColorLightBlue", + "18 ColorYellow", + "19 ColorLime", + "20 ColorPink", + "21 ColorGray", + "22 ColorLightGray", + "23 ColorCyan", + "24 ColorPurple", + "25 ColorBlue", + "26 ColorBrown", + "27 ColorGreen", + "28 ColorRed", + "29 ColorBlack", + "30 Gold", + "31 Diamond", + "32 Lapis", + "33 Emerald", + "34 Podzol", + "35 Nether", + "36 TerracottaWhite", + "37 TerracottaOrange", + "38 TerracottaMagenta", + "39 TerracottaLightBlue", + "40 TerracottaYellow", + "41 TerracottaLime", + "42 TerracottaPink", + "43 TerracottaGray", + "44 TerracottaLightGray", + "45 TerracottaCyan", + "46 TerracottaPurple", + "47 TerracottaBlue", + "48 TerracottaBrown", + "49 TerracottaGreen", + "50 TerracottaRed", + "51 TerracottaBlack", + "52 CrimsonNylium", + "53 CrimsonStem", + "54 CrimsonHyphae", + "55 WarpedNylium", + "56 WarpedStem", + "57 WarpedHyphae", + "58 WarpedWartBlock", + "59 Deepslate", + "60 RawIron", + "61 GlowLichen", + "Unknown", + "Unknown"}; \ No newline at end of file diff --git a/utilities/BlockListManager/BlockListManager.h b/utilities/BlockListManager/BlockListManager.h index dd066cf2..471cb9c3 100644 --- a/utilities/BlockListManager/BlockListManager.h +++ b/utilities/BlockListManager/BlockListManager.h @@ -1,41 +1,13 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. +#ifndef SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BLOCKLISTMANAGER_H +#define SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BLOCKLISTMANAGER_H - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef BLOCKLISTMANAGER_H -#define BLOCKLISTMANAGER_H - -#include #include -#include -#include -#include -#include - -#include "../../SlopeCraftL/SlopeCraftL.h" +#include -#include "TokiBaseColor.h" -#include "TokiBlock.h" - -#define DispLine qDebug() << "File = " << __FILE__ << " , Line = " << __LINE__; +#include +#include +#include +#include "BaseColor.h" struct basecolorOption { uint8_t baseColor{0xFF}; @@ -54,79 +26,118 @@ QString serialize_preset(const blockListPreset &preset) noexcept; class BlockListDeleter { public: - void operator()(SlopeCraft::BlockListInterface *ptr) noexcept { - SlopeCraft::SCL_destroyBlockList(ptr); + void operator()(SlopeCraft::block_list_interface *ptr) const noexcept { + SlopeCraft::SCL_destroy_block_list(ptr); } }; -class BlockListManager : public QObject { +struct selection { + std::vector ids; + + [[nodiscard]] bool operator==(const selection &b) const noexcept { + if (this->ids.size() != b.ids.size()) { + return false; + } + for (size_t i = 0; i < this->ids.size(); i++) { + if (this->ids[i] != b.ids[i]) { + return false; + } + } + return true; + } +}; + +template <> +struct std::hash { + uint64_t operator()(const selection &s) const noexcept; +}; + +class BlockListManager : public QWidget { Q_OBJECT + private: + std::vector> basecolor_widgets; + std::vector< + std::pair>> + blockslists; + std::function callback_get_version{nullptr}; public: - explicit BlockListManager(QHBoxLayout *_area, QObject *parent = nullptr); - + explicit BlockListManager(QWidget *parent = nullptr); ~BlockListManager(); - bool setupFixedBlockList(const QString &filename, - const QString &imgdir) noexcept; - bool setupCustomBlockList(const QString &filename, - const QString &imgdir) noexcept; + void setup_basecolors() noexcept; - private: - bool impl_setupBlockList(const QString &filename, const QString &dirname, - std::unique_ptr &dst) noexcept; + bool add_blocklist(QString filename) noexcept; - public: - void setSelected(uchar baseColor, ushort blockSeq); + tl::expected remove_blocklist( + QString blocklist_name) noexcept; - void setEnabled(uchar baseColor, bool isEnable); + void finish_blocklist() noexcept; - void setLabelColors(const QRgb *); + void set_version_callback( + const std::function &cb) noexcept { + this->callback_get_version = cb; + } - void setVersion(uchar); + SCL_gameVersion version() const noexcept { + return this->callback_get_version(); + } - void getEnableList(bool *) const; - void getSimpleBlockList(const SlopeCraft::AbstractBlock **) const; - std::vector getSimpleBlockList() const; - std::vector getTokiBlockList() const; - std::vector getQRadioButtonList() const; - std::vector toPreset() const; + void when_version_updated() noexcept; - void getTokiBaseColors(std::vector *) const; + void when_lang_updated(SCL_language lang) noexcept { + for (auto &bcw : this->basecolor_widgets) { + bcw->update_lang(lang); + } + } - int getBlockNum() const; - void getBlockPtrs(const SlopeCraft::AbstractBlock **, uint8_t *) const; + void select_block_by_callback(const select_callback_t &fun) noexcept { + for (auto &bcw : this->basecolor_widgets) { + bcw->select_by_callback(fun); + } + } - bool loadPreset(const blockListPreset &preset); + void get_blocklist(std::vector &enable_list, + std::vector + &block_list) const noexcept; - bool loadInternalPreset(const blockListPreset &preset) noexcept; + bool loadPreset(const blockListPreset &preset) noexcept; - blockListPreset currentPreset() const noexcept; + blockListPreset to_preset() const noexcept; - public slots: + size_t num_basecolor_widgets() const noexcept { + assert(this->basecolor_widgets.size() < 256); + return this->basecolor_widgets.size(); + } - signals: - void translate(Language); - void switchToCustom() const; - void blockListChanged() const; + BaseColorWidget *basecolorwidget_at(size_t basecolor) noexcept { + return this->basecolor_widgets[basecolor].get(); + } - private: - bool isApplyingPreset; - QHBoxLayout *area; - std::vector tbcs; + const BaseColorWidget *basecolorwidget_at(size_t basecolor) const noexcept { + return this->basecolor_widgets[basecolor].get(); + } - std::unique_ptr BL_fixed{ - nullptr}; - std::unique_ptr BL_custom{ - nullptr}; + [[nodiscard]] selection current_selection() const noexcept; + + [[nodiscard]] std::vector< + std::pair> + get_block_lists() const noexcept { + std::vector> + ret; + for (auto &[name, list] : this->blockslists) { + ret.emplace_back(name, list.get()); + } + return ret; + } - static const QString baseColorNames[64]; + signals: + void changed(); - private slots: - void receiveClicked() const; + private: + std::unique_ptr + impl_addblocklist(const QByteArray &file_content) noexcept; }; -bool isValidBlockInfo(const QJsonObject &); - -#endif // BLOCKLISTMANAGER_H +#endif // SLOPECRAFT_UTILITIES_BLOCKLISTMANAGER_BLOCKLISTMANAGER_H diff --git a/utilities/BlockListManager/BlockListManager_en_US.ts b/utilities/BlockListManager/BlockListManager_en_US.ts deleted file mode 100644 index 07c7129f..00000000 --- a/utilities/BlockListManager/BlockListManager_en_US.ts +++ /dev/null @@ -1,42 +0,0 @@ - - - - - BlockListManager - - - - 解析方块列表失败 - Failed to parse block list - - - - - 加载预设错误 - Failed to load preset - - - - 预设文件包含的基色数量 (%1) 与实际情况 (%2) 不符 - There 're %1 basecolors in the preset file, while actually there are %2 basecolors - - - - 预设中为基色%1指定的方块 id 是"%2",没有找到这个方块 id - Block "%2" is assigned to base color %1, but failed to find a block with such id - - - - QObject - - - 基色 %1 的预设被重复定义。一个基色只能被定义一次。 - The preset of base color %1 is defined multiple time. A base color can be defined only once. - - - - 解析预设 json 时发生异常:"%1" - Exception occured when parsing preset json: "%1" - - - diff --git a/utilities/BlockListManager/BlockListManager_global.h b/utilities/BlockListManager/BlockListManager_global.h deleted file mode 100644 index 832d3d00..00000000 --- a/utilities/BlockListManager/BlockListManager_global.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef BLOCKLISTMANAGER_GLOBAL_H -#define BLOCKLISTMANAGER_GLOBAL_H - -#include - -#if defined(BLOCKLISTMANAGER_LIBRARY) -#define BLOCKLISTMANAGER_EXPORT Q_DECL_EXPORT -#else -#define BLOCKLISTMANAGER_EXPORT Q_DECL_IMPORT -#endif - -#endif // BLOCKLISTMANAGER_GLOBAL_H diff --git a/utilities/BlockListManager/CMakeLists.txt b/utilities/BlockListManager/CMakeLists.txt index 96176f9f..324f9aa9 100644 --- a/utilities/BlockListManager/CMakeLists.txt +++ b/utilities/BlockListManager/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.5) project(SlopeCraft_BlockListManager VERSION ${SlopeCraft_version} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -8,51 +7,50 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets LinguistTools REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets LinguistTools REQUIRED) - -include_directories(${CMAKE_SOURCE_DIR}) - -include(${CMAKE_SOURCE_DIR}/cmake/find_nlohmann_json.cmake) +find_package(Qt6 COMPONENTS Widgets LinguistTools REQUIRED) +find_package(Boost CONFIG REQUIRED) set(BlockListManager_header_files - BlockListManager.h - BlockListManager_global.h - TokiBaseColor.h - TokiBlock.h) + BaseColor.h + Block.h) set(BlockListManager_source_files - BlockListManager.cpp - TokiBaseColor.cpp - TokiBlock.cpp + BaseColor.cpp + Block.cpp BLM_preset.cpp) -set(BlockListManager_ts_files - ${CMAKE_CURRENT_SOURCE_DIR}/BlockListManager_en_US.ts +set(BlockListManager_ui_files + BaseColorWidget.ui ) -if(${SlopeCraft_update_ts_files}) - execute_process( - COMMAND ${SlopeCraft_Qt_lupdate_executable} ${BlockListManager_header_files} ${BlockListManager_source_files} "-ts" ${BlockListManager_ts_files} ${SlopeCraft_ts_flags} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) -endif() +set(BlockListManager_ts_files + others/BlockListManager_en_US.ts + + # ${CMAKE_CURRENT_SOURCE_DIR}/BlockListManager_en_US.ts +) -set(BlockListManaer_project_sources +set(BlockListManager_project_sources ${BlockListManager_header_files} ${BlockListManager_source_files} - ${BlockListManager_ts_files} -) + ${BlockListManager_ui_files} -include_directories(../SlopeCraftL) - -add_library(BlockListManager STATIC ${BlockListManaer_project_sources}) -target_link_libraries(BlockListManager PUBLIC Qt${QT_VERSION_MAJOR}::Widgets SlopeCraftL) -target_compile_definitions(BlockListManager PRIVATE BlockListManager_LIBRARY) -target_include_directories(BlockListManager PRIVATE ${SlopeCraft_Nlohmann_json_include_dir}) + # ${BlockListManager_ts_files} +) +# include_directories(../SlopeCraftL) +add_library(BlockListManager STATIC ${BlockListManager_project_sources}) +target_link_libraries(BlockListManager PUBLIC Qt6::Widgets SlopeCraftL) +target_include_directories(BlockListManager INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(BlockListManager PRIVATE ${SlopeCraft_Nlohmann_json_include_dir} ${Boost_INCLUDE_DIRS}) +target_compile_features(BlockListManager PUBLIC cxx_std_23) + +qt_add_lupdate(BlockListManager + TS_FILES ${BlockListManager_ts_files} + SOURCES ${BlockListManager_project_sources} + OPTIONS ${SC_lupdate_flags} +) qt_add_lrelease(BlockListManager TS_FILES ${BlockListManager_ts_files} QM_FILES_OUTPUT_VARIABLE BlockListManager_qm_files) qt_add_resources(BlockListManager "BLM_translations" diff --git a/utilities/BlockListManager/TokiBaseColor.cpp b/utilities/BlockListManager/TokiBaseColor.cpp deleted file mode 100644 index fc595ef3..00000000 --- a/utilities/BlockListManager/TokiBaseColor.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TokiBaseColor.h" - -uchar TokiBaseColor::mcVer = 17; - -TokiBaseColor::TokiBaseColor(uchar _baseColor, QGridLayout *_layout, - QObject *parent) - : QObject(parent) { - layout = _layout; - baseColor = _baseColor; - // 负责创建 QCheckBox 和对应的弹簧 - - layout->addWidget(checkBox = new QCheckBox("启用"), 0, 1); - checkBox->setChecked(false); - checkBox->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, - QSizePolicy::Policy::Preferred)); - checkBox->setEnabled(baseColor != 0); - - connect(checkBox, &QCheckBox::toggled, this, &TokiBaseColor::updateEnabled); - connect(checkBox, &QCheckBox::toggled, this, &TokiBaseColor::userClicked); - connect(this, &TokiBaseColor::translate, this, - &TokiBaseColor::translateCheckBox); - // 创建弹簧 - QSpacerItem *si = new QSpacerItem(40, 20, QSizePolicy::Policy::Expanding, - QSizePolicy::Policy::Preferred); - layout->addItem(si, 0, 2); - - isEnabled = true; - tbs.clear(); - selected = 65535; - versionCheck(); -} - -TokiBaseColor::~TokiBaseColor() { - for (ushort i = 0; i < tbs.size(); i++) delete tbs[i]; -} - -void TokiBaseColor::addTokiBlock(SlopeCraft::AbstractBlock *blkp) noexcept { - QRadioButton *qrb = new QRadioButton; - int rows = 1 + tbs.size() / 2; - int cols = 1 + tbs.size() % 2; - layout->addWidget(qrb, rows, cols); - - TokiBlock *tb = new TokiBlock(qrb, blkp, tbs.size(), this); - tbs.push_back(tb); - - if (tb->getSimpleBlock()->getVersion() > mcVer) { - tb->getNCTarget()->setEnabled(false); - tb->getNCTarget()->setChecked(false); - } - - connect(tb, &TokiBlock::radioBtnClicked, this, - &TokiBaseColor::receiveClicked); - connect(this, &TokiBaseColor::translate, tb, &TokiBlock::translate); - connect(tb, &TokiBlock::radioBtnClicked, this, &TokiBaseColor::userClicked); -} - -void TokiBaseColor::makeLabel(QRgb color) { - QLabel *qL = new QLabel(""); - layout->addWidget(qL, 0, 0, ceil(1 + tbs.size() / 2.0), 1); - QPalette pl; - pl.setColor(QPalette::ColorRole::Text, Qt::black); - pl.setColor( - QPalette::ColorRole::Window, - QColor(qRed(color), qGreen(color), qBlue(color), 255 * bool(baseColor))); - - qL->setPalette(pl); - qL->setSizePolicy(QSizePolicy::Policy::Preferred, - QSizePolicy::Policy::Preferred); - qL->setMinimumWidth(30); - qL->setFrameShape(QFrame::Shape::StyledPanel); - qL->setFrameShadow(QFrame::Shadow::Plain); - qL->setLineWidth(1); - qL->setAutoFillBackground(true); -} - -void TokiBaseColor::receiveClicked(ushort _selected) { - selected = _selected % tbs.size(); - versionCheck(); -} - -bool TokiBaseColor::isAllOverVersion() const { // 判断是否所有方块都超版本了 - bool isAllOver = true; - for (auto it = tbs.cbegin(); it != tbs.cend(); it++) { - isAllOver &= ((*it)->block->getVersion() > mcVer); - } - return isAllOver; -} - -void TokiBaseColor::versionCheck() { - checkBox->setEnabled(baseColor != 0 && !isAllOverVersion()); - if (!checkBox->isEnabled()) { - checkBox->setChecked(false || baseColor == 0); - } - isEnabled = checkBox->isChecked(); - - if (tbs.size() <= 0) { - selected = 65535; - return; - } - if (tbs.size() == 1) { - selected = 0; - tbs[0]->getNCTarget()->setChecked(true); - tbs[0]->getNCTarget()->setEnabled(false); - return; - } - std::vector scores(tbs.size()); - for (ushort idx = 0; idx < tbs.size(); idx++) { - if (tbs[idx]->getSimpleBlock()->getVersion() <= mcVer) { - scores[idx] = (tbs[idx]->getTarget()->isChecked()) ? 100 : 51; - tbs[idx]->getNCTarget()->setEnabled(true); - } else { - scores[idx] = (idx <= 0); - tbs[idx]->getNCTarget()->setEnabled(false); - } - } - ushort maxIndex = 0; - for (ushort idx = 0; idx < scores.size(); idx++) { - if (scores[idx] > scores[maxIndex]) maxIndex = idx; - } - selected = maxIndex; - if (!tbs[selected]->getTarget()->isChecked()) { - tbs[selected]->getNCTarget()->setChecked(true); - } -} - -void TokiBaseColor::setSelected(ushort sel) { - tbs[sel]->getNCTarget()->setChecked(true); - versionCheck(); -} - -void TokiBaseColor::updateEnabled(bool isChecked) { - isEnabled = isChecked; - versionCheck(); -} - -void TokiBaseColor::translateCheckBox(Language lang) { - switch (lang) { - case Language::ZH: - checkBox->setText("启用"); - break; - case Language::EN: - checkBox->setText("Enable"); - break; - } -} - -const TokiBlock *TokiBaseColor::getTokiBlock() const { - // qDebug("getTokiBlock"); - // qDebug()< &dest) const { - dest.clear(); - dest.reserve(tbs.size()); - for (const auto it : tbs) { - dest.emplace_back(it); - } -} diff --git a/utilities/BlockListManager/TokiBaseColor.h b/utilities/BlockListManager/TokiBaseColor.h deleted file mode 100644 index ebae60ed..00000000 --- a/utilities/BlockListManager/TokiBaseColor.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef TOKIBASECOLOR_H -#define TOKIBASECOLOR_H - -#include -#include -#include -#include -#include -#include -#include - -#include "TokiBlock.h" - -class BlockListManager; - -class TokiBaseColor : public QObject { - Q_OBJECT - friend class BlockListManager; - - public: - explicit TokiBaseColor(uchar, QGridLayout *, QObject *parent = nullptr); - ~TokiBaseColor(); - - const TokiBlock *getTokiBlock() const; - - bool getEnabled() const; - - ushort getSelected() const; - - void addTokiBlock(SlopeCraft::AbstractBlock *blkp) noexcept; - - void makeLabel(QRgb); - - void getTokiBlockList(std::vector &) const; - - static uchar mcVer; - - signals: - void userClicked(); - void translate(Language); - - private: - uchar baseColor; - bool isEnabled; - ushort selected; - QGridLayout *layout; - QCheckBox *checkBox; - std::vector tbs; - bool isAllOverVersion() const; - void setSelected(ushort); - private slots: - void receiveClicked(ushort); - void updateEnabled(bool); - void versionCheck(); - void translateCheckBox(Language); -}; - -#endif // TOKIBASECOLOR_H diff --git a/utilities/BlockListManager/TokiBlock.cpp b/utilities/BlockListManager/TokiBlock.cpp deleted file mode 100644 index 9c0a09af..00000000 --- a/utilities/BlockListManager/TokiBlock.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "TokiBlock.h" - -using namespace SlopeCraft; - -TokiBlock::TokiBlock(QRadioButton *_target, SlopeCraft::AbstractBlock *blkp, - uint16_t _self, QObject *parent) - : QObject(parent) { - this->self = _self; - this->target = _target; - this->block = blkp; - - target->setText(nameZH()); - target->setChecked(true); - - connect(target, &QRadioButton::clicked, this, &TokiBlock::onTargetClicked); - - QImage img(16, 16, QImage::Format_ARGB32); - - this->block->getImage((uint32_t *)img.scanLine(0), true); - - this->target->setIcon(QIcon(QPixmap::fromImage(img))); -} - -void TokiBlock::translate(Language lang) { - switch (lang) { - case Language::ZH: - target->setText(nameZH()); - break; - case Language::EN: - target->setText(nameEN()); - break; - } - return; -} - -TokiBlock::~TokiBlock() {} - -void TokiBlock::onTargetClicked(bool isChecked) { - if (isChecked) emit radioBtnClicked(self); -} - -const QRadioButton *TokiBlock::getTarget() const { return target; } - -const AbstractBlock *TokiBlock::getSimpleBlock() const { return block; } - -QRadioButton *TokiBlock::getNCTarget() const { return target; } diff --git a/utilities/BlockListManager/TokiBlock.h b/utilities/BlockListManager/TokiBlock.h deleted file mode 100644 index 581a299d..00000000 --- a/utilities/BlockListManager/TokiBlock.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef TOKIBLOCK_H -#define TOKIBLOCK_H - -#include -#include -#include -#include -#include -#include - -#include "../../SlopeCraftL/SlopeCraftL.h" - -class TokiBaseColor; - -enum class Language { ZH, EN }; - -class TokiBlock : public QObject { - Q_OBJECT - friend class TokiBaseColor; - - public: - explicit TokiBlock(QRadioButton *_target, SlopeCraft::AbstractBlock *blkp, - uint16_t _self, QObject *parent); - - ~TokiBlock(); - const QRadioButton *getTarget() const; - const SlopeCraft::AbstractBlock *getSimpleBlock() const; - - QString nameZH() const noexcept { - return QString::fromUtf8(this->block->getNameZH()); - } - QString nameEN() const noexcept { - return QString::fromUtf8(this->block->getNameEN()); - } - - signals: - void radioBtnClicked(ushort); - - private: - ushort self; // 指明自己是所属基色的第 i 个方块 - QRadioButton *target; - SlopeCraft::AbstractBlock *block; - QRadioButton *getNCTarget() const; - private slots: - void translate(Language); - void onTargetClicked(bool); -}; - -#endif // TOKIBLOCK_H diff --git a/utilities/BlockListManager/others/BlockListManager_en_US.ts b/utilities/BlockListManager/others/BlockListManager_en_US.ts new file mode 100644 index 00000000..469601a4 --- /dev/null +++ b/utilities/BlockListManager/others/BlockListManager_en_US.ts @@ -0,0 +1,83 @@ + + + + + BaseColorWidget + + + 启用 + Enable + + + + 无法删除方块 %1,基色 %2 只拥有 %3个方块,若继续删除,则该基色将没有方块,SlopeCraft 可能崩溃。 + Failed to remove %1: base color %2 has only %3 block now, if you continue to delete, there will not be any block for this base color, and SlopeCraft will possible crash. + + + + BlockListManager + + + 解析方块列表失败 + Failed to parse block list + + + + 解析方块列表成功,但出现警告 + Block list parsed with warnings + + + + + 无法加载方块列表 + Failed to parse block list + + + + 名为 %1 的方块列表已经加载,不允许加载同名的方块列表。 + Block list named %1 was already loaded, and it's not allowed to load multiple block list with identity name. + + + + 无法读取文件 %1 ,它可能被误删。 + Unable to read zip archive "%1", it might be deleted accidentally. + + + + 无法删除方块列表 "%1",没有加载同名的方块列表。已加载:%2 + Failed to load block list \"%1\", no block list for such name. Loaded: %2 + + + + + 加载预设错误 + Failed to load preset + + + + 预设文件包含的基色数量 (%1) 与实际情况 (%2) 不符 + There 're %1 basecolors in the preset file, while actually there are + %2 basecolors + + + + 预设中为基色%1指定的方块 id 是"%2",没有找到这个方块 id + Block "%2" is assigned to base color %1, but failed to find a + block with such id + + + + QObject + + + 基色 %1 的预设被重复定义。一个基色只能被定义一次。 + The preset of base color %1 is defined multiple time. A base color can be + defined only once. + + + + 解析预设 json 时发生异常:"%1" + Exception occured when parsing preset json: "%1" + + + diff --git a/utilities/CMakeLists.txt b/utilities/CMakeLists.txt index 1f3e57ff..bbfde7be 100644 --- a/utilities/CMakeLists.txt +++ b/utilities/CMakeLists.txt @@ -1,14 +1,15 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -configure_file(SC_version_buildtime.h.in - SC_version_buildtime.h) - -include(install.cmake) +if (${LINUX}) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif () add_subdirectory(MCDataVersion) +add_subdirectory(version_set) add_subdirectory(GPUWrapper) add_subdirectory(ColorManip) add_subdirectory(ProcessBlockId) +add_subdirectory(AdaptiveLabel) add_subdirectory(BlockListManager) add_subdirectory(ExternalConverters) add_subdirectory(Schem) @@ -17,6 +18,26 @@ add_subdirectory(uiPack) add_subdirectory(MapImageCvter) add_subdirectory(VersionDialog) add_subdirectory(VCLConfigLoader) +add_subdirectory(FlatDiagram) +add_subdirectory(libpngReader) +add_subdirectory(StatMemory) +add_subdirectory(sNBT_formatter) + +function(SC_process_boolean value_name) + if (${${value_name}}) + set(${value_name} "true" PARENT_SCOPE) + else () + set(${value_name} "false" PARENT_SCOPE) + endif () +endfunction(SC_process_boolean value_name) + +SC_process_boolean(SlopeCraft_vectorize) +SC_process_boolean(SlopeCraft_gprof) + +configure_file(SC_version_buildtime.h.in + SC_version_buildtime.h) + +include(install.cmake) # add_subdirectory(libAbstractGUI) # add_subdirectory(libSCGUI) \ No newline at end of file diff --git a/utilities/ColorManip/CIEDE00.cpp b/utilities/ColorManip/CIEDE00.cpp index 0e13976d..c40c7973 100644 --- a/utilities/ColorManip/CIEDE00.cpp +++ b/utilities/ColorManip/CIEDE00.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,8 +22,8 @@ This file is part of SlopeCraft. #include "ColorManip.h" #include -#define deg2rad(deg) ((deg)*M_PI / 180.0) -#define rad2deg(rad) ((rad)*180.0 / M_PI) +#define deg2rad(deg) ((deg) * M_PI / 180.0) +#define rad2deg(rad) ((rad) * 180.0 / M_PI) const float kL = 1.0; const float kC = 1.0; const float kH = 1.0; @@ -47,15 +47,13 @@ float Lab00_diff(float L1, float a1, float b1, float L2, float a2, h1p = 0; else h1p = std::atan2(b1, a1p); - if (h1p < 0) - h1p += 2 * M_PI; + if (h1p < 0) h1p += 2 * M_PI; if (b2 == 0 && a2p == 0) h2p = 0; else h2p = std::atan2(b2, a2p); - if (h2p < 0) - h2p += 2 * M_PI; + if (h2p < 0) h2p += 2 * M_PI; float dLp = L2 - L1; float dCp = C2p - C1p; diff --git a/utilities/ColorManip/CMakeLists.txt b/utilities/ColorManip/CMakeLists.txt index 7d9ea4b5..7bc954ff 100644 --- a/utilities/ColorManip/CMakeLists.txt +++ b/utilities/ColorManip/CMakeLists.txt @@ -1,10 +1,11 @@ project(ColorManip VERSION ${SlopeCraft_version} LANGUAGES C CXX) -# set(CMAKE_CXX_STANDARD 20) -# set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# include_directories(../SlopeCraftL) find_package(OpenMP REQUIRED) +find_package(Eigen3 REQUIRED) +find_package(Heu REQUIRED) +find_package(cereal REQUIRED) +find_package(xsimd REQUIRED) +find_package(Boost CONFIG REQUIRED) add_library(ColorManip @@ -15,8 +16,8 @@ add_library(ColorManip newColorSet.hpp newTokiColor.hpp - StaticMembers.cpp + hash.cpp colorset_maptical.hpp imageConvert.hpp newColorSet.hpp @@ -25,25 +26,24 @@ add_library(ColorManip target_compile_features(ColorManip PUBLIC cxx_std_20) -# set_target_properties(ColorManip PROPERTIES CXX_STANDARD_REQUIRED ON) - -# get_target_property(cm_cxx_std ColorManip CXX_STANDARD) -# message(STATUS "cm_cxx_std = " ${cm_cxx_std}) target_include_directories(ColorManip PUBLIC - ${SlopeCraft_Eigen3_include_dir} - ${CMAKE_SOURCE_DIR}/utilities) - + ${CMAKE_SOURCE_DIR}/utilities + ${SlopeCraft_Chocobo1_Hash_include_dir}) target_include_directories(ColorManip PRIVATE - ${SlopeCraft_HeuristicFlow_include_dir}) + ${Boost_INCLUDE_DIRS}) + +target_link_libraries(ColorManip PRIVATE + Heu::Genetic) -target_link_libraries(ColorManip PUBLIC OpenMP::OpenMP_CXX) +target_link_libraries(ColorManip PUBLIC + OpenMP::OpenMP_CXX + Eigen3::Eigen + cereal::cereal + xsimd) # target_compile_options(ColorManip BEFORE PUBLIC "-std=c++17") target_compile_options(ColorManip PRIVATE ${SlopeCraft_vectorize_flags}) -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set_target_properties(ColorManip PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() # ColorManip links to GPUInterface target_link_libraries(ColorManip PUBLIC GPUInterface) @@ -51,19 +51,18 @@ target_include_directories(ColorManip INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) find_package(OpenCL 3.0) -if(${OpenCL_FOUND}) +if (${OpenCL_FOUND}) add_executable(test_init_program tests/test_init_program.cpp) target_link_libraries(test_init_program PRIVATE OpenMP::OpenMP_CXX ColorManip) add_executable(test_algo tests/test_algo.cpp) target_link_libraries(test_algo PRIVATE OpenMP::OpenMP_CXX ColorManip) - include(${CMAKE_SOURCE_DIR}/cmake/configure_cli11.cmake) target_include_directories(test_algo PRIVATE ${cli11_include_dir}) - foreach(_algo r R H X l L) + foreach (_algo r R H X l L) add_test(NAME test_algo_${_algo} COMMAND test_algo --algo ${_algo} --platform-idx 0 --device-idx 0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endforeach(_algo r R H X l L) -endif() + endforeach (_algo r R H X l L) +endif () diff --git a/utilities/ColorManip/ColorCvt.cpp b/utilities/ColorManip/ColorCvt.cpp index 6578fcc6..9025dc99 100644 --- a/utilities/ColorManip/ColorCvt.cpp +++ b/utilities/ColorManip/ColorCvt.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ This file is part of SlopeCraft. static constexpr float threshold = 1e-10f; -#define deg2rad(deg) ((deg)*M_PI / 180.0) +#define deg2rad(deg) ((deg) * M_PI / 180.0) ARGB ComposeColor(const ARGB top, const ARGB back) noexcept { int red = (getR(top) * getA(top) + getR(back) * (255 - getA(top))) / 255; @@ -47,11 +47,9 @@ ARGB ComposeColor_background_half_transparent(const ARGB top, if (alpha_back <= 0) { return top; } - if (alpha_top <= 0) - return back; + if (alpha_top <= 0) return back; - if (alpha_top >= 255) - return top; + if (alpha_top >= 255) return top; // int result_alpha = alpha_top + alpha_back - alpha_back * alpha_top / // 255; @@ -227,10 +225,8 @@ void Lab2XYZ(float L, float a, float b, float &X, float &Y, float &Z) noexcept { } inline float squeeze01(float t) noexcept { - if (t < 0.0) - return 0.0f; - if (t > 1.0) - return 1.0f; + if (t < 0.0) return 0.0f; + if (t > 1.0) return 1.0f; return t; } diff --git a/utilities/ColorManip/ColorDiff.cpp b/utilities/ColorManip/ColorDiff.cpp index 490c5ba2..25163fa6 100644 --- a/utilities/ColorManip/ColorDiff.cpp +++ b/utilities/ColorManip/ColorDiff.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,6 +22,8 @@ This file is part of SlopeCraft. #include "ColorManip.h" #include +#include +#include "newTokiColor.hpp" inline float square(float x) { return x * x; } @@ -33,7 +35,7 @@ float color_diff_RGB_plus(const float r1, const float g1, const float b1, constexpr float w_r = 1.0f, w_g = 2.0f, w_b = 1.0f; const float SqrModSquare = - (r1 * r1 + g1 * g1 + b1 * b1) * (r2 * r2 + g2 * g2 + b2 * b2); + std::sqrt((r1 * r1 + g1 * g1 + b1 * b1) * (r2 * r2 + g2 * g2 + b2 * b2)); const float deltaR = r1 - r2; const float deltaG = g1 - g2; @@ -71,12 +73,14 @@ float color_diff_RGB_plus(const float r1, const float g1, const float b1, (w_r + w_g + w_b) + S_theta * S_ratio * theta * theta; + assert(!std::isnan(result)); + assert(result >= 0); + return result; } float color_diff_HSV(float h1, float s1, float v1, float h2, float s2, float v2) noexcept { - const float sv_1 = s1 * v1; const float sv_2 = s2 * v2; @@ -85,4 +89,309 @@ float color_diff_HSV(float h1, float s1, float v1, float h2, float s2, const float dZ = 50.0f * (v1 - v2); return dX * dX + dY * dY + dZ * dZ; +} + +// using selected_arch = xsimd::default_arch; +using batch_t = xsimd::batch; +constexpr size_t batch_size = batch_t::size; +void colordiff_RGB_batch(std::span r1p, std::span g1p, + std::span b1p, + std::span rgb2, + std::span dest) noexcept { + assert(r1p.size() == g1p.size()); + assert(g1p.size() == b1p.size()); + assert(b1p.size() == dest.size()); + + const size_t color_count = r1p.size(); + + const size_t vec_size = color_count - color_count % batch_size; + + const float r2{rgb2[0]}, g2{rgb2[1]}, b2{rgb2[2]}; + + for (size_t idx = 0; idx < vec_size; idx += batch_size) { + batch_t r1{batch_t ::load_aligned(r1p.data() + idx)}; + batch_t g1{batch_t ::load_aligned(g1p.data() + idx)}; + batch_t b1{batch_t ::load_aligned(b1p.data() + idx)}; + + auto dr = r1 - r2; + auto dg = g1 - g2; + auto db = b1 - b2; + batch_t diff = dr * dr + dg * dg + db * db; + diff.store_aligned(dest.data() + idx); + } + for (size_t idx = vec_size; idx < color_count; idx++) { + const float r1 = r1p[idx], g1 = g1p[idx], b1 = b1p[idx]; + const float dr = r1 - r2; + const float dg = g1 - g2; + const float db = b1 - b2; + auto diff = dr * dr + dg * dg + db * db; + dest[idx] = diff; + } +} + +inline void assert_if_nan([[maybe_unused]] batch_t val) noexcept { + for (size_t i = 0; i < batch_size; i++) { + assert(!std::isnan(val.get(i))); + } +} + +void colordiff_RGBplus_batch(std::span r1p, + std::span g1p, + std::span b1p, + std::span c3, + std::span dest) noexcept { + assert(r1p.size() == g1p.size()); + assert(g1p.size() == b1p.size()); + assert(b1p.size() == dest.size()); + + std::fill(dest.begin(), dest.end(), NAN); + + const size_t color_count = r1p.size(); + const size_t vec_size = color_count - color_count % batch_size; + const float r2 = c3[0], g2 = c3[1], b2 = c3[2]; + + // const batch_t r2{_r2}, g2{_g2}, b2{_b2}; + + // const batch_t thre_{threshold}; + + const float rr_plus_gg_plus_bb_2 = (r2 * r2 + g2 * g2 + b2 * b2); + constexpr float w_r = 1.0f, w_g = 2.0f, w_b = 1.0f; + + for (size_t i = 0; i < vec_size; i += batch_size) { + batch_t r1{batch_t ::load_aligned(r1p.data() + i)}; + batch_t g1{batch_t ::load_aligned(g1p.data() + i)}; + batch_t b1{batch_t ::load_aligned(b1p.data() + i)}; + + auto deltaR = r1 - r2; + auto deltaG = g1 - g2; + auto deltaB = b1 - b2; + + batch_t SqrModSquare; + { + const batch_t rr_plus_gg_plus_bb_1 = r1 * r1 + g1 * g1 + b1 * b1; + + SqrModSquare = rr_plus_gg_plus_bb_1 * rr_plus_gg_plus_bb_2; + SqrModSquare = xsimd::sqrt(SqrModSquare); + } + assert_if_nan(SqrModSquare); + + const batch_t sigma_rgb = (r1 + g1 + b1 + r2 + g2 + b2) * float(1.0f / 3); + + const batch_t sigma_rgb_plus_thre = sigma_rgb + threshold; + + const batch_t r1_plus_r2 = r1 + r2; + const batch_t g1_plus_g2 = g1 + g2; + const batch_t b1_plus_b2 = b1 + b2; + batch_t S_r, S_g, S_b; + { + batch_t temp_r = r1_plus_r2 / sigma_rgb_plus_thre; + batch_t temp_g = g1_plus_g2 / sigma_rgb_plus_thre; + batch_t temp_b = b1_plus_b2 / sigma_rgb_plus_thre; + + S_r = min(temp_r, batch_t{1.0f}); + S_g = min(temp_g, batch_t{1.0f}); + S_b = min(temp_b, batch_t{1.0f}); + } + + const batch_t sumRGBsquare = r1 * r2 + g1 * g2 + b1 * b2; + + batch_t theta; + { + batch_t temp1 = sumRGBsquare / (SqrModSquare + threshold); + + temp1 /= 1.01f; + const batch_t temp2 = acos(temp1); + // batch_t temp2 = _mm256_acos_ps__manually(temp1); + theta = temp2 * float(2.0 / M_PI); + } + + const batch_t OnedDeltaR = abs(deltaR) / (r1_plus_r2 * thre); + const batch_t OnedDeltaG = abs(deltaG) / (g1_plus_g2 * thre); + const batch_t OnedDeltaB = abs(deltaB) / (b1_plus_b2 * thre); + + const batch_t sumOnedDelta = OnedDeltaR + OnedDeltaG + OnedDeltaB + thre; + + batch_t S_tr = OnedDeltaR / sumOnedDelta * (S_r * S_r); + batch_t S_tg = OnedDeltaG / sumOnedDelta * (S_g * S_g); + batch_t S_tb = OnedDeltaB / sumOnedDelta * (S_b * S_b); + + batch_t S_theta = S_tr + S_tg + S_tb; + + batch_t S_ratio; + { + batch_t max_r = max(r1, {r2}); + batch_t max_g = max(g1, {g2}); + batch_t max_b = max(b1, {b2}); + S_ratio = max(max_r, max(max_g, max_b)); + } + /* + * + const float result = + (S_r * S_r * w_r * deltaR * deltaR + S_g * S_g * w_g * deltaG * + deltaG + S_b * S_b * w_b * deltaB * deltaB) / (w_r + w_g + w_b) + S_theta * + S_ratio * theta * theta; + * */ + batch_t diff; + { + batch_t temp_r = S_r * S_r * deltaR * deltaR * w_r; + batch_t temp_g = S_g * S_g * deltaG * deltaG * w_g; + batch_t temp_b = S_b * S_b * deltaB * deltaB * w_b; + batch_t wr_plus_wr_plus_wb{w_r + w_b + w_g}; + batch_t temp_X = (temp_r + temp_g + temp_b) / wr_plus_wr_plus_wb; + + batch_t temp_Y = S_theta * S_ratio * theta * theta; + + diff = temp_X + temp_Y; + } + diff.store_aligned(dest.data() + i); + } + + for (size_t i = vec_size; i < color_count; i++) { + dest[i] = color_diff_RGB_plus(r1p[i], g1p[i], b1p[i], r2, g2, b2); + } +} + +void colordiff_HSV_batch(std::span h1p, std::span s1p, + std::span v1p, + std::span hsv2, + std::span dest) noexcept { + assert(h1p.size() == s1p.size()); + assert(s1p.size() == v1p.size()); + assert(v1p.size() == dest.size()); + + const size_t color_count = h1p.size(); + const size_t vec_size = color_count - color_count % batch_size; + + const float h2 = hsv2[0]; + const float s2 = hsv2[1]; + const float v2 = hsv2[2]; + + for (size_t idx = 0; idx < vec_size; idx += batch_size) { + const batch_t h1 = batch_t::load_aligned(h1p.data() + idx); + const batch_t s1 = batch_t::load_aligned(s1p.data() + idx); + const batch_t v1 = batch_t::load_aligned(v1p.data() + idx); + auto sv_1 = s1 * v1; + auto sv_2 = s2 * v2; + + const auto dX = 50.0f * (cos(h1) * sv_1 - std::cos(h2) * sv_2); + const auto dY = 50.0f * (sin(h1) * sv_1 - std::sin(h2) * sv_2); + const auto dZ = 50.0f * (v1 - v2); + + const auto diff = dX * dX + dY * dY + dZ * dZ; + diff.store_aligned(dest.data() + idx); + } + + for (size_t idx = vec_size; idx < color_count; idx++) { + const float diff = + color_diff_HSV(hsv2[0], hsv2[1], hsv2[2], h1p[idx], s1p[idx], v1p[idx]); + dest[idx] = diff; + } +} + +void colordiff_Lab94_batch(std::span l1p, + std::span a1p, + std::span b1p, + std::span lab2, + std::span dest) noexcept { + const auto &c3 = lab2; + + assert(l1p.size() == a1p.size()); + assert(a1p.size() == b1p.size()); + assert(b1p.size() == dest.size()); + + const size_t color_count = l1p.size(); + const size_t vec_size = color_count - color_count % batch_size; + + const float L2 = c3[0]; + const float a2 = c3[1]; + const float b2 = c3[2]; + //__m256 C1_2; + const float sqrt_C1_2 = sqrt(a2 * a2 + b2 * b2); + const float SC_2 = square(sqrt_C1_2 * 0.045f + 1.0f); + + for (size_t i = 0; i < vec_size; i += batch_size) { + batch_t L1 = batch_t::load_aligned(l1p.data() + i); + batch_t a1 = batch_t::load_aligned(a1p.data() + i); + batch_t b1 = batch_t::load_aligned(b1p.data() + i); + + batch_t deltaL_2; + { + batch_t Ldiff = L1 - L2; + deltaL_2 = (Ldiff * Ldiff); + } + + batch_t C2_2 = ((a1 * a1) + (b1 * b1)); + + batch_t deltaCab_2; + { + batch_t temp = (sqrt_C1_2 - sqrt(C2_2)); + deltaCab_2 = (temp * temp); + } + + batch_t deltaHab_2; + { + batch_t a_diff = (a1 - a2); + batch_t b_diff = (b1 - b2); + + deltaHab_2 = ((a_diff * a_diff) + (b_diff * b_diff)); + deltaHab_2 = (deltaHab_2 - deltaCab_2); + } + + // constexpr float SL = 1; + // constexpr float kL = 1; + // constexpr float K1 = 0.045f; + constexpr float K2 = 0.015f; + + batch_t SH_2; + { + batch_t temp = ((sqrt(C2_2) * K2) + 1.0f); + SH_2 = (temp * temp); + } + + batch_t diff; + { + batch_t temp_C = (deltaCab_2 / SC_2); + batch_t temp_H = (deltaHab_2 / SH_2); + diff = (deltaL_2 + (temp_C + temp_H)); + } + + diff.store_aligned(dest.data() + i); + } + + { + const float L = c3[0]; + const float a = c3[1]; + const float b = c3[2]; + const float _C1_2 = a * a + b * b; + const float _SC_2 = + (sqrt(_C1_2) * 0.045f + 1.0f) * (sqrt(_C1_2) * 0.045f + 1.0f); + + for (size_t i = vec_size; i < color_count; i++) { + // auto deltaL_2 = (Allowed->lab(0) - L).square(); + + const float deltaL_2 = (l1p[i] - L) * (l1p[i] - L); + + const float C2_2 = a1p[i] * a1p[i] + b1p[i] * b1p[i]; + float deltaCab_2; + { + float temp = sqrt(_C1_2) - sqrt(C2_2); + deltaCab_2 = temp * temp; + } + + float deltaHab_2; + { + float diff_a = a1p[i] - a; + float diff_b = b1p[i] - b; + deltaHab_2 = diff_a * diff_a + diff_b * diff_b; + } + + float SH_2; + { + float temp = sqrt(C2_2) * 0.015f + 1.0f; + SH_2 = temp * temp; + } + // delete &SH_2; + dest[i] = deltaL_2 + deltaCab_2 / _SC_2 + deltaHab_2 / SH_2; + } + } } \ No newline at end of file diff --git a/utilities/ColorManip/ColorManip.h b/utilities/ColorManip/ColorManip.h index ee909e8a..2ba5ce30 100644 --- a/utilities/ColorManip/ColorManip.h +++ b/utilities/ColorManip/ColorManip.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,6 +24,8 @@ This file is part of SlopeCraft. #define COLORMANIP_COLORMANIP_H #include +#include +#include // #include using ARGB = uint32_t; @@ -71,4 +73,25 @@ float color_diff_RGB_plus(const float r1, const float g1, const float b1, float color_diff_HSV(float h1, float s1, float v1, float h2, float s2, float v2) noexcept; +void colordiff_RGB_batch(std::span r1, std::span g1, + std::span b1, + std::span rgb2, + std::span dest) noexcept; + +void colordiff_RGBplus_batch(std::span r1, + std::span g1, + std::span b1, + std::span rgb2, + std::span dest) noexcept; + +void colordiff_HSV_batch(std::span h1, std::span s1, + std::span v1, + std::span hsv2, + std::span dest) noexcept; + +void colordiff_Lab94_batch(std::span l1, std::span a1, + std::span b1, + std::span lab2, + std::span dest) noexcept; + #endif \ No newline at end of file diff --git a/utilities/ColorManip/StaticMembers.cpp b/utilities/ColorManip/StaticMembers.cpp deleted file mode 100644 index 1e8fd3e2..00000000 --- a/utilities/ColorManip/StaticMembers.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "newTokiColor.hpp" - -#include "colorset_maptical.hpp" -/* -std::array newTokiColorBase::DepthCount = {64, 64, 64, 64}; -bool newTokiColorBase::needFindSide = false; -*/ - -std::array newtokicolor_base_maptical::DepthCount = {64, 64, 64, - 64}; -bool newtokicolor_base_maptical::needFindSide = false; -/* -SCL_convertAlgo newTokiColorBase::convertAlgo = - SCL_convertAlgo::RGB_Better; - */ diff --git a/utilities/ColorManip/colorset_maptical.hpp b/utilities/ColorManip/colorset_maptical.hpp index 2d993c3c..f55f8ab8 100644 --- a/utilities/ColorManip/colorset_maptical.hpp +++ b/utilities/ColorManip/colorset_maptical.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ This file is part of SlopeCraft. #endif class newtokicolor_base_maptical { -public: + public: using TempVectorXf_t = Eigen::Array; @@ -48,59 +48,57 @@ class newtokicolor_base_maptical { // 记录与result的深度值不同的两个有损优化候选色(升序排列),Depth=3时无效 std::array sideResult; - uint8_t Result{0}; // 最终调色结果 + uint8_t Result{0}; // 最终调色结果 -public: - static bool needFindSide; - static std::array DepthCount; - // static ::SCL_convertAlgo convertAlgo; + public: + // static bool needFindSide; + // static std::array DepthCount; + // static ::SCL_convertAlgo convertAlgo; -public: + public: inline bool is_result_computed() const noexcept { return (Result != 0); } }; class alignas(32) colorset_maptical_basic { -private: - Eigen::Array __rgb; - Eigen::Array __hsv; - Eigen::Array __lab; - Eigen::Array __xyz; + private: + Eigen::Array rgb_table; + Eigen::Array hsv_table; + Eigen::Array lab_table; + Eigen::Array xyz_table; -public: + public: /// The default constructor is deleted colorset_maptical_basic() = delete; /// Construct from color source colorset_maptical_basic(const float *const rgbsrc) { - if (rgbsrc == nullptr) { - exit(1); - return; - } - memcpy(__rgb.data(), rgbsrc, sizeof(__rgb)); + assert(rgbsrc); + memcpy(rgb_table.data(), rgbsrc, sizeof(rgb_table)); for (int row = 0; row < 256; row++) { - const float r = __rgb(row, 0), g = __rgb(row, 1), b = __rgb(row, 2); - RGB2HSV(r, g, b, __hsv(row, 0), __hsv(row, 1), __hsv(row, 2)); - RGB2XYZ(r, g, b, __xyz(row, 0), __xyz(row, 1), __xyz(row, 2)); - XYZ2Lab(XYZ(row, 0), XYZ(row, 1), XYZ(row, 2), __lab(row, 0), - __lab(row, 1), __lab(row, 2)); + const float r = rgb_table(row, 0), g = rgb_table(row, 1), + b = rgb_table(row, 2); + RGB2HSV(r, g, b, hsv_table(row, 0), hsv_table(row, 1), hsv_table(row, 2)); + RGB2XYZ(r, g, b, xyz_table(row, 0), xyz_table(row, 1), xyz_table(row, 2)); + XYZ2Lab(XYZ(row, 0), XYZ(row, 1), XYZ(row, 2), lab_table(row, 0), + lab_table(row, 1), lab_table(row, 2)); } } /// get the color count inline constexpr int color_count() const noexcept { return 256; } - inline const auto &RGB_mat() const noexcept { return __rgb; } + inline const auto &RGB_mat() const noexcept { return rgb_table; } inline float RGB(const int r, const int c) const noexcept { - return __rgb(r, c); + return rgb_table(r, c); } inline float HSV(const int r, const int c) const noexcept { - return __hsv(r, c); + return hsv_table(r, c); } inline float Lab(const int r, const int c) const noexcept { - return __lab(r, c); + return lab_table(r, c); } inline float XYZ(const int r, const int c) const noexcept { - return __xyz(r, c); + return xyz_table(r, c); } static inline uint8_t Map(const int r) noexcept { @@ -117,86 +115,89 @@ class alignas(32) colorset_maptical_basic { }; class alignas(32) colorset_maptical_allowed { -public: + public: using color_col = Eigen::Array; static constexpr uint8_t invalid_color_id = 0; -private: - std::array __rgb; - std::array __hsv; - std::array __lab; - std::array __xyz; - Eigen::Array __map; + private: + std::array rgb_table; + std::array hsv_table; + std::array lab_table; + std::array xyz_table; + Eigen::Array map_table; // std::array __map; - int _color_count; + int color_count_{0}; + + std::array depth_counter; + + public: + bool need_find_side{false}; + [[nodiscard]] const std::array &depth_count() const noexcept { + return this->depth_counter; + } -public: - inline int color_count() const noexcept { return _color_count; } + inline int color_count() const noexcept { return color_count_; } inline float RGB(int r, int c) const noexcept { assert(r < color_count()); - return __rgb[c](r); + return rgb_table[c](r); } inline float HSV(int r, int c) const noexcept { assert(r < color_count()); - return __hsv[c](r); + return hsv_table[c](r); } inline float Lab(int r, int c) const noexcept { assert(r < color_count()); - return __lab[c](r); + return lab_table[c](r); } inline float XYZ(int r, int c) const noexcept { assert(r < color_count()); - return __xyz[c](r); + return xyz_table[c](r); } inline uint8_t Map(int r) const noexcept { assert(r < color_count()); - return __map[r]; + return map_table[r]; } inline auto rgb(int channel) const noexcept { - return __rgb[channel].head(_color_count); + return rgb_table[channel].head(color_count_); } inline auto hsv(int channel) const noexcept { - return __hsv[channel].head(_color_count); + return hsv_table[channel].head(color_count_); } inline auto lab(int channel) const noexcept { - return __lab[channel].head(_color_count); + return lab_table[channel].head(color_count_); } inline auto xyz(int channel) const noexcept { - return __xyz[channel].head(_color_count); + return xyz_table[channel].head(color_count_); } - inline auto map() const noexcept { return __map.head(_color_count); } + inline auto map() const noexcept { return map_table.head(color_count_); } inline const float *rgb_data(int channel) const noexcept { - return __rgb[channel].data(); + return rgb_table[channel].data(); } inline const float *hsv_data(int channel) const noexcept { - return __hsv[channel].data(); + return hsv_table[channel].data(); } inline const float *lab_data(int channel) const noexcept { - return __lab[channel].data(); + return lab_table[channel].data(); } inline const float *xyz_data(int channel) const noexcept { - return __xyz[channel].data(); + return xyz_table[channel].data(); } - inline const uint8_t *map_data() const noexcept { return __map.data(); } + inline const uint8_t *map_data() const noexcept { return map_table.data(); } bool apply_allowed(const colorset_maptical_basic &src, - const bool *const allow_list) noexcept { - if (allow_list == nullptr) { - return false; - } - + std::span allow_list) noexcept { const int new_color_count = allow_list_counter(allow_list); if (new_color_count <= 3) { @@ -204,14 +205,14 @@ class alignas(32) colorset_maptical_allowed { } for (int c = 0; c < 3; c++) { - __rgb[c].setZero(); - __hsv[c].setZero(); - __lab[c].setZero(); - __xyz[c].setZero(); + rgb_table[c].setZero(); + hsv_table[c].setZero(); + lab_table[c].setZero(); + xyz_table[c].setZero(); } - __map.setZero(); + map_table.setZero(); - _color_count = new_color_count; + color_count_ = new_color_count; for (int writeidx = 0, readidx = 0; readidx < 256; readidx++) { const int base = (readidx & 0b111111); @@ -221,31 +222,31 @@ class alignas(32) colorset_maptical_allowed { if (allow_list[readidx]) { for (int c = 0; c < 3; c++) { - __rgb[c](writeidx) = src.RGB(readidx, c); - __hsv[c](writeidx) = src.HSV(readidx, c); - __lab[c](writeidx) = src.Lab(readidx, c); - __xyz[c](writeidx) = src.XYZ(readidx, c); + rgb_table[c](writeidx) = src.RGB(readidx, c); + hsv_table[c](writeidx) = src.HSV(readidx, c); + lab_table[c](writeidx) = src.Lab(readidx, c); + xyz_table[c](writeidx) = src.XYZ(readidx, c); } - __map[writeidx] = src.Map(readidx); + map_table[writeidx] = src.Map(readidx); writeidx++; } } - - newtokicolor_base_maptical::DepthCount.fill(0); - for (int idx = 0; idx < this->color_count(); idx++) { + this->depth_counter.fill(0); + for (int idx = 0; idx < new_color_count; idx++) { const uint8_t mapcolor = this->Map(idx); const uint8_t base = mapcolor >> 2; if (base != 0) { const uint8_t depth = mapcolor & 0b11; - newtokicolor_base_maptical::DepthCount[depth]++; + this->depth_counter[depth]++; } } return true; } -private: - inline int allow_list_counter(const bool *const allow_list) const noexcept { + private: + inline int allow_list_counter( + std::span allow_list) const noexcept { int result = 0; for (int idx = 0; idx < 256; idx++) { const int base = (idx & 0b111111); @@ -258,4 +259,4 @@ class alignas(32) colorset_maptical_allowed { } }; -#endif // COLORMANIP_COLORSET_MAPTICAL_HPP \ No newline at end of file +#endif // COLORMANIP_COLORSET_MAPTICAL_HPP \ No newline at end of file diff --git a/utilities/ColorManip/colorset_optical.hpp b/utilities/ColorManip/colorset_optical.hpp index c6ac44cf..81a3a752 100644 --- a/utilities/ColorManip/colorset_optical.hpp +++ b/utilities/ColorManip/colorset_optical.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -29,53 +29,69 @@ This file is part of SlopeCraft. #include class alignas(32) colorset_optical_base { -protected: - std::array __rgb; - std::array __hsv; - std::array __lab; - std::array __xyz; + protected: + std::array rgb_table; + std::array hsv_table; + std::array lab_table; + std::array xyz_table; -private: - int _color_count; + private: + int color_count_; -public: - inline int color_count() const noexcept { return _color_count; } + public: + inline int color_count() const noexcept { return color_count_; } - inline float RGB(int r, int c) const noexcept { return __rgb[c](r); } - inline auto rgb(int c) const noexcept { return __rgb[c].head(_color_count); } - inline const float *rgb_data(int c) const noexcept { return __rgb[c].data(); } + inline float RGB(int r, int c) const noexcept { return rgb_table[c](r); } + inline auto rgb(int c) const noexcept { + return rgb_table[c].head(color_count_); + } + inline const float *rgb_data(int c) const noexcept { + return rgb_table[c].data(); + } - inline float HSV(int r, int c) const noexcept { return __hsv[c](r); } - inline auto hsv(int c) const noexcept { return __hsv[c].head(_color_count); } - inline const float *hsv_data(int c) const noexcept { return __hsv[c].data(); } + inline float HSV(int r, int c) const noexcept { return hsv_table[c](r); } + inline auto hsv(int c) const noexcept { + return hsv_table[c].head(color_count_); + } + inline const float *hsv_data(int c) const noexcept { + return hsv_table[c].data(); + } - inline float Lab(int r, int c) const noexcept { return __lab[c](r); } - inline auto lab(int c) const noexcept { return __lab[c].head(_color_count); } - inline const float *lab_data(int c) const noexcept { return __lab[c].data(); } + inline float Lab(int r, int c) const noexcept { return lab_table[c](r); } + inline auto lab(int c) const noexcept { + return lab_table[c].head(color_count_); + } + inline const float *lab_data(int c) const noexcept { + return lab_table[c].data(); + } - inline float XYZ(int r, int c) const noexcept { return __xyz[c](r); } - inline auto xyz(int c) const noexcept { return __xyz[c].head(_color_count); } - inline const float *xyz_data(int c) const noexcept { return __xyz[c].data(); } + inline float XYZ(int r, int c) const noexcept { return xyz_table[c](r); } + inline auto xyz(int c) const noexcept { + return xyz_table[c].head(color_count_); + } + inline const float *xyz_data(int c) const noexcept { + return xyz_table[c].data(); + } -protected: + protected: void resize(int new_color_count) noexcept { if (new_color_count < 0) { new_color_count = 0; } for (int c = 0; c < 3; c++) { - __rgb[c].setZero(new_color_count); - __hsv[c].setZero(new_color_count); - __lab[c].setZero(new_color_count); - __xyz[c].setZero(new_color_count); + rgb_table[c].setZero(new_color_count); + hsv_table[c].setZero(new_color_count); + lab_table[c].setZero(new_color_count); + xyz_table[c].setZero(new_color_count); } - this->_color_count = new_color_count; + this->color_count_ = new_color_count; } }; class colorset_optical_basic : public colorset_optical_base { -public: + public: /// in the basic colorset, id=idx; inline uint16_t color_id(uint16_t idx) const noexcept { return idx; } colorset_optical_basic() { this->resize(0); } @@ -103,22 +119,23 @@ class colorset_optical_basic : public colorset_optical_base { rbgsrc_colmajor, new_color_count, 3); for (int c = 0; c < 3; c++) { - memcpy(this->__rgb[c].data(), rbgsrc_colmajor + (new_color_count * c), + memcpy(this->rgb_table[c].data(), + rbgsrc_colmajor + (new_color_count * c), sizeof(float) * new_color_count); //= rgbsrcmap.col(c); } } for (int coloridx = 0; coloridx < new_color_count; coloridx++) { - RGB2HSV(this->__rgb[0](coloridx), this->__rgb[1](coloridx), - this->__rgb[2](coloridx), this->__hsv[0](coloridx), - this->__hsv[1](coloridx), this->__hsv[2](coloridx)); - RGB2XYZ(this->__rgb[0](coloridx), this->__rgb[1](coloridx), - this->__rgb[2](coloridx), this->__xyz[0](coloridx), - this->__xyz[1](coloridx), this->__xyz[2](coloridx)); - XYZ2Lab(this->__xyz[0](coloridx), this->__xyz[1](coloridx), - this->__xyz[2](coloridx), this->__lab[0](coloridx), - this->__lab[1](coloridx), this->__lab[2](coloridx)); + RGB2HSV(this->rgb_table[0](coloridx), this->rgb_table[1](coloridx), + this->rgb_table[2](coloridx), this->hsv_table[0](coloridx), + this->hsv_table[1](coloridx), this->hsv_table[2](coloridx)); + RGB2XYZ(this->rgb_table[0](coloridx), this->rgb_table[1](coloridx), + this->rgb_table[2](coloridx), this->xyz_table[0](coloridx), + this->xyz_table[1](coloridx), this->xyz_table[2](coloridx)); + XYZ2Lab(this->xyz_table[0](coloridx), this->xyz_table[1](coloridx), + this->xyz_table[2](coloridx), this->lab_table[0](coloridx), + this->lab_table[1](coloridx), this->lab_table[2](coloridx)); } return true; @@ -132,26 +149,26 @@ class colorset_optical_basic : public colorset_optical_base { return color_id; } - inline uint16_t - colorindex_of_colorid(const uint16_t color_id) const noexcept { + inline uint16_t colorindex_of_colorid( + const uint16_t color_id) const noexcept { return this->colorindex_of_id(color_id); } }; class colorset_optical_allowed : public colorset_optical_base { -public: + public: static constexpr uint16_t invalid_color_id = 0xFFFF; -private: - Eigen::Array __color_id; + private: + Eigen::Array color_id_; void resize(int new_color_count) { colorset_optical_base::resize(new_color_count); - __color_id.resize(new_color_count); - __color_id.fill(invalid_color_id); + color_id_.resize(new_color_count); + color_id_.fill(invalid_color_id); } -public: + public: colorset_optical_allowed() { this->resize(0); } bool apply_allowed(const colorset_optical_basic &src, @@ -179,12 +196,12 @@ class colorset_optical_allowed : public colorset_optical_base { } for (int c = 0; c < 3; c++) { - this->__rgb[c][writeidx] = src.RGB(readidx, c); - this->__hsv[c][writeidx] = src.HSV(readidx, c); - this->__lab[c][writeidx] = src.Lab(readidx, c); - this->__xyz[c][writeidx] = src.XYZ(readidx, c); + this->rgb_table[c][writeidx] = src.RGB(readidx, c); + this->hsv_table[c][writeidx] = src.HSV(readidx, c); + this->lab_table[c][writeidx] = src.Lab(readidx, c); + this->xyz_table[c][writeidx] = src.XYZ(readidx, c); } - __color_id[writeidx] = src.color_id(readidx); + color_id_[writeidx] = src.color_id(readidx); writeidx++; } @@ -193,17 +210,17 @@ class colorset_optical_allowed : public colorset_optical_base { } inline uint16_t color_id(uint16_t idx) const noexcept { - assert(idx < __color_id.size()); - return __color_id[idx]; + assert(idx < color_id_.size()); + return color_id_[idx]; } }; class TempVecOptical : public Eigen::Map { -public: + public: static constexpr size_t capacity = 65536; using Base_t = Eigen::Map; -public: + public: TempVecOptical(int rows, int cols) : Base_t((float *)SC_aligned_alloc(64, capacity * sizeof(float)), rows, cols) { @@ -229,18 +246,19 @@ class TempVecOptical : public Eigen::Map { }; class newtokicolor_base_optical { -public: - using TempVectorXf_t = TempVecOptical; + public: + // using TempVectorXf_t = TempVecOptical; + using TempVectorXf_t = Eigen::ArrayXf; using result_t = uint16_t; -protected: + protected: result_t result_color_id{ - colorset_optical_allowed::invalid_color_id}; // the final color index -public: + colorset_optical_allowed::invalid_color_id}; // the final color index + public: inline result_t color_id() const noexcept { return result_color_id; } inline bool is_result_computed() const noexcept { return (result_color_id != colorset_optical_allowed::invalid_color_id); } }; -#endif // COLORMANIP_COLORSET_OPTICAL_HPP \ No newline at end of file +#endif // COLORMANIP_COLORSET_OPTICAL_HPP \ No newline at end of file diff --git a/utilities/ColorManip/hash.cpp b/utilities/ColorManip/hash.cpp new file mode 100644 index 00000000..9d3d0fd0 --- /dev/null +++ b/utilities/ColorManip/hash.cpp @@ -0,0 +1,47 @@ +#include "newColorSet.hpp" +#include "imageConvert.hpp" +#include +#include + +namespace buuid = boost::uuids::detail; + +/* +std::vector internal::hash_of_colorset( + const hash_temp &temp) noexcept { + Chocobo1::SHA3_512 stream; + + for (const auto &cptrs : temp.color_ptrs) { + for (const float *fptr : cptrs) { + stream.addData(fptr, temp.color_count * sizeof(float)); + } + } + + stream.addData(temp.color_id_ptr, + temp.color_count * + (temp.is_maptical ? sizeof(uint8_t) : sizeof(uint16_t))); + stream.addData(&temp.color_count, sizeof(temp.color_count)); + + return stream.finalize().toVector(); +} + +*/ + +std::vector libImageCvt::hash_of_image( + Eigen::Map> img) noexcept { + buuid::sha1 hash; + { + const auto rows{img.rows()}; + const auto cols{img.cols()}; + hash.process_bytes(&rows, sizeof(rows)); + hash.process_bytes(&cols, sizeof(cols)); + } + + hash.process_bytes(img.data(), sizeof(uint32_t) * img.size()); + buuid::sha1::digest_type dig; + hash.get_digest(dig); + + std::span temp{reinterpret_cast(dig), + sizeof(dig)}; + + return {temp.begin(), temp.end()}; +} \ No newline at end of file diff --git a/utilities/ColorManip/imageConvert.hpp b/utilities/ColorManip/imageConvert.hpp index 05f5506f..741b39da 100644 --- a/utilities/ColorManip/imageConvert.hpp +++ b/utilities/ColorManip/imageConvert.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -33,6 +33,7 @@ This file is part of SlopeCraft. #include #include #include +#include #include "../SC_GlobalEnums.h" #include "ColorManip.h" @@ -41,30 +42,31 @@ This file is part of SlopeCraft. #ifdef RGB #undef RGB -#endif // #ifdef RGB +#endif // #ifdef RGB namespace libImageCvt { using ::Eigen::Dynamic; -static const Eigen::Array - dithermap_LR({{0.0 / 16.0, 0.0 / 16.0, 7.0 / 16.0}, - {3.0 / 16.0, 5.0 / 16.0, 1.0 / 16.0}}); +static const Eigen::Array dithermap_LR( + {{0.0 / 16.0, 0.0 / 16.0, 7.0 / 16.0}, + {3.0 / 16.0, 5.0 / 16.0, 1.0 / 16.0}}); -static const Eigen::Array - dithermap_RL({{7.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0}, - {1.0 / 16.0, 5.0 / 16.0, 3.0 / 16.0}}); +static const Eigen::Array dithermap_RL( + {{7.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0}, + {1.0 / 16.0, 5.0 / 16.0, 3.0 / 16.0}}); -template struct GPU_wrapper_wrapper { +template +struct GPU_wrapper_wrapper { constexpr bool have_gpu_resource() const noexcept { return false; } }; -template <> struct GPU_wrapper_wrapper { - -protected: +template <> +struct GPU_wrapper_wrapper { + protected: gpu_wrapper::gpu_interface *gpu{nullptr}; -public: + public: bool have_gpu_resource() const noexcept { return this->gpu != nullptr; } void set_gpu_resource(gpu_wrapper::gpu_interface *gi) noexcept { @@ -81,7 +83,7 @@ template <> struct GPU_wrapper_wrapper { template class ImageCvter : public GPU_wrapper_wrapper { -public: + public: using basic_colorset_t = colorset_new; using allowed_colorset_t = colorset_new; using TokiColor_t = @@ -90,46 +92,56 @@ class ImageCvter : public GPU_wrapper_wrapper { using coloridx_t = colorid_t; // These static member must be implemented by caller - static const basic_colorset_t &basic_colorset; - static const allowed_colorset_t &allowed_colorset; + // static const basic_colorset_t &basic_colorset; + // static const allowed_colorset_t &allowed_colorset; + + ImageCvter(const basic_colorset_t &basic, const allowed_colorset_t &allowed) + : basic_colorset{basic}, allowed_colorset{allowed} {} + + ImageCvter(ImageCvter &&) = default; -protected: - Eigen::ArrayXX _raw_image; + protected: + const basic_colorset_t &basic_colorset; + const allowed_colorset_t &allowed_colorset; + Eigen::ArrayXX raw_image_; ::SCL_convertAlgo algo; - std::unordered_map _color_hash; + bool dither{false}; + std::unordered_map color_hash_; - Eigen::ArrayXX _dithered_image; + Eigen::ArrayXX dithered_image_; // Eigen::ArrayXX colorid_matrix; -public: + public: uiPack ui; // SCL_convertAlgo convert_algo{SCL_convertAlgo::RGB_Better}; inline void clear_images() noexcept { - this->_raw_image.resize(0, 0); - this->_dithered_image.resize(0, 0); + this->raw_image_.resize(0, 0); + this->dithered_image_.resize(0, 0); } /// When the colorset is changed, the hash must be cleared. inline void on_color_set_changed() noexcept { this->clear_color_hash(); } /// Call this function when the color set is changed. - inline void clear_color_hash() noexcept { this->_color_hash.clear(); } + inline void clear_color_hash() noexcept { this->color_hash_.clear(); } inline ::SCL_convertAlgo convert_algo() const noexcept { return this->algo; } - inline int64_t rows() const noexcept { return _raw_image.rows(); } - inline int64_t cols() const noexcept { return _raw_image.cols(); } - inline int64_t size() const noexcept { return _raw_image.size(); } + inline bool is_dither() const noexcept { return this->dither; } - inline const auto &raw_image() const noexcept { return _raw_image; } + inline int64_t rows() const noexcept { return raw_image_.rows(); } + inline int64_t cols() const noexcept { return raw_image_.cols(); } + inline int64_t size() const noexcept { return raw_image_.size(); } - inline const auto &color_hash() const noexcept { return _color_hash; } + inline const auto &raw_image() const noexcept { return raw_image_; } - void set_raw_image(const ARGB *const data, const int64_t _rows, - const int64_t _cols, + inline const auto &color_hash() const noexcept { return color_hash_; } + + void set_raw_image(const ARGB *const data, const int64_t n_rows, + const int64_t n_cols, const bool is_col_major = true) noexcept { - if (_rows <= 0 || _cols <= 0) { + if (n_rows <= 0 || n_cols <= 0) { return; } if (data == nullptr) { @@ -138,24 +150,31 @@ class ImageCvter : public GPU_wrapper_wrapper { if (is_col_major) { Eigen::Map> - map(data, _rows, _cols); - this->_raw_image = map; + map(data, n_rows, n_cols); + this->raw_image_ = map; } else { Eigen::Map> - map(data, _rows, _cols); - this->_raw_image = map; + map(data, n_rows, n_cols); + this->raw_image_ = map; } + // filter full-transparent colors + // for (ARGB &color : this->raw_image_.) { + // if (getA(color) <= 0) { + // color = ARGB32(0, 0, 0, 0); + // } + // } } - bool convert_image(::SCL_convertAlgo __algo, bool dither, + bool convert_image(::SCL_convertAlgo algo_, bool dither_, bool try_gpu = false) noexcept { - if (__algo == ::SCL_convertAlgo::gaCvter) { - __algo = ::SCL_convertAlgo::RGB_Better; + if (algo_ == ::SCL_convertAlgo::gaCvter) { + algo_ = ::SCL_convertAlgo::RGB_Better; } + this->dither = dither_; ui.rangeSet(0, 100, 0); - this->algo = __algo; + this->algo = algo_; this->add_colors_to_hash(); ui.rangeSet(0, 100, 25); if (!this->match_all_TokiColors(try_gpu)) { @@ -163,34 +182,54 @@ class ImageCvter : public GPU_wrapper_wrapper { } ui.rangeSet(0, 100, 50); - if (dither) { - switch (this->algo) { - case ::SCL_convertAlgo::RGB: - this->template __impl_dither<::SCL_convertAlgo::RGB>(); - break; - case ::SCL_convertAlgo::RGB_Better: - this->template __impl_dither<::SCL_convertAlgo::RGB_Better>(); - break; - case ::SCL_convertAlgo::HSV: - this->template __impl_dither<::SCL_convertAlgo::HSV>(); - break; - case ::SCL_convertAlgo::Lab94: - this->template __impl_dither<::SCL_convertAlgo::Lab94>(); - break; - case ::SCL_convertAlgo::Lab00: - this->template __impl_dither<::SCL_convertAlgo::Lab00>(); - break; - case ::SCL_convertAlgo::XYZ: - this->template __impl_dither<::SCL_convertAlgo::XYZ>(); - break; + // handle possible full-transparent pixel + { + convert_unit cu{ARGB32(0, 0, 0, 0), this->algo}; + TokiColor_t tk; + tk.compute(cu, this->allowed_colorset); + this->color_hash_.emplace(cu, tk); + } - default: - abort(); - return false; + if (this->dither) { + switch (this->algo) { + case ::SCL_convertAlgo::RGB: + this->template impl_dither__<::SCL_convertAlgo::RGB>(); + break; + case ::SCL_convertAlgo::RGB_Better: + this->template impl_dither__<::SCL_convertAlgo::RGB_Better>(); + break; + case ::SCL_convertAlgo::HSV: + this->template impl_dither__<::SCL_convertAlgo::HSV>(); + break; + case ::SCL_convertAlgo::Lab94: + this->template impl_dither__<::SCL_convertAlgo::Lab94>(); + break; + case ::SCL_convertAlgo::Lab00: + this->template impl_dither__<::SCL_convertAlgo::Lab00>(); + break; + case ::SCL_convertAlgo::XYZ: + this->template impl_dither__<::SCL_convertAlgo::XYZ>(); + break; + + default: + abort(); + return false; } } else { - this->_dithered_image = this->_raw_image; + this->dithered_image_ = this->raw_image_; } + + // for (int64_t idx = 0; idx < this->_dithered_image.size(); idx++) { + // const auto current_color{this->_dithered_image(idx)}; + // if (getA(current_color) > 0) { + // continue; + // } + // const convert_unit key{current_color, this->algo}; + // if (!this->_color_hash.contains(key)) { + // this->_color_hash.emplace(key, uint8_t{0}); + // } + // } + ui.rangeSet(0, 100, 100); return true; // fill_coloridmat_by_hash(this->colorid_matrix); @@ -201,10 +240,16 @@ class ImageCvter : public GPU_wrapper_wrapper { result.setZero(this->rows(), this->cols()); for (int64_t idx = 0; idx < this->size(); idx++) { - auto it = this->_color_hash.find( - convert_unit(this->_dithered_image(idx), this->algo)); + const auto current_color = this->dithered_image_(idx); + + auto it = this->color_hash_.find(convert_unit(current_color, this->algo)); + + if (it == this->color_hash_.end()) { + if (getA(current_color) <= 0) { + result(idx) = 0; + continue; + } - if (it == this->_color_hash.end()) { abort(); } @@ -221,10 +266,10 @@ class ImageCvter : public GPU_wrapper_wrapper { // sizeof(uint16_t)); for (int64_t idx = 0; idx < this->size(); idx++) { - auto it = this->_color_hash.find( - convert_unit(this->_dithered_image(idx), this->algo)); + auto it = this->color_hash_.find( + convert_unit(this->dithered_image_(idx), this->algo)); - if (it == this->_color_hash.end()) { + if (it == this->color_hash_.end()) { abort(); } @@ -236,10 +281,14 @@ class ImageCvter : public GPU_wrapper_wrapper { assert(r >= 0 && r < this->rows()); assert(c >= 0 && c < this->cols()); - auto it = this->_color_hash.find( - convert_unit{this->_dithered_image(r, c), this->algo}); - if (it == this->_color_hash.end()) { - abort(); + const auto current_color = this->dithered_image_(r, c); + auto it = this->color_hash_.find(convert_unit{current_color, this->algo}); + if (it == this->color_hash_.end()) { + if (getA(current_color) > 0) { + // logical impossible + abort(); + } + return 0; } return it->second.color_id(); } @@ -250,10 +299,10 @@ class ImageCvter : public GPU_wrapper_wrapper { converted_image(dest.data()); } - inline void - converted_image(ARGB *const data_dest, int64_t *const rows_dest = nullptr, - int64_t *const cols_dest = nullptr, - const bool is_dest_col_major = true) const noexcept { + inline void converted_image( + ARGB *const data_dest, int64_t *const rows_dest = nullptr, + int64_t *const cols_dest = nullptr, + const bool is_dest_col_major = true) const noexcept { if (rows_dest != nullptr) { *rows_dest = this->rows(); } @@ -262,44 +311,56 @@ class ImageCvter : public GPU_wrapper_wrapper { } if (data_dest != nullptr) { + Eigen::Map< + Eigen::Array> + dest{data_dest, rows(), cols()}; for (int64_t r = 0; r < rows(); r++) { for (int64_t c = 0; c < cols(); c++) { - const int64_t idx = - (is_dest_col_major) ? (c * rows() + r) : (r * cols() + c); - const ARGB argb = this->_dithered_image(r, c); - auto it = this->_color_hash.find(convert_unit(argb, this->algo)); - if (it == this->_color_hash.end()) { + // const int64_t idx = + // (is_dest_col_major) ? (r * cols() + c) : (c * rows() + + // r); + ARGB argb = this->dithered_image_(r, c); + // process full-transparent image + if (::getA(argb) <= 0) { + argb = ARGB32(0, 0, 0, 0); + } + auto it = this->color_hash_.find(convert_unit{argb, this->algo}); + if (it == this->color_hash_.end()) { + // logical impossible abort(); - return; } const auto color_id = it->second.color_id(); const auto color_index = basic_colorset.colorindex_of_colorid(color_id); + ARGB color; if (color_index != allowed_colorset_t::invalid_color_id) { - data_dest[idx] = RGB2ARGB(basic_colorset.RGB(color_index, 0), - basic_colorset.RGB(color_index, 1), - basic_colorset.RGB(color_index, 2)); + color = RGB2ARGB(basic_colorset.RGB(color_index, 0), + basic_colorset.RGB(color_index, 1), + basic_colorset.RGB(color_index, 2)); } else { - data_dest[idx] = 0x00'00'00'00; + color = 0x00'00'00'00; } + dest(r, c) = color; } } + if (is_dest_col_major) { + dest.transposeInPlace(); + } } } -private: + private: void add_colors_to_hash() noexcept { // this->_color_hash.clear(); - for (int64_t idx = 0; idx < this->_raw_image.size(); idx++) { - const ARGB argb = this->_raw_image(idx); + for (int64_t idx = 0; idx < this->raw_image_.size(); idx++) { + const ARGB argb = this->raw_image_(idx); convert_unit cu(argb, this->algo); - auto it = _color_hash.find(cu); + auto it = color_hash_.find(cu); // this key isn't inserted - if (it == _color_hash.end()) - this->_color_hash.emplace(cu, TokiColor_t()); + if (it == color_hash_.end()) this->color_hash_.emplace(cu, TokiColor_t()); } } @@ -308,7 +369,6 @@ class ImageCvter : public GPU_wrapper_wrapper { this->match_all_TokiColors_cpu(); return true; } else { - if constexpr (gpu_wrapper::have_api) { // If converter have gpu resources, compute by gpu if (try_gpu && this->have_gpu_resource()) { @@ -327,18 +387,18 @@ class ImageCvter : public GPU_wrapper_wrapper { // const int threadCount = omp_get_num_threads(); std::vector *> tasks; - tasks.reserve(_color_hash.size()); + tasks.reserve(color_hash_.size()); tasks.clear(); - for (auto &pair : _color_hash) { - if (!pair.second.is_result_computed()) - tasks.emplace_back(&pair); + for (auto &pair : color_hash_) { + if (!pair.second.is_result_computed()) tasks.emplace_back(&pair); } const size_t taskCount = tasks.size(); #pragma omp parallel for schedule(dynamic) for (int taskIdx = 0; taskIdx < (int)taskCount; taskIdx++) { - tasks[taskIdx]->second.compute(tasks[taskIdx]->first); + tasks[taskIdx]->second.compute(tasks[taskIdx]->first, + this->allowed_colorset); } // #warning we should parallelize here /* @@ -359,8 +419,8 @@ class ImageCvter : public GPU_wrapper_wrapper { #pragma omp parallel for for (int64_t c = 0; c < this->cols(); c++) { for (int64_t r = 0; r < this->rows(); r++) { - auto it = this->_color_hash.find(this->_raw_image(r, c)); - if (it == this->_color_hash.end()) { + auto it = this->color_hash_.find(this->raw_image_(r, c)); + if (it == this->color_hash_.end()) { abort(); } @@ -373,7 +433,8 @@ class ImageCvter : public GPU_wrapper_wrapper { } } - template bool match_all_TokiColors_gpu() noexcept { + template + bool match_all_TokiColors_gpu() noexcept { static_assert(!is_not_optical, "GPU boosting is only avaliable for VisualCraftL."); // static_assert(gpu_wrapper::have_api, "No avaliable GPU api."); @@ -384,13 +445,13 @@ class ImageCvter : public GPU_wrapper_wrapper { } std::vector *> tasks; - tasks.reserve(_color_hash.size()); + tasks.reserve(color_hash_.size()); tasks.clear(); - for (auto &pair : this->_color_hash) { + for (auto &pair : this->color_hash_) { if (!pair.second.is_result_computed()) { - if ((pair.first._ARGB & 0xFF'00'00'00) == 0) { - pair.second.compute(pair.first); + if ((pair.first.ARGB_ & 0xFF'00'00'00) == 0) { + pair.second.compute(pair.first, this->allowed_colorset); } else { tasks.emplace_back(&pair); } @@ -412,7 +473,6 @@ class ImageCvter : public GPU_wrapper_wrapper { if (gpu_task_count > 0) { std::vector> task_colors(gpu_task_count); for (size_t tid = 0; tid < gpu_task_count; tid++) { - if (tasks[tid]->first.algo != algo) { return false; } @@ -434,35 +494,35 @@ class ImageCvter : public GPU_wrapper_wrapper { std::array colorset_ptrs{nullptr, nullptr, nullptr}; switch (algo) { - case SCL_convertAlgo::RGB: - case SCL_convertAlgo::RGB_Better: - colorset_ptrs = {TokiColor_t::Allowed->rgb_data(0), - TokiColor_t::Allowed->rgb_data(1), - TokiColor_t::Allowed->rgb_data(2)}; - break; - case SCL_convertAlgo::HSV: - colorset_ptrs = {TokiColor_t::Allowed->hsv_data(0), - TokiColor_t::Allowed->hsv_data(1), - TokiColor_t::Allowed->hsv_data(2)}; - break; - case SCL_convertAlgo::Lab94: - case SCL_convertAlgo::Lab00: - colorset_ptrs = {TokiColor_t::Allowed->lab_data(0), - TokiColor_t::Allowed->lab_data(1), - TokiColor_t::Allowed->lab_data(2)}; - break; - case SCL_convertAlgo::XYZ: - colorset_ptrs = {TokiColor_t::Allowed->xyz_data(0), - TokiColor_t::Allowed->xyz_data(1), - TokiColor_t::Allowed->xyz_data(2)}; - break; - default: - abort(); + case SCL_convertAlgo::RGB: + case SCL_convertAlgo::RGB_Better: + colorset_ptrs = {this->allowed_colorset.rgb_data(0), + this->allowed_colorset.rgb_data(1), + this->allowed_colorset.rgb_data(2)}; + break; + case SCL_convertAlgo::HSV: + colorset_ptrs = {this->allowed_colorset.hsv_data(0), + this->allowed_colorset.hsv_data(1), + this->allowed_colorset.hsv_data(2)}; + break; + case SCL_convertAlgo::Lab94: + case SCL_convertAlgo::Lab00: + colorset_ptrs = {this->allowed_colorset.lab_data(0), + this->allowed_colorset.lab_data(1), + this->allowed_colorset.lab_data(2)}; + break; + case SCL_convertAlgo::XYZ: + colorset_ptrs = {this->allowed_colorset.xyz_data(0), + this->allowed_colorset.xyz_data(1), + this->allowed_colorset.xyz_data(2)}; + break; + default: + abort(); } if (gpu_task_count > 0) { // set colorset for ocl - this->gpu->set_colorset_v(TokiColor_t::Allowed->color_count(), + this->gpu->set_colorset_v(this->allowed_colorset.color_count(), colorset_ptrs); if (!this->gpu->ok_v()) { return false; @@ -476,7 +536,7 @@ class ImageCvter : public GPU_wrapper_wrapper { // compute rest tasks on cpu for (uint64_t ctid = 0; ctid < cpu_task_count; ctid++) { const uint64_t tid = gpu_task_count + ctid; - tasks[tid]->second.compute(tasks[tid]->first); + tasks[tid]->second.compute(tasks[tid]->first, this->allowed_colorset); } if (gpu_task_count > 0) { @@ -491,11 +551,11 @@ class ImageCvter : public GPU_wrapper_wrapper { TokiColor_t &tc = tasks[tid]->second; const uint16_t tempidx = this->gpu->result_idx_v()[tid]; - if (tempidx >= TokiColor_t::Allowed->color_count()) { + if (tempidx >= this->allowed_colorset.color_count()) { abort(); } - tc.set_gpu_result(TokiColor_t::Allowed->color_id(tempidx), + tc.set_gpu_result(this->allowed_colorset.color_id(tempidx), this->gpu->result_diff_v()[tid]); } @@ -505,40 +565,39 @@ class ImageCvter : public GPU_wrapper_wrapper { template inline static ARGB ColorCvt(float c0, float c1, float c2) noexcept { switch (algo) { - case ::SCL_convertAlgo::RGB: - case ::SCL_convertAlgo::RGB_Better: - case ::SCL_convertAlgo::gaCvter: - return RGB2ARGB(c0, c1, c2); - case ::SCL_convertAlgo::HSV: - return HSV2ARGB(c0, c1, c2); - case ::SCL_convertAlgo::Lab00: - case ::SCL_convertAlgo::Lab94: - return Lab2ARGB(c0, c1, c2); - case ::SCL_convertAlgo::XYZ: - return XYZ2ARGB(c0, c1, c2); + case ::SCL_convertAlgo::RGB: + case ::SCL_convertAlgo::RGB_Better: + case ::SCL_convertAlgo::gaCvter: + return RGB2ARGB(c0, c1, c2); + case ::SCL_convertAlgo::HSV: + return HSV2ARGB(c0, c1, c2); + case ::SCL_convertAlgo::Lab00: + case ::SCL_convertAlgo::Lab94: + return Lab2ARGB(c0, c1, c2); + case ::SCL_convertAlgo::XYZ: + return XYZ2ARGB(c0, c1, c2); } // unreachable abort(); - return 0; } - template void __impl_dither() noexcept { + template + void impl_dither__() noexcept { std::array dither_c3; for (auto &i : dither_c3) { i.setZero(this->rows() + 2, this->cols() + 2); } // dest.setZero(this->rows(), this->cols()); - this->_dithered_image.setZero(this->rows(), this->cols()); + this->dithered_image_.setZero(this->rows(), this->cols()); for (int64_t r = 0; r < this->rows(); r++) { for (int64_t c = 0; c < this->cols(); c++) { - auto it = this->_color_hash.find( - convert_unit(this->_raw_image(r, c), this->algo)); - if (it == this->_color_hash.end()) { + auto it = this->color_hash_.find( + convert_unit(this->raw_image_(r, c), this->algo)); + if (it == this->color_hash_.end()) { // unreachable abort(); - return; } for (int ch = 0; ch < 3; ch++) { dither_c3[ch](r + 1, c + 1) = it->first.to_c3()[ch]; @@ -546,12 +605,24 @@ class ImageCvter : public GPU_wrapper_wrapper { } } + // auto handle_full_transparent_pixel = [this](ARGB color) { + // convert_unit cu{color, this->algo}; + // if (this->color_hash_.contains(cu)) { + // return; + // } + // TokiColor_t tk; + // tk.compute(cu, this->allowed_colorset); + // this->color_hash_.emplace(cu, tk); + // }; + // int64_t inserted_count = 0; bool is_dir_LR = true; for (int64_t row = 0; row < this->rows(); row++) { if (is_dir_LR) for (int64_t col = 0; col < this->cols(); col++) { - if (::getA(this->_raw_image(row, col) <= 0)) { + if (::getA(this->raw_image_(row, col)) <= 0) { + // found full-transparent pixel + // handle_full_transparent_pixel(this->raw_image_(row, col)); continue; } @@ -559,15 +630,15 @@ class ImageCvter : public GPU_wrapper_wrapper { dither_c3[0](row + 1, col + 1), dither_c3[1](row + 1, col + 1), dither_c3[2](row + 1, col + 1)); // ditheredImage(r, c) = Current; - this->_dithered_image(row, col) = current_argb; + this->dithered_image_(row, col) = current_argb; auto it_to_old_color = - this->_color_hash.find(convert_unit(current_argb, this->algo)); + this->color_hash_.find(convert_unit(current_argb, this->algo)); // if this color isn't matched, match it. - if (it_to_old_color == this->_color_hash.end()) { + if (it_to_old_color == this->color_hash_.end()) { convert_unit cu(current_argb, this->algo); - auto ret = this->_color_hash.emplace(cu, TokiColor_t()); + auto ret = this->color_hash_.emplace(cu, TokiColor_t()); it_to_old_color = ret.first; - it_to_old_color->second.compute(cu); + it_to_old_color->second.compute(cu, this->allowed_colorset); // inserted_count++; } @@ -592,21 +663,22 @@ class ImageCvter : public GPU_wrapper_wrapper { } else for (int64_t col = this->cols() - 1; col >= 0; col--) { - if (::getA(this->_raw_image(row, col) <= 0)) { + if (::getA(this->raw_image_(row, col)) <= 0) { + // handle_full_transparent_pixel(this->raw_image_(row, col)); continue; } const ARGB current_argb = ColorCvt( dither_c3[0](row + 1, col + 1), dither_c3[1](row + 1, col + 1), dither_c3[2](row + 1, col + 1)); - this->_dithered_image(row, col) = current_argb; + this->dithered_image_(row, col) = current_argb; convert_unit cu(current_argb, this->algo); - auto it_to_old_color = this->_color_hash.find(cu); + auto it_to_old_color = this->color_hash_.find(cu); // if this color isn't matched, match it. - if (it_to_old_color == this->_color_hash.end()) { - auto ret = this->_color_hash.emplace(cu, TokiColor_t()); + if (it_to_old_color == this->color_hash_.end()) { + auto ret = this->color_hash_.emplace(cu, TokiColor_t()); it_to_old_color = ret.first; - it_to_old_color->second.compute(cu); + it_to_old_color->second.compute(cu, this->allowed_colorset); // inserted_count++; } @@ -636,8 +708,25 @@ class ImageCvter : public GPU_wrapper_wrapper { } return; } + + public: + [[deprecated]] uint64_t task_hash() const noexcept { + return this->task_hash(this->algo, this->dither); + } + + uint64_t task_hash(SCL_convertAlgo a, bool d) const noexcept { + const auto &img = this->raw_image_; + return std::hash()( + + std::string_view{(const char *)img.data(), + img.size() * sizeof(uint32_t)}) ^ + std::hash()((char)a) ^ std::hash()(d); + } }; -} // namespace libImageCvt +std::vector hash_of_image( + Eigen::Map> img) noexcept; + +} // namespace libImageCvt -#endif // COLORMANIP_IMAGECONVERT_HPP +#endif // COLORMANIP_IMAGECONVERT_HPP diff --git a/utilities/ColorManip/newColorSet.hpp b/utilities/ColorManip/newColorSet.hpp index f326ae4b..48c2704c 100644 --- a/utilities/ColorManip/newColorSet.hpp +++ b/utilities/ColorManip/newColorSet.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -28,11 +28,24 @@ This file is part of SlopeCraft. #include #include #include - +#include #include // using Eigen::Dynamic; +namespace internal { + +struct hash_temp { + std::array, 4> color_ptrs; + const void *color_id_ptr; + int color_count; + bool is_maptical; +}; + +// std::vector hash_of_colorset(const hash_temp &) noexcept; + +} // namespace internal + template class colorset_new : public std::conditional_t< is_not_optical, @@ -40,7 +53,7 @@ class colorset_new : public std::conditional_t< colorset_maptical_allowed>, std::conditional_t> { -public: + public: template colorset_new(const float *const src) : colorset_maptical_basic(src) { static_assert(is_basic, @@ -54,21 +67,80 @@ class colorset_new : public std::conditional_t< inline float color_value(const SCL_convertAlgo algo, const int r, const int c) const noexcept { switch (algo) { - case SCL_convertAlgo::gaCvter: - case SCL_convertAlgo::RGB: - case SCL_convertAlgo::RGB_Better: - return this->RGB(r, c); - - case SCL_convertAlgo::HSV: - return this->HSV(r, c); - case SCL_convertAlgo::Lab94: - case SCL_convertAlgo::Lab00: - return this->Lab(r, c); - case SCL_convertAlgo::XYZ: - return this->XYZ(r, c); + case SCL_convertAlgo::gaCvter: + case SCL_convertAlgo::RGB: + case SCL_convertAlgo::RGB_Better: + return this->RGB(r, c); + + case SCL_convertAlgo::HSV: + return this->HSV(r, c); + case SCL_convertAlgo::Lab94: + case SCL_convertAlgo::Lab00: + return this->Lab(r, c); + case SCL_convertAlgo::XYZ: + return this->XYZ(r, c); } return NAN; } + + std::span rgb_data_span(int ch) const noexcept { + return {this->rgb_data(ch), (size_t)this->color_count()}; + } + + std::span hsv_data_span(int ch) const noexcept { + return {this->hsv_data(ch), (size_t)this->color_count()}; + } + + std::span lab_data_span(int ch) const noexcept { + return {this->lab_data(ch), (size_t)this->color_count()}; + } + + std::span xyz_data_span(int ch) const noexcept { + return {this->xyz_data(ch), (size_t)this->color_count()}; + } + + private: + template + internal::hash_temp hash_temp() const noexcept { + internal::hash_temp temp; + temp.color_count = this->color_count(); + temp.color_ptrs[0] = {this->rgb_data(0), this->rgb_data(1), + this->rgb_data(2)}; + temp.color_ptrs[1] = {this->hsv_data(0), this->hsv_data(1), + this->hsv_data(2)}; + temp.color_ptrs[2] = {this->lab_data(0), this->lab_data(1), + this->lab_data(2)}; + temp.color_ptrs[3] = {this->xyz_data(0), this->xyz_data(1), + this->xyz_data(2)}; + + temp.color_id_ptr = this->map_data(); + temp.is_maptical = is_not_optical; + return temp; + } + /* + template + std::vector hash() const noexcept { + return internal::hash_of_colorset(temp); + } + */ + + public: + template + void hash_add_data(hash_stream &stream) const noexcept { + auto temp = this->hash_temp(); + + for (const auto &cptrs : temp.color_ptrs) { + for (const float *fptr : cptrs) { + stream.process_bytes(fptr, temp.color_count * sizeof(float)); + } + } + + stream.process_bytes( + temp.color_id_ptr, + temp.color_count * + (temp.is_maptical ? sizeof(uint8_t) : sizeof(uint16_t))); + stream.process_bytes(&temp.color_count, sizeof(temp.color_count)); + } }; -#endif // SCL_NEWCOLORSET_HPP \ No newline at end of file +#endif // SCL_NEWCOLORSET_HPP \ No newline at end of file diff --git a/utilities/ColorManip/newTokiColor.hpp b/utilities/ColorManip/newTokiColor.hpp index 59c894d5..6b6dafd4 100644 --- a/utilities/ColorManip/newTokiColor.hpp +++ b/utilities/ColorManip/newTokiColor.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -33,97 +33,69 @@ This file is part of SlopeCraft. #include #include #include - -#ifdef SC_VECTORIZE_AVX2 -#include -#include - -constexpr int num_float_per_m256 = 256 / 32; - -union alignas(32) f32_i32 { - float f32[8]; - int i32[8]; - __m256i m256i; -}; - -inline void take_abs_f32_8(float *p) noexcept { - for (size_t i = 0; i < 8; i++) { - *p = std::abs(*p); - } -} - -/** - * This function is a candidate when the real instruction can't be used. - */ - -/** - * This function is a candidate when the real instruction can't be used. - */ -inline __m256 _mm256_acos_ps__manually(__m256 x) noexcept { - alignas(32) float y[num_float_per_m256]; - - _mm256_store_ps(y, x); - - for (int i = 0; i < num_float_per_m256; i++) { - y[i] = std::acos(y[i]); - } - - return _mm256_load_ps(y); -} - -// #warning rua~ -#endif // SC_VECTORIZE_AVX2 +#include +#include // using Eigen::Dynamic; namespace { inline constexpr float threshold = 1e-10f; -} // namespace +} // namespace struct convert_unit { - explicit convert_unit(ARGB _a, ::SCL_convertAlgo _c) : _ARGB(_a), algo(_c) {} - ARGB _ARGB; + convert_unit() = default; + explicit convert_unit(ARGB a_, ::SCL_convertAlgo c_) : ARGB_(a_), algo(c_) {} + ARGB ARGB_; ::SCL_convertAlgo algo; inline bool operator==(const convert_unit another) const noexcept { - return (_ARGB == another._ARGB) && (algo == another.algo); + return (ARGB_ == another.ARGB_) && (algo == another.algo); } inline Eigen::Array3f to_c3() const noexcept { Eigen::Array3f c3; - const ::ARGB rawColor = this->_ARGB; + const ::ARGB rawColor = this->ARGB_; switch (this->algo) { - case ::SCL_convertAlgo::RGB: - case ::SCL_convertAlgo::RGB_Better: - case ::SCL_convertAlgo::gaCvter: - c3[0] = std::max(getR(rawColor) / 255.0f, threshold); - c3[1] = std::max(getG(rawColor) / 255.0f, threshold); - c3[2] = std::max(getB(rawColor) / 255.0f, threshold); - break; - - case ::SCL_convertAlgo::HSV: - RGB2HSV(getR(rawColor) / 255.0f, getG(rawColor) / 255.0f, - getB(rawColor) / 255.0f, c3[0], c3[1], c3[2]); - break; - case ::SCL_convertAlgo::Lab94: - case ::SCL_convertAlgo::Lab00: - float X, Y, Z; - RGB2XYZ(getR(rawColor) / 255.0f, getG(rawColor) / 255.0f, - getB(rawColor) / 255.0f, X, Y, Z); - XYZ2Lab(X, Y, Z, c3[0], c3[1], c3[2]); - break; - default: - RGB2XYZ(getR(rawColor) / 255.0f, getG(rawColor) / 255.0f, - getB(rawColor) / 255.0f, c3[0], c3[1], c3[2]); - break; + case ::SCL_convertAlgo::RGB: + case ::SCL_convertAlgo::RGB_Better: + case ::SCL_convertAlgo::gaCvter: + c3[0] = std::max(getR(rawColor) / 255.0f, threshold); + c3[1] = std::max(getG(rawColor) / 255.0f, threshold); + c3[2] = std::max(getB(rawColor) / 255.0f, threshold); + break; + + case ::SCL_convertAlgo::HSV: + RGB2HSV(getR(rawColor) / 255.0f, getG(rawColor) / 255.0f, + getB(rawColor) / 255.0f, c3[0], c3[1], c3[2]); + break; + case ::SCL_convertAlgo::Lab94: + case ::SCL_convertAlgo::Lab00: + float X, Y, Z; + RGB2XYZ(getR(rawColor) / 255.0f, getG(rawColor) / 255.0f, + getB(rawColor) / 255.0f, X, Y, Z); + XYZ2Lab(X, Y, Z, c3[0], c3[1], c3[2]); + break; + default: + RGB2XYZ(getR(rawColor) / 255.0f, getG(rawColor) / 255.0f, + getB(rawColor) / 255.0f, c3[0], c3[1], c3[2]); + break; } return c3; } + + template + void save(archive &ar) const { + ar(this->ARGB_, this->algo); + } + template + void load(archive &ar) { + ar(this->ARGB_, this->algo); + } }; struct hash_cvt_unit { -public: + public: inline size_t operator()(const convert_unit cu) const noexcept { - return std::hash()(cu._ARGB) ^ + return std::hash()(cu.ARGB_) ^ std::hash()(uint8_t(cu.algo)); } }; @@ -132,7 +104,7 @@ template class newTokiColor : public ::std::conditional_t { -public: + public: using Base_t = ::std::conditional_t; @@ -140,13 +112,13 @@ class newTokiColor using result_t = typename Base_t::result_t; // Eigen::Array3f c3; // color in some colorspace - float ResultDiff; // color diff for the result + float ResultDiff; // color diff for the result // These two members must be defined by caller - static const basic_t *const Basic; - static const allowed_t *const Allowed; + // static const basic_t *const Basic; + // static const allowed_t *const Allowed; -public: + public: explicit newTokiColor() { if constexpr (is_not_optical) { this->Result = 0; @@ -156,17 +128,17 @@ class newTokiColor } template - void set_gpu_result(uint16_t __result_color_id, - float __result_diff) noexcept { + void set_gpu_result(uint16_t result_color_id__, + float result_diff__) noexcept { static_assert(!is_not_optical, "set_gpu_result is only avaliable for VisualCraftL."); - this->ResultDiff = __result_diff; - this->result_color_id = __result_color_id; + this->ResultDiff = result_diff__; + this->result_color_id = result_color_id__; } - auto compute(convert_unit cu) noexcept { - if (getA(cu._ARGB) == 0) { + auto compute(convert_unit cu, const allowed_t &allowed) noexcept { + if (getA(cu.ARGB_) == 0) { if constexpr (is_not_optical) { this->Result = 0; return this->Result; @@ -188,22 +160,21 @@ class newTokiColor const Eigen::Array3f c3 = cu.to_c3(); switch (cu.algo) { - case ::SCL_convertAlgo::RGB: - return applyRGB(c3); - case ::SCL_convertAlgo::RGB_Better: - return applyRGB_plus(c3); - case ::SCL_convertAlgo::HSV: - return applyHSV(c3); - case ::SCL_convertAlgo::Lab94: - return applyLab94(c3); - case ::SCL_convertAlgo::Lab00: - return applyLab00(c3); - case ::SCL_convertAlgo::XYZ: - return applyXYZ(c3); - - default: - abort(); - return result_t(0); + case ::SCL_convertAlgo::RGB: + return applyRGB(c3, allowed); + case ::SCL_convertAlgo::RGB_Better: + return applyRGB_plus(c3, allowed); + case ::SCL_convertAlgo::HSV: + return applyHSV(c3, allowed); + case ::SCL_convertAlgo::Lab94: + return applyLab94(c3, allowed); + case ::SCL_convertAlgo::Lab00: + return applyLab00(c3, allowed); + case ::SCL_convertAlgo::XYZ: + return applyXYZ(c3, allowed); + + default: + abort(); } } @@ -215,48 +186,56 @@ class newTokiColor } } -private: - auto find_result(const TempVectorXf_t &diff) noexcept { + private: + auto find_result(const TempVectorXf_t &diff, + const allowed_t &allowed_colorset) noexcept { + if (diff.isNaN().any()) { + for (int idx = 0; idx < diff.size(); idx++) { + assert(!std::isnan(diff[idx])); + assert(diff[idx] >= 0); + } + } int tempidx = 0; this->ResultDiff = diff.minCoeff(&tempidx); if constexpr (is_not_optical) { - this->Result = Allowed->Map(tempidx); - if (Base_t::needFindSide) - this->doSide(diff); + this->Result = allowed_colorset.Map(tempidx); + if (allowed_colorset.need_find_side) this->doSide(diff, allowed_colorset); return this->Result; } else { - - this->result_color_id = Allowed->color_id(tempidx); + this->result_color_id = allowed_colorset.color_id(tempidx); // std::cout << tempidx << '\t' << this->result_color_id << '\n'; return this->color_id(); } } - auto find_result(const std::vector &diff) noexcept { + auto find_result(std::span &diff, + const allowed_t &allowed_colorset) noexcept { int minidx = 0; float min = diff[0]; for (int i = 1; i < int(diff.size()); i++) { + assert(!std::isnan(diff[i])); + assert(diff[i] >= 0); if (diff[i] < min) { minidx = i; min = diff[i]; } } if constexpr (is_not_optical) { - this->Result = Allowed->Map(minidx); - if (Base_t::needFindSide) - this->doSide(diff); + this->Result = allowed_colorset.Map(minidx); + if (allowed_colorset.need_find_side) this->doSide(diff); return this->Result; } else { - this->result_color_id = Allowed->color_id(minidx); + this->result_color_id = allowed_colorset.color_id(minidx); return this->color_id(); } } - template void doSide(const TempVectorXf_t &Diff) { + template + void doSide(const TempVectorXf_t &Diff, const allowed_t &allowed_colorset) { static_assert(is_not_optical); int tempIndex = 0; @@ -266,326 +245,146 @@ class newTokiColor this->sideSelectivity[1] = 1e35f; this->sideResult[1] = 0; - // using Base_t::DepthCount; - // using Base_t::needFindSide; + if (!allowed_colorset.need_find_side) return; - if (!Base_t::needFindSide) - return; - // qDebug("开始doSide"); - // qDebug()<<"size(Diff)=["<Result % 4) { - case 3: - return; - case 0: // 1,2 - if (Base_t::DepthCount[1]) { - this->sideSelectivity[0] = - Diff.segment(Base_t::DepthCount[0], Base_t::DepthCount[1]) - .minCoeff(&tempIndex); - this->sideResult[0] = Allowed->Map(Base_t::DepthCount[0] + tempIndex); - } - if (Base_t::DepthCount[2]) { - this->sideSelectivity[1] = - Diff.segment(Base_t::DepthCount[0] + Base_t::DepthCount[1], - Base_t::DepthCount[2]) - .minCoeff(&tempIndex); - this->sideResult[1] = Allowed->Map(Base_t::DepthCount[0] + - Base_t::DepthCount[1] + tempIndex); - } - break; - case 1: // 0,2 - if (Base_t::DepthCount[0]) { - this->sideSelectivity[0] = - Diff.segment(0, Base_t::DepthCount[0]).minCoeff(&tempIndex); - this->sideResult[0] = Allowed->Map(0 + tempIndex); - } - if (Base_t::DepthCount[2]) { - this->sideSelectivity[1] = - Diff.segment(Base_t::DepthCount[0] + Base_t::DepthCount[1], - Base_t::DepthCount[2]) - .minCoeff(&tempIndex); - this->sideResult[1] = Allowed->Map(Base_t::DepthCount[0] + - Base_t::DepthCount[1] + tempIndex); - } - break; - case 2: // 0,1 - if (Base_t::DepthCount[0]) { - this->sideSelectivity[0] = - Diff.segment(0, Base_t::DepthCount[0]).minCoeff(&tempIndex); - this->sideResult[0] = Allowed->Map(0 + tempIndex); - } - if (Base_t::DepthCount[1]) { - this->sideSelectivity[1] = - Diff.segment(Base_t::DepthCount[0], Base_t::DepthCount[1]) - .minCoeff(&tempIndex); - this->sideResult[1] = Allowed->Map(Base_t::DepthCount[0] + tempIndex); - } - break; + case 3: + return; + case 0: // 1,2 + if (allowed_colorset.depth_count()[1]) { + this->sideSelectivity[0] = + Diff.segment(allowed_colorset.depth_count()[0], + allowed_colorset.depth_count()[1]) + .minCoeff(&tempIndex); + this->sideResult[0] = allowed_colorset.Map( + allowed_colorset.depth_count()[0] + tempIndex); + } + if (allowed_colorset.depth_count()[2]) { + this->sideSelectivity[1] = + Diff.segment(allowed_colorset.depth_count()[0] + + allowed_colorset.depth_count()[1], + allowed_colorset.depth_count()[2]) + .minCoeff(&tempIndex); + this->sideResult[1] = allowed_colorset.Map( + allowed_colorset.depth_count()[0] + + allowed_colorset.depth_count()[1] + tempIndex); + } + break; + case 1: // 0,2 + if (allowed_colorset.depth_count()[0]) { + this->sideSelectivity[0] = + Diff.segment(0, allowed_colorset.depth_count()[0]) + .minCoeff(&tempIndex); + this->sideResult[0] = allowed_colorset.Map(0 + tempIndex); + } + if (allowed_colorset.depth_count()[2]) { + this->sideSelectivity[1] = + Diff.segment(allowed_colorset.depth_count()[0] + + allowed_colorset.depth_count()[1], + allowed_colorset.depth_count()[2]) + .minCoeff(&tempIndex); + this->sideResult[1] = allowed_colorset.Map( + allowed_colorset.depth_count()[0] + + allowed_colorset.depth_count()[1] + tempIndex); + } + break; + case 2: // 0,1 + if (allowed_colorset.depth_count()[0]) { + this->sideSelectivity[0] = + Diff.segment(0, allowed_colorset.depth_count()[0]) + .minCoeff(&tempIndex); + this->sideResult[0] = allowed_colorset.Map(0 + tempIndex); + } + if (allowed_colorset.depth_count()[1]) { + this->sideSelectivity[1] = + Diff.segment(allowed_colorset.depth_count()[0], + allowed_colorset.depth_count()[1]) + .minCoeff(&tempIndex); + this->sideResult[1] = allowed_colorset.Map( + allowed_colorset.depth_count()[0] + tempIndex); + } + break; } // sideSelectivity[0]-=1.0;sideSelectivity[1]-=1.0; // sideSelectivity[0]*=100.0;sideSelectivity[1]*=100.0; - /* - qDebug()<<"side[0]=["<sideSelectivity, this->sideResult, this->Result, this->ResultDiff); + } - return find_result(Diff); + template + void load(archive &ar) { + static_assert(is_not_optical, + "Serialization is only avaliable for maptical maps"); + ar(this->sideSelectivity, this->sideResult, this->Result, this->ResultDiff); } }; -#endif // NEWTOKICOLOR_HPP \ No newline at end of file +#endif // NEWTOKICOLOR_HPP \ No newline at end of file diff --git a/utilities/ColorManip/seralize_funs.hpp b/utilities/ColorManip/seralize_funs.hpp new file mode 100644 index 00000000..9079e408 --- /dev/null +++ b/utilities/ColorManip/seralize_funs.hpp @@ -0,0 +1,35 @@ +#ifndef SLOPECRAFT_UTILITIES_COLORMANIP_SERIALIZEFUNS_HPP +#define SLOPECRAFT_UTILITIES_COLORMANIP_SERIALIZEFUNS_HPP + +#include +#include +#include +#include + +namespace cereal { +template +void save(archive &ar, const Eigen::ArrayXX &img) { + static_assert(std::is_arithmetic_v, "T must be arithmetic"); + ar(cereal::make_size_tag(img.rows())); + ar(cereal::make_size_tag(img.cols())); + ar(cereal::binary_data(img.data(), img.size() * sizeof(T))); +} + +template +void load(archive &ar, Eigen::ArrayXX &img) { + static_assert(std::is_arithmetic_v, "T must be arithmetic"); + Eigen::Index rows{0}, cols{0}; + + ar(cereal::make_size_tag(rows)); + ar(cereal::make_size_tag(cols)); + + if (rows < 0 || cols < 0) { + throw std::runtime_error{"Negative size"}; + } + + img.resize(rows, cols); + ar(cereal::binary_data(img.data(), rows * cols * sizeof(T))); +} +} // namespace cereal + +#endif // SLOPECRAFT_UTILITIES_COLORMANIP_SERIALIZEFUNS_HPP \ No newline at end of file diff --git a/utilities/ColorManip/tests/test_algo.cpp b/utilities/ColorManip/tests/test_algo.cpp index fba3fb89..96afdad5 100644 --- a/utilities/ColorManip/tests/test_algo.cpp +++ b/utilities/ColorManip/tests/test_algo.cpp @@ -94,7 +94,6 @@ int list_gpu() noexcept { void fill_c3arr(std::vector> &dst, std::mt19937 &mt, SCL_convertAlgo algo) noexcept { - std::uniform_real_distribution randf(0, 1); for (auto &c3 : dst) { for (float &val : c3) { @@ -102,31 +101,35 @@ void fill_c3arr(std::vector> &dst, std::mt19937 &mt, } switch (algo) { - case SCL_convertAlgo::RGB: - case SCL_convertAlgo::RGB_Better: - break; - case SCL_convertAlgo::HSV: - RGB2HSV(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); - break; - case SCL_convertAlgo::Lab00: - case SCL_convertAlgo::Lab94: - RGB2XYZ(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); - XYZ2Lab(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); - break; - case SCL_convertAlgo::XYZ: - RGB2XYZ(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); - break; - default: - abort(); - break; + case SCL_convertAlgo::RGB: + case SCL_convertAlgo::RGB_Better: + break; + case SCL_convertAlgo::HSV: + RGB2HSV(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); + break; + case SCL_convertAlgo::Lab00: + case SCL_convertAlgo::Lab94: + RGB2XYZ(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); + XYZ2Lab(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); + break; + case SCL_convertAlgo::XYZ: + RGB2XYZ(c3[0], c3[1], c3[2], c3[0], c3[1], c3[2]); + break; + default: + abort(); + break; } } } -#define HANDLE_ERR(gi, ret) \ - if (!gi->ok_v()) { \ - cout << gi->error_detail_v() << " : " << gi->error_code_v() << endl; \ - return ret; \ +#define HANDLE_ERR(gi, ret) \ + if (gi == nullptr) { \ + cout << "failed to create gpu resource handle. gi==nullptr" << endl; \ + return ret; \ + } \ + if (!gi->ok_v()) { \ + cout << gi->error_detail_v() << " : " << gi->error_code_v() << endl; \ + return ret; \ } int run_task(task_t &task) noexcept { @@ -143,6 +146,12 @@ int run_task(task_t &task) noexcept { eig_colorset = map_colorset.transpose(); } + + if (gpu_wrapper::platform_num() <= 0) { + cout << "No avaliable opencl platforms." << endl; + return 0; + } + auto plat = gpu_wrapper::platform_wrapper::create(task.platidx); auto dev = gpu_wrapper::device_wrapper::create(plat, task.devidx); diff --git a/utilities/ExternalConverters/ExternalConverterStaticInterface.h b/utilities/ExternalConverters/ExternalConverterStaticInterface.h index 2302aaef..28f964cc 100644 --- a/utilities/ExternalConverters/ExternalConverterStaticInterface.h +++ b/utilities/ExternalConverters/ExternalConverterStaticInterface.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -40,11 +40,11 @@ extern int colorCount4External(); extern Eigen::Map BasicRGB4External(int channel); -extern Eigen::Map AllowedRGB4External(int channel); +// extern Eigen::Map AllowedRGB4External(int channel); +// +// extern Eigen::Map> +// AllowedMapList4External(); -extern Eigen::Map> -AllowedMapList4External(); +} // namespace SlopeCraft -} // namespace SlopeCraft - -#endif // EXTERNAL_CONVERTER_STATIC_INTERFACE_H \ No newline at end of file +#endif // EXTERNAL_CONVERTER_STATIC_INTERFACE_H \ No newline at end of file diff --git a/utilities/ExternalConverters/GAConverter/CMakeLists.txt b/utilities/ExternalConverters/GAConverter/CMakeLists.txt index 266e8e82..5c978372 100644 --- a/utilities/ExternalConverters/GAConverter/CMakeLists.txt +++ b/utilities/ExternalConverters/GAConverter/CMakeLists.txt @@ -1,8 +1,9 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.20) project(SlopeCraft_GAConverter VERSION ${SlopeCraft_version} LANGUAGES CXX) # set(CMAKE_CXX_STANDARD 20) find_package(OpenMP REQUIRED) +find_package(Heu REQUIRED) set(SlopeCraft_GAConverter_sources @@ -17,18 +18,16 @@ set(SlopeCraft_GAConverter_sources ) add_library(GAConverter STATIC ${SlopeCraft_GAConverter_sources}) +target_include_directories(GAConverter PRIVATE "${CMAKE_BINARY_DIR}/SlopeCraftL") +target_link_libraries(GAConverter PUBLIC ColorManip) +#target_include_directories(GAConverter PUBLIC +# ../../ColorManip +#) -target_include_directories(GAConverter PUBLIC ${SlopeCraft_Eigen3_include_dir} - ${SlopeCraft_HeuristicFlow_include_dir} - ../../ColorManip -) - -target_link_libraries(GAConverter PUBLIC OpenMP::OpenMP_CXX) +find_package(Eigen3 REQUIRED) +target_link_libraries(GAConverter PUBLIC OpenMP::OpenMP_CXX Eigen3::Eigen Heu::Genetic) -target_compile_features(GAConverter PRIVATE cxx_std_20) +target_compile_features(GAConverter PRIVATE cxx_std_23) -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set_target_properties(GAConverter PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() # target_compile_options(GAConverter BEFORE PUBLIC "-std=c++17") \ No newline at end of file diff --git a/utilities/ExternalConverters/GAConverter/GAConverter.cpp b/utilities/ExternalConverters/GAConverter/GAConverter.cpp index 942c02cd..4a9b25f4 100644 --- a/utilities/ExternalConverters/GAConverter/GAConverter.cpp +++ b/utilities/ExternalConverters/GAConverter/GAConverter.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -32,7 +32,9 @@ using namespace GACvter; namespace GACvter { -mapColor2Gray_LUT_t mapColor2Gray; +void delete_GA_converter(GAConverter *g) noexcept { delete g; } +// #error "Fix singleton here" +// mapColor2Gray_LUT_t mapColor2Gray; const mutateMap_t mutateMap = GACvter::makeMutateMap(); @@ -48,39 +50,31 @@ inline ARGB mapColor2ARGB32(const mapColor_t mC) noexcept { return ARGB32(rgb(0), rgb(1), rgb(2), (mC < 4) ? 0 : 255); } -} // namespace GACvter +} // namespace GACvter GAConverter::GAConverter() { this->setTournamentSize(3); } -void GACvter::updateMapColor2GrayLUT() { - static std::mutex lock; - - if (!lock.try_lock()) { // there's another thread writting currently. Just - // wait until it finishes. - lock.lock(); - lock.unlock(); - return; - } else { - for (int row = 0; row < 256; row++) { - const mapColor_t mC = index2mapColor(row); - const float r = SlopeCraft::BasicRGB4External(0)[row]; - const float g = SlopeCraft::BasicRGB4External(1)[row]; - const float b = SlopeCraft::BasicRGB4External(2)[row]; - mapColor2Gray[mC] = GrayMax * RGB2Gray_Gamma(r, g, b); - } - lock.unlock(); +mapColor2Gray_LUT_t GACvter::updateMapColor2GrayLUT() { + mapColor2Gray_LUT_t result{}; + result.fill(0); + for (int row = 0; row < 256; row++) { + const mapColor_t mC = index2mapColor(row); + const float r = SlopeCraft::BasicRGB4External(0)[row]; + const float g = SlopeCraft::BasicRGB4External(1)[row]; + const float b = SlopeCraft::BasicRGB4External(2)[row]; + result[mC] = GrayMax * RGB2Gray_Gamma(r, g, b); } + return result; } template void privateMutateFun(const Var_t *parent, Var_t *child, const CvterInfo *arg) noexcept { - constexpr float ratio = 0.01; constexpr float Threshold = ratio * 2 - 1; *child = *parent; - if constexpr (strong //&& false - ) { // strong mutation + if constexpr (strong //&& false + ) { // strong mutation static Eigen::ArrayXXf randMat(arg->rawImageCache.rows(), arg->rawImageCache.cols()); randMat.setRandom(); @@ -91,7 +85,7 @@ void privateMutateFun(const Var_t *parent, Var_t *child, mutateMap(parent->operator()(idx), heu::randIdx(OrderMax - 1)); } } - } else { // weak mutation + } else { // weak mutation const int idx = heu::randIdx(parent->size()); child->operator()(idx) = mutateMap(parent->operator()(idx), heu::randIdx(OrderMax - 1)); @@ -99,10 +93,9 @@ void privateMutateFun(const Var_t *parent, Var_t *child, } void GACvter::iFun(Var_t *v, const CvterInfo *arg) noexcept { - v->setZero(arg->rawImageCache.rows(), arg->rawImageCache.cols()); - if (heu::randD() < 1.0 / 3) { // generate by random + if (heu::randD() < 1.0 / 3) { // generate by random std::unordered_map iniToolCpy = arg->iniTool; for (auto &i : iniToolCpy) { @@ -113,14 +106,14 @@ void GACvter::iFun(Var_t *v, const CvterInfo *arg) noexcept { v->operator()(i) = iniToolCpy[arg->rawImageCache(i)]; } - } else { // generate by seed and mutation + } else { // generate by seed and mutation - if (heu::randD() < 0.4) { // strong mutation + if (heu::randD() < 0.4) { // strong mutation privateMutateFun(&arg->seeds[heu::randIdx(arg->seeds.size())], v, arg); - } else { // weak mutation + } else { // weak mutation privateMutateFun(&arg->seeds[heu::randIdx(arg->seeds.size())], v, arg); @@ -129,10 +122,9 @@ void GACvter::iFun(Var_t *v, const CvterInfo *arg) noexcept { } void GACvter::fFun(const Var_t *v, const CvterInfo *arg, double *f) noexcept { - GrayImage gray(arg->rawImageCache.rows(), arg->rawImageCache.cols()), edged; for (int64_t i = 0; i < arg->rawImageCache.size(); i++) { - gray(i) = mapColor2Gray[arg->colorMap(i).mapColor(v->operator()(i))]; + gray(i) = arg->mapColor2Gray[arg->colorMap(i).mapColor(v->operator()(i))]; } applyGaussian(gray, &edged, Gaussian); @@ -146,7 +138,6 @@ void GACvter::fFun(const Var_t *v, const CvterInfo *arg, double *f) noexcept { void GACvter::cFun(const Var_t *p1, const Var_t *p2, Var_t *c1, Var_t *c2, const CvterInfo *arg) noexcept { - const uint32_t rows = arg->rawImageCache.rows(); const uint32_t cols = arg->rawImageCache.cols(); const uint32_t rS = heu::randD(1, rows - 2); @@ -174,7 +165,6 @@ void GACvter::cFun(const Var_t *p1, const Var_t *p2, Var_t *c1, Var_t *c2, void GACvter::mFun(const Var_t *parent, Var_t *child, const CvterInfo *arg) noexcept { - if (arg->strongMutation) { privateMutateFun(parent, child, arg); } else { @@ -208,13 +198,15 @@ void GACvter::GAConverter::setRawImage(const EImage &src) noexcept { this->_args.iniTool.reserve(colorHash.size()); for (auto &i : colorHash) { - i.second.calculate(i.first); + i.second.calculate(i.first, *this->_args.allowed_colorset); this->_args.iniTool[i.first] = 0; } for (int i = 0; i < this->_args.rawImageCache.size(); i++) { this->_args.colorMap(i) = colorHash[this->_args.rawImageCache(i)]; } + + this->_args.mapColor2Gray = updateMapColor2GrayLUT(); } void GACvter::GAConverter::setSeeds( diff --git a/utilities/ExternalConverters/GAConverter/GAConverter.h b/utilities/ExternalConverters/GAConverter/GAConverter.h index de726410..f7111153 100644 --- a/utilities/ExternalConverters/GAConverter/GAConverter.h +++ b/utilities/ExternalConverters/GAConverter/GAConverter.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -35,7 +35,7 @@ void cFun(const Var_t *, const Var_t *, Var_t *, Var_t *, const CvterInfo *) noexcept; void mFun(const Var_t *, Var_t *, const CvterInfo *) noexcept; -void updateMapColor2GrayLUT(); +[[nodiscard]] mapColor2Gray_LUT_t updateMapColor2GrayLUT(); class GAConverter : private heu::SOGA; -public: + public: GAConverter(); void setUiPack(const uiPack &) noexcept; @@ -67,8 +67,8 @@ class GAConverter } void setRawImage(const EImage &) noexcept; - void - setSeeds(const std::vector *> &) noexcept; + void setSeeds( + const std::vector *> &) noexcept; using Base_t::option; using Base_t::setOption; @@ -77,7 +77,7 @@ class GAConverter void resultImage(EImage *); -private: + private: friend void ::GACvter::iFun(Var_t *, const CvterInfo *) noexcept; friend void ::GACvter::fFun(const Var_t *, const CvterInfo *, double *) noexcept; @@ -93,7 +93,7 @@ class GAConverter static constexpr std::clock_t reportInterval = 2 * CLOCKS_PER_SEC; -protected: + protected: template inline void __impl_recordFitness() noexcept { Base_t::template __impl_recordFitness(); @@ -109,6 +109,6 @@ class GAConverter } }; -} // namespace GACvter +} // namespace GACvter -#endif // SCL_GACVTER_GACONVERTER_H +#endif // SCL_GACVTER_GACONVERTER_H diff --git a/utilities/ExternalConverters/GAConverter/GACvterDefines.hpp b/utilities/ExternalConverters/GAConverter/GACvterDefines.hpp index 5558cd58..cce96e97 100644 --- a/utilities/ExternalConverters/GAConverter/GACvterDefines.hpp +++ b/utilities/ExternalConverters/GAConverter/GACvterDefines.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -27,6 +27,7 @@ This file is part of SlopeCraft. #define _USE_MATH_DEFINES #endif +#include #include #include #include @@ -45,7 +46,7 @@ using GrayImage = Eigen::ArrayXX; using mapColor_t = uint8_t; using order_t = uint8_t; -const order_t OrderMax = 4; +constexpr order_t OrderMax = 4; using mutateMap_t = Eigen::Array; @@ -64,7 +65,7 @@ const Eigen::Array Gaussian = {{2, 4, 5, 4, 2}, {5, 12, 15, 12, 5}, {4, 9, 12, 9, 4}, {2, 4, 5, 4, 2}}; -const int GauSum = Gaussian.sum(); +// const int GauSum = Gaussian.sum(); inline mutateMap_t makeMutateMap() { mutateMap_t res; @@ -133,6 +134,6 @@ void EImg2GrayImg(const EImage &e, GrayImage *gImg) noexcept { } } } -} // namespace GACvter +} // namespace GACvter -#endif // GACVTERDEFINES_H +#endif // GACVTERDEFINES_H diff --git a/utilities/ExternalConverters/GAConverter/sortColor.cpp b/utilities/ExternalConverters/GAConverter/sortColor.cpp index 4d133e38..fd8db067 100644 --- a/utilities/ExternalConverters/GAConverter/sortColor.cpp +++ b/utilities/ExternalConverters/GAConverter/sortColor.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -21,21 +21,22 @@ This file is part of SlopeCraft. */ #include "sortColor.h" +#include "../../../SlopeCraftL/SCLDefines.h" using namespace GACvter; using namespace SlopeCraft; sortColor::sortColor() noexcept {} -void sortColor::calculate(ARGB rgb) noexcept { +void sortColor::calculate(ARGB rgb, + const colorset_allowed_t& allowed) noexcept { const float r = getR(rgb) / 255.0f, g = getG(rgb) / 255.0f, b = getB(rgb) / 255.0f; - auto diffR = SlopeCraft::AllowedRGB4External(0) - r; - // SlopeCraft::Allowed4External.col(0)-r; - // SlopeCraft::Allowed4External.col(0)-r; - auto diffG = SlopeCraft::AllowedRGB4External(1) - g; - auto diffB = SlopeCraft::AllowedRGB4External(2) - b; + // auto diffR = SlopeCraft::AllowedRGB4External(0) - r; + auto diffR = allowed.rgb(0) - r; + auto diffG = allowed.rgb(1) - g; + auto diffB = allowed.rgb(2) - b; TempVectorXf diff = diffR.square() + diffG.square() + diffB.square(); @@ -43,7 +44,8 @@ void sortColor::calculate(ARGB rgb) noexcept { int tempIdx = 0; // errors[o]= diff.minCoeff(&tempIdx); - mapCs[o] = SlopeCraft::AllowedMapList4External()[tempIdx]; + mapCs[o] = allowed.Map( + tempIdx); // SlopeCraft::AllowedMapList4External()[tempIdx]; // Converter::mapColorSrc->operator[](tempIdx); diff[tempIdx] = heu::internal::pinfF; } diff --git a/utilities/ExternalConverters/GAConverter/sortColor.h b/utilities/ExternalConverters/GAConverter/sortColor.h index edbcfd61..8bd8610a 100644 --- a/utilities/ExternalConverters/GAConverter/sortColor.h +++ b/utilities/ExternalConverters/GAConverter/sortColor.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,14 +24,15 @@ This file is part of SlopeCraft. #define SORTCOLOR_H #include "GACvterDefines.hpp" +#include "../../SlopeCraftL/SCLDefines.h" namespace GACvter { class sortColor { -public: + public: sortColor() noexcept; - void calculate(ARGB) noexcept; + void calculate(ARGB, const colorset_allowed_t& allowed) noexcept; inline mapColor_t mapColor(order_t o) const noexcept { return mapCs[o]; } /* @@ -40,10 +41,10 @@ class sortColor { } */ -private: + private: std::array mapCs; // std::array errors; }; -} // namespace GACvter -#endif // SORTCOLOR_H +} // namespace GACvter +#endif // SORTCOLOR_H diff --git a/utilities/ExternalConverters/GAConverter/uiPack.h b/utilities/ExternalConverters/GAConverter/uiPack.h index fe404a08..edab976c 100644 --- a/utilities/ExternalConverters/GAConverter/uiPack.h +++ b/utilities/ExternalConverters/GAConverter/uiPack.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -36,15 +36,17 @@ using uiPack = ::uiPack; struct CvterInfo { EImage rawImageCache; heu::MatrixDynamicSize - colorMap; // the relationship between pixels and sortColor s + colorMap; // the relationship between pixels and sortColor s std::clock_t prevClock; bool strongMutation; GrayImage edgeFeatureMap; - std::vector seeds; // seeds to initialize population + std::vector seeds; // seeds to initialize population std::unordered_map iniTool; uiPack ui; + mapColor2Gray_LUT_t mapColor2Gray{}; + const colorset_allowed_t* allowed_colorset{nullptr}; }; -} // namespace GACvter +} // namespace GACvter -#endif // UIPACK_H +#endif // UIPACK_H diff --git a/utilities/FlatDiagram/CMakeLists.txt b/utilities/FlatDiagram/CMakeLists.txt new file mode 100644 index 00000000..155dbb88 --- /dev/null +++ b/utilities/FlatDiagram/CMakeLists.txt @@ -0,0 +1,16 @@ +project(ColorManip VERSION ${SlopeCraft_version} LANGUAGES C CXX) + +find_package(Eigen3 REQUIRED) + +add_library(FlatDiagram STATIC + FlatDiagram.h + FlatDiagram.cpp) + +target_include_directories(FlatDiagram INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(FlatDiagram + PUBLIC + ColorManip + PNG::PNG + Eigen3::Eigen +) +target_compile_features(FlatDiagram PUBLIC cxx_std_23) diff --git a/utilities/FlatDiagram/FlatDiagram.cpp b/utilities/FlatDiagram/FlatDiagram.cpp new file mode 100644 index 00000000..07858372 --- /dev/null +++ b/utilities/FlatDiagram/FlatDiagram.cpp @@ -0,0 +1,158 @@ +#include +#include "FlatDiagram.h" +#include +#include +#include + +#include + +void libFlatDiagram::reverse_color(uint32_t *ptr, size_t num_pixels) noexcept { + // this can be vertorized by compiler optimization + for (uint32_t *p = ptr; p < ptr + num_pixels; p++) { + *p = reverse_color(*p); + } +} + +void libFlatDiagram::ARGB_to_AGBR(uint32_t *ptr, size_t num_pixels) noexcept { + for (uint32_t *p = ptr; p < ptr + num_pixels; p++) { + const uint32_t A = getA(*p); + *p = (*p) << 8 | A; + *p = reverse_byte(*p); + } +} + +void libFlatDiagram::draw_flat_diagram_to_memory( + Eigen::Map buffer, const fd_option &opt, + const get_blk_image_callback_t &blk_image_at) { + assert(buffer.cols() == opt.cols * 16); + assert(buffer.rows() >= (opt.row_end - opt.row_start) * 16); + + // copy block images + for (int64_t r = opt.row_start; r < opt.row_end; r++) { + const int r_pixel_beg = (r - opt.row_start) * 16; + for (int64_t c = 0; c < opt.cols; c++) { + const int c_pixel_beg = c * 16; + /* + const bool is_src_aligned = + (reinterpret_cast( + blkp->project_image_on_exposed_face.data()) % + 32) == 0; + const bool is_aligned = is_dst_aligned && is_src_aligned; + */ + + buffer.block<16, 16>(r_pixel_beg, c_pixel_beg) = blk_image_at(r, c); + } + } + + for (int64_t br = opt.row_start; br < opt.row_end; br++) { + if ((opt.split_line_row_margin > 0) && + (br % opt.split_line_row_margin == 0)) { + const int64_t pr = (br - opt.row_start) * 16; + + reverse_color(&buffer(pr, 0), buffer.cols()); + } + } + + for (int64_t bc = 0; bc < opt.cols; bc++) { + if ((opt.split_line_col_margin > 0) && + (bc % opt.split_line_col_margin == 0)) { + const int64_t pc = bc * 16; + + for (int64_t pr = 0; pr < buffer.rows(); pr++) { + buffer(pr, pc) = reverse_color(buffer(pr, pc)); + } + } + } +} + +std::string libFlatDiagram::export_flat_diagram( + std::string_view png_filename, const fd_option &opt, + const get_blk_image_callback_t &blk_image_at, + std::span> texts) noexcept { + const int64_t rows_capacity_by_blocks = 16; + + EImgRowMajor_t buffer(rows_capacity_by_blocks * 16, opt.cols * 16); + + FILE *fp = fopen(png_filename.data(), "wb"); + + if (fp == nullptr) { + return std::format("fopen failed to create png file {}.", png_filename); + } + + png_struct *png = + png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (png == nullptr) { + fclose(fp); + return std::format("fopen failed to create png struct for png file {}.", + png_filename); + } + + png_info *png_info = png_create_info_struct(png); + if (png_info == nullptr) { + png_destroy_write_struct(&png, &png_info); + fclose(fp); + return std::format( + "fopen failed to create png info struct for png file {}.", + png_filename); + } + + png_init_io(png, fp); + + png_set_compression_level(png, opt.png_compress_level); + + png_set_compression_mem_level(png, opt.png_compress_memory_level); + + // png_set_text_compression_level(png, 8); + + png_set_IHDR(png, png_info, opt.cols * 16, 16 * (opt.row_end - opt.row_start), + 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, png_info); + + { + std::vector png_txts; + png_txts.reserve(texts.size()); + for (auto &[first, second] : texts) { + png_text temp; + temp.compression = -1; + temp.key = first.data(); + temp.text = second.data(); + png_txts.emplace_back(temp); + } + + png_set_text(png, png_info, png_txts.data(), png_txts.size()); + } + + try { + for (int64_t ridx = opt.row_start; ridx < opt.row_end; + ridx += rows_capacity_by_blocks) { + const int64_t rows_this_time = + std::min(opt.row_end - ridx, rows_capacity_by_blocks); + buffer.fill(0xFFFFFFFF); + + fd_option opt_temp = opt; + opt_temp.row_start = ridx; + opt_temp.row_end = ridx + rows_this_time; + + draw_flat_diagram_to_memory({buffer.data(), buffer.rows(), buffer.cols()}, + opt_temp, blk_image_at); + + ARGB_to_AGBR(buffer.data(), rows_this_time * 16 * opt.cols * 16); + + for (int64_t pix_r = 0; pix_r < rows_this_time * 16; pix_r++) { + png_write_row(png, + reinterpret_cast(&buffer(pix_r, 0))); + } + } + } catch (const std::exception &e) { + return std::format("Exception occurred while writing flat diagram: {}", + e.what()); + } + + png_write_end(png, png_info); + + png_destroy_write_struct(&png, &png_info); + fclose(fp); + + return {}; +} \ No newline at end of file diff --git a/utilities/FlatDiagram/FlatDiagram.h b/utilities/FlatDiagram/FlatDiagram.h new file mode 100644 index 00000000..0f6c66ed --- /dev/null +++ b/utilities/FlatDiagram/FlatDiagram.h @@ -0,0 +1,66 @@ +#ifndef SLOPECRAFT_UTILITIES_FLATDIAGRAM_FLATDIAGRAM_H +#define SLOPECRAFT_UTILITIES_FLATDIAGRAM_FLATDIAGRAM_H + +#include +#include +#include +#include +#include +#include + +namespace libFlatDiagram { + +using EImgRowMajor_t = + Eigen::Array; + +constexpr uint32_t reverse_color(uint32_t ARGB_src) noexcept { + return ARGB32(255 - getR(ARGB_src), 255 - getG(ARGB_src), + 255 - getB(ARGB_src), getA(ARGB_src)); +} + +constexpr uint32_t reverse_byte(uint32_t v) noexcept { + const uint32_t a = (v & 0xFF'00'00'00) >> 24; + const uint32_t b = (v & 0x00'FF'00'00) >> 8; + const uint32_t c = (v & 0x00'00'FF'00) << 8; + const uint32_t d = (v & 0x00'00'00'FF) << 24; + + return a | b | c | d; +} + +void reverse_color(uint32_t *ptr, size_t num_pixels) noexcept; + +void ARGB_to_AGBR(uint32_t *ptr, size_t num_pixels) noexcept; + +struct fd_option { + // [row_start,row_end) * [o,col_count) will be written + int64_t row_start; // by block + int64_t row_end; // by block + int64_t cols; // by block + int32_t split_line_row_margin; // 0 or negative number means no split lines + int32_t split_line_col_margin; // 0 or negative number means no split lines + int png_compress_level{9}; + int png_compress_memory_level{8}; +}; + +using block_img_ref_t = + Eigen::Map>; + +constexpr size_t ret_size = sizeof(block_img_ref_t); + +using get_blk_image_callback_t = + std::function; + +constexpr size_t callback_size = sizeof(get_blk_image_callback_t); + +void draw_flat_diagram_to_memory(Eigen::Map buffer, + const fd_option &opt, + const get_blk_image_callback_t &blk_image_at); + +std::string export_flat_diagram( + std::string_view png_filename, const fd_option &opt, + const get_blk_image_callback_t &blk_image_at, + std::span> texts) noexcept; + +} // namespace libFlatDiagram + +#endif // SLOPECRAFT_UTILITIES_FLATDIAGRAM_FLATDIAGRAM_H \ No newline at end of file diff --git a/utilities/GPUWrapper/CMakeLists.txt b/utilities/GPUWrapper/CMakeLists.txt index 479ea003..359c35a3 100644 --- a/utilities/GPUWrapper/CMakeLists.txt +++ b/utilities/GPUWrapper/CMakeLists.txt @@ -4,21 +4,23 @@ add_library(GPUInterface STATIC GPU_interface.h) target_include_directories(GPUInterface INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_features(GPUInterface PUBLIC cxx_std_20) +target_compile_features(GPUInterface PUBLIC cxx_std_23) -if(${LINUX}) +if (${LINUX}) set_target_properties(GPUInterface PROPERTIES TARGET_INDENPENDENT_CODE TRUE) -endif() +endif () -if(${SlopeCraft_GPU_API} STREQUAL "OpenCL") +if (${SlopeCraft_GPU_API} STREQUAL "OpenCL") add_subdirectory(OpenCL) target_compile_definitions(GPUInterface PUBLIC -DSLOPECRAFT_GPU_API="OpenCL") return() -endif() - -if(${SlopeCraft_GPU_API} STREQUAL "None") +elseif (${SlopeCraft_GPU_API} STREQUAL "Vulkan") + add_subdirectory(Vulkan) + target_compile_definitions(GPUInterface PUBLIC -DSLOPECRAFT_GPU_API="Vulkan") + return() +elseif (${SlopeCraft_GPU_API} STREQUAL "None") add_subdirectory(None) return() -endif() +endif () message(FATAL_ERROR "Invalid value for \"SlopeCraft_GPU_API\" : ${SlopeCraft_GPU_API}") \ No newline at end of file diff --git a/utilities/GPUWrapper/GPU_interface.h b/utilities/GPUWrapper/GPU_interface.h index 8eb068a4..36050d40 100644 --- a/utilities/GPUWrapper/GPU_interface.h +++ b/utilities/GPUWrapper/GPU_interface.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -26,6 +26,8 @@ This file is part of SlopeCraft. #include "../SC_GlobalEnums.h" #include #include +#include +#include namespace gpu_wrapper { @@ -33,14 +35,14 @@ namespace gpu_wrapper { static constexpr bool have_api = true; #else static constexpr bool have_api = false; -#endif // #ifdef SLOPECRAFT_GPU_API +#endif // #ifdef SLOPECRAFT_GPU_API const char *api_name() noexcept; size_t platform_num() noexcept; class platform_wrapper { -public: + public: platform_wrapper() = default; virtual ~platform_wrapper() = default; @@ -50,30 +52,33 @@ class platform_wrapper { // virtual size_t index() const noexcept = 0; - [[nodiscard]] static platform_wrapper * - create(size_t idx, int *errorcode = nullptr) noexcept; + [[nodiscard]] static platform_wrapper *create( + size_t idx, int *errorcode = nullptr) noexcept; static void destroy(platform_wrapper *) noexcept; }; class device_wrapper { -public: + public: device_wrapper() = default; virtual ~device_wrapper() = default; virtual const char *name_v() const noexcept = 0; - [[nodiscard]] static device_wrapper * - create(platform_wrapper *pw, size_t idx, int *errorcode = nullptr) noexcept; + [[nodiscard]] static device_wrapper *create( + platform_wrapper *pw, size_t idx, int *errorcode = nullptr) noexcept; static void destroy(device_wrapper *) noexcept; }; class gpu_interface { -public: + public: gpu_interface() = default; virtual ~gpu_interface() = default; [[nodiscard]] static gpu_interface *create(platform_wrapper *pw, device_wrapper *dw) noexcept; + [[nodiscard]] static gpu_interface *create( + platform_wrapper *pw, device_wrapper *dw, + std::pair &err) noexcept; static void destroy(gpu_interface *) noexcept; virtual const char *api_v() const noexcept = 0; @@ -82,9 +87,9 @@ class gpu_interface { virtual bool ok_v() const noexcept = 0; virtual std::string error_detail_v() const noexcept = 0; - virtual void - set_colorset_v(size_t color_num, - const std::array &color_ptrs) noexcept = 0; + virtual void set_colorset_v( + size_t color_num, + const std::array &color_ptrs) noexcept = 0; virtual void set_task_v(size_t task_num, const std::array *data) noexcept = 0; @@ -104,6 +109,6 @@ class gpu_interface { virtual size_t local_work_group_size_v() const noexcept = 0; }; -} // namespace gpu_wrapper +} // namespace gpu_wrapper -#endif // SLOPECRAFT_UTILITIES_GPUWRAPPER_GPUINTERFACE_H \ No newline at end of file +#endif // SLOPECRAFT_UTILITIES_GPUWRAPPER_GPUINTERFACE_H \ No newline at end of file diff --git a/utilities/GPUWrapper/None/GPU_interface.cpp b/utilities/GPUWrapper/None/GPU_interface.cpp index 783ba06b..94844141 100644 --- a/utilities/GPUWrapper/None/GPU_interface.cpp +++ b/utilities/GPUWrapper/None/GPU_interface.cpp @@ -1,11 +1,12 @@ #include "../GPU_interface.h" +#include const char *gpu_wrapper::api_name() noexcept { return "None"; } size_t gpu_wrapper::platform_num() noexcept { return 0; } -gpu_wrapper::platform_wrapper * -gpu_wrapper::platform_wrapper::create(size_t, int *) noexcept { +gpu_wrapper::platform_wrapper *gpu_wrapper::platform_wrapper::create( + size_t, int *) noexcept { return nullptr; } @@ -14,18 +15,24 @@ void gpu_wrapper::platform_wrapper::destroy( return; } -gpu_wrapper::device_wrapper * -gpu_wrapper::device_wrapper::create(platform_wrapper *, size_t, - int *) noexcept { +gpu_wrapper::device_wrapper *gpu_wrapper::device_wrapper::create( + platform_wrapper *, size_t, int *) noexcept { return nullptr; } void gpu_wrapper::device_wrapper::destroy(device_wrapper *) noexcept { return; } -gpu_wrapper::gpu_interface * -gpu_wrapper::gpu_interface::create(platform_wrapper *, - device_wrapper *) noexcept { +gpu_wrapper::gpu_interface *gpu_wrapper::gpu_interface::create( + platform_wrapper *, device_wrapper *) noexcept { return nullptr; } -void gpu_wrapper::gpu_interface::destroy(gpu_interface *) noexcept { return; } \ No newline at end of file +gpu_wrapper::gpu_interface *gpu_wrapper::gpu_interface::create( + gpu_wrapper::platform_wrapper *pw, gpu_wrapper::device_wrapper *dw, + std::pair &err) noexcept { + err.first = INT_MAX; + err.second = "VisualCraft is built without any GPU api."; + return nullptr; +} + +void gpu_wrapper::gpu_interface::destroy(gpu_interface *) noexcept { return; } diff --git a/utilities/GPUWrapper/OpenCL/CMakeLists.txt b/utilities/GPUWrapper/OpenCL/CMakeLists.txt index e47b4301..072774e9 100644 --- a/utilities/GPUWrapper/OpenCL/CMakeLists.txt +++ b/utilities/GPUWrapper/OpenCL/CMakeLists.txt @@ -2,34 +2,56 @@ find_package(OpenCL 3.0 REQUIRED) message(STATUS "Configuring OpenCL") -include(${CMAKE_SOURCE_DIR}/cmake/find_ResourceCreator.cmake) -include(${CMAKE_SOURCE_DIR}/3rdParty/ResourceCreator.cmake/ResourceCreator.cmake) -add_library(OCLWrapper STATIC +target_sources(GPUInterface PRIVATE OCLWrapper.h OCLWrapper.cpp GPU_interface.cpp) -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set_target_properties(OCLWrapper PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() +# convert ColorDiff.cl to a resource file (not windows rc). It is generated by ResourceCreator.cmake (3rd party cmake lib) +add_resource_config_time(ColorManip_cl_rc ColorDiff.cl) + +target_include_directories(GPUInterface PUBLIC ${CMAKE_SOURCE_DIR}) +target_include_directories(GPUInterface INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_features(GPUInterface PUBLIC cxx_std_20) + +find_package(Eigen3 REQUIRED) +target_link_libraries(GPUInterface + PUBLIC + OpenCL::OpenCL + ColorManip_cl_rc + Eigen3::Eigen +) + +message(STATUS "OpenCL_INCLUDE_DIR = ${OpenCL_INCLUDE_DIR}") +message(STATUS "OpenCL_INCLUDE_DIRS = ${OpenCL_INCLUDE_DIRS}") + +if (NOT EXISTS ${OpenCL_INCLUDE_DIR}/CL/cl.hpp) + target_compile_definitions(GPUInterface PRIVATE SLOPECRAFT_NO_CL_HPP) + message(STATUS "Failed to find cl.hpp. This file doesn't exists in opencl include dir. + SlopeCraft assumes that opencl.hpp exists, and will try to include it.") + + # file(GLOB_RECURSE cl_h_file "${OpenCL_INCLUDE_DIR}/*/cl.h") + # file(GLOB_RECURSE cl_hpp_file "${OpenCL_INCLUDE_DIR}/*/cl.hpp") + # file(GLOB_RECURSE opencl_hpp_file "${OpenCL_INCLUDE_DIR}/*/opencl.hpp") + # message(WARNING "CL/cl.hpp will not be found correctly.") + # message(STATUS "Manually found cl_h_file = ${cl_h_file}") + # message(STATUS "Manually found cl_hpp_file = ${cl_hpp_file}") + # message(STATUS "Manually found opencl_hpp_file = ${opencl_hpp_file}") + file(GLOB_RECURSE all_cl_files "${OpenCL_INCLUDE_DIR}/CL/*") + message(STATUS "All opencl headers: ${all_cl_files}") +else () + message(STATUS "Found opencl C++ header: ${OpenCL_INCLUDE_DIR}/CL/cl.hpp") +endif () -add_resource(ColorManip_cl_rc ColorDiff.cl) -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/__rsc_ColorManip_cl_rc.c) - -mark_as_advanced(SlopeCraft_resource_file_to_touch_and_remove) -list(APPEND SlopeCraft_resource_file_to_touch_and_remove ${CMAKE_CURRENT_BINARY_DIR}/__rsc_ColorManip_cl_rc.c) - -target_include_directories(OCLWrapper PRIVATE ${SlopeCraft_Eigen3_include_dir}) -target_include_directories(OCLWrapper PUBLIC ${CMAKE_SOURCE_DIR}) -target_include_directories(OCLWrapper INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_features(OCLWrapper PUBLIC cxx_std_20) -target_link_libraries(OCLWrapper PUBLIC OpenCL::OpenCL ColorManip_cl_rc) - -target_link_libraries(GPUInterface PUBLIC OCLWrapper) target_compile_definitions(GPUInterface PUBLIC SLOPECRAFT_HAVE_OPENCL) -if(${LINUX}) - set_target_properties(GPUInterface PROPERTIES TARGET_INDENPENDENT_CODE TRUE) -endif() \ No newline at end of file +find_program(clang_exe NAMES "clang") +if (clang_exe) + set(binfile ${CMAKE_CURRENT_BINARY_DIR}/ColorDiff.bc) + add_custom_target(test_opencl_source ALL + COMMAND ${clang_exe} ${CMAKE_CURRENT_SOURCE_DIR}/ColorDiff.cl -c -o ${binfile} + SOURCES ColorDiff.cl + BYPRODUCTS ${binfile}) +endif () \ No newline at end of file diff --git a/utilities/GPUWrapper/OpenCL/ColorDiff.cl b/utilities/GPUWrapper/OpenCL/ColorDiff.cl index 4de0fef6..4d126397 100644 --- a/utilities/GPUWrapper/OpenCL/ColorDiff.cl +++ b/utilities/GPUWrapper/OpenCL/ColorDiff.cl @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -21,8 +21,8 @@ This file is part of SlopeCraft. */ #define SC_OCL_SPOT_NAN true -//#define SC_OCL_SPOT_NAN false - +__constant const float pi_fp32 = M_PI; +// #define SC_OCL_SPOT_NAN false /// Function definations // compute sum(v*v) @@ -57,7 +57,6 @@ float color_diff_RGB_XYZ(float3 RGB1, float3 RGB2) { } float color_diff_RGB_Better(float3 rgb1, float3 rgb2) { - const float w_r = 1.0f, w_g = 2.0f, w_b = 1.0f; const float3 w_vec3 = {w_r, w_g, w_b}; const float thre = 1e-4f; @@ -69,20 +68,20 @@ float color_diff_RGB_Better(float3 rgb1, float3 rgb2) { const float SigmaRGB = (sum3(rgb1) + sum3(rgb2)) / 3.0f; const float3 S_rgb_vec3 = fmin((rgb1 + rgb2) / (SigmaRGB + thre), one_vec3); - - - if(SC_OCL_SPOT_NAN&&have_nan(S_rgb_vec3)) { + + if (SC_OCL_SPOT_NAN && have_nan(S_rgb_vec3)) { printf("S_rgb_vec3 contains nan.\n"); return NAN; } const float sumRGBSquare = dot(rgb1, rgb2); - const float theta = - 2.0f / M_PI * acos((sumRGBSquare * rsqrt(SqrModSquare + thre)) / 1.01f); + const float theta = 2.0f / pi_fp32 * + acos((sumRGBSquare * rsqrt(SqrModSquare + thre)) / 1.01f); - if(SC_OCL_SPOT_NAN&&isnan(theta)) { - printf("theta is nan. sumRGBSquare = %f, SqrModSquare = %f.\n",sumRGBSquare,SqrModSquare); + if (SC_OCL_SPOT_NAN && isnan(theta)) { + printf("theta is nan. sumRGBSquare = %f, SqrModSquare = %f.\n", + sumRGBSquare, SqrModSquare); return NAN; } @@ -92,8 +91,8 @@ float color_diff_RGB_Better(float3 rgb1, float3 rgb2) { const float3 S_t_rgb_vec3 = OnedDelta_rgb_vec3 / sumOnedDelta * S_rgb_vec3 * S_rgb_vec3; - - if(SC_OCL_SPOT_NAN&&have_nan(S_t_rgb_vec3)) { + + if (SC_OCL_SPOT_NAN && have_nan(S_t_rgb_vec3)) { printf("S_t_rgb_vec3 contains nan.\n"); return NAN; } @@ -108,13 +107,13 @@ float color_diff_RGB_Better(float3 rgb1, float3 rgb2) { S_rgb_vec3 * S_rgb_vec3 * delta_rgb_vec3 * delta_rgb_vec3 * w_vec3; const float part1 = sum3(SS_w_delta_delta_vec3) / sum3(w_vec3); - if(SC_OCL_SPOT_NAN&&isnan(part1)) { + if (SC_OCL_SPOT_NAN && isnan(part1)) { printf("part1 is nan.\n"); return NAN; } const float part2 = S_theta * S_ratio * theta * theta; - if(SC_OCL_SPOT_NAN&&isnan(part2)) { + if (SC_OCL_SPOT_NAN && isnan(part2)) { printf("part2 is nan.\n"); return NAN; } @@ -165,10 +164,9 @@ float color_diff_Lab94(float3 lab1_vec3, float3 lab2_vec3) { } float color_diff_Lab00(float3 lab1_vec3, float3 lab2_vec3) { - - const float kL = 1.0; - const float kC = 1.0; - const float kH = 1.0; + const float kL = 1.0f; + const float kC = 1.0f; + const float kH = 1.0f; const float L1 = lab1_vec3[0]; const float a1 = lab1_vec3[1]; const float b1 = lab1_vec3[2]; @@ -192,16 +190,14 @@ float color_diff_Lab00(float3 lab1_vec3, float3 lab2_vec3) { else h1p = atan2(b1, a1p); - if (h1p < 0) - h1p += 2 * M_PI; + if (h1p < 0) h1p += 2 * pi_fp32; if (b2 == 0 && a2p == 0) h2p = 0; else h2p = atan2(b2, a2p); - - if (h2p < 0) - h2p += 2 * M_PI; + + if (h2p < 0) h2p += 2 * pi_fp32; float dLp = L2 - L1; float dCp = C2p - C1p; @@ -233,20 +229,20 @@ float color_diff_Lab00(float3 lab1_vec3, float3 lab2_vec3) { mhp = (h1p + h2p - radians(360.0f)) / 2; } - float T = 1 - 0.17 * cos(mhp - radians(30.0f)) + 0.24 * cos(2 * mhp) + - 0.32 * cos(3 * mhp + radians(6.0f)) - - 0.20 * cos(4 * mhp - radians(63.0f)); + float T = 1 - 0.17f * cos(mhp - radians(30.0f)) + 0.24f * cos(2 * mhp) + + 0.32f * cos(3 * mhp + radians(6.0f)) - + 0.20f * cos(4 * mhp - radians(63.0f)); float dTheta = radians(30.0f) * exp(-square((mhp - radians(275.0f)) / radians(25.0f))); float RC = 2 * sqrt(pow(mCp, 7) / (pow(25.0f, 7.0f) + pow(mCp, 7.0f))); float square_mLp_minus_50 = square(mLp - 50); - float SL = 1 + 0.015 * square_mLp_minus_50 / sqrt(20 + square_mLp_minus_50); + float SL = 1 + 0.015f * square_mLp_minus_50 / sqrt(20 + square_mLp_minus_50); - float SC = 1 + 0.045 * mCp; + float SC = 1 + 0.045f * mCp; - float SH = 1 + 0.015 * mCp * T; + float SH = 1 + 0.015f * mCp * T; float RT = -RC * sin(2 * dTheta); @@ -257,51 +253,54 @@ float color_diff_Lab00(float3 lab1_vec3, float3 lab2_vec3) { return Diffsquare; } - -#define SC_MAKE_COLORDIFF_KERNEL_FUN(kfun_name, diff_fun) \ - __kernel void kfun_name( \ - __global const float *colorset_colors, const ushort colorset_size, \ - __global const float *unconverted_colors, \ - __global ushort *result_idx_dst, __global float *result_diff_dst) { \ - const size_t global_idx = get_global_id(0); \ - const float3 unconverted = {unconverted_colors[global_idx * 3 + 0], \ - unconverted_colors[global_idx * 3 + 1], \ - unconverted_colors[global_idx * 3 + 2]}; \ - if (true && have_nan(unconverted)) { \ - printf("Nan spotted at unconverted. Unconverted = {%f,%f,%f}, " \ - "get_global_id = %llu.\n", \ - unconverted[0], unconverted[1], unconverted[2], global_idx); \ - return; \ - } \ - \ - ushort result_idx = USHRT_MAX - 1; \ - float result_diff = FLT_MAX / 2; \ - \ - for (ushort idx = 0; idx < colorset_size; idx++) { \ - const float3 color_ava = {colorset_colors[idx * 3 + 0], \ - colorset_colors[idx * 3 + 1], \ - colorset_colors[idx * 3 + 2]}; \ - if (true && have_nan(color_ava)) { \ - printf("Nan spotted at color_ava. color_ava = {%f,%f,%f}, " \ - "get_global_id = %llu.\n", \ - color_ava[0], color_ava[1], color_ava[2], global_idx); \ - return; \ - } \ - \ - const float diff_sq = diff_fun(color_ava, unconverted); \ - if (true && isnan(diff_sq)) { \ - printf("Spotted nan at idx = %u.\n", (idx)); \ - return; \ - } \ - if (result_diff > diff_sq) { \ - /* this branch may be optimized */ \ - result_idx = idx; \ - result_diff = diff_sq; \ - } \ - } \ - \ - result_idx_dst[global_idx] = result_idx; \ - result_diff_dst[global_idx] = result_diff; \ +#define SC_MAKE_COLORDIFF_KERNEL_FUN(kfun_name, diff_fun) \ + __kernel void kfun_name( \ + __global const float *colorset_colors, const ushort colorset_size, \ + __global const float *unconverted_colors, \ + __global ushort *result_idx_dst, __global float *result_diff_dst) { \ + const size_t global_idx = get_global_id(0); \ + const float3 unconverted = {unconverted_colors[global_idx * 3 + 0], \ + unconverted_colors[global_idx * 3 + 1], \ + unconverted_colors[global_idx * 3 + 2]}; \ + if (true && have_nan(unconverted)) { \ + printf( \ + "Nan spotted at unconverted. Unconverted = {%f,%f,%f}, " \ + "get_global_id = %u.\n", \ + unconverted[0], unconverted[1], unconverted[2], \ + (unsigned int)global_idx); \ + return; \ + } \ + \ + ushort result_idx = USHRT_MAX - 1; \ + float result_diff = FLT_MAX / 2; \ + \ + for (ushort idx = 0; idx < colorset_size; idx++) { \ + const float3 color_ava = {colorset_colors[idx * 3 + 0], \ + colorset_colors[idx * 3 + 1], \ + colorset_colors[idx * 3 + 2]}; \ + if (true && have_nan(color_ava)) { \ + printf( \ + "Nan spotted at color_ava. color_ava = {%f,%f,%f}, " \ + "get_global_id = %u.\n", \ + color_ava[0], color_ava[1], color_ava[2], \ + (unsigned int)global_idx); \ + return; \ + } \ + \ + const float diff_sq = diff_fun(color_ava, unconverted); \ + if (true && isnan(diff_sq)) { \ + printf("Spotted nan at idx = %u.\n", (idx)); \ + return; \ + } \ + if (result_diff > diff_sq) { \ + /* this branch may be optimized */ \ + result_idx = idx; \ + result_diff = diff_sq; \ + } \ + } \ + \ + result_idx_dst[global_idx] = result_idx; \ + result_diff_dst[global_idx] = result_diff; \ } SC_MAKE_COLORDIFF_KERNEL_FUN(match_color_RGB, color_diff_RGB_XYZ) diff --git a/utilities/GPUWrapper/OpenCL/GPU_interface.cpp b/utilities/GPUWrapper/OpenCL/GPU_interface.cpp index 7775e25a..685ab0ac 100644 --- a/utilities/GPUWrapper/OpenCL/GPU_interface.cpp +++ b/utilities/GPUWrapper/OpenCL/GPU_interface.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -49,7 +49,6 @@ void ::gpu_wrapper::platform_wrapper::destroy(platform_wrapper *pw) noexcept { } ocl_warpper::ocl_device::ocl_device(cl::Device __dev) : device(__dev) { - this->name = __dev.getInfo(&this->err); } @@ -72,10 +71,11 @@ ::gpu_wrapper::device_wrapper * ::gpu_wrapper::device_wrapper::create( void ::gpu_wrapper::device_wrapper::destroy(device_wrapper *dw) noexcept { delete static_cast<::ocl_warpper::ocl_device *>(dw); } - -gpu_wrapper::gpu_interface * -gpu_wrapper::gpu_interface::create(gpu_wrapper::platform_wrapper *pw, - gpu_wrapper::device_wrapper *dw) noexcept { +gpu_wrapper::gpu_interface *gpu_wrapper::gpu_interface::create( + gpu_wrapper::platform_wrapper *pw, gpu_wrapper::device_wrapper *dw, + std::pair &err) noexcept { + err.first = 0; + err.second.clear(); ocl_warpper::ocl_platform *plat = static_cast(pw); ocl_warpper::ocl_device *dev = static_cast(dw); @@ -84,6 +84,8 @@ gpu_wrapper::gpu_interface::create(gpu_wrapper::platform_wrapper *pw, new ocl_warpper::ocl_resource(plat->platform, dev->device); if (!ret->ok()) { + err.first = ret->error_code(); + err.second = ret->error_detail(); delete ret; return nullptr; } @@ -91,8 +93,14 @@ gpu_wrapper::gpu_interface::create(gpu_wrapper::platform_wrapper *pw, return static_cast<::gpu_wrapper::gpu_interface *>(ret); } -void ::gpu_wrapper::gpu_interface::destroy(gpu_interface *gi) noexcept { +gpu_wrapper::gpu_interface *gpu_wrapper::gpu_interface::create( + gpu_wrapper::platform_wrapper *pw, + gpu_wrapper::device_wrapper *dw) noexcept { + std::pair temp; + return create(pw, dw, temp); +} +void ::gpu_wrapper::gpu_interface::destroy(gpu_interface *gi) noexcept { delete static_cast(gi); return; } diff --git a/utilities/GPUWrapper/OpenCL/OCLWrapper.cpp b/utilities/GPUWrapper/OpenCL/OCLWrapper.cpp index 3cad951e..f0ded588 100644 --- a/utilities/GPUWrapper/OpenCL/OCLWrapper.cpp +++ b/utilities/GPUWrapper/OpenCL/OCLWrapper.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,14 +22,16 @@ This file is part of SlopeCraft. #include "OCLWrapper.h" #include "../GPU_interface.h" +#include +#include -#include #include -#include #include +extern "C" { extern const unsigned char ColorManip_cl_rc[]; extern const unsigned int ColorManip_cl_rc_length; +} size_t ocl_warpper::platform_num() noexcept { cl_uint ret; @@ -45,7 +47,6 @@ size_t ocl_warpper::platform_num() noexcept { cl::Platform private_fun_get_platform(size_t platform_idx, cl_int &err) noexcept { - constexpr size_t buffersize = 128; cl_platform_id plats[buffersize]; @@ -67,12 +68,10 @@ cl::Platform private_fun_get_platform(size_t platform_idx, ocl_warpper::ocl_platform::ocl_platform(size_t idx) { this->platform = private_fun_get_platform(idx, this->err); - if (this->err != CL_SUCCESS) - return; + if (this->err != CL_SUCCESS) return; this->name = this->platform.getInfo(&this->err); - if (this->err != CL_SUCCESS) - return; + if (this->err != CL_SUCCESS) return; this->err = this->platform.getDevices(CL_DEVICE_TYPE_ALL, &this->devices); } @@ -112,26 +111,54 @@ void ocl_warpper::ocl_resource::init_resource() noexcept { return; } - this->queue = - cl::CommandQueue(this->context, this->device, NULL, &this->error); + this->queue = cl::CommandQueue(this->context, this->device, + cl::QueueProperties::None, &this->error); if (!this->ok()) { this->err_msg = "Failed to create command queue."; return; } +// Here the code differs according to if there is cl.hpp. API to create +// cl::Program is different in cl.hpp and opencl.hpp. I don't know why there are +// 2 differernt opencl C++ bindings. +#ifndef SLOPECRAFT_NO_CL_HPP + // This works with cl.hpp std::pair src; src.first = (const char *)ColorManip_cl_rc; src.second = ColorManip_cl_rc_length; - this->program = cl::Program(this->context, {src}, &this->error); + this->program = cl::Program{this->context, {src}, &this->error}; +#else + // This is for opencl.hpp + std::string source_code{(const char *)ColorManip_cl_rc, + ColorManip_cl_rc_length}; + source_code.push_back('\0'); + this->program = cl::Program{ + this->context, std::vector{source_code}, &this->error}; +#endif + if (!this->ok()) { this->err_msg = "Failed to create program with source files."; return; } - this->error = this->program.build(); + this->error = this->program.build( + "-cl-mad-enable -cl-unsafe-math-optimizations " + "-cl-single-precision-constant", + nullptr, nullptr); if (!this->ok()) { - this->err_msg = "Failed to build program."; + cl_int ec_get_build_log{CL_SUCCESS}; + auto build_log = this->program.getBuildInfo( + this->device, &ec_get_build_log); + if (ec_get_build_log == CL_SUCCESS) { + this->err_msg = + std::format("Failed to build program. Build log:\n{}", build_log); + } else { + this->err_msg = std::format( + "Failed to build program, and then failed to retrieve build log with " + "error code {}", + ec_get_build_log); + } return; } @@ -226,8 +253,9 @@ void ocl_warpper::ocl_resource::resize_task(size_t task_num) noexcept { this->context, this->task.rawcolor_f32_3_device, task_f32_3_required_bytes, CL_MEM_READ_ONLY, false); if (!this->ok()) { - this->err_msg = "Failed to allocate device memory for " - "this->task.rawcolor_f32_3_device."; + this->err_msg = + "Failed to allocate device memory for " + "this->task.rawcolor_f32_3_device."; return; } @@ -235,8 +263,9 @@ void ocl_warpper::ocl_resource::resize_task(size_t task_num) noexcept { this->context, this->task.result_idx_u16_device, result_idx_required_bytes, CL_MEM_WRITE_ONLY, false); if (!this->ok()) { - this->err_msg = "Failed to allocate device memory for " - "this->task.result_idx_u16_device."; + this->err_msg = + "Failed to allocate device memory for " + "this->task.result_idx_u16_device."; return; } @@ -244,8 +273,9 @@ void ocl_warpper::ocl_resource::resize_task(size_t task_num) noexcept { this->context, this->task.result_diff_f32_device, result_diff_required_bytes, CL_MEM_WRITE_ONLY, false); if (!this->ok()) { - this->err_msg = "Failed to allocate device memory for " - "this->task.result_diff_f32_device."; + this->err_msg = + "Failed to allocate device memory for " + "this->task.result_diff_f32_device."; return; } return; @@ -255,8 +285,7 @@ void ocl_warpper::ocl_resource::resize_colorset(size_t color_num) noexcept { this->error = private_fun_change_buf_size( this->context, this->colorset.colorset_float3, color_num * 3 * sizeof(float), CL_MEM_READ_ONLY, false); - if (!this->ok()) - return; + if (!this->ok()) return; this->colorset.colorset_color_num = color_num; } @@ -294,26 +323,25 @@ void ocl_warpper::ocl_resource::set_colorset( } } -cl::Kernel * -ocl_warpper::ocl_resource::kernel_by_algo(::SCL_convertAlgo algo) noexcept { - +cl::Kernel *ocl_warpper::ocl_resource::kernel_by_algo( + ::SCL_convertAlgo algo) noexcept { switch (algo) { - case SCL_convertAlgo::RGB: - return &this->k_RGB; - case SCL_convertAlgo::RGB_Better: - return &this->k_RGB_Better; - case SCL_convertAlgo::HSV: - return &this->k_HSV; - case SCL_convertAlgo::Lab94: - return &this->k_Lab94; - case SCL_convertAlgo::Lab00: - return &this->k_Lab00; - case SCL_convertAlgo::XYZ: - return &this->k_XYZ; - default: - return nullptr; - } - return nullptr; + case SCL_convertAlgo::RGB: + return &this->k_RGB; + case SCL_convertAlgo::RGB_Better: + return &this->k_RGB_Better; + case SCL_convertAlgo::HSV: + return &this->k_HSV; + case SCL_convertAlgo::Lab94: + return &this->k_Lab94; + case SCL_convertAlgo::Lab00: + return &this->k_Lab00; + case SCL_convertAlgo::XYZ: + return &this->k_XYZ; + default: + return nullptr; + } + // return nullptr; } void ocl_warpper::ocl_resource::set_task( @@ -403,7 +431,6 @@ void ocl_warpper::ocl_resource::set_task(const std::array *src, */ void ocl_warpper::ocl_resource::set_args(::SCL_convertAlgo algo) noexcept { - this->wait(); if (!this->ok()) { this->err_msg = "Failed to wait."; @@ -472,7 +499,6 @@ cl_int private_fun_change_buf_size(cl::Context &context, cl::Buffer &buf, void ocl_warpper::ocl_resource::execute(::SCL_convertAlgo algo, bool wait) noexcept { - this->set_args(algo); if (!this->ok()) { this->err_msg = "Failed to set args."; diff --git a/utilities/GPUWrapper/OpenCL/OCLWrapper.h b/utilities/GPUWrapper/OpenCL/OCLWrapper.h index 2021c1ec..22cc375a 100644 --- a/utilities/GPUWrapper/OpenCL/OCLWrapper.h +++ b/utilities/GPUWrapper/OpenCL/OCLWrapper.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,11 +22,15 @@ This file is part of SlopeCraft. #ifndef SLOPECRAFT_UTILITIES_COLORDIRR_OPENCL_H #define SLOPECRAFT_UTILITIES_COLORDIRR_OPENCL_H +#ifdef SLOPECRAFT_NO_CL_HPP +#include +#else +#include +#endif #include "../GPU_interface.h" -#include #include -#include +#include #include #include @@ -35,7 +39,7 @@ namespace ocl_warpper { size_t platform_num() noexcept; class ocl_resource : public ::gpu_wrapper::gpu_interface { -public: + public: struct task_rcs { // std::vector buf_unconverted_ARGB_host; cl::Buffer rawcolor_f32_3_device; @@ -50,7 +54,7 @@ class ocl_resource : public ::gpu_wrapper::gpu_interface { uint16_t colorset_color_num{0}; }; -private: + private: cl_int error{CL_SUCCESS}; std::string err_msg{""}; cl::Platform platform; @@ -71,10 +75,10 @@ class ocl_resource : public ::gpu_wrapper::gpu_interface { // buffers to for colorset -private: + private: void init_resource() noexcept; -public: + public: inline int error_code() const noexcept { return this->error; } inline bool ok() const noexcept { return (this->error == CL_SUCCESS); } @@ -82,7 +86,7 @@ class ocl_resource : public ::gpu_wrapper::gpu_interface { return this->err_msg; } -public: + public: ocl_resource(); ocl_resource(cl::Platform plat, cl::Device dev); @@ -120,7 +124,7 @@ class ocl_resource : public ::gpu_wrapper::gpu_interface { return this->local_work_group_size(); } -private: + private: void resize_task(size_t task_num) noexcept; void resize_colorset(size_t color_num) noexcept; @@ -128,7 +132,7 @@ class ocl_resource : public ::gpu_wrapper::gpu_interface { void set_args(::SCL_convertAlgo algo) noexcept; -public: + public: // overrided functions const char *api_v() const noexcept override { return "OpenCL"; } @@ -173,7 +177,7 @@ class ocl_resource : public ::gpu_wrapper::gpu_interface { }; class ocl_platform : public ::gpu_wrapper::platform_wrapper { -public: + public: ocl_platform() = delete; ocl_platform(size_t idx); cl::Platform platform; @@ -190,7 +194,7 @@ class ocl_platform : public ::gpu_wrapper::platform_wrapper { }; class ocl_device : public ::gpu_wrapper::device_wrapper { -public: + public: ocl_device() = delete; ocl_device(cl::Device); @@ -201,6 +205,6 @@ class ocl_device : public ::gpu_wrapper::device_wrapper { const char *name_v() const noexcept override { return this->name.c_str(); } }; -} // namespace ocl_warpper +} // namespace ocl_warpper -#endif // SLOPECRAFT_UTILITIES_COLORDIRR_OPENCL_H \ No newline at end of file +#endif // SLOPECRAFT_UTILITIES_COLORDIRR_OPENCL_H \ No newline at end of file diff --git a/utilities/GPUWrapper/Vulkan/CMakeLists.txt b/utilities/GPUWrapper/Vulkan/CMakeLists.txt new file mode 100644 index 00000000..20ac7149 --- /dev/null +++ b/utilities/GPUWrapper/Vulkan/CMakeLists.txt @@ -0,0 +1,40 @@ +find_package(Vulkan + COMPONENTS glslc + REQUIRED) +find_package(Eigen3 REQUIRED) + +include(${CMAKE_SOURCE_DIR}/cmake/optional_deps/kompute.cmake) +find_package(kompute REQUIRED) + +find_package(tl-expected REQUIRED) + +target_sources(GPUInterface PRIVATE GPU_interface.cpp) +target_link_libraries(GPUInterface PUBLIC + Vulkan::Vulkan + kompute::kompute + Eigen3::Eigen + tl::expected +) +target_compile_features(GPUInterface PRIVATE cxx_std_23) + +get_target_property(glslc_exe Vulkan::glslc LOCATION) +message(STATUS "glslc found at ${glslc_exe}") + +set(shader_filename "${CMAKE_CURRENT_BINARY_DIR}/compute.spv") + +message(STATUS "Building compute shader to spirv...") +execute_process(COMMAND ${glslc_exe} -fshader-stage=compute compute.glsl -o ${shader_filename} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + + COMMAND_ERROR_IS_FATAL ANY) + +add_resource_config_time(VkComputeShaderSPIRV_rc ${shader_filename}) +target_link_libraries(GPUInterface PRIVATE VkComputeShaderSPIRV_rc) + +set(shader_test_filename "${CMAKE_CURRENT_BINARY_DIR}/compute_test.spv") +add_custom_target(GPUInterface_shader_test ALL + COMMAND ${glslc_exe} -fshader-stage=compute compute.glsl -o ${shader_test_filename} + SOURCES compute.glsl + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + BYPRODUCTS ${shader_test_filename} + COMMENT "Building compute shader ${shader_test_filename}") \ No newline at end of file diff --git a/utilities/GPUWrapper/Vulkan/GPU_interface.cpp b/utilities/GPUWrapper/Vulkan/GPU_interface.cpp new file mode 100644 index 00000000..2154c5a9 --- /dev/null +++ b/utilities/GPUWrapper/Vulkan/GPU_interface.cpp @@ -0,0 +1,495 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "GPUWrapper/GPU_interface.h" + +extern "C" { +extern const unsigned char VkComputeShaderSPIRV_rc[]; +extern const unsigned int VkComputeShaderSPIRV_rc_length; +} + +namespace gpu_wrapper { + +enum class error_code : int { + ok = 0, + create_instance_failure = 2000000000, + create_instance_failure_invalid_platform_index = 2000000001, + create_physical_device_failure = 2000001000, + create_logical_device_failure = 2000002000, + create_logical_device_failure_no_suitable_queue_family = 2000002001, + create_kompute_manager_failure = 2000003000, + manager_set_colorset_failure = 2000004000, + manager_set_tasks_failure = 2000005000, + manager_execute_failure = 2000006000, + manager_wait_failure = 2000007000, +}; + +struct error_pair { + error_code code; + std::string message; +}; + +void set_error_code(int* dst_nullable, error_code code) noexcept { + if (dst_nullable) { + *dst_nullable = int(code); + } +} + +constexpr uint32_t ceil_up_to(uint32_t num, uint32_t align) noexcept { + if (num % align == 0) { + return num; + } + + return (num / align + 1) * align; +} + +std::vector get_spirv() noexcept { + assert(VkComputeShaderSPIRV_rc_length % 4 == 0); + auto data = reinterpret_cast(VkComputeShaderSPIRV_rc); + + return std::vector{ + data, data + size_t(VkComputeShaderSPIRV_rc_length / 4)}; +} + +tl::expected, error_pair> +create_vk_instance() noexcept { + vk::ApplicationInfo app_info{"VisualCraftL", VK_MAKE_VERSION(5, 0, 0), + "NoEngine", VK_MAKE_VERSION(1, 0, 0), + VK_API_VERSION_1_3}; + vk::InstanceCreateInfo ici; + ici.pApplicationInfo = &app_info; + std::array extension_names{ + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, + }; + ici.enabledExtensionCount = extension_names.size(); + ici.ppEnabledExtensionNames = extension_names.data(); + + std::array layer_names{ + //"VK_LAYER_LUNARG_assistant_layer", + //"VK_LAYER_LUNARG_standard_validation", + "VK_LAYER_KHRONOS_validation", + }; + ici.enabledLayerCount = layer_names.size(); + ici.ppEnabledLayerNames = layer_names.data(); + + try { + auto instance_r = vk::createInstance(ici); + auto instance = std::make_shared(instance_r); + assert(instance != nullptr); + return instance; + + } catch (const std::exception& e) { + std::print("Failed to create vulkan instance, exception details: {}", + e.what()); + return nullptr; + } +} + +const char* api_name() noexcept { return "Vulkan"; } + +size_t platform_num() noexcept { return 1; } + +class platform_impl : public platform_wrapper { + public: + platform_impl(std::shared_ptr i) : instance{i} {} + std::shared_ptr instance; + + const char* name_v() const noexcept final { return "Vulkan"; } + size_t num_devices_v() const noexcept final { + return this->instance->enumeratePhysicalDevices().size(); + } +}; + +platform_wrapper* platform_wrapper::create(size_t idx, + int* errorcode) noexcept { + if (idx != 0) { + set_error_code(errorcode, + error_code::create_instance_failure_invalid_platform_index); + return nullptr; + } + auto i_exp = create_vk_instance(); + if (i_exp) { + set_error_code(errorcode, error_code::ok); + return new platform_impl{i_exp.value()}; + } + set_error_code(errorcode, i_exp.error().code); + return nullptr; +} + +void platform_wrapper::destroy(gpu_wrapper::platform_wrapper* p) noexcept { + delete p; +} + +class device_impl : public device_wrapper { + public: + uint32_t device_index; + std::shared_ptr phy_device; + vk::PhysicalDeviceProperties properties; + std::shared_ptr device; + uint32_t selected_queue_family_index{}; + const char* name_v() const noexcept final { + return this->properties.deviceName; + } +}; + +std::optional select_queue_family( + std::span queue_family_props) noexcept { + std::vector indices; + indices.reserve(queue_family_props.size()); + + // for (auto [idx, family] : stdrange::enumerate_view{queue_family_props}) + for (size_t idx = 0; idx < queue_family_props.size(); idx++) { + auto& family = queue_family_props[idx]; + auto flags = family.queueFlags; + if (flags & vk::QueueFlagBits::eCompute) { + indices.emplace_back(idx); + } + } + if (indices.empty()) return std::nullopt; + + std::sort(indices.begin(), indices.end(), + [queue_family_props](uint32_t i, uint32_t j) -> bool { + const auto& a = queue_family_props[i]; + const auto& b = queue_family_props[j]; + if (a.queueFlags == b.queueFlags) { + return a.queueCount > b.queueCount; + } + const auto compute_required_flags = + vk::QueueFlagBits::eCompute | vk::QueueFlagBits::eTransfer; + const int a_compute = bool(a.queueFlags & compute_required_flags); + const int b_compute = bool(b.queueFlags & compute_required_flags); + + if (a_compute != b_compute) { + if (a_compute) + return true; + else + return false; + } + + assert(a_compute + b_compute == 2); + + const int a_graphics = + bool(a.queueFlags & vk::QueueFlagBits::eGraphics); + const int b_graphics = + bool(b.queueFlags & vk::QueueFlagBits::eGraphics); + + if (a_graphics != b_graphics) { + if (a_graphics) + return false; + else + return true; + } + + return a.queueCount > b.queueCount; + }); + + return indices.front(); +} + +device_wrapper* device_wrapper::create(gpu_wrapper::platform_wrapper* pw, + size_t idx, int* ec) noexcept { + auto& plat = dynamic_cast(*pw); + std::vector devices; + try { + devices = plat.instance->enumeratePhysicalDevices(); + } catch (const std::exception& e) { + std::println("Failed to enumerate physical devices, detail: {}", e.what()); + set_error_code(ec, error_code::create_physical_device_failure); + return nullptr; + } + try { + auto result = new device_impl; + result->device_index = idx; + result->phy_device = std::make_shared(devices[idx]); + auto& phy_device = *result->phy_device; + result->properties = phy_device.getProperties(); + { + auto qf_props = phy_device.getQueueFamilyProperties(); + + auto sqfi_opt = select_queue_family(qf_props); + if (!sqfi_opt) { + set_error_code( + ec, + error_code::create_logical_device_failure_no_suitable_queue_family); + delete result; + return nullptr; + } + result->selected_queue_family_index = sqfi_opt.value(); + } + { + vk::DeviceCreateInfo dci{}; + std::array extensions{ + VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, + VK_KHR_16BIT_STORAGE_EXTENSION_NAME, + }; + dci.enabledExtensionCount = extensions.size(); + dci.ppEnabledExtensionNames = extensions.data(); + + // check if 16 bit support is fully ok + vk::PhysicalDeviceFeatures2 pdf2; + vk::PhysicalDeviceVulkan11Features pdfv11; + pdf2.pNext = &pdfv11; + phy_device.getFeatures2(&pdf2); + assert(pdf2.features.shaderInt16); + assert(pdfv11.uniformAndStorageBuffer16BitAccess); + assert(pdfv11.storageBuffer16BitAccess); + dci.pEnabledFeatures = nullptr; + dci.pNext = &pdf2; + + // queue info + vk::DeviceQueueCreateInfo dqci; + dqci.queueCount = 1; + constexpr float prior = 1.0f; + dqci.pQueuePriorities = &prior; + dqci.queueFamilyIndex = result->selected_queue_family_index; + + dci.pQueueCreateInfos = &dqci; + dci.queueCreateInfoCount = 1; + + try { + result->device = + std::make_shared(phy_device.createDevice(dci)); + } catch (const std::exception& e) { + std::println("Failed to create logical device, details: {}", e.what()); + set_error_code(ec, error_code::create_logical_device_failure); + delete result; + return nullptr; + } + } + + set_error_code(ec, error_code::ok); + return result; + } catch (const std::exception& e) { + std::println("Failed to create logical device because {}", e.what()); + set_error_code(ec, error_code::create_logical_device_failure); + return nullptr; + } catch (...) { + std::println("Failed to create logical device because unknown exception."); + set_error_code(ec, error_code::create_logical_device_failure); + return nullptr; + } +} + +void device_wrapper::destroy(gpu_wrapper::device_wrapper* dw) noexcept { + delete dw; +} + +struct task_option { + uint32_t task_num; + uint32_t color_count; + uint32_t algo; +}; + +class gpu_impl : public gpu_interface { + private: + kp::Manager manager; + + // Its size won't be changed + std::shared_ptr> compute_option; + // Their sizes may be changed + std::shared_ptr> colorset; + // std::vector colorset_host; + std::shared_ptr> tasks; + // std::vector tasks_host; + std::shared_ptr> result_idx; + // std::vector result_idx_host; + std::shared_ptr> result_diff; + // std::vector result_diff_host; + + std::shared_ptr algorithm; + + std::shared_ptr sequence; + + size_t task_count{UINT64_MAX}; + uint16_t color_count{UINT16_MAX}; + + error_pair error{error_code::ok, ""}; + + template + void resize_tensor_size(std::shared_ptr> tensor, + size_t required_size) { + const size_t cur_size = tensor->size(); + if (cur_size >= required_size) { + return; + } + + tensor->rebuild(nullptr, required_size, sizeof(T)); + } + + public: + gpu_impl(platform_impl& pi, device_impl& di) + : manager{pi.instance, di.phy_device, di.device} { + // setup compute queues that ought to be setup by manager constructor + this->manager.computeQueueFamilyIndices() = { + di.selected_queue_family_index}; + this->manager.computeQueues() = {std::make_shared( + di.device->getQueue(di.selected_queue_family_index, 0))}; + + this->compute_option = this->manager.tensorT({task_option{}}); + + this->colorset = + this->manager.tensorT({0}, kp::Tensor::TensorTypes::eDevice); + this->tasks = + this->manager.tensorT({0}, kp::Tensor::TensorTypes::eDevice); + this->result_diff = + this->manager.tensorT({0}, kp::Tensor::TensorTypes::eDevice); + this->result_idx = + this->manager.tensorT({0}, kp::Tensor::TensorTypes::eDevice); + + this->algorithm = this->manager.algorithm( + {this->compute_option, this->colorset, this->tasks, this->result_diff, + this->result_idx}, + get_spirv()); + + this->sequence = this->manager.sequence(); + } + + const char* api_v() const noexcept final { return "Vulkan"; } + + void set_colorset_v( + size_t color_num, + const std::array& color_ptrs) noexcept final { + try { + this->color_count = color_num; + this->resize_tensor_size(this->colorset, 3 * color_num); + // this->colorset_host.resize(3 * color_num); + + Eigen::Map< + Eigen::Array> + trans{this->colorset->data(), static_cast(color_num), 3}; + + for (size_t c = 0; c < 3; c++) { + for (size_t r = 0; r < color_num; r++) { + trans(r, c) = color_ptrs[c][r]; + } + } + } catch (const std::exception& e) { + this->error.code = error_code::manager_set_colorset_failure; + this->error.message = + std::format("Failed to set colorset, detail: {}", e.what()); + } + } + + void set_task_v(size_t task_num, + const std::array* data) noexcept final { + try { + this->task_count = task_num; + this->resize_tensor_size(this->tasks, task_num * 3); + this->resize_tensor_size(this->result_idx, task_num); + this->resize_tensor_size(this->result_diff, task_num); + + memcpy(this->tasks->data(), data, task_num * sizeof(float[3])); + } catch (const std::exception& e) { + this->error.code = error_code::manager_set_tasks_failure; + this->error.message = + std::format("Failed to set tasks, detail: {}", e.what()); + } + } + + void wait_v() noexcept final { + try { + this->sequence->eval(); + } catch (const std::exception& e) { + this->error.code = error_code::manager_wait_failure; + this->error.message = + std::format("Failed to wait for results, detail: {}", e.what()); + } + // std::println("Computation result: ["); + // for (uint32_t i = 0; i < this->task_count; i++) { + // std::println("\t[task {}, result idx = {}, result diff = {}]", i, + // this->result_idx->data()[i], + // this->result_diff->data()[i]); + // } + // std::println("]"); + } + + void execute_v(::SCL_convertAlgo algo, bool wait) noexcept final { + try { + task_option option{uint32_t(this->task_count), this->color_count, + uint32_t(algo)}; + this->compute_option->setData({option}); + + this->sequence->record( + {this->compute_option, this->colorset, this->tasks}); + + const uint32_t local_wg_size = this->local_work_group_size_v(); + + const uint32_t work_group_num = + ceil_up_to(this->task_count, local_wg_size) / local_wg_size; + this->algorithm->rebuild( + {this->compute_option, this->colorset, this->tasks, this->result_diff, + this->result_idx}, + get_spirv(), {work_group_num, 1, 1}); + + this->sequence->record(this->algorithm) + ->record( + {this->result_diff, this->result_idx}); + + if (wait) { + this->wait_v(); + } + } catch (const std::exception& e) { + this->error.code = error_code::manager_execute_failure; + this->error.message = + std::format("Failed to execute computation, detail: {}", e.what()); + } + } + + size_t task_count_v() const noexcept final { return this->task_count; } + std::string device_vendor_v() const noexcept final { + return this->manager.getDeviceProperties().deviceName; + } + const uint16_t* result_idx_v() const noexcept final { + return this->result_idx->data(); + } + const float* result_diff_v() const noexcept final { + return this->result_diff->data(); + } + size_t local_work_group_size_v() const noexcept { return 64; } + + // error handling + int error_code_v() const noexcept final { return int(this->error.code); } + bool ok_v() const noexcept final { + return (this->error.code == error_code::ok); + } + std::string error_detail_v() const noexcept final { + return this->error.message; + } +}; + +gpu_interface* gpu_interface::create(gpu_wrapper::platform_wrapper* pw, + gpu_wrapper::device_wrapper* dw) noexcept { + std::pair temp; + return create(pw, dw, temp); +} +gpu_interface* gpu_interface::create( + platform_wrapper* pw, device_wrapper* dw, + std::pair& err) noexcept { + auto p = dynamic_cast(pw); + auto d = dynamic_cast(dw); + err.first = 0; + // err.second.clear(); + auto result = new gpu_impl{*p, *d}; + err.first = result->error_code_v(); + err.second = result->error_detail_v(); + if (err.first != 0) { + delete result; + return nullptr; + } + return result; +} + +void gpu_interface::destroy(gpu_wrapper::gpu_interface* gi) noexcept { + delete gi; +} +} // namespace gpu_wrapper \ No newline at end of file diff --git a/utilities/GPUWrapper/Vulkan/compute.glsl b/utilities/GPUWrapper/Vulkan/compute.glsl new file mode 100644 index 00000000..23646469 --- /dev/null +++ b/utilities/GPUWrapper/Vulkan/compute.glsl @@ -0,0 +1,343 @@ +#version 450 +//#extension GL_EXT_shader_8bit_storage: enable +#extension GL_EXT_shader_16bit_storage: enable + +#extension GL_EXT_debug_printf: enable + +#define NAN float(0.0f / 0.0f) +#define pi_fp32 float(radians(180)) + +//struct task_info { +// uint task_num; +// uint colorset_size; +// //uint16_t algo; +//}; + +const uint ALGO_RGB = 114; +const uint ALGO_RGB_BETTER = 82; +const uint ALGO_HSV = 72; +const uint ALGO_LAB94 = 108; +const uint ALGO_LAB00 = 76; +const uint ALGO_XYZ = 88; + +const bool SC_OCL_SPOT_NAN = true; + +layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout (std140, binding = 0) readonly buffer bufOpt { + uint task_num; + uint colorset_size; + uint algo; +} option; +layout (binding = 1) readonly buffer bufCC { + float colorset_colors[]; +}; +layout (binding = 2) readonly buffer bufUC { + float unconverted_colors[]; +}; +layout (binding = 3) writeonly buffer bufRDD { + float result_diff_dst[]; +}; +layout (binding = 4) writeonly buffer bufRID { + uint16_t result_idx_dst[]; +}; + + +bool have_nan(vec3 v) { + bool ret = false; + for (uint i = 0; i < 3; i++) { + ret = ret || isnan(v[i]); + } + return ret; +} + +float norm2(vec3 v) { return dot(v, v); } +float sum3(vec3 v) { return v[0] + v[1] + v[2]; } +float square(float s) { return s * s; } +vec3 square_vec3(vec3 v) { return v * v; } + + + +float color_diff_RGB_XYZ(vec3 RGB1, vec3 RGB2); +float color_diff_RGB_Better(vec3 rgb1, vec3 rgb2); +float color_diff_HSV(vec3 hsv1_vec3, vec3 hsv2_vec3); +float color_diff_Lab94(vec3 lab1_vec3, vec3 lab2_vec3); +float color_diff_Lab00(vec3 lab1_vec3, vec3 lab2_vec3); + +void compute(const uint global_idx); + +void main() { + + const uint global_idx = gl_GlobalInvocationID.x; + if (global_idx >= option.task_num) { + return; + } + //debugPrintfEXT("global_idx = %u, task_num = %u, colorset_size = %u, algo = %u", global_idx, option.colorset_size, option.algo); + compute(global_idx); +} + + +void compute(const uint global_idx) { + const vec3 unconverted = { unconverted_colors[global_idx * 3 + 0], + unconverted_colors[global_idx * 3 + 1], + unconverted_colors[global_idx * 3 + 2] }; + + + uint result_idx = 65535 - 1; + float result_diff = 1e30 / 2; + + for (uint idx = 0; idx < option.colorset_size; idx++) { + const vec3 color_ava = { colorset_colors[idx * 3 + 0], + colorset_colors[idx * 3 + 1], + colorset_colors[idx * 3 + 2] }; + if (true && have_nan(color_ava)) { + debugPrintfEXT( + "Nan spotted at color_ava. color_ava = {%f,%f,%f}, get_global_id = %u.n", + color_ava[0], color_ava[1], color_ava[2], + uint(global_idx)); + return; + } + + float diff_sq = 0; + + /* + + float color_diff_RGB_XYZ(vec3 RGB1, vec3 RGB2); + float color_diff_RGB_Better(vec3 rgb1, vec3 rgb2); + float color_diff_HSV(vec3 hsv1_vec3, vec3 hsv2_vec3); + float color_diff_Lab94(vec3 lab1_vec3, vec3 lab2_vec3); + float color_diff_Lab00(vec3 lab1_vec3, vec3 lab2_vec3); + */ + switch (uint(option.algo)) { + case ALGO_RGB_BETTER: + diff_sq = color_diff_RGB_Better(color_ava, unconverted); + break; + case ALGO_HSV: + diff_sq = color_diff_HSV(color_ava, unconverted); + break; + case ALGO_LAB94: + diff_sq = color_diff_Lab94(color_ava, unconverted); + break; + case ALGO_LAB00: + diff_sq = color_diff_Lab00(color_ava, unconverted); + break; + default : + diff_sq = color_diff_RGB_XYZ(color_ava, unconverted); + + } + if (true && isnan(diff_sq)) { + debugPrintfEXT("Spotted nan at idx = %u.n", (idx)); + return; + } + if (result_diff > diff_sq) { + /* this branch may be optimized */ + result_idx = idx; + result_diff = diff_sq; + } + } + + result_idx_dst[global_idx] = uint16_t(result_idx); + result_diff_dst[global_idx] = result_diff; + +} + +float color_diff_RGB_Better(vec3 rgb1, vec3 rgb2) { + const float w_r = 1.0f, w_g = 2.0f, w_b = 1.0f; + const vec3 w_vec3 = { w_r, w_g, w_b }; + const float thre = 1e-4f; + const vec3 one_vec3 = { 1, 1, 1 }; + + const float SqrModSquare = norm2(rgb1) * norm2(rgb2); + + const vec3 delta_rgb_vec3 = rgb1 - rgb2; + + const float SigmaRGB = (sum3(rgb1) + sum3(rgb2)) / 3.0f; + const vec3 S_rgb_vec3 = min((rgb1 + rgb2) / (SigmaRGB + thre), one_vec3); + + if (SC_OCL_SPOT_NAN && have_nan(S_rgb_vec3)) { + debugPrintfEXT("S_rgb_vec3 contains nan.\n"); + return NAN; + } + + const float sumRGBSquare = dot(rgb1, rgb2); + + const float theta = 2.0f / pi_fp32 * + acos((sumRGBSquare * inversesqrt(SqrModSquare + thre)) / 1.01f); + + if (SC_OCL_SPOT_NAN && isnan(theta)) { + debugPrintfEXT("theta is nan. sumRGBSquare = %f, SqrModSquare = %f.\n", + sumRGBSquare, SqrModSquare); + return NAN; + } + + const vec3 OnedDelta_rgb_vec3 = abs(delta_rgb_vec3) / (rgb1 + rgb2 + thre); + + const float sumOnedDelta = sum3(OnedDelta_rgb_vec3) + thre; + + const vec3 S_t_rgb_vec3 = + OnedDelta_rgb_vec3 / sumOnedDelta * S_rgb_vec3 * S_rgb_vec3; + + if (SC_OCL_SPOT_NAN && have_nan(S_t_rgb_vec3)) { + debugPrintfEXT("S_t_rgb_vec3 contains nan.\n"); + return NAN; + } + + const float S_theta = sum3(S_t_rgb_vec3); + + const vec3 rgb_max_vec3 = max(rgb1, rgb2); + const float S_ratio = + max(rgb_max_vec3[0], max(rgb_max_vec3[1], rgb_max_vec3[2])); + + const vec3 SS_w_delta_delta_vec3 = + S_rgb_vec3 * S_rgb_vec3 * delta_rgb_vec3 * delta_rgb_vec3 * w_vec3; + + const float part1 = sum3(SS_w_delta_delta_vec3) / sum3(w_vec3); + if (SC_OCL_SPOT_NAN && isnan(part1)) { + debugPrintfEXT("part1 is nan.\n"); + return NAN; + } + + const float part2 = S_theta * S_ratio * theta * theta; + if (SC_OCL_SPOT_NAN && isnan(part2)) { + debugPrintfEXT("part2 is nan.\n"); + return NAN; + } + + return part1 + part2; +} + +float color_diff_RGB_XYZ(vec3 RGB1, vec3 RGB2) { + return norm2(RGB1 - RGB2); +} + +float color_diff_HSV(vec3 hsv1_vec3, vec3 hsv2_vec3) { + const float h1 = hsv1_vec3[0]; + const float s1 = hsv1_vec3[1]; + const float v1 = hsv1_vec3[2]; + + const float h2 = hsv2_vec3[0]; + const float s2 = hsv2_vec3[1]; + const float v2 = hsv2_vec3[2]; + + const float sv_1 = s1 * v1; + const float sv_2 = s2 * v2; + + const float dX = 50.0f * (cos(h1) * sv_1 - cos(h2) * sv_2); + const float dY = 50.0f * (sin(h1) * sv_1 - sin(h2) * sv_2); + const float dZ = 50.0f * (v1 - v2); + + return dX * dX + dY * dY + dZ * dZ; +} + +float color_diff_Lab94(vec3 lab1_vec3, vec3 lab2_vec3) { + const float L1 = lab1_vec3[0]; + const float a1 = lab1_vec3[1]; + const float b1 = lab1_vec3[2]; + + const float L2 = lab2_vec3[0]; + const float a2 = lab2_vec3[1]; + const float b2 = lab2_vec3[2]; + + const float deltaL_2 = square(L1 - L2); + const float C1_2 = a1 * a1 + b1 * b1; + const float C2_2 = a2 * a2 + b2 * b2; + + const float deltaCab_2 = square(sqrt(C1_2) - sqrt(C2_2)); + const float deltaHab_2 = square(a2 - a1) + square(b2 - b1) - deltaCab_2; + + const float SC_2 = square(sqrt(C1_2) * 0.045f + 1.0f); + const float SH_2 = square(sqrt(C2_2) * 0.015f + 1.0f); + + const float result = deltaL_2 + deltaCab_2 / SC_2 + deltaHab_2 / SH_2; + return result; +} + +float color_diff_Lab00(vec3 lab1_vec3, vec3 lab2_vec3) { + const float kL = 1.0f; + const float kC = 1.0f; + const float kH = 1.0f; + const float L1 = lab1_vec3[0]; + const float a1 = lab1_vec3[1]; + const float b1 = lab1_vec3[2]; + + const float L2 = lab2_vec3[0]; + const float a2 = lab2_vec3[1]; + const float b2 = lab2_vec3[2]; + + float C1sab = sqrt(a1 * a1 + b1 * b1); + float C2sab = sqrt(a2 * a2 + b2 * b2); + float mCsab = (C1sab + C2sab) / 2; + float pow_mCsab_7 = pow(mCsab, 7); + float G = 0.5 * (1 - sqrt(pow_mCsab_7 / (pow_mCsab_7 + pow(25.0f, 7.0f)))); + float a1p = (1 + G) * a1; + float a2p = (1 + G) * a2; + float C1p = sqrt(a1p * a1p + b1 * b1); + float C2p = sqrt(a2p * a2p + b2 * b2); + float h1p, h2p; + if (b1 == 0 && a1p == 0) + h1p = 0; + else + h1p = atan(b1, a1p); + + if (h1p < 0) h1p += 2 * pi_fp32; + + if (b2 == 0 && a2p == 0) + h2p = 0; + else + h2p = atan(b2, a2p); + + if (h2p < 0) h2p += 2 * pi_fp32; + + float dLp = L2 - L1; + float dCp = C2p - C1p; + float dhp; + if (C1p * C2p == 0) { + dhp = 0; + } else { + if (abs(h2p - h1p) <= radians(180.0f)) { + dhp = h2p - h1p; + } else if (h2p - h1p > radians(180.0f)) { + dhp = h2p - h1p - radians(360.0f); + } else { + dhp = h2p - h1p + radians(360.0f); + } + } + + float dHp = 2 * sqrt(C1p * C2p) * sin(dhp / 2.0); + + float mLp = (L1 + L2) / 2; + float mCp = (C1p + C2p) / 2; + float mhp; + if (C1p * C2p == 0) { + mhp = (h1p + h2p); + } else if (abs(h2p - h1p) <= radians(180.0f)) { + mhp = (h1p + h2p) / 2; + } else if (h1p + h2p < radians(360.0f)) { + mhp = (h1p + h2p + radians(360.0f)) / 2; + } else { + mhp = (h1p + h2p - radians(360.0f)) / 2; + } + + float T = 1 - 0.17f * cos(mhp - radians(30.0f)) + 0.24f * cos(2 * mhp) + + 0.32f * cos(3 * mhp + radians(6.0f)) - + 0.20f * cos(4 * mhp - radians(63.0f)); + + float dTheta = + radians(30.0f) * exp(-square((mhp - radians(275.0f)) / radians(25.0f))); + + float RC = 2 * sqrt(pow(mCp, 7) / (pow(25.0f, 7.0f) + pow(mCp, 7.0f))); + float square_mLp_minus_50 = square(mLp - 50); + float SL = 1 + 0.015f * square_mLp_minus_50 * inversesqrt(20 + square_mLp_minus_50); + + float SC = 1 + 0.045f * mCp; + + float SH = 1 + 0.015f * mCp * T; + + float RT = -RC * sin(2 * dTheta); + + float Diffsquare = square(dLp / SL / kL) + square(dCp / SC / kC) + + square(dHp / SH / kH) + + RT * (dCp / SC / kC) * (dHp / SH / kH); + + return Diffsquare; +} diff --git a/utilities/MCDataVersion/CMakeLists.txt b/utilities/MCDataVersion/CMakeLists.txt index cdc727f8..748169ef 100644 --- a/utilities/MCDataVersion/CMakeLists.txt +++ b/utilities/MCDataVersion/CMakeLists.txt @@ -5,15 +5,9 @@ add_library(MCDataVersion STATIC MCDataVersion.cpp ) -include(${CMAKE_SOURCE_DIR}/cmake/configure_magic_enum.cmake) - -find_package(magic_enum REQUIRED) +find_package(magic_enum REQUIRED CONFIG) target_link_libraries(MCDataVersion PUBLIC magic_enum::magic_enum) target_compile_features(MCDataVersion PRIVATE cxx_std_20) target_include_directories(MCDataVersion INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set_target_properties(MCDataVersion PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() \ No newline at end of file diff --git a/utilities/MCDataVersion/MCDataVersion.cpp b/utilities/MCDataVersion/MCDataVersion.cpp index 5a3d1060..dcac823e 100644 --- a/utilities/MCDataVersion/MCDataVersion.cpp +++ b/utilities/MCDataVersion/MCDataVersion.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -22,10 +22,10 @@ This file is part of SlopeCraft. #include "MCDataVersion.h" -#include +#include -MCDataVersion::MCDataVersion_t -MCDataVersion::string_to_data_version(const char *str, bool *ok) noexcept { +MCDataVersion::MCDataVersion_t MCDataVersion::string_to_data_version( + const char *str, bool *ok) noexcept { const auto mcdv = magic_enum::enum_cast(str); if (mcdv.has_value()) { @@ -41,62 +41,70 @@ MCDataVersion::string_to_data_version(const char *str, bool *ok) noexcept { } const char *MCDataVersion::data_version_to_string( MCDataVersion::MCDataVersion_t v) noexcept { - const auto mcdv = magic_enum::enum_name(v); + std::string_view mcdv = magic_enum::enum_name(v); return mcdv.data(); } -MCDataVersion::MCDataVersion_t -MCDataVersion::max_supported_version(SCL_gameVersion v) noexcept { +MCDataVersion::MCDataVersion_t MCDataVersion::max_supported_version( + SCL_gameVersion v) noexcept { switch (v) { - case SCL_gameVersion::MC12: - return MCDataVersion_t::Java_1_12_2; - case SCL_gameVersion::MC13: - return MCDataVersion_t::Java_1_13_2; - case SCL_gameVersion::MC14: - return MCDataVersion_t::Java_1_14_4; - case SCL_gameVersion::MC15: - return MCDataVersion_t::Java_1_15_2; - case SCL_gameVersion::MC16: - return MCDataVersion_t::Java_1_16_5; - case SCL_gameVersion::MC17: - return MCDataVersion_t::Java_1_17_1; - case SCL_gameVersion::MC18: - return MCDataVersion_t::Java_1_18_2; - case SCL_gameVersion::MC19: - return MCDataVersion_t::Java_1_19_3; - default: - abort(); - return {}; + case SCL_gameVersion::ANCIENT: + case SCL_gameVersion::MC12: + return MCDataVersion_t::Java_1_12_2; + case SCL_gameVersion::MC13: + return MCDataVersion_t::Java_1_13_2; + case SCL_gameVersion::MC14: + return MCDataVersion_t::Java_1_14_4; + case SCL_gameVersion::MC15: + return MCDataVersion_t::Java_1_15_2; + case SCL_gameVersion::MC16: + return MCDataVersion_t::Java_1_16_5; + case SCL_gameVersion::MC17: + return MCDataVersion_t::Java_1_17_1; + case SCL_gameVersion::MC18: + return MCDataVersion_t::Java_1_18_2; + case SCL_gameVersion::MC19: + return MCDataVersion_t::Java_1_19_3; + case SCL_gameVersion::MC20: + return MCDataVersion_t::Java_1_20_6; + case SCL_gameVersion::MC21: + case SCL_gameVersion::FUTURE: + return MCDataVersion_t::Java_1_21_1; } + abort(); } -MCDataVersion::MCDataVersion_t -MCDataVersion::min_supported_version(SCL_gameVersion v) noexcept { +MCDataVersion::MCDataVersion_t MCDataVersion::min_supported_version( + SCL_gameVersion v) noexcept { switch (v) { - case SCL_gameVersion::MC12: - return MCDataVersion_t::Java_1_12; - case SCL_gameVersion::MC13: - return MCDataVersion_t::Java_1_13; - case SCL_gameVersion::MC14: - return MCDataVersion_t::Java_1_14; - case SCL_gameVersion::MC15: - return MCDataVersion_t::Java_1_15; - case SCL_gameVersion::MC16: - return MCDataVersion_t::Java_1_16; - case SCL_gameVersion::MC17: - return MCDataVersion_t::Java_1_17; - case SCL_gameVersion::MC18: - return MCDataVersion_t::Java_1_18; - case SCL_gameVersion::MC19: - return MCDataVersion_t::Java_1_19; - default: - abort(); - return {}; + case SCL_gameVersion::ANCIENT: + case SCL_gameVersion::MC12: + return MCDataVersion_t::Java_1_12; + case SCL_gameVersion::MC13: + return MCDataVersion_t::Java_1_13; + case SCL_gameVersion::MC14: + return MCDataVersion_t::Java_1_14; + case SCL_gameVersion::MC15: + return MCDataVersion_t::Java_1_15; + case SCL_gameVersion::MC16: + return MCDataVersion_t::Java_1_16; + case SCL_gameVersion::MC17: + return MCDataVersion_t::Java_1_17; + case SCL_gameVersion::MC18: + return MCDataVersion_t::Java_1_18; + case SCL_gameVersion::MC19: + return MCDataVersion_t::Java_1_19; + case SCL_gameVersion::MC20: + return MCDataVersion_t::Java_1_20; + case SCL_gameVersion::MC21: + case SCL_gameVersion::FUTURE: + return MCDataVersion_t::Java_1_21; } + abort(); } -MCDataVersion::MCDataVersion_t -MCDataVersion::suggested_version(SCL_gameVersion v) noexcept { +MCDataVersion::MCDataVersion_t MCDataVersion::suggested_version( + SCL_gameVersion v) noexcept { return max_supported_version(v); } diff --git a/utilities/MCDataVersion/MCDataVersion.h b/utilities/MCDataVersion/MCDataVersion.h index 3a45111e..4b1e53ff 100644 --- a/utilities/MCDataVersion/MCDataVersion.h +++ b/utilities/MCDataVersion/MCDataVersion.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -25,11 +25,160 @@ This file is part of SlopeCraft. #include -#include +#include namespace MCDataVersion { enum class MCDataVersion_t : int { + Java_1_21_11 = 4671, + Java_1_21_11__rc3 = 4670, + Java_1_21_11__rc2 = 4669, + Java_1_21_11__rc1 = 4668, + Java_1_21_11__pre5 = 4667, + Java_1_21_11__pre4 = 4666, + Java_1_21_11__pre3 = 4665, + Java_1_21_11__pre2 = 4664, + Java_1_21_11__pre1 = 4663, + Snapshot_25w46a = 4662, + Snapshot_25w45a = 4660, + Snapshot_25w44a = 4659, + Snapshot_25w43a = 4655, + Snapshot_25w42a = 4654, + Snapshot_25w41a = 4653, + Java_1_21_10 = 4556, + Java_1_21_10__rc1 = 4555, + Java_1_21_9 = 4554, + Java_1_21_9__rc1 = 4553, + Java_1_21_9__pre4 = 4552, + Java_1_21_9__pre3 = 4551, + Java_1_21_9__pre2 = 4550, + Java_1_21_9__pre1 = 4549, + Snapshot_25w37a = 4547, + Snapshot_25w36b = 4546, + Snapshot_25w36a = 4545, + Snapshot_25w35a = 4542, + Snapshot_25w34b = 4540, + Snapshot_25w34a = 4539, + Snapshot_25w33a = 4538, + Snapshot_25w32a = 4536, + Snapshot_25w31a = 4534, + Java_1_21_8 = 4440, + Java_1_21_8__rc1 = 4439, + Java_1_21_7 = 4438, + Java_1_21_7__rc2 = 4437, + Java_1_21_7__rc1 = 4436, + Java_1_21_6 = 4435, + Java_1_21_6__rc1 = 4434, + Java_1_21_6__pre4 = 4433, + Java_1_21_6__pre3 = 4432, + Java_1_21_6__pre2 = 4431, + Java_1_21_6__pre1 = 4430, + Snapshot_25w21a = 4429, + Snapshot_25w20a = 4428, + Snapshot_25w19a = 4427, + Snapshot_25w18a = 4426, + Snapshot_25w17a = 4425, + Snapshot_25w16a = 4423, + Snapshot_25w15a = 4422, + Java_1_21_5 = 4325, + Java_1_21_5__rc2 = 4324, + Java_1_21_5__rc1 = 4323, + Java_1_21_5__pre3 = 4322, + Java_1_21_5__pre2 = 4321, + Java_1_21_5__pre1 = 4320, + Snapshot_25w10a = 4319, + Snapshot_25w09b = 4318, + Snapshot_25w09a = 4317, + Snapshot_25w08a = 4316, + Snapshot_25w07a = 4315, + Snapshot_25w06a = 4313, + Snapshot_25w05a = 4310, + Snapshot_25w04a = 4308, + Snapshot_25w03a = 4304, + Snapshot_25w02a = 4298, + Java_1_21_4 = 4189, + Java_1_21_4__rc3 = 4188, + Java_1_21_4__rc2 = 4186, + Java_1_21_4__rc1 = 4184, + Java_1_21_4__pre3 = 4183, + Java_1_21_4__pre2 = 4182, + Java_1_21_4__pre1 = 4179, + Snapshot_24w46a = 4178, + Snapshot_24w45a = 4177, + Snapshot_24w44a = 4174, + Java_1_21_3 = 4082, + Java_1_21_2 = 4080, + Java_1_21_2__rc2 = 4079, + Java_1_21_2__rc1 = 4078, + Java_1_21_2__pre5 = 4077, + Java_1_21_2__pre4 = 4076, + Java_1_21_2__pre3 = 4075, + Java_1_21_2__pre2 = 4074, + Java_1_21_2__pre1 = 4073, + Snapshot_24w40a = 4072, + Snapshot_24w39a = 4069, + Snapshot_24w38a = 4066, + Snapshot_24w37a = 4065, + Snapshot_24w36a = 4063, + Snapshot_24w35a = 4062, + Snapshot_24w34a = 4060, + Snapshot_24w33a = 4058, + Java_1_21_1 = 3955, + Java_1_21_1__rc1 = 3954, + Java_1_21 = 3953, + Java_1_21__rc1 = 3952, + Java_1_21__pre4 = 3951, + Java_1_21__pre3 = 3950, + Java_1_21__pre2 = 3949, + Java_1_21__pre1 = 3948, + Snapshot_24w21b = 3947, + Snapshot_24w21a = 3946, + Snapshot_24w20a = 3944, + Snapshot_24w19b = 3942, + Snapshot_24w19a = 3941, + Snapshot_24w18a = 3940, + Java_1_20_6 = 3839, + Java_1_20_6__rc1 = 3838, + Java_1_20_5 = 3837, + Snapshot_23w51b = 3802, + Snapshot_23w51a = 3801, + Java_1_20_4 = 3700, + Java_1_20_4_rc1 = 3699, + Java_1_20_3 = 3698, + Java_1_20_3__rc2 = 3577, + Java_1_20_3__rc1 = 3576, + Java_1_20_2_pre4 = 3575, + Java_1_20_2_pre3 = 3574, + Java_1_20_2_pre2 = 3573, + Java_1_20_2_pre1 = 3572, + Snapshot_23w35a = 3571, + Snapshot_23w33a = 3570, + Snapshot_23w32a = 3569, + Snapshot_23w31a = 3567, + Java_1_20_1 = 3465, + Java_1_20_1__rc1 = 3464, + Java_1_20 = 3463, + Java_1_20__rc1 = 3462, + Java_1_20__pre7 = 3461, + Java_1_20__pre6 = 3460, + Java_1_20__pre5 = 3458, + Java_1_20__pre4 = 3457, + Java_1_20__pre3 = 3456, + Java_1_20__pre2 = 3455, + Java_1_20__pre1 = 3454, + Snapshot_23w_18a = 3453, + Snapshot_23w_17a = 3452, + Snapshot_23w_16a = 3449, + Snapshot_23w_14a = 3445, + Snapshot_23w_13a = 3442, + Snapshot_23w_12a = 3442, + Java_1_19_4 = 3337, + Java_1_19_4__rc3 = 3336, + Java_1_19_4__rc2 = 3335, + Java_1_19_4__rc1 = 3334, + Java_1_19_4__pre4 = 3333, + Java_1_19_4__pre3 = 3332, + Java_1_19_4__pre2 = 3331, Java_1_19_4__pre1 = 3330, Snapshot_23w07a = 3329, Snapshot_23w06a = 3326, @@ -515,5 +664,5 @@ inline bool is_data_version_suitable(SCL_gameVersion v, return true; } -} // namespace MCDataVersion -#endif // SLOPECRAFT_UTILITIES_MCDATAVERSION_H \ No newline at end of file +} // namespace MCDataVersion +#endif // SLOPECRAFT_UTILITIES_MCDATAVERSION_H \ No newline at end of file diff --git a/utilities/MapImageCvter/CMakeLists.txt b/utilities/MapImageCvter/CMakeLists.txt index 05ef30bd..cc52f0f6 100644 --- a/utilities/MapImageCvter/CMakeLists.txt +++ b/utilities/MapImageCvter/CMakeLists.txt @@ -1,6 +1,7 @@ set(CMAKE_CXX_STANDARD 20) find_package(OpenMP REQUIRED) +find_package(cereal REQUIRED) add_library(MapImageCvter STATIC @@ -9,15 +10,16 @@ add_library(MapImageCvter target_include_directories(MapImageCvter PUBLIC ${CMAKE_SOURCE_DIR}/utilities - ${SlopeCraft_Eigen3_include_dir} ${SlopeCraft_HeuristicFlow_include_dir} ) -target_link_libraries(MapImageCvter PUBLIC ColorManip GAConverter OpenMP::OpenMP_CXX) -target_compile_options(MapImageCvter PRIVATE ${SlopeCraft_vectorize_flags}) +target_link_libraries(MapImageCvter PUBLIC + ColorManip + GAConverter + OpenMP::OpenMP_CXX + cereal::cereal) -target_compile_features(MapImageCvter PUBLIC cxx_std_20) +target_include_directories(MapImageCvter PRIVATE "${CMAKE_BINARY_DIR}/SlopeCraftL") +target_compile_options(MapImageCvter PRIVATE ${SlopeCraft_vectorize_flags}) -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set_target_properties(MapImageCvter PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() \ No newline at end of file +target_compile_features(MapImageCvter PUBLIC cxx_std_23) diff --git a/utilities/MapImageCvter/MapImageCvter.cpp b/utilities/MapImageCvter/MapImageCvter.cpp index 0bd477ff..288350b7 100644 --- a/utilities/MapImageCvter/MapImageCvter.cpp +++ b/utilities/MapImageCvter/MapImageCvter.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,11 +24,17 @@ This file is part of SlopeCraft. #include #include +#include +#include +#include using namespace libImageCvt; -libMapImageCvt::MapImageCvter::MapImageCvter() - : gacvter(new GACvter::GAConverter) {} +libMapImageCvt::MapImageCvter::MapImageCvter( + const Base_t::basic_colorset_t &basic, + const Base_t::allowed_colorset_t &allowed) + : libImageCvt::ImageCvter{basic, allowed}, + gacvter(new GACvter::GAConverter) {} void libMapImageCvt::MapImageCvter::convert_image( const ::SCL_convertAlgo algo, bool dither, @@ -60,11 +66,80 @@ void libMapImageCvt::MapImageCvter::convert_image( gacvter->run(); - Eigen::ArrayXX raw_image_cache = this->_raw_image; + Eigen::ArrayXX raw_image_cache = this->raw_image_; - gacvter->resultImage(&this->_raw_image); + gacvter->resultImage(&this->raw_image_); Base_t::convert_image(::SCL_convertAlgo::RGB_Better, dither); - this->_raw_image = raw_image_cache; + this->raw_image_ = raw_image_cache; +} + +bool libMapImageCvt::MapImageCvter::save_cache( + const char *filename) const noexcept { + std::ofstream ofs{filename, std::ios::binary}; + if (!ofs) { + return false; + } + + { + cereal::BinaryOutputArchive boa{ofs}; + + boa(*this); + } + + ofs.close(); + + return true; +} + +bool libMapImageCvt::MapImageCvter::examine_cache( + const char *filename, uint64_t expected_task_hash, + MapImageCvter *itermediate) const noexcept { + std::ifstream ifs{filename, std::ios::binary}; + if (!ifs) { // cache file not exist + return false; + } + MapImageCvter im{this->basic_colorset, this->allowed_colorset}; + + try { + cereal::BinaryInputArchive bia{ifs}; + bia(im); + } catch (...) { // the cache is broken + return false; + } + + if (im.task_hash() != expected_task_hash) { // the cache may be modified + return false; + } + + if (itermediate != nullptr) { + itermediate->load_from_itermediate(std::move(im)); + } + + return true; +} + +bool libMapImageCvt::MapImageCvter::load_cache( + const char *filename, uint64_t expected_task_hash) noexcept { + MapImageCvter temp{this->basic_colorset, this->allowed_colorset}; + if (!this->examine_cache(filename, expected_task_hash, &temp)) { + return false; + } + + this->load_from_itermediate(std::move(temp)); + + assert(this->raw_image_.rows() == this->dithered_image_.rows()); + assert(this->raw_image_.cols() == this->dithered_image_.cols()); + return true; +} + +bool libMapImageCvt::MapImageCvter::load_cache(const char *filename) noexcept { + MapImageCvter temp{this->basic_colorset, this->allowed_colorset}; + + this->load_from_itermediate(std::move(temp)); + + assert(this->raw_image_.rows() == this->dithered_image_.rows()); + assert(this->raw_image_.cols() == this->dithered_image_.cols()); + return true; } \ No newline at end of file diff --git a/utilities/MapImageCvter/MapImageCvter.h b/utilities/MapImageCvter/MapImageCvter.h index ff73d2f9..e63f86c0 100644 --- a/utilities/MapImageCvter/MapImageCvter.h +++ b/utilities/MapImageCvter/MapImageCvter.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -25,12 +25,16 @@ This file is part of SlopeCraft. #include #include - +#include +#include +#include #include +#include namespace GACvter { class GAConverter; -} +void delete_GA_converter(GAConverter *) noexcept; +} // namespace GACvter namespace heu { struct GAOption; @@ -39,17 +43,22 @@ struct GAOption; namespace libMapImageCvt { class MapImageCvter : public ::libImageCvt::ImageCvter { -private: - std::unique_ptr gacvter; + private: + using deleter_t = decltype([](GACvter::GAConverter *g) { + GACvter::delete_GA_converter(g); + }); + std::unique_ptr gacvter; -public: + public: using Base_t = ::libImageCvt::ImageCvter; using TokiColor_t = typename Base_t::TokiColor_t; static constexpr int size_of_tokicolor = sizeof(TokiColor_t); - explicit MapImageCvter(); + MapImageCvter(const Base_t::basic_colorset_t &basic, + const Base_t::allowed_colorset_t &allowed); + MapImageCvter(MapImageCvter &&) = default; ~MapImageCvter() = default; @@ -75,23 +84,111 @@ class MapImageCvter : public ::libImageCvt::ImageCvter { return result; } - void col_TokiColor_ptrs(int64_t col, - const TokiColor_t **const dest) const noexcept { - if (dest == nullptr) { + void col_TokiColor_ptrs( + int64_t col, std::vector &dest) const noexcept { + if (col < 0 || col > this->cols()) { return; } + dest.clear(); + for (int64_t r = 0; r < this->rows(); r++) { + auto it = this->color_hash_.find( + convert_unit(this->dithered_image_(r, col), this->algo)); + dest.emplace_back(&it->second); + } + } - if (col < 0 || col > this->cols()) { - return; + // temp is a temporary container to pass ownership + void load_from_itermediate(MapImageCvter &&temp) noexcept { + this->raw_image_ = std::move(temp.raw_image_); + this->algo = temp.algo; + this->dithered_image_ = std::move(temp.dithered_image_); + + assert(this->raw_image_.rows() == this->dithered_image_.rows()); + assert(this->raw_image_.cols() == this->dithered_image_.cols()); + if (this->color_hash_.empty()) { + this->color_hash_ = std::move(temp.color_hash_); + } else { + temp.color_hash_.merge(this->color_hash_); + this->color_hash_ = std::move(temp.color_hash_); } + } - for (int64_t r = 0; r < this->rows(); r++) { - auto it = this->_color_hash.find( - convert_unit(this->_dithered_image(r, col), this->algo)); - dest[r] = &it->second; + private: + friend class cereal::access; + template + void save(archive &ar) const { + assert(this->raw_image_.rows() == this->dithered_image_.rows()); + assert(this->raw_image_.cols() == this->dithered_image_.cols()); + ar(this->raw_image_); + ar(this->algo); + ar(this->dither); + // ar(this->_color_hash); + ar(this->dithered_image_); + // save required colorset + { + std::unordered_set colors_dithered_img; + colors_dithered_img.reserve(this->dithered_image_.size()); + for (int64_t i = 0; i < this->dithered_image_.size(); i++) { + colors_dithered_img.emplace(this->dithered_image_(i)); + colors_dithered_img.emplace(this->raw_image_(i)); + } + + const size_t size_colorset = colors_dithered_img.size(); + ar(size_colorset); + for (uint32_t color : colors_dithered_img) { + auto it = + this->color_hash().find(convert_unit{color, this->convert_algo()}); + if (it == this->color_hash().end()) { + assert(getA(color) <= 0); + continue; + } + + ar(it->first, it->second); + } } } + + template + void load(archive &ar) { + ar(this->raw_image_); + ar(this->algo); + ar(this->dither); + // ar(this->_color_hash); + ar(this->dithered_image_); + + assert(this->raw_image_.rows() == this->dithered_image_.rows()); + assert(this->raw_image_.cols() == this->dithered_image_.cols()); + + { + size_t size_colorset{0}; + ar(size_colorset); + for (size_t idx = 0; idx < size_colorset; idx++) { + convert_unit key; + TokiColor_t val; + ar(key, val); + + this->color_hash_.emplace(key, val); + } + + for (int64_t i = 0; i < this->dithered_image_.size(); i++) { + auto it = this->color_hash_.find( + convert_unit{this->dithered_image_(i), this->convert_algo()}); + if (it == this->color_hash_.end()) { + throw std::runtime_error{ + "One or more colors not found in cached colorhash"}; + } + } + } + } + + public: + bool save_cache(const char *filename) const noexcept; + bool examine_cache(const char *filename, uint64_t expected_task_hash, + MapImageCvter *itermediate = nullptr) const noexcept; + [[nodiscard]] bool load_cache(const char *filename, + uint64_t expected_task_hash) noexcept; + bool load_cache(const char *filename) noexcept; }; -} // namespace libMapImageCvt -#endif // SCL_MAPIMAGECVTER_MAPIMAGECVTER_H \ No newline at end of file +} // namespace libMapImageCvt +#endif // SCL_MAPIMAGECVTER_MAPIMAGECVTER_H \ No newline at end of file diff --git a/utilities/NBTWriter/CMakeLists.txt b/utilities/NBTWriter/CMakeLists.txt index 754c7e53..c720478a 100644 --- a/utilities/NBTWriter/CMakeLists.txt +++ b/utilities/NBTWriter/CMakeLists.txt @@ -12,9 +12,3 @@ add_library(NBTWriter NBTWriter.cpp) target_link_libraries(NBTWriter PUBLIC ZLIB::ZLIB) - -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set_target_properties(NBTWriter PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() - -# target_compile_options(NBTWriter BEFORE PUBLIC -std=c++17) diff --git a/utilities/NBTWriter/NBTWriter.cpp b/utilities/NBTWriter/NBTWriter.cpp index d46e0e3f..0be0d782 100644 --- a/utilities/NBTWriter/NBTWriter.cpp +++ b/utilities/NBTWriter/NBTWriter.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,11 +24,9 @@ This file is part of SlopeCraft. #include #include - using namespace NBT::internal; bool NBTWriterBase_nocompress::open(const char *newFileName) noexcept { - if (file != nullptr) { return false; } @@ -54,7 +52,6 @@ bool NBTWriterBase_nocompress::open(const char *newFileName) noexcept { } bool NBTWriterBase_gzip::open(const char *newFileName) noexcept { - if (this->file != nullptr) { return false; } diff --git a/utilities/NBTWriter/NBTWriter.h b/utilities/NBTWriter/NBTWriter.h index a2e86c45..db7e6d25 100644 --- a/utilities/NBTWriter/NBTWriter.h +++ b/utilities/NBTWriter/NBTWriter.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -31,7 +31,7 @@ This file is part of SlopeCraft. #include #include #include - +#include #include // #include @@ -44,15 +44,15 @@ namespace NBT { namespace internal { class NBTWriterBase_nocompress { -protected: + protected: uint64_t bytesWritten{0}; int write_data(const void *data, const size_t bytes) noexcept; -private: + private: FILE *file{NULL}; -public: + public: /** * \brief open a file * \param newFileName the file to be opened @@ -81,15 +81,15 @@ class NBTWriterBase_nocompress { }; class NBTWriterBase_gzip { -protected: + protected: uint64_t bytesWritten{0}; int write_data(const void *data, const size_t bytes) noexcept; -private: + private: gzFile_s *file{NULL}; -public: + public: /** * \brief open a file * \param newFileName the file to be opened @@ -117,7 +117,7 @@ class NBTWriterBase_gzip { inline const gzFile_s *file_ptr() const noexcept { return file; } }; -} // namespace internal +} // namespace internal constexpr char idEnd = 0; constexpr char idByte = 1; @@ -154,8 +154,8 @@ enum tagType : char { * \param t To be converted * \return Converted */ -template inline T convertLEBE(T t) { - +template +inline T convertLEBE(T t) { uint8_t *ptr = reinterpret_cast(&t); for (int idx = 0; idx * 2 + 1 <= int(sizeof(T)); idx++) { @@ -173,7 +173,7 @@ class NBTWriter : public std::conditional_t { -public: + public: struct task_t { task_t() : currentTagType(tagType::End), taskSize(0) {} task_t(const tagType type, int size) @@ -183,10 +183,10 @@ class NBTWriter int taskSize; }; -private: + private: std::stack tasks; -public: + public: /** * \brief Default constructor */ @@ -212,13 +212,11 @@ class NBTWriter * \return If closing succeeds */ bool close() { - if (!this->is_open()) { return false; } - if (!tasks.empty()) - emergencyFill(); + if (!tasks.empty()) emergencyFill(); constexpr char fileTail[1] = {idEnd}; @@ -234,7 +232,6 @@ class NBTWriter * file can be loaded at least. \return Bytes written */ int emergencyFill() { - if (tasks.empty()) { return 0; } @@ -242,7 +239,6 @@ class NBTWriter int bytes = 0; while (!tasks.empty()) { - if (isInCompound()) { bytes += endCompound(); continue; @@ -251,70 +247,70 @@ class NBTWriter // cout<<"tasks.size() = "< - int writeSingleTag(const tagType type, const char *Name, T value) { + int writeSingleTag(const tagType type, std::string_view Name, T value) { static_assert(std::is_trivial_v); if (!this->is_open()) { @@ -418,21 +412,22 @@ class NBTWriter } int bytes = 0; - const uint16_t realNameL = strlen(Name); + if (Name.size() >= UINT16_MAX) { + return 0; + } + const uint16_t realNameL = Name.size(); const uint16_t flippedNameL = convertLEBE(realNameL); if (isInCompound()) { - bytes += this->write_data(&type, sizeof(char)); bytes += this->write_data(&flippedNameL, sizeof(flippedNameL)); - bytes += this->write_data(Name, realNameL); + bytes += this->write_data(Name.data(), realNameL); bytes += this->write_data(&value, sizeof(T)); } else { - if (!typeMatch(type)) { return 0; } @@ -451,7 +446,7 @@ class NBTWriter * \param value Value of tag * \return Bytes written */ - inline int writeByte(const char *Name, int8_t value) { + inline int writeByte(std::string_view Name, int8_t value) { return writeSingleTag(tagType::Byte, Name, value); } @@ -461,7 +456,7 @@ class NBTWriter * \param value Value of tag * \return Bytes written */ - inline int writeShort(const char *Name, int16_t value) { + inline int writeShort(std::string_view Name, int16_t value) { return writeSingleTag(tagType::Short, Name, value); } @@ -471,7 +466,7 @@ class NBTWriter * \param value Value of tag * \return Bytes written */ - inline int writeInt(const char *Name, int32_t value) { + inline int writeInt(std::string_view Name, int32_t value) { return writeSingleTag(tagType::Int, Name, value); } @@ -481,7 +476,7 @@ class NBTWriter * \param value Value of tag * \return Bytes written */ - inline int writeLong(const char *Name, int64_t value) { + inline int writeLong(std::string_view Name, int64_t value) { return writeSingleTag(tagType::Long, Name, value); } @@ -491,7 +486,7 @@ class NBTWriter * \param value Value of tag * \return Bytes written */ - inline int writeFloat(const char *Name, float value) { + inline int writeFloat(std::string_view Name, float value) { return writeSingleTag(tagType::Float, Name, value); } @@ -501,7 +496,7 @@ class NBTWriter * \param value Value of tag * \return Bytes written */ - inline int writeDouble(const char *Name, double value) { + inline int writeDouble(std::string_view Name, double value) { return writeSingleTag(tagType::Double, Name, value); } @@ -510,18 +505,17 @@ class NBTWriter * \param Name Name of tag * \return Bytes written */ - int writeCompound(const char *Name = "") { - if (!this->is_open()) - return 0; + int writeCompound(std::string_view Name = "") { + if (!this->is_open()) return 0; int bytes = 0; - const int16_t realNameL = ::strlen(Name); + const int16_t realNameL = Name.size(); const int16_t flippedNameL = convertLEBE(realNameL); if (isInCompound()) { bytes += this->write_data(&idCompound, sizeof(char)); bytes += this->write_data(&flippedNameL, sizeof(int16_t)); - bytes += this->write_data(Name, realNameL); + bytes += this->write_data(Name.data(), realNameL); tasks.emplace(task_t(End, 0)); return bytes; @@ -541,8 +535,7 @@ class NBTWriter * \return Bytes written */ int endCompound() { - if (!this->is_open()) - return 0; + if (!this->is_open()) return 0; if (!isInCompound()) { return 0; @@ -568,7 +561,8 @@ class NBTWriter * \param listSize Size of list * \return Bytes written */ - int writeListHead(const char *Name, tagType elementType, const int listSize) { + int writeListHead(std::string_view Name, tagType elementType, + const int listSize) { if (!this->is_open()) { return 0; } @@ -578,14 +572,14 @@ class NBTWriter } int bytes = 0; - const int16_t realNameL = ::strlen(Name); + const int16_t realNameL = Name.size(); const int16_t flippedNameL = convertLEBE(realNameL); const int32_t flippedListSize = convertLEBE(listSize); if (isInCompound()) { bytes += this->write_data(&idList, sizeof(char)); bytes += this->write_data(&flippedNameL, sizeof(int16_t)); - bytes += this->write_data(Name, realNameL); + bytes += this->write_data(Name.data(), realNameL); bytes += this->write_data(&elementType, sizeof(char)); bytes += this->write_data(&flippedListSize, sizeof(int32_t)); @@ -612,10 +606,9 @@ class NBTWriter return 0; } -private: + private: template - int writeArrayHead(const char *Name, const int32_t arraySize) { - + int writeArrayHead(std::string_view Name, const int32_t arraySize) { if (!this->is_open()) { return 0; } @@ -629,24 +622,22 @@ class NBTWriter : ((elementType == Int) ? (IntArray) : (LongArray)); int bytes = 0; - const int16_t realNameL = ::strlen(Name); + const int16_t realNameL = Name.size(); const int16_t flippedNameL = convertLEBE(realNameL); const int32_t flippedArraySize = convertLEBE(arraySize); if (isInCompound()) { - bytes += this->write_data(&arrayId, sizeof(char)); bytes += this->write_data(&flippedNameL, sizeof(int16_t)); - bytes += this->write_data(Name, realNameL); + bytes += this->write_data(Name.data(), realNameL); bytes += this->write_data(&flippedArraySize, sizeof(int32_t)); tasks.emplace(task_t(elementType, arraySize)); if (arraySize == 0) { - onElementWritten(); } @@ -654,13 +645,11 @@ class NBTWriter } if (isInListOrArray() && typeMatch(arrayId)) { - bytes += this->write_data(&flippedArraySize, sizeof(int32_t)); tasks.emplace(task_t(elementType, arraySize)); if (arraySize == 0) { - onElementWritten(); } @@ -670,14 +659,14 @@ class NBTWriter return 0; } -public: + public: /** * \brief Start to write a byte array * \param Name Name of the array * \param arraySize Elements of the array * \return Bytes written */ - inline int writeByteArrayHead(const char *Name, const int arraySize) { + inline int writeByteArrayHead(std::string_view Name, const int arraySize) { return writeArrayHead(Name, arraySize); } @@ -687,7 +676,7 @@ class NBTWriter * \param arraySize Elements of the array * \return Bytes written */ - inline int writeIntArrayHead(const char *Name, const int arraySize) { + inline int writeIntArrayHead(std::string_view Name, const int arraySize) { return writeArrayHead(Name, arraySize); } @@ -697,7 +686,7 @@ class NBTWriter * \param arraySize Elements of the array * \return Bytes written */ - inline int writeLongArrayHead(const char *Name, const int arraySize) { + inline int writeLongArrayHead(std::string_view Name, const int arraySize) { return writeArrayHead(Name, arraySize); } @@ -707,14 +696,14 @@ class NBTWriter * \param value Value of a string * \return Bytes written */ - int writeString(const char *Name, const char *value) { + int writeString(std::string_view Name, const char *value) { if (!this->is_open()) { return 0; } int bytes = 0; - const int16_t realNameL = ::strlen(Name); + const int16_t realNameL = Name.size(); const int16_t flippedNameL = convertLEBE(realNameL); const int16_t realValueL = ::strlen(value); @@ -723,7 +712,7 @@ class NBTWriter if (isInCompound()) { bytes += this->write_data(&idString, sizeof(char)); bytes += this->write_data(&flippedNameL, sizeof(int16_t)); - bytes += this->write_data(Name, realNameL); + bytes += this->write_data(Name.data(), realNameL); bytes += this->write_data(&flippedValueL, sizeof(int16_t)); bytes += this->write_data(value, realValueL); @@ -732,7 +721,6 @@ class NBTWriter } if (isInListOrArray() && typeMatch(String)) { - bytes += this->write_data(&flippedValueL, sizeof(int16_t)); bytes += this->write_data(value, realValueL); @@ -757,6 +745,6 @@ class NBTWriter inline size_t byteCount() const { return this->bytesWritten; } }; -}; // namespace NBT +}; // namespace NBT -#endif // SCL_NBTWRITER_H +#endif // SCL_NBTWRITER_H diff --git a/utilities/ProcessBlockId/CMakeLists.txt b/utilities/ProcessBlockId/CMakeLists.txt index 0c8f058e..92056b0b 100644 --- a/utilities/ProcessBlockId/CMakeLists.txt +++ b/utilities/ProcessBlockId/CMakeLists.txt @@ -12,7 +12,3 @@ add_executable(test_process_block_id test_process_blk_id.cpp) target_link_libraries(test_process_block_id PRIVATE ProcessBlockId) - -if(${LINUX}) - set_target_properties(ProcessBlockId PROPERTIES POSITION_INDEPENDENT_CODE ON) -endif() \ No newline at end of file diff --git a/utilities/ProcessBlockId/process_block_id.cpp b/utilities/ProcessBlockId/process_block_id.cpp index 0307716d..f1917683 100644 --- a/utilities/ProcessBlockId/process_block_id.cpp +++ b/utilities/ProcessBlockId/process_block_id.cpp @@ -1,4 +1,5 @@ #include "process_block_id.h" +#include bool blkid::is_valid_id(std::string_view str) noexcept { if (str.find_first_of(':') != str.find_first_of(':')) { diff --git a/utilities/ProcessBlockId/process_block_id.h b/utilities/ProcessBlockId/process_block_id.h index cf1c1340..568ff9d1 100644 --- a/utilities/ProcessBlockId/process_block_id.h +++ b/utilities/ProcessBlockId/process_block_id.h @@ -1,10 +1,12 @@ #ifndef SLOPECRAFT_UTILITIES_PROCESS_BLOCK_ID_PROCESS_BLOCK_ID_H #define SLOPECRAFT_UTILITIES_PROCESS_BLOCK_ID_PROCESS_BLOCK_ID_H +#include #include #include #include #include +#include namespace blkid { diff --git a/utilities/SC_GlobalEnums.h b/utilities/SC_GlobalEnums.h index a052c8f8..cf0cbaef 100644 --- a/utilities/SC_GlobalEnums.h +++ b/utilities/SC_GlobalEnums.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -50,11 +50,15 @@ enum class SCL_gameVersion : int { MC18 = 18, /// 1.19 MC19 = 19, + /// 1.20 + MC20 = 20, + /// 1.21 + MC21 = 21, /// future version FUTURE = 255 }; -const SCL_gameVersion max_version = SCL_gameVersion::MC19; +constexpr SCL_gameVersion max_version = SCL_gameVersion::MC21; /// color difference formula used to match colors enum class SCL_convertAlgo : char { @@ -80,13 +84,13 @@ enum class SCL_colorSpace : char { enum class SCL_compressSettings : int { /// don't compress - noCompress = 0, + noCompress = 0b00, /// compress in lossless only - NaturalOnly = 1, + NaturalOnly = 0b01, /// compress in lossy only - ForcedOnly = 2, + ForcedOnly = 0b10, /// compress with both lossless and lossy - Both = 3 + Both = 0b11 }; enum class SCL_glassBridgeSettings : int { @@ -105,19 +109,19 @@ enum class SCL_mapTypes : int { FileOnly = 2 }; -enum class SCL_step : int { - /// the instance is created - nothing, - /// map type is set and waitting for image - wait4Image, - /// image is ready and ready for converting - convertionReady, - /// image is converted and ready for building 3D structure, exporting as - /// file-only map(s) can be done in this step - converted, - /// 3D structure is built and ready for exporting 3d structure - builded, -}; +// enum class SCL_step : int { +// /// the instance is created +// nothing, +// /// map type is set and waitting for image +// wait4Image, +// /// image is ready and ready for converting +// convertionReady, +// /// image is converted and ready for building 3D structure, exporting as +// /// file-only map(s) can be done in this step +// converted, +// /// 3D structure is built and ready for exporting 3d structure +// builded, +// }; enum class SCL_errorFlag : int { /// no error @@ -151,15 +155,25 @@ enum class SCL_errorFlag : int { /// is_air_structure_void to false and you don't have minecraft:air in the /// palette EXPORT_SCHEM_STRUCTURE_REQUIRES_AIR = 0x0C, - /// the major game version is less that MC12 or greater than MC19 + /// the major game version is less that MC12 or greater than MC20 UNKNOWN_MAJOR_GAME_VERSION = 0x0D, /// Exporting as WESchem doesn't support MC12, since the format of schematic /// is greatly different. EXPORT_SCHEM_MC12_NOT_SUPPORTED = 0x0E, + EXPORT_MAP_DATA_FAILURE = 0x0F, + + EXPORT_FLAT_DIAGRAM_ON_WRONG_MAP_TYPE = 0x10, + + EXPORT_FLAT_DIAGRAM_FAILURE = 0x11, + /// Failed to allocate memory. Upto 5.3, it will only appear when building 3D + /// for a huge image + MEMORY_ALLOCATE_FAILED = 0x12, + + EXPORT_SCHEM_HAS_INVALID_ENTITY = 0x13, }; -enum class SCL_workStatues : int { +enum class SCL_workStatus : int { /// waiting none = -1, @@ -200,4 +214,21 @@ enum class SCL_HalfTpPixelSt : char { IgnoreAlpha = 'R' }; +enum class SCL_language : int { Chinese = 0, English = 1 }; + +enum class SCL_map_facing : int { + wall_north, + wall_south, + wall_east, + wall_west, + top_north, + top_south, + top_east, + top_west, + bottom_north, + bottom_south, + bottom_east, + bottom_west, +}; + #endif \ No newline at end of file diff --git a/utilities/SC_aligned_alloc.hpp b/utilities/SC_aligned_alloc.hpp index 3d16d092..f490cf47 100644 --- a/utilities/SC_aligned_alloc.hpp +++ b/utilities/SC_aligned_alloc.hpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -41,4 +41,4 @@ inline void SC_aligned_free(void *ptr) noexcept { #endif } -#endif // SLOPECRAFT_UTITILITIES_ALIGNED_ALLOC_HPP \ No newline at end of file +#endif // SLOPECRAFT_UTITILITIES_ALIGNED_ALLOC_HPP \ No newline at end of file diff --git a/utilities/SC_version_buildtime.h.in b/utilities/SC_version_buildtime.h.in index 92c541b2..84eeedf1 100644 --- a/utilities/SC_version_buildtime.h.in +++ b/utilities/SC_version_buildtime.h.in @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -30,8 +30,8 @@ This file is part of SlopeCraft. #define SC_VERSION_PATCH_U16 @PROJECT_VERSION_PATCH@ #define SC_VERSION_TWEAK_U16 0 -#define SC_MAKE_VERSION_U64(major, minor, patch, tweak) \ - ((uint64_t(major) << 48) | (uint64_t(minor) << 32) | \ +#define SC_MAKE_VERSION_U64(major, minor, patch, tweak) \ + ((uint64_t(major) << 48) | (uint64_t(minor) << 32) | \ (uint64_t(patch) << 16) | (uint64_t(tweak))) #define SC_VERSION_STR "@PROJECT_VERSION@" @@ -40,4 +40,9 @@ inline constexpr uint64_t SC_VERSION_U64 = SC_MAKE_VERSION_U64(SC_VERSION_MAJOR_U16, SC_VERSION_MINOR_U16, SC_VERSION_PATCH_U16, SC_VERSION_TWEAK_U16); -#endif // SLOPECRAFT_SC_VERSON_BUILTTIME_H \ No newline at end of file +#define CMAKE_BUILD_TYPE "@CMAKE_BUILD_TYPE@" +#define SC_GPU_API "@SlopeCraft_GPU_API@" +#define SC_VECTORIZE @SlopeCraft_vectorize@ +#define SC_GPROF @SlopeCraft_gprof@ + +#endif // SLOPECRAFT_SC_VERSON_BUILTTIME_H \ No newline at end of file diff --git a/utilities/Schem/CMakeLists.txt b/utilities/Schem/CMakeLists.txt index 10861aba..2b90a74c 100644 --- a/utilities/Schem/CMakeLists.txt +++ b/utilities/Schem/CMakeLists.txt @@ -1,23 +1,37 @@ project(Schem VERSION ${SlopeCraft_version} LANGUAGES CXX) # set(CMAKE_CXX_STANDARD 20) - -# include_directories(${SlopeCraft_Eigen3_include_dir}) add_library(Schem Schem.h - Schem.cpp bit_shrink.h - bit_shrink.cpp) + bit_shrink.cpp + mushroom.h + mushroom.cpp + entity.h + entity.cpp + item.cpp + item.h +) +target_compile_features(Schem PUBLIC cxx_std_14) + +find_package(Eigen3 REQUIRED) -target_include_directories(Schem PUBLIC ${SlopeCraft_Eigen3_include_dir}) +find_package(cereal REQUIRED) +find_package(magic_enum REQUIRED) + +find_package(Boost CONFIG) # target_compile_options(Schem BEFORE PUBLIC -std=c++17) -target_link_libraries(Schem PUBLIC NBTWriter) -target_compile_features(Schem PRIVATE cxx_std_20) +target_link_libraries(Schem PUBLIC + NBTWriter + Eigen3::Eigen + cereal::cereal + magic_enum::magic_enum + NBTWriter +) +if (TARGET Boost::multi_array) + target_link_libraries(Schem PUBLIC Boost::multi_array) +endif () target_link_libraries(Schem PUBLIC MCDataVersion ProcessBlockId) - -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set_target_properties(Schem PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -endif() \ No newline at end of file diff --git a/utilities/Schem/Schem.cpp b/utilities/Schem/Schem.cpp index 5c3a1f37..c055f02d 100644 --- a/utilities/Schem/Schem.cpp +++ b/utilities/Schem/Schem.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,13 +20,17 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "Schem.h" - +#include +#include +#include #include - #include #include #include +#include +#include + +#include "Schem.h" #include "../NBTWriter/NBTWriter.h" #include "bit_shrink.h" @@ -39,6 +43,36 @@ litematic_info::litematic_info() : time_created(std::time(nullptr) * 1000), time_modified(std::time(nullptr) * 1000) {} +void Schem::resize(int64_t x, int64_t y, int64_t z) { + if (x < 0 || y < 0 || z < 0) { + return; + } + this->xzy.resize(x, z, y); +} + +std::string Schem::check_size() const noexcept { + return check_size(this->x_range(), this->y_range(), this->z_range()); +} + +std::string Schem::check_size(int64_t x, int64_t y, int64_t z) noexcept { + if (x < 0 || y < 0 || z < 0) { + return std::format( + "Size in one or more dimensions is negative. x= {}, y= {}, z= {}", x, y, + z); + } + return {}; +} + +void Schem::stat_blocks(std::vector &dest) const noexcept { + dest.resize(this->palette_size()); + std::fill(dest.begin(), dest.end(), 0); + + for (ele_t block_index : *this) { + assert(block_index < this->palette_size()); + dest[block_index] += 1; + } +} + void Schem::set_block_id(const char *const *const block_ids, const int num) noexcept { if (num < 0) { @@ -54,6 +88,13 @@ void Schem::set_block_id(const char *const *const block_ids, } } +void Schem::set_block_id(std::span id) noexcept { + this->block_id_list.resize(id.size()); + for (size_t idx = 0; idx < id.size(); idx++) { + this->block_id_list[idx] = id[idx]; + } +} + int64_t Schem::non_zero_count() const noexcept { int64_t val = 0; @@ -100,7 +141,107 @@ bool Schem::have_invalid_block( return false; } -enum class __mushroom_type : uint8_t { +Schem Schem::slice_no_check(std::span, 3> + xyz_index_range) const noexcept { + Schem ret; + ret.block_id_list = this->block_id_list; + ret.MC_major_ver = this->MC_major_ver; + ret.MC_data_ver = this->MC_data_ver; + + ret.entities.reserve(this->entities.size()); + Eigen::Array3d pos_min, pos_max; + for (size_t dim = 0; dim < 3; dim++) { + pos_min[dim] = xyz_index_range[dim].first; + pos_max[dim] = xyz_index_range[dim].second; + } + { + Eigen::array start, extent; + start[0] = xyz_index_range[0].first; + start[1] = xyz_index_range[2].first; + start[2] = xyz_index_range[1].first; + extent[0] = xyz_index_range[0].second - xyz_index_range[0].first; + extent[1] = xyz_index_range[2].second - xyz_index_range[2].first; + extent[2] = xyz_index_range[1].second - xyz_index_range[1].first; + // avoid Tensor::resize which is problematic on mac + decltype(ret.xzy) temp{this->xzy.slice(start, extent)}; + ret.xzy = std::move(temp); + } + + for (auto &entity : this->entities) { + const auto pos_std = entity->position(); + const Eigen::Array3d pos{pos_std[0], pos_std[1], pos_std[2]}; + if ((pos >= pos_min and pos < pos_max).all()) { + ret.entities.emplace_back(entity->clone()); + } + } + + return ret; +} + +tl::expected, 3>, std::string> +Schem::split_by_block_size( + std::span x_block_length, + std::span y_block_length, + std::span z_block_length) const noexcept { + // check input block length and compute index ranges + std::array>, 3> xyz_block_index_pairs; + { + std::array, 3> xyz_block_len{ + x_block_length, y_block_length, z_block_length}; + std::array block_len_sum{0, 0, 0}; + std::array shape{this->x_range(), this->y_range(), + this->z_range()}; + for (size_t dim = 0; dim < 3; dim++) { + int64_t cur_block_start_index = 0; + for (size_t blk_idx = 0; blk_idx < xyz_block_len[dim].size(); blk_idx++) { + const auto block_len = xyz_block_len[dim][blk_idx]; + if (block_len <= 0) { + return tl::make_unexpected(std::format( + "Found non-positive block length in dim = {}, block index = {}", + dim, blk_idx)); + } + block_len_sum[dim] += block_len; + const int64_t cur_block_end_index = + static_cast(block_len_sum[dim]); + assert(cur_block_end_index > cur_block_start_index); + xyz_block_index_pairs[dim].emplace_back(cur_block_start_index, + cur_block_end_index); + cur_block_start_index = cur_block_end_index; + } + if (block_len_sum[dim] not_eq shape[dim]) { + return tl::make_unexpected(std::format( + "The sum of block length of dim {} is {}, which is not identical " + "to shape on this dim({}), ", + dim, block_len_sum[dim], shape[dim])); + } + } + } + + boost::multi_array, 3> ret{ + boost::extents[xyz_block_index_pairs[0].size()] + [xyz_block_index_pairs[1].size()] + [xyz_block_index_pairs[2].size()]}; + + for (size_t x_idx = 0; x_idx < xyz_block_index_pairs[0].size(); x_idx++) { + const auto x_range = xyz_block_index_pairs[0][x_idx]; + for (size_t y_idx = 0; y_idx < xyz_block_index_pairs[1].size(); y_idx++) { + const auto y_range = xyz_block_index_pairs[1][y_idx]; + for (size_t z_idx = 0; z_idx < xyz_block_index_pairs[2].size(); z_idx++) { + const auto z_range = xyz_block_index_pairs[2][z_idx]; + const Eigen::Array offset{x_range.first, y_range.first, + z_range.first}; + auto &dest = ret[x_idx][y_idx][z_idx]; + dest.offset = offset; + dest.content = + this->slice_no_check(std::array, 3>{ + x_range, y_range, z_range}); + } + } + } + return ret; +} + +enum class mushroom_type : uint8_t { not_mushroom = 0, red_mushroom = 1, brown_mushroom = 2, @@ -154,7 +295,7 @@ void Schem::process_mushroom_states() noexcept { u6_to_ele_stem[u6] = this->block_id_list.size() - 1; } } - std::vector<__mushroom_type> is_mushroom_LUT; + std::vector is_mushroom_LUT; is_mushroom_LUT.resize(this->block_id_list.size()); for (int blockidx = 0; blockidx < int(this->block_id_list.size()); @@ -162,18 +303,18 @@ void Schem::process_mushroom_states() noexcept { const auto &block_id = this->block_id_list[blockidx]; const auto pure_id = ::to_pure_block_id(block_id); if (pure_id == id_red) { - is_mushroom_LUT[blockidx] = __mushroom_type::red_mushroom; + is_mushroom_LUT[blockidx] = mushroom_type::red_mushroom; continue; } if (pure_id == id_brown) { - is_mushroom_LUT[blockidx] = __mushroom_type::brown_mushroom; + is_mushroom_LUT[blockidx] = mushroom_type::brown_mushroom; continue; } if (pure_id == id_stem) { - is_mushroom_LUT[blockidx] = __mushroom_type::mushroom_stem; + is_mushroom_LUT[blockidx] = mushroom_type::mushroom_stem; continue; } - is_mushroom_LUT[blockidx] = __mushroom_type::not_mushroom; + is_mushroom_LUT[blockidx] = mushroom_type::not_mushroom; } // fix the correct state @@ -181,9 +322,9 @@ void Schem::process_mushroom_states() noexcept { for (int64_t z = 0; z < z_range(); z++) { for (int64_t x = 0; x < x_range(); x++) { // if current block is not mushroom, continue - const __mushroom_type current_mushroom_type = + const mushroom_type current_mushroom_type = is_mushroom_LUT[this->operator()(x, y, z)]; - if (current_mushroom_type == __mushroom_type::not_mushroom) { + if (current_mushroom_type == mushroom_type::not_mushroom) { continue; } @@ -191,42 +332,42 @@ void Schem::process_mushroom_states() noexcept { // match the correct side if (x + 1 < x_range()) { const ele_t ele_of_side = this->operator()(x + 1, y, z); - if (is_mushroom_LUT[ele_of_side] != __mushroom_type::not_mushroom) { + if (is_mushroom_LUT[ele_of_side] != mushroom_type::not_mushroom) { side.east() = false; } } if (x - 1 >= 0) { const ele_t ele_of_side = this->operator()(x - 1, y, z); - if (is_mushroom_LUT[ele_of_side] != __mushroom_type::not_mushroom) { + if (is_mushroom_LUT[ele_of_side] != mushroom_type::not_mushroom) { side.west() = false; } } if (y + 1 < y_range()) { const ele_t ele_of_side = this->operator()(x, y + 1, z); - if (is_mushroom_LUT[ele_of_side] != __mushroom_type::not_mushroom) { + if (is_mushroom_LUT[ele_of_side] != mushroom_type::not_mushroom) { side.up() = false; } } if (y - 1 >= 0) { const ele_t ele_of_side = this->operator()(x, y - 1, z); - if (is_mushroom_LUT[ele_of_side] != __mushroom_type::not_mushroom) { + if (is_mushroom_LUT[ele_of_side] != mushroom_type::not_mushroom) { side.down() = false; } } if (z + 1 < z_range()) { const ele_t ele_of_side = this->operator()(x, y, z + 1); - if (is_mushroom_LUT[ele_of_side] != __mushroom_type::not_mushroom) { + if (is_mushroom_LUT[ele_of_side] != mushroom_type::not_mushroom) { side.south() = false; } } if (z - 1 >= 0) { const ele_t ele_of_side = this->operator()(x, y, z - 1); - if (is_mushroom_LUT[ele_of_side] != __mushroom_type::not_mushroom) { + if (is_mushroom_LUT[ele_of_side] != mushroom_type::not_mushroom) { side.north() = false; } } @@ -234,10 +375,10 @@ void Schem::process_mushroom_states() noexcept { // write in the correct value of ele_t ele_t corrected_ele = invalid_ele_t; switch (current_mushroom_type) { - case __mushroom_type::brown_mushroom: + case mushroom_type::brown_mushroom: corrected_ele = u6_to_ele_brown[side.u6()]; break; - case __mushroom_type::red_mushroom: + case mushroom_type::red_mushroom: corrected_ele = u6_to_ele_red[side.u6()]; break; default: @@ -252,49 +393,43 @@ void Schem::process_mushroom_states() noexcept { return; } -bool Schem::export_litematic(std::string_view filename, - const litematic_info &info, - SCL_errorFlag *const error_flag, - std::string *const error_str) const noexcept { - if (std::filesystem::path(filename).extension() != ".litematic") { +tl::expected> Schem::pre_check( + std::string_view filename, std::string_view extension) const noexcept { + if (std::filesystem::path(filename).extension() != extension) { // wrong extension - - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_WRONG_EXTENSION; - } - if (error_str != nullptr) { - *error_str = "The filename externsion must be \".litematic\"."; - } - return false; + return tl::make_unexpected(std::make_pair( + SCL_errorFlag::EXPORT_SCHEM_WRONG_EXTENSION, + std::format("The filename extension must be \"{}\".", extension))); } // check for invalid blocks { std::array pos; if (this->have_invalid_block(&pos[0], &pos[1], &pos[2])) { - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_BLOCKS; - } - if (error_str != nullptr) { - *error_str = "The first invalid block is at x="; - *error_str += std::to_string(pos[0]) + ", y="; - *error_str += std::to_string(pos[1]) + ", z="; - *error_str += std::to_string(pos[2]); - } - return false; + return tl::make_unexpected(std::make_pair( + SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_BLOCKS, + std::format("The first invalid block is at x={}, y={}, z={}", pos[0], + pos[1], pos[2]))); } } + return {}; +} +tl::expected> +Schem::export_litematic(std::string_view filename, + const litematic_info &info) const noexcept { + // + { + auto res = this->pre_check(filename, ".litematic"); + if (not res) { + return res; + } + } NBT::NBTWriter lite; if (!lite.open(filename.data())) { - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE; - } - if (error_str != nullptr) { - *error_str = "Failed to open file : "; - *error_str += filename; - } - return false; + return tl::make_unexpected( + std::make_pair(SCL_errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE, + std::format("Failed to open file: {}", filename))); } lite.writeCompound("Metadata"); @@ -345,14 +480,13 @@ bool Schem::export_litematic(std::string_view filename, // progressRangeSet(wind, 0, 100 + Build.size(), 100); - // reportWorkingStatue(wind, workStatues::writingBlockPalette); + // reportWorkingStatue(wind, workStatus::writingBlockPalette); // write block palette lite.writeListHead("BlockStatePalette", NBT::Compound, this->palette_size()); { std::string pure_block_id; pure_block_id.reserve(1024); - memset(pure_block_id.data(), 0, pure_block_id.capacity()); std::vector> properties; properties.reserve(64); @@ -376,10 +510,8 @@ bool Schem::export_litematic(std::string_view filename, } } - lite.writeListHead("Entities", NBT::Compound, 0); lite.writeListHead("PendingBlockTicks", NBT::Compound, 0); - lite.writeListHead("PendingFluidTiccks", NBT::Compound, 0); - lite.writeListHead("TileEntities", NBT::Compound, 0); + lite.writeListHead("PendingFluidTicks", NBT::Compound, 0); // write 3D std::vector shrinked; @@ -394,6 +526,21 @@ bool Schem::export_litematic(std::string_view filename, } } // progressAdd(wind, size3D[0]); + + lite.writeListHead("Entities", NBT::tagType::Compound, + this->entities.size()); + for (auto &entity : this->entities) { + assert(entity); + lite.writeCompound(); + auto res = entity->dump(lite, this->MC_data_ver); + if (not res) { + lite.endCompound(); + return tl::make_unexpected( + std::make_pair(SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_ENTITY, + std::move(res.error()))); + } + lite.endCompound(); + } } lite.endCompound(); // end current region } @@ -411,64 +558,32 @@ bool Schem::export_litematic(std::string_view filename, case ::SCL_gameVersion::MC17: case ::SCL_gameVersion::MC18: case ::SCL_gameVersion::MC19: + case ::SCL_gameVersion::MC20: + case ::SCL_gameVersion::MC21: lite.writeInt("MinecraftDataVersion", (int)this->MC_version_number()); lite.writeInt("Version", 5); break; default: - std::cerr << "Wrong game version!" << std::endl; lite.close(); - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::UNKNOWN_MAJOR_GAME_VERSION; - } - if (error_str != nullptr) { - *error_str = - "Unknown major game version! Only 1.12 to 1.19 is " - "supported, but given value " + - std::to_string((int)this->MC_major_ver); - } - return false; + return tl::make_unexpected(std::make_pair( + SCL_errorFlag::UNKNOWN_MAJOR_GAME_VERSION, + std::format("Unknown major game version! Only 1.12 to 1.19 is " + "supported, but given value {}", + int(this->MC_major_ver)))); } lite.close(); - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::NO_ERROR_OCCUR; - } - if (error_str != nullptr) { - *error_str = ""; - } - return true; + return {}; } -bool Schem::export_structure(std::string_view filename, - const bool is_air_structure_void, - SCL_errorFlag *const error_flag, - std::string *const error_str) const noexcept { - if (std::filesystem::path(filename).extension() != ".nbt") { - // wrong extension - - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_WRONG_EXTENSION; - } - if (error_str != nullptr) { - *error_str = "The filename externsion must be \".nbt\"."; - } - return false; - } - - // check for invalid blocks +tl::expected> +Schem::export_structure(std::string_view filename, + const bool is_air_structure_void) const noexcept { + // { - std::array pos; - if (this->have_invalid_block(&pos[0], &pos[1], &pos[2])) { - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_BLOCKS; - } - if (error_str != nullptr) { - *error_str = "The first invalid block is at x="; - *error_str += std::to_string(pos[0]) + ", y="; - *error_str += std::to_string(pos[1]) + ", z="; - *error_str += std::to_string(pos[2]); - } - return false; + auto res = this->pre_check(filename, ".nbt"); + if (not res) { + return res; } } @@ -487,42 +602,31 @@ bool Schem::export_structure(std::string_view filename, "minecraft:air in your block palette." << std::endl; - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_STRUCTURE_REQUIRES_AIR; - } - if (error_str != nullptr) { - *error_str = - "You assigned is_air_structure_void=false, but there is no " - "minecraft:air in your block palette."; - } - return false; + return tl::make_unexpected(std::make_pair( + SCL_errorFlag::EXPORT_SCHEM_STRUCTURE_REQUIRES_AIR, + "You assigned is_air_structure_void=false, but there is no " + "minecraft:air in your block palette.")); } /* - reportWorkingStatue(wind, workStatues::writingMetaInfo); + reportWorkingStatue(wind, workStatus::writingMetaInfo); progressRangeSet(wind, 0, 100 + Build.size(), 0); */ NBT::NBTWriter file; if (!file.open(filename.data())) { - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE; - } - if (error_str != nullptr) { - *error_str = "Failed to open file : "; - *error_str += filename; - } - return false; + return tl::make_unexpected( + std::make_pair(SCL_errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE, + std::format("Failed to open file {}", filename))); } - file.writeListHead("entities", NBT::Byte, 0); file.writeListHead("size", NBT::Int, 3); { file.writeInt("This should never be shown", x_range()); file.writeInt("This should never be shown", y_range()); file.writeInt("This should never be shown", z_range()); } - // reportWorkingStatue(wind, workStatues::writingBlockPalette); + // reportWorkingStatue(wind, workStatus::writingBlockPalette); file.writeListHead("palette", NBT::Compound, palette_size()); { std::string pure_block_id; @@ -594,7 +698,41 @@ bool Schem::export_structure(std::string_view filename, } } } - // finish writting the whole 3D array + // finish writing the whole 3D array + + // write entities + file.writeListHead("entities", NBT::tagType::Compound, + this->entities.size()); + for (auto &entity : this->entities) { + file.writeCompound(); + { + file.writeListHead("pos", NBT::tagType::Double, 3); + for (double pos : entity->position()) { + file.writeDouble("", pos); + } + assert(file.isInCompound()); + file.writeListHead("blockPos", NBT::tagType::Int, 3); + for (double pos : entity->position()) { + file.writeInt("", std::floor(pos)); + } + assert(file.isInCompound()); + file.writeCompound("nbt"); + { + auto res = entity->dump(file, this->MC_data_ver); + if (not res) { + file.endCompound(); + file.close_file(); + return tl::make_unexpected( + std::make_pair(SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_ENTITY, + std::move(res.error()))); + } + } + file.endCompound(); + } + file.endCompound(); + } + // finish writing entities + assert(file.isInCompound()); switch (this->MC_major_ver) { case ::SCL_gameVersion::MC12: @@ -605,158 +743,222 @@ bool Schem::export_structure(std::string_view filename, case ::SCL_gameVersion::MC17: case ::SCL_gameVersion::MC18: case ::SCL_gameVersion::MC19: + case ::SCL_gameVersion::MC20: + case ::SCL_gameVersion::MC21: file.writeInt("MinecraftDataVersion", (int)this->MC_data_ver); break; default: std::cerr << "Wrong game version!" << std::endl; file.close(); - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::UNKNOWN_MAJOR_GAME_VERSION; - } - if (error_str != nullptr) { - *error_str = - "Unknown major game version! Only 1.12 to 1.19 is " - "supported, but given value " + - std::to_string((int)this->MC_major_ver); - } - return false; + return tl::make_unexpected(std::make_pair( + SCL_errorFlag::UNKNOWN_MAJOR_GAME_VERSION, + std::format("Unknown major game version! Only 1.12 to 1.21 is " + "supported, but given value {}", + (int)this->MC_major_ver))); } } file.close(); - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::NO_ERROR_OCCUR; - } - if (error_str != nullptr) { - *error_str = ""; - } - return true; + return {}; } -bool Schem::export_WESchem(std::string_view filename, - const WorldEditSchem_info &info, - SCL_errorFlag *const error_flag, - std::string *const error_str) const noexcept { - if (std::filesystem::path(filename).extension() != ".schem") { - // wrong extension - - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_WRONG_EXTENSION; - } - if (error_str != nullptr) { - *error_str = "The filename externsion must be \".schem\"."; - } - return false; - } - - // check for invalid blocks +tl::expected> Schem::export_WESchem( + std::string_view filename, const WorldEditSchem_info &info) const noexcept { + // { - std::array pos; - if (this->have_invalid_block(&pos[0], &pos[1], &pos[2])) { - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_HAS_INVALID_BLOCKS; - } - if (error_str != nullptr) { - *error_str = "The first invalid block is at x="; - *error_str += std::to_string(pos[0]) + ", y="; - *error_str += std::to_string(pos[1]) + ", z="; - *error_str += std::to_string(pos[2]); - } - return false; + auto res = this->pre_check(filename, ".schem"); + if (not res) { + return res; } } if (this->MC_major_ver <= SCL_gameVersion::MC12) { - if (error_flag != nullptr) { - *error_flag = ::SCL_errorFlag::EXPORT_SCHEM_MC12_NOT_SUPPORTED; - } - if (error_str != nullptr) { - *error_str = - "Exporting a schematic as 1.12 WorldEdit .schematic format " - "is not supported. Try other tools."; - } - return false; + return tl::make_unexpected(std::make_pair( + ::SCL_errorFlag::EXPORT_SCHEM_MC12_NOT_SUPPORTED, + "Exporting a schematic as 1.12 WorldEdit .schematic format " + "is not supported. Try other tools.")); } NBT::NBTWriter file; - if (!file.open(filename.data())) { - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE; - } - if (error_str != nullptr) { - *error_str = "Failed to open file : "; - *error_str += filename; - } - return false; + if (not file.open(filename.data())) { + return tl::make_unexpected( + std::make_pair(SCL_errorFlag::EXPORT_SCHEM_FAILED_TO_CREATE_FILE, + std::format("Failed to open file {}", filename))); } - // write metadata - file.writeCompound("Metadata"); - { - file.writeInt("WEOffsetX", info.WE_offset[0]); - file.writeInt("WEOffsetY", info.WE_offset[1]); - file.writeInt("WEOffsetZ", info.WE_offset[2]); - file.writeString("Name", info.schem_name_utf8.data()); - file.writeString("Author", info.author_utf8.data()); - file.writeLong("Date", info.date); - - file.writeListHead("RequiredMods", NBT::String, - info.required_mods_utf8.size()); + auto write_version = [&]() { // data version + file.writeInt("DataVersion", (int)this->MC_data_ver); + }; + auto write_palette = [&]() { + file.writeCompound("Palette"); { - for (const auto &str : info.required_mods_utf8) { - file.writeString("", str.data()); + for (int idx = 0; idx < static_cast(block_id_list.size()); idx++) { + // continue; + [[maybe_unused]] const auto ret = + file.writeInt(block_id_list[idx], idx); + assert(ret > 0); } - } - // finish list - } // finish compound - file.endCompound(); + } // finish palette + file.endCompound(); + }; + auto write_offset = [&]() { + file.writeIntArrayHead("Offset", 3); + { + file.writeInt("", info.offset[0]); + file.writeInt("", info.offset[1]); + file.writeInt("", info.offset[2]); + } // end array + }; + auto write_shape = [&]() { + file.writeShort("Width", x_range()); + file.writeShort("Height", y_range()); + file.writeShort("Length", z_range()); + }; - file.writeCompound("Palette"); - { - for (int idx = 0; idx < int(block_id_list.size()); idx++) { - file.writeInt(block_id_list[idx].c_str(), idx); - } - } // finished palette - file.endCompound(); + std::vector blockdata; + ::shrink_bytes_weSchem( + {this->xzy.data(), static_cast(this->xzy.size())}, + block_id_list.size(), &blockdata); + auto write_blocks = [&](const char *key) { + std::span data{reinterpret_cast(blockdata.data()), + blockdata.size() * sizeof(uint8_t)}; + file.writeByteArrayHead(key, data.size()); + for (int8_t d : data) { + file.writeByte("", d); + } + // end array + }; + + if (this->MC_major_ver <= SCL_gameVersion::MC19) { + // write metadata + file.writeCompound("Metadata"); + { + file.writeInt("WEOffsetX", info.WE_offset[0]); + file.writeInt("WEOffsetY", info.WE_offset[1]); + file.writeInt("WEOffsetZ", info.WE_offset[2]); + file.writeString("Name", info.schem_name_utf8.data()); + file.writeString("Author", info.author_utf8.data()); + file.writeLong("Date", info.date); + + file.writeListHead("RequiredMods", NBT::String, + info.required_mods_utf8.size()); + { + for (const auto &str : info.required_mods_utf8) { + file.writeString("", str.data()); + } + } + // finish list + } // finish compound + file.endCompound(); - file.writeListHead("BlockEntities", NBT::Compound, 0); + file.writeInt("Version", 2); // schematic format version + write_version(); - file.writeInt("DataVersion", (int)this->MC_data_ver); + write_palette(); + file.writeInt("PaletteMax", block_id_list.size()); - file.writeShort("Width", x_range()); - file.writeShort("Height", y_range()); - file.writeShort("Length", z_range()); + file.writeListHead("BlockEntities", NBT::Compound, 0); - file.writeInt("Version", 2); + write_shape(); + write_offset(); + write_blocks("BlockData"); + } else { + // 1.20+ + file.writeCompound("Schematic"); + { + file.writeCompound("Metadata"); + { + file.writeLong("Date", info.date); - file.writeInt("PaletteMax", block_id_list.size()); + file.writeCompound("WorldEdit"); + { + file.writeString("Version", "unknown"); + file.writeString("EditingPlatform", "enginehub:fabric"); + file.writeIntArrayHead("Origin", 3); + { + for (int i = 0; i < 3; i++) { + file.writeInt("", 0); + } + } + } + file.endCompound(); + } + file.endCompound(); // finish metadata - std::vector blockdata; - ::shrink_bytes_weSchem(xzy.data(), xzy.size(), block_id_list.size(), - &blockdata); + file.writeInt("Version", 3); // schematic format version + write_version(); + write_shape(); + write_offset(); - file.writeByteArrayHead("BlockData", blockdata.size()); - { - const int8_t *data = reinterpret_cast(blockdata.data()); - for (int64_t idx = 0; idx < int64_t(blockdata.size()); idx++) { - file.writeByte("", data[idx]); + file.writeCompound("Blocks"); + { + file.writeListHead("BlockEntities", NBT::tagType::Compound, 0); + write_palette(); + write_blocks("Data"); + } + file.endCompound(); // finish Blocks } - } // end array - - file.writeIntArrayHead("Offset", 3); - { - file.writeInt("x", info.offset[0]); - file.writeInt("y", info.offset[1]); - file.writeInt("z", info.offset[2]); - } // end array + file.endCompound(); + } file.close(); - if (error_flag != nullptr) { - *error_flag = SCL_errorFlag::NO_ERROR_OCCUR; + return {}; +} + +tl::expected +libSchem::Schem::remove_unused_ids() noexcept { + remove_unused_id_result stat; + stat.id_count_before = this->palette_size(); + std::vector id_used; + id_used.resize(this->palette_size(), false); + for (const ele_t blkid : *this) { + if (blkid >= this->palette_size()) [[unlikely]] { + return tl::make_unexpected( + std::format("The scheme required block with id = {}, but the block " + "palette has only {} blocks", + blkid, this->palette_size())); + } + id_used[blkid] = true; + } + + std::vector id_map_old_to_new; + { + ele_t next_id = 0; + for (size_t id = 0; id < id_used.size(); id++) { + if (id_used[id]) { + id_map_old_to_new.emplace_back(next_id++); + } else { + id_map_old_to_new.emplace_back(invalid_ele_t); + } + } } - if (error_str != nullptr) { - *error_str = ""; + if (id_map_old_to_new.empty()) { + id_map_old_to_new.emplace_back(0); } - return true; -} + // update block id list, remove unused block from palette + { + ele_t idx = 0; + for (auto it = this->block_id_list.begin(); + it not_eq this->block_id_list.end();) { + if (id_used[idx]) { + idx++; + ++it; + continue; + } + it = this->block_id_list.erase(it); + idx++; + } + } + // assert(this->block_id_list.size()==id_map_old_to_new.size()); + // update 3d matrix xzy + + for (ele_t &blkid : *this) { + assert(id_used[blkid]); + const auto new_id = id_map_old_to_new[blkid]; + blkid = new_id; + assert(new_id < this->block_id_list.size()); + } + stat.id_count_after = this->block_id_list.size(); + return stat; +} \ No newline at end of file diff --git a/utilities/Schem/Schem.h b/utilities/Schem/Schem.h index a73bfb36..d010fea1 100644 --- a/utilities/Schem/Schem.h +++ b/utilities/Schem/Schem.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -27,10 +27,21 @@ This file is part of SlopeCraft. #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include #include "SC_GlobalEnums.h" +#include "entity.h" namespace libSchem { // template @@ -41,8 +52,8 @@ struct litematic_info { std::string regionname_utf8{"Software developer : TokiNoBug."}; std::string author_utf8{"SlopeCraft"}; std::string destricption_utf8{"This litematic is generated by SlopeCraft."}; - uint64_t time_created; //< Miliseconds since 1970 - uint64_t time_modified; //< Miliseconds since 1970 + uint64_t time_created; //< Miliseconds since 1970 + uint64_t time_modified; //< Miliseconds since 1970 }; struct WorldEditSchem_info { @@ -53,18 +64,18 @@ struct WorldEditSchem_info { "WorldEdit schem generated by SlopeCraft, deveploer TokiNoBug."}; std::string author_utf8{"SlopeCraft"}; std::vector required_mods_utf8{}; - uint64_t date; //< Miliseconds since 1970 + uint64_t date; //< Miliseconds since 1970 }; class Schem { -public: + public: // using ele_t = std::conditional_t<(max_block_count > 256), uint16_t, // uint8_t>; using ele_t = uint16_t; static constexpr ele_t invalid_ele_t = ~ele_t(0); -private: + private: /// The 3 indices are stored in [x][z][y] col-major, and in minecraft the /// best storage is [y][z][x] row-major Eigen::Tensor xzy; @@ -74,25 +85,43 @@ class Schem { ::SCL_gameVersion MC_major_ver; MCDataVersion::MCDataVersion_t MC_data_ver; -public: + std::vector> entities; + + public: Schem() { xzy.resize(0, 0, 0); } + Schem(const Schem &src) noexcept + : block_id_list{src.block_id_list}, + MC_major_ver{src.MC_major_ver}, + MC_data_ver{src.MC_data_ver}, + xzy{src.xzy} { + this->entities.reserve(src.entities.size()); + for (auto &entity : src.entities) { + this->entities.emplace_back(entity->clone()); + } + } + Schem(Schem &&) = default; Schem(int64_t x, int64_t y, int64_t z) { - xzy.resize(x, y, z); + xzy.resize(x, z, y); xzy.setZero(); } + Schem &operator=(Schem &&src) = default; + Schem &operator=(const Schem &src) noexcept { + Schem temp{src}; + std::swap(*this, temp); + return *this; + } + + std::string check_size() const noexcept; + static std::string check_size(int64_t x, int64_t y, int64_t z) noexcept; + inline void set_zero() noexcept { xzy.setZero(); } inline ele_t *data() noexcept { return xzy.data(); } inline const ele_t *data() const noexcept { return xzy.data(); } - inline void resize(int64_t x, int64_t y, int64_t z) noexcept { - if (x < 0 || y < 0 || z < 0) { - return; - } - xzy.resize(x, z, y); - } + void resize(int64_t x, int64_t y, int64_t z); inline bool check_version_id() const noexcept { return MCDataVersion::is_data_version_suitable(this->MC_major_ver, @@ -153,11 +182,18 @@ class Schem { } } + void stat_blocks(std::vector &dest) const noexcept; + [[nodiscard]] std::vector stat_blocks() const noexcept { + std::vector buf; + this->stat_blocks(buf); + return buf; + } + inline int64_t x_range() const noexcept { return xzy.dimension(0); } inline int64_t y_range() const noexcept { return xzy.dimension(2); } inline int64_t z_range() const noexcept { return xzy.dimension(1); } - inline int palette_size() const noexcept { return block_id_list.size(); } + inline size_t palette_size() const noexcept { return block_id_list.size(); } inline int64_t size() const noexcept { return xzy.size(); } @@ -165,13 +201,14 @@ class Schem { /// Schem will copy these strings void set_block_id(const char *const *const block_ids, const int num) noexcept; + void set_block_id(std::span id) noexcept; inline MCDataVersion::MCDataVersion_t MC_version_number() const noexcept { return this->MC_data_ver; } - inline void - set_MC_version_number(const MCDataVersion::MCDataVersion_t _) noexcept { + inline void set_MC_version_number( + const MCDataVersion::MCDataVersion_t _) noexcept { this->MC_data_ver = _; } @@ -194,8 +231,8 @@ class Schem { * \return true The schem contains invalid block(s) * \return false The schem don't contain any invalid block. */ - bool - have_invalid_block(int64_t *first_invalid_block_idx = nullptr) const noexcept; + bool have_invalid_block( + int64_t *first_invalid_block_idx = nullptr) const noexcept; /** * \brief Search for invalid block. @@ -212,23 +249,147 @@ class Schem { void process_mushroom_states() noexcept; -public: - bool export_litematic(std::string_view filename, - const litematic_info &info = litematic_info(), - SCL_errorFlag *const error_flag = nullptr, - std::string *const error_str = nullptr) const noexcept; - - bool export_structure(std::string_view filename, - const bool is_air_structure_void = true, - SCL_errorFlag *const error_flag = nullptr, - std::string *const error_str = nullptr) const noexcept; - - bool export_WESchem(std::string_view filename, - const WorldEditSchem_info &info = WorldEditSchem_info(), - SCL_errorFlag *const error_flag = nullptr, - std::string *const error_str = nullptr) const noexcept; + void process_mushroom_states_fast() noexcept; + + auto &entity_list() noexcept { return this->entities; } + auto &entity_list() const noexcept { return this->entities; } + + template + struct schem_slice { + Eigen::Array offset; + T content; + }; + + [[nodiscard]] tl::expected, 3>, + std::string> + split_by_block_size(std::span x_block_length, + std::span y_block_length, + std::span z_block_length) const noexcept; + + struct remove_unused_id_result { + size_t id_count_before; + size_t id_count_after; + }; + + [[nodiscard]] tl::expected + remove_unused_ids() noexcept; + + protected: + [[nodiscard]] Schem slice_no_check( + std::span, 3> xyz_index_range) + const noexcept; + + public: + static void update_error_dest( + const tl::expected> &result, + SCL_errorFlag *const error_flag, std::string *const error_str) noexcept { + if (result) { + if (error_flag not_eq nullptr) { + *error_flag = SCL_errorFlag::NO_ERROR_OCCUR; + } + if (error_str not_eq nullptr) { + error_str->clear(); + } + } else { + auto &err = result.error(); + if (error_flag not_eq nullptr) { + *error_flag = err.first; + } + if (error_str not_eq nullptr) { + *error_str = err.second; + } + } + } + tl::expected> export_litematic( + std::string_view filename, const litematic_info &info) const noexcept; + + tl::expected> export_structure( + std::string_view filename, + const bool is_air_structure_void) const noexcept; + + tl::expected> export_WESchem( + std::string_view filename, + const WorldEditSchem_info &info) const noexcept; + + [[deprecated]] bool export_litematic( + std::string_view filename, const litematic_info &info, + SCL_errorFlag *const error_flag, + std::string *const error_str) const noexcept { + auto res = this->export_litematic(filename, info); + update_error_dest(res, error_flag, error_str); + return res.has_value(); + } + + [[deprecated]] bool export_structure( + std::string_view filename, const bool is_air_structure_void, + SCL_errorFlag *const error_flag, + std::string *const error_str) const noexcept { + auto res = this->export_structure(filename, is_air_structure_void); + update_error_dest(res, error_flag, error_str); + return res.has_value(); + } + + [[deprecated]] bool export_WESchem( + std::string_view filename, const WorldEditSchem_info &info, + SCL_errorFlag *const error_flag, + std::string *const error_str) const noexcept { + auto res = this->export_WESchem(filename, info); + update_error_dest(res, error_flag, error_str); + return res.has_value(); + } + + private: + friend class cereal::access; + + tl::expected> pre_check( + std::string_view filename, std::string_view extension) const noexcept; + + template + void save(archive &ar) const { + ar(this->MC_major_ver); + ar(this->MC_data_ver); + ar(this->block_id_list); + const int64_t x{this->x_range()}, y{this->y_range()}, z{this->z_range()}; + ar(x, y, z); + ar(cereal::binary_data(this->xzy.data(), + this->xzy.size() * sizeof(uint16_t))); + } + + template + void load(archive &ar) { + ar(this->MC_major_ver); + ar(this->MC_data_ver); + ar(this->block_id_list); + int64_t x, y, z; + ar(x, y, z); + { + std::string err = check_size(x, y, z); + if (!err.empty()) { + throw std::runtime_error{err}; + } + } + this->resize(x, y, z); + ar(cereal::binary_data(this->xzy.data(), + this->xzy.size() * sizeof(uint16_t))); + } }; -} // namespace libSchem +/** + * Find minimum value >= a that can is multiple of b + * @tparam int_t + * @param a + * @param b + * @return + */ +template + requires std::integral && std::integral +constexpr int_t ceil_up_to(int_t a, uint_t b) { + if (a % b == 0) { + return a; + } + return ((a / b) + 1) * b; +} + +} // namespace libSchem -#endif // SCHEM_SCHEM_H \ No newline at end of file +#endif // SCHEM_SCHEM_H \ No newline at end of file diff --git a/utilities/Schem/bit_shrink.cpp b/utilities/Schem/bit_shrink.cpp index 5ab29c41..f4724285 100644 --- a/utilities/Schem/bit_shrink.cpp +++ b/utilities/Schem/bit_shrink.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -21,15 +21,12 @@ This file is part of SlopeCraft. */ #include "bit_shrink.h" - +#include "Schem.h" #include -#include #include #include #include -#include - #include // no flip now @@ -104,32 +101,34 @@ void bit_shrink_inverse_skip(void *const dest, const int64_t head_skip_bits, void shrink_bits(const uint16_t *const src, const size_t src_count, const int block_types, std::vector *const dest) noexcept { - if (src == nullptr || dest == nullptr || src_count <= 0 || block_types <= 1) { + if (src == nullptr || dest == nullptr || src_count <= 0) { return; } - const int bits_per_element = std::ceil(std::log2(block_types)); + const int bits_per_element = + std::max(std::ceil(std::log2(std::max(block_types, 1))), 2); //::std::cout << "bits_per_element = " << bits_per_element << ::std::endl; - - if (bits_per_element > 16) { - exit(1); - return; - } + assert(bits_per_element >= 2); + assert(bits_per_element <= 16); + // if (bits_per_element > 16) { + // exit(1); + // return; + // } const size_t total_bits = bits_per_element * src_count; - const size_t bytes_required = std::ceil(total_bits / 8.0); + const size_t bytes_required = libSchem::ceil_up_to(total_bits, 8) / 8; const size_t uint64_t_required = - std::ceil(bytes_required / float(sizeof(uint64_t))); + libSchem::ceil_up_to(bytes_required, sizeof(uint64_t)) / sizeof(uint64_t); dest->resize(uint64_t_required); memset(dest->data(), 0, bytes_required); const size_t head_skip_bits = bytes_required * 8 - total_bits; - //::std::cout << "total_bits = " << total_bits << '\n'; - //::std::cout << "bytes_required = " << bytes_required << '\n'; - //::std::cout << "head_skip_bits = " << head_skip_bits << '\n'; + // ::std::cout << "total_bits = " << total_bits << '\n'; + // ::std::cout << "bytes_required = " << bytes_required << '\n'; + // ::std::cout << "head_skip_bits = " << head_skip_bits << '\n'; bit_shrink_inverse_skip(dest->data(), head_skip_bits, src, src_count, bits_per_element); @@ -157,22 +156,12 @@ bool process_block_id( return ok; } -void shrink_bytes_weSchem(const uint16_t *src, const size_t src_count, - const int palette_max, +void shrink_bytes_weSchem(std::span src, const int palette_max, std::vector *const dest) noexcept { - if (palette_max <= 128) { - dest->resize(src_count); - for (size_t idx = 0; idx < src_count; idx++) { - dest->at(idx) = src[idx] & 0xFF; - } - return; - } - - dest->reserve(src_count * 2); + dest->reserve(src.size() * 2); dest->clear(); - for (size_t idx = 0; idx < src_count; idx++) { - uint16_t temp = src[idx]; + for (const uint16_t temp : src) { if (temp < 128) { dest->emplace_back(temp); } else { diff --git a/utilities/Schem/bit_shrink.h b/utilities/Schem/bit_shrink.h index 34f236f0..db442687 100644 --- a/utilities/Schem/bit_shrink.h +++ b/utilities/Schem/bit_shrink.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ This file is part of SlopeCraft. #define SCHEM_BITSHRINK_H #include +#include #include #include #include @@ -54,8 +55,7 @@ bool process_block_id( const std::string_view id, std::string *const pure_id, std::vector> *const traits); -void shrink_bytes_weSchem(const uint16_t *src, const size_t src_count, - const int palette_max, +void shrink_bytes_weSchem(std::span src, const int palette_max, std::vector *const dest) noexcept; class __mushroom_sides { diff --git a/utilities/Schem/entity.cpp b/utilities/Schem/entity.cpp new file mode 100644 index 00000000..9e912ebc --- /dev/null +++ b/utilities/Schem/entity.cpp @@ -0,0 +1,88 @@ +// +// Created by Joseph on 2024/8/6. +// + +#include +#include + +#include "entity.h" + +tl::expected libSchem::entity::dump( + NBT::NBTWriter &destination, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + if (not destination.isInCompound()) { + return tl::make_unexpected( + "You should write entities fields into a compound, but the NBTWriter " + "is not writing a compound."); + } + size_t bytes = 0; + + if (this->id() not_eq "player") { + bytes += destination.writeString("id", this->id().data()); + } + { + destination.writeListHead("Pos", NBT::tagType::Double, 3); + for (double cord : this->position()) { + bytes += destination.writeDouble("", cord); + } + assert(destination.isInCompound()); + } + { + bytes += destination.writeListHead("Rotation", NBT::tagType::Float, 2); + for (float rot : this->rotation()) { + bytes += destination.writeFloat("", rot); + } + assert(destination.isInCompound()); + } + + return bytes; +} + +tl::expected libSchem::hangable::dump( + NBT::NBTWriter &destination, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + auto res = entity::dump(destination, data_version); + if (not res) { + return res; + } + size_t bytes = res.value(); + bytes += + destination.writeByte("Facing", static_cast(this->direction_)); + + const std::array keys{"TileX", "TileY", "TileZ"}; + for (size_t dim = 0; dim < 3; dim++) { + bytes += destination.writeInt(keys[dim], this->tile_position_[dim]); + } + return bytes; +} + +tl::expected libSchem::item_frame::dump( + NBT::NBTWriter &destination, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + auto res = hangable::dump(destination, data_version); + if (not res) { + return res; + } + size_t bytes = res.value(); + bytes += destination.writeByte("Fixed", this->fixed_); + bytes += destination.writeByte("Invisible", this->invisible_); + bytes += destination.writeFloat("ItemDropChance", this->item_drop_chance); + if (this->item_rotation < 0 or this->item_rotation > 7) { + return tl::make_unexpected( + std::format("Invalid item rotation {}, expected in range [0,7]", + int(this->item_rotation))); + } + bytes += destination.writeByte("ItemRotation", this->item_rotation); + + if (this->item_ not_eq nullptr) { + bytes += destination.writeCompound("Item"); + auto res_item = this->item_->dump(destination, data_version); + if (not res_item) { + return tl::make_unexpected( + std::format("Failed to dump item fields: {}", res_item.error())); + } + bytes += res_item.value(); + bytes += destination.endCompound(); + } + return bytes; +} \ No newline at end of file diff --git a/utilities/Schem/entity.h b/utilities/Schem/entity.h new file mode 100644 index 00000000..774fb288 --- /dev/null +++ b/utilities/Schem/entity.h @@ -0,0 +1,142 @@ +// +// Created by Joseph on 2024/8/6. +// + +#ifndef SLOPECRAFT_ENTITY_H +#define SLOPECRAFT_ENTITY_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "item.h" +#include + +namespace NBT { +template +class NBTWriter; +} + +namespace libSchem { +class entity { + public: + virtual std::string_view id() const noexcept = 0; + virtual ~entity() = default; + + /// xyz + [[nodiscard]] virtual std::array position() const noexcept = 0; + /// [horizontal, vertical]. north = [-180,0], east = [-90,0], south = [0,0], + /// west = [90,0], north(again) = [180,0]. + [[nodiscard]] virtual std::array rotation() const noexcept = 0; + + virtual tl::expected dump( + NBT::NBTWriter& destination, + MCDataVersion::MCDataVersion_t data_version) const noexcept; + + virtual std::unique_ptr clone() const noexcept = 0; +}; + +enum class hangable_facing_direction : int8_t { + south = 3, + west = 4, + north = 2, + east = 5, + top = 1, + bottom = 0, +}; + +class hangable : public entity { + public: + hangable_facing_direction direction_{hangable_facing_direction::north}; + std::array tile_position_{0, 0, 0}; + + std::array position() const noexcept override { + std::array ret; + for (size_t i = 0; i < 3; i++) { + ret[i] = this->tile_position_[i]; + } + return ret; + } + + std::array rotation() const noexcept override { + using hfd = hangable_facing_direction; + switch (this->direction_) { + case hfd::north: + return {-180, 0}; + case hfd::east: + return {-90, 0}; + case hfd::south: + return {0, 0}; + case hfd::west: + return {90, 0}; + case hfd::top: + return {0, 90}; + case hfd::bottom: + return {0, -90}; + } + return {0, 0}; + } + + tl::expected dump( + NBT::NBTWriter& destination, + MCDataVersion::MCDataVersion_t data_version) const noexcept override; +}; + +enum class item_frame_variant : uint8_t { + common, + glowing, +}; + +class item_frame : public hangable { + public: + /// true to prevent it from dropping if it has no support block, being moved + /// (e.g. by pistons), taking damage (except from creative players), and + /// placing an item in it, removing its item, or rotating it. + bool fixed_{false}; + /// Whether the item frame is invisible. The contained item or map remains + /// visible. + bool invisible_{false}; + + int8_t item_rotation{0}; + float item_drop_chance{1}; + + std::unique_ptr item_{nullptr}; + + item_frame_variant variant_{item_frame_variant::common}; + + explicit item_frame() = default; + + explicit item_frame(const item_frame& src) + : fixed_{src.fixed_}, + invisible_{src.invisible_}, + item_rotation{src.item_rotation}, + item_drop_chance{src.item_drop_chance}, + variant_{src.variant_}, + item_{src.item_->clone()} {} + + std::string_view id() const noexcept override { + switch (this->variant_) { + case item_frame_variant::common: + return "item_frame"; + case item_frame_variant::glowing: + return "glow_item_frame"; + } + return {}; + } + + tl::expected dump( + NBT::NBTWriter& destination, + MCDataVersion::MCDataVersion_t data_version) const noexcept override; + + std::unique_ptr clone() const noexcept override { + return std::make_unique(*this); + } +}; +} // namespace libSchem + +#endif // SLOPECRAFT_ENTITY_H diff --git a/utilities/Schem/item.cpp b/utilities/Schem/item.cpp new file mode 100644 index 00000000..9d7e1c61 --- /dev/null +++ b/utilities/Schem/item.cpp @@ -0,0 +1,77 @@ +// +// Created by Joseph on 2024/8/6. +// + +#include "item.h" +#include + +tl::expected libSchem::item::dump( + NBT::NBTWriter &dest, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + auto res = this->dump_basic_fields(dest, data_version); + if (not res) { + return res; + } + size_t bytes = res.value(); + const char *key = nullptr; + if (data_version < MCDataVersion::MCDataVersion_t::Java_1_20_5) { + key = "tags"; + } else { + key = "components"; + } + { + bytes += dest.writeCompound(key); + auto res_tags = this->dump_tags(dest, data_version); + if (not res_tags) { + dest.endCompound(); + return res_tags; + } + bytes += res_tags.value(); + bytes += dest.endCompound(); + } + return bytes; +} + +tl::expected libSchem::item::dump_basic_fields( + NBT::NBTWriter &dest, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + if (not dest.isInCompound()) { + return tl::make_unexpected( + "You should dump item fields into a compound, but the NBT write is not " + "writing a compound."); + } + + size_t bytes = 0; + bytes += dest.writeString("id", this->id().data()); + if (data_version < MCDataVersion::MCDataVersion_t::Java_1_20_5) { + bytes += dest.writeByte("Count", this->count_); + } else { + bytes += dest.writeInt("count", this->count_); + } + return bytes; +} + +tl::expected libSchem::item::dump_tags( + NBT::NBTWriter &dest, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + if (not dest.isInCompound()) { + return tl::make_unexpected("You should dump item tags into a compound."); + } + return 0; +} + +tl::expected libSchem::filled_map::dump_tags( + NBT::NBTWriter &dest, + MCDataVersion::MCDataVersion_t data_version) const noexcept { + auto res = item::dump_tags(dest, data_version); + if (not res) { + return res; + } + size_t bytes = res.value(); + if (data_version < MCDataVersion::MCDataVersion_t::Java_1_20_5) { + bytes += dest.writeInt("map", this->map_id); + } else { + bytes += dest.writeInt("minecraft:map_id", this->map_id); + } + return bytes; +} \ No newline at end of file diff --git a/utilities/Schem/item.h b/utilities/Schem/item.h new file mode 100644 index 00000000..7a5aef64 --- /dev/null +++ b/utilities/Schem/item.h @@ -0,0 +1,64 @@ +// +// Created by Joseph on 2024/8/6. +// + +#ifndef SLOPECRAFT_ITEM_H +#define SLOPECRAFT_ITEM_H + +#include +#include +#include +#include +#include + +#include +#include + +namespace NBT { +template +class NBTWriter; +} + +namespace libSchem { +class item { + public: + virtual ~item() = default; + + virtual std::string_view id() const noexcept = 0; + + int8_t count_{1}; + virtual tl::expected dump( + NBT::NBTWriter&, + MCDataVersion::MCDataVersion_t data_version) const noexcept; + + [[nodiscard]] virtual std::unique_ptr clone() const noexcept = 0; + + protected: + virtual tl::expected dump_basic_fields( + NBT::NBTWriter&, + MCDataVersion::MCDataVersion_t data_version) const noexcept; + virtual tl::expected dump_tags( + NBT::NBTWriter&, + MCDataVersion::MCDataVersion_t data_version) const noexcept; +}; + +class filled_map : public item { + public: + /// The map number + int map_id{0}; + + std::string_view id() const noexcept override { + return "minecraft:filled_map"; + } + std::unique_ptr clone() const noexcept override { + return std::make_unique(*this); + } + + protected: + tl::expected dump_tags( + NBT::NBTWriter&, + MCDataVersion::MCDataVersion_t data_version) const noexcept override; +}; +} // namespace libSchem + +#endif // SLOPECRAFT_ITEM_H diff --git a/utilities/Schem/mushroom.cpp b/utilities/Schem/mushroom.cpp new file mode 100644 index 00000000..1d3a56a5 --- /dev/null +++ b/utilities/Schem/mushroom.cpp @@ -0,0 +1,254 @@ +#include "mushroom.h" +#include +#include +#include +#include +namespace lsi = libSchem::internal; + +#include "Schem.h" + +std::optional lsi::pureid_to_type( + std::string_view pid) noexcept { + if (pid == "brown_mushroom_block") { + return mushroom_type::brown; + } + if (pid == "red_mushroom_block") { + return mushroom_type::red; + } + if (pid == "mushroom_stem") { + return mushroom_type::stem; + } + return std::nullopt; +} + +bool lsi::is_mushroom(std::string_view blkid) noexcept { + blkid::char_range pure_name; + if (!blkid::process_blk_id(blkid, nullptr, &pure_name, nullptr)) { + // invalid block id + return false; + } + + return pureid_to_type({pure_name.begin(), pure_name.end()}).has_value(); +} + +lsi::mushroom_state::mushroom_state(std::array _stoma) + : m_is_stoma{_stoma} {} + +std::string lsi::mushroom_state::to_block_state_list() const noexcept { + return std::format("up={},down={},north={},south={},east={},west={}", + m_is_stoma[0], m_is_stoma[1], m_is_stoma[2], m_is_stoma[3], + m_is_stoma[4], m_is_stoma[5]); +} + +uint8_t lsi::mushroom_state::to_compressed_bits() const noexcept { + uint8_t ret{0}; + for (bool val : this->m_is_stoma) { + ret |= uint8_t(val); + ret = ret << 1; + } + + return ret; +} + +std::optional private_fun_bsl_to_mushroom_state( + const std::vector> + &bs) noexcept { + using namespace lsi; + std::array stoma; + stoma.fill(0); + std::array set_times; + set_times.fill(0); + + for (const auto &pair : bs) { + std::string_view key_str{pair.first.begin(), pair.first.end()}; + auto key = magic_enum::enum_cast(key_str); + if (!key.has_value()) { + // invalid key + return std::nullopt; + } + + const auto dir = key.value(); + std::string_view value_str{pair.second.begin(), pair.second.end()}; + + bool val; + bool is_val_ok{false}; + if (value_str == "true") { + val = true; + is_val_ok = true; + } + if (value_str == "false") { + val = false; + is_val_ok = true; + } + + if (!is_val_ok) { + return std::nullopt; + } + + stoma[size_t(dir)] = val; + set_times[size_t(dir)]++; + } + + for (auto times : set_times) { + if (times > 1) { + return std::nullopt; + } + } + + return mushroom_state{stoma}; +} + +std::optional lsi::mushroom_state::from_block_state_list( + std::string_view bsl) noexcept { + using namespace blkid; + std::vector> bs; + + if (!blkid::process_state_list({&*bsl.begin(), &*bsl.end()}, &bs, nullptr)) { + // invalid blockstatelist + return std::nullopt; + } + + return private_fun_bsl_to_mushroom_state(bs); +} + +std::string_view lsi::mushroom_block::pureid() const noexcept { + switch (this->m_type) { + case mushroom_type::brown: + return "brown_mushroom_block"; + case mushroom_type::red: + return "red_mushroom_block"; + case mushroom_type::stem: + return "mushroom_stem"; + } + + return "Invalid enum value"; +} + +std::string lsi::mushroom_block::id(bool with_namespacename) const noexcept { + std::string_view nsn = (with_namespacename ? "minecraft::" : ""); + return std::format("{}{}[{}]", nsn, this->pureid(), + m_state.to_block_state_list()); +} + +std::optional lsi::mushroom_block::from_block_id( + std::string_view blikd) noexcept { + using namespace blkid; + + char_range pure_id; + std::vector> bs; + if (!process_blk_id(blikd, nullptr, &pure_id, &bs)) { + return std::nullopt; + } + + auto type = pureid_to_type({pure_id.begin(), pure_id.end()}); + if (!type.has_value()) { + return std::nullopt; + } + + auto state = private_fun_bsl_to_mushroom_state(bs); + if (!state.has_value()) { + return std::nullopt; + } + + mushroom_block ret; + ret.m_state = state.value(); + ret.m_type = type.value(); + return ret; +} + +struct mushroom_map { + using ele_t = libSchem::Schem::ele_t; + std::unordered_map + blk_to_ele; + std::unordered_map ele_to_blk; + std::vector *palette{nullptr}; + + inline bool is_mushroom(ele_t e) const noexcept { + return this->ele_to_blk.contains(e); + } + + inline ele_t operator[](libSchem::internal::mushroom_block mb) noexcept { + auto it = this->blk_to_ele.find(mb); + if (it != this->blk_to_ele.end()) { + return it->second; + } + const ele_t new_idx = this->palette->size(); + this->palette->emplace_back(mb.id(true)); + + this->blk_to_ele.emplace(mb, new_idx); + this->ele_to_blk.emplace(new_idx, mb); + return new_idx; + } + + inline libSchem::internal::mushroom_block at(ele_t idx) const noexcept { + assert(this->is_mushroom(idx)); + return this->ele_to_blk.at(idx); + } + + inline void emplace(ele_t ele, + libSchem::internal::mushroom_block blk) noexcept { + this->blk_to_ele.emplace(blk, ele); + this->ele_to_blk.emplace(ele, blk); + } +}; + +void libSchem::Schem::process_mushroom_states_fast() noexcept { + using libSchem::internal::direction; + using libSchem::internal::mushroom_block; + mushroom_map mmap; + mmap.blk_to_ele.reserve(64 * 3); + mmap.ele_to_blk.reserve(64 * 3); + mmap.palette = &this->block_id_list; + + for (ele_t i = 0; i < this->block_id_list.size(); i++) { + const auto &id = this->block_id_list[i]; + + auto mb = mushroom_block::from_block_id(id); + if (!mb.has_value()) { + continue; + } + + mmap.ele_to_blk.emplace(i, mb.value()); + mmap.blk_to_ele.emplace(mb.value(), i); + } + + auto &self = *this; + + for (int64_t y = 0; y < this->y_range(); y++) { + for (int64_t z = 0; z < this->z_range(); z++) { + for (int64_t x = 0; x < this->x_range(); x++) { + // skip if not a mushroom + if (!mmap.is_mushroom(self(x, y, z))) { + continue; + } + + mushroom_block cur_blk = mmap.at(self(x, y, z)); + + if ((x > 0) && mmap.is_mushroom(self(x - 1, y, z))) { + cur_blk.state().set_stoma(direction::west, true); + } + if ((x < this->x_range() - 1) && mmap.is_mushroom(self(x + 1, y, z))) { + cur_blk.state().set_stoma(direction::east, true); + } + + if ((y > 0) && mmap.is_mushroom(self(x, y - 1, z))) { + cur_blk.state().set_stoma(direction::down, true); + } + if ((y < this->y_range() - 1) && mmap.is_mushroom(self(x, y + 1, z))) { + cur_blk.state().set_stoma(direction::up, true); + } + + if ((z > 0) && mmap.is_mushroom(self(x, y, z - 1))) { + cur_blk.state().set_stoma(direction::north, true); + } + if ((z < this->z_range() - 1) && mmap.is_mushroom(self(x, y, z + 1))) { + cur_blk.state().set_stoma(direction::south, true); + } + + // this step append a new block id when necessary, it's not read only + self(x, y, z) = mmap[cur_blk]; + } + } + } +} \ No newline at end of file diff --git a/utilities/Schem/mushroom.h b/utilities/Schem/mushroom.h new file mode 100644 index 00000000..31ef79b4 --- /dev/null +++ b/utilities/Schem/mushroom.h @@ -0,0 +1,102 @@ +#ifndef SLOPECRAFT_UTILITIES_SCHEM_MUSHROOM_H +#define SLOPECRAFT_UTILITIES_SCHEM_MUSHROOM_H +#include +#include +#include +#include +#include +#include +#include + +namespace libSchem { +namespace internal { + +enum class mushroom_type : uint8_t { red, brown, stem }; +enum class direction : uint8_t { up, down, north, south, east, west }; + +bool is_mushroom(std::string_view blkid) noexcept; + +std::optional pureid_to_type(std::string_view pid) noexcept; + +class mushroom_state { + private: + std::array m_is_stoma{false, false, false, false, false}; + + public: + mushroom_state() = default; + mushroom_state(std::array _stoma); + inline bool is_stoma(direction dir) const noexcept { + assert(size_t(dir) < 6); + return this->m_is_stoma[size_t(dir)]; + } + + inline void set_stoma(direction dir, bool set_to_stoma) noexcept { + assert(size_t(dir) < 6); + this->m_is_stoma[size_t(dir)] = set_to_stoma; + } + + std::string to_block_state_list() const noexcept; + + static std::optional from_block_state_list( + std::string_view bsl) noexcept; + + uint8_t to_compressed_bits() const noexcept; + + inline bool operator==(const mushroom_state another) const noexcept { + return this->to_compressed_bits() == another.to_compressed_bits(); + } +}; + +struct mushroom_state_hash { + inline uint64_t operator()(mushroom_state ms) const noexcept { + return std::hash()(ms.to_compressed_bits()); + } +}; + +class mushroom_block { + private: + // std::string m_pureid{""}; + mushroom_state m_state; + mushroom_type m_type; + + public: + inline mushroom_state& state() noexcept { return this->m_state; } + inline mushroom_state state() const noexcept { return this->m_state; } + inline void set_state(mushroom_state ms) noexcept { this->m_state = ms; } + + std::string_view pureid() const noexcept; + + /* +inline void set_pureid(std::string_view pid) noexcept { + this->m_pureid = pid; +} +*/ + + inline mushroom_type type() const noexcept { return this->m_type; } + + inline void set_type(mushroom_type mt) noexcept { this->m_type = mt; } + + std::string id(bool with_namespacename = true) const noexcept; + + static std::optional from_block_id( + std::string_view blikd) noexcept; + + inline bool operator==(const mushroom_block another) const noexcept { + return (this->m_state == another.m_state) && + (this->m_type == another.m_type); + } +}; + +struct mushroom_block_hash { + inline uint64_t operator()(mushroom_block mb) const noexcept { + const uint16_t upper = uint8_t(mb.type()); + const uint16_t lower = mb.state().to_compressed_bits(); + + return std::hash()((upper << 8) | lower); + } +}; + +} // namespace internal +} // namespace libSchem + +#endif // SLOPECRAFT_UTILITIES_SCHEM_MUSHROOM_H \ No newline at end of file diff --git a/utilities/StatMemory/CMakeLists.txt b/utilities/StatMemory/CMakeLists.txt new file mode 100644 index 00000000..04e72d55 --- /dev/null +++ b/utilities/StatMemory/CMakeLists.txt @@ -0,0 +1,56 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + + +add_library(StatMemory STATIC stat_memory.h) +find_package(tl-expected REQUIRED) +target_link_libraries(StatMemory PUBLIC tl::expected) +target_include_directories(StatMemory PUBLIC $) +target_compile_features(StatMemory PUBLIC cxx_std_23) +# Set source file by system +if (${WIN32}) + target_sources(StatMemory PRIVATE stat_memory_src/windows.cpp) + target_link_libraries(StatMemory PUBLIC psapi.lib) +elseif (${LINUX}) + target_sources(StatMemory PRIVATE stat_memory_src/linux.cpp) +elseif (${APPLE}) + target_sources(StatMemory PRIVATE stat_memory_src/macos.cpp) +else () + message(FATAL_ERROR "Unsupported system ${CMAKE_SYSTEM_NAME}, not windows, linux or macos") +endif () + +add_executable(test_stat_memory others/test_stat_memory.cpp) +target_link_libraries(test_stat_memory PRIVATE StatMemory) +add_test(NAME stat_memory + COMMAND test_stat_memory) + +if (${MINGW}) + # Link this to fix std::print under mingw (gcc>=14) + target_link_libraries(test_stat_memory PRIVATE stdc++exp) +endif () + +find_package(Qt6 COMPONENTS Widgets REQUIRED) +add_library(MemoryPolicyDialog STATIC + MemoryPolicyDialog.h + MemoryPolicyDialog.cpp + MemoryPolicyDialog.ui +) +target_link_libraries(MemoryPolicyDialog PUBLIC Qt6::Widgets) +target_link_libraries(MemoryPolicyDialog PUBLIC StatMemory) + +qt_add_lupdate(MemoryPolicyDialog + TS_FILES others/MemoryPolicyDialog_en_US.ts + OPTIONS ${SC_lupdate_flags} +) +qt_add_lrelease(MemoryPolicyDialog + TS_FILES others/MemoryPolicyDialog_en_US.ts + QM_FILES_OUTPUT_VARIABLE MemoryPolicyDialog_qm_files +) +qt_add_resources(MemoryPolicyDialog "MPD_translations" + PREFIX "/i18n" + BASE ${CMAKE_CURRENT_BINARY_DIR} + FILES ${MemoryPolicyDialog_qm_files} +) \ No newline at end of file diff --git a/utilities/StatMemory/MemoryPolicyDialog.cpp b/utilities/StatMemory/MemoryPolicyDialog.cpp new file mode 100644 index 00000000..6bce66ae --- /dev/null +++ b/utilities/StatMemory/MemoryPolicyDialog.cpp @@ -0,0 +1,50 @@ +// +// Created by Joseph on 2024/7/16. +// + +#include + +#include "MemoryPolicyDialog.h" +#include "ui_MemoryPolicyDialog.h" + +MemoryPolicyDialog::MemoryPolicyDialog(QWidget *parent) + : QDialog{parent}, ui{new Ui::MemoryPolicyDialog} { + this->ui->setupUi(this); + this->reset(memory_policy{}); +} + +MemoryPolicyDialog::MemoryPolicyDialog(QWidget *parent, + const memory_policy &value) + : MemoryPolicyDialog{parent} { + this->reset(value); +} + +MemoryPolicyDialog::~MemoryPolicyDialog() { this->ui.reset(); } + +memory_policy MemoryPolicyDialog::current_value() const noexcept { + return memory_policy{ + .auto_cache = this->ui->cb_auto_cache->isChecked(), + .self_maximum_memory = + uint64_t(this->ui->sb_self_limit->value()) * 1024 * 1024 * 1024, + .system_minimum_free = this->ui->dsb_system_limit->value(), + }; +} + +void MemoryPolicyDialog::reset(const memory_policy &value) noexcept { + this->ui->cb_auto_cache->setCheckState( + value.auto_cache ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->ui->sb_self_limit->setValue( + uint64_t(std::ceil(value.self_maximum_memory / (1024.0 * 1024 * 1024)))); + + this->ui->dsb_system_limit->setValue(value.system_minimum_free); +} + +void MemoryPolicyDialog::on_pb_ok_clicked() noexcept { emit this->accept(); } + +void MemoryPolicyDialog::on_pb_cancel_clicked() noexcept { + emit this->reject(); +} + +void MemoryPolicyDialog::on_pb_reset_clicked() noexcept { + this->reset(memory_policy{}); +} \ No newline at end of file diff --git a/utilities/StatMemory/MemoryPolicyDialog.h b/utilities/StatMemory/MemoryPolicyDialog.h new file mode 100644 index 00000000..99f121e6 --- /dev/null +++ b/utilities/StatMemory/MemoryPolicyDialog.h @@ -0,0 +1,75 @@ +// +// Created by Joseph on 2024/7/16. +// + +#ifndef SLOPECRAFT_MEMORYPOLICYDIALOG_H +#define SLOPECRAFT_MEMORYPOLICYDIALOG_H + +#include +#include + +#include "stat_memory.h" + +struct memory_policy { + bool auto_cache{true}; + uint64_t self_maximum_memory{4ull << 30}; + double system_minimum_free{0.2}; + + // memory_policy() = default; + + [[nodiscard]] bool should_cache( + const system_memory_info& smi) const noexcept { + if (not this->auto_cache) { + return false; + } + return double(smi.free) < smi.total * this->self_maximum_memory; + } + + [[nodiscard]] bool should_cache(const self_memory_usage& smu) const noexcept { + if (not this->auto_cache) { + return false; + } + return smu.used > this->self_maximum_memory; + } + + [[nodiscard]] bool should_cache() const noexcept { + if (not this->auto_cache) { + return false; + } + const auto sys_info_opt = get_self_memory_info(); + if (sys_info_opt and this->should_cache(sys_info_opt.value())) { + return true; + } + const auto self_info_opt = get_self_memory_info(); + if (self_info_opt and this->should_cache(self_info_opt.value())) { + return true; + } + return false; + } +}; + +class MemoryPolicyDialog; +namespace Ui { +class MemoryPolicyDialog; +} + +class MemoryPolicyDialog : public QDialog { + Q_OBJECT + private: + std::unique_ptr ui; + + public: + explicit MemoryPolicyDialog(QWidget* parent); + MemoryPolicyDialog(QWidget* parent, const memory_policy&); + ~MemoryPolicyDialog(); + + [[nodiscard]] memory_policy current_value() const noexcept; + void reset(const memory_policy&) noexcept; + + private slots: + void on_pb_ok_clicked() noexcept; + void on_pb_cancel_clicked() noexcept; + void on_pb_reset_clicked() noexcept; +}; + +#endif // SLOPECRAFT_MEMORYPOLICYDIALOG_H diff --git a/utilities/StatMemory/MemoryPolicyDialog.ui b/utilities/StatMemory/MemoryPolicyDialog.ui new file mode 100644 index 00000000..bc29f2e9 --- /dev/null +++ b/utilities/StatMemory/MemoryPolicyDialog.ui @@ -0,0 +1,119 @@ + + + MemoryPolicyDialog + + + + 0 + 0 + 400 + 195 + + + + 内存使用策略 + + + + + + Gib时自动缓存 + + + 当本进程占用> + + + 1 + + + 16384 + + + 4 + + + + + + + 当系统剩余内存< + + + %时自动缓存 + + + 1 + + + 0.000000000000000 + + + 5.000000000000000 + + + 20.000000000000000 + + + + + + + 提示:以上只是缓存数据的目标,不能保证内存占用一定小于设置的上限。缓存数据的实质是将构建的三维结构暂存硬盘(并释放相应内存),需要时再读取,因此必然使导出地图画变慢。 + + + false + + + true + + + + + + + 自动缓存数据以节约内存 + + + true + + + + + + + 确定 + + + + + + + 取消 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 恢复默认 + + + + + + + + diff --git a/utilities/StatMemory/others/MemoryPolicyDialog_en_US.ts b/utilities/StatMemory/others/MemoryPolicyDialog_en_US.ts new file mode 100644 index 00000000..22926e20 --- /dev/null +++ b/utilities/StatMemory/others/MemoryPolicyDialog_en_US.ts @@ -0,0 +1,57 @@ + + + + + MemoryPolicyDialog + + + 内存使用策略 + Memory Usage Policy + + + + Gib时自动缓存 + Gib(s) + + + + 当本进程占用> + When this process takes > + + + + 当系统剩余内存< + When system free memory < + + + + %时自动缓存 + % + + + + 提示:以上只是缓存数据的目标,不能保证内存占用一定小于设置的上限。缓存数据的实质是将构建的三维结构暂存硬盘(并释放相应内存),需要时再读取,因此必然使导出地图画变慢。 + Hint: Settings above is simply the target of caching, it's not guarented that the memory usage must be less than the assigned upper bound. Caching is to save 3d structures to disk(and release the memory), they will be read when required, leading to worse performance. + + + + 自动缓存数据以节约内存 + Enable auto caching to save memory + + + + 确定 + Ok + + + + 取消 + Cancel + + + + 恢复默认 + Reset default + + + diff --git a/utilities/StatMemory/others/test_stat_memory.cpp b/utilities/StatMemory/others/test_stat_memory.cpp new file mode 100644 index 00000000..fe6bcbeb --- /dev/null +++ b/utilities/StatMemory/others/test_stat_memory.cpp @@ -0,0 +1,30 @@ +#include "stat_memory.h" +#include +#include +#include + +int main() { + int fail_count = 0; + { + const auto sys_info = get_system_memory_info(); + if (sys_info) { + const auto &val = sys_info.value(); + std::println("System free memory: {}", val.free); + std::println("System total memory: {}", val.total); + } else { + std::println("Failed to get system memory info:\n{}", sys_info.error()); + fail_count++; + } + } + + const auto self_info = get_self_memory_info(); + if (self_info) { + const auto &val = self_info.value(); + std::println("Memory used by this process: \"{}\"", val.used); + } else { + std::println("Failed to get self memory info:\n{}", self_info.error()); + fail_count++; + } + + return fail_count; +} \ No newline at end of file diff --git a/utilities/StatMemory/stat_memory.h b/utilities/StatMemory/stat_memory.h new file mode 100644 index 00000000..d80ca2f3 --- /dev/null +++ b/utilities/StatMemory/stat_memory.h @@ -0,0 +1,29 @@ +// +// Created by Joseph on 2024/7/16. +// + +#ifndef SLOPECRAFT_STAT_MEMORY_H +#define SLOPECRAFT_STAT_MEMORY_H + +#include +#include +#include + +// All units in byte +struct system_memory_info { + uint64_t total; + uint64_t free; +}; + +[[nodiscard]] tl::expected +get_system_memory_info() noexcept; + +// All units in byte +struct self_memory_usage { + uint64_t used; +}; + +[[nodiscard]] tl::expected +get_self_memory_info() noexcept; + +#endif // SLOPECRAFT_STAT_MEMORY_H diff --git a/utilities/StatMemory/stat_memory_src/linux.cpp b/utilities/StatMemory/stat_memory_src/linux.cpp new file mode 100644 index 00000000..0b7de0f1 --- /dev/null +++ b/utilities/StatMemory/stat_memory_src/linux.cpp @@ -0,0 +1,154 @@ +#include "stat_memory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +tl::expected, std::string> parse_linux_file( + const char* filename, + const std::function& skip_this_line = {}) noexcept { + std::map fields; + std::string mem_info; + { + FILE* fd = fopen(filename, "r"); + if (fd == nullptr) { + return tl::make_unexpected( + std::format("Failed to read \"{}\", fopen returned NULL", filename)); + } + std::array buf; + while (true) { + const size_t bytes = fread(buf.data(), 1, buf.size(), fd); + mem_info.append(buf.data(), bytes); + if (bytes < buf.size()) { // end of file + break; + } + } + fclose(fd); + } + for (auto line : std::views::split(mem_info, '\n')) { + std::string_view line_sv{line.data(), line.size()}; + if (skip_this_line and skip_this_line(line_sv)) { + // if skip_this_line is not null, and this line is considered to be + // skipped + continue; + } + + if (line_sv.empty()) { + continue; + } + const size_t idx_of_colon = line_sv.find_first_of(':'); + if (idx_of_colon == line_sv.npos) { // Failed to parse this line, skip it. + continue; + // return tl::make_unexpected( + // std::format("Failed to parse \"{}\" from {}", line_sv, + // filename)); + } + + std::string_view key{line_sv.begin(), idx_of_colon}; + + std::string_view value_str{line_sv.begin() + idx_of_colon + 1, + line_sv.end()}; + std::string number_str{value_str.begin(), value_str.end()}; + uint64_t amplifier = 1; + for (char& c : number_str) { // to lower + if (c >= 'A' and c <= 'Z') { + c = std::tolower(c); + } + } + if (number_str.ends_with("kb")) { + amplifier = 1000; + for (int i = 0; i < 2; i++) number_str.pop_back(); + } + if (number_str.ends_with("kib")) { + amplifier = 1024; + for (int i = 0; i < 3; i++) number_str.pop_back(); + } + if (number_str.ends_with("mb")) { + amplifier = 1000 * 1000; + for (int i = 0; i < 2; i++) number_str.pop_back(); + } + if (number_str.ends_with("mib")) { + amplifier = 1024 * 1024; + for (int i = 0; i < 3; i++) number_str.pop_back(); + } + if (number_str.ends_with("gb")) { + amplifier = 1000 * 1000 * 1000; + for (int i = 0; i < 2; i++) number_str.pop_back(); + } + if (number_str.ends_with("gib")) { + amplifier = 1024 * 1024 * 1024; + for (int i = 0; i < 3; i++) number_str.pop_back(); + } + + const int64_t number = std::atoll(number_str.c_str()) * amplifier; + + fields.emplace(key, number); + } + + return fields; +} + +tl::expected +get_system_memory_info() noexcept { + auto fileds = parse_linux_file("/proc/meminfo", [](std::string_view line) { + if (line.starts_with("MemTotal")) return false; + if (line.starts_with("MemFree")) return false; + return true; + }); + if (not fileds) { + return tl::make_unexpected(std::move(fileds).error()); + } + auto& value = fileds.value(); + system_memory_info ret; + auto it = value.find("MemTotal"); + if (it != value.end()) { + ret.total = it->second; + } else { + return tl::make_unexpected( + "Failed to parse field \"MemTotal\" from /proc/meminfo"); + } + it = value.find("MemFree"); + if (it not_eq value.end()) { + ret.free = it->second; + } else { + return tl::make_unexpected( + "Failed to parse field \"MemFree\" from /proc/meminfo"); + } + return ret; +} + +[[nodiscard]] tl::expected +get_self_memory_info() noexcept { + const int64_t pid = getpid(); + const std::string file = std::format("/proc/{}/status", pid); + auto fields = parse_linux_file(file.c_str(), [](std::string_view line) { + return not line.starts_with("VmSize"); + }); + if (not fields) { + return tl::make_unexpected(std::move(fields.error())); + } + + auto& val = fields.value(); + auto it = val.find("VmSize"); + if (it not_eq val.end()) { + const int64_t used_memory = it->second; + if (used_memory < 0) { + return tl::make_unexpected(std::format( + "VmSize from {} is negative(the value is {})", file, used_memory)); + } + return self_memory_usage{uint64_t(used_memory)}; + } + + return tl::make_unexpected( + std::format("Failed to parse field VmSize from {}", file)); +} diff --git a/utilities/StatMemory/stat_memory_src/macos.cpp b/utilities/StatMemory/stat_memory_src/macos.cpp new file mode 100644 index 00000000..61277c8e --- /dev/null +++ b/utilities/StatMemory/stat_memory_src/macos.cpp @@ -0,0 +1,12 @@ +#include "stat_memory.h" +tl::expected +get_system_memory_info() noexcept { +#warning "TODO: get system memory on macos" + return tl::make_unexpected("Yet not implemented for macos"); +} + +[[nodiscard]] tl::expected +get_self_memory_info() noexcept { +#warning "TODO: get system memory on macos" + return tl::make_unexpected("Yet not implemented for macos"); +} \ No newline at end of file diff --git a/utilities/StatMemory/stat_memory_src/windows.cpp b/utilities/StatMemory/stat_memory_src/windows.cpp new file mode 100644 index 00000000..06ca89b8 --- /dev/null +++ b/utilities/StatMemory/stat_memory_src/windows.cpp @@ -0,0 +1,35 @@ +#include "stat_memory.h" +#include +#include + +#ifdef max +#undef max +#endif + +#include + +tl::expected +get_system_memory_info() noexcept { + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + + return system_memory_info{ + .total = ms.dwTotalPhys, + .free = ms.dwAvailPhys, + }; +} + +[[nodiscard]] tl::expected +get_self_memory_info() noexcept { + HANDLE handle = GetCurrentProcess(); + if (handle == nullptr) { + return tl::make_unexpected( + "win32 api failed, GetCurrentProcess returned nullptr instead of a " + "handle to current process."); + } + PROCESS_MEMORY_COUNTERS pmc; + GetProcessMemoryInfo(handle, &pmc, sizeof(pmc)); + self_memory_usage result{.used = + std::max(pmc.PagefileUsage, pmc.WorkingSetSize)}; + return result; +} \ No newline at end of file diff --git a/utilities/VersionDialog/CMakeLists.txt b/utilities/VersionDialog/CMakeLists.txt index ed260b13..649289d4 100644 --- a/utilities/VersionDialog/CMakeLists.txt +++ b/utilities/VersionDialog/CMakeLists.txt @@ -29,16 +29,14 @@ add_library(VersionDialog STATIC target_include_directories(VersionDialog INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(VersionDialog PUBLIC Qt::Widgets Qt::Core Qt::Network) target_compile_features(VersionDialog PUBLIC cxx_std_20) - -include(${CMAKE_SOURCE_DIR}/cmake/find_nlohmann_json.cmake) target_include_directories(VersionDialog PRIVATE ${SlopeCraft_Nlohmann_json_include_dir}) -if(${SlopeCraft_update_ts_files}) - execute_process(COMMAND ${SlopeCraft_Qt_lupdate_executable} ${VersionDialog_header_files} ${VersionDialog_source_files} ${VersionDialog_ui_files} -ts others/VersionDialog_en_US.ts ${SlopeCraft_ts_flags} -no-obsolete - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND_ERROR_IS_FATAL ANY) -endif() - # translation +qt_add_lupdate(VersionDialog + TS_FILES ${BlockListManager_ts_files} + SOURCES ${VersionDialog_project_sources} + OPTIONS ${SC_lupdate_flags} +) qt_add_lrelease(VersionDialog TS_FILES ${VersionDialog_ts_files} QM_FILES_OUTPUT_VARIABLE VD_qm_files) qt_add_resources(VersionDialog "VersionDialog_translations" diff --git a/utilities/VersionDialog/VersionDialog.cpp b/utilities/VersionDialog/VersionDialog.cpp index dbe5b3f1..b627d5f0 100644 --- a/utilities/VersionDialog/VersionDialog.cpp +++ b/utilities/VersionDialog/VersionDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -38,8 +38,8 @@ VersionDialog::~VersionDialog() { delete this->ui; } void VersionDialog::setup_text(QString title, QString content, QString markdown_content, - QString url_download) noexcept { - this->url_download = url_download; + QString url_download_) noexcept { + this->url_download = url_download_; this->setWindowTitle(title); this->ui->label->setText(content); this->ui->tb->setMarkdown(markdown_content); @@ -71,11 +71,11 @@ uint64_t version_string_to_u64(const char *str) noexcept { #include -version_info extract_latest_version( - std::string_view json_all_releaese) noexcept(false) { +version_info extract_latest_version(std::string_view json_all_release) noexcept( + false) { using njson = nlohmann::json; - njson jo = njson::parse(json_all_releaese); + njson jo = njson::parse(json_all_release); version_info ret; @@ -90,7 +90,7 @@ version_info extract_latest_version( const uint64_t ver = version_string_to_u64(tag.data()); - if ((ver >> 48) != SC_VERSION_MAJOR_U16) { + if ((ver >> 48) not_eq SC_VERSION_MAJOR_U16) { continue; } @@ -109,7 +109,6 @@ version_info extract_latest_version( if (latest.empty()) { throw std::runtime_error( "Failed to find any release that matches current major version."); - return {}; } ret.tag_name = QString::fromStdString(latest.at("tag_name")); @@ -128,11 +127,10 @@ void version_dialog_private_fun_when_network_finished( QWidget *window, QNetworkReply *reply, bool is_manually, QString software_name) noexcept; -void VersionDialog::start_network_request(QWidget *window, - QString software_name, - const QUrl &url, - QNetworkAccessManager &manager, - bool is_manually) noexcept { +void VersionDialog::start_network_request( + [[maybe_unused]] QWidget *window, QString software_name, const QUrl &url, + QNetworkAccessManager &manager, + [[maybe_unused]] bool is_manually) noexcept { QNetworkRequest request(url); QNetworkReply *reply = manager.get(request); @@ -156,12 +154,17 @@ void version_dialog_private_fun_when_network_finished( try { info = extract_latest_version(content_qba.data()); } catch (std::exception &e) { - const QString home_path = QDir::homePath(); +#if WIN32 + const QString home_path = QDir::homePath() + "/AppData/Local"; const QString data_dir_name = "SlopeCraft"; - QString data_dir = QDir::homePath() + "/" + data_dir_name; +#else + const QString home_path = QDir::homePath(); + const QString data_dir_name = ".SlopeCraft"; +#endif + QString data_dir = home_path + "/" + data_dir_name; QString log_file = data_dir.append("/UpdateCheckFailure.log"); { - if (!QDir(data_dir).exists()) { + if (not QDir(data_dir).exists()) { QDir{home_path}.mkpath(data_dir_name); } @@ -170,17 +173,16 @@ void version_dialog_private_fun_when_network_finished( log.write(content_qba); log.close(); } - - QMessageBox::warning( - window, QWidget::tr("获取最新版本失败"), - QWidget::tr("解析 \"%1\" " - "返回的结果时出现错误:\n\n%" - "2\n\n这不是一个致命错误,不影响软件使用。\n解析失败的信" - "息已经存储在日志文件中 (%3)。") - .arg(reply->url().toString()) - .arg(e.what()) - .arg(log_file), - QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}); + if (is_manually) { + QMessageBox::warning( + window, QWidget::tr("获取最新版本失败"), + QWidget::tr("解析 \"%1\" " + "返回的结果时出现错误:\n\n%" + "2\n\n这不是一个致命错误,不影响软件使用。\n解析失败的信" + "息已经存储在日志文件中 (%3)。") + .arg(reply->url().toString(), e.what(), log_file), + QMessageBox::StandardButtons{QMessageBox::StandardButton::Ignore}); + } reply->deleteLater(); return; } @@ -207,8 +209,7 @@ void version_dialog_private_fun_when_network_finished( QMessageBox::information( window, QWidget::tr("检查更新成功"), QWidget::tr("您使用的版本 (%1) 比已发布的 (%2) 更新,可能是测试版。") - .arg(SC_VERSION_STR) - .arg(tag_name)); + .arg(SC_VERSION_STR, tag_name)); } return; } @@ -219,11 +220,10 @@ void version_dialog_private_fun_when_network_finished( vd->setAttribute(Qt::WidgetAttribute::WA_AlwaysStackOnTop, true); vd->setWindowFlag(Qt::WindowType::Window, true); - vd->setup_text(QWidget::tr("%1 已更新").arg(software_name), - QWidget::tr("最新版本为%1,当前版本为%2") - .arg(tag_name) - .arg(SC_VERSION_STR), - info.body, info.html_url); + vd->setup_text( + QWidget::tr("%1 已更新").arg(software_name), + QWidget::tr("最新版本为%1,当前版本为%2").arg(tag_name, SC_VERSION_STR), + info.body, info.html_url); vd->show(); return; diff --git a/utilities/VersionDialog/VersionDialog.h b/utilities/VersionDialog/VersionDialog.h index 823b47ca..56b4ec72 100644 --- a/utilities/VersionDialog/VersionDialog.h +++ b/utilities/VersionDialog/VersionDialog.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -63,7 +63,7 @@ struct version_info { uint64_t version_u64; }; -version_info extract_latest_version( - std::string_view json_all_releaese) noexcept(false); +version_info extract_latest_version(std::string_view json_all_release) noexcept( + false); #endif // SLOPECRAFT_VISUALCRAFT_VERSION_DIALOG_H \ No newline at end of file diff --git a/utilities/install.cmake b/utilities/install.cmake index 630f2b7e..ebe845af 100644 --- a/utilities/install.cmake +++ b/utilities/install.cmake @@ -1,19 +1,3 @@ -if(CMAKE_SYSTEM_NAME MATCHES "Windows") - install(FILES - ${CMAKE_BINARY_DIR}/utilities/SC_version_buildtime.h - DESTINATION ${CMAKE_INSTALL_PREFIX}/../install_SlopeCraftL/Cpp/include) - - return() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - install(FILES - ${CMAKE_BINARY_DIR}/utilities/SC_version_buildtime.h - DESTINATION include) - - return() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") - return() -endif() \ No newline at end of file +install(FILES + ${CMAKE_BINARY_DIR}/utilities/SC_version_buildtime.h + DESTINATION include/SlopeCraft) \ No newline at end of file diff --git a/utilities/libAbstractGUI/CMakeLists.txt b/utilities/libAbstractGUI/CMakeLists.txt deleted file mode 100644 index f40cd20f..00000000 --- a/utilities/libAbstractGUI/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -project(SlopeCraft_libSCGUI VERSION ${SlopeCraft_version} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 20) - -add_library(AbstractGUI STATIC libAbstractGUI.h libAbstractGUI.cpp) \ No newline at end of file diff --git a/utilities/libAbstractGUI/libAbstractGUI.cpp b/utilities/libAbstractGUI/libAbstractGUI.cpp deleted file mode 100644 index 42474a12..00000000 --- a/utilities/libAbstractGUI/libAbstractGUI.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "libAbstractGUI.h" - -using namespace libAbstractGUI; - -abstract_widget (*callback_create_widget)(const widget_type) noexcept = nullptr; -void (*callback_destroy_widget)(abstract_widget) noexcept = nullptr; - -::std::string (*callback_get_text)(const abstract_widget) noexcept = nullptr; -void (*callback_set_text)(abstract_widget, - ::std::string_view text) noexcept = nullptr; - -bool (*callback_is_enabled)(const abstract_widget) noexcept = nullptr; -void (*callback_set_enabled)(abstract_widget, - const bool set_enabled) noexcept = nullptr; - -void (*callback_set_freezed)(abstract_widget, - const bool set_to_freezed) noexcept = nullptr; - -extern bool (*callback_is_checked)(const state_button) noexcept; -extern void (*callback_set_checked)(state_button, - const bool set_to_checked) noexcept; - -void (*callback_get_image)(const label, uint32_t *const dest_ARGB32, - const size_t dest_capacity_in_bytes, int *const rows, - int *const cols) noexcept = nullptr; -void (*callback_set_image)(label, const uint32_t *data_ARGB32, const int rows, - const int cols, const bool) noexcept = nullptr; -void (*callback_fill_label_with_color)(label, const uint32_t ARGB32) noexcept = - nullptr; - -// manipulate progress bar -void (*callback_get_progress_bar)(const progress_bar, int *const min, - int *const max, - int *const val) noexcept = nullptr; -void (*callback_set_progress_bar)(progress_bar, const int min, const int max, - const int val) noexcept = nullptr; - -// manipulate grid layout -void (*callback_get_gird_layout_size)(const grid_layout, int *const rows, - int *const cols) noexcept = nullptr; -void (*callback_set_grid_layout_size)(grid_layout, const int rows, - const int cols) noexcept = nullptr; -abstract_widget (*callback_get_widget_at)(const grid_layout, const int r, - const int c) noexcept = nullptr; -void (*callback_set_widget_at)(grid_layout, abstract_widget, const int r, - const int c) noexcept = nullptr; - -// image io -bool (*callback_read_image_from_file)( - ::std::string_view filename, uint32_t *const dest_ARGB32, - const size_t dest_capacity_in_bytes, int *const rows, int *const cols, - bool *const is_row_major) noexcept = nullptr; -bool (*callback_write_image_to_file)( - ::std::string_view filename, const uint32_t *const data_ARGB32, - const int rows, const int cols, const bool is_row_major) noexcept = nullptr; -// File dialogs -::std::string (*callback_get_open_filename)( - ::std::string_view window_caption, ::std::string_view start_dir, - ::std::string_view filter) noexcept = nullptr; -::std::vector<::std::string> (*callback_get_open_filenames)( - ::std::string_view window_title, ::std::string_view start_dir, - ::std::string_view filter) noexcept = nullptr; - -::std::string (*callback_get_exisiting_directory_name)( - ::std::string_view window_caption, - ::std::string_view start_dir) noexcept = nullptr; -::std::vector<::std::string> (*callback_get_exisiting_directory_names)( - ::std::string_view window_caption, - ::std::string_view start_dir) noexcept = nullptr; \ No newline at end of file diff --git a/utilities/libAbstractGUI/libAbstractGUI.h b/utilities/libAbstractGUI/libAbstractGUI.h deleted file mode 100644 index 3001999d..00000000 --- a/utilities/libAbstractGUI/libAbstractGUI.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef SLOPECRAFT_UTILITIES_LIBABSTRACTGUI_LIBABSTRACTGUI_H -#define SLOPECRAFT_UTILITIES_LIBABSTRACTGUI_LIBABSTRACTGUI_H - -#include -#include -#include - -namespace libAbstractGUI { -/// @brief Supported language -enum class language : uint8_t { en_US, zh_CN }; - -enum class widget_type : size_t { - unknown, - radio_button, - check_box, - push_button, - progress_bar, - label, - grid_layout -}; - -struct abstract_widget { - void *ptr = nullptr; - widget_type type = widget_type::unknown; - - inline bool is_null() const noexcept { return ptr == nullptr; } - - inline bool is_unknown() const noexcept { - return type == widget_type::unknown; - } -}; - -struct abstract_button : public abstract_widget {}; - -struct push_button : public abstract_button { - push_button() { this->type = widget_type::push_button; } -}; - -struct state_button : public abstract_button {}; -struct radio_button : public state_button { - radio_button() { this->type = widget_type::radio_button; } -}; -struct check_box : public state_button { - check_box() { this->type = widget_type::check_box; } -}; - -struct progress_bar : public abstract_widget { - progress_bar() { this->type = widget_type::progress_bar; } -}; - -struct label : public abstract_widget { - label() { this->type = widget_type::label; } -}; - -struct grid_layout : public abstract_widget { - grid_layout() { this->type = widget_type::grid_layout; } -}; - -// Global function ptrs starts with callback_ shoule be implemented by gui lib, -// and libAbstractGUI will call them when necessary. Their default value is -// nullptr -// -// Functions starts with on_ are implemented in this lib. -extern abstract_widget (*callback_create_widget)(const widget_type) noexcept; -extern void (*callback_destroy_widget)(abstract_widget) noexcept; - -extern ::std::string (*callback_get_text)(const abstract_widget) noexcept; -extern void (*callback_set_text)(abstract_widget, - ::std::string_view text) noexcept; - -extern bool (*callback_is_enabled)(const abstract_widget) noexcept; -extern void (*callback_set_enabled)(abstract_widget, - const bool set_enabled) noexcept; - -extern void (*callback_set_freezed)(abstract_widget, - const bool set_to_freezed) noexcept; - -// manipulate buttons -extern bool (*callback_is_checked)(const state_button) noexcept; -extern void (*callback_set_checked)(state_button, - const bool set_to_checked) noexcept; - -// manipulate labels -extern void (*callback_get_image)(const label, uint32_t *const dest_ARGB32, - const size_t dest_capacity_in_bytes, - int *const rows, int *const cols) noexcept; -extern void (*callback_set_image)(label, const uint32_t *data_ARGB32, - const int rows, const int cols, - const bool is_row_major) noexcept; -extern void (*callback_fill_label_with_color)(label, - const uint32_t ARGB32) noexcept; - -// manipulate progress bar -extern void (*callback_get_progress_bar)(const progress_bar, int *const min, - int *const max, - int *const val) noexcept; -extern void (*callback_set_progress_bar)(progress_bar, const int min, - const int max, const int val) noexcept; - -// manipulate grid layout -extern void (*callback_get_gird_layout_size)(const grid_layout, int *const rows, - int *const cols) noexcept; -extern void (*callback_set_grid_layout_size)(grid_layout, const int rows, - const int cols) noexcept; -extern abstract_widget (*callback_get_widget_at)(const grid_layout, const int r, - const int c) noexcept; -extern void (*callback_set_widget_at)(grid_layout, abstract_widget, const int r, - const int c) noexcept; - -// image file io -extern bool (*callback_read_image_from_file)( - ::std::string_view filename, uint32_t *const dest_ARGB32, - const size_t dest_capacity_in_bytes, int *const rows, int *const cols, - bool *const is_row_major) noexcept; -extern bool (*callback_write_image_to_file)(::std::string_view filename, - const uint32_t *const data_ARGB32, - const int rows, const int cols, - const bool is_row_major) noexcept; -// File dialogs -extern ::std::string (*callback_get_open_filename)( - ::std::string_view window_caption, ::std::string_view start_dir, - ::std::string_view filter) noexcept; -extern ::std::vector<::std::string> (*callback_get_open_filenames)( - ::std::string_view window_title, ::std::string_view start_dir, - ::std::string_view filter) noexcept; - -extern ::std::string (*callback_get_exisiting_directory_name)( - ::std::string_view window_caption, ::std::string_view start_dir) noexcept; -extern ::std::vector<::std::string> (*callback_get_exisiting_directory_names)( - ::std::string_view window_caption, ::std::string_view start_dir) noexcept; - -} // namespace libAbstractGUI - -#endif // SLOPECRAFT_UTILITIES_LIBABSTRACTGUI_LIBABSTRACTGUI_H \ No newline at end of file diff --git a/utilities/libSCGUI/CMakeLists.txt b/utilities/libSCGUI/CMakeLists.txt deleted file mode 100644 index 1124fbd8..00000000 --- a/utilities/libSCGUI/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -project(SlopeCraft_libSCGUI VERSION ${SlopeCraft_version} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 20) - -include_directories(${CMAKE_SOURCE_DIR}/utilities/libAbstractGUI - ${CMAKE_SOURCE_DIR}/SlopeCraftL - ${SlopeCraft_Eigen3_include_dir}) - -add_library(SCGUI STATIC libSCGUI.h libSCGUI.cpp) - -target_link_libraries(SCGUI PRIVATE AbstractGUI SlopeCraftL) \ No newline at end of file diff --git a/utilities/libSCGUI/libSCGUI.cpp b/utilities/libSCGUI/libSCGUI.cpp deleted file mode 100644 index 83d2e355..00000000 --- a/utilities/libSCGUI/libSCGUI.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#include "libSCGUI.h" -#include "libAbstractGUI.h" -#include "libSCGUI/libSCGUI.h" - -// #include - -using namespace libSCGUI; -using namespace libAbstractGUI; -using std::string, std::vector, std::string_view; - -void SCGUI::on_load_image_clicked() noexcept { - - const vector image_files = callback_get_open_filenames( - "Select one or multiple images", prev_dir, "*.png;;*.jpg;;*.jpeg;;*.bmp"); - - if (image_files.size() <= 0) { - return; - } - - if (image_files.size() == 1) { - string_view filename = image_files.front(); - - if (filename.size() <= 0) { - return; - } - int img_rows = 0, img_cols = 0; - callback_read_image_from_file(filename, nullptr, 0, &img_rows, &img_cols, - nullptr); - - if (img_rows <= 0 || img_cols <= 0) { - // the image seems to be broken. - return; - } - bool is_row_major = false; - uint32_t *buffer = - (uint32_t *)malloc(sizeof(uint32_t) * img_rows * img_cols); - callback_read_image_from_file(filename, buffer, - sizeof(uint32_t) * img_rows * img_cols, - &img_rows, &img_cols, &is_row_major); - // change the storage order to col-major - if (is_row_major) { - for (int r = 0; r < img_rows; r++) { - for (int c = 0; c < img_cols; c++) { - std::swap(buffer[r * img_cols + c], buffer[c * img_rows + r]); - } - } - } - - kernel->setRawImage(buffer, img_rows, img_cols); - callback_set_image(label_show_raw_image, buffer, img_rows, img_cols, - is_row_major); - callback_set_image(label_show_image_before_convert, buffer, img_rows, - img_cols, is_row_major); - free(buffer); - } - -#warning batch op not finished yet. -} - -void cb_progress_range_set(void *libAbstractGUI_progress_bar_ptr, int min, - int max, int val) { - callback_set_progress_bar( - *reinterpret_cast(libAbstractGUI_progress_bar_ptr), min, - max, val); -} -void cb_progress_add(void *libAbstractGUI_progress_bar_ptr, int delta) { - int min, max, val; - callback_get_progress_bar( - *reinterpret_cast(libAbstractGUI_progress_bar_ptr), &min, - &max, &val); - callback_set_progress_bar( - *reinterpret_cast(libAbstractGUI_progress_bar_ptr), min, - max, val + delta); -} - -void SCGUI::on_convert_clicked(const ::SCL_convertAlgo algo, - const bool dither) noexcept { - callback_set_freezed(main_window, true); - kernel->setWindPtr(&progressbar_convert_image); - kernel->setProgressRangeSet(cb_progress_range_set); - kernel->setProgressAdd(cb_progress_add); - - kernel->convert(algo, dither); - refreash_converted_image(); - callback_set_freezed(main_window, false); -} - -void SCGUI::refreash_raw_image() noexcept {} - -void SCGUI::refreash_converted_image() noexcept { - int rows = 0, cols = 0; - kernel->getConvertedImage(&rows, &cols, nullptr); - - uint32_t *buffer = (uint32_t *)malloc(sizeof(uint32_t) * rows * cols); - - kernel->getConvertedImage(nullptr, nullptr, buffer); - - callback_set_image(label_show_image_after_convert, buffer, rows, cols, false); - - free(buffer); -} \ No newline at end of file diff --git a/utilities/libSCGUI/libSCGUI.h b/utilities/libSCGUI/libSCGUI.h deleted file mode 100644 index 7a315125..00000000 --- a/utilities/libSCGUI/libSCGUI.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - Copyright © 2021-2023 TokiNoBug -This file is part of SlopeCraft. - - SlopeCraft is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - SlopeCraft is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with SlopeCraft. If not, see . - - Contact with me: - github:https://github.com/SlopeCraft/SlopeCraft - bilibili:https://space.bilibili.com/351429231 -*/ - -#ifndef SLOPECRAFT_UTILITIES_LIBSCGUI_LIBSCGUI_H -#define SLOPECRAFT_UTILITIES_LIBSCGUI_LIBSCGUI_H - -#include - -#include - -namespace libSCGUI { -class SCGUI { -private: - ::SlopeCraft::Kernel *kernel; - ::std::string prev_dir = ""; - -public: - libAbstractGUI::abstract_widget main_window; - libAbstractGUI::label label_show_raw_image; - // these 2 labels are about image conversion, actually they can be equal. - libAbstractGUI::label label_show_image_before_convert; - libAbstractGUI::label label_show_image_after_convert; - - libAbstractGUI::progress_bar progressbar_convert_image; - -public: - SCGUI() { kernel = ::SlopeCraft::Kernel::create(); } - -public: - void on_load_image_clicked() noexcept; - void on_convert_clicked(const ::SCL_convertAlgo, const bool dither) noexcept; - void refreash_raw_image() noexcept; - void refreash_converted_image() noexcept; - - void on_build3D_clicked() noexcept; - void on_export3D_clicked() noexcept; -}; -} // namespace libSCGUI - -#endif // SLOPECRAFT_UTILITIES_LIBSCGUI_LIBSCGUI_H \ No newline at end of file diff --git a/utilities/libpngReader/CMakeLists.txt b/utilities/libpngReader/CMakeLists.txt new file mode 100644 index 00000000..9a4b5add --- /dev/null +++ b/utilities/libpngReader/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(libpng_reader STATIC + libpng_reader.cpp + libpng_reader.h) + +find_package(PNG REQUIRED) + +target_compile_features(libpng_reader PUBLIC cxx_std_23) +target_link_libraries(libpng_reader PUBLIC PNG::PNG) +target_include_directories(libpng_reader INTERFACE $) \ No newline at end of file diff --git a/utilities/libpngReader/libpng_reader.cpp b/utilities/libpngReader/libpng_reader.cpp new file mode 100644 index 00000000..678faa19 --- /dev/null +++ b/utilities/libpngReader/libpng_reader.cpp @@ -0,0 +1,190 @@ +// +// Created by Joseph on 2024/4/8. +// + +#include +#include +#include +#include + +#include "libpng_reader.h" + +struct read_buffer_wrapper { + const void *data; + int64_t offset{0}; + int64_t max_length; +}; + +void png_callback_read_data_from_memory(png_struct *png, png_byte *data, + size_t read_length) { + read_buffer_wrapper *const ioptr = + reinterpret_cast(png_get_io_ptr(png)); + const size_t can_read_bytes = ioptr->max_length - ioptr->offset; + if (can_read_bytes < read_length) { + png_error(png, "EOF"); + return; + } + + memcpy(data, (char *)(ioptr->data) + ioptr->offset, read_length); + ioptr->offset += read_length; +} + +std::tuple, std::string> +parse_png_into_argb32(std::span encoded, + std::vector &pixels) noexcept { + std::function allocator = + [&pixels](const image_info &info) { + pixels.resize(static_cast(info.rows) * + static_cast(info.cols)); + return pixels.data(); + }; + + return parse_png_into_argb32_flex(encoded, allocator); +} + +std::tuple, std::string> +parse_png_into_argb32_flex( + std::span encoded, + const std::function allocator) noexcept { + std::string warnings{}; + if (encoded.size() < 8) { + return { + std::unexpected(std::format( + "File is too small ({} bytes) to be possible PNG", encoded.size())), + warnings}; + } + try { + png_struct *png = + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png == NULL) { + return {std::unexpected("Failed to create png read struct."), warnings}; + } + + png_info *info = png_create_info_struct(png); + if (info == NULL) { + png_destroy_read_struct(&png, &info, NULL); + return {std::unexpected("Failed to create png info struct."), warnings}; + } + + png_info *info_end = png_create_info_struct(png); + if (info_end == NULL) { + png_destroy_read_struct(&png, &info, &info_end); + return {std::unexpected("Failed to create png info_end struct."), + warnings}; + } + { + const auto compare_result = png_sig_cmp(encoded.data(), 0, 8); + if (compare_result not_eq 0) { + return {std::unexpected("Not a png file."), warnings}; + } + } + + // make a ioptr for libpng + read_buffer_wrapper wrapper; + wrapper.data = encoded.data(); + wrapper.offset = 0; // read file header + wrapper.max_length = encoded.size_bytes(); + + png_set_read_fn(png, &wrapper, png_callback_read_data_from_memory); + + png_read_info(png, info); + + uint32_t width{0}, height{0}; + int bit_depth, color_type, interlace_method, compress_method, filter_method; + bool add_alpha = false; + + png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, + &interlace_method, &compress_method, &filter_method); + + // cout << "\nwidth = " << width; + // cout << "\nheight = " << height; + // cout << "\nbit_depth = " << bit_depth; + // cout << "\ncolor_type = " << color_type << " ("; + + if (bit_depth > 8) { + png_set_strip_16(png); + } + if (bit_depth < 8) png_set_expand(png); + + switch (color_type) { + case PNG_COLOR_TYPE_GRAY: // fixed + png_set_gray_to_rgb(png); + add_alpha = true; + // cout << "PNG_COLOR_TYPE_GRAY"; + break; + case PNG_COLOR_TYPE_PALETTE: // fixed + + png_set_palette_to_rgb(png); + png_set_bgr(png); + { + int num_trans = 0; + png_get_tRNS(png, info, NULL, &num_trans, NULL); + if (num_trans <= 0) { + add_alpha = true; + } + // cout << "num_trans = " << num_trans << endl; + } + + // cout << "PNG_COLOR_TYPE_PALETTE"; + break; + case PNG_COLOR_TYPE_RGB: // fixed + png_set_bgr(png); + add_alpha = true; + // cout << "PNG_COLOR_TYPE_RGB"; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: // fixed + png_set_bgr(png); + // cout << "PNG_COLOR_TYPE_RGB_ALPHA"; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: // fixed + png_set_gray_to_rgb(png); + // png_set_swap_alpha(png); + // cout << "PNG_COLOR_TYPE_GRAY_ALPHA"; + break; + default: + png_destroy_read_struct(&png, &info, &info_end); + return { + std::unexpected(std::format("Unknown color type {}", color_type)), + warnings}; + } + // cout << ")\n"; + // #warning here + + const auto img_info = image_info{height, width}; + uint32_t *const pixels_data = allocator(img_info); + assert(pixels_data not_eq nullptr); + // pixels.resize(height * width); + // img->resize(height, width); + + std::vector row_ptrs; + row_ptrs.resize(height); + for (int r = 0; r < int(height); r++) { + row_ptrs[r] = reinterpret_cast(pixels_data + r * width); + } + + png_read_image(png, row_ptrs.data()); + + if (add_alpha) { // add alpha manually + for (int r = 0; r < int(height); r++) { + uint8_t *const current_row = row_ptrs[r]; + for (int pixel_idx = width - 1; pixel_idx > 0; pixel_idx--) { + const uint8_t *const data_src = current_row + pixel_idx * 3; + uint8_t *const data_dest = current_row + pixel_idx * 4; + + for (int i = 2; i >= 0; i--) { + data_dest[i] = data_src[i]; + } + data_dest[3] = 0xFF; + } + current_row[3] = 0xFF; + } + } + + // png_destroy_info_struct(png, &info); + png_destroy_read_struct(&png, &info, &info_end); + + return {img_info, warnings}; + } catch (const std::exception &e) { + return {std::unexpected(e.what()), warnings}; + } +} diff --git a/utilities/libpngReader/libpng_reader.h b/utilities/libpngReader/libpng_reader.h new file mode 100644 index 00000000..1f923a09 --- /dev/null +++ b/utilities/libpngReader/libpng_reader.h @@ -0,0 +1,29 @@ +// +// Created by Joseph on 2024/4/8. +// + +#ifndef SLOPECRAFT_LIBPNG_READER_H +#define SLOPECRAFT_LIBPNG_READER_H + +#include +#include +#include +#include +#include +#include +#include + +struct image_info { + uint32_t rows{0}; + uint32_t cols{0}; +}; + +[[nodiscard]] std::tuple, std::string> +parse_png_into_argb32(std::span png_file_in_bytes, + std::vector& pixels_row_major) noexcept; + +[[nodiscard]] std::tuple, std::string> +parse_png_into_argb32_flex( + std::span png_file_in_bytes, + const std::function allocator) noexcept; +#endif // SLOPECRAFT_LIBPNG_READER_H diff --git a/utilities/object_pool.hpp b/utilities/object_pool.hpp deleted file mode 100644 index abee2d99..00000000 --- a/utilities/object_pool.hpp +++ /dev/null @@ -1,778 +0,0 @@ -// 2020/03/13 - modified by Tsung-Wei Huang -// - fixed bug in aligning memory -// -// 2020/02/02 - modified by Tsung-Wei Huang -// - new implementation motivated by Hoard -// -// 2019/07/10 - modified by Tsung-Wei Huang -// - replace raw pointer with smart pointer -// -// 2019/06/13 - created by Tsung-Wei Huang -// - implemented an object pool class - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace tf { - -#define TF_ENABLE_POOLABLE_ON_THIS \ - template friend class ObjectPool; \ - void* _object_pool_block - -// Class: ObjectPool -// -// The class implements an efficient thread-safe object pool motivated -// by the Hoard memory allocator algorithm. -// Different from the normal memory allocator, object pool allocates -// only one object at a time. -// -// Internall, we use the following variables to maintain blocks and heaps: -// X: size in byte of a item slot -// M: number of items per block -// F: emptiness threshold -// B: number of bins per local heap (bin[B-1] is the full list) -// W: number of items per bin -// K: shrinkness constant -// -// Example scenario 1: -// M = 30 -// F = 4 -// W = (30+4-1)/4 = 8 -// -// b0: 0, 1, 2, 3, 4, 5, 6, 7 -// b1: 8, 9, 10, 11, 12, 13, 14, 15 -// b2: 16, 17, 18, 19, 20, 21, 22, 23 -// b3: 24, 25, 26, 27, 28, 29 -// b4: 30 (anything equal to M) -// -// Example scenario 2: -// M = 32 -// F = 4 -// W = (32+4-1)/4 = 8 -// b0: 0, 1, 2, 3, 4, 5, 6, 7 -// b1: 8, 9, 10, 11, 12, 13, 14, 15 -// b2: 16, 17, 18, 19, 20, 21, 22, 23 -// b3: 24, 25, 26, 27, 28, 29, 30, 31 -// b4: 32 (anything equal to M) -// -template -class ObjectPool { - - // the data column must be sufficient to hold the pointer in freelist - constexpr static size_t X = (std::max)(sizeof(T*), sizeof(T)); - //constexpr static size_t X = sizeof(long double) + std::max(sizeof(T*), sizeof(T)); - //constexpr static size_t M = (S - offsetof(Block, data)) / X; - constexpr static size_t M = S / X; - constexpr static size_t F = 4; - constexpr static size_t B = F + 1; - constexpr static size_t W = (M + F - 1) / F; - constexpr static size_t K = 4; - - static_assert( - S && (!(S & (S-1))), "block size S must be a power of two" - ); - - static_assert( - M >= 128, "block size S must be larger enough to pool at least 128 objects" - ); - - struct Blocklist { - Blocklist* prev; - Blocklist* next; - }; - - struct GlobalHeap { - std::mutex mutex; - Blocklist list; - }; - - struct LocalHeap { - std::mutex mutex; - Blocklist lists[B]; - size_t u {0}; - size_t a {0}; - }; - - struct Block { - std::atomic heap; - Blocklist list_node; - size_t i; - size_t u; - T* top; - // long double padding; - char data[S]; - }; - - public: - - /** - @brief constructs an object pool from a number of anticipated threads - */ - explicit ObjectPool(unsigned = std::thread::hardware_concurrency()); - - /** - @brief destructs the object pool - */ - ~ObjectPool(); - - /** - @brief acquires a pointer to a object constructed from a given argument list - */ - template - T* animate(ArgsT&&... args); - - /** - @brief recycles a object pointed by @c ptr and destroys it - */ - void recycle(T* ptr); - - size_t num_bins_per_local_heap() const; - size_t num_objects_per_bin() const; - size_t num_objects_per_block() const; - size_t num_available_objects() const; - size_t num_allocated_objects() const; - size_t capacity() const; - size_t num_local_heaps() const; - size_t num_global_heaps() const; - size_t num_heaps() const; - - float emptiness_threshold() const; - - private: - - const size_t _lheap_mask; - - GlobalHeap _gheap; - - std::vector _lheaps; - - LocalHeap& _this_heap(); - - constexpr unsigned _next_pow2(unsigned n) const; - - template - constexpr size_t _offset_in_class(const Q P::*member) const; - - template - constexpr P* _parent_class_of(Q*, const Q P::*member); - - template - constexpr P* _parent_class_of(const Q*, const Q P::*member) const; - - constexpr Block* _block_of(Blocklist*); - constexpr Block* _block_of(const Blocklist*) const; - - size_t _bin(size_t) const; - - T* _allocate(Block*); - - void _deallocate(Block*, T*); - void _blocklist_init_head(Blocklist*); - void _blocklist_add_impl(Blocklist*, Blocklist*, Blocklist*); - void _blocklist_push_front(Blocklist*, Blocklist*); - void _blocklist_push_back(Blocklist*, Blocklist*); - void _blocklist_del_impl(Blocklist*, Blocklist*); - void _blocklist_del(Blocklist*); - void _blocklist_replace(Blocklist*, Blocklist*); - void _blocklist_move_front(Blocklist*, Blocklist*); - void _blocklist_move_back(Blocklist*, Blocklist*); - bool _blocklist_is_first(const Blocklist*, const Blocklist*); - bool _blocklist_is_last(const Blocklist*, const Blocklist*); - bool _blocklist_is_empty(const Blocklist*); - bool _blocklist_is_singular(const Blocklist*); - - template - void _for_each_block_safe(Blocklist*, C&&); - - template - void _for_each_block(Blocklist*, C&&); - -}; - -// ---------------------------------------------------------------------------- -// ObjectPool definition -// ---------------------------------------------------------------------------- - -// Constructor -template -ObjectPool::ObjectPool(unsigned t) : - //_heap_mask {(_next_pow2(t) << 1) - 1u}, - //_heap_mask { _next_pow2(t<<1) - 1u }, - //_heap_mask {(t << 1) - 1}, - _lheap_mask { _next_pow2((t+1) << 1) - 1 }, - _lheaps { _lheap_mask + 1 } { - - _blocklist_init_head(&_gheap.list); - - for(auto& h : _lheaps) { - for(size_t i=0; i -ObjectPool::~ObjectPool() { - - // clear local heaps - for(auto& h : _lheaps) { - for(size_t i=0; i -size_t ObjectPool::num_bins_per_local_heap() const { - return B; -} - -// Function: num_objects_per_bin -template -size_t ObjectPool::num_objects_per_bin() const { - return W; -} - -// Function: num_objects_per_block -template -size_t ObjectPool::num_objects_per_block() const { - return M; -} - -// Function: emptiness_threshold -template -float ObjectPool::emptiness_threshold() const { - return 1.0f/F; -} - -// Function: num_global_heaps -template -size_t ObjectPool::num_global_heaps() const { - return 1; -} - -// Function: num_lheaps -template -size_t ObjectPool::num_local_heaps() const { - return _lheaps.size(); -} - -// Function: num_heaps -template -size_t ObjectPool::num_heaps() const { - return _lheaps.size() + 1; -} - -// Function: capacity -template -size_t ObjectPool::capacity() const { - - size_t n = 0; - - // global heap - for(auto p=_gheap.list.next; p!=&_gheap.list; p=p->next) { - n += M; - }; - - // local heap - for(auto& h : _lheaps) { - n += h.a; - } - - return n; -} - -// Function: num_available_objects -template -size_t ObjectPool::num_available_objects() const { - - size_t n = 0; - - // global heap - for(auto p=_gheap.list.next; p!=&_gheap.list; p=p->next) { - n += (M - _block_of(p)->u); - }; - - // local heap - for(auto& h : _lheaps) { - n += (h.a - h.u); - } - return n; -} - -// Function: num_allocated_objects -template -size_t ObjectPool::num_allocated_objects() const { - - size_t n = 0; - - // global heap - for(auto p=_gheap.list.next; p!=&_gheap.list; p=p->next) { - n += _block_of(p)->u; - }; - - // local heap - for(auto& h : _lheaps) { - n += h.u; - } - return n; -} - -// Function: _bin -template -size_t ObjectPool::_bin(size_t u) const { - return u == M ? F : u/W; -} - -// Function: _offset_in_class -template -template -constexpr size_t ObjectPool::_offset_in_class( - const Q P::*member) const { - return (size_t) &( reinterpret_cast(0)->*member); -} - -// C macro: parent_class_of(list_pointer, Block, list) -// C++: parent_class_of(list_pointer, &Block::list) -template -template -constexpr P* ObjectPool::_parent_class_of( - Q* ptr, const Q P::*member -) { - return (P*)( (char*)ptr - _offset_in_class(member)); -} - -// Function: _parent_class_of -template -template -constexpr P* ObjectPool::_parent_class_of( - const Q* ptr, const Q P::*member -) const { - return (P*)( (char*)ptr - _offset_in_class(member)); -} - -// Function: _block_of -template -constexpr typename ObjectPool::Block* -ObjectPool::_block_of(Blocklist* list) { - return _parent_class_of(list, &Block::list_node); -} - -// Function: _block_of -template -constexpr typename ObjectPool::Block* -ObjectPool::_block_of(const Blocklist* list) const { - return _parent_class_of(list, &Block::list_node); -} - -// Procedure: initialize a list head -template -void ObjectPool::_blocklist_init_head(Blocklist *list) { - list->next = list; - list->prev = list; -} - -// Procedure: _blocklist_add_impl -// Insert a new entry between two known consecutive entries. -// -// This is only for internal list manipulation where we know -// the prev/next entries already! -template -void ObjectPool::_blocklist_add_impl( - Blocklist *curr, Blocklist *prev, Blocklist *next -) { - next->prev = curr; - curr->next = next; - curr->prev = prev; - prev->next = curr; -} - -// list_push_front - add a new entry -// @curr: curr entry to be added -// @head: list head to add it after -// -// Insert a new entry after the specified head. -// This is good for implementing stacks. -// -template -void ObjectPool::_blocklist_push_front( - Blocklist *curr, Blocklist *head -) { - _blocklist_add_impl(curr, head, head->next); -} - -// list_add_tail - add a new entry -// @curr: curr entry to be added -// @head: list head to add it before -// -// Insert a new entry before the specified head. -// This is useful for implementing queues. -// -template -void ObjectPool::_blocklist_push_back( - Blocklist *curr, Blocklist *head -) { - _blocklist_add_impl(curr, head->prev, head); -} - -// Delete a list entry by making the prev/next entries -// point to each other. -// -// This is only for internal list manipulation where we know -// the prev/next entries already! -// -template -void ObjectPool::_blocklist_del_impl( - Blocklist * prev, Blocklist * next -) { - next->prev = prev; - prev->next = next; -} - -// _blocklist_del - deletes entry from list. -// @entry: the element to delete from the list. -// Note: list_empty() on entry does not return true after this, the entry is -// in an undefined state. -template -void ObjectPool::_blocklist_del(Blocklist *entry) { - _blocklist_del_impl(entry->prev, entry->next); - entry->next = nullptr; - entry->prev = nullptr; -} - -// list_replace - replace old entry by new one -// @old : the element to be replaced -// @curr : the new element to insert -// -// If @old was empty, it will be overwritten. -template -void ObjectPool::_blocklist_replace( - Blocklist *old, Blocklist *curr -) { - curr->next = old->next; - curr->next->prev = curr; - curr->prev = old->prev; - curr->prev->next = curr; -} - -// list_move - delete from one list and add as another's head -// @list: the entry to move -// @head: the head that will precede our entry -template -void ObjectPool::_blocklist_move_front( - Blocklist *list, Blocklist *head -) { - _blocklist_del_impl(list->prev, list->next); - _blocklist_push_front(list, head); -} - -// list_move_tail - delete from one list and add as another's tail -// @list: the entry to move -// @head: the head that will follow our entry -template -void ObjectPool::_blocklist_move_back( - Blocklist *list, Blocklist *head -) { - _blocklist_del_impl(list->prev, list->next); - _blocklist_push_back(list, head); -} - -// list_is_first - tests whether @list is the last entry in list @head -// @list: the entry to test -// @head: the head of the list -template -bool ObjectPool::_blocklist_is_first( - const Blocklist *list, const Blocklist *head -) { - return list->prev == head; -} - -// list_is_last - tests whether @list is the last entry in list @head -// @list: the entry to test -// @head: the head of the list -template -bool ObjectPool::_blocklist_is_last( - const Blocklist *list, const Blocklist *head -) { - return list->next == head; -} - -// list_empty - tests whether a list is empty -// @head: the list to test. -template -bool ObjectPool::_blocklist_is_empty(const Blocklist *head) { - return head->next == head; -} - -// list_is_singular - tests whether a list has just one entry. -// @head: the list to test. -template -bool ObjectPool::_blocklist_is_singular( - const Blocklist *head -) { - return !_blocklist_is_empty(head) && (head->next == head->prev); -} - -// Procedure: _for_each_block -template -template -void ObjectPool::_for_each_block(Blocklist* head, C&& c) { - Blocklist* p; - for(p=head->next; p!=head; p=p->next) { - c(_block_of(p)); - } -} - -// Procedure: _for_each_block_safe -// Iterate each item of a list - safe to free -template -template -void ObjectPool::_for_each_block_safe(Blocklist* head, C&& c) { - Blocklist* p; - Blocklist* t; - for(p=head->next, t=p->next; p!=head; p=t, t=p->next) { - c(_block_of(p)); - } -} - -// Function: _allocate -// allocate a spot from the block -template -T* ObjectPool::_allocate(Block* s) { - if(s->top == nullptr) { - return reinterpret_cast(s->data + s->i++ * X); - } - else { - T* retval = s->top; - s->top = *(reinterpret_cast(s->top)); - return retval; - } -} - -// Procedure: _deallocate -template -void ObjectPool::_deallocate(Block* s, T* ptr) { - *(reinterpret_cast(ptr)) = s->top; - s->top = ptr; -} - -// Function: allocate -template -template -T* ObjectPool::animate(ArgsT&&... args) { - - //std::cout << "construct a new item\n"; - - // my logically mapped heap - LocalHeap& h = _this_heap(); - - Block* s {nullptr}; - - h.mutex.lock(); - - // scan the list of superblocks from most full to least - int f = static_cast(F-1); - for(; f>=0; f--) { - if(!_blocklist_is_empty(&h.lists[f])) { - s = _block_of(h.lists[f].next); - break; - } - } - - // no superblock found - if(f == -1) { - - // check heap 0 for a superblock - _gheap.mutex.lock(); - if(!_blocklist_is_empty(&_gheap.list)) { - - s = _block_of(_gheap.list.next); - - //printf("get a superblock from global heap %lu\n", s->u); - assert(s->u < M && s->heap == nullptr); - f = static_cast(_bin(s->u + 1)); - - _blocklist_move_front(&s->list_node, &h.lists[f]); - - s->heap = &h; // must be within the global heap lock - _gheap.mutex.unlock(); - - h.u = h.u + s->u; - h.a = h.a + M; - } - // create a new block - else { - //printf("create a new superblock\n"); - _gheap.mutex.unlock(); - f = 0; - //s = static_cast(std::malloc(sizeof(Block))); - s = new Block(); - - if(s == nullptr) { - throw std::bad_alloc(); - } - - s->heap = &h; - s->i = 0; - s->u = 0; - s->top = nullptr; - - _blocklist_push_front(&s->list_node, &h.lists[f]); - - h.a = h.a + M; - } - } - - // the superblock must have at least one space - //assert(s->u < M); - //printf("%lu %lu %lu\n", h.u, h.a, s->u); - //assert(h.u < h.a); - - h.u = h.u + 1; - s->u = s->u + 1; - - // take one item from the superblock - T* mem = _allocate(s); - - int b = static_cast(_bin(s->u)); - - if(b != f) { - //printf("move superblock from list[%d] to list[%d]\n", f, b); - _blocklist_move_front(&s->list_node, &h.lists[b]); - } - - //std::cout << "s.i " << s->i << '\n' - // << "s.u " << s->u << '\n' - // << "h.u " << h.u << '\n' - // << "h.a " << h.a << '\n'; - - h.mutex.unlock(); - - //printf("allocate %p (s=%p)\n", mem, s); - - new (mem) T(std::forward(args)...); - - mem->_object_pool_block = s; - - return mem; -} - -// Function: destruct -template -void ObjectPool::recycle(T* mem) { - - //Block* s = *reinterpret_cast( - // reinterpret_cast(mem) - sizeof(Block**) - //); - - //Block* s= *(reinterpret_cast(mem) - O); // (mem) - 1 - - Block* s = static_cast(mem->_object_pool_block); - - mem->~T(); - - //printf("deallocate %p (s=%p) M=%lu W=%lu X=%lu\n", mem, s, M, W, X); - - // here we need a loop because when we lock the heap, - // other threads may have removed the superblock to another heap - bool sync = false; - - do { - LocalHeap* h = s->heap.load(std::memory_order_relaxed); - - // the block is in global heap - if(h == nullptr) { - std::lock_guard glock(_gheap.mutex); - if(s->heap == h) { - sync = true; - _deallocate(s, mem); - s->u = s->u - 1; - } - } - else { - std::lock_guard llock(h->mutex); - if(s->heap == h) { - sync = true; - // deallocate the item from the superblock - size_t f = _bin(s->u); - _deallocate(s, mem); - s->u = s->u - 1; - h->u = h->u - 1; - - size_t b = _bin(s->u); - - if(b != f) { - //printf("move superblock from list[%d] to list[%d]\n", f, b); - _blocklist_move_front(&s->list_node, &h->lists[b]); - } - - // transfer a mostly-empty superblock to global heap - if((h->u + K*M < h->a) && (h->u < ((F-1) * h->a / F))) { - for(size_t i=0; ilists[i])) { - Block* x = _block_of(h->lists[i].next); - //printf("transfer a block (x.u=%lu/x.i=%lu) to the global heap\n", x->u, x->i); - assert(h->u > x->u && h->a > M); - h->u = h->u - x->u; - h->a = h->a - M; - x->heap = nullptr; - std::lock_guard glock(_gheap.mutex); - _blocklist_move_front(&x->list_node, &_gheap.list); - break; - } - } - } - } - } - } while(!sync); - - //std::cout << "s.i " << s->i << '\n' - // << "s.u " << s->u << '\n'; -} - -// Function: _this_heap -template -typename ObjectPool::LocalHeap& -ObjectPool::_this_heap() { - // here we don't use thread local since object pool might be - // created and destroyed multiple times - //thread_local auto hv = std::hash()(std::this_thread::get_id()); - //return _lheaps[hv & _lheap_mask]; - - return _lheaps[ - std::hash()(std::this_thread::get_id()) & _lheap_mask - ]; -} - -// Function: _next_pow2 -template -constexpr unsigned ObjectPool::_next_pow2(unsigned n) const { - if(n == 0) return 1; - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; -} - -} // end namespace tf -------------------------------------------------------- diff --git a/utilities/sNBT_formatter/CMakeLists.txt b/utilities/sNBT_formatter/CMakeLists.txt new file mode 100644 index 00000000..e2b7c364 --- /dev/null +++ b/utilities/sNBT_formatter/CMakeLists.txt @@ -0,0 +1,4 @@ +find_package(libnbt++ REQUIRED) + +add_library(sNBT-formatter STATIC sNBT_formatter.h sNBT_formatter.cpp) +target_link_libraries(sNBT-formatter PUBLIC nbt++) \ No newline at end of file diff --git a/utilities/sNBT_formatter/sNBT_formatter.cpp b/utilities/sNBT_formatter/sNBT_formatter.cpp new file mode 100644 index 00000000..7d9b0e39 --- /dev/null +++ b/utilities/sNBT_formatter/sNBT_formatter.cpp @@ -0,0 +1,109 @@ +// +// Created by Joseph on 2024/7/25. +// + +#include "sNBT_formatter.h" + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_byte &b) { + os << static_cast(b.get()) << "b"; +} // We don't want to print a character + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_short &s) { + this->os << s.get() << 's'; +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_int &i) { + this->os << i.get(); +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_long &l) { + this->os << l.get() << 'l'; +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_float &f) { + this->os << f.get() << 'f'; +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_double &d) { + this->os << d.get() << 'd'; +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_byte_array &ba) { + this->os << "[B;"; + for (size_t i = 0; i < ba.size() - 1; i++) { + this->os << ba[i] << 'b'; + } + if (ba.size() > 0) { + this->os << ba[ba.size() - 1] << "b]"; + } else { + os << "]"; + } +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_int_array &ia) { + this->os << "[I;"; + for (size_t i = 0; i < ia.size() - 1; i++) { + this->os << ia[i]; + } + if (ia.size() > 0) { + this->os << ia[ia.size() - 1] << ']'; + } else { + os << "]"; + } +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_long_array &la) { + this->os << "[L;"; + for (size_t i = 0; i < la.size() - 1; i++) { + this->os << la[i] << 'l'; + } + if (la.size() > 0) { + this->os << la[la.size() - 1] << "l]"; + } else { + os << "]"; + } +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_string &s) { + if (s.get().find('\"') not_eq s.get().npos) { + os << '\'' << s.get() << '\''; + } else { + os << '"' << s.get() << '"'; + } +} // TODO: escape special characters + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_list &l) { + os << "["; + for (size_t idx = 0; idx < l.size(); idx++) { + l[idx].get().accept(*this); + if (idx < l.size() - 1) { + os << ','; + } + } + os << "]"; +} + +void sNBT::sNBT_format_visitor::visit(const nbt::tag_compound &c) { + if (c.size() == 0) { + this->os << "{}"; + return; + } + this->os << "{"; + size_t idx = 0; + for (const auto &pair : c) { + const auto &key = pair.first; + const auto &val = pair.second; + if (key.find(':') not_eq key.npos) { + this->os << '\"' << key << '\"' << ':'; + } else { + this->os << key << ':'; + } + // this->os << '\"' << key << "\":"; + val.get().accept(*this); + if (idx < c.size() - 1) { + this->os << ','; + } + idx++; + } + this->os << "}"; +} \ No newline at end of file diff --git a/utilities/sNBT_formatter/sNBT_formatter.h b/utilities/sNBT_formatter/sNBT_formatter.h new file mode 100644 index 00000000..73a5c007 --- /dev/null +++ b/utilities/sNBT_formatter/sNBT_formatter.h @@ -0,0 +1,55 @@ +// +// Created by Joseph on 2024/7/25. +// + +#ifndef SLOPECRAFT_SNBT_FORMATTOR_H +#define SLOPECRAFT_SNBT_FORMATTOR_H + +#include +#include +#include +#include + +#include +#include + +namespace sNBT { + +class sNBT_format_visitor : public nbt::const_nbt_visitor { + private: + std::ostream &os; + + auto iterator() const noexcept { + return std::ostream_iterator{this->os}; + } + + public: + sNBT_format_visitor(std::ostream &dest) : os{dest} {} + + void visit(const nbt::tag_byte &b) override; + + void visit(const nbt::tag_short &s) override; + + void visit(const nbt::tag_int &i) override; + + void visit(const nbt::tag_long &l) override; + + void visit(const nbt::tag_float &f) override; + + void visit(const nbt::tag_double &d) override; + + void visit(const nbt::tag_byte_array &ba) override; + + void visit(const nbt::tag_int_array &ia) override; + + void visit(const nbt::tag_long_array &la) override; + + void visit(const nbt::tag_string &s) override; + + void visit(const nbt::tag_list &l) override; + + void visit(const nbt::tag_compound &c) override; +}; +} // namespace sNBT + +#endif // SLOPECRAFT_SNBT_FORMATTOR_H diff --git a/utilities/uiPack/uiPack.h b/utilities/uiPack/uiPack.h index 7c60d3e8..46f4ad59 100644 --- a/utilities/uiPack/uiPack.h +++ b/utilities/uiPack/uiPack.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -24,20 +24,18 @@ This file is part of SlopeCraft. #define SCL_UIPACK_UIPACK_H struct uiPack { -public: + public: void *_uiPtr{nullptr}; void (*progressRangeSet)(void *, int, int, int){nullptr}; void (*progressAdd)(void *, int){nullptr}; -public: + public: inline void rangeSet(int a, int b, int c) const noexcept { - if (progressRangeSet != nullptr) - progressRangeSet(_uiPtr, a, b, c); + if (progressRangeSet != nullptr) progressRangeSet(_uiPtr, a, b, c); } inline void add(int d) const noexcept { - if (progressAdd != nullptr) - progressAdd(_uiPtr, d); + if (progressAdd != nullptr) progressAdd(_uiPtr, d); } }; -#endif // SCL_UIPACK_UIPACK_H +#endif // SCL_UIPACK_UIPACK_H diff --git a/utilities/version_set/CMakeLists.txt b/utilities/version_set/CMakeLists.txt new file mode 100644 index 00000000..acbcc4b4 --- /dev/null +++ b/utilities/version_set/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(version_set INTERFACE) +target_sources(version_set INTERFACE version_set.hpp) +target_include_directories(version_set INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/utilities/version_set/version_set.hpp b/utilities/version_set/version_set.hpp new file mode 100644 index 00000000..3043ac25 --- /dev/null +++ b/utilities/version_set/version_set.hpp @@ -0,0 +1,64 @@ +// +// Created by Joseph on 2024/9/15. +// + +#ifndef SLOPECRAFT_VERSION_SET_H +#define SLOPECRAFT_VERSION_SET_H + +#include +#include +#include "SC_GlobalEnums.h" + +constexpr inline size_t major_version_to_idx(SCL_gameVersion v) noexcept { + switch (v) { + case SCL_gameVersion::FUTURE: + return 31; + + default: + return size_t(v); + } +} + +class version_set { + private: + std::bitset<32> set{0}; + + public: + version_set() = default; + + explicit version_set(uint32_t val) : set(val) {} + + static version_set all() noexcept { + version_set ret(~uint32_t(0)); + return ret; + } + + inline bool match(SCL_gameVersion v) const noexcept { + return set[major_version_to_idx(v)]; + } + + inline auto operator[](SCL_gameVersion v) noexcept { + return set[major_version_to_idx(v)]; + } + + inline auto operator[](SCL_gameVersion v) const noexcept { + return set[major_version_to_idx(v)]; + } + + inline uint64_t to_u32() const noexcept { return set.to_ulong(); } + + inline bool operator==(const version_set &vs) const noexcept { + return this->to_u32() == vs.to_u32(); + } + + SCL_gameVersion introduced_version() const noexcept { + for (size_t i = 0; i < 32; i++) { + if (this->set[i]) { + return SCL_gameVersion(i); + } + } + return SCL_gameVersion::FUTURE; + } +}; + +#endif // SLOPECRAFT_VERSION_SET_H diff --git a/vccl/CMakeLists.txt b/vccl/CMakeLists.txt index 4101cd4f..48ffa87a 100644 --- a/vccl/CMakeLists.txt +++ b/vccl/CMakeLists.txt @@ -3,12 +3,10 @@ project(vccl VERSION ${SlopeCraft_version} LANGUAGES CXX) set(vccl_win_sources) -if(CMAKE_SYSTEM_NAME STREQUAL "Windows") +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") configure_file(others/vccl.rc.in others/vccl.rc) set(vccl_win_sources ${CMAKE_CURRENT_BINARY_DIR}/others/vccl.rc) -endif() - -include(${CMAKE_SOURCE_DIR}/cmake/configure_vanilla_zips.cmake) +endif () configure_file(vccl-config-to-bin-dir.json.in vccl-config.json) @@ -18,11 +16,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets LinguistTools Network Concurrent REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets LinguistTools Network Concurrent REQUIRED) - -include(${CMAKE_SOURCE_DIR}/cmake/configure_fmtlib.cmake) -find_package(fmt REQUIRED) +find_package(Qt6 COMPONENTS Widgets LinguistTools REQUIRED) find_package(magic_enum REQUIRED) qt_add_executable(vccl @@ -34,11 +28,7 @@ qt_add_executable(vccl ${vccl_win_sources} ) -target_link_libraries(vccl PRIVATE VisualCraftL Qt6::Core Qt6::Gui fmt::fmt magic_enum::magic_enum) - -include(${CMAKE_SOURCE_DIR}/cmake/configure_cli11.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/find_nlohmann_json.cmake) - +target_link_libraries(vccl PRIVATE VisualCraftL Qt6::Core Qt6::Gui magic_enum::magic_enum) target_include_directories(vccl PRIVATE ${cli11_include_dir} ${SlopeCraft_Nlohmann_json_include_dir}) find_package(OpenMP REQUIRED) @@ -56,6 +46,19 @@ set_target_properties(vccl PROPERTIES MACOSX_BUNDLE TRUE ) +if (${WIN32}) + add_custom_target(SC_create_symlink_VCCL + COMMAND mklink VisualCraftL.dll "..\\VisualCraftL\\VisualCraftL.dll" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS VisualCraftL + COMMENT "Create symlink to VisualCraftL.dll for vccl.exe") + add_dependencies(SC_create_all_symlinks SC_create_symlink_VCCL) +endif () + +if (${MINGW}) + # Link this to fix std::print under mingw (gcc>=14) + target_link_libraries(vccl PRIVATE stdc++exp) +endif () include(install.cmake) include(add_test_vccl.cmake) \ No newline at end of file diff --git a/vccl/add_test_vccl.cmake b/vccl/add_test_vccl.cmake index 41c0f4bb..82de7436 100644 --- a/vccl/add_test_vccl.cmake +++ b/vccl/add_test_vccl.cmake @@ -1,31 +1,60 @@ -include(${CMAKE_SOURCE_DIR}/cmake/configure_vanilla_zips.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/configure_images.cmake) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_vccl_images) set(temp_testname_prefix test_vccl_images/) +file(GLOB test_source_images "${CMAKE_SOURCE_DIR}/binaries/images/*") + +set(test_source_images_space_list) + +foreach (img ${test_source_images}) + if (${img} MATCHES " ") + set(test_source_images_space_list "${test_source_images_space_list} \"${img}\"") + else () + set(test_source_images_space_list "${test_source_images_space_list} ${img}") + endif () +endforeach (img ${test_source_images}) +string(LENGTH ${test_source_images_space_list} len) +math(EXPR len "${len}-1") +string(SUBSTRING ${test_source_images_space_list} 1 ${len} test_source_images_space_list) +unset(len) + + +message(STATUS "test_source_images_space_list = ${test_source_images_space_list}") + set(list_faces "up" "down" "north" "south" "east" "west") +#set(algos "RGB" "RGB+" "HSV" "Lab94" "Lab00") set(algos "RGB") set(dither "false") +cmake_policy(PUSH) + +cmake_policy(SET CMP0110 OLD) + + +if (${SlopeCraft_GPU_API} STREQUAL "None") + set(gpu_flags) +else () + set(gpu_flags --gpu --platform ${SlopeCraft_vccl_test_gpu_platform_idx} --device ${SlopeCraft_vccl_test_gpu_device_idx}) +endif () + # set(dither "true" "false") -foreach(_layers RANGE 1 3 1) - foreach(_ver RANGE 12 19) +foreach (_layers RANGE 1 3 1) + foreach (_ver RANGE 12 21) set(VCL_current_var_name VCL_resource_${mcver}) set(zip_file ${VCL_current_var_name}) set(generate_schem) - if(NOT _ver EQUAL 12) + if (NOT _ver EQUAL 12) set(generate_schem "--schem") - endif() + endif () - foreach(_face ${list_faces}) - foreach(_algo ${algos}) - foreach(_dither ${dither}) + foreach (_face ${list_faces}) + foreach (_algo ${algos}) + foreach (_dither ${dither}) # message(STATUS ${temp_testname_prefix}_${_ver}_${_face}) set(test_name ${temp_testname_prefix}ver=${_ver}_face=${_face}_layer=${_layers}_algo=${_algo}_dither=${_dither}_) @@ -34,12 +63,16 @@ foreach(_layers RANGE 1 3 1) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} # COMMAND vccl --bsl ${CMAKE_SOURCE_DIR}/VisualCraftL/VCL_blocks_fixed.json --rp ${zip_file} --mcver ${_ver} --face ${_face} --layers ${_layers} --img ${VCL_test_images} --dither=${_dither} -j1 --out-image --benchmark --prefix ${test_name} --gpu --disable-config - COMMAND vccl --mcver ${_ver} --face ${_face} --layers ${_layers} --img ${CMAKE_SOURCE_DIR}/binaries/images/* --dither=${_dither} -j20 --out-image --benchmark --prefix ${test_name} --gpu --lite --nbt ${generate_schem} + COMMAND vccl --img ${test_source_images} --mcver ${_ver} --face ${_face} --layers ${_layers} --dither=${_dither} -j20 --out-image --benchmark --prefix ${test_name} --lite --nbt ${generate_schem} ${gpu_flags} + COMMAND_EXPAND_LISTS ) # - endforeach(_dither ${dither}) - endforeach(_algo ${algos}) - endforeach(_face ${list_faces}) - endforeach(_ver RANGE 12 19) -endforeach(_layers RANGE 1 3 1) \ No newline at end of file + endforeach (_dither ${dither}) + endforeach (_algo ${algos}) + endforeach (_face ${list_faces}) + endforeach (_ver RANGE 12 21) +endforeach (_layers RANGE 1 3 1) + + +cmake_policy(POP) \ No newline at end of file diff --git a/vccl/deploy_qt_for_vccl_macos.cmake.in b/vccl/deploy_qt_for_vccl_macos.cmake.in new file mode 100644 index 00000000..e5a7a2e8 --- /dev/null +++ b/vccl/deploy_qt_for_vccl_macos.cmake.in @@ -0,0 +1,35 @@ +# This script should run during installation + +set(this_script_file @deployqt_vccl_script@) +set(macdeployqt_flags @SlopeCraft_macdeployqt_flags_install@) +option(install_mode "" ON) + +if (install_mode) + execute_process(COMMAND ${CMAKE_COMMAND} -Dinstall_mode:BOOL=false -P ${this_script_file} + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX} + COMMAND_ERROR_IS_FATAL ANY + ) + return() +endif () + +message(STATUS "Running macdeployqt for @AppName@ ...") +execute_process( + COMMAND "@SlopeCraft_Qt_macdeployqt_executable@" "@AppName@.app" "${macdeployqt_flags}" + WORKING_DIRECTORY "@vccl_prefix@" + OUTPUT_QUIET + COMMAND_ERROR_IS_FATAL ANY) + +# message(WARNING "CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_SOURCE_DIR}") + +file(CREATE_LINK + "@vccl_prefix@/vccl.app/Contents/MacOS/vccl" + "vccl" + SYMBOLIC) +file(CREATE_LINK + "@vccl_prefix@/vccl.app/Contents/MacOS/vccl-config.json" + "vccl-config.json" + SYMBOLIC) +file(CREATE_LINK + "@vccl_prefix@/vccl.app/Contents/MacOS/Blocks_VCL" + "Blocks_VCL" + SYMBOLIC) diff --git a/vccl/install.cmake b/vccl/install.cmake index dd921b0a..9bb4e0f4 100644 --- a/vccl/install.cmake +++ b/vccl/install.cmake @@ -1,44 +1,89 @@ set(AppName vccl) -configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) +#configure_file(${CMAKE_SOURCE_DIR}/cmake/deploy_qt.cmake.in +# ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake +# @ONLY) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Windows") install(TARGETS vccl - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) + EXPORT SlopeCraftTargets + RUNTIME DESTINATION .) install(FILES vccl-config.json - DESTINATION ${CMAKE_INSTALL_PREFIX}) + DESTINATION .) - # Run windeployqt at build time - add_custom_target(Windeployqt-vccl ALL - COMMAND ${SlopeCraft_Qt_windeployqt_executable} vccl.exe - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS vccl) + QD_add_deployqt(vccl + BUILD_MODE + FLAGS ${SlopeCraft_windeployqt_flags_build}) + QD_add_deployqt(vccl + INSTALL_MODE INSTALL_DESTINATION . + FLAGS ${SlopeCraft_windeployqt_flags_install}) + DLLD_add_deploy(vccl + BUILD_MODE + INSTALL_MODE INSTALL_DESTINATION . + IGNORE VisualCraftL.dll libVisualCraftL.dll) - # Run windeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) + # # Run windeployqt at build time + # add_custom_target(Windeployqt-vccl + # COMMAND ${SlopeCraft_Qt_windeployqt_executable} vccl.exe ${SlopeCraft_windeployqt_flags_build} + # COMMAND_EXPAND_LISTS + # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + # DEPENDS vccl) + # add_dependencies(SC_deploy_all Windeployqt-vccl) + # + # # Run windeployqt at install time + # install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) return() -endif() +endif () -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") install(TARGETS vccl + EXPORT SlopeCraftTargets RUNTIME DESTINATION bin LIBRARY DESTINATION lib) install(FILES vccl-config.json DESTINATION bin) + + # Install platforms and imageformats plugins + include(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake) + return() -endif() +endif () + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + set(vccl_prefix vccl-contents) + set(deployqt_vccl_script ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt_for_vccl_macos.cmake) + find_program(SlopeCraft_Qt_macdeployqt_executable macdeployqt REQUIRED) + configure_file(deploy_qt_for_vccl_macos.cmake.in + ${deployqt_vccl_script} + @ONLY) -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + include(${CMAKE_SOURCE_DIR}/VisualCraftL/setup_zip_names.cmake) install(TARGETS vccl - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX}) + EXPORT SlopeCraftTargets + RUNTIME DESTINATION ${vccl_prefix} + BUNDLE DESTINATION ${vccl_prefix}) # Install config json file, vccl will try to find it by ./vccl-config.json install(FILES vccl-config.json - DESTINATION ${CMAKE_INSTALL_PREFIX}/vccl.app/Contents/MacOS) + DESTINATION ${vccl_prefix}/vccl.app/Contents/MacOS) + + # Install zips. In vccl-config.json or vc-config.json, they are referred like ./Blocks_VCL/Vanilla_1_19_3.zip + install(FILES ${VCL_app_files} + DESTINATION ${vccl_prefix}/vccl.app/Contents/MacOS/Blocks_VCL) + + install(TARGETS VisualCraftL + #EXPORT SlopeCraftTargets + RUNTIME DESTINATION ${vccl_prefix}/vccl.app/Contents/Frameworks + LIBRARY DESTINATION ${vccl_prefix}/vccl.app/Contents/Frameworks) + + # Do not run deploy_qt.cmake, but a specialied one + install(SCRIPT ${deployqt_vccl_script} + DESTINATION .) - # Run macdeployqt at install time - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/deploy_qt.cmake) + DylibD_add_deploy(vccl + INSTALL_DESTINATION ${vccl_prefix}) + RCS_add_codesign(vccl + INSTALL_DESTINATION ${vccl_prefix}) return() -endif() \ No newline at end of file +endif () \ No newline at end of file diff --git a/vccl/vccl-config-to-bin-dir.json.in b/vccl/vccl-config-to-bin-dir.json.in index 90b7ed3c..0221ff4a 100644 --- a/vccl/vccl-config-to-bin-dir.json.in +++ b/vccl/vccl-config-to-bin-dir.json.in @@ -31,6 +31,14 @@ [ 19, "@VCL_resource_19@" + ], + [ + 20, + "@VCL_resource_20@" + ], + [ + 21, + "@VCL_resource_21@" ] ], "default_block_state_list": [ diff --git a/vccl/vccl-config.json b/vccl/vccl-config.json index 585685b0..1317190c 100644 --- a/vccl/vccl-config.json +++ b/vccl/vccl-config.json @@ -1,39 +1,47 @@ { - "default_resource_pack_zip": [ - [ - 12, - "./Blocks_VCL/Vanilla_1_12_2.zip" - ], - [ - 13, - "./Blocks_VCL/Vanilla_1_13_2.zip" - ], - [ - 14, - "./Blocks_VCL/Vanilla_1_14_4.zip" - ], - [ - 15, - "./Blocks_VCL/Vanilla_1_15_2.zip" - ], - [ - 16, - "./Blocks_VCL/Vanilla_1_16_5.zip" - ], - [ - 17, - "./Blocks_VCL/Vanilla_1_17_1.zip" - ], - [ - 18, - "./Blocks_VCL/Vanilla_1_18_2.zip" - ], - [ - 19, - "./Blocks_VCL/Vanilla_1_19_3.zip" - ] - ], - "default_block_state_list": [ - "./Blocks_VCL/VCL_blocks_fixed.json" + "default_resource_pack_zip": [ + [ + 12, + "./Blocks_VCL/Vanilla_1_12_2.zip" + ], + [ + 13, + "./Blocks_VCL/Vanilla_1_13_2.zip" + ], + [ + 14, + "./Blocks_VCL/Vanilla_1_14_4.zip" + ], + [ + 15, + "./Blocks_VCL/Vanilla_1_15_2.zip" + ], + [ + 16, + "./Blocks_VCL/Vanilla_1_16_5.zip" + ], + [ + 17, + "./Blocks_VCL/Vanilla_1_17_1.zip" + ], + [ + 18, + "./Blocks_VCL/Vanilla_1_18_2.zip" + ], + [ + 19, + "./Blocks_VCL/Vanilla_1_19_3.zip" + ], + [ + 20, + "./Blocks_VCL/Vanilla_1_20_6.zip" + ], + [ + 21, + "./Blocks_VCL/Vanilla_1_21_11.zip" ] + ], + "default_block_state_list": [ + "./Blocks_VCL/VCL_blocks_fixed.json" + ] } \ No newline at end of file diff --git a/vccl/vccl.cpp b/vccl/vccl.cpp index 3723f564..6c43b6b2 100644 --- a/vccl/vccl.cpp +++ b/vccl/vccl.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,13 +20,18 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ +#include +#include +#include #include #include -#include "vccl_internal.h" #include +#include +#include "vccl_internal.h" #include -#include +#include +#include using std::cout, std::endl; @@ -42,9 +47,11 @@ int main(int argc, char **argv) { input.prefer_gpu = true; } - app.set_version_flag("--version,-v", - std::string("vccl version : ") + SC_VERSION_STR + - ", VisualCraftL version : " + VCL_version_string()); + app.set_version_flag("--version,-v", SC_VERSION_STR); + bool show_config{false}; + app.add_flag("--show-config,--sc", show_config, + "Show buildtime configuration and exit.") + ->default_val(false); // resource app.add_option("--resource-pack,--rp", input.zips, "Resource packs") @@ -57,7 +64,7 @@ int main(int argc, char **argv) { int __version; app.add_option("--mcver", __version, "MC version") ->default_val(19) - ->check(CLI::Range(12, 19, "Avaliable versions")); + ->check(CLI::Range(12, 21, "Avaliable versions")); app.add_option("--layers,--layer", input.layers, "Max layers") ->default_val(1) @@ -85,7 +92,7 @@ int main(int argc, char **argv) { "conversion") ->default_val(false); app.add_flag("--show-num-color,--snc", input.show_color_num, - "Show the number of colos") + "Show the number of colors") ->default_val(false); // images @@ -175,6 +182,15 @@ int main(int argc, char **argv) { CLI11_PARSE(app, argc, argv); + if (show_config) { + std::println("Version : {}", SC_VERSION_STR); + std::println("Build type : {}", CMAKE_BUILD_TYPE); + std::println("GPU API : {}", SC_GPU_API); + std::println("Vectorize : {}", SC_VECTORIZE); + std::println("Gprof : {}", SC_GPROF); + return 0; + } + if (input.list_gpu) { return list_gpu(); } @@ -221,6 +237,7 @@ int main(int argc, char **argv) { } QCoreApplication qapp(argc, argv); + QImageReader::setAllocationLimit(INT32_MAX); if (input.list_supported_formats) { list_supported_formats(); qapp.quit(); diff --git a/vccl/vccl_internal.h b/vccl/vccl_internal.h index 1022d058..3984dfdf 100644 --- a/vccl/vccl_internal.h +++ b/vccl/vccl_internal.h @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -31,7 +31,6 @@ This file is part of SlopeCraft. #include struct inputs { - // resource std::vector zips; std::vector jsons; @@ -98,4 +97,4 @@ int list_gpu(); void list_supported_formats() noexcept; -#endif // #ifndef SLOPECRAFT_VCCL_INTERNAL_H \ No newline at end of file +#endif // #ifndef SLOPECRAFT_VCCL_INTERNAL_H \ No newline at end of file diff --git a/vccl/vccl_parse_default.cpp b/vccl/vccl_parse_default.cpp index fb3aa6bb..d53a97e2 100644 --- a/vccl/vccl_parse_default.cpp +++ b/vccl/vccl_parse_default.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2021-2023 TokiNoBug + Copyright © 2021-2026 TokiNoBug This file is part of SlopeCraft. SlopeCraft is free software: you can redistribute it and/or modify @@ -20,23 +20,23 @@ This file is part of SlopeCraft. bilibili:https://space.bilibili.com/351429231 */ -#include "vccl_internal.h" -#include -#include #include -#include +#include #include -#include +#include #include +#include +#include +#include "vccl_internal.h" void cb_progress_range_set(void *, int, int, int) {} void cb_progress_add(void *, int) {} using std::cout, std::endl; -#define VCCL_PRIVATE_MACRO_MAKE_CASE(enum_val) \ - if (str == #enum_val) { \ - ok = true; \ - return SCL_convertAlgo::enum_val; \ +#define VCCL_PRIVATE_MACRO_MAKE_CASE(enum_val) \ + if (str == #enum_val) { \ + ok = true; \ + return SCL_convertAlgo::enum_val; \ } SCL_convertAlgo str_to_algo(std::string_view str, bool &ok) noexcept { @@ -57,7 +57,6 @@ int list_gpu() { const size_t plat_num = VCL_platform_num(); cout << plat_num << " platforms found on this computer : \n"; for (size_t pid = 0; pid < plat_num; pid++) { - VCL_GPU_Platform *plat = VCL_get_platform(pid); if (plat == nullptr) { cout << "Failed to get platform " << pid << '\n'; @@ -155,7 +154,6 @@ int set_allowed(VCL_block_state_list *bsl, const inputs &input) noexcept { return 0; } - void list_supported_formats() noexcept { auto fmts = QImageReader::supportedImageFormats(); cout << "Supported image formats : "; @@ -183,9 +181,8 @@ int run(const inputs &input) noexcept { kernel->set_prefer_gpu(input.prefer_gpu); if (input.prefer_gpu) { - bool ok = true; + bool ok; while (true) { - auto plat = VCL_get_platform(input.platform_idx); if (plat == nullptr) { ok = false; @@ -231,7 +228,7 @@ int run(const inputs &input) noexcept { wt = omp_get_wtime() - wt; if (input.benchmark) { - cout << fmt::format( + std::print( "Parsing resource pack and block state list in {} miliseconds.\n", wt * 1000); } @@ -245,25 +242,23 @@ int run(const inputs &input) noexcept { } if (input.show_color_num) { - cout << fmt::format("{} colors avaliable.\n", - VCL_get_allowed_colors(nullptr, 0)); + std::println("{} colors avaliable.", VCL_get_allowed_colors(nullptr, 0)); } if (input.export_test_lite) { - std::string filename = fmt::format("{}test_all_blocks_mc={}.litematic", + std::string filename = std::format("{}test_all_blocks_mc={}.litematic", input.prefix, int(input.version)); wt = omp_get_wtime(); if (!VCL_export_test_litematic(filename.c_str())) { - cout << fmt::format("Failed to export test litematic \"{}\"", filename) - << endl; + std::print("Failed to export test litematic \"{}\"\n", filename); return __LINE__; } wt = omp_get_wtime() - wt; if (input.benchmark) { - cout << fmt::format("Exported \"{}\" in {} seconds.\n", filename, wt); + std::print("Exported \"{}\" in {} seconds.\n", filename, wt); } } @@ -280,7 +275,7 @@ int run(const inputs &input) noexcept { QImage img(QString::fromLocal8Bit(img_filename.c_str())); if (img.isNull()) { - cout << fmt::format("Failed to open image {}", img_filename) << endl; + std::print("Failed to open image {}\n", img_filename); VCL_destroy_kernel(kernel); return __LINE__; } @@ -308,8 +303,8 @@ int run(const inputs &input) noexcept { wt = omp_get_wtime() - wt; if (input.benchmark) { - cout << fmt::format("Converted {} pixels in {} seconds.\n", - img.height() * img.width(), wt); + std::print("Converted {} pixels in {} seconds.\n", + img.height() * img.width(), wt); } if (input.make_converted_image) { @@ -324,7 +319,7 @@ int run(const inputs &input) noexcept { const bool ok = img.save(QString::fromLocal8Bit(dst_name_str.c_str())); if (!ok) { - cout << fmt::format("Failed to save image {}\n", dst_name_str); + std::print("Failed to save image {}\n", dst_name_str); return __LINE__; } // cout << dst_path << endl; @@ -333,7 +328,6 @@ int run(const inputs &input) noexcept { if (input.make_flat_diagram) { double wtime[3]; for (uint8_t layer = 0; layer < input.layers; layer++) { - std::string dst_name_str(input.prefix); dst_name_str += pure_filename_no_extension; dst_name_str += "_flagdiagram_layer="; @@ -347,16 +341,14 @@ int run(const inputs &input) noexcept { option.split_line_col_margin = input.flat_diagram_splitline_margin_col; wtime[layer] = omp_get_wtime(); if (!kernel->export_flag_diagram(dst_name_str.c_str(), option, layer)) { - cout << fmt::format("Failed to export flat diagram {}\n", - dst_name_str); + std::print("Failed to export flat diagram {}\n", dst_name_str); return __LINE__; } wtime[layer] = omp_get_wtime() - wtime[layer]; } if (input.benchmark) { - cout << fmt::format("Export flatdiagram containing {} images in ", - input.layers); + std::print("Export flatdiagram containing {} images in ", input.layers); for (int i = 0; i < input.layers; i++) { cout << wtime[i] << ", "; } @@ -376,8 +368,7 @@ int run(const inputs &input) noexcept { wt = omp_get_wtime() - wt; if (input.benchmark) { - cout << fmt::format("Built {} blocks in {} seconds.\n", - kernel->xyz_size(), wt); + std::print("Built {} blocks in {} seconds.\n", kernel->xyz_size(), wt); } if (input.make_litematic) { @@ -390,13 +381,13 @@ int run(const inputs &input) noexcept { wt = omp_get_wtime() - wt; if (!success) { - cout << fmt::format("Failed to export {}.", filename) << endl; + std::println("Failed to export {}.", filename); return __LINE__; } if (input.benchmark) { - cout << fmt::format("Export litematic with {} blocks in {} seconds.\n", - kernel->xyz_size(), wt); + std::println("Export litematic with {} blocks in {} seconds.", + kernel->xyz_size(), wt); } } @@ -410,13 +401,13 @@ int run(const inputs &input) noexcept { wt = omp_get_wtime() - wt; if (!success) { - cout << fmt::format("Failed to export {}.", filename) << endl; + std::println("Failed to export {}.", filename); return __LINE__; } if (input.benchmark) { - cout << fmt::format("Export WE schem with {} blocks in {} seconds.\n", - kernel->xyz_size(), wt); + std::print("Export WE schem with {} blocks in {} seconds.\n", + kernel->xyz_size(), wt); } } @@ -430,12 +421,12 @@ int run(const inputs &input) noexcept { wt = omp_get_wtime() - wt; if (!success) { - cout << fmt::format("Failed to export {}.", filename) << endl; + std::println("Failed to export {}.", filename); return __LINE__; } if (input.benchmark) { - cout << fmt::format( + std::print( "Export vanilla structure file with {} blocks in {} seconds.\n", kernel->xyz_size(), wt); }