[
  {
    "path": ".clang-format",
    "content": "# Commented out parameters are those with the same value as base LLVM style.\n# We can uncomment them if we want to change their value, or enforce the\n# chosen value in case the base style changes (last sync: Clang 14.0).\n---\n### General config, applies to all languages ###\nBasedOnStyle:  LLVM\nAccessModifierOffset: -4\nAlignAfterOpenBracket: DontAlign\n# AlignArrayOfStructures: None\n# AlignConsecutiveMacros: None\n# AlignConsecutiveAssignments: None\n# AlignConsecutiveBitFields: None\n# AlignConsecutiveDeclarations: None\n# AlignEscapedNewlines: Right\nAlignOperands:   DontAlign\nAlignTrailingComments: false\n# AllowAllArgumentsOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: false\n# AllowShortEnumsOnASingleLine: true\n# AllowShortBlocksOnASingleLine: Never\n# AllowShortCaseLabelsOnASingleLine: false\n# AllowShortFunctionsOnASingleLine: All\n# AllowShortLambdasOnASingleLine: All\n# AllowShortIfStatementsOnASingleLine: Never\n# AllowShortLoopsOnASingleLine: false\n# AlwaysBreakAfterDefinitionReturnType: None\n# AlwaysBreakAfterReturnType: None\n# AlwaysBreakBeforeMultilineStrings: false\n# AlwaysBreakTemplateDeclarations: MultiLine\n# AttributeMacros:\n#   - __capability\n# BinPackArguments: true\n# BinPackParameters: true\n# BraceWrapping:\n#   AfterCaseLabel:  false\n#   AfterClass:      false\n#   AfterControlStatement: Never\n#   AfterEnum:       false\n#   AfterFunction:   false\n#   AfterNamespace:  false\n#   AfterObjCDeclaration: false\n#   AfterStruct:     false\n#   AfterUnion:      false\n#   AfterExternBlock: false\n#   BeforeCatch:     false\n#   BeforeElse:      false\n#   BeforeLambdaBody: false\n#   BeforeWhile:     false\n#   IndentBraces:    false\n#   SplitEmptyFunction: true\n#   SplitEmptyRecord: true\n#   SplitEmptyNamespace: true\n# BreakBeforeBinaryOperators: None\n# BreakBeforeConceptDeclarations: true\n# BreakBeforeBraces: Attach\n# BreakBeforeInheritanceComma: false\n# BreakInheritanceList: BeforeColon\n# BreakBeforeTernaryOperators: true\n# BreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: AfterColon\n# BreakStringLiterals: true\nColumnLimit:     0\n# CommentPragmas:  '^ IWYU pragma:'\n# QualifierAlignment: Leave\n# CompactNamespaces: false\nConstructorInitializerIndentWidth: 8\nContinuationIndentWidth: 8\nCpp11BracedListStyle: false\n# DeriveLineEnding: true\n# DerivePointerAlignment: false\n# DisableFormat:   false\n# EmptyLineAfterAccessModifier: Never\n# EmptyLineBeforeAccessModifier: LogicalBlock\n# ExperimentalAutoDetectBinPacking: false\n# PackConstructorInitializers: BinPack\nConstructorInitializerAllOnOneLineOrOnePerLine: true\n# AllowAllConstructorInitializersOnNextLine: true\n# FixNamespaceComments: true\n# ForEachMacros:\n#   - foreach\n#   - Q_FOREACH\n#   - BOOST_FOREACH\n# IfMacros:\n#   - KJ_IF_MAYBE\n# IncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '\".*\"'\n    Priority:        1\n  - Regex:           '^<.*\\.h>'\n    Priority:        2\n  - Regex:           '^<.*'\n    Priority:        3\n# IncludeIsMainRegex: '(Test)?$'\n# IncludeIsMainSourceRegex: ''\n# IndentAccessModifiers: false\nIndentCaseLabels: true\n# IndentCaseBlocks: false\n# IndentGotoLabels: true\n# IndentPPDirectives: None\n# IndentExternBlock: AfterExternBlock\n# IndentRequires:  false\nIndentWidth:     4\n# IndentWrappedFunctionNames: false\n# InsertTrailingCommas: None\n# JavaScriptQuotes: Leave\n# JavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\n# LambdaBodyIndentation: Signature\n# MacroBlockBegin: ''\n# MacroBlockEnd:   ''\n# MaxEmptyLinesToKeep: 1\n# NamespaceIndentation: None\n# PenaltyBreakAssignment: 2\n# PenaltyBreakBeforeFirstCallParameter: 19\n# PenaltyBreakComment: 300\n# PenaltyBreakFirstLessLess: 120\n# PenaltyBreakOpenParenthesis: 0\n# PenaltyBreakString: 1000\n# PenaltyBreakTemplateDeclaration: 10\n# PenaltyExcessCharacter: 1000000\n# PenaltyReturnTypeOnItsOwnLine: 60\n# PenaltyIndentedWhitespace: 0\n# PointerAlignment: Right\n# PPIndentWidth:   -1\n# ReferenceAlignment: Pointer\n# ReflowComments:  true\n# RemoveBracesLLVM: false\n# SeparateDefinitionBlocks: Leave\n# ShortNamespaceLines: 1\n# SortIncludes:    CaseSensitive\n# SortJavaStaticImport: Before\n# SortUsingDeclarations: true\n# SpaceAfterCStyleCast: false\n# SpaceAfterLogicalNot: false\n# SpaceAfterTemplateKeyword: true\n# SpaceBeforeAssignmentOperators: true\n# SpaceBeforeCaseColon: false\n# SpaceBeforeCpp11BracedList: false\n# SpaceBeforeCtorInitializerColon: true\n# SpaceBeforeInheritanceColon: true\n# SpaceBeforeParens: ControlStatements\n# SpaceBeforeParensOptions:\n#   AfterControlStatements: true\n#   AfterForeachMacros: true\n#   AfterFunctionDefinitionName: false\n#   AfterFunctionDeclarationName: false\n#   AfterIfMacros:   true\n#   AfterOverloadedOperator: false\n#   BeforeNonEmptyParentheses: false\n# SpaceAroundPointerQualifiers: Default\n# SpaceBeforeRangeBasedForLoopColon: true\n# SpaceInEmptyBlock: false\n# SpaceInEmptyParentheses: false\n# SpacesBeforeTrailingComments: 1\n# SpacesInAngles:  Never\n# SpacesInConditionalStatement: false\n# SpacesInContainerLiterals: true\n# SpacesInCStyleCastParentheses: false\n## Godot TODO: We'll want to use a min of 1, but we need to see how to fix\n## our comment capitalization at the same time.\nSpacesInLineCommentPrefix:\n  Minimum:         0\n  Maximum:         -1\n# SpacesInParentheses: false\n# SpacesInSquareBrackets: false\n# SpaceBeforeSquareBrackets: false\n# BitFieldColonSpacing: Both\n# StatementAttributeLikeMacros:\n#   - Q_EMIT\n# StatementMacros:\n#   - Q_UNUSED\n#   - QT_REQUIRE_VERSION\nTabWidth:        4\n# UseCRLF:         false\nUseTab:          Always\n# WhitespaceSensitiveMacros:\n#   - STRINGIZE\n#   - PP_STRINGIZE\n#   - BOOST_PP_STRINGIZE\n#   - NS_SWIFT_NAME\n#   - CF_SWIFT_NAME\n---\n### C++ specific config ###\nLanguage:        Cpp\nStandard:        c++17\n---\n### ObjC specific config ###\nLanguage:        ObjC\n# ObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 4\n# ObjCBreakBeforeNestedBlockParam: true\n# ObjCSpaceAfterProperty: false\n# ObjCSpaceBeforeProtocolList: true\n---\n### Java specific config ###\nLanguage:        Java\n# BreakAfterJavaFieldAnnotations: false\nJavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax']\n...\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Normalize EOL for all files that Git considers text files.\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [TokisanGames] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: # Replace with a single thanks.dev username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Report a technical issue\ndescription: Report a potential bug or technical issue in Terrain3D\nbody:\n\n- type: markdown\n  attributes:\n    value: |\n      Has your issue already been addressed in [Installation](https://terrain3d.readthedocs.io/en/latest/docs/installation.html), [Preparing textures](https://terrain3d.readthedocs.io/en/latest/docs/texture_prep.html), [Troubleshooting](https://terrain3d.readthedocs.io/en/latest/docs/troubleshooting.html), or [Tips](https://terrain3d.readthedocs.io/en/latest/docs/tips_technical.html)?\n\n- type: input\n  attributes:\n    label: Terrain3D version\n    description: >\n      Release version or the commit string of a development build. No -dev versions, provide the commit shown in `git log`.\n    placeholder: v1.0.0\n  validations:\n    required: true\n\n- type: input\n  attributes:\n    label: System information\n    description: |\n      - Specify the version of Godot and your OS, GPU and rendering backend (Vulkan Forward+, Mobile, Compatibility/WebGL).\n      - You can copy this information to your clipboard by using *Help > Copy System Info* at the top of the editor window.\n    placeholder: Godot v4.1.3.stable - Windows 10/64 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3070 Laptop GPU (NVIDIA; 31.0.15.4633)\n  validations:\n    required: true\n\n- type: dropdown\n  id: demo\n  attributes:\n    label: Is the issue reproducable in the demo?\n    description: |\n      Try to isolate the problem. E.g. If you're having issues with a texture, put it in the demo and see if it causes the same issue.\n    options:\n      - ''\n      - 'Yes'\n      - 'No'\n      - 'Not applicable'\n  validations:\n    required: true\n\n- type: textarea\n  attributes:\n    label: Issue description\n    description: |\n      Briefly describe the issue. What doesn't work, what are you expecting, and what have you done to troubleshooting it?\n      You can paste or drag in screenshots or videos. Format code with <code>```</code> before and after on their own lines.\n  validations:\n    required: true\n    \n- type: textarea\n  attributes:\n    label: Logs\n    description: |\n      Terrain3D has [extensive logging](https://terrain3d.readthedocs.io/en/latest/docs/troubleshooting.html#debug-logs). Enable it and attach a text file, or copy the logs here. Format logs with <code>```</code> before and after on their own lines.\n  validations:\n    required: false\n    \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n\ncontact_links:\n  - name: Documentation\n    url: https://terrain3d.readthedocs.io/\n    about: Many questions and issues have already been answered, including status, tips, and troubleshooting.\n  - name: Ask questions on Discord\n    url: https://tokisan.com/discord\n    about: Just have a question about features? Please use discord.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature request\ndescription: Suggest an idea for Terrain3D, or something else that isn't a technical issue\nbody:\n\n- type: markdown\n  attributes:\n    value: |\n      Have you reviewed the [status of current and pending features](https://terrain3d.readthedocs.io/en/latest/docs/tips_technical.html#are-certain-features-supported)?\n- type: textarea\n  attributes:\n    label: Description\n    description: |\n      Please describe the feature you would like to see in Terrain3D, how it might work, and why it's helpful.\n      Include examples of other terrain systems, GDC talks, white papers, and links to blogs or code that will be good reference info.\n  validations:\n    required: true\n"
  },
  {
    "path": ".github/actions/base-deps/action.yml",
    "content": "name: Setup Base Dependencies\ndescription: Setup base dependencies\ninputs:\n  platform:\n    required: true\n    description: Target platform.\nruns:\n  using: \"composite\"\n  steps:\n    - name: Setup Python 3.x\n      uses: actions/setup-python@v5\n      with:\n        python-version: 3.x\n\n    - name: Setup SCons 4.4\n      shell: bash\n      run: |\n        python -c \"import sys; print(sys.version)\"\n        python -m pip install scons==4.4.0\n        scons --version\n\n    - name: Setup Android Dependencies\n      if: inputs.platform == 'android'\n      uses: nttld/setup-ndk@v1\n      with:\n        ndk-version: r28b\n        link-to-sdk: true\n\n    - name: Setup Windows Dependencies\n      if: inputs.platform == 'windows'\n      shell: sh\n      run: |\n          sudo apt-get install mingw-w64\n          sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix\n          sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix\n\n    - name: Setup Web Dependencies\n      if: inputs.platform == 'web'\n      uses: mymindstorm/setup-emsdk@v14\n      with:\n        version: 3.1.64\n        no-cache: true\n\n    - name: Verify Emscripten setup\n      if: inputs.platform == 'web'\n      shell: bash\n      run: |\n        emcc -v\n"
  },
  {
    "path": ".github/actions/build-cache/action.yml",
    "content": "name: Setup Build Cache\ndescription: Setup build cache.\ninputs:\n  cache-name:\n    description: The cache base name (job name by default).\n    default: \"${{ github.job }}\"\n  scons-cache:\n    description: The scons cache path.\n    default: \"${{ github.workspace }}/.scons-cache/\"\nruns:\n  using: \"composite\"\n  steps:\n    # Upload cache on completion and check it out now\n    - name: Load .scons_cache directory\n      uses: actions/cache@v4\n      with:\n        path: ${{ inputs.scons-cache }}\n        key: ${{ inputs.cache-name }}-${{ github.ref }}\n\n        # We try to match an existing cache to restore from it. Each potential key is checked against\n        # all existing caches as a prefix. E.g. 'linux-template-minimal' would match any cache that\n        # starts with \"linux-template-minimal\", such as \"linux-template-minimal-master-refs/heads/master-6588a4a29af1621086feac0117d5d4d37af957fd\".\n        #\n        # We check these prefixes in this order:\n        #\n        #   1. The exact match, including the base branch, the commit reference, and the SHA hash of the commit.\n        #   2. A partial match for the same base branch and the same commit reference.\n        #   3. A partial match for the same base branch and the base branch commit reference.\n        #   4. A partial match for the same base branch only (not ideal, matches any PR with the same base branch).\n\n        restore-keys: |\n          ${{ inputs.cache-name }}-${{ github.ref }}\n          ${{ inputs.cache-name }}-refs/heads/main\n          ${{ inputs.cache-name }}"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "﻿name: 🤖 Android Builds\non: [ workflow_call, workflow_dispatch ]\n\njobs:\n  build:\n    name: 🤖 Android ${{ matrix.arch }} ${{ matrix.target }}\n    runs-on: ubuntu-22.04\n    strategy:\n      fail-fast: false\n      matrix:\n        platform: [android]\n        target: [debug, release]\n        arch: [arm64, arm32]\n\n    steps:\n      - name: Checkout Terrain3D\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Setup Base Dependencies\n        uses: ./.github/actions/base-deps\n        with:\n            platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n            ARCH: '${{ matrix.arch }}'\n        shell: sh\n        run: |\n          scons target=$TARGET platform='${{ matrix.platform }}' arch=$ARCH debug_symbols=no -j2\n\n      - name: Include Files\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n\n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "# Modeled off of godot-cpp https://github.com/godotengine/godot-cpp/blob/master/.github/workflows/ci.yml\n\nname: 🛠️ Build All\non: \n    push:\n        branches: [ main, 1.1-godot4.4 ]\n    pull_request:\n        paths: [ '**' ]\n    workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build:\n    name: ${{ matrix.name }} ${{ matrix.target }}\n    runs-on: ${{ matrix.runner }}\n    strategy:\n      fail-fast: false\n      matrix:\n        identifier: [linux, windows, macos, ios, android-arm32, android-arm64, web-nothreads]\n        target: [debug, release]\n        include:\n          - identifier: linux\n            platform: linux\n            name: 🐧 Linux\n            runner: ubuntu-22.04\n            flags: arch=x86_64\n            \n          - identifier: windows\n            platform: windows\n            name: 🪟 Windows\n            runner: ubuntu-22.04\n            flags: arch=x86_64\n\n          - identifier: macos\n            platform: macos\n            name: 🍎 macOS\n            runner: macos-latest\n            flags: arch=universal\n\n          - identifier: ios\n            platform: ios\n            name: 🍏 iOS\n            runner: macos-latest\n            flags: arch=universal\n\n          - identifier: android-arm32\n            platform: android\n            name: 🤖 Android Arm32\n            runner: ubuntu-22.04\n            flags: arch=arm32\n\n          - identifier: android-arm64\n            platform: android\n            name: 🤖 Android Arm64\n            runner: ubuntu-22.04\n            flags: arch=arm64\n\n          - identifier: web-nothreads\n            platform: web\n            name: 🌐 Web No-threads\n            runner: ubuntu-22.04\n            flags: threads=no\n\n    steps:\n      - name: Checkout Terrain3D\n        env:\n            FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n        \n      - name: Setup Base Dependencies\n        env:\n            FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        uses: ./.github/actions/base-deps\n        with:\n            platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        env:\n            FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.identifier }}-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n        shell: sh\n        run: |\n            scons target=$TARGET platform='${{ matrix.platform }}' ${{ matrix.flags }} debug_symbols=no -j2\n\n      - name: Strip Libraries (Windows/Linux)\n        if: ${{ matrix.platform == 'windows' || matrix.platform == 'linux' }}\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/\n          strip project/addons/terrain_3d/bin/libterrain.*\n          ls -l project/addons/terrain_3d/bin/\n\n      - name: Include Files\n        shell: sh\n        run: |\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        env:\n            FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.identifier }}-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n\n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        env:\n            FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".github/workflows/ios.yml",
    "content": "﻿name: 🍏 iOS Builds\non: [ workflow_call, workflow_dispatch ]\n\njobs:\n  build:\n    name: 🍏 iOS ${{ matrix.arch }} ${{ matrix.target }}\n    runs-on: macos-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        platform: [ios]\n        target: [debug, release]\n        arch: [universal]\n\n    steps:\n      - name: Checkout Terrain3D\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Setup Base Dependencies\n        uses: ./.github/actions/base-deps\n        with:\n            platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.platform }}-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n            ARCH: '${{ matrix.arch }}'\n        shell: sh\n        run: |\n            scons target=$TARGET platform='${{ matrix.platform }}' arch=$ARCH debug_symbols=no -j2\n\n      - name: Include Files\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/*\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n\n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".github/workflows/linux.yml",
    "content": "﻿name: 🐧 Linux Builds\non: [ workflow_call, workflow_dispatch ]\n\njobs:\n  build:\n    name: 🐧 Linux ${{ matrix.arch }} ${{ matrix.target }}\n    runs-on: ubuntu-22.04\n    strategy:\n      fail-fast: false\n      matrix:\n        platform: [linux]\n        target: [debug, release]\n        arch: [x86_64]\n\n    steps:\n      - name: Checkout Terrain3D\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Setup Base Dependencies\n        uses: ./.github/actions/base-deps\n        with:\n            platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.platform }}-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n            ARCH: '${{ matrix.arch }}'\n        shell: sh\n        run: |\n          scons target=$TARGET platform='${{ matrix.platform }}' arch=$ARCH debug_symbols=no -j2\n\n      - name: Prepare Files\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/\n          strip project/addons/terrain_3d/bin/libterrain.*\n          ls -l project/addons/terrain_3d/bin/\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n  \n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "﻿name: 🍎 macOS Builds\non: [ workflow_call, workflow_dispatch ]\n\njobs:\n  build:\n    name: 🍎 macOS ${{ matrix.arch }} ${{ matrix.target }}\n    runs-on: macos-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        platform: [macos]\n        target: [debug, release]\n        arch: [universal]\n\n    steps:\n      - name: Checkout Terrain3D\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Setup Base Dependencies\n        uses: ./.github/actions/base-deps\n        with:\n            platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.platform }}-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n            ARCH: '${{ matrix.arch }}'\n        shell: sh\n        run: |\n            scons target=$TARGET platform='${{ matrix.platform }}' arch=$ARCH debug_symbols=no -j2\n\n      - name: Include Files\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/*/\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n\n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".github/workflows/web.yml",
    "content": "﻿name: 🌐 Web Builds\non: [ workflow_call, workflow_dispatch ]\n\njobs:\n  build:\n    name: 🌐 Web ${{ matrix.arch }} ${{ matrix.target }}\n    runs-on: ubuntu-22.04\n    strategy:\n      fail-fast: false\n      matrix:\n        platform: [web]\n        target: [debug, release]\n        #threads: [yes, no]\n\n    steps:\n      - name: Checkout Terrain3D\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Setup Base Dependencies\n        uses: ./.github/actions/base-deps\n        with:\n          platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.platform }}-nothreads-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n        shell: sh\n        run: |\n          scons target=$TARGET platform='${{ matrix.platform }}' threads=no debug_symbols=no -j2\n\n      - name: Prepare Files\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.platform }}-nothreads-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n  \n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "﻿name: 🪟 Windows Builds\non: [ workflow_call, workflow_dispatch ]\n\njobs:\n  build:\n    name: 🪟 Windows ${{ matrix.arch }} ${{ matrix.target }}\n    runs-on: ubuntu-22.04\n    strategy:\n      fail-fast: false\n      matrix:\n        platform: [windows]\n        target: [debug, release]\n        arch: [x86_64]\n\n    steps:\n      - name: Checkout Terrain3D\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Setup Base Dependencies\n        uses: ./.github/actions/base-deps\n        with:\n            platform: ${{ matrix.platform }}\n\n      - name: Setup Build Cache\n        uses: ./.github/actions/build-cache\n        with:\n          cache-name: ${{ matrix.platform }}-${{ matrix.target }}\n        continue-on-error: true\n\n      - name: Build Terrain3D\n        env:\n            SCONS_CACHE: \"${{ github.workspace }}/.scons-cache/\"\n            TARGET: 'template_${{ matrix.target }}'\n            ARCH: '${{ matrix.arch }}'\n        shell: sh\n        run: |\n            scons target=$TARGET platform='${{ matrix.platform }}' arch=$ARCH debug_symbols=no -j2\n\n      - name: Prepare Files\n        shell: sh\n        run: |\n          ls -l project/addons/terrain_3d/bin/\n          strip project/addons/terrain_3d/bin/libterrain.*\n          ls -l project/addons/terrain_3d/bin/\n          cp '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.txt' ${{ github.workspace }}/project/addons/terrain_3d/\n\n      - name: Upload Package\n        uses: actions/upload-artifact@v4\n        with:\n          include-hidden-files: true\n          name: t3d-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.target }}\n          path: |\n            ${{ github.workspace }}/project/\n\n  merge:\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          include-hidden-files: true\n          name: ${{ github.event.repository.name }}\n          pattern: t3d-*\n          delete-merged: true"
  },
  {
    "path": ".gitignore",
    "content": "# Terrain3D\nproject/addons/terrain_3d/bin/\nproject/addons/explore-editor-theme/\nproject/_dev/\nproject/_export/\nproject/_tests/\nproject/test/\nsrc/_archive/\nsrc/gen/\n_misc/\n_patches/\n*.zip\n\n# Docs\ndoc/_build\ndoc/art/\n\n# Godot-specific ignores (Include script cache. See godot issue #75388)\n/project/.import/\n/project/.godot/\nexport.cfg\nexport_presets.cfg\n\n# Objects\n.scons-cache/\n*.os\n*.obj\n*.o\n\n# SConstruct\n.sconf_temp\n.sconsign.dblite\n*.pyc\n\n# MacOS\n.DS_Store\n\n# Editors\n.vscode/\n.vs/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"godot-cpp\"]\n\tpath = godot-cpp\n\turl = https://github.com/godotengine/godot-cpp.git\n\tbranch = master\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\nversion: 2\n\nbuild:\n  os: \"ubuntu-22.04\"\n  tools:\n    python: \"3.11\"\n\npython:\n  install:\n    - requirements: doc/requirements.txt\n\nsphinx:\n  configuration: doc/conf.py\n\n# Possible options: htmlzip, pdf, epub\nformats: []"
  },
  {
    "path": "AUTHORS.md",
    "content": "﻿# Terrain3D Authors\n\nThe creation of this plugin is thanks to the following contributors.\n\n## Core Team\n* Cory Petkovsek [@TokisanGames](https://github.com/TokisanGames)\n* Roope Palmroos [@outobugi](https://github.com/outobugi)\n* Emerson Rowland [@Xtarsia](https://github.com/Xtarsia)\n\n## MVPs\n* Tom Coxon [@tcoxon](https://github.com/tcoxon)\n* Aidan Davey [@aidandavey](https://github.com/aidandavey)\n\n## Contributors\n* Lorenz [@lw64](https://github.com/lw64)\n* Loic Chen [@painfulexistence](https://github.com/painfulexistence)\n* [@Dekker3D](https://github.com/Dekker3D)\n* Laurent Senta [@laurentsenta](https://github.com/laurentsenta)\n* Ryan [@Ryan-000](https://github.com/Ryan-000)\n* Jacob Coughenour [@jacobcoughenour](https://github.com/jacobcoughenour)\n* Slashscreen [@SlashScreen](https://github.com/SlashScreen)\n* Roman Shapiro [@rds1983](https://github.com/rds1983)\n* Malido [@Malidos](https://github.com/Malidos)\n* Bruno Meneguello [@bkmeneguello](https://github.com/bkmeneguello)\n* xht [@xanhast](https://github.com/xanhast)\n* [@wenqiangwang](https://github.com/wenqiangwang)\n* [@k1r4n8](https://github.com/k1r4n8)\n* [@jesus-g20](https://github.com/jesus-g20)\n* [@jeffercize](https://github.com/jeffercize)\n* [@artoonu](https://github.com/artoonu)\n* [@Zennii](https://github.com/Zennii)\n* Sven Cannivy [@svencan](https://github.com/svencan)\n* StAkira [@stakira](https://github.com/stakira)\n* Sean Otto [@seanj29](https://github.com/seanj29)\n* Scott Davis [@scottdavis](https://github.com/scottdavis)\n* Rose [@az-raven](https://github.com/az-raven)\n* [@OzelotVanilla](https://github.com/OzelotVanilla)\n* Matt [@FishOfTheNorthStar](https://github.com/FishOfTheNorthStar)\n* L [@lfxu](https://github.com/lfxu)\n* Joyless [@Joy-less](https://github.com/Joy-less)\n* Johan Frohlander [@pew-jfrohlander](https://github.com/pew-jfrohlander)\n* [@GabrielPlante](https://github.com/GabrielPlante)\n* Furq [@Furqit](https://github.com/Furqit)\n* Feiyun Wang [@feiyunw](https://github.com/feiyunw)\n* Brian [@epitaque](https://github.com/epitaque)\n* Dissonant Void [@DissonantVoid](https://github.com/DissonantVoid)\n* [@directedchaossoftware](https://github.com/directedchaossoftware)\n* [@CrowhopTech](https://github.com/CrowhopTech)\n* Benjamin Wolff [@benjiwolff](https://github.com/benjiwolff)"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing To Terrain3D\n\nWe need your help to make this the best terrain plugin for Godot.\n\nPlease see [System Architecture](https://terrain3d.readthedocs.io/en/stable/docs/system_architecture.html) to gain an understanding of how the system works. Then review the [roadmap](https://github.com/users/TokisanGames/projects/3) for priority of issues.\n\nIf you wish to take on a major component, it's best to join our [discord server](https://tokisan.com/discord) and discuss your plans in #terrain3d-dev to make sure your efforts are aligned with other plans.\n\n**Table of Contents**\n* [Important Directories](#important-directories)\n* [Setup Your System](#setup-your-system)\n* [PR Workflow](#pr-workflow)\n* [Code Style](#code-style)\n* [Documentation](#documentation)\n* [Maintainers](#maintainers)\n\n \n## Important Directories\n\n* `src` - C++ source for the library\n* `src/shaders` - GLSL source for the default shader\n* `doc/doc_classes` - XML docs for C++ classes\n* `doc/docs` - MD tutorial docs\n* `project/addons/terrain_3d`\n  * `src` - GDScript for the editor plugin: the user interface for hand editing\n  * `menu` - GDScript for the tools menu: bakers, channel packer\n  * `tools` - GDScript for the importer, which will eventually be merged into the menu\n  * `utils` - GDScript for other objects, eg. terrain_3d_objects.gd\n  * `extras` - GDScript examples for users\n\n\n## Setup Your System\n\nMake sure you are setup to [build the plugin from source](https://terrain3d.readthedocs.io/en/stable/docs/building_from_source.html). \n\n### Install clang-format\n\nclang-format will adjust the style of your code to a consistent standard. Once you install it you can manually run it on all of your code to see or apply changes, and you can set it up to run automatically upon each commit.\n\n#### Installing clang-format binary onto your system.\n* Download version 13 or later\n* Make sure the LLVM binary directory where `clang-format` is stored gets added to the `PATH` during installation\n* Linux/OSX: Install the `clang-format` package, or all of `LLVM` or `clang` if your distribution doesn't provide the standalone tool\n* Windows: Download LLVM for Windows from <https://releases.llvm.org/download.html>\n\n#### Using clang-format automatically\n\nWe use Godot's clang-format hooks that will format your code upon making a commit. Install the hooks into your repo after cloning.\n\n* Copy `tools/hooks/*` into `.git/hooks` or run `python tools/install-hooks.py`\n\n#### Using clang-format manually\n\n* View a formatted file, no changes on disk: `clang-format <filenames>`\n* See what changes would be made: `git-clang-format --diff <filenames>`\n* Change the files in place: `clang-format -i <filenames>`\n\n \n## PR Workflow\n\nWe use the standard [Godot PR workflow](https://contributing.godotengine.org/en/latest/organization/pull_requests/creating_pull_requests.html). Please submit PRs according to the same process Godot uses.\n\nThis includes: \n* Creating a new branch (not main) before submitting the PR.\n* Never using git merge, or the `sync` button. Only fetch, push, pull.\n* To update your PR to the latest main, rebase it then force push into your branch.\n  * `git pull --rebase upstream main`\n  * `git push -f`\n\nRead the guide above for more details.\n\n\n## Code Style\n\n### GDScript\n\nIn general, follow the [Godot GDScript style guidelines](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html). \nIn addition:\n* All variables and functions are static typed, with a colon then space (eg. `var state: int = 3`)\n* Auto static typing can be used *only* when the type is specifically assigned (eg. `var point := Vector2(1, 1)`)\n* Two blank lines between functions\n\n### GLSL\n\n* Similar to C++ formatting below, except use `float` and no clang-format\n* Private uniforms are prefaced with `_` and are hidden from the inspector and not accessible via set/get_shader_param()\n\n### C++\n\nIn general, follow the [Godot C++ style guidelines](https://contributing.godotengine.org/en/latest/engine/guidelines/code_style.html).\nIn addition:\n\nUse const correctness:\n* Function parameters that won't be changed (almost all) should be marked const. Exceptions are pointers, or where passing a variable the function is supposed to modify, eg. Terrain3D::_generate_triangles\n* Functions that won't change the object should be marked const (e.g. most get_ functions)\n\nPass by reference:\n* Pass everything larger than 4 bytes by reference, including Ref<> and arrays, dictionaries, RIDs. e.g. `const Transform3D &xform`\n\n* Floats:\n* Use `real_t` instead of `float`\n* Format float literals like `0.0f` or `0.f`\n* Float literals and `real_t` variables can share operations (e.g. `mydouble += 1.0f`) unless the compiler complains. e.g. `Math::lerp(mydouble, real_t(0.0f), real_t(1.0f))`\n\n* Standard Library & Godot Functions:\n* Use `std::abs`, not `Math::abs` (same), and definitely not `abs` (broken on mingw)\n* Use `std::isnan`, not `Math::is_nan` (same)\n\nBraces:\n* Everything braced - no if/for one-liners. Including switch cases\n* One line setters/getters can go in the header file\n* Opening brace on the initial line (eg. `if (condition) {`), and ending brace at the same tab stop as the initial line\n\nPrivate & Public:\n* Private variables/functions prefaced with `_`\n* One initial public section for constants\n* Private/public/protected for members and functions in that order, in header and cpp files\n* Functions in h and cpp files in same order\n\nOther formatting:\n* One blank line between functions\n* All code passed through clang-format. See above\n\n\n## Documentation\n\nAll PRs that include new methods and features or changed functionality should include documentation updates. This could be in the form of a tutorial page for the user manual, or API changes to the XML Class Reference.\n\n### User Manual\n\nTutorials and usage documentation lives in [doc/docs](https://github.com/TokisanGames/Terrain3D/tree/main/doc/docs) and is written in Markdown (*.md). Images are stored in `images` and videos are stored [_static/video](https://github.com/TokisanGames/Terrain3D/tree/main/doc/_static/video). \n\nPages also need to be included in the table of contents `doc/index.rst`. Readthedocs will then be able to find everything it needs to build the html documentation upon a commit.\n\n### Class Reference\n\nThe class reference documentation that contributors edit is stored in [XML files](https://github.com/TokisanGames/Terrain3D/tree/main/doc/classes). These files are used as the source for generated documentation.\n\nEdit the class reference according to the [Godot class reference primer](https://docs.godotengine.org/en/stable/engine_details/class_reference/index.html).\n\nGodot's doc-tool is used to extract or update the class structure from the compiled addon. See below for instructions.\n\n### Using the Documentation Generation Tools\n\nThis step isn't required for contributors. You may ask for help generating the XML class structure so you can edit it, or generating the resulting RST files. \n\n#### To setup your system\n\n1. Use a bash shell available in linux, [gitforwindows](https://gitforwindows.org), or [Microsoft's WSL](https://learn.microsoft.com/en-us/windows/wsl/install).\n2. Install the following modules using python's pip: `pip install docutils myst-parser sphinx sphinx-rtd-theme sphinx-rtd-dark-mode`.\n3. Edit `doc/build_docs.sh` and adjust the paths to your Godot executable and `make_rst.py`, found in the Godot repository.\n\n#### To edit the documentation\n\n1. Build Terrain3D with your updated code.\n2. Within the `doc` folder, run `./build_docs.sh`. The following will occur:\n  - The Godot executable dumps the XML structure for all classes, including those of installed addons.\n  - Any existing XML files (eg Terrain3D*) will be updated with the new structure, leaving prior written documentation.\n  - Sphinx RST files are generated from the XML files.\n  - All non-Terrain3D XML files are removed.\n  - A local html copy of the docs are generated from the Markdown and RST files, and a browser is open to view them.\n3. Fill in the XML files with documentation of the new generated structure and make any other changes to the Markdown files.\n4. Run the script again to update the RST files. This isn't necessary for Markdown updates, except to view the changes locally.\n5. Push your updates to the Markdown, XML, and RST files to the repository. Due to the nature of generation scripts, carefully review the changes so you only push those you intend.\n6. Readthedocs will detect commits to the main tree and will build the online html docs from the Markdown and RST files.\n\nDoc generation via Sphinx is configured by conf.py and requirements.txt. Readthedocs also reads .readthedocs.yaml. The website is configured to automatically build based on specifically chosen branches and tags. `latest` is `main`. `stable` is a tag that we must manually update to point to the latest stable commit.\n\n\n## Maintainers\n\nThere are various responsibilities and processes maintainers need to do to update Terrain3D.\n\n1. Ensure PR builds are successful, and occasionally make changes to the build scripts when Github makes changes.\n2. Ensure PRs are up to code standards and include XML documentation. You may need to generate the XML for them first.\n3. [Update docs](#using-the-documentation-generation-tools) to generate the XML and RST files. Readthedocs will update automatically once PRs are merged. Though if it fails, you may need to log in and figure out why. It can be a bit finicky. They have automatic tags.\n4. [Update C# bindings](generating_csharp_bindings.md) as the API changes.\n5. Update versions and tags as indicated below.\n\n\n## Updating New Versions and Releases of Terrain3D\n\nEdit the following files on new releases and versions.\n\n### New Terrain3D Release Version\n* Set src/terrain_3d.h : _version\n* Set project/addons/terrain_3d/plugin.cfg : version\n* Set doc/conf.py : version\n* Rebuild the docs with doc/build_docs.sh\n* Review minimum version in terrain.gdextension\n* Create a new tag for github\n* Create a new branch for new milestones (1.0) so readthedocs will create a new version. You may need to enable it on their website.\n* Reassign the `stable` tag for readthedocs to update that doc build. `latest` automatically builds off of `main`.\n\n\n### New Terrain3DRegion Data Format Version\n* Update src/terrain_3d_data.h : CURRENT_DATA_VERSION\n* Update docs/data_format.md\n\n\n### New Year:\n* Update Copyright header in all source files and conf.py\n\n\n## Maintaining multiple versions\n\nOccasionally we might maintain two builds of the same version, such as `1.1-godot4.4` and `1.1` for Godot 4.5+. In this case the difference was the former used the godot-cpp 4.4 API, the latter used the 4.5 API. There was a minor but important difference in our code. I wanted all commits from one branch to be in the other branch, except for the few that changed the godot-cpp API. Here's how that process worked.\n\n1. At the time, `main` was 1.1-dev and I had made a separate `1.1-godot4.4` branch. I made a commit changing godot-cpp to the 4.5 API.\n2. Then after some commits, I cherry-picked all of the new ones from `main` into `1.1-godot4.4`.\n3. On `main`, I created a tag called `_last-cherry-pick` so that when I periodically updated the 4.4 branch I knew where I left off.\n4. Bulk cherry-picking is easy to do with the following:\n\n```\ngit checkout 1.1-godot4.4                              # Start in the destination branch\ngit cherry-pick --no-merges _last-cherry-pick..main    # Use any two hashes or tags\ngit diff main                                          # Ensure the only difference is the 4.5 API change in this example\ngit push                                               # Upload all bulk cherry-picked commits\n```\n\n\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "MIT License\n\nCopyright (c) 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "![Terrain3D Logo](/doc/docs/images/terrain3d.jpg)\n\n# Terrain3D\nA high performance, editable terrain system for Godot 4.\n\n\n## Features\n* Written in C++ as a GDExtension addon, which works with official builds of Godot Engine\n* [Can be accessed](https://terrain3d.readthedocs.io/en/stable/docs/programming_languages.html) by GDScript, C#, and any language Godot supports\n* Terrains as small as 64x64m up to 65.5x65.5km (4295km^2) in non-contiguous and variable sized regions\n* Up to 32 textures\n* Up to 10 levels of detail for the terrain mesh\n* Foliage instancing, with up to 10 levels of detail, and a shadow impostor\n* Sculpting, holes, texture painting, texture detiling, painting colors and wetness\n* Imports heightmaps from [HTerrain](https://github.com/Zylann/godot_heightmap_plugin/), Gaea, World Creator, World Machine, Unity, Unreal and any tool that can export a heightmap. See [heightmaps](https://terrain3d.readthedocs.io/en/stable/docs/heightmaps.html)\n\n\n## Games Using Terrain3D\n\nPlease see the [featured games using Terrain3D](https://terrain3d.readthedocs.io/en/latest/docs/games.html) for examples of what it can do.\n\n\n## Getting Started\n\n1. Read the [Introduction](https://terrain3d.readthedocs.io/en/stable/docs/introduction.html) to understand how this terrain system works.\n\n2. Read the [Installation & Upgrade](https://terrain3d.readthedocs.io/en/stable/docs/installation.html) instructions.\n\n3. Watch the [tutorial videos](https://terrain3d.readthedocs.io/en/stable/docs/tutorial_videos.html) and read through the documentation.\n\n4. For support, read [Getting Help](https://terrain3d.readthedocs.io/en/stable/docs/getting_help.html) and join our [Discord server](https://tokisan.com/discord).\n\n\n## Credit\nDeveloped for the Godot community by:\n\n|||\n|--|--|\n| **Cory Petkovsek, Tokisan Games** | [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/twitter.png?raw=true\" width=\"24\"/>](https://twitter.com/TokisanGames) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/github.png?raw=true\" width=\"24\"/>](https://github.com/TokisanGames) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true\" width=\"24\"/>](https://tokisan.com/) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/discord.png?raw=true\" width=\"24\"/>](https://tokisan.com/discord) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/youtube.png?raw=true\" width=\"24\"/>](https://www.youtube.com/@TokisanGames)|\n| **Roope Palmroos, Outobugi Games** | [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/twitter.png?raw=true\" width=\"24\"/>](https://twitter.com/outobugi) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/github.png?raw=true\" width=\"24\"/>](https://github.com/outobugi) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true\" width=\"24\"/>](https://outobugi.com/) [<img src=\"https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/youtube.png?raw=true\" width=\"24\"/>](https://www.youtube.com/@outobugi)|\n\nAnd the contribution team in [AUTHORS.md](https://terrain3d.readthedocs.io/en/stable/docs/authors.html) and on the right of the github page.\n\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](https://github.com/TokisanGames/Terrain3D/blob/main/CONTRIBUTING.md) if you would like to help make Terrain3D the best terrain system for Godot.\n\n\n## License\n\nThis addon has been released under the [MIT License](https://github.com/TokisanGames/Terrain3D/blob/main/LICENSE.txt).\n"
  },
  {
    "path": "SConstruct",
    "content": "#!/usr/bin/env python\nfrom glob import glob\nfrom pathlib import Path\nimport os\n\n# TODO: Do not copy environment after godot-cpp/test is updated <https://github.com/godotengine/godot-cpp/blob/master/test/SConstruct>.\nenv = SConscript(\"godot-cpp/SConstruct\")\n\n# Add source files.\nenv.Append(CPPPATH=[\"src/\"])\nsources = Glob(\"src/*.cpp\")\n\n# Find gdextension path even if the directory or extension is renamed (e.g. project/addons/example/example.gdextension).\n(extension_path,) = glob(\"project/addons/terrain_3d/*.gdextension\")\n\n# Find the addon path (e.g. project/addons/example).\naddon_path = Path(extension_path).parent\n\n# Find the project name from the gdextension file (e.g. example).\nproject_name = Path(extension_path).stem\n\nscons_cache_path = os.environ.get(\"SCONS_CACHE\")\nif scons_cache_path != None:\n    CacheDir(scons_cache_path)\n    print(\"Scons cache enabled... (path: '\" + scons_cache_path + \"')\")\n\n# Embed documentation into the engine\nif env[\"target\"] in [\"editor\", \"template_debug\"]:\n    doc_data = env.GodotCPPDocData(\"src/gen/doc_data.gen.cpp\", source=Glob(\"doc/doc_classes/*.xml\"))\n    sources.append(doc_data)\n\n# Create the library target (e.g. libexample.linux.debug.x86_64.so).\ndebug_or_release = \"release\" if env[\"target\"] == \"template_release\" else \"debug\"\nif env[\"platform\"] == \"macos\":\n    library = env.SharedLibrary(\n        \"{0}/bin/lib{1}.{2}.{3}.framework/{1}.{2}.{3}\".format(\n            addon_path,\n            project_name,\n            env[\"platform\"],\n            debug_or_release,\n        ),\n        source=sources,\n    )\nelse:\n    library = env.SharedLibrary(\n        \"{}/bin/lib{}.{}.{}.{}{}\".format(\n            addon_path,\n            project_name,\n            env[\"platform\"],\n            debug_or_release,\n            env[\"arch\"],\n            env[\"SHLIBSUFFIX\"],\n        ),\n        source=sources,\n    )\n\n## Option to use C++20 for this extension by replacing CXXFLAGS\n#if env.get(\"is_msvc\", False):\n#    env.Replace(CXXFLAGS=[\"/std:c++20\"])\n#else:\n#    env.Replace(CXXFLAGS=[\"-std=c++20\"])\n\n## Reenable CXXFLAGS removed by the above from godot-cpp/tools/godotcpp.py\n# Disable exception handling. Godot doesn't use exceptions anywhere, and this\n# saves around 20% of binary size and very significant build time.\n#if env[\"disable_exceptions\"]:\n#    if env.get(\"is_msvc\", False):\n#        env.Append(CPPDEFINES=[(\"_HAS_EXCEPTIONS\", 0)])\n#    else:\n#        env.Append(CXXFLAGS=[\"-fno-exceptions\"])\n#elif env.get(\"is_msvc\", False):\n#    env.Append(CXXFLAGS=[\"/EHsc\"])\n\nDefault(library)\n"
  },
  {
    "path": "Terrain3D.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.7.34024.191\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Terrain3D\", \"Terrain3D.vcxproj\", \"{B8850C81-3339-46A9-9668-CAA004E84629}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Debug|x64.Build.0 = Debug|x64\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Debug|x86.Build.0 = Debug|Win32\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Release|x64.ActiveCfg = Release|x64\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Release|x64.Build.0 = Release|x64\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Release|x86.ActiveCfg = Release|Win32\n\t\t{B8850C81-3339-46A9-9668-CAA004E84629}.Release|x86.Build.0 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {119F9825-B9FE-4F51-9225-7EA96BF5BF7F}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Terrain3D.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>17.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{b8850c81-3339-46a9-9668-caa004e84629}</ProjectGuid>\n    <RootNamespace>Terrain3D</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Makefile</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <NMakeBuildCommandLine>scons dev_build=yes</NMakeBuildCommandLine>\n    <NMakeReBuildCommandLine>scons dev_build=yes</NMakeReBuildCommandLine>\n    <NMakeCleanCommandLine>scons --clean</NMakeCleanCommandLine>\n    <NMakeIncludeSearchPath>$(SolutionDir)\\src;$(SolutionDir)\\godot-cpp\\gdextension;$(SolutionDir)\\godot-cpp\\gen\\include;$(SolutionDir)\\godot-cpp\\include</NMakeIncludeSearchPath>\n    <AdditionalOptions>/std:c++17</AdditionalOptions>\n    <OutDir>.vs</OutDir>\n    <IntDir>.vs</IntDir>\n    <NMakePreprocessorDefinitions>GDEXTENSION</NMakePreprocessorDefinitions>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n    <BuildLog>\n      <Path>.vs/Terrain3D-build.log</Path>\n    </BuildLog>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClInclude Include=\"src\\constants.h\" />\n    <ClInclude Include=\"src\\generated_texture.h\" />\n    <ClInclude Include=\"src\\target_node_3d.h\" />\n    <ClInclude Include=\"src\\terrain_3d_mesher.h\" />\n    <ClInclude Include=\"src\\register_types.h\" />\n    <ClInclude Include=\"src\\terrain_3d.h\" />\n    <ClInclude Include=\"src\\terrain_3d_asset_resource.h\" />\n    <ClInclude Include=\"src\\terrain_3d_collision.h\" />\n    <ClInclude Include=\"src\\terrain_3d_data.h\" />\n    <ClInclude Include=\"src\\terrain_3d_editor.h\" />\n    <ClInclude Include=\"src\\logger.h\" />\n    <ClInclude Include=\"src\\terrain_3d_instancer.h\" />\n    <ClInclude Include=\"src\\terrain_3d_mesh_asset.h\" />\n    <ClInclude Include=\"src\\terrain_3d_region.h\" />\n    <ClInclude Include=\"src\\terrain_3d_texture_asset.h\" />\n    <ClInclude Include=\"src\\terrain_3d_util.h\" />\n    <ClInclude Include=\"src\\terrain_3d_material.h\" />\n    <ClInclude Include=\"src\\terrain_3d_assets.h\" />\n    <ClInclude Include=\"src\\unit_testing.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"src\\generated_texture.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_mesher.cpp\" />\n    <ClCompile Include=\"src\\register_types.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_collision.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_data.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_editor.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_instancer.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_material.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_mesh_asset.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_region.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_texture_asset.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_assets.cpp\" />\n    <ClCompile Include=\"src\\terrain_3d_util.cpp\" />\n    <ClCompile Include=\"src\\unit_testing.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\".github\\actions\\base-deps\\action.yml\" />\n    <None Include=\".github\\actions\\build-cache\\action.yml\" />\n    <None Include=\".github\\ISSUE_TEMPLATE\\bug_report.yml\" />\n    <None Include=\".github\\ISSUE_TEMPLATE\\config.yml\" />\n    <None Include=\".github\\ISSUE_TEMPLATE\\feature_request.yml\" />\n    <None Include=\".github\\workflows\\android.yml\" />\n    <None Include=\".github\\workflows\\build.yml\" />\n    <None Include=\".github\\workflows\\ios.yml\" />\n    <None Include=\".github\\workflows\\linux.yml\" />\n    <None Include=\".github\\workflows\\macos.yml\" />\n    <None Include=\".github\\workflows\\web.yml\" />\n    <None Include=\".github\\workflows\\windows.yml\" />\n    <None Include=\".gitignore\" />\n    <None Include=\"AUTHORS.md\" />\n    <None Include=\"CONTRIBUTING.md\" />\n    <None Include=\"doc\\build_docs.sh\" />\n    <None Include=\"doc\\conf.py\" />\n    <None Include=\"doc\\docs\\collision.md\" />\n    <None Include=\"doc\\docs\\generating_csharp_bindings.md\" />\n    <None Include=\"doc\\docs\\introduction.md\" />\n    <None Include=\"doc\\docs\\double_precision.md\" />\n    <None Include=\"doc\\docs\\games.md\" />\n    <None Include=\"doc\\docs\\instancer.md\" />\n    <None Include=\"doc\\docs\\keyboard_shortcuts.md\" />\n    <None Include=\"doc\\docs\\platforms.md\" />\n    <None Include=\"doc\\docs\\nightly_builds.md\" />\n    <None Include=\"doc\\docs\\press.md\" />\n    <None Include=\"doc\\docs\\data_format.md\" />\n    <None Include=\"doc\\docs\\heightmaps.md\" />\n    <None Include=\"doc\\docs\\programming_languages.rst\" />\n    <None Include=\"doc\\docs\\displacement.md\" />\n    <None Include=\"doc\\docs\\texture_painting.md\" />\n    <None Include=\"doc\\docs\\user_interface.md\" />\n    <None Include=\"doc\\index.rst\" />\n    <None Include=\"doc\\docs\\tips_environment.md\">\n      <SubType>\n      </SubType>\n    </None>\n    <None Include=\"project\\addons\\terrain_3d\\extras\\shaders\\lightweight.gdshader\" />\n    <None Include=\"project\\addons\\terrain_3d\\extras\\shaders\\minimum.gdshader\" />\n    <None Include=\"project\\addons\\terrain_3d\\extras\\shaders\\ocean_shader.gdshader\" />\n    <None Include=\"project\\addons\\terrain_3d\\plugin.cfg\" />\n    <None Include=\"project\\addons\\terrain_3d\\terrain.gdextension\" />\n    <None Include=\"README.md\" />\n    <None Include=\"SConstruct\" />\n    <None Include=\"src\\shaders\\debug_views.glsl\" />\n    <None Include=\"src\\shaders\\displacement.glsl\" />\n    <None Include=\"src\\shaders\\displacement_buffer.glsl\" />\n    <None Include=\"src\\shaders\\macro_variation.glsl\" />\n    <None Include=\"src\\shaders\\main.glsl\" />\n    <None Include=\"src\\shaders\\overlays.glsl\" />\n    <None Include=\"src\\shaders\\pbr_views.glsl\">\n      <FileType>Document</FileType>\n    </None>\n    <None Include=\"src\\shaders\\projection.glsl\" />\n    <None Include=\"src\\shaders\\samplers.glsl\" />\n    <None Include=\"src\\shaders\\backgrounds.glsl\" />\n    <None Include=\"src\\shaders\\editor_functions.glsl\" />\n    <None Include=\"src\\shaders\\dual_scaling.glsl\" />\n    <None Include=\"src\\shaders\\auto_shader.glsl\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\".readthedocs.yaml\" />\n    <Text Include=\"doc\\docs\\import_export.md\" />\n    <Text Include=\"doc\\docs\\occlusion_culling.md\" />\n    <Text Include=\"doc\\docs\\shader_design.md\" />\n    <Text Include=\"doc\\docs\\system_architecture.md\" />\n    <Text Include=\"doc\\docs\\installation.md\" />\n    <Text Include=\"doc\\docs\\controlmap_format.md\" />\n    <Text Include=\"doc\\docs\\getting_help.md\" />\n    <Text Include=\"doc\\docs\\tutorial_videos.md\" />\n    <Text Include=\"doc\\docs\\troubleshooting.md\" />\n    <Text Include=\"doc\\docs\\tips_technical.md\" />\n    <Text Include=\"doc\\docs\\texture_prep.md\" />\n    <Text Include=\"doc\\docs\\building_from_source.md\" />\n    <Text Include=\"doc\\requirements.txt\" />\n    <Text Include=\"doc\\_static\\theme_overrides.css\" />\n    <Text Include=\"LICENSE.txt\" />\n    <Text Include=\"doc\\docs\\navigation.md\" />\n    <Text Include=\"src\\shaders\\gpu_depth.glsl\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Xml Include=\"doc\\doc_classes\\Terrain3D.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DCollision.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DData.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DEditor.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DInstancer.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DMaterial.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DMeshAsset.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DRegion.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DTextureAsset.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DAssets.xml\" />\n    <Xml Include=\"doc\\doc_classes\\Terrain3DUtil.xml\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Terrain3D.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"1. Project Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n    <Filter Include=\"2. Docs\">\n      <UniqueIdentifier>{cddadac5-d2a5-45b0-947d-d3e2b055c87c}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"6. C++\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"5. Headers\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"4. Shaders\">\n      <UniqueIdentifier>{fa4f3a1c-e2a4-4421-9b19-e15c14fce184}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"3. XML\">\n      <UniqueIdentifier>{0d771ba6-8e4a-4985-895c-d9b8eec8b3fd}</UniqueIdentifier>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"src\\terrain_3d_assets.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_mesher.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\register_types.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_editor.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_material.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\constants.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\generated_texture.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\logger.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_util.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_instancer.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_mesh_asset.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_texture_asset.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_asset_resource.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_region.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_data.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\terrain_3d_collision.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\target_node_3d.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n    <ClInclude Include=\"src\\unit_testing.h\">\n      <Filter>5. Headers</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"src\\terrain_3d_mesher.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\register_types.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_editor.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_texture_asset.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_assets.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_material.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\generated_texture.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_util.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_instancer.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_mesh_asset.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_region.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_data.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\terrain_3d_collision.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n    <ClCompile Include=\"src\\unit_testing.cpp\">\n      <Filter>6. C++</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\".github\\actions\\build-cache\\action.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\actions\\base-deps\\action.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\workflows\\android.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\workflows\\build.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\workflows\\linux.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\workflows\\macos.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"project\\addons\\terrain_3d\\plugin.cfg\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\workflows\\windows.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"project\\addons\\terrain_3d\\terrain.gdextension\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"src\\shaders\\debug_views.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\main.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\backgrounds.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\".gitignore\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"SConstruct\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"README.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"CONTRIBUTING.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"AUTHORS.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\index.rst\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"src\\shaders\\auto_shader.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\dual_scaling.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\editor_functions.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\samplers.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"doc\\docs\\texture_painting.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\conf.py\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"doc\\docs\\press.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\platforms.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\".github\\workflows\\ios.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"doc\\docs\\nightly_builds.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\double_precision.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\games.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\instancer.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\user_interface.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\".github\\ISSUE_TEMPLATE\\bug_report.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\ISSUE_TEMPLATE\\config.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\".github\\ISSUE_TEMPLATE\\feature_request.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"doc\\docs\\collision.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\data_format.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\introduction.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\".github\\workflows\\web.yml\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"project\\addons\\terrain_3d\\extras\\shaders\\minimum.gdshader\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"doc\\docs\\heightmaps.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\tips_environment.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"src\\shaders\\overlays.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"doc\\docs\\keyboard_shortcuts.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"project\\addons\\terrain_3d\\extras\\shaders\\lightweight.gdshader\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"doc\\docs\\generating_csharp_bindings.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"doc\\docs\\programming_languages.rst\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"src\\shaders\\displacement.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\displacement_buffer.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\pbr_views.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"doc\\docs\\displacement.md\">\n      <Filter>2. Docs</Filter>\n    </None>\n    <None Include=\"src\\shaders\\macro_variation.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"src\\shaders\\projection.glsl\">\n      <Filter>4. Shaders</Filter>\n    </None>\n    <None Include=\"doc\\build_docs.sh\">\n      <Filter>1. Project Files</Filter>\n    </None>\n    <None Include=\"project\\addons\\terrain_3d\\extras\\shaders\\ocean_shader.gdshader\">\n      <Filter>4. Shaders</Filter>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\".readthedocs.yaml\">\n      <Filter>1. Project Files</Filter>\n    </Text>\n    <Text Include=\"LICENSE.txt\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\building_from_source.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\import_export.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\occlusion_culling.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\shader_design.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\system_architecture.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\texture_prep.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\tips_technical.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\troubleshooting.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\installation.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\controlmap_format.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\getting_help.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\_static\\theme_overrides.css\">\n      <Filter>1. Project Files</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\navigation.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"doc\\docs\\tutorial_videos.md\">\n      <Filter>2. Docs</Filter>\n    </Text>\n    <Text Include=\"src\\shaders\\gpu_depth.glsl\">\n      <Filter>4. Shaders</Filter>\n    </Text>\n    <Text Include=\"doc\\requirements.txt\">\n      <Filter>1. Project Files</Filter>\n    </Text>\n  </ItemGroup>\n  <ItemGroup>\n    <Xml Include=\"doc\\doc_classes\\Terrain3D.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DAssets.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DData.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DEditor.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DInstancer.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DMaterial.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DMeshAsset.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DRegion.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DTextureAsset.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DUtil.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n    <Xml Include=\"doc\\doc_classes\\Terrain3DCollision.xml\">\n      <Filter>3. XML</Filter>\n    </Xml>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Terrain3D.vcxproj.user",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <ShowAllFiles>false</ShowAllFiles>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LocalDebuggerCommand>c:\\gd\\bin\\Godot_v4.5.1-stable_win64.exe</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>-e project.godot</LocalDebuggerCommandArguments>\n    <LocalDebuggerWorkingDirectory>project</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "doc/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\ninstall:\n\tpython -m venv .venv && \\\n\t. .venv/bin/activate && \\\n\tpip install -r requirements.txt && \\\n\techo \"Setup complete. Remember to run 'source .venv/bin/activate' to activate the virtual environment.\"\n\nserve:\n\tsource ./.venv/bin/activate && \\\n\tmake html && \\\n\tpython -m http.server --directory _build/html\n"
  },
  {
    "path": "doc/_static/theme_overrides.css",
    "content": "/* override table width restrictions */\n.wy-table-responsive table td, .wy-table-responsive table th {\n\twhite-space: normal;\n}\n\n.wy-table-responsive {\n\tmargin-bottom: 24px;\n\tmax-width: 100%;\n\toverflow: visible;\n}\n"
  },
  {
    "path": "doc/api/class_terrain3d.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3D.xml.\n\n.. _class_Terrain3D:\n\nTerrain3D\n=========\n\n**Inherits:** ``Node3D``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nTerrain3D is a high performance, editable terrain system for Godot 4. It provides a clipmap based terrain that supports terrains from 64x64m up to 65.5x65.5km with multiple LODs, 32 textures, and editor tools for importing or creating terrains.\n\nThis class handles mesh generation, and management of the whole system. See `System Architecture <https://terrain3d.readthedocs.io/en/stable/docs/system_architecture.html>`__ for design details.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`Terrain3DAssets<class_Terrain3DAssets>`               | :ref:`assets<class_Terrain3D_property_assets>`                                                 |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``Shader``                                                  | :ref:`buffer_shader_override<class_Terrain3D_property_buffer_shader_override>`                 |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`buffer_shader_override_enabled<class_Terrain3D_property_buffer_shader_override_enabled>` | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | RenderingServer.ShadowCastingSetting                        | :ref:`cast_shadows<class_Terrain3D_property_cast_shadows>`                                     | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``Node3D``                                                  | :ref:`clipmap_target<class_Terrain3D_property_clipmap_target>`                                 |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`Terrain3DCollision<class_Terrain3DCollision>`         | :ref:`collision<class_Terrain3D_property_collision>`                                           |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`collision_layer<class_Terrain3D_property_collision_layer>`                               | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`collision_mask<class_Terrain3D_property_collision_mask>`                                 | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` | :ref:`collision_mode<class_Terrain3D_property_collision_mode>`                                 | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`collision_priority<class_Terrain3D_property_collision_priority>`                         | ``1.0``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`collision_radius<class_Terrain3D_property_collision_radius>`                             | ``64``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`collision_shape_size<class_Terrain3D_property_collision_shape_size>`                     | ``16``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``Node3D``                                                  | :ref:`collision_target<class_Terrain3D_property_collision_target>`                             |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`cull_margin<class_Terrain3D_property_cull_margin>`                                       | ``0.0``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`Terrain3DData<class_Terrain3DData>`                   | :ref:`data<class_Terrain3D_property_data>`                                                     |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``String``                                                  | :ref:`data_directory<class_Terrain3D_property_data_directory>`                                 | ``\"\"``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`DebugLevel<enum_Terrain3D_DebugLevel>`                | :ref:`debug_level<class_Terrain3D_property_debug_level>`                                       | ``0``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`displacement_scale<class_Terrain3D_property_displacement_scale>`                         | ``1.0``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`displacement_sharpness<class_Terrain3D_property_displacement_sharpness>`                 | ``0.25``        |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`free_editor_textures<class_Terrain3D_property_free_editor_textures>`                     | ``true``        |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | GeometryInstance3D.GIMode                                   | :ref:`gi_mode<class_Terrain3D_property_gi_mode>`                                               | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`Terrain3DInstancer<class_Terrain3DInstancer>`         | :ref:`instancer<class_Terrain3D_property_instancer>`                                           |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` | :ref:`instancer_mode<class_Terrain3D_property_instancer_mode>`                                 | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`label_distance<class_Terrain3D_property_label_distance>`                                 | ``0.0``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`label_size<class_Terrain3D_property_label_size>`                                         | ``48``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`Terrain3DMaterial<class_Terrain3DMaterial>`           | :ref:`material<class_Terrain3D_property_material>`                                             |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`mesh_lods<class_Terrain3D_property_mesh_lods>`                                           | ``7``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`mesh_size<class_Terrain3D_property_mesh_size>`                                           | ``48``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`mouse_layer<class_Terrain3D_property_mouse_layer>`                                       | ``32``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | RenderingServer.ShadowCastingSetting                        | :ref:`ocean_cast_shadows<class_Terrain3D_property_ocean_cast_shadows>`                         | ``0``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`ocean_cull_margin<class_Terrain3D_property_ocean_cull_margin>`                           | ``20.0``        |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`ocean_enabled<class_Terrain3D_property_ocean_enabled>`                                   | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | GeometryInstance3D.GIMode                                   | :ref:`ocean_gi_mode<class_Terrain3D_property_ocean_gi_mode>`                                   | ``0``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``Node3D``                                                  | :ref:`ocean_light_target<class_Terrain3D_property_ocean_light_target>`                         |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``Material``                                                | :ref:`ocean_material<class_Terrain3D_property_ocean_material>`                                 |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`ocean_mesh_lods<class_Terrain3D_property_ocean_mesh_lods>`                               | ``7``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`ocean_mesh_size<class_Terrain3D_property_ocean_mesh_size>`                               | ``32``          |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`ocean_render_layers<class_Terrain3D_property_ocean_render_layers>`                       | ``1``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`ocean_tessellation_level<class_Terrain3D_property_ocean_tessellation_level>`             | ``0``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`ocean_vertex_spacing<class_Terrain3D_property_ocean_vertex_spacing>`                     | ``4.0``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``PhysicsMaterial``                                         | :ref:`physics_material<class_Terrain3D_property_physics_material>`                             |                 |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | :ref:`RegionSize<enum_Terrain3D_RegionSize>`                | :ref:`region_size<class_Terrain3D_property_region_size>`                                       | ``256``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`render_layers<class_Terrain3D_property_render_layers>`                                   | ``2147483649``  |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`save_16_bit<class_Terrain3D_property_save_16_bit>`                                       | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_autoshader<class_Terrain3D_property_show_autoshader>`                               | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_checkered<class_Terrain3D_property_show_checkered>`                                 | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_colormap<class_Terrain3D_property_show_colormap>`                                   | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_contours<class_Terrain3D_property_show_contours>`                                   | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_control_angle<class_Terrain3D_property_show_control_angle>`                         | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_control_blend<class_Terrain3D_property_show_control_blend>`                         | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_control_scale<class_Terrain3D_property_show_control_scale>`                         | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_control_texture<class_Terrain3D_property_show_control_texture>`                     | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_displacement_buffer<class_Terrain3D_property_show_displacement_buffer>`             | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_grey<class_Terrain3D_property_show_grey>`                                           | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_grid<class_Terrain3D_property_show_grid>`                                           | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_heightmap<class_Terrain3D_property_show_heightmap>`                                 | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_instancer_grid<class_Terrain3D_property_show_instancer_grid>`                       | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_jaggedness<class_Terrain3D_property_show_jaggedness>`                               | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_navigation<class_Terrain3D_property_show_navigation>`                               | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_region_grid<class_Terrain3D_property_show_region_grid>`                             | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_roughmap<class_Terrain3D_property_show_roughmap>`                                   | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_texture_albedo<class_Terrain3D_property_show_texture_albedo>`                       | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_texture_ao<class_Terrain3D_property_show_texture_ao>`                               | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_texture_height<class_Terrain3D_property_show_texture_height>`                       | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_texture_normal<class_Terrain3D_property_show_texture_normal>`                       | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_texture_rough<class_Terrain3D_property_show_texture_rough>`                         | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``bool``                                                    | :ref:`show_vertex_grid<class_Terrain3D_property_show_vertex_grid>`                             | ``false``       |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``int``                                                     | :ref:`tessellation_level<class_Terrain3D_property_tessellation_level>`                         | ``0``           |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``String``                                                  | :ref:`version<class_Terrain3D_property_version>`                                               | ``\"1.1.0-dev\"`` |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n   | ``float``                                                   | :ref:`vertex_spacing<class_Terrain3D_property_vertex_spacing>`                                 | ``1.0``         |\n   +-------------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Mesh``                                      | :ref:`bake_mesh<class_Terrain3D_method_bake_mesh>`\\ (\\ lod\\: ``int``, filter\\: :ref:`HeightFilter<enum_Terrain3DData_HeightFilter>` = 0\\ ) |const|                                                            |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedVector3Array``                        | :ref:`generate_nav_mesh_source_geometry<class_Terrain3D_method_generate_nav_mesh_source_geometry>`\\ (\\ global_aabb\\: ``AABB``, require_nav\\: ``bool`` = true\\ ) |const|                                       |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Camera3D``                                  | :ref:`get_camera<class_Terrain3D_method_get_camera>`\\ (\\ ) |const|                                                                                                                                            |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector3``                                   | :ref:`get_clipmap_target_position<class_Terrain3D_method_get_clipmap_target_position>`\\ (\\ ) |const|                                                                                                          |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector3``                                   | :ref:`get_collision_target_position<class_Terrain3D_method_get_collision_target_position>`\\ (\\ ) |const|                                                                                                      |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DEditor<class_Terrain3DEditor>` | :ref:`get_editor<class_Terrain3D_method_get_editor>`\\ (\\ ) |const|                                                                                                                                            |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector3``                                   | :ref:`get_intersection<class_Terrain3D_method_get_intersection>`\\ (\\ src_pos\\: ``Vector3``, direction\\: ``Vector3``, gpu_mode\\: ``bool`` = false\\ )                                                           |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Object``                                    | :ref:`get_plugin<class_Terrain3D_method_get_plugin>`\\ (\\ ) |const|                                                                                                                                            |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Dictionary``                                | :ref:`get_raycast_result<class_Terrain3D_method_get_raycast_result>`\\ (\\ src_pos\\: ``Vector3``, direction\\: ``Vector3``, collision_mask\\: ``int`` = 4294967295, exclude_terrain\\: ``bool`` = false\\ ) |const| |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`set_camera<class_Terrain3D_method_set_camera>`\\ (\\ camera\\: ``Camera3D``\\ )                                                                                                                             |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`set_editor<class_Terrain3D_method_set_editor>`\\ (\\ editor\\: :ref:`Terrain3DEditor<class_Terrain3DEditor>`\\ )                                                                                            |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`set_plugin<class_Terrain3D_method_set_plugin>`\\ (\\ plugin\\: ``Object``\\ )                                                                                                                               |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`snap<class_Terrain3D_method_snap>`\\ (\\ )                                                                                                                                                                |\n   +-----------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nSignals\n-------\n\n.. _class_Terrain3D_signal_assets_changed:\n\n.. rst-class:: classref-signal\n\n**assets_changed**\\ (\\ ) :ref:`🔗<class_Terrain3D_signal_assets_changed>`\n\nEmitted when :ref:`assets<class_Terrain3D_property_assets>` is changed.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_signal_material_changed:\n\n.. rst-class:: classref-signal\n\n**material_changed**\\ (\\ ) :ref:`🔗<class_Terrain3D_signal_material_changed>`\n\nEmitted when :ref:`material<class_Terrain3D_property_material>` is changed.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3D_DebugLevel:\n\n.. rst-class:: classref-enumeration\n\nenum **DebugLevel**: :ref:`🔗<enum_Terrain3D_DebugLevel>`\n\n.. _class_Terrain3D_constant_ERROR:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`DebugLevel<enum_Terrain3D_DebugLevel>` **ERROR** = ``0``\n\nErrors and warnings always print.\n\n.. _class_Terrain3D_constant_INFO:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`DebugLevel<enum_Terrain3D_DebugLevel>` **INFO** = ``1``\n\nTypically every function call and other important informational messages.\n\n.. _class_Terrain3D_constant_DEBUG:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`DebugLevel<enum_Terrain3D_DebugLevel>` **DEBUG** = ``2``\n\nDetailed steps within functions.\n\n.. _class_Terrain3D_constant_EXTREME:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`DebugLevel<enum_Terrain3D_DebugLevel>` **EXTREME** = ``3``\n\nMessages for continuous operations like snapping and editing.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _enum_Terrain3D_RegionSize:\n\n.. rst-class:: classref-enumeration\n\nenum **RegionSize**: :ref:`🔗<enum_Terrain3D_RegionSize>`\n\n.. _class_Terrain3D_constant_SIZE_64:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **SIZE_64** = ``64``\n\nThe region size is 64 x 64 meters, vertices, and pixels on Image maps.\n\n.. _class_Terrain3D_constant_SIZE_128:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **SIZE_128** = ``128``\n\nThe region size is 128 x 128 meters, vertices, and pixels on Image maps.\n\n.. _class_Terrain3D_constant_SIZE_256:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **SIZE_256** = ``256``\n\nThe region size is 256 x 256 meters, vertices, and pixels on Image maps. (default)\n\n.. _class_Terrain3D_constant_SIZE_512:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **SIZE_512** = ``512``\n\nThe region size is 512 x 512 meters, vertices, and pixels on Image maps.\n\n.. _class_Terrain3D_constant_SIZE_1024:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **SIZE_1024** = ``1024``\n\nThe region size is 1024 x 1024 meters, vertices, and pixels on Image maps.\n\n.. _class_Terrain3D_constant_SIZE_2048:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **SIZE_2048** = ``2048``\n\nThe region size is 2048 x 2048 meters, vertices, and pixels on Image maps.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3D_property_assets:\n\n.. rst-class:: classref-property\n\n:ref:`Terrain3DAssets<class_Terrain3DAssets>` **assets** :ref:`🔗<class_Terrain3D_property_assets>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_assets**\\ (\\ value\\: :ref:`Terrain3DAssets<class_Terrain3DAssets>`\\ )\n- :ref:`Terrain3DAssets<class_Terrain3DAssets>` **get_assets**\\ (\\ )\n\nThe list of texture and mesh assets used by Terrain3D. You can optionally save this as an external ``.tres`` text file if you wish to share it with Terrain3D nodes in other scenes.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_buffer_shader_override:\n\n.. rst-class:: classref-property\n\n``Shader`` **buffer_shader_override** :ref:`🔗<class_Terrain3D_property_buffer_shader_override>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_buffer_shader_override**\\ (\\ value\\: ``Shader``\\ )\n- ``Shader`` **get_buffer_shader_override**\\ (\\ )\n\n.. container:: contribute\n\n\tThere is currently no description for this property. Please help us by `contributing one <https://contributing.godotengine.org/en/latest/documentation/class_reference.html>`__!\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_buffer_shader_override_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **buffer_shader_override_enabled** = ``false`` :ref:`🔗<class_Terrain3D_property_buffer_shader_override_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_buffer_shader_override_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_buffer_shader_override_enabled**\\ (\\ )\n\n.. container:: contribute\n\n\tThere is currently no description for this property. Please help us by `contributing one <https://contributing.godotengine.org/en/latest/documentation/class_reference.html>`__!\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_cast_shadows:\n\n.. rst-class:: classref-property\n\nRenderingServer.ShadowCastingSetting **cast_shadows** = ``1`` :ref:`🔗<class_Terrain3D_property_cast_shadows>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_cast_shadows**\\ (\\ value\\: RenderingServer.ShadowCastingSetting\\ )\n- RenderingServer.ShadowCastingSetting **get_cast_shadows**\\ (\\ )\n\nTells the renderer how to cast shadows from the terrain onto other objects. This sets ``GeometryInstance3D.ShadowCastingSetting`` in the engine.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_clipmap_target:\n\n.. rst-class:: classref-property\n\n``Node3D`` **clipmap_target** :ref:`🔗<class_Terrain3D_property_clipmap_target>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_clipmap_target**\\ (\\ value\\: ``Node3D``\\ )\n- ``Node3D`` **get_clipmap_target**\\ (\\ )\n\nThe terrain clipmap mesh and lods will center itself at the position of this node. If null, or if in the editor, it will fall back to the camera position. See :ref:`set_camera()<class_Terrain3D_method_set_camera>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision:\n\n.. rst-class:: classref-property\n\n:ref:`Terrain3DCollision<class_Terrain3DCollision>` **collision** :ref:`🔗<class_Terrain3D_property_collision>`\n\n.. rst-class:: classref-property-setget\n\n- :ref:`Terrain3DCollision<class_Terrain3DCollision>` **get_collision**\\ (\\ )\n\nThe active :ref:`Terrain3DCollision<class_Terrain3DCollision>` object.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_layer:\n\n.. rst-class:: classref-property\n\n``int`` **collision_layer** = ``1`` :ref:`🔗<class_Terrain3D_property_collision_layer>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_layer**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_collision_layer**\\ (\\ )\n\nThe physics layers the terrain lives on. Sets ``CollisionObject3D.collision_layer``.\n\nAlias for :ref:`Terrain3DCollision.layer<class_Terrain3DCollision_property_layer>`.\n\nAlso see :ref:`collision_mask<class_Terrain3D_property_collision_mask>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_mask:\n\n.. rst-class:: classref-property\n\n``int`` **collision_mask** = ``1`` :ref:`🔗<class_Terrain3D_property_collision_mask>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_mask**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_collision_mask**\\ (\\ )\n\nThe physics layers the physics body scans for colliding objects. Sets ``CollisionObject3D.collision_mask``.\n\nAlias for :ref:`Terrain3DCollision.mask<class_Terrain3DCollision_property_mask>`.\n\nAlso see :ref:`collision_layer<class_Terrain3D_property_collision_layer>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_mode:\n\n.. rst-class:: classref-property\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **collision_mode** = ``1`` :ref:`🔗<class_Terrain3D_property_collision_mode>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_mode**\\ (\\ value\\: :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>`\\ )\n- :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **get_collision_mode**\\ (\\ )\n\nThe selected mode determines if collision is generated and how. See :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` for details.\n\nAlias for :ref:`Terrain3DCollision.mode<class_Terrain3DCollision_property_mode>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_priority:\n\n.. rst-class:: classref-property\n\n``float`` **collision_priority** = ``1.0`` :ref:`🔗<class_Terrain3D_property_collision_priority>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_priority**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_collision_priority**\\ (\\ )\n\nThe priority with which the physics server uses to solve collisions. The higher the priority, the lower the penetration of a colliding object. Sets ``CollisionObject3D.collision_priority``.\n\nAlias for :ref:`Terrain3DCollision.priority<class_Terrain3DCollision_property_priority>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_radius:\n\n.. rst-class:: classref-property\n\n``int`` **collision_radius** = ``64`` :ref:`🔗<class_Terrain3D_property_collision_radius>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_radius**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_collision_radius**\\ (\\ )\n\nIf :ref:`collision_mode<class_Terrain3D_property_collision_mode>` is Dynamic, this is the distance range within which collision shapes will be generated.\n\nAlias for :ref:`Terrain3DCollision.radius<class_Terrain3DCollision_property_radius>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_shape_size:\n\n.. rst-class:: classref-property\n\n``int`` **collision_shape_size** = ``16`` :ref:`🔗<class_Terrain3D_property_collision_shape_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_shape_size**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_collision_shape_size**\\ (\\ )\n\nIf :ref:`collision_mode<class_Terrain3D_property_collision_mode>` is Dynamic, this is the size of each collision shape.\n\nAlias for :ref:`Terrain3DCollision.shape_size<class_Terrain3DCollision_property_shape_size>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_collision_target:\n\n.. rst-class:: classref-property\n\n``Node3D`` **collision_target** :ref:`🔗<class_Terrain3D_property_collision_target>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_collision_target**\\ (\\ value\\: ``Node3D``\\ )\n- ``Node3D`` **get_collision_target**\\ (\\ )\n\nIn dynamic mode, the terrain collision will center itself at the position of this node. If null, it will fall back to the :ref:`clipmap_target<class_Terrain3D_property_clipmap_target>` position and failing that will use the camera position. The camera is always used in the editor. See :ref:`set_camera()<class_Terrain3D_method_set_camera>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_cull_margin:\n\n.. rst-class:: classref-property\n\n``float`` **cull_margin** = ``0.0`` :ref:`🔗<class_Terrain3D_property_cull_margin>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_cull_margin**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_cull_margin**\\ (\\ )\n\nThis margin is added to the vertical component of the terrain mesh bounding boxes (AABB). The terrain already sets its AABB from :ref:`Terrain3DData.get_height_range()<class_Terrain3DData_method_get_height_range>`, which is calculated while sculpting. This setting only needs to be used if the shader has expanded the terrain beyond the AABB and the terrain meshes are being culled at certain viewing angles. This might happen from using :ref:`Terrain3DMaterial.world_background<class_Terrain3DMaterial_property_world_background>` with NOISE and a height value larger than the terrain heights. This setting is similar to ``GeometryInstance3D.extra_cull_margin``, but it only affects the Y axis.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_data:\n\n.. rst-class:: classref-property\n\n:ref:`Terrain3DData<class_Terrain3DData>` **data** :ref:`🔗<class_Terrain3D_property_data>`\n\n.. rst-class:: classref-property-setget\n\n- :ref:`Terrain3DData<class_Terrain3DData>` **get_data**\\ (\\ )\n\nThis class manages loading, saving, adding, and removing of Terrain3DRegions and access to their content.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_data_directory:\n\n.. rst-class:: classref-property\n\n``String`` **data_directory** = ``\"\"`` :ref:`🔗<class_Terrain3D_property_data_directory>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_data_directory**\\ (\\ value\\: ``String``\\ )\n- ``String`` **get_data_directory**\\ (\\ )\n\nThe directory where terrain data will be saved to and loaded from.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_debug_level:\n\n.. rst-class:: classref-property\n\n:ref:`DebugLevel<enum_Terrain3D_DebugLevel>` **debug_level** = ``0`` :ref:`🔗<class_Terrain3D_property_debug_level>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_debug_level**\\ (\\ value\\: :ref:`DebugLevel<enum_Terrain3D_DebugLevel>`\\ )\n- :ref:`DebugLevel<enum_Terrain3D_DebugLevel>` **get_debug_level**\\ (\\ )\n\nThe verbosity of debug messages printed to the console. Errors and warnings are always printed. This can also be set via command line using ``--terrain3d-debug=LEVEL`` where ``LEVEL`` is one of ``ERROR, INFO, DEBUG, EXTREME``. The last includes continuously recurring messages like position updates for the mesh as the camera moves around.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_displacement_scale:\n\n.. rst-class:: classref-property\n\n``float`` **displacement_scale** = ``1.0`` :ref:`🔗<class_Terrain3D_property_displacement_scale>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_displacement_scale**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_displacement_scale**\\ (\\ )\n\nA global multiplier for all displaced textures. This is the maximum distance that 2 adjacent verticies can be vertically seperated by. Setting this 1.0 would mean a maximum of + 0.5m, and -0.5m deviation from the collision mesh.\n\nAlias for :ref:`Terrain3DMaterial.displacement_scale<class_Terrain3DMaterial_property_displacement_scale>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_displacement_sharpness:\n\n.. rst-class:: classref-property\n\n``float`` **displacement_sharpness** = ``0.25`` :ref:`🔗<class_Terrain3D_property_displacement_sharpness>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_displacement_sharpness**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_displacement_sharpness**\\ (\\ )\n\nAdjusts the transition between textures. When set at `1.0`, the blending of displacment between textures will match the aldebo/normal blend sharpness exactly. Lower values will have a softer transition, avoiding harsh shapes, without compromising the abldeo and normal blend sharpness. If set at or very near to `0.0`, it is possible that more displaced textures can affect less displaced textures at low blend values even if not visible.\n\nAlias for :ref:`Terrain3DMaterial.displacement_sharpness<class_Terrain3DMaterial_property_displacement_sharpness>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_free_editor_textures:\n\n.. rst-class:: classref-property\n\n``bool`` **free_editor_textures** = ``true`` :ref:`🔗<class_Terrain3D_property_free_editor_textures>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_free_editor_textures**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_free_editor_textures**\\ (\\ )\n\nFrees ground textures used for editing in _ready(). These textures are used to generate the TextureArrays, so if you don't change any :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>` settings in game, this can be enabled. Also reloads the texture asset list in _enter_tree() in case you load scenes via code and need the textures again. Calls :ref:`Terrain3DAssets.clear_textures()<class_Terrain3DAssets_method_clear_textures>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_gi_mode:\n\n.. rst-class:: classref-property\n\nGeometryInstance3D.GIMode **gi_mode** = ``1`` :ref:`🔗<class_Terrain3D_property_gi_mode>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_gi_mode**\\ (\\ value\\: GeometryInstance3D.GIMode\\ )\n- GeometryInstance3D.GIMode **get_gi_mode**\\ (\\ )\n\nTells the renderer which global illumination mode to use for the terrain mesh. This sets ``GeometryInstance3D.gi_mode`` in the engine.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_instancer:\n\n.. rst-class:: classref-property\n\n:ref:`Terrain3DInstancer<class_Terrain3DInstancer>` **instancer** :ref:`🔗<class_Terrain3D_property_instancer>`\n\n.. rst-class:: classref-property-setget\n\n- :ref:`Terrain3DInstancer<class_Terrain3DInstancer>` **get_instancer**\\ (\\ )\n\nThe active :ref:`Terrain3DInstancer<class_Terrain3DInstancer>` object.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_instancer_mode:\n\n.. rst-class:: classref-property\n\n:ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` **instancer_mode** = ``1`` :ref:`🔗<class_Terrain3D_property_instancer_mode>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_instancer_mode**\\ (\\ value\\: :ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>`\\ )\n- :ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` **get_instancer_mode**\\ (\\ )\n\nNormal - Generates MultiMeshInstance3Ds and renders all instances as normal.\n\nDisabled - prevents the instancer from creating any MultiMeshInstance3Ds.\n\nAlias for :ref:`Terrain3DInstancer.mode<class_Terrain3DInstancer_property_mode>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_label_distance:\n\n.. rst-class:: classref-property\n\n``float`` **label_distance** = ``0.0`` :ref:`🔗<class_Terrain3D_property_label_distance>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_label_distance**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_label_distance**\\ (\\ )\n\nIf label_distance is non-zero (try 1024-4096) it will generate and display region coordinates in the viewport so you can identify the exact region files you are editing. This setting is the visible distance of the labels.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_label_size:\n\n.. rst-class:: classref-property\n\n``int`` **label_size** = ``48`` :ref:`🔗<class_Terrain3D_property_label_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_label_size**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_label_size**\\ (\\ )\n\nSets the font size for region labels. See :ref:`label_distance<class_Terrain3D_property_label_distance>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_material:\n\n.. rst-class:: classref-property\n\n:ref:`Terrain3DMaterial<class_Terrain3DMaterial>` **material** :ref:`🔗<class_Terrain3D_property_material>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_material**\\ (\\ value\\: :ref:`Terrain3DMaterial<class_Terrain3DMaterial>`\\ )\n- :ref:`Terrain3DMaterial<class_Terrain3DMaterial>` **get_material**\\ (\\ )\n\nA custom material for Terrain3D. You can optionally save this as an external ``.tres`` text file if you wish to share it with instances of Terrain3D in other scenes. See :ref:`Terrain3DMaterial<class_Terrain3DMaterial>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_mesh_lods:\n\n.. rst-class:: classref-property\n\n``int`` **mesh_lods** = ``7`` :ref:`🔗<class_Terrain3D_property_mesh_lods>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mesh_lods**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_mesh_lods**\\ (\\ )\n\nThe number of lods generated for the terrain meshes. Enable wireframe mode in the viewport to see them.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_mesh_size:\n\n.. rst-class:: classref-property\n\n``int`` **mesh_size** = ``48`` :ref:`🔗<class_Terrain3D_property_mesh_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mesh_size**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_mesh_size**\\ (\\ )\n\nThe correlated size of the terrain meshes. Lod0 has ``4*mesh_size + 2`` quads per side. E.g. when mesh_size=8, lod0 has 34 quads to a side, including 2 quads for seams.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_mouse_layer:\n\n.. rst-class:: classref-property\n\n``int`` **mouse_layer** = ``32`` :ref:`🔗<class_Terrain3D_property_mouse_layer>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mouse_layer**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_mouse_layer**\\ (\\ )\n\nGodot supports 32 render layers. For most objects, only layers 1-20 are available for selection in the inspector. 21-32 are settable via code, and are considered reserved for editor plugins.\n\nThis variable sets the editor render layer (21-32) to be used by ``get_intersection``, which the mouse cursor uses.\n\nYou may place other objects on this layer, however ``get_intersection`` will report intersections with them. So either dedicate this layer to Terrain3D, or if you must use all 32 layers, dedicate this one during editing or when using ``get_intersection``, and then you can use it during game play.\n\nSee :ref:`get_intersection()<class_Terrain3D_method_get_intersection>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_cast_shadows:\n\n.. rst-class:: classref-property\n\nRenderingServer.ShadowCastingSetting **ocean_cast_shadows** = ``0`` :ref:`🔗<class_Terrain3D_property_ocean_cast_shadows>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_cast_shadows**\\ (\\ value\\: RenderingServer.ShadowCastingSetting\\ )\n- RenderingServer.ShadowCastingSetting **get_ocean_cast_shadows**\\ (\\ )\n\nTells the renderer how to cast shadows from the ocean onto other objects. This sets ``GeometryInstance3D.ShadowCastingSetting`` in the engine.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_cull_margin:\n\n.. rst-class:: classref-property\n\n``float`` **ocean_cull_margin** = ``20.0`` :ref:`🔗<class_Terrain3D_property_ocean_cull_margin>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_cull_margin**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_ocean_cull_margin**\\ (\\ )\n\nThis margin is added to the vertical component of the ocean mesh bounding boxes (AABB). When you set the height of your waves in the shader, this margin should be adjusted. If it's too small, the meshes will clip at certain camera angles. If you set it too large, the renderer may have slightly more work to do. This setting is similar to ``GeometryInstance3D.extra_cull_margin``, but it only affects the Y axis.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **ocean_enabled** = ``false`` :ref:`🔗<class_Terrain3D_property_ocean_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_ocean_enabled**\\ (\\ )\n\nGenerates another clipmap mesh, which you can apply an ocean shader to and configure independently of the terrain mesh.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_gi_mode:\n\n.. rst-class:: classref-property\n\nGeometryInstance3D.GIMode **ocean_gi_mode** = ``0`` :ref:`🔗<class_Terrain3D_property_ocean_gi_mode>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_gi_mode**\\ (\\ value\\: GeometryInstance3D.GIMode\\ )\n- GeometryInstance3D.GIMode **get_ocean_gi_mode**\\ (\\ )\n\nTells the renderer which global illumination mode to use for the ocean mesh. This sets ``GeometryInstance3D.gi_mode`` in the engine.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_light_target:\n\n.. rst-class:: classref-property\n\n``Node3D`` **ocean_light_target** :ref:`🔗<class_Terrain3D_property_ocean_light_target>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_light_target**\\ (\\ value\\: ``Node3D``\\ )\n- ``Node3D`` **get_ocean_light_target**\\ (\\ )\n\nThis sets the _light_direction and _light_color uniforms in the ocean shader, if they are present. You can use this for light scattering, detecting if the light is above the horizon, albedo coloring, etc.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_material:\n\n.. rst-class:: classref-property\n\n``Material`` **ocean_material** :ref:`🔗<class_Terrain3D_property_ocean_material>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_material**\\ (\\ value\\: ``Material``\\ )\n- ``Material`` **get_ocean_material**\\ (\\ )\n\nYou can assign a ``StandardMaterial`` here for testing, but you really need a ``ShaderMaterial``. Start with the example in ``addons/terrain_3d/extras/shaders/M_ocean.tres``, which you can build on. Or use any of the ocean shaders available around the internet, provided you set `skip_vertex_transform` and copy the geomorphing code from our `vertex()` shader, which will properly handle the LOD transitions on the clipmap.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_mesh_lods:\n\n.. rst-class:: classref-property\n\n``int`` **ocean_mesh_lods** = ``7`` :ref:`🔗<class_Terrain3D_property_ocean_mesh_lods>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_mesh_lods**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_ocean_mesh_lods**\\ (\\ )\n\nThe number of lods generated for the ocean meshes. Enable wireframe mode in the viewport to see them.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_mesh_size:\n\n.. rst-class:: classref-property\n\n``int`` **ocean_mesh_size** = ``32`` :ref:`🔗<class_Terrain3D_property_ocean_mesh_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_mesh_size**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_ocean_mesh_size**\\ (\\ )\n\nThe correlated size of the ocean meshes. Lod0 has ``4*ocean_mesh_size + 2`` quads per side. E.g. when ocean_mesh_size=8, lod0 has 34 quads to a side, including 2 quads for seams.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_render_layers:\n\n.. rst-class:: classref-property\n\n``int`` **ocean_render_layers** = ``1`` :ref:`🔗<class_Terrain3D_property_ocean_render_layers>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_render_layers**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_ocean_render_layers**\\ (\\ )\n\nThe render layers the ocean is drawn on. This sets ``VisualInstance3D.layers`` in the engine.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_tessellation_level:\n\n.. rst-class:: classref-property\n\n``int`` **ocean_tessellation_level** = ``0`` :ref:`🔗<class_Terrain3D_property_ocean_tessellation_level>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_tessellation_level**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_ocean_tessellation_level**\\ (\\ )\n\nThis setting creates up to 6 additional subdivisions of the ocean mesh below LOD0, which provides more vertices for the vertex shader if desired. You can see it in wireframe mode.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_ocean_vertex_spacing:\n\n.. rst-class:: classref-property\n\n``float`` **ocean_vertex_spacing** = ``4.0`` :ref:`🔗<class_Terrain3D_property_ocean_vertex_spacing>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ocean_vertex_spacing**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_ocean_vertex_spacing**\\ (\\ )\n\nThe distance between vertices, settable up to 100. Godot units are typically considered to be meters. This laterally scales the ocean vertices on X and Z axes, but does not scale wave height.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_physics_material:\n\n.. rst-class:: classref-property\n\n``PhysicsMaterial`` **physics_material** :ref:`🔗<class_Terrain3D_property_physics_material>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_physics_material**\\ (\\ value\\: ``PhysicsMaterial``\\ )\n- ``PhysicsMaterial`` **get_physics_material**\\ (\\ )\n\nApplies a ``PhysicsMaterial`` override to the entire terrain StaticBody.\n\nAlias for :ref:`Terrain3DCollision.physics_material<class_Terrain3DCollision_property_physics_material>` See that entry for details.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_region_size:\n\n.. rst-class:: classref-property\n\n:ref:`RegionSize<enum_Terrain3D_RegionSize>` **region_size** = ``256`` :ref:`🔗<class_Terrain3D_property_region_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **change_region_size**\\ (\\ value\\: :ref:`RegionSize<enum_Terrain3D_RegionSize>`\\ )\n- :ref:`RegionSize<enum_Terrain3D_RegionSize>` **get_region_size**\\ (\\ )\n\nThe number of vertices in each region, and the number of pixels for each map in :ref:`Terrain3DRegion<class_Terrain3DRegion>`. 1 pixel always corresponds to 1 vertex. :ref:`vertex_spacing<class_Terrain3D_property_vertex_spacing>` laterally scales regions, but does not change the number of vertices or pixels in each.\n\nThere is no undo for this operation. However you can apply it again to reslice, as long as your data doesn't hit the maximum boundaries.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_render_layers:\n\n.. rst-class:: classref-property\n\n``int`` **render_layers** = ``2147483649`` :ref:`🔗<class_Terrain3D_property_render_layers>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_render_layers**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_render_layers**\\ (\\ )\n\nThe render layers the terrain is drawn on. This sets ``VisualInstance3D.layers`` in the engine. The defaults is layer 1 and 32 (for the mouse cursor). When you set this via code, make sure the layer for :ref:`mouse_layer<class_Terrain3D_property_mouse_layer>` is included, or set that variable again after this so that the mouse cursor and :ref:`get_intersection()<class_Terrain3D_method_get_intersection>` work.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_save_16_bit:\n\n.. rst-class:: classref-property\n\n``bool`` **save_16_bit** = ``false`` :ref:`🔗<class_Terrain3D_property_save_16_bit>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_save_16_bit**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_save_16_bit**\\ (\\ )\n\nIf enabled, heightmaps are saved as 16-bit half-precision to reduce file size. Files are always loaded in 32-bit for editing. Upon save, a copy of the heightmap is converted to 16-bit for writing. It does not change what is currently in memory.\n\nThis process is lossy. 16-bit precision gets increasingly worse with every power of 2. At a height of 256m, the precision interval is .25m. At 512m it is .5m. At 1024m it is 1m. Saving a height of 1024.4m will be rounded down to 1024m.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_autoshader:\n\n.. rst-class:: classref-property\n\n``bool`` **show_autoshader** = ``false`` :ref:`🔗<class_Terrain3D_property_show_autoshader>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_autoshader**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_autoshader**\\ (\\ )\n\nDisplays the area designated for use by the autoshader, which shows materials based upon slope.\n\nAlias for :ref:`Terrain3DMaterial.show_autoshader<class_Terrain3DMaterial_property_show_autoshader>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_checkered:\n\n.. rst-class:: classref-property\n\n``bool`` **show_checkered** = ``false`` :ref:`🔗<class_Terrain3D_property_show_checkered>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_checkered**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_checkered**\\ (\\ )\n\nShows a checkerboard display using a shader rendered pattern. This is turned on if the Texture List is empty.\n\nNote that when a blank texture slot is created, a 1k checkerboard texture is generated and stored in the texture slot. That takes VRAM. The two patterns have a slightly different scale.\n\nAlias for :ref:`Terrain3DMaterial.show_checkered<class_Terrain3DMaterial_property_show_checkered>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_colormap:\n\n.. rst-class:: classref-property\n\n``bool`` **show_colormap** = ``false`` :ref:`🔗<class_Terrain3D_property_show_colormap>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_colormap**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_colormap**\\ (\\ )\n\nShows the color map in the albedo channel.\n\nAlias for :ref:`Terrain3DMaterial.show_colormap<class_Terrain3DMaterial_property_show_colormap>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_contours:\n\n.. rst-class:: classref-property\n\n``bool`` **show_contours** = ``false`` :ref:`🔗<class_Terrain3D_property_show_contours>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_contours**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_contours**\\ (\\ )\n\nOverlays contour lines on the terrain. Customize the options in the material when enabled. Press `4` with the mouse in the viewport to toggle.\n\nAlias for :ref:`Terrain3DMaterial.show_contours<class_Terrain3DMaterial_property_show_contours>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_control_angle:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_angle** = ``false`` :ref:`🔗<class_Terrain3D_property_show_control_angle>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_angle**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_angle**\\ (\\ )\n\nAlbedo shows the painted angle. Orange means 0°, Yellow 270°, Cyan 180°, Violet 90°. Or warm colors towards -Z, cool colors +Z, greens/yellows +X, reds/blues -X. Draw all angles coming from the center of a circle for a better understanding.\n\nAlias for :ref:`Terrain3DMaterial.show_control_angle<class_Terrain3DMaterial_property_show_control_angle>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_control_blend:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_blend** = ``false`` :ref:`🔗<class_Terrain3D_property_show_control_blend>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_blend**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_blend**\\ (\\ )\n\nDisplays the values used to blend the textures. Blue shows the autoshader blending, red shows manually painted blending.\n\nAlias for :ref:`Terrain3DMaterial.show_control_blend<class_Terrain3DMaterial_property_show_control_blend>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_control_scale:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_scale** = ``false`` :ref:`🔗<class_Terrain3D_property_show_control_scale>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_scale**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_scale**\\ (\\ )\n\nAlbedo shows the painted scale. Larger scales are more red, smaller scales are more blue. 0.5 middle grey is the default 100% scale.\n\nAlias for :ref:`Terrain3DMaterial.show_control_scale<class_Terrain3DMaterial_property_show_control_scale>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_control_texture:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_texture** = ``false`` :ref:`🔗<class_Terrain3D_property_show_control_texture>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_texture**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_texture**\\ (\\ )\n\nAlbedo shows the base and overlay texture indices defined by the control map. Red pixels indicate the base texture, with brightness showing texture ids 0 to 31. Green pixels indicate the overlay texture. Yellow indicates both.\n\nAlias for :ref:`Terrain3DMaterial.show_control_texture<class_Terrain3DMaterial_property_show_control_texture>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_displacement_buffer:\n\n.. rst-class:: classref-property\n\n``bool`` **show_displacement_buffer** = ``false`` :ref:`🔗<class_Terrain3D_property_show_displacement_buffer>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_displacement_buffer**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_displacement_buffer**\\ (\\ )\n\nAlias for :ref:`Terrain3DMaterial.show_displacement_buffer<class_Terrain3DMaterial_property_show_displacement_buffer>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_grey:\n\n.. rst-class:: classref-property\n\n``bool`` **show_grey** = ``false`` :ref:`🔗<class_Terrain3D_property_show_grey>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_grey**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_grey**\\ (\\ )\n\nAlbedo is set to 0.2 grey.\n\nAlias for :ref:`Terrain3DMaterial.show_grey<class_Terrain3DMaterial_property_show_grey>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_grid** = ``false`` :ref:`🔗<class_Terrain3D_property_show_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_region_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_region_grid**\\ (\\ )\n\nAlias for :ref:`Terrain3DMaterial.show_region_grid<class_Terrain3DMaterial_property_show_region_grid>`. Press `1` with the mouse in the viewport to toggle.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_heightmap:\n\n.. rst-class:: classref-property\n\n``bool`` **show_heightmap** = ``false`` :ref:`🔗<class_Terrain3D_property_show_heightmap>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_heightmap**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_heightmap**\\ (\\ )\n\nAlbedo is a white to black gradient depending on height. The gradient is scaled to a height of 300, so above that or far below 0 will be all white or black.\n\nAlias for :ref:`Terrain3DMaterial.show_heightmap<class_Terrain3DMaterial_property_show_heightmap>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_instancer_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_instancer_grid** = ``false`` :ref:`🔗<class_Terrain3D_property_show_instancer_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_instancer_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_instancer_grid**\\ (\\ )\n\nOverlays the 32x32m instancer grid on the terrain, which shows how the instancer data is partitioned. Press `2` with the mouse in the viewport to toggle.\n\nAlias for :ref:`Terrain3DMaterial.show_instancer_grid<class_Terrain3DMaterial_property_show_instancer_grid>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_jaggedness:\n\n.. rst-class:: classref-property\n\n``bool`` **show_jaggedness** = ``false`` :ref:`🔗<class_Terrain3D_property_show_jaggedness>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_jaggedness**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_jaggedness**\\ (\\ )\n\nHighlights non-smooth areas of the terrain. Jagged peaks, troughs, or edges that are a bit rough with sharp angles between vertices.\n\nAlias for :ref:`Terrain3DMaterial.show_jaggedness<class_Terrain3DMaterial_property_show_jaggedness>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_navigation:\n\n.. rst-class:: classref-property\n\n``bool`` **show_navigation** = ``false`` :ref:`🔗<class_Terrain3D_property_show_navigation>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_navigation**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_navigation**\\ (\\ )\n\nDisplays the area designated for generating the navigation mesh.\n\nAlias for :ref:`Terrain3DMaterial.show_navigation<class_Terrain3DMaterial_property_show_navigation>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_region_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_region_grid** = ``false`` :ref:`🔗<class_Terrain3D_property_show_region_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_region_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_region_grid**\\ (\\ )\n\nOverlays the region grid on the terrain. This is more accurate than the region grid gizmo for determining where the region border is when editing. Press `1` with the mouse in the viewport to toggle.\n\nAlias for :ref:`Terrain3DMaterial.show_region_grid<class_Terrain3DMaterial_property_show_region_grid>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_roughmap:\n\n.. rst-class:: classref-property\n\n``bool`` **show_roughmap** = ``false`` :ref:`🔗<class_Terrain3D_property_show_roughmap>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_roughmap**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_roughmap**\\ (\\ )\n\nAlbedo is set to the roughness modification map as grey scale. Middle grey, 0.5 means no roughness modification. Black would be high gloss while white is very rough.\n\nAlias for :ref:`Terrain3DMaterial.show_roughmap<class_Terrain3DMaterial_property_show_roughmap>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_texture_albedo:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_albedo** = ``false`` :ref:`🔗<class_Terrain3D_property_show_texture_albedo>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_albedo**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_albedo**\\ (\\ )\n\nAlbedo textures are shown only. Other channels are excluded.\n\nAlias for :ref:`Terrain3DMaterial.show_texture_albedo<class_Terrain3DMaterial_property_show_texture_albedo>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_texture_ao:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_ao** = ``false`` :ref:`🔗<class_Terrain3D_property_show_texture_ao>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_ao**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_ao**\\ (\\ )\n\nAlbedo is set to the painted Ambient Occlusion textures.\n\nAlias for :ref:`Terrain3DMaterial.show_texture_ao<class_Terrain3DMaterial_property_show_texture_ao>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_texture_height:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_height** = ``false`` :ref:`🔗<class_Terrain3D_property_show_texture_height>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_height**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_height**\\ (\\ )\n\nAlbedo is set to the painted Height textures.\n\nAlias for :ref:`Terrain3DMaterial.show_texture_height<class_Terrain3DMaterial_property_show_texture_height>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_texture_normal:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_normal** = ``false`` :ref:`🔗<class_Terrain3D_property_show_texture_normal>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_normal**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_normal**\\ (\\ )\n\nAlbedo is set to the painted Normal textures.\n\nAlias for :ref:`Terrain3DMaterial.show_texture_normal<class_Terrain3DMaterial_property_show_texture_normal>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_texture_rough:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_rough** = ``false`` :ref:`🔗<class_Terrain3D_property_show_texture_rough>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_rough**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_rough**\\ (\\ )\n\nAlbedo is set to the painted Roughness textures. This is different from the roughness modification map above.\n\nAlias for :ref:`Terrain3DMaterial.show_texture_rough<class_Terrain3DMaterial_property_show_texture_rough>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_show_vertex_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_vertex_grid** = ``false`` :ref:`🔗<class_Terrain3D_property_show_vertex_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_vertex_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_vertex_grid**\\ (\\ )\n\nOverlays the vertex grid on the terrain, showing where each vertex is. Press `3` with the mouse in the viewport to toggle.\n\nAlias for :ref:`Terrain3DMaterial.show_vertex_grid<class_Terrain3DMaterial_property_show_vertex_grid>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_tessellation_level:\n\n.. rst-class:: classref-property\n\n``int`` **tessellation_level** = ``0`` :ref:`🔗<class_Terrain3D_property_tessellation_level>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_tessellation_level**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_tessellation_level**\\ (\\ )\n\nEnables displacement using texture heights for additional mesh detail when set above 0. This creates up to 6 additional subdivisions of the terrain mesh below LOD0, and adds a displacement buffer configurable in the material. You can see this in wireframe mode. Set to 0 to disable displacement. See :ref:`Terrain3DMaterial.show_displacement_buffer<class_Terrain3DMaterial_property_show_displacement_buffer>` and look at the Displacement Buffer debug view.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_version:\n\n.. rst-class:: classref-property\n\n``String`` **version** = ``\"1.1.0-dev\"`` :ref:`🔗<class_Terrain3D_property_version>`\n\n.. rst-class:: classref-property-setget\n\n- ``String`` **get_version**\\ (\\ )\n\nThe current version of Terrain3D.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_property_vertex_spacing:\n\n.. rst-class:: classref-property\n\n``float`` **vertex_spacing** = ``1.0`` :ref:`🔗<class_Terrain3D_property_vertex_spacing>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_vertex_spacing**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_vertex_spacing**\\ (\\ )\n\nThe distance between vertices, settable up to 100. Godot units are typically considered to be meters. This laterally scales the terrain on X and Z axes.\n\nThis variable changes the global position of landscape features. A mountain peak might be at (512, 512), but with a vertex spacing of 2.0 it is now located at (1024, 1024). It retains the same height.\n\nAll Terrain3D functions with a global_position expect an absolute global value. If you would normally use :ref:`Terrain3DData.import_images()<class_Terrain3DData_method_import_images>` to import an image in the region at (-1024, -1024), with a vertex_spacing of 2, you'll need to import that image at (-2048, -2048) to place it in the same region.\n\nTo scale heights, export the height map and reimport it with a new height scale.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3D_method_bake_mesh:\n\n.. rst-class:: classref-method\n\n``Mesh`` **bake_mesh**\\ (\\ lod\\: ``int``, filter\\: :ref:`HeightFilter<enum_Terrain3DData_HeightFilter>` = 0\\ ) |const| :ref:`🔗<class_Terrain3D_method_bake_mesh>`\n\nGenerates a static ArrayMesh for the terrain.\n\n\\ ``lod`` - Determines the granularity of the generated mesh. The range is 0-8. 4 is recommended.\n\n\\ ``filter`` - Controls how vertex Y coordinates are generated from the height map. See :ref:`HeightFilter<enum_Terrain3DData_HeightFilter>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_generate_nav_mesh_source_geometry:\n\n.. rst-class:: classref-method\n\n``PackedVector3Array`` **generate_nav_mesh_source_geometry**\\ (\\ global_aabb\\: ``AABB``, require_nav\\: ``bool`` = true\\ ) |const| :ref:`🔗<class_Terrain3D_method_generate_nav_mesh_source_geometry>`\n\nGenerates source geometry faces for input to nav mesh baking. Geometry is only generated where there are no holes and the terrain has been painted as navigable.\n\n\\ ``global_aabb`` - If non-empty, geometry will be generated only within this AABB. If empty, geometry will be generated for the entire terrain.\n\n\\ ``require_nav`` - If true, this function will only generate geometry for terrain marked navigable. Otherwise, geometry is generated for the entire terrain within the AABB (which can be useful for dynamic and/or runtime nav mesh baking).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_camera:\n\n.. rst-class:: classref-method\n\n``Camera3D`` **get_camera**\\ (\\ ) |const| :ref:`🔗<class_Terrain3D_method_get_camera>`\n\nReturns the camera the terrain is currently tracking for position, if not overridden by :ref:`clipmap_target<class_Terrain3D_property_clipmap_target>`. See :ref:`set_camera()<class_Terrain3D_method_set_camera>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_clipmap_target_position:\n\n.. rst-class:: classref-method\n\n``Vector3`` **get_clipmap_target_position**\\ (\\ ) |const| :ref:`🔗<class_Terrain3D_method_get_clipmap_target_position>`\n\nReturns the position on which the terrain mesh is centered, which may be the camera or a target node. See :ref:`clipmap_target<class_Terrain3D_property_clipmap_target>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_collision_target_position:\n\n.. rst-class:: classref-method\n\n``Vector3`` **get_collision_target_position**\\ (\\ ) |const| :ref:`🔗<class_Terrain3D_method_get_collision_target_position>`\n\nReturns the position on which the terrain collision is centered, which may be the camera or a target node. See :ref:`collision_target<class_Terrain3D_property_collision_target>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_editor:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DEditor<class_Terrain3DEditor>` **get_editor**\\ (\\ ) |const| :ref:`🔗<class_Terrain3D_method_get_editor>`\n\nReturns the current Terrain3DEditor instance, if it has been set.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_intersection:\n\n.. rst-class:: classref-method\n\n``Vector3`` **get_intersection**\\ (\\ src_pos\\: ``Vector3``, direction\\: ``Vector3``, gpu_mode\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3D_method_get_intersection>`\n\nCasts a ray from ``src_pos`` pointing towards ``direction``, attempting to intersect the terrain. This operation is does not use physics and is not a typical raycast, so enabling collision is unnecessary. This function likely won't work if src_pos is below the terrain.\n\n\n\nThis function can operate in one of two modes selected by ``gpu_mode``:\n\n- If gpu_mode is disabled (default), it raymarches from src_pos until the terrain is intersected, up to 4000m away. This works with one function call, and can only intersect the terrain where regions exist. It is slower than gpu_mode and gets increasingly slower the farther away the terrain is, though you may not notice.\n\n\n\n- If gpu_mode is enabled, it uses the GPU to detect the mouse. This works wherever the terrain is visible, even outside of regions, but may need to be called twice.\n\n\n\nGPU mode places a camera at the specified point and \"looks\" at the terrain. It uses the depth texture to determine how far away the intersection point is. It requires the use of an editor render layer, (default 32, set with :ref:`mouse_layer<class_Terrain3D_property_mouse_layer>`) while using this function.\n\n\n\nThe main caveats of using this mode is that the call to get_intersection() requests a viewport be drawn, but cannot wait for it to finish as there is no \"await\" in C++ and no force draw function in Godot. So the return value is one frame behind, and invalid on the first call. This also means the function cannot be used more than once per frame. This mode works well when used continuously, once per frame, where one frame of difference won't matter. The editor uses this mode to place the mouse cursor decal.\n\n\n\nThis mode can also be used by your plugins and games, such as a space ship firing lasers at the terrain and causing an explosion at the hit point. However if the calls aren't continuous, eg driven by the mouse, you'll need to call once to capture the viewport image, wait for it to be drawn, then call again to get the result:\n\n::\n\n    var target_point = terrain.get_intersection(camera_pos, camera_dir, true)\n    await RenderingServer.frame_post_draw\n    target_point = terrain.get_intersection(camera_pos, camera_dir, true)\n\n\n\nPossible return values:\n\n- If the terrain is hit, the intersection point is returned.\n\n- If there is no intersection, eg. the ray points towards the sky, it returns the maximum double float value ``Vector3(3.402823466e+38F,...)``. You can check this case with this code: ``if point.z > 3.4e38:``\\ \n\n- On error, it returns ``Vector3(NAN, NAN, NAN)`` and prints a message to the console.\n\n\n\nAlso see :ref:`get_raycast_result()<class_Terrain3D_method_get_raycast_result>` and :ref:`Terrain3DData.get_height()<class_Terrain3DData_method_get_height>` for alternative functions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_plugin:\n\n.. rst-class:: classref-method\n\n``Object`` **get_plugin**\\ (\\ ) |const| :ref:`🔗<class_Terrain3D_method_get_plugin>`\n\nReturns the EditorPlugin Object connected to Terrain3D.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_get_raycast_result:\n\n.. rst-class:: classref-method\n\n``Dictionary`` **get_raycast_result**\\ (\\ src_pos\\: ``Vector3``, direction\\: ``Vector3``, collision_mask\\: ``int`` = 4294967295, exclude_terrain\\: ``bool`` = false\\ ) |const| :ref:`🔗<class_Terrain3D_method_get_raycast_result>`\n\nThis is a helper function that creates a general physics-based raycast and returns the resulting dictionary; it's not limited to terrain use. Raycasts can only detect collision. It is used by our editor using the `on_collision` option to instance on non-terrain meshes.\n\nDirection is added to src_pos and includes magnitude. So to run a raycast from (100, 100, 100) to the ground 100m below, direction would be (0, -110, 0) with margin.\n\nCollision_mask has the physics layers the query will detect as a bitmask. By default, all collision layers are detected.\n\nSee `PhysicsDirectSpaceState3D.intersect_ray <https://docs.godotengine.org/en/stable/classes/class_physicsdirectspacestate3d.html#class-physicsdirectspacestate3d-method-intersect-ray>`__ for how to interpret the resulting dictionary.\n\nAlso see :ref:`get_intersection()<class_Terrain3D_method_get_intersection>` and :ref:`Terrain3DData.get_height()<class_Terrain3DData_method_get_height>` for alternative functions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_set_camera:\n\n.. rst-class:: classref-method\n\n|void| **set_camera**\\ (\\ camera\\: ``Camera3D``\\ ) :ref:`🔗<class_Terrain3D_method_set_camera>`\n\nSpecifies the camera on which the terrain centers. It attempts to aquire the camera from the active viewport.\n\nIf the camera is instanced in a sub scene or by code, Terrain3D might not be able to find it, will issue an error, and the terrain will center at (0,0,0) causing LODs to not update until a trackable node is set.\n\nEither specify the camera, or specify the clipmap and/or collision targets. It will use the targets first and fall back to the camera if they are null.\n\nSee :ref:`clipmap_target<class_Terrain3D_property_clipmap_target>` and :ref:`collision_target<class_Terrain3D_property_collision_target>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_set_editor:\n\n.. rst-class:: classref-method\n\n|void| **set_editor**\\ (\\ editor\\: :ref:`Terrain3DEditor<class_Terrain3DEditor>`\\ ) :ref:`🔗<class_Terrain3D_method_set_editor>`\n\nSets the current Terrain3DEditor instance.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_set_plugin:\n\n.. rst-class:: classref-method\n\n|void| **set_plugin**\\ (\\ plugin\\: ``Object``\\ ) :ref:`🔗<class_Terrain3D_method_set_plugin>`\n\nSets the EditorPlugin Object connected to Terrain3D.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3D_method_snap:\n\n.. rst-class:: classref-method\n\n|void| **snap**\\ (\\ ) :ref:`🔗<class_Terrain3D_method_snap>`\n\nQueues the terrain mesh and collision to snap their positions to the target nodes on the next physics frame. Typically this only happens if the targets have moved sufficiently far. See :ref:`clipmap_target<class_Terrain3D_property_clipmap_target>` and :ref:`collision_target<class_Terrain3D_property_collision_target>`.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dassets.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DAssets.xml.\n\n.. _class_Terrain3DAssets:\n\nTerrain3DAssets\n===============\n\n**Inherits:** ``Resource``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class contains arrays of :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>` and :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` resources. It is a savable resource, so you can save it to disk and use the same asset list in multiple scenes that use Terrain3D. The amount of data is small, so it can be saved as a git-friendly, text based .tres file or left within the scene file.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +----------------------------------------------------------------------------------------+------------------------------------------------------------------+--------+\n   | :ref:`Array<class_Array>`\\[:ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`\\]       | :ref:`mesh_list<class_Terrain3DAssets_property_mesh_list>`       | ``[]`` |\n   +----------------------------------------------------------------------------------------+------------------------------------------------------------------+--------+\n   | :ref:`Array<class_Array>`\\[:ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`\\] | :ref:`texture_list<class_Terrain3DAssets_property_texture_list>` | ``[]`` |\n   +----------------------------------------------------------------------------------------+------------------------------------------------------------------+--------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                    | :ref:`clear_textures<class_Terrain3DAssets_method_clear_textures>`\\ (\\ update\\: ``bool`` = false\\ )                                                                             |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                    | :ref:`create_mesh_thumbnails<class_Terrain3DAssets_method_create_mesh_thumbnails>`\\ (\\ id\\: ``int`` = -1, size\\: ``Vector2i`` = Vector2i(512, 512), force\\: ``bool`` = false\\ ) |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``                                                   | :ref:`get_albedo_array_rid<class_Terrain3DAssets_method_get_albedo_array_rid>`\\ (\\ ) |const|                                                                                    |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`       | :ref:`get_mesh_asset<class_Terrain3DAssets_method_get_mesh_asset>`\\ (\\ id\\: ``int``\\ ) |const|                                                                                  |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                   | :ref:`get_mesh_count<class_Terrain3DAssets_method_get_mesh_count>`\\ (\\ ) |const|                                                                                                |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``                                                   | :ref:`get_normal_array_rid<class_Terrain3DAssets_method_get_normal_array_rid>`\\ (\\ ) |const|                                                                                    |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedFloat32Array``                                    | :ref:`get_texture_ao_light_affects<class_Terrain3DAssets_method_get_texture_ao_light_affects>`\\ (\\ ) |const|                                                                    |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedFloat32Array``                                    | :ref:`get_texture_ao_strengths<class_Terrain3DAssets_method_get_texture_ao_strengths>`\\ (\\ ) |const|                                                                            |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>` | :ref:`get_texture_asset<class_Terrain3DAssets_method_get_texture_asset>`\\ (\\ id\\: ``int``\\ ) |const|                                                                            |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedColorArray``                                      | :ref:`get_texture_colors<class_Terrain3DAssets_method_get_texture_colors>`\\ (\\ ) |const|                                                                                        |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                   | :ref:`get_texture_count<class_Terrain3DAssets_method_get_texture_count>`\\ (\\ ) |const|                                                                                          |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedVector2Array``                                    | :ref:`get_texture_detiles<class_Terrain3DAssets_method_get_texture_detiles>`\\ (\\ ) |const|                                                                                      |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedVector2Array``                                    | :ref:`get_texture_displacements<class_Terrain3DAssets_method_get_texture_displacements>`\\ (\\ ) |const|                                                                          |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedFloat32Array``                                    | :ref:`get_texture_normal_depths<class_Terrain3DAssets_method_get_texture_normal_depths>`\\ (\\ ) |const|                                                                          |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedFloat32Array``                                    | :ref:`get_texture_roughness_mods<class_Terrain3DAssets_method_get_texture_roughness_mods>`\\ (\\ ) |const|                                                                        |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedFloat32Array``                                    | :ref:`get_texture_uv_scales<class_Terrain3DAssets_method_get_texture_uv_scales>`\\ (\\ ) |const|                                                                                  |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | Error                                                     | :ref:`save<class_Terrain3DAssets_method_save>`\\ (\\ path\\: ``String`` = \"\"\\ )                                                                                                    |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                    | :ref:`set_mesh_asset<class_Terrain3DAssets_method_set_mesh_asset>`\\ (\\ id\\: ``int``, mesh\\: :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`\\ )                              |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                    | :ref:`set_texture_asset<class_Terrain3DAssets_method_set_texture_asset>`\\ (\\ id\\: ``int``, texture\\: :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`\\ )               |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                    | :ref:`update_mesh_list<class_Terrain3DAssets_method_update_mesh_list>`\\ (\\ )                                                                                                    |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                    | :ref:`update_texture_list<class_Terrain3DAssets_method_update_texture_list>`\\ (\\ )                                                                                              |\n   +-----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nSignals\n-------\n\n.. _class_Terrain3DAssets_signal_meshes_changed:\n\n.. rst-class:: classref-signal\n\n**meshes_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DAssets_signal_meshes_changed>`\n\nEmitted when the mesh list is updated, which happens as a result of a :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` changing.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_signal_textures_changed:\n\n.. rst-class:: classref-signal\n\n**textures_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DAssets_signal_textures_changed>`\n\nEmitted when this list is updated due to changes in the texture slots, or the files or settings of any :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DAssets_AssetType:\n\n.. rst-class:: classref-enumeration\n\nenum **AssetType**: :ref:`🔗<enum_Terrain3DAssets_AssetType>`\n\n.. _class_Terrain3DAssets_constant_TYPE_TEXTURE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`AssetType<enum_Terrain3DAssets_AssetType>` **TYPE_TEXTURE** = ``0``\n\nAsset is type Terrain3DTextureAsset.\n\n.. _class_Terrain3DAssets_constant_TYPE_MESH:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`AssetType<enum_Terrain3DAssets_AssetType>` **TYPE_MESH** = ``1``\n\nAsset is type Terrain3DMeshAsset.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nConstants\n---------\n\n.. _class_Terrain3DAssets_constant_MAX_TEXTURES:\n\n.. rst-class:: classref-constant\n\n**MAX_TEXTURES** = ``32`` :ref:`🔗<class_Terrain3DAssets_constant_MAX_TEXTURES>`\n\nHard coded maximum number of textures, with IDs in the range of 0-31. Cannot easily be expanded.\n\n.. _class_Terrain3DAssets_constant_MAX_MESHES:\n\n.. rst-class:: classref-constant\n\n**MAX_MESHES** = ``256`` :ref:`🔗<class_Terrain3DAssets_constant_MAX_MESHES>`\n\nLimit of the maximum number of meshes. Arbitrary, easily expanded.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DAssets_property_mesh_list:\n\n.. rst-class:: classref-property\n\n:ref:`Array<class_Array>`\\[:ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`\\] **mesh_list** = ``[]`` :ref:`🔗<class_Terrain3DAssets_property_mesh_list>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mesh_list**\\ (\\ value\\: :ref:`Array<class_Array>`\\[:ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`\\]\\ )\n- :ref:`Array<class_Array>`\\[:ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`\\] **get_mesh_list**\\ (\\ )\n\nThe list of mesh assets.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_property_texture_list:\n\n.. rst-class:: classref-property\n\n:ref:`Array<class_Array>`\\[:ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`\\] **texture_list** = ``[]`` :ref:`🔗<class_Terrain3DAssets_property_texture_list>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_texture_list**\\ (\\ value\\: :ref:`Array<class_Array>`\\[:ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`\\]\\ )\n- :ref:`Array<class_Array>`\\[:ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`\\] **get_texture_list**\\ (\\ )\n\nThe list of texture assets.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DAssets_method_clear_textures:\n\n.. rst-class:: classref-method\n\n|void| **clear_textures**\\ (\\ update\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DAssets_method_clear_textures>`\n\nAfter textures are loaded, they are combined into a TextureArray. The originals remain in VRAM and are only used if the :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>` settings are changed and regenerating the TextureArrays are necessary. Use this function to clear the originals if not needed. It removes all textures from the asset list, freeing them if they are not referenced by other objects.\n\nUpdate will regenerate the texture arrays housing the textures drawn on the terrain. This will remove all textures and turn the terrain checkerboard.\n\nA similar ``clear_meshes`` is less useful so hasn't been included. However you can do the same thing with ``get_mesh_list().clear()``.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_create_mesh_thumbnails:\n\n.. rst-class:: classref-method\n\n|void| **create_mesh_thumbnails**\\ (\\ id\\: ``int`` = -1, size\\: ``Vector2i`` = Vector2i(512, 512), force\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DAssets_method_create_mesh_thumbnails>`\n\nGenerates mesh asset preview thumbnails for the asset dock, stored within each mesh asset. Specify id -1 to generate all. By default, mesh thumbnails are not recreated if they already exist. Specify ``force`` to regenerate existing thumbnails.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_albedo_array_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_albedo_array_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_albedo_array_rid>`\n\nReturns the resource ID of the TextureArray generated from combining all albedo and height textures.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_mesh_asset:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` **get_mesh_asset**\\ (\\ id\\: ``int``\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_mesh_asset>`\n\nReturns the specified Terrain3DMeshAsset resource.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_mesh_count:\n\n.. rst-class:: classref-method\n\n``int`` **get_mesh_count**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_mesh_count>`\n\nReturns the number of mesh assets in the list.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_normal_array_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_normal_array_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_normal_array_rid>`\n\nReturns the resource ID of the TextureArray generated from combining all normal and roughness textures.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_ao_light_affects:\n\n.. rst-class:: classref-method\n\n``PackedFloat32Array`` **get_texture_ao_light_affects**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_ao_light_affects>`\n\nReturns the array of ao light affects values each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_ao_strengths:\n\n.. rst-class:: classref-method\n\n``PackedFloat32Array`` **get_texture_ao_strengths**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_ao_strengths>`\n\nReturns the array of AO strengths for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_asset:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>` **get_texture_asset**\\ (\\ id\\: ``int``\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_asset>`\n\nReturns the Terrain3DTextureAsset with the specified ID.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_colors:\n\n.. rst-class:: classref-method\n\n``PackedColorArray`` **get_texture_colors**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_colors>`\n\nReturns the array of albedo tints for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_count:\n\n.. rst-class:: classref-method\n\n``int`` **get_texture_count**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_count>`\n\nReturns the number of texture slots used.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_detiles:\n\n.. rst-class:: classref-method\n\n``PackedVector2Array`` **get_texture_detiles**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_detiles>`\n\nReturns the array of detiling values for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_displacements:\n\n.. rst-class:: classref-method\n\n``PackedVector2Array`` **get_texture_displacements**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_displacements>`\n\nReturns the array of displacement offset and scale values for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_normal_depths:\n\n.. rst-class:: classref-method\n\n``PackedFloat32Array`` **get_texture_normal_depths**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_normal_depths>`\n\nReturns the array of normal strengths for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_roughness_mods:\n\n.. rst-class:: classref-method\n\n``PackedFloat32Array`` **get_texture_roughness_mods**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_roughness_mods>`\n\nReturns the array of roughness modification values for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_get_texture_uv_scales:\n\n.. rst-class:: classref-method\n\n``PackedFloat32Array`` **get_texture_uv_scales**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DAssets_method_get_texture_uv_scales>`\n\nReturns the array of uv scale values for each texture asset, indexed by asset id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_save:\n\n.. rst-class:: classref-method\n\nError **save**\\ (\\ path\\: ``String`` = \"\"\\ ) :ref:`🔗<class_Terrain3DAssets_method_save>`\n\nSaves this texture list resource to disk, if saved as an external ``.tres`` or ``.res`` resource file.\n\npath - specifies a directory and file name to use from now on.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_set_mesh_asset:\n\n.. rst-class:: classref-method\n\n|void| **set_mesh_asset**\\ (\\ id\\: ``int``, mesh\\: :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>`\\ ) :ref:`🔗<class_Terrain3DAssets_method_set_mesh_asset>`\n\nAssigns the Terrain3DMeshAsset to the specified ID slot. It can be null to clear the slot. See :ref:`set_texture_asset()<class_Terrain3DAssets_method_set_texture_asset>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_set_texture_asset:\n\n.. rst-class:: classref-method\n\n|void| **set_texture_asset**\\ (\\ id\\: ``int``, texture\\: :ref:`Terrain3DTextureAsset<class_Terrain3DTextureAsset>`\\ ) :ref:`🔗<class_Terrain3DAssets_method_set_texture_asset>`\n\nAdds a Terrain3DTextureAsset at the specified ID slot. The texture can be null to clear the slot, or remove it if its the last in the list. If the specified slot is full, it will be swapped with the source texture ID, or will find the next available ID.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_update_mesh_list:\n\n.. rst-class:: classref-method\n\n|void| **update_mesh_list**\\ (\\ ) :ref:`🔗<class_Terrain3DAssets_method_update_mesh_list>`\n\nUpdates the internal list of meshes used by the instancer.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DAssets_method_update_texture_list:\n\n.. rst-class:: classref-method\n\n|void| **update_texture_list**\\ (\\ ) :ref:`🔗<class_Terrain3DAssets_method_update_texture_list>`\n\nRegenerates the texture arrays from the list of texture assets, which is sent to the shader.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dcollision.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DCollision.xml.\n\n.. _class_Terrain3DCollision:\n\nTerrain3DCollision\n==================\n\n**Inherits:** ``Object``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class manages collision.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | ``int``                                                     | :ref:`layer<class_Terrain3DCollision_property_layer>`                       | ``1``   |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | ``int``                                                     | :ref:`mask<class_Terrain3DCollision_property_mask>`                         | ``1``   |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` | :ref:`mode<class_Terrain3DCollision_property_mode>`                         | ``1``   |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | ``PhysicsMaterial``                                         | :ref:`physics_material<class_Terrain3DCollision_property_physics_material>` |         |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | ``float``                                                   | :ref:`priority<class_Terrain3DCollision_property_priority>`                 | ``1.0`` |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | ``int``                                                     | :ref:`radius<class_Terrain3DCollision_property_radius>`                     | ``64``  |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n   | ``int``                                                     | :ref:`shape_size<class_Terrain3DCollision_property_shape_size>`             | ``16``  |\n   +-------------------------------------------------------------+-----------------------------------------------------------------------------+---------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`build<class_Terrain3DCollision_method_build>`\\ (\\ )                                                                                                  |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`destroy<class_Terrain3DCollision_method_destroy>`\\ (\\ )                                                                                              |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``  | :ref:`get_rid<class_Terrain3DCollision_method_get_rid>`\\ (\\ ) |const|                                                                                      |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool`` | :ref:`is_dynamic_mode<class_Terrain3DCollision_method_is_dynamic_mode>`\\ (\\ ) |const|                                                                      |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool`` | :ref:`is_editor_mode<class_Terrain3DCollision_method_is_editor_mode>`\\ (\\ ) |const|                                                                        |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool`` | :ref:`is_enabled<class_Terrain3DCollision_method_is_enabled>`\\ (\\ ) |const|                                                                                |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`update<class_Terrain3DCollision_method_update>`\\ (\\ region_location\\: ``Vector2i`` = Vector2i(2147483647, 2147483647), rebuild\\: ``bool`` = false\\ ) |\n   +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DCollision_CollisionMode:\n\n.. rst-class:: classref-enumeration\n\nenum **CollisionMode**: :ref:`🔗<enum_Terrain3DCollision_CollisionMode>`\n\n.. _class_Terrain3DCollision_constant_DISABLED:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **DISABLED** = ``0``\n\nNo collision shapes will be generated.\n\n.. _class_Terrain3DCollision_constant_DYNAMIC_GAME:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **DYNAMIC_GAME** = ``1``\n\nCollision shapes are generated around the camera as it moves; in game only.\n\n.. _class_Terrain3DCollision_constant_DYNAMIC_EDITOR:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **DYNAMIC_EDITOR** = ``2``\n\nCollision shapes are generated around the camera as it moves; in the editor and in game. Enable ``View Gizmos`` in the viewport menu to see them.\n\n.. _class_Terrain3DCollision_constant_FULL_GAME:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **FULL_GAME** = ``3``\n\nCollision shapes are generated for all regions in game only. One shape per region.\n\n.. _class_Terrain3DCollision_constant_FULL_EDITOR:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **FULL_EDITOR** = ``4``\n\nCollision shapes are generated for all regions in the editor and in game. One shape per region. This mode is necessary for some 3rd party plugins to detect the terrain using collision. Enable ``View Gizmos`` in the viewport menu to see the collision mesh.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DCollision_property_layer:\n\n.. rst-class:: classref-property\n\n``int`` **layer** = ``1`` :ref:`🔗<class_Terrain3DCollision_property_layer>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_layer**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_layer**\\ (\\ )\n\nThe physics layers the terrain lives on. Sets ``CollisionObject3D.collision_layer``. Also see :ref:`mask<class_Terrain3DCollision_property_mask>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_property_mask:\n\n.. rst-class:: classref-property\n\n``int`` **mask** = ``1`` :ref:`🔗<class_Terrain3DCollision_property_mask>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mask**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_mask**\\ (\\ )\n\nThe physics layers the physics body scans for colliding objects. Sets ``CollisionObject3D.collision_mask``. Also see :ref:`layer<class_Terrain3DCollision_property_layer>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_property_mode:\n\n.. rst-class:: classref-property\n\n:ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **mode** = ``1`` :ref:`🔗<class_Terrain3DCollision_property_mode>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mode**\\ (\\ value\\: :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>`\\ )\n- :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` **get_mode**\\ (\\ )\n\nThe selected mode determines if collision is generated and how. See :ref:`CollisionMode<enum_Terrain3DCollision_CollisionMode>` for details.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_property_physics_material:\n\n.. rst-class:: classref-property\n\n``PhysicsMaterial`` **physics_material** :ref:`🔗<class_Terrain3DCollision_property_physics_material>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_physics_material**\\ (\\ value\\: ``PhysicsMaterial``\\ )\n- ``PhysicsMaterial`` **get_physics_material**\\ (\\ )\n\nApplies a ``PhysicsMaterial`` override to the entire terrain StaticBody.\n\nThere's no ability built into Godot to change physics material parameters based on texture or any other factor. However, it might be possible to extend `PhysicsMaterial` in order to inject code into the queries. It would need references to an object position and a terrain, and then it could run :ref:`Terrain3DData.get_texture_id()<class_Terrain3DData_method_get_texture_id>` based on the position and return different physics settings per texture. That would change the settings for the entire terrain for that moment.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_property_priority:\n\n.. rst-class:: classref-property\n\n``float`` **priority** = ``1.0`` :ref:`🔗<class_Terrain3DCollision_property_priority>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_priority**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_priority**\\ (\\ )\n\nThe priority with which the physics server uses to solve collisions. The higher the priority, the lower the penetration of a colliding object. Sets ``CollisionObject3D.collision_priority``.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_property_radius:\n\n.. rst-class:: classref-property\n\n``int`` **radius** = ``64`` :ref:`🔗<class_Terrain3DCollision_property_radius>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_radius**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_radius**\\ (\\ )\n\nIf :ref:`mode<class_Terrain3DCollision_property_mode>` is Dynamic, this is the distance range within which collision shapes will be generated.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_property_shape_size:\n\n.. rst-class:: classref-property\n\n``int`` **shape_size** = ``16`` :ref:`🔗<class_Terrain3DCollision_property_shape_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_shape_size**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_shape_size**\\ (\\ )\n\nIf :ref:`mode<class_Terrain3DCollision_property_mode>` is Dynamic, this is the size of each collision shape.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DCollision_method_build:\n\n.. rst-class:: classref-method\n\n|void| **build**\\ (\\ ) :ref:`🔗<class_Terrain3DCollision_method_build>`\n\nCreates collision shapes and calls :ref:`update()<class_Terrain3DCollision_method_update>` to shape them. Calls :ref:`destroy()<class_Terrain3DCollision_method_destroy>` first, so it is safe to call this to fully rebuild collision any time.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_method_destroy:\n\n.. rst-class:: classref-method\n\n|void| **destroy**\\ (\\ ) :ref:`🔗<class_Terrain3DCollision_method_destroy>`\n\nRemoves all collision shapes and frees any memory used.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_method_get_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DCollision_method_get_rid>`\n\nReturns the RID of the active StaticBody.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_method_is_dynamic_mode:\n\n.. rst-class:: classref-method\n\n``bool`` **is_dynamic_mode**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DCollision_method_is_dynamic_mode>`\n\nReturns true if :ref:`mode<class_Terrain3DCollision_property_mode>` is ``Dynamic / Editor`` or ``Dynamic / Game``.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_method_is_editor_mode:\n\n.. rst-class:: classref-method\n\n``bool`` **is_editor_mode**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DCollision_method_is_editor_mode>`\n\nReturns true if :ref:`mode<class_Terrain3DCollision_property_mode>` is ``Full / Editor`` or ``Dynamic / Editor``.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_method_is_enabled:\n\n.. rst-class:: classref-method\n\n``bool`` **is_enabled**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DCollision_method_is_enabled>`\n\nReturns true if :ref:`mode<class_Terrain3DCollision_property_mode>` is not ``Disabled``.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DCollision_method_update:\n\n.. rst-class:: classref-method\n\n|void| **update**\\ (\\ region_location\\: ``Vector2i`` = Vector2i(2147483647, 2147483647), rebuild\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DCollision_method_update>`\n\n- If :ref:`mode<class_Terrain3DCollision_property_mode>` is Full, recalculates the specified or all existing collision shapes. Specify a ``region_location`` to update only that region, or omit it or use `Vector2i.MAX` to update all regions. If regions have been added or removed, set ``rebuild`` to true or call :ref:`build()<class_Terrain3DCollision_method_build>` instead. A full update can be slow.\n\n- If :ref:`mode<class_Terrain3DCollision_property_mode>` is Dynamic, repositions collision shapes around the camera and recalculates ones that moved. Set ``rebuild`` to true to recalculate all shapes within :ref:`radius<class_Terrain3DCollision_property_radius>`. ``region_location`` is ignored. This is very fast, and can be updated at 60fps for little cost.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3ddata.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DData.xml.\n\n.. _class_Terrain3DData:\n\nTerrain3DData\n=============\n\n**Inherits:** ``Object``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nTerrain3D divides all data into regions which fit on a grid in the world. These coordinates are called region locations. The map data are stored in instances of :ref:`Terrain3DRegion<class_Terrain3DRegion>`, which are saved to individual files. This class manages region loading, unloading, data retreival and manipulation.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +-------------------------------------------+------------------------------------------------------------------------+--------+\n   | :ref:`Array<class_Array>`\\[``Image``\\]    | :ref:`color_maps<class_Terrain3DData_property_color_maps>`             | ``[]`` |\n   +-------------------------------------------+------------------------------------------------------------------------+--------+\n   | :ref:`Array<class_Array>`\\[``Image``\\]    | :ref:`control_maps<class_Terrain3DData_property_control_maps>`         | ``[]`` |\n   +-------------------------------------------+------------------------------------------------------------------------+--------+\n   | :ref:`Array<class_Array>`\\[``Image``\\]    | :ref:`height_maps<class_Terrain3DData_property_height_maps>`           | ``[]`` |\n   +-------------------------------------------+------------------------------------------------------------------------+--------+\n   | :ref:`Array<class_Array>`\\[``Vector2i``\\] | :ref:`region_locations<class_Terrain3DData_property_region_locations>` | ``[]`` |\n   +-------------------------------------------+------------------------------------------------------------------------+--------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | Error                                                                      | :ref:`add_region<class_Terrain3DData_method_add_region>`\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, update\\: ``bool`` = true\\ )                                                                           |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DRegion<class_Terrain3DRegion>`                              | :ref:`add_region_blank<class_Terrain3DData_method_add_region_blank>`\\ (\\ region_location\\: ``Vector2i``, update\\: ``bool`` = true\\ )                                                                                       |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DRegion<class_Terrain3DRegion>`                              | :ref:`add_region_blankp<class_Terrain3DData_method_add_region_blankp>`\\ (\\ global_position\\: ``Vector3``, update\\: ``bool`` = true\\ )                                                                                      |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`calc_height_range<class_Terrain3DData_method_calc_height_range>`\\ (\\ recursive\\: ``bool`` = false\\ )                                                                                                                 |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`change_region_size<class_Terrain3DData_method_change_region_size>`\\ (\\ region_size\\: ``int``\\ )                                                                                                                      |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`do_for_regions<class_Terrain3DData_method_do_for_regions>`\\ (\\ area\\: ``Rect2i``, callback\\: ``Callable``\\ )                                                                                                         |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`dump<class_Terrain3DData_method_dump>`\\ (\\ verbose\\: ``bool`` = false\\ ) |const|                                                                                                                                     |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | Error                                                                      | :ref:`export_image<class_Terrain3DData_method_export_image>`\\ (\\ file_name\\: ``String``, map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const|                                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Color``                                                                  | :ref:`get_color<class_Terrain3DData_method_get_color>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                        |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``                                                                    | :ref:`get_color_maps_rid<class_Terrain3DData_method_get_color_maps_rid>`\\ (\\ ) |const|                                                                                                                                     |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_control<class_Terrain3DData_method_get_control>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                    |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``float``                                                                  | :ref:`get_control_angle<class_Terrain3DData_method_get_control_angle>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                        |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`get_control_auto<class_Terrain3DData_method_get_control_auto>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                          |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_control_base_id<class_Terrain3DData_method_get_control_base_id>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                    |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``float``                                                                  | :ref:`get_control_blend<class_Terrain3DData_method_get_control_blend>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                        |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`get_control_hole<class_Terrain3DData_method_get_control_hole>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                          |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``                                                                    | :ref:`get_control_maps_rid<class_Terrain3DData_method_get_control_maps_rid>`\\ (\\ ) |const|                                                                                                                                 |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`get_control_navigation<class_Terrain3DData_method_get_control_navigation>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                              |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_control_overlay_id<class_Terrain3DData_method_get_control_overlay_id>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                              |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``float``                                                                  | :ref:`get_control_scale<class_Terrain3DData_method_get_control_scale>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                        |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``float``                                                                  | :ref:`get_height<class_Terrain3DData_method_get_height>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                      |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``                                                                    | :ref:`get_height_maps_rid<class_Terrain3DData_method_get_height_maps_rid>`\\ (\\ ) |const|                                                                                                                                   |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector2``                                                                | :ref:`get_height_range<class_Terrain3DData_method_get_height_range>`\\ (\\ ) |const|                                                                                                                                         |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Array<class_Array>`\\[``Image``\\]                                     | :ref:`get_maps<class_Terrain3DData_method_get_maps>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const|                                                                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector3``                                                                | :ref:`get_mesh_vertex<class_Terrain3DData_method_get_mesh_vertex>`\\ (\\ lod\\: ``int``, filter\\: :ref:`HeightFilter<enum_Terrain3DData_HeightFilter>`, global_position\\: ``Vector3``\\ ) |const|                              |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector3``                                                                | :ref:`get_normal<class_Terrain3DData_method_get_normal>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                      |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Color``                                                                  | :ref:`get_pixel<class_Terrain3DData_method_get_pixel>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, global_position\\: ``Vector3``\\ ) |const|                                                               |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DRegion<class_Terrain3DRegion>`                              | :ref:`get_region<class_Terrain3DData_method_get_region>`\\ (\\ region_location\\: ``Vector2i``\\ ) |const|                                                                                                                     |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_region_count<class_Terrain3DData_method_get_region_count>`\\ (\\ ) |const|                                                                                                                                         |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_region_id<class_Terrain3DData_method_get_region_id>`\\ (\\ region_location\\: ``Vector2i``\\ ) |const|                                                                                                               |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_region_idp<class_Terrain3DData_method_get_region_idp>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                              |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector2i``                                                               | :ref:`get_region_location<class_Terrain3DData_method_get_region_location>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                    |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``PackedInt32Array``                                                       | :ref:`get_region_map<class_Terrain3DData_method_get_region_map>`\\ (\\ ) |const|                                                                                                                                             |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``                                                                    | :ref:`get_region_map_index<class_Terrain3DData_method_get_region_map_index>`\\ (\\ region_location\\: ``Vector2i``\\ ) |static|                                                                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DRegion<class_Terrain3DRegion>`                              | :ref:`get_regionp<class_Terrain3DData_method_get_regionp>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                    |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Array<class_Array>`\\[:ref:`Terrain3DRegion<class_Terrain3DRegion>`\\] | :ref:`get_regions_active<class_Terrain3DData_method_get_regions_active>`\\ (\\ copy\\: ``bool`` = false, deep\\: ``bool`` = false\\ ) |const|                                                                                   |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Dictionary``                                                             | :ref:`get_regions_all<class_Terrain3DData_method_get_regions_all>`\\ (\\ ) |const|                                                                                                                                           |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``float``                                                                  | :ref:`get_roughness<class_Terrain3DData_method_get_roughness>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector3``                                                                | :ref:`get_texture_id<class_Terrain3DData_method_get_texture_id>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                              |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`has_region<class_Terrain3DData_method_has_region>`\\ (\\ region_location\\: ``Vector2i``\\ ) |const|                                                                                                                     |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`has_regionp<class_Terrain3DData_method_has_regionp>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                    |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`import_images<class_Terrain3DData_method_import_images>`\\ (\\ images\\: :ref:`Array<class_Array>`\\[``Image``\\], global_position\\: ``Vector3`` = Vector3(0, 0, 0), offset\\: ``float`` = 0.0, scale\\: ``float`` = 1.0\\ ) |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`is_in_slope<class_Terrain3DData_method_is_in_slope>`\\ (\\ global_position\\: ``Vector3``, slope_range\\: ``Vector2``, normal\\: ``Vector3`` = Vector3(0, 0, 0)\\ ) |const|                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`is_region_deleted<class_Terrain3DData_method_is_region_deleted>`\\ (\\ region_location\\: ``Vector2i``\\ ) |const|                                                                                                       |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                                                   | :ref:`is_region_modified<class_Terrain3DData_method_is_region_modified>`\\ (\\ region_location\\: ``Vector2i``\\ ) |const|                                                                                                     |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``                                                                  | :ref:`layered_to_image<class_Terrain3DData_method_layered_to_image>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const|                                                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`load_directory<class_Terrain3DData_method_load_directory>`\\ (\\ directory\\: ``String``\\ )                                                                                                                             |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`load_region<class_Terrain3DData_method_load_region>`\\ (\\ region_location\\: ``Vector2i``, directory\\: ``String``, update\\: ``bool`` = true\\ )                                                                         |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`remove_region<class_Terrain3DData_method_remove_region>`\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, update\\: ``bool`` = true\\ )                                                                     |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`remove_regionl<class_Terrain3DData_method_remove_regionl>`\\ (\\ region_location\\: ``Vector2i``, update\\: ``bool`` = true\\ )                                                                                           |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`remove_regionp<class_Terrain3DData_method_remove_regionp>`\\ (\\ global_position\\: ``Vector3``, update\\: ``bool`` = true\\ )                                                                                            |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`save_directory<class_Terrain3DData_method_save_directory>`\\ (\\ directory\\: ``String``\\ )                                                                                                                             |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`save_region<class_Terrain3DData_method_save_region>`\\ (\\ region_location\\: ``Vector2i``, directory\\: ``String``, save_16_bit\\: ``bool`` = false\\ )                                                                   |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_color<class_Terrain3DData_method_set_color>`\\ (\\ global_position\\: ``Vector3``, color\\: ``Color``\\ )                                                                                                             |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control<class_Terrain3DData_method_set_control>`\\ (\\ global_position\\: ``Vector3``, control\\: ``int``\\ )                                                                                                         |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_angle<class_Terrain3DData_method_set_control_angle>`\\ (\\ global_position\\: ``Vector3``, degrees\\: ``float``\\ )                                                                                           |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_auto<class_Terrain3DData_method_set_control_auto>`\\ (\\ global_position\\: ``Vector3``, enable\\: ``bool``\\ )                                                                                               |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_base_id<class_Terrain3DData_method_set_control_base_id>`\\ (\\ global_position\\: ``Vector3``, texture_id\\: ``int``\\ )                                                                                      |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_blend<class_Terrain3DData_method_set_control_blend>`\\ (\\ global_position\\: ``Vector3``, blend_value\\: ``float``\\ )                                                                                       |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_hole<class_Terrain3DData_method_set_control_hole>`\\ (\\ global_position\\: ``Vector3``, enable\\: ``bool``\\ )                                                                                               |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_navigation<class_Terrain3DData_method_set_control_navigation>`\\ (\\ global_position\\: ``Vector3``, enable\\: ``bool``\\ )                                                                                   |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_overlay_id<class_Terrain3DData_method_set_control_overlay_id>`\\ (\\ global_position\\: ``Vector3``, texture_id\\: ``int``\\ )                                                                                |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_control_scale<class_Terrain3DData_method_set_control_scale>`\\ (\\ global_position\\: ``Vector3``, percentage_modifier\\: ``float``\\ )                                                                               |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_height<class_Terrain3DData_method_set_height>`\\ (\\ global_position\\: ``Vector3``, height\\: ``float``\\ )                                                                                                          |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_pixel<class_Terrain3DData_method_set_pixel>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, global_position\\: ``Vector3``, pixel\\: ``Color``\\ )                                                    |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_region_deleted<class_Terrain3DData_method_set_region_deleted>`\\ (\\ region_location\\: ``Vector2i``, deleted\\: ``bool``\\ )                                                                                         |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_region_modified<class_Terrain3DData_method_set_region_modified>`\\ (\\ region_location\\: ``Vector2i``, modified\\: ``bool``\\ )                                                                                      |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`set_roughness<class_Terrain3DData_method_set_roughness>`\\ (\\ global_position\\: ``Vector3``, roughness\\: ``float``\\ )                                                                                                 |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                                                     | :ref:`update_maps<class_Terrain3DData_method_update_maps>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>` = 3, all_regions\\: ``bool`` = true, generate_mipmaps\\: ``bool`` = false\\ )                          |\n   +----------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nSignals\n-------\n\n.. _class_Terrain3DData_signal_color_maps_changed:\n\n.. rst-class:: classref-signal\n\n**color_maps_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DData_signal_color_maps_changed>`\n\nEmitted when the color maps array is regenerated.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_signal_control_maps_changed:\n\n.. rst-class:: classref-signal\n\n**control_maps_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DData_signal_control_maps_changed>`\n\nEmitted when the control maps array is regenerated.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_signal_height_maps_changed:\n\n.. rst-class:: classref-signal\n\n**height_maps_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DData_signal_height_maps_changed>`\n\nEmitted when the height maps array is regenerated.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_signal_maps_changed:\n\n.. rst-class:: classref-signal\n\n**maps_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DData_signal_maps_changed>`\n\nEmitted when the region map or any map array has been regenerated.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_signal_maps_edited:\n\n.. rst-class:: classref-signal\n\n**maps_edited**\\ (\\ edited_area\\: ``AABB``\\ ) :ref:`🔗<class_Terrain3DData_signal_maps_edited>`\n\nThis signal is emitted whenever the editor (:ref:`Terrain3DEditor<class_Terrain3DEditor>`) is used to:\n\n- add or remove a region\n\n- alter a region map with a brush tool\n\n- undo or redo any of the above operations\n\nThe parameter contains the axis-aligned bounding box of the area edited.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_signal_region_map_changed:\n\n.. rst-class:: classref-signal\n\n**region_map_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DData_signal_region_map_changed>`\n\nEmitted when the region map is regenerated.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DData_HeightFilter:\n\n.. rst-class:: classref-enumeration\n\nenum **HeightFilter**: :ref:`🔗<enum_Terrain3DData_HeightFilter>`\n\n.. _class_Terrain3DData_constant_HEIGHT_FILTER_NEAREST:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`HeightFilter<enum_Terrain3DData_HeightFilter>` **HEIGHT_FILTER_NEAREST** = ``0``\n\nSamples the height map at the exact coordinates given.\n\n.. _class_Terrain3DData_constant_HEIGHT_FILTER_MINIMUM:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`HeightFilter<enum_Terrain3DData_HeightFilter>` **HEIGHT_FILTER_MINIMUM** = ``1``\n\nSamples (1 << lod) \\* 2 heights around the given coordinates and returns the lowest.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nConstants\n---------\n\n.. _class_Terrain3DData_constant_REGION_MAP_SIZE:\n\n.. rst-class:: classref-constant\n\n**REGION_MAP_SIZE** = ``32`` :ref:`🔗<class_Terrain3DData_constant_REGION_MAP_SIZE>`\n\nHard coded number of regions on a side. The total number of regions is this squared.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DData_property_color_maps:\n\n.. rst-class:: classref-property\n\n:ref:`Array<class_Array>`\\[``Image``\\] **color_maps** = ``[]`` :ref:`🔗<class_Terrain3DData_property_color_maps>`\n\n.. rst-class:: classref-property-setget\n\n- :ref:`Array<class_Array>`\\[``Image``\\] **get_color_maps**\\ (\\ )\n\nAn Array\\ ``Image`` containing references to all of the color maps in all regions. See :ref:`Terrain3DRegion.color_map<class_Terrain3DRegion_property_color_map>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_property_control_maps:\n\n.. rst-class:: classref-property\n\n:ref:`Array<class_Array>`\\[``Image``\\] **control_maps** = ``[]`` :ref:`🔗<class_Terrain3DData_property_control_maps>`\n\n.. rst-class:: classref-property-setget\n\n- :ref:`Array<class_Array>`\\[``Image``\\] **get_control_maps**\\ (\\ )\n\nAn Array\\ ``Image`` containing references to all of the control maps in all regions. See :ref:`Terrain3DRegion.control_map<class_Terrain3DRegion_property_control_map>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_property_height_maps:\n\n.. rst-class:: classref-property\n\n:ref:`Array<class_Array>`\\[``Image``\\] **height_maps** = ``[]`` :ref:`🔗<class_Terrain3DData_property_height_maps>`\n\n.. rst-class:: classref-property-setget\n\n- :ref:`Array<class_Array>`\\[``Image``\\] **get_height_maps**\\ (\\ )\n\nAn Array\\ ``Image`` containing references to all of the height maps in all regions. See :ref:`Terrain3DRegion.height_map<class_Terrain3DRegion_property_height_map>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_property_region_locations:\n\n.. rst-class:: classref-property\n\n:ref:`Array<class_Array>`\\[``Vector2i``\\] **region_locations** = ``[]`` :ref:`🔗<class_Terrain3DData_property_region_locations>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_region_locations**\\ (\\ value\\: :ref:`Array<class_Array>`\\[``Vector2i``\\]\\ )\n- :ref:`Array<class_Array>`\\[``Vector2i``\\] **get_region_locations**\\ (\\ )\n\nThe array of all active region locations; those not marked for deletion.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DData_method_add_region:\n\n.. rst-class:: classref-method\n\nError **add_region**\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_add_region>`\n\nAdds a region for sculpting and painting.\n\nThe region should already be configured with the desired location and maps before sending to this function.\n\nUpon saving, this region will be written to a data file stored in :ref:`Terrain3D.data_directory<class_Terrain3D_property_data_directory>`.\n\n- update - regenerates the texture arrays if true. Set to false if bulk adding many regions, then true on the last one or use :ref:`update_maps()<class_Terrain3DData_method_update_maps>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_add_region_blank:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DRegion<class_Terrain3DRegion>` **add_region_blank**\\ (\\ region_location\\: ``Vector2i``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_add_region_blank>`\n\nCreates and adds a blank region at the specified location. See :ref:`add_region()<class_Terrain3DData_method_add_region>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_add_region_blankp:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DRegion<class_Terrain3DRegion>` **add_region_blankp**\\ (\\ global_position\\: ``Vector3``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_add_region_blankp>`\n\nCreates and adds a blank region at a region location encompassing the specified global position. See :ref:`add_region()<class_Terrain3DData_method_add_region>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_calc_height_range:\n\n.. rst-class:: classref-method\n\n|void| **calc_height_range**\\ (\\ recursive\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DData_method_calc_height_range>`\n\nRecalculates the master height range for the whole terrain by summing the height ranges of all active regions.\n\nRecursive mode does the same, but has each region recalculate heights from each heightmap pixel. See :ref:`Terrain3DRegion.calc_height_range()<class_Terrain3DRegion_method_calc_height_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_change_region_size:\n\n.. rst-class:: classref-method\n\n|void| **change_region_size**\\ (\\ region_size\\: ``int``\\ ) :ref:`🔗<class_Terrain3DData_method_change_region_size>`\n\nReslices terrain data to fit the new region size. This is a destructive process for which there is no undo. However Godot does make an undo entry, which will reslice in reverse. Files on disk are not added or removed until the scene is saved.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_do_for_regions:\n\n.. rst-class:: classref-method\n\n|void| **do_for_regions**\\ (\\ area\\: ``Rect2i``, callback\\: ``Callable``\\ ) :ref:`🔗<class_Terrain3DData_method_do_for_regions>`\n\nCalls the callback function for every region within the given area. If using vertex_spacing, area values should be descaled.\n\nThe callable receives: source Terrain3DRegion, source Rect2i, dest Rect2i, (bindings)\n\nYou may wish to append .bind() to the callback to pass along variables. For instance internally this function is called when changing region size. We bind the destination Terrain3DRegion, then use do_for_regions to copy segments of source regions to segments of destination regions. See the code for change_region_size() for more.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_dump:\n\n.. rst-class:: classref-method\n\n|void| **dump**\\ (\\ verbose\\: ``bool`` = false\\ ) |const| :ref:`🔗<class_Terrain3DData_method_dump>`\n\nCalls :ref:`Terrain3DRegion.dump()<class_Terrain3DRegion_method_dump>` for all regions loaded, active and inactive.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_export_image:\n\n.. rst-class:: classref-method\n\nError **export_image**\\ (\\ file_name\\: ``String``, map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const| :ref:`🔗<class_Terrain3DData_method_export_image>`\n\nExports the specified map type as one of r16/raw, exr, jpg, png, webp, res, tres. \n\nR16 or exr are recommended for roundtrip external editing.\n\nR16 can be edited by Krita, however you must know the dimensions and min/max before reimporting. This information is printed to the console.\n\nRes/tres stores in Godot's native data format.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_color:\n\n.. rst-class:: classref-method\n\n``Color`` **get_color**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_color>`\n\nReturns the associated pixel on the color map at the requested position.\n\nReturns ``Color(NAN, NAN, NAN, NAN)`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_color_maps_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_color_maps_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_color_maps_rid>`\n\nReturns the resource ID of the generated height map Texture Array sent to the shader. You can use this RID with the RenderingServer to set it as a shader parameter for a sampler2DArray uniform in your own shader. See `Tips <https://terrain3d.readthedocs.io/en/stable/docs/tips.html#using-the-generated-height-map-in-other-shaders>`__ for an example.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control:\n\n.. rst-class:: classref-method\n\n``int`` **get_control**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control>`\n\nReturns the associated pixel on the control map at the requested position.\n\nReturns ``4,294,967,295`` aka ``UINT32_MAX`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_angle:\n\n.. rst-class:: classref-method\n\n``float`` **get_control_angle**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_angle>`\n\nReturns the angle, aka uv rotation, on the control map at the requested position. Values are fixed to 22.5 degree intervals, for a maximum of 16 angles. 360 / 16 = 22.5.\n\nReturns ``NAN`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_auto:\n\n.. rst-class:: classref-method\n\n``bool`` **get_control_auto**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_auto>`\n\nReturns whether the autoshader is enabled on the control map at the requested position.\n\nReturns ``false`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_base_id:\n\n.. rst-class:: classref-method\n\n``int`` **get_control_base_id**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_base_id>`\n\nReturns the base texture ID on the control map at the requested position. Values are 0 - 31, which matches the ID of the texture asset in the asset dock.\n\nReturns ``4,294,967,295`` aka ``UINT32_MAX`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_blend:\n\n.. rst-class:: classref-method\n\n``float`` **get_control_blend**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_blend>`\n\nReturns the blend value between the base texture ID and the overlay texture ID. The value is clamped between 0.0 - 1.0 where 0.0 shows only the base texture, and 1.0 shows only the overlay texture.\n\nReturns ``NAN`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_hole:\n\n.. rst-class:: classref-method\n\n``bool`` **get_control_hole**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_hole>`\n\nReturns whether there is a hole on the control map at the requested position.\n\nReturns ``false`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_maps_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_control_maps_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_maps_rid>`\n\nReturns the resource ID of the generated control map Texture Array sent to the shader. You can use this RID with the RenderingServer to set it as a shader parameter for a sampler2DArray uniform in your own shader. See `Tips <https://terrain3d.readthedocs.io/en/stable/docs/tips.html#using-the-generated-height-map-in-other-shaders>`__ for an example.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_navigation:\n\n.. rst-class:: classref-method\n\n``bool`` **get_control_navigation**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_navigation>`\n\nReturns whether navigation is enabled on the control map at the requested position.\n\nReturns ``false`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_overlay_id:\n\n.. rst-class:: classref-method\n\n``int`` **get_control_overlay_id**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_overlay_id>`\n\nReturns the overlay texture ID on the control map at the requested position. Values are 0 - 31, which matches the ID of the texture asset in the asset dock.\n\nReturns ``4,294,967,295`` aka ``UINT32_MAX`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_control_scale:\n\n.. rst-class:: classref-method\n\n``float`` **get_control_scale**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_control_scale>`\n\nReturns the uv scale on the control map at the requested position. The value is rounded to the nearest 20% difference from 100%, ranging between -60% to +80%. Eg. +20% or -40%.\n\nReturns ``NAN`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_height:\n\n.. rst-class:: classref-method\n\n``float`` **get_height**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_height>`\n\nReturns the height at the requested position. If the position is close to a vertex, the pixel height on the heightmap is returned. Otherwise the value is interpolated from the 4 vertices surrounding the position.\n\nReturns ``NAN`` if the requested position is a hole or outside of defined regions.\n\nAlso see :ref:`Terrain3D.get_raycast_result()<class_Terrain3D_method_get_raycast_result>` and :ref:`Terrain3D.get_intersection()<class_Terrain3D_method_get_intersection>` for alternative functions\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_height_maps_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_height_maps_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_height_maps_rid>`\n\nReturns the resource ID of the generated height map texture array sent to the shader. You can use this RID with the RenderingServer to set it as a shader parameter for a sampler2DArray uniform in your own shader. See `Tips <https://terrain3d.readthedocs.io/en/stable/docs/tips.html#using-the-generated-height-map-in-other-shaders>`__ for an example.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_height_range:\n\n.. rst-class:: classref-method\n\n``Vector2`` **get_height_range**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_height_range>`\n\nReturns the highest and lowest heights for the sculpted terrain used to set the world AABB. See :ref:`calc_height_range()<class_Terrain3DData_method_calc_height_range>`.\n\nAny :ref:`Terrain3DMaterial.world_background<class_Terrain3DMaterial_property_world_background>` used that extends the mesh outside of this range will not change this variable. You need to set :ref:`Terrain3D.cull_margin<class_Terrain3D_property_cull_margin>` or the renderer will clip meshes.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_maps:\n\n.. rst-class:: classref-method\n\n:ref:`Array<class_Array>`\\[``Image``\\] **get_maps**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_maps>`\n\nReturns an Array of Images from all regions of the specified map type.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_mesh_vertex:\n\n.. rst-class:: classref-method\n\n``Vector3`` **get_mesh_vertex**\\ (\\ lod\\: ``int``, filter\\: :ref:`HeightFilter<enum_Terrain3DData_HeightFilter>`, global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_mesh_vertex>`\n\nReturns the position of a terrain vertex at a certain LOD. If the position is outside of defined regions or there is a hole, it returns ``NAN`` in the vector's Y coordinate.\n\n\\ ``lod`` - Determines how many heights around the given global position will be sampled. Range 0 - 8.\n\n\\ ``filter`` - Specifies how samples are filtered. See :ref:`HeightFilter<enum_Terrain3DData_HeightFilter>`.\n\n\\ ``global_position`` - X and Z coordinates of the vertex. Heights will be sampled around these coordinates.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_normal:\n\n.. rst-class:: classref-method\n\n``Vector3`` **get_normal**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_normal>`\n\nReturns the terrain normal at the specified position. This function uses :ref:`get_height()<class_Terrain3DData_method_get_height>`.\n\nReturns ``Vector3(NAN, NAN, NAN)`` if the requested position is a hole or outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_pixel:\n\n.. rst-class:: classref-method\n\n``Color`` **get_pixel**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_pixel>`\n\nReturns the pixel for the map type associated with the specified position.\n\nReturns ``Color(NAN, NAN, NAN, NAN)`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DRegion<class_Terrain3DRegion>` **get_region**\\ (\\ region_location\\: ``Vector2i``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_region>`\n\nReturn the :ref:`Terrain3DRegion<class_Terrain3DRegion>` at the specified location. This will return inactive regions marked for deletion. Check with :ref:`Terrain3DRegion.deleted<class_Terrain3DRegion_property_deleted>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region_count:\n\n.. rst-class:: classref-method\n\n``int`` **get_region_count**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_region_count>`\n\nReturns the number of active regions; those not marked for deletion.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region_id:\n\n.. rst-class:: classref-method\n\n``int`` **get_region_id**\\ (\\ region_location\\: ``Vector2i``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_region_id>`\n\nReturns -1 if no region or out of bounds at the given location, otherwise returns the current region id.\n\nThe region_id is the index into the TextureArrays sent to the shader, and can change at any time. Gamedevs should generally index regions by location. However, this function is useful to determine if the location is a valid region.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region_idp:\n\n.. rst-class:: classref-method\n\n``int`` **get_region_idp**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_region_idp>`\n\nReturns the region id at a global position. See :ref:`get_region_id()<class_Terrain3DData_method_get_region_id>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region_location:\n\n.. rst-class:: classref-method\n\n``Vector2i`` **get_region_location**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_region_location>`\n\nReturns the calculated region location for the given global position. This is just a calculation and does no bounds checking or verification that a region exists. See :ref:`get_region_map_index()<class_Terrain3DData_method_get_region_map_index>` for bounds checking, or :ref:`has_region()<class_Terrain3DData_method_has_region>` for checking existance.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region_map:\n\n.. rst-class:: classref-method\n\n``PackedInt32Array`` **get_region_map**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_region_map>`\n\nReturns a fully populated 32 x 32 array. The array location contains the region id + 1, or 0, which means no region.\n\nSee :ref:`get_region_map_index()<class_Terrain3DData_method_get_region_map_index>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_region_map_index:\n\n.. rst-class:: classref-method\n\n``int`` **get_region_map_index**\\ (\\ region_location\\: ``Vector2i``\\ ) |static| :ref:`🔗<class_Terrain3DData_method_get_region_map_index>`\n\nGiven a region location, returns the index into the region map array. See :ref:`get_region_map()<class_Terrain3DData_method_get_region_map>`.\n\nYou can use this function to quickly determine if a location is within the greater world bounds (-16,-16) to (15, 15). It returns -1 if not.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_regionp:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DRegion<class_Terrain3DRegion>` **get_regionp**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_regionp>`\n\nReturns the region at the specified global position. This will return inactive regions marked for deletion. Check with :ref:`Terrain3DRegion.deleted<class_Terrain3DRegion_property_deleted>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_regions_active:\n\n.. rst-class:: classref-method\n\n:ref:`Array<class_Array>`\\[:ref:`Terrain3DRegion<class_Terrain3DRegion>`\\] **get_regions_active**\\ (\\ copy\\: ``bool`` = false, deep\\: ``bool`` = false\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_regions_active>`\n\nReturns an array of active regions not marked for deletion. Each region knows its own location. See :ref:`Terrain3DRegion.location<class_Terrain3DRegion_property_location>`.\n\n- copy - returns a shallow copy of the regions; region map references are copied.\n\n- deep - returns a deep copy of the regions; region maps are full duplicates.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_regions_all:\n\n.. rst-class:: classref-method\n\n``Dictionary`` **get_regions_all**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_regions_all>`\n\nReturns all regions in a dictionary indexed by region location. Some regions may be marked for deletion.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_roughness:\n\n.. rst-class:: classref-method\n\n``float`` **get_roughness**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_roughness>`\n\nReturns the roughness modifier (wetness) on the color map alpha channel associated with the specified position.\n\nReturns ``Color(NAN, NAN, NAN, NAN)`` if the position is outside of defined regions.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_get_texture_id:\n\n.. rst-class:: classref-method\n\n``Vector3`` **get_texture_id**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_get_texture_id>`\n\nReturns ``Vector3(base texture id, overlay id, blend value)``.\n\nReturns ``Vector3(NAN, NAN, NAN)`` if the position is a hole or outside of defined regions.\n\nThis is often used for playing sounds on footsteps. It's up to the gamedev to determine which is visually apparent based on shader settings.\n\nDue to blending, it won't be pixel perfect. Try having your player controller print this value while walking around to see how the blending values look. Perhaps you'll find that the overlay texture is visible starting at a blend value of .3 to .5, otherwise the base is visible. You can also observe the control blend debug view with :ref:`Terrain3DMaterial.show_control_blend<class_Terrain3DMaterial_property_show_control_blend>`.\n\nObserving how this is done in The Witcher 3, there are only about 6 sounds used (snow, foliage, dirt, gravel, rock, wood), and except for wood, they are not pixel perfect. Wood is easy to do by detecting if the player is walking on wood meshes. The other 5 sounds are played when the player is in an area where the textures are blending. So it might play rock while over a dirt area. This shows pixel perfect accuracy is not important. It will still provide a seamless audio visual experience.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_has_region:\n\n.. rst-class:: classref-method\n\n``bool`` **has_region**\\ (\\ region_location\\: ``Vector2i``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_has_region>`\n\nReturns true if the specified region location has an active region.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_has_regionp:\n\n.. rst-class:: classref-method\n\n``bool`` **has_regionp**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_has_regionp>`\n\nReturns true if the specified global position has an active region.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_import_images:\n\n.. rst-class:: classref-method\n\n|void| **import_images**\\ (\\ images\\: :ref:`Array<class_Array>`\\[``Image``\\], global_position\\: ``Vector3`` = Vector3(0, 0, 0), offset\\: ``float`` = 0.0, scale\\: ``float`` = 1.0\\ ) :ref:`🔗<class_Terrain3DData_method_import_images>`\n\nImports an Image set (Height, Control, Color) into this resource. It does NOT normalize values to 0-1. You must do that using get_min_max() and adjusting scale and offset.\n\n\\ ``images`` - MapType.TYPE_MAX sized array of Images for Height, Control, Color. Images can be blank or null.\n\n\\ ``global_position`` - X,0,Z position on the region map. Valid range is :ref:`Terrain3D.vertex_spacing<class_Terrain3D_property_vertex_spacing>` \\* :ref:`Terrain3D.region_size<class_Terrain3D_property_region_size>` \\* (+/-16, +/-16).\n\n\\ ``offset`` - Add this factor to all height values, can be negative.\n\n\\ ``scale`` - Scale all height values by this factor (applied after offset).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_is_in_slope:\n\n.. rst-class:: classref-method\n\n``bool`` **is_in_slope**\\ (\\ global_position\\: ``Vector3``, slope_range\\: ``Vector2``, normal\\: ``Vector3`` = Vector3(0, 0, 0)\\ ) |const| :ref:`🔗<class_Terrain3DData_method_is_in_slope>`\n\nReturns true if the slope of the terrain at the given position is within the slope range. If normal is provided it will use that instead of querying the terrain.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_is_region_deleted:\n\n.. rst-class:: classref-method\n\n``bool`` **is_region_deleted**\\ (\\ region_location\\: ``Vector2i``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_is_region_deleted>`\n\nReturns true if the region at the  location exists and is marked as deleted. Syntactic sugar for :ref:`Terrain3DRegion.deleted<class_Terrain3DRegion_property_deleted>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_is_region_modified:\n\n.. rst-class:: classref-method\n\n``bool`` **is_region_modified**\\ (\\ region_location\\: ``Vector2i``\\ ) |const| :ref:`🔗<class_Terrain3DData_method_is_region_modified>`\n\nReturns true if the region at the location exists and is marked as modified. Syntactic sugar for :ref:`Terrain3DRegion.modified<class_Terrain3DRegion_property_modified>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_layered_to_image:\n\n.. rst-class:: classref-method\n\n``Image`` **layered_to_image**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const| :ref:`🔗<class_Terrain3DData_method_layered_to_image>`\n\nReturns an Image of the given map type that contains all regions in one large image. If the world has multiple islands, this function will return an image large enough to encompass all used regions, with black areas in between the islands.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_load_directory:\n\n.. rst-class:: classref-method\n\n|void| **load_directory**\\ (\\ directory\\: ``String``\\ ) :ref:`🔗<class_Terrain3DData_method_load_directory>`\n\nLoads all of the Terrain3DRegion files found in the specified directory. Then it rebuilds all map arrays.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_load_region:\n\n.. rst-class:: classref-method\n\n|void| **load_region**\\ (\\ region_location\\: ``Vector2i``, directory\\: ``String``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_load_region>`\n\nLoads the specified region location file.\n\n- update - rebuild maps if true.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_remove_region:\n\n.. rst-class:: classref-method\n\n|void| **remove_region**\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_remove_region>`\n\nMarks the specified region as deleted. This deactivates it so it won't render it on screen once maps are updated, unless marked not deleted. The file will be deleted from disk upon saving.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_remove_regionl:\n\n.. rst-class:: classref-method\n\n|void| **remove_regionl**\\ (\\ region_location\\: ``Vector2i``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_remove_regionl>`\n\nRemoves the region at the specified location. See :ref:`remove_region()<class_Terrain3DData_method_remove_region>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_remove_regionp:\n\n.. rst-class:: classref-method\n\n|void| **remove_regionp**\\ (\\ global_position\\: ``Vector3``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DData_method_remove_regionp>`\n\nRemoves the region at the specified global_position. See :ref:`remove_region()<class_Terrain3DData_method_remove_region>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_save_directory:\n\n.. rst-class:: classref-method\n\n|void| **save_directory**\\ (\\ directory\\: ``String``\\ ) :ref:`🔗<class_Terrain3DData_method_save_directory>`\n\nThis saves all active regions into the specified directory.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_save_region:\n\n.. rst-class:: classref-method\n\n|void| **save_region**\\ (\\ region_location\\: ``Vector2i``, directory\\: ``String``, save_16_bit\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DData_method_save_region>`\n\nSaves the specified active region to the directory. See :ref:`Terrain3DRegion.save()<class_Terrain3DRegion_method_save>`.\n\n- region_location - the region to save.\n\n- 16_bit - converts the edited 32-bit heightmap to 16-bit. This is a lossy operation.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_color:\n\n.. rst-class:: classref-method\n\n|void| **set_color**\\ (\\ global_position\\: ``Vector3``, color\\: ``Color``\\ ) :ref:`🔗<class_Terrain3DData_method_set_color>`\n\nSets the color on the color map pixel associated with the specified position. See :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control:\n\n.. rst-class:: classref-method\n\n|void| **set_control**\\ (\\ global_position\\: ``Vector3``, control\\: ``int``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control>`\n\nSets the value on the control map pixel associated with the specified position. See :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_angle:\n\n.. rst-class:: classref-method\n\n|void| **set_control_angle**\\ (\\ global_position\\: ``Vector3``, degrees\\: ``float``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_angle>`\n\nSets the angle, aka uv rotation, on the control map at the requested position. Values are rounded to the nearest 22.5 degree interval, for a maximum of 16 angles. 360 / 16 = 22.5.\n\nSee :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_auto:\n\n.. rst-class:: classref-method\n\n|void| **set_control_auto**\\ (\\ global_position\\: ``Vector3``, enable\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_auto>`\n\nSets if the material should render the autoshader or manual texturing on the control map at the requested position.\n\nSee :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_base_id:\n\n.. rst-class:: classref-method\n\n|void| **set_control_base_id**\\ (\\ global_position\\: ``Vector3``, texture_id\\: ``int``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_base_id>`\n\nSets the base texture ID on the control map at the requested position. Values are clamped to 0 - 31, matching the ID of the texture asset in the asset dock.\n\nSee :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_blend:\n\n.. rst-class:: classref-method\n\n|void| **set_control_blend**\\ (\\ global_position\\: ``Vector3``, blend_value\\: ``float``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_blend>`\n\nSets the blend value between the base texture ID, and the overlay texture ID. The value is clamped between 0.0 - 1.0 where 0.0 shows only the base texture, and 1.0 shows only the overlay texture.\n\nSee :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_hole:\n\n.. rst-class:: classref-method\n\n|void| **set_control_hole**\\ (\\ global_position\\: ``Vector3``, enable\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_hole>`\n\nSets if a hole should be rendered on the control map at the requested position. See :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_navigation:\n\n.. rst-class:: classref-method\n\n|void| **set_control_navigation**\\ (\\ global_position\\: ``Vector3``, enable\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_navigation>`\n\nSets if navigation generation is enabled on the control map at the requested position. See :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_overlay_id:\n\n.. rst-class:: classref-method\n\n|void| **set_control_overlay_id**\\ (\\ global_position\\: ``Vector3``, texture_id\\: ``int``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_overlay_id>`\n\nSets the overlay texture ID on the control map at the requested position. Values are clamped to 0 - 31, matching the ID of the texture asset in the asset dock.\n\nSee :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_control_scale:\n\n.. rst-class:: classref-method\n\n|void| **set_control_scale**\\ (\\ global_position\\: ``Vector3``, percentage_modifier\\: ``float``\\ ) :ref:`🔗<class_Terrain3DData_method_set_control_scale>`\n\nSets the uv scale on the control map at the requested position. The value is rounded to the nearest 20% difference from 100%, ranging between -60% to +80%.\n\nSee :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_height:\n\n.. rst-class:: classref-method\n\n|void| **set_height**\\ (\\ global_position\\: ``Vector3``, height\\: ``float``\\ ) :ref:`🔗<class_Terrain3DData_method_set_height>`\n\nSets the height value on the heightmap pixel associated with the specified position. See :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\nUnlike :ref:`get_height()<class_Terrain3DData_method_get_height>`, which interpolates between vertices, this function does not and will set the pixel at floored coordinates.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_pixel:\n\n.. rst-class:: classref-method\n\n|void| **set_pixel**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, global_position\\: ``Vector3``, pixel\\: ``Color``\\ ) :ref:`🔗<class_Terrain3DData_method_set_pixel>`\n\nSets the pixel for the map type associated with the specified position. This method is fine for setting a few pixels, but if you wish to modify thousands of pixels quickly, you should get the region and use :ref:`Terrain3DRegion.get_map()<class_Terrain3DRegion_method_get_map>`, then edit the images directly.\n\nAfter setting pixels you need to call :ref:`update_maps()<class_Terrain3DData_method_update_maps>`. You may also need to regenerate collision if you don't have dynamic collision enabled.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_region_deleted:\n\n.. rst-class:: classref-method\n\n|void| **set_region_deleted**\\ (\\ region_location\\: ``Vector2i``, deleted\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DData_method_set_region_deleted>`\n\nMarks a region as deleted. It will stop displaying when maps are updated. The file will be removed on save.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_region_modified:\n\n.. rst-class:: classref-method\n\n|void| **set_region_modified**\\ (\\ region_location\\: ``Vector2i``, modified\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DData_method_set_region_modified>`\n\nSets the region as modified. It will be written to disk when saved. Syntactic sugar for :ref:`Terrain3DRegion.modified<class_Terrain3DRegion_property_modified>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_set_roughness:\n\n.. rst-class:: classref-method\n\n|void| **set_roughness**\\ (\\ global_position\\: ``Vector3``, roughness\\: ``float``\\ ) :ref:`🔗<class_Terrain3DData_method_set_roughness>`\n\nSets the roughness modifier (wetness) on the color map alpha channel associated with the specified position. See :ref:`set_pixel()<class_Terrain3DData_method_set_pixel>` for important information.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DData_method_update_maps:\n\n.. rst-class:: classref-method\n\n|void| **update_maps**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>` = 3, all_regions\\: ``bool`` = true, generate_mipmaps\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DData_method_update_maps>`\n\nRegenerates the region map and the TextureArrays that combine the requested map types. This function needs to be called after editing any of the maps.\n\nBy default, this function rebuilds all maps for all regions.\n\n- map_type - Regenerate only maps of this type.\n\n- all_regions - Regenerate all regions if true, otherwise only those marked with :ref:`Terrain3DRegion.edited<class_Terrain3DRegion_property_edited>`.\n\n- generate_mipmaps - Regenerate mipmaps if map_type is color or all (max), for the regions specified above. This can also be done on individual regions before calling this function with ``region.get_color_map().generate_mipmaps()``.\n\nFor frequent editing, rather than enabling all_regions, it is more optimal to only update changed regions as follows:\n\n::\n\n    terrain.data.set_height(global_position, 10.0)\n    var region:Terrain3DRegion = terrain.data.get_regionp(global_position)\n    region.set_edited(true)\n    terrain.data.update_maps(Terrain3DRegion.TYPE_HEIGHT, false)\n    region.set_edited(false)\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3deditor.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DEditor.xml.\n\n.. _class_Terrain3DEditor:\n\nTerrain3DEditor\n===============\n\n**Inherits:** ``Object``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class handles all of the sculpting and painting operations for Terrain3D.\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`apply_undo<class_Terrain3DEditor_method_apply_undo>`\\ (\\ data\\: ``Dictionary``\\ )                                              |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`backup_region<class_Terrain3DEditor_method_backup_region>`\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`\\ )       |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Operation<enum_Terrain3DEditor_Operation>` | :ref:`get_operation<class_Terrain3DEditor_method_get_operation>`\\ (\\ ) |const|                                                       |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3D<class_Terrain3D>`                | :ref:`get_terrain<class_Terrain3DEditor_method_get_terrain>`\\ (\\ ) |const|                                                           |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Tool<enum_Terrain3DEditor_Tool>`           | :ref:`get_tool<class_Terrain3DEditor_method_get_tool>`\\ (\\ ) |const|                                                                 |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                         | :ref:`is_operating<class_Terrain3DEditor_method_is_operating>`\\ (\\ ) |const|                                                         |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`operate<class_Terrain3DEditor_method_operate>`\\ (\\ position\\: ``Vector3``, camera_direction\\: ``float``\\ )                     |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`set_brush_data<class_Terrain3DEditor_method_set_brush_data>`\\ (\\ data\\: ``Dictionary``\\ )                                      |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`set_operation<class_Terrain3DEditor_method_set_operation>`\\ (\\ operation\\: :ref:`Operation<enum_Terrain3DEditor_Operation>`\\ ) |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`set_terrain<class_Terrain3DEditor_method_set_terrain>`\\ (\\ terrain\\: :ref:`Terrain3D<class_Terrain3D>`\\ )                      |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`set_tool<class_Terrain3DEditor_method_set_tool>`\\ (\\ tool\\: :ref:`Tool<enum_Terrain3DEditor_Tool>`\\ )                          |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`start_operation<class_Terrain3DEditor_method_start_operation>`\\ (\\ position\\: ``Vector3``\\ )                                   |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                           | :ref:`stop_operation<class_Terrain3DEditor_method_stop_operation>`\\ (\\ )                                                             |\n   +--------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DEditor_Operation:\n\n.. rst-class:: classref-enumeration\n\nenum **Operation**: :ref:`🔗<enum_Terrain3DEditor_Operation>`\n\n.. _class_Terrain3DEditor_constant_ADD:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **ADD** = ``0``\n\nAdditive operations.\n\n.. _class_Terrain3DEditor_constant_SUBTRACT:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **SUBTRACT** = ``1``\n\nSubtractive operations.\n\n.. _class_Terrain3DEditor_constant_REPLACE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **REPLACE** = ``2``\n\nReplacing operations.\n\n.. _class_Terrain3DEditor_constant_AVERAGE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **AVERAGE** = ``3``\n\nAveraging operations.\n\n.. _class_Terrain3DEditor_constant_GRADIENT:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **GRADIENT** = ``4``\n\nGradient operations.\n\n.. _class_Terrain3DEditor_constant_OP_MAX:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **OP_MAX** = ``5``\n\nThe number of elements in this enum.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _enum_Terrain3DEditor_Tool:\n\n.. rst-class:: classref-enumeration\n\nenum **Tool**: :ref:`🔗<enum_Terrain3DEditor_Tool>`\n\n.. _class_Terrain3DEditor_constant_SCULPT:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **SCULPT** = ``1``\n\n.. container:: contribute\n\n\tThere is currently no description for this enum. Please help us by `contributing one <https://contributing.godotengine.org/en/latest/documentation/class_reference.html>`__!\n\n\n\n.. _class_Terrain3DEditor_constant_HEIGHT:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **HEIGHT** = ``2``\n\nSculpt heights.\n\n.. _class_Terrain3DEditor_constant_TEXTURE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **TEXTURE** = ``3``\n\nPaint textures.\n\n.. _class_Terrain3DEditor_constant_COLOR:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **COLOR** = ``4``\n\nPaint on the color map.\n\n.. _class_Terrain3DEditor_constant_ROUGHNESS:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **ROUGHNESS** = ``5``\n\nPaint a roughness modifier, aka wetness.\n\n.. _class_Terrain3DEditor_constant_ANGLE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **ANGLE** = ``10``\n\nPaint textures rotated by an angle.\n\n.. _class_Terrain3DEditor_constant_SCALE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **SCALE** = ``11``\n\nPaint textures scaled by a value.\n\n.. _class_Terrain3DEditor_constant_AUTOSHADER:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **AUTOSHADER** = ``6``\n\nPaint where the shader automatically textures.\n\n.. _class_Terrain3DEditor_constant_HOLES:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **HOLES** = ``7``\n\nPaint where vertices will be invalidated to leave holes.\n\n.. _class_Terrain3DEditor_constant_NAVIGATION:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **NAVIGATION** = ``8``\n\nPaint where navigation will be generated.\n\n.. _class_Terrain3DEditor_constant_INSTANCER:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **INSTANCER** = ``9``\n\nPaint MultiMesh instances on the ground.\n\n.. _class_Terrain3DEditor_constant_REGION:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **REGION** = ``0``\n\nAdd/remove regions.\n\n.. _class_Terrain3DEditor_constant_TOOL_MAX:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **TOOL_MAX** = ``12``\n\nThe number of elements in this enum.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DEditor_method_apply_undo:\n\n.. rst-class:: classref-method\n\n|void| **apply_undo**\\ (\\ data\\: ``Dictionary``\\ ) :ref:`🔗<class_Terrain3DEditor_method_apply_undo>`\n\nUndo the previous changes, with the provided data. Used by Godot, not gamedevs.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_backup_region:\n\n.. rst-class:: classref-method\n\n|void| **backup_region**\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`\\ ) :ref:`🔗<class_Terrain3DEditor_method_backup_region>`\n\nAdds a region to the currently pending operation undo snapshot. :ref:`is_operating()<class_Terrain3DEditor_method_is_operating>` must be true.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_get_operation:\n\n.. rst-class:: classref-method\n\n:ref:`Operation<enum_Terrain3DEditor_Operation>` **get_operation**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DEditor_method_get_operation>`\n\nReturns the current selected tool operation (eg. add, subtract).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_get_terrain:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3D<class_Terrain3D>` **get_terrain**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DEditor_method_get_terrain>`\n\nReturns the instance of Terrain3D this class is conneced to.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_get_tool:\n\n.. rst-class:: classref-method\n\n:ref:`Tool<enum_Terrain3DEditor_Tool>` **get_tool**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DEditor_method_get_tool>`\n\nReturns the current tool selected in the editor plugin.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_is_operating:\n\n.. rst-class:: classref-method\n\n``bool`` **is_operating**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DEditor_method_is_operating>`\n\nReturns true if currently in the middle of a brushing operation.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_operate:\n\n.. rst-class:: classref-method\n\n|void| **operate**\\ (\\ position\\: ``Vector3``, camera_direction\\: ``float``\\ ) :ref:`🔗<class_Terrain3DEditor_method_operate>`\n\nStart brushing.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_set_brush_data:\n\n.. rst-class:: classref-method\n\n|void| **set_brush_data**\\ (\\ data\\: ``Dictionary``\\ ) :ref:`🔗<class_Terrain3DEditor_method_set_brush_data>`\n\nSets all brush settings used in the editor plugin.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_set_operation:\n\n.. rst-class:: classref-method\n\n|void| **set_operation**\\ (\\ operation\\: :ref:`Operation<enum_Terrain3DEditor_Operation>`\\ ) :ref:`🔗<class_Terrain3DEditor_method_set_operation>`\n\nSets the tool operation used in the editor plugin.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_set_terrain:\n\n.. rst-class:: classref-method\n\n|void| **set_terrain**\\ (\\ terrain\\: :ref:`Terrain3D<class_Terrain3D>`\\ ) :ref:`🔗<class_Terrain3DEditor_method_set_terrain>`\n\nSets the instance of Terrain3D this class is connected to.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_set_tool:\n\n.. rst-class:: classref-method\n\n|void| **set_tool**\\ (\\ tool\\: :ref:`Tool<enum_Terrain3DEditor_Tool>`\\ ) :ref:`🔗<class_Terrain3DEditor_method_set_tool>`\n\nSets the tool selected in the editor plugin.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_start_operation:\n\n.. rst-class:: classref-method\n\n|void| **start_operation**\\ (\\ position\\: ``Vector3``\\ ) :ref:`🔗<class_Terrain3DEditor_method_start_operation>`\n\nBegin a sculpting or painting operation. Prepares to create an undo/redo commit.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DEditor_method_stop_operation:\n\n.. rst-class:: classref-method\n\n|void| **stop_operation**\\ (\\ ) :ref:`🔗<class_Terrain3DEditor_method_stop_operation>`\n\nEnd a sculpting or painting operation. Commits any regions marked with :ref:`Terrain3DRegion.edited<class_Terrain3DRegion_property_edited>` in the undo/redo system and clears that flag.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dinstancer.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DInstancer.xml.\n\n.. _class_Terrain3DInstancer:\n\nTerrain3DInstancer\n==================\n\n**Inherits:** ``Object``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class places mesh instances defined in the Terrain3D asset dock into MultiMeshInstance3Ds on the ground.\n\nData is currently stored in :ref:`Terrain3DRegion.instances<class_Terrain3DRegion_property_instances>` and loaded into MultiMeshInstances, which are attached to the scene tree and managed by this class.\n\n\\ **The methods available for adding instances are:**\\ \n\n- :ref:`add_transforms()<class_Terrain3DInstancer_method_add_transforms>` - Accepts your list of transforms and parses them by region and cell location and stores in our data storage. Recommended for general API instancing.\n\n- :ref:`add_multimesh()<class_Terrain3DInstancer_method_add_multimesh>` - Pulls the transforms out of your MultiMesh and calls add_transforms.\n\n- :ref:`add_instances()<class_Terrain3DInstancer_method_add_instances>` - A feature rich function designed for hand editing via Terrain3DEditor.\n\n- Creating your own instance data and inserting it directly into :ref:`Terrain3DRegion.instances<class_Terrain3DRegion_property_instances>`. It's not difficult to do this in GDScript, but a thorough understanding of the C++ code in this class is recommended.\n\n\\ **The methods available for removing instances are:**\\ \n\n- :ref:`remove_instances()<class_Terrain3DInstancer_method_remove_instances>` - Like add_instances, this is can be used procedurally but is designed for hand editing.\n\n- :ref:`clear_by_mesh()<class_Terrain3DInstancer_method_clear_by_mesh>`, :ref:`clear_by_location()<class_Terrain3DInstancer_method_clear_by_location>` - To erase large sections of instances.\n\n- Editing :ref:`Terrain3DRegion.instances<class_Terrain3DRegion_property_instances>` directly.\n\nAfter modifying region data, run :ref:`update_mmis()<class_Terrain3DInstancer_method_update_mmis>` to rebuild the MultiMeshInstance3Ds.\n\n\\ **Read More:**\\ \n\n- **Tutorial:** `Foliage Instancing <https://terrain3d.readthedocs.io/en/stable/docs/instancer.html>`__\\ \n\n- **Godot Reference:** `MultiMesh <https://docs.godotengine.org/en/stable/classes/class_multimesh.html>`__\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +-------------------------------------------------------------+-----------------------------------------------------+-------+\n   | :ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` | :ref:`mode<class_Terrain3DInstancer_property_mode>` | ``1`` |\n   +-------------------------------------------------------------+-----------------------------------------------------+-------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`add_instances<class_Terrain3DInstancer_method_add_instances>`\\ (\\ global_position\\: ``Vector3``, params\\: ``Dictionary``\\ )                                                                                                                                        |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`add_multimesh<class_Terrain3DInstancer_method_add_multimesh>`\\ (\\ mesh_id\\: ``int``, multimesh\\: ``MultiMesh``, transform\\: ``Transform3D`` = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0), update\\: ``bool`` = true\\ )                                         |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`add_transforms<class_Terrain3DInstancer_method_add_transforms>`\\ (\\ mesh_id\\: ``int``, transforms\\: :ref:`Array<class_Array>`\\[``Transform3D``\\], colors\\: ``PackedColorArray`` = PackedColorArray(), update\\: ``bool`` = true\\ )                                  |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`append_location<class_Terrain3DInstancer_method_append_location>`\\ (\\ region_location\\: ``Vector2i``, mesh_id\\: ``int``, transforms\\: :ref:`Array<class_Array>`\\[``Transform3D``\\], colors\\: ``PackedColorArray``, update\\: ``bool`` = true\\ )                     |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`append_region<class_Terrain3DInstancer_method_append_region>`\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, mesh_id\\: ``int``, transforms\\: :ref:`Array<class_Array>`\\[``Transform3D``\\], colors\\: ``PackedColorArray``, update\\: ``bool`` = true\\ ) |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`clear_by_location<class_Terrain3DInstancer_method_clear_by_location>`\\ (\\ region_location\\: ``Vector2i``, mesh_id\\: ``int``\\ )                                                                                                                                     |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`clear_by_mesh<class_Terrain3DInstancer_method_clear_by_mesh>`\\ (\\ mesh_id\\: ``int``\\ )                                                                                                                                                                             |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`clear_by_region<class_Terrain3DInstancer_method_clear_by_region>`\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, mesh_id\\: ``int``\\ )                                                                                                                 |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``  | :ref:`get_closest_mesh_id<class_Terrain3DInstancer_method_get_closest_mesh_id>`\\ (\\ global_position\\: ``Vector3``\\ ) |const|                                                                                                                                             |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool`` | :ref:`is_enabled<class_Terrain3DInstancer_method_is_enabled>`\\ (\\ ) |const|                                                                                                                                                                                              |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`remove_instances<class_Terrain3DInstancer_method_remove_instances>`\\ (\\ global_position\\: ``Vector3``, params\\: ``Dictionary``\\ )                                                                                                                                  |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`swap_ids<class_Terrain3DInstancer_method_swap_ids>`\\ (\\ src_id\\: ``int``, dest_id\\: ``int``\\ )                                                                                                                                                                     |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`update_mmis<class_Terrain3DInstancer_method_update_mmis>`\\ (\\ mesh_id\\: ``int`` = -1, region_location\\: ``Vector2i`` = Vector2i(2147483647, 2147483647), rebuild_all\\: ``bool`` = false\\ )                                                                         |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|   | :ref:`update_transforms<class_Terrain3DInstancer_method_update_transforms>`\\ (\\ aabb\\: ``AABB``\\ )                                                                                                                                                                       |\n   +----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DInstancer_InstancerMode:\n\n.. rst-class:: classref-enumeration\n\nenum **InstancerMode**: :ref:`🔗<enum_Terrain3DInstancer_InstancerMode>`\n\n.. _class_Terrain3DInstancer_constant_NORMAL:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` **NORMAL** = ``1``\n\nCreate MultiMeshInstance3Ds and render instances as normal.\n\n.. _class_Terrain3DInstancer_constant_DISABLED:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` **DISABLED** = ``0``\n\nDisables creation of MultiMeshInstance3Ds and instances.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DInstancer_property_mode:\n\n.. rst-class:: classref-property\n\n:ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` **mode** = ``1`` :ref:`🔗<class_Terrain3DInstancer_property_mode>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_mode**\\ (\\ value\\: :ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>`\\ )\n- :ref:`InstancerMode<enum_Terrain3DInstancer_InstancerMode>` **get_mode**\\ (\\ )\n\nNormal - Generates MMIs and renders  all instances as normal.\n\nDisabled - prevents the instancer from creating any MultiMeshInstance3Ds.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DInstancer_method_add_instances:\n\n.. rst-class:: classref-method\n\n|void| **add_instances**\\ (\\ global_position\\: ``Vector3``, params\\: ``Dictionary``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_add_instances>`\n\nUsed by Terrain3DEditor to place instances given many brush parameters. In addition to the brush position, it also uses the following parameters: asset_id:int, size:float, strength:float, fixed_scale:float, random_scale:float, fixed_spin:float, random_spin:float, fixed_tilt:float, random_tilt:float, align_to_normal:bool, height_offset:float, random_height:float, vertex_color:Color, random_hue:float, random_darken:float, slope:Vector2, on_collision:bool, raycast_height:float. All of these settings are set in the editor via tool_settings.gd.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_add_multimesh:\n\n.. rst-class:: classref-method\n\n|void| **add_multimesh**\\ (\\ mesh_id\\: ``int``, multimesh\\: ``MultiMesh``, transform\\: ``Transform3D`` = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0), update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DInstancer_method_add_multimesh>`\n\nAllows procedural placement of meshes, or importing from another MultiMeshInstancer placement tool. The specified mesh_id should already be setup as a :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` in the asset dock. This function extracts the instance transforms and colors from a multimesh and passes it to :ref:`add_transforms()<class_Terrain3DInstancer_method_add_transforms>`.\n\nUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_add_transforms:\n\n.. rst-class:: classref-method\n\n|void| **add_transforms**\\ (\\ mesh_id\\: ``int``, transforms\\: :ref:`Array<class_Array>`\\[``Transform3D``\\], colors\\: ``PackedColorArray`` = PackedColorArray(), update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DInstancer_method_add_transforms>`\n\nAllows procedural placement of meshes. The mesh_id should already be setup as a :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` in the asset dock. You provide the array of Transform3Ds and optional Colors, which will be parsed into our data storage.\n\nThis function adds the :ref:`Terrain3DMeshAsset.height_offset<class_Terrain3DMeshAsset_property_height_offset>` to the transform along its local Y axis.\n\nUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_append_location:\n\n.. rst-class:: classref-method\n\n|void| **append_location**\\ (\\ region_location\\: ``Vector2i``, mesh_id\\: ``int``, transforms\\: :ref:`Array<class_Array>`\\[``Transform3D``\\], colors\\: ``PackedColorArray``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DInstancer_method_append_location>`\n\nAppends new transforms to the existing data within a region location. The mesh_id should already be setup as a :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` in the asset dock.\n\nUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_append_region:\n\n.. rst-class:: classref-method\n\n|void| **append_region**\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, mesh_id\\: ``int``, transforms\\: :ref:`Array<class_Array>`\\[``Transform3D``\\], colors\\: ``PackedColorArray``, update\\: ``bool`` = true\\ ) :ref:`🔗<class_Terrain3DInstancer_method_append_region>`\n\nAppends new transforms to the existing data within a region location. The mesh_id should already be setup as a :ref:`Terrain3DMeshAsset<class_Terrain3DMeshAsset>` in the asset dock.\n\nUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_clear_by_location:\n\n.. rst-class:: classref-method\n\n|void| **clear_by_location**\\ (\\ region_location\\: ``Vector2i``, mesh_id\\: ``int``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_clear_by_location>`\n\nRemoves all instancer data and MultiMeshInstance nodes attached to the tree for the specified region location and mesh id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_clear_by_mesh:\n\n.. rst-class:: classref-method\n\n|void| **clear_by_mesh**\\ (\\ mesh_id\\: ``int``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_clear_by_mesh>`\n\nRemoves all instancer data and MultiMeshInstance nodes attached to the tree for all regions for the specified mesh id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_clear_by_region:\n\n.. rst-class:: classref-method\n\n|void| **clear_by_region**\\ (\\ region\\: :ref:`Terrain3DRegion<class_Terrain3DRegion>`, mesh_id\\: ``int``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_clear_by_region>`\n\nRemoves all instancer data and MultiMeshInstance nodes attached to the tree for the specified region and mesh id.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_get_closest_mesh_id:\n\n.. rst-class:: classref-method\n\n``int`` **get_closest_mesh_id**\\ (\\ global_position\\: ``Vector3``\\ ) |const| :ref:`🔗<class_Terrain3DInstancer_method_get_closest_mesh_id>`\n\nReturns the mesh instance ID closest to the specified global position on the ground.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_is_enabled:\n\n.. rst-class:: classref-method\n\n``bool`` **is_enabled**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DInstancer_method_is_enabled>`\n\nReturns true if :ref:`mode<class_Terrain3DInstancer_property_mode>` is not disabled.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_remove_instances:\n\n.. rst-class:: classref-method\n\n|void| **remove_instances**\\ (\\ global_position\\: ``Vector3``, params\\: ``Dictionary``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_remove_instances>`\n\nUses parameters asset_id:int, size:float, strength:float, slope:Vector2, on_collision:bool, raycast_height:float to randomly remove instances within the indicated brush position and size.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_swap_ids:\n\n.. rst-class:: classref-method\n\n|void| **swap_ids**\\ (\\ src_id\\: ``int``, dest_id\\: ``int``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_swap_ids>`\n\nSwaps the ID of two meshes without changing the mesh instances on the ground.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_update_mmis:\n\n.. rst-class:: classref-method\n\n|void| **update_mmis**\\ (\\ mesh_id\\: ``int`` = -1, region_location\\: ``Vector2i`` = Vector2i(2147483647, 2147483647), rebuild_all\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DInstancer_method_update_mmis>`\n\nQueues a request to rebuild the MMIs for the specified IDs and regions on the next RenderingServer.frame_pre_draw signal. This is safe to call multiple times per frame and it will de-duplicate and do the most general requests. So if you first call it for region (0,0), mesh 52, then later in the frame ask for all regions, mesh 52, it will do only the latter.\n\n- mesh_id - rebuild MMIs for this mesh id. Use `-1` for all IDs.\n\n- region_location - rebuild MMIs for this region. Use `Vector2i.MAX` for all regions.\n\n- rebuild_all - destroy all MMIs first before rebuilding.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DInstancer_method_update_transforms:\n\n.. rst-class:: classref-method\n\n|void| **update_transforms**\\ (\\ aabb\\: ``AABB``\\ ) :ref:`🔗<class_Terrain3DInstancer_method_update_transforms>`\n\nReviews all existing instance transforms within an AABB and adjusts their heights to match the terrain.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dmaterial.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DMaterial.xml.\n\n.. _class_Terrain3DMaterial:\n\nTerrain3DMaterial\n=================\n\n**Inherits:** ``Resource``\n\nA custom shader material resource for Terrain3D.\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class handles options for both the built-in shader and any custom override shader. It collects compiled texture data from the other classes and sends all of it to the shader via the RenderingServer.\n\nIt is a savable resource, so you can save it to disk and use the same material settings in multiple scenes that use Terrain3D. The amount of data is small, assuming you have saved your shader parameter textures to disk, so it can be saved as a git-friendly, text based .tres file or left within the scene file.\n\nWhile it does mimic some of the functionality of ShaderMaterial, it does not derive from any of the Godot Material classes. It will fail any ``is Material`` checks. It is a ``Resource``.\n\nInspector settings above `Custom Shader` and :ref:`shader_override<class_Terrain3DMaterial_property_shader_override>` are used to determine what code is used in the current shader. Inspector settings in `Shader Uniforms` are the public uniforms (not prefaced with `\\_`) available in the current shader.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``Dictionary``                                                   | :ref:`_shader_parameters<class_Terrain3DMaterial_property__shader_parameters>`                         | ``{}``    |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`auto_shader_enabled<class_Terrain3DMaterial_property_auto_shader_enabled>`                       | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``Shader``                                                       | :ref:`buffer_shader_override<class_Terrain3DMaterial_property_buffer_shader_override>`                 |           |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`buffer_shader_override_enabled<class_Terrain3DMaterial_property_buffer_shader_override_enabled>` | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``float``                                                        | :ref:`displacement_scale<class_Terrain3DMaterial_property_displacement_scale>`                         | ``1.0``   |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``float``                                                        | :ref:`displacement_sharpness<class_Terrain3DMaterial_property_displacement_sharpness>`                 | ``0.5``   |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`dual_scaling_enabled<class_Terrain3DMaterial_property_dual_scaling_enabled>`                     | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`macro_variation_enabled<class_Terrain3DMaterial_property_macro_variation_enabled>`               | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`output_albedo<class_Terrain3DMaterial_property_output_albedo>`                                   | ``true``  |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`output_ambient_occlusion<class_Terrain3DMaterial_property_output_ambient_occlusion>`             | ``true``  |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`output_normal_map<class_Terrain3DMaterial_property_output_normal_map>`                           | ``true``  |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`output_roughness<class_Terrain3DMaterial_property_output_roughness>`                             | ``true``  |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`projection_enabled<class_Terrain3DMaterial_property_projection_enabled>`                         | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``Shader``                                                       | :ref:`shader_override<class_Terrain3DMaterial_property_shader_override>`                               |           |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`shader_override_enabled<class_Terrain3DMaterial_property_shader_override_enabled>`               | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_autoshader<class_Terrain3DMaterial_property_show_autoshader>`                               | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_checkered<class_Terrain3DMaterial_property_show_checkered>`                                 | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_colormap<class_Terrain3DMaterial_property_show_colormap>`                                   | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_contours<class_Terrain3DMaterial_property_show_contours>`                                   | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_control_angle<class_Terrain3DMaterial_property_show_control_angle>`                         | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_control_blend<class_Terrain3DMaterial_property_show_control_blend>`                         | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_control_scale<class_Terrain3DMaterial_property_show_control_scale>`                         | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_control_texture<class_Terrain3DMaterial_property_show_control_texture>`                     | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_displacement_buffer<class_Terrain3DMaterial_property_show_displacement_buffer>`             | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_grey<class_Terrain3DMaterial_property_show_grey>`                                           | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_heightmap<class_Terrain3DMaterial_property_show_heightmap>`                                 | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_instancer_grid<class_Terrain3DMaterial_property_show_instancer_grid>`                       | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_jaggedness<class_Terrain3DMaterial_property_show_jaggedness>`                               | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_navigation<class_Terrain3DMaterial_property_show_navigation>`                               | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_region_grid<class_Terrain3DMaterial_property_show_region_grid>`                             | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_roughmap<class_Terrain3DMaterial_property_show_roughmap>`                                   | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_texture_albedo<class_Terrain3DMaterial_property_show_texture_albedo>`                       | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_texture_ao<class_Terrain3DMaterial_property_show_texture_ao>`                               | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_texture_height<class_Terrain3DMaterial_property_show_texture_height>`                       | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_texture_normal<class_Terrain3DMaterial_property_show_texture_normal>`                       | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_texture_rough<class_Terrain3DMaterial_property_show_texture_rough>`                         | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | ``bool``                                                         | :ref:`show_vertex_grid<class_Terrain3DMaterial_property_show_vertex_grid>`                             | ``false`` |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | :ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` | :ref:`texture_filtering<class_Terrain3DMaterial_property_texture_filtering>`                           | ``0``     |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n   | :ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>`   | :ref:`world_background<class_Terrain3DMaterial_property_world_background>`                             | ``1``     |\n   +------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------+-----------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``     | :ref:`get_buffer_material_rid<class_Terrain3DMaterial_method_get_buffer_material_rid>`\\ (\\ ) |const|                       |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``     | :ref:`get_buffer_shader_rid<class_Terrain3DMaterial_method_get_buffer_shader_rid>`\\ (\\ ) |const|                           |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``     | :ref:`get_material_rid<class_Terrain3DMaterial_method_get_material_rid>`\\ (\\ ) |const|                                     |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | ``Variant`` | :ref:`get_shader_param<class_Terrain3DMaterial_method_get_shader_param>`\\ (\\ name\\: ``StringName``\\ ) |const|              |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | ``RID``     | :ref:`get_shader_rid<class_Terrain3DMaterial_method_get_shader_rid>`\\ (\\ ) |const|                                         |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | Error       | :ref:`save<class_Terrain3DMaterial_method_save>`\\ (\\ path\\: ``String`` = \"\"\\ )                                             |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | |void|      | :ref:`set_shader_param<class_Terrain3DMaterial_method_set_shader_param>`\\ (\\ name\\: ``StringName``, value\\: ``Variant``\\ ) |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n   | |void|      | :ref:`update<class_Terrain3DMaterial_method_update>`\\ (\\ flags\\: ``int`` = 0\\ )                                            |\n   +-------------+----------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DMaterial_WorldBackground:\n\n.. rst-class:: classref-enumeration\n\nenum **WorldBackground**: :ref:`🔗<enum_Terrain3DMaterial_WorldBackground>`\n\n.. _class_Terrain3DMaterial_constant_NONE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>` **NONE** = ``0``\n\nOutside of the defined regions, hide the mesh.\n\n.. _class_Terrain3DMaterial_constant_FLAT:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>` **FLAT** = ``1``\n\nOutside of the defined regions, show a flat terrain.\n\n.. _class_Terrain3DMaterial_constant_NOISE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>` **NOISE** = ``2``\n\nOutside of the defined regions, generate visual-only hills.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _enum_Terrain3DMaterial_TextureFiltering:\n\n.. rst-class:: classref-enumeration\n\nenum **TextureFiltering**: :ref:`🔗<enum_Terrain3DMaterial_TextureFiltering>`\n\n.. _class_Terrain3DMaterial_constant_LINEAR_ANISOTROPIC:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` **LINEAR_ANISOTROPIC** = ``0``\n\nTextures are filtered using a blend of 4 adjacent pixels, with anisotropic filtering which improves the sharpness of distant terrain off axis from the camera. Use this for most cases for high quality renders.\n\n.. _class_Terrain3DMaterial_constant_LINEAR:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` **LINEAR** = ``1``\n\nTextures are filtered using a blend of 4 adjacent pixels. Use this for most cases for high quality renders.\n\n.. _class_Terrain3DMaterial_constant_NEAREST_ANISOTROPIC:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` **NEAREST_ANISOTROPIC** = ``2``\n\nTextures are filtered using the nearest pixel only with anisotropic filtering which improves the sharpness of distant terrain off axis from the camera. It is faster than LINEAR, but the texture will look pixelated. Use this for a low-poly look, with a very low uv_scale.\n\n.. _class_Terrain3DMaterial_constant_NEAREST:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` **NEAREST** = ``3``\n\nTextures are filtered using the nearest pixel only. It is faster than LINEAR, but the texture will look pixelated. Use this for a low-poly look, with a very low uv_scale.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _enum_Terrain3DMaterial_UpdateFlags:\n\n.. rst-class:: classref-enumeration\n\nenum **UpdateFlags**: :ref:`🔗<enum_Terrain3DMaterial_UpdateFlags>`\n\n.. _class_Terrain3DMaterial_constant_UNIFORMS_ONLY:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`UpdateFlags<enum_Terrain3DMaterial_UpdateFlags>` **UNIFORMS_ONLY** = ``0``\n\nNon-texture array values are assigned to the shader. This is the default and is always done.\n\n.. _class_Terrain3DMaterial_constant_TEXTURE_ARRAYS:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`UpdateFlags<enum_Terrain3DMaterial_UpdateFlags>` **TEXTURE_ARRAYS** = ``1``\n\nThe ground texture arrays are assigned to the shader, along with the values in `UNIFORMS_ONLY`.\n\n.. _class_Terrain3DMaterial_constant_REGION_ARRAYS:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`UpdateFlags<enum_Terrain3DMaterial_UpdateFlags>` **REGION_ARRAYS** = ``2``\n\nThe region data texture arrays are assigned to the shader, along with the values in `UNIFORMS_ONLY`.\n\n.. _class_Terrain3DMaterial_constant_UPDATE_ARRAYS:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`UpdateFlags<enum_Terrain3DMaterial_UpdateFlags>` **UPDATE_ARRAYS** = ``3``\n\nValues in `TEXTURE_ARRAYS` and `REGION_ARRAYS` are assigned to the shader.\n\n.. _class_Terrain3DMaterial_constant_FULL_REBUILD:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`UpdateFlags<enum_Terrain3DMaterial_UpdateFlags>` **FULL_REBUILD** = ``7``\n\nThe shader is rebuilt, then all values in `UPDATE_ARRAYS` are assigned to the shader.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DMaterial_property__shader_parameters:\n\n.. rst-class:: classref-property\n\n``Dictionary`` **_shader_parameters** = ``{}`` :ref:`🔗<class_Terrain3DMaterial_property__shader_parameters>`\n\nThis private dictionary stores all of the shader parameters in the resource. It is not a cache.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_auto_shader_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **auto_shader_enabled** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_auto_shader_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_auto_shader_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_auto_shader_enabled**\\ (\\ )\n\nEnables selecting two texture IDs that will automatically be applied to the terrain based upon slope.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_buffer_shader_override:\n\n.. rst-class:: classref-property\n\n``Shader`` **buffer_shader_override** :ref:`🔗<class_Terrain3DMaterial_property_buffer_shader_override>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_buffer_shader_override**\\ (\\ value\\: ``Shader``\\ )\n- ``Shader`` **get_buffer_shader_override**\\ (\\ )\n\nIf buffer_shader_override_enabled is true and this Shader is valid, the displacement buffer material will use this custom shader code. If this is blank when you enable the override, the system will generate a shader with the current settings. A visual shader will also work here. However we only generate a text based shader so currently a visual shader needs to be constructed with the base code before it can work.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_buffer_shader_override_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **buffer_shader_override_enabled** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_buffer_shader_override_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_buffer_shader_override_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_buffer_shader_override_enabled**\\ (\\ )\n\nEnables use of the :ref:`buffer_shader_override<class_Terrain3DMaterial_property_buffer_shader_override>` shader code. Generates default code if shader_override is blank.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_displacement_scale:\n\n.. rst-class:: classref-property\n\n``float`` **displacement_scale** = ``1.0`` :ref:`🔗<class_Terrain3DMaterial_property_displacement_scale>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_displacement_scale**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_displacement_scale**\\ (\\ )\n\nA global multiplier for all displaced textures. This is the maximum distance that 2 adjacent verticies can be vertically seperated by. Setting this 1.0 would mean a maximum of + 0.5m, and -0.5m deviation from the collision mesh.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_displacement_sharpness:\n\n.. rst-class:: classref-property\n\n``float`` **displacement_sharpness** = ``0.5`` :ref:`🔗<class_Terrain3DMaterial_property_displacement_sharpness>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_displacement_sharpness**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_displacement_sharpness**\\ (\\ )\n\nAdjusts the transition between textures. When set at `1.0`, the blending of displacment between textures will match the aldebo/normal blend sharpness exactly. Lower values will have a softer transition, avoiding harsh shapes, without compromising the abldeo and normal blend sharpness. If set at or very near to `0.0`, it is possible that more displaced textures can affect less displaced textures at low blend values even if not visible.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_dual_scaling_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **dual_scaling_enabled** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_dual_scaling_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_dual_scaling_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_dual_scaling_enabled**\\ (\\ )\n\nEnables selecting one texture ID that will have multiple scales applied based upon camera distance. Use it for something like a rock texture so up close it will be nicely detailed, and far away mountains can be covered in the same rock texture without looking tiled. The two blend together at a specified distance.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_macro_variation_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **macro_variation_enabled** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_macro_variation_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_macro_variation_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_macro_variation_enabled**\\ (\\ )\n\nAllows you to add a couple of noise patterns at different scales and colors to add variation to your terrain to avoid tiled textures.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_output_albedo:\n\n.. rst-class:: classref-property\n\n``bool`` **output_albedo** = ``true`` :ref:`🔗<class_Terrain3DMaterial_property_output_albedo>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_output_albedo_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_output_albedo_enabled**\\ (\\ )\n\nEnables the Albedo, aka Base Color or Diffuse, output channel in the shader.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_output_ambient_occlusion:\n\n.. rst-class:: classref-property\n\n``bool`` **output_ambient_occlusion** = ``true`` :ref:`🔗<class_Terrain3DMaterial_property_output_ambient_occlusion>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_output_ambient_occlusion_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_output_ambient_occlusion_enabled**\\ (\\ )\n\nEnables the Ambient Occlusion output channel in the shader.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_output_normal_map:\n\n.. rst-class:: classref-property\n\n``bool`` **output_normal_map** = ``true`` :ref:`🔗<class_Terrain3DMaterial_property_output_normal_map>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_output_normal_map_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_output_normal_map_enabled**\\ (\\ )\n\nEnables the Normal Map output channel in the shader.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_output_roughness:\n\n.. rst-class:: classref-property\n\n``bool`` **output_roughness** = ``true`` :ref:`🔗<class_Terrain3DMaterial_property_output_roughness>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_output_roughness_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_output_roughness_enabled**\\ (\\ )\n\nEnables the Roughness output channel in the shader.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_projection_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **projection_enabled** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_projection_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_projection_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_projection_enabled**\\ (\\ )\n\nEnables textures to be projected vertically when placed on slopes above 45 degrees. This is useful for mapping textures on cliff faces without stretching, even though the polygons are stretched.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_shader_override:\n\n.. rst-class:: classref-property\n\n``Shader`` **shader_override** :ref:`🔗<class_Terrain3DMaterial_property_shader_override>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_shader_override**\\ (\\ value\\: ``Shader``\\ )\n- ``Shader`` **get_shader_override**\\ (\\ )\n\nIf shader_override_enabled is true and this Shader is valid, the material will use this custom shader code. If this is blank when you enable the override, the system will generate a shader with the current settings. So if you have a debug view enabled, the generated shader will have all of that code. A visual shader will also work here. However we only generate a text based shader so currently a visual shader needs to be constructed with the base code before it can work.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_shader_override_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **shader_override_enabled** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_shader_override_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_shader_override_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_shader_override_enabled**\\ (\\ )\n\nEnables using the :ref:`shader_override<class_Terrain3DMaterial_property_shader_override>` shader. An editable shader is generated from the current one if shader_override is blank.\n\nThe inspector settings above this group determine the code that is used in the current shader. The settings below are uniforms for the current shader.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_autoshader:\n\n.. rst-class:: classref-property\n\n``bool`` **show_autoshader** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_autoshader>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_autoshader**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_autoshader**\\ (\\ )\n\nDisplays the area designated for use by the autoshader, which shows materials based upon slope.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_checkered:\n\n.. rst-class:: classref-property\n\n``bool`` **show_checkered** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_checkered>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_checkered**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_checkered**\\ (\\ )\n\nShows a checkerboard display using a shader rendered pattern. This is turned on if the Texture List is empty.\n\nNote that when a blank texture slot is created, a 1k checkerboard texture is generated and stored in the texture slot. That takes VRAM. The two patterns have a slightly different scale.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_colormap:\n\n.. rst-class:: classref-property\n\n``bool`` **show_colormap** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_colormap>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_colormap**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_colormap**\\ (\\ )\n\nShows the color map in the albedo channel.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_contours:\n\n.. rst-class:: classref-property\n\n``bool`` **show_contours** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_contours>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_contours**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_contours**\\ (\\ )\n\nOverlays contour lines on the terrain. Customize the options in the material when enabled. Press `4` with the mouse in the viewport to toggle.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_control_angle:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_angle** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_control_angle>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_angle**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_angle**\\ (\\ )\n\nAlbedo shows the painted angle. Orange means 0°, Yellow 270°, Cyan 180°, Violet 90°. Or warm colors towards -Z, cool colors +Z, greens/yellows +X, reds/blues -X. Draw all angles coming from the center of a circle for a better understanding.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_control_blend:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_blend** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_control_blend>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_blend**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_blend**\\ (\\ )\n\nDisplays the values used to blend the textures. Blue shows the autoshader blending, red shows manually painted blending.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_control_scale:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_scale** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_control_scale>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_scale**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_scale**\\ (\\ )\n\nAlbedo shows the painted scale. Larger scales are more red, smaller scales are more blue. 0.5 middle grey is the default 100% scale.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_control_texture:\n\n.. rst-class:: classref-property\n\n``bool`` **show_control_texture** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_control_texture>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_control_texture**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_control_texture**\\ (\\ )\n\nAlbedo shows the base and overlay texture indices defined by the control map. Red pixels indicate the base texture, with brightness showing texture ids 0 to 31. Green pixels indicate the overlay texture. Yellow indicates both.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_displacement_buffer:\n\n.. rst-class:: classref-property\n\n``bool`` **show_displacement_buffer** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_displacement_buffer>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_displacement_buffer**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_displacement_buffer**\\ (\\ )\n\nShows the resulting displacement buffer vertex differential from 0. Black matches collision exactly. Green shows extrusions, Red for depressions into the terrain.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_grey:\n\n.. rst-class:: classref-property\n\n``bool`` **show_grey** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_grey>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_grey**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_grey**\\ (\\ )\n\nAlbedo is set to 0.2 grey.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_heightmap:\n\n.. rst-class:: classref-property\n\n``bool`` **show_heightmap** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_heightmap>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_heightmap**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_heightmap**\\ (\\ )\n\nAlbedo is a white to black gradient depending on height. The gradient is scaled to a height of 300, so above that or far below 0 will be all white or black.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_instancer_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_instancer_grid** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_instancer_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_instancer_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_instancer_grid**\\ (\\ )\n\nOverlays the 32x32m instancer grid on the terrain, which shows how the instancer data is partitioned. Press `2` with the mouse in the viewport to toggle.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_jaggedness:\n\n.. rst-class:: classref-property\n\n``bool`` **show_jaggedness** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_jaggedness>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_jaggedness**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_jaggedness**\\ (\\ )\n\nHighlights non-smooth areas of the terrain. Jagged peaks, troughs, or edges that are a bit rough with sharp angles between vertices.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_navigation:\n\n.. rst-class:: classref-property\n\n``bool`` **show_navigation** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_navigation>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_navigation**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_navigation**\\ (\\ )\n\nDisplays the area designated for generating the navigation mesh.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_region_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_region_grid** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_region_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_region_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_region_grid**\\ (\\ )\n\nOverlays the region grid on the terrain. This is more accurate than the region grid gizmo for determining where the region border is when editing. Press `1` with the mouse in the viewport to toggle.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_roughmap:\n\n.. rst-class:: classref-property\n\n``bool`` **show_roughmap** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_roughmap>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_roughmap**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_roughmap**\\ (\\ )\n\nAlbedo is set to the roughness modification map as grey scale. Middle grey, 0.5 means no roughness modification. Black would be high gloss while white is very rough.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_texture_albedo:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_albedo** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_texture_albedo>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_albedo**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_albedo**\\ (\\ )\n\nAlbedo textures are shown only. Other channels are excluded.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_texture_ao:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_ao** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_texture_ao>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_ao**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_ao**\\ (\\ )\n\nAlbedo is set to the painted Ambient Occlusion textures.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_texture_height:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_height** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_texture_height>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_height**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_height**\\ (\\ )\n\nAlbedo is set to the painted Height textures.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_texture_normal:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_normal** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_texture_normal>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_normal**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_normal**\\ (\\ )\n\nAlbedo is set to the painted Normal textures.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_texture_rough:\n\n.. rst-class:: classref-property\n\n``bool`` **show_texture_rough** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_texture_rough>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_texture_rough**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_texture_rough**\\ (\\ )\n\nAlbedo is set to the painted Roughness textures. This is different from the roughness modification map above.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_show_vertex_grid:\n\n.. rst-class:: classref-property\n\n``bool`` **show_vertex_grid** = ``false`` :ref:`🔗<class_Terrain3DMaterial_property_show_vertex_grid>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_show_vertex_grid**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **get_show_vertex_grid**\\ (\\ )\n\nOverlays the vertex grid on the terrain, showing where each vertex is. Press `3` with the mouse in the viewport to toggle.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_texture_filtering:\n\n.. rst-class:: classref-property\n\n:ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` **texture_filtering** = ``0`` :ref:`🔗<class_Terrain3DMaterial_property_texture_filtering>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_texture_filtering**\\ (\\ value\\: :ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>`\\ )\n- :ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` **get_texture_filtering**\\ (\\ )\n\nSets how the renderer should filter textures. See :ref:`TextureFiltering<enum_Terrain3DMaterial_TextureFiltering>` for options.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_property_world_background:\n\n.. rst-class:: classref-property\n\n:ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>` **world_background** = ``1`` :ref:`🔗<class_Terrain3DMaterial_property_world_background>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_world_background**\\ (\\ value\\: :ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>`\\ )\n- :ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>` **get_world_background**\\ (\\ )\n\nSets how the mesh outside of defined regions behave. See :ref:`WorldBackground<enum_Terrain3DMaterial_WorldBackground>` for options.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DMaterial_method_get_buffer_material_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_buffer_material_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMaterial_method_get_buffer_material_rid>`\n\nReturns the RID of the displacement buffer material used with the Rendering Server. This is set per instance of this class.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_get_buffer_shader_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_buffer_shader_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMaterial_method_get_buffer_shader_rid>`\n\nReturns the RID of the displacement buffer shader used with the Rendering Server.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_get_material_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_material_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMaterial_method_get_material_rid>`\n\nReturns the RID of the material used with the Rendering Server. This is set per instance of this class.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_get_shader_param:\n\n.. rst-class:: classref-method\n\n``Variant`` **get_shader_param**\\ (\\ name\\: ``StringName``\\ ) |const| :ref:`🔗<class_Terrain3DMaterial_method_get_shader_param>`\n\nRetrieve a parameter from the active shader (built-in or override shader).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_get_shader_rid:\n\n.. rst-class:: classref-method\n\n``RID`` **get_shader_rid**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMaterial_method_get_shader_rid>`\n\nReturns the RID of the built in shader used with the Rendering Server. This is different from any shader override which has its own RID.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_save:\n\n.. rst-class:: classref-method\n\nError **save**\\ (\\ path\\: ``String`` = \"\"\\ ) :ref:`🔗<class_Terrain3DMaterial_method_save>`\n\nSaves this material resource to disk, if saved as an external ``.tres`` or ``.res`` resource file.\n\npath - specifies a directory and file name to use from now on.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_set_shader_param:\n\n.. rst-class:: classref-method\n\n|void| **set_shader_param**\\ (\\ name\\: ``StringName``, value\\: ``Variant``\\ ) :ref:`🔗<class_Terrain3DMaterial_method_set_shader_param>`\n\nSet a parameter in the active shader (built-in or override shader).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMaterial_method_update:\n\n.. rst-class:: classref-method\n\n|void| **update**\\ (\\ flags\\: ``int`` = 0\\ ) :ref:`🔗<class_Terrain3DMaterial_method_update>`\n\nSends uniform values to the shader. See :ref:`UpdateFlags<enum_Terrain3DMaterial_UpdateFlags>` for options.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dmeshasset.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DMeshAsset.xml.\n\n.. _class_Terrain3DMeshAsset:\n\nTerrain3DMeshAsset\n==================\n\n**Inherits:** ``Resource``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class manages meshes used for instancing. There are two broad types of meshes it can host.\n\n1. Generated Texture Card - this class will generate a QuadMesh. The typical use for this is to create a material in the override material, place a 2D grass texture in the `albedo texture` slot, and enable alpha scissor. This will generate low poly grass.\n\n2. Scene File - you can provide your own mesh in a scene file, which is specifically a PackedScene (.tscn, .scn, .glb, .fbx, etc). You can override the material if desired. MultiMeshes only support one mesh object, so complex objects like tree trunks and leaves, or a door frame and door either need to be combined into one object with multiple materials, or placed by another method. The system will look for MeshInstance3D nodes in the file to use as Levels of Detail (LODs). Ideally they have suffixes like `LOD0`, `LOD1`. We support up to 10 LODs, but recommend no more than 4.\n\n\\ **Read More:**\\ \n\n- **Tutorial:** `Foliage Instancing <https://terrain3d.readthedocs.io/en/stable/docs/instancer.html>`__\\ \n\n- **Godot Reference:** `MultiMesh <https://docs.godotengine.org/en/stable/classes/class_multimesh.html>`__, `MultiMeshInstance3D <https://docs.godotengine.org/en/stable/classes/class_meshinstance3d.html#class-meshinstance3d>`__\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | RenderingServer.ShadowCastingSetting            | :ref:`cast_shadows<class_Terrain3DMeshAsset_property_cast_shadows>`           | ``1``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`density<class_Terrain3DMeshAsset_property_density>`                     | ``0.0``           |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``bool``                                        | :ref:`enabled<class_Terrain3DMeshAsset_property_enabled>`                     | ``true``          |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`fade_margin<class_Terrain3DMeshAsset_property_fade_margin>`             | ``0.0``           |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`generated_faces<class_Terrain3DMeshAsset_property_generated_faces>`     | ``2``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``Vector2``                                     | :ref:`generated_size<class_Terrain3DMeshAsset_property_generated_size>`       | ``Vector2(1, 1)`` |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | :ref:`GenType<enum_Terrain3DMeshAsset_GenType>` | :ref:`generated_type<class_Terrain3DMeshAsset_property_generated_type>`       | ``0``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`height_offset<class_Terrain3DMeshAsset_property_height_offset>`         | ``0.0``           |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`id<class_Terrain3DMeshAsset_property_id>`                               | ``0``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`last_lod<class_Terrain3DMeshAsset_property_last_lod>`                   | ``9``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`last_shadow_lod<class_Terrain3DMeshAsset_property_last_shadow_lod>`     | ``9``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`               | ``32.0``          |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod1_range<class_Terrain3DMeshAsset_property_lod1_range>`               | ``64.0``          |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod2_range<class_Terrain3DMeshAsset_property_lod2_range>`               | ``96.0``          |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod3_range<class_Terrain3DMeshAsset_property_lod3_range>`               | ``128.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod4_range<class_Terrain3DMeshAsset_property_lod4_range>`               | ``160.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod5_range<class_Terrain3DMeshAsset_property_lod5_range>`               | ``192.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod6_range<class_Terrain3DMeshAsset_property_lod6_range>`               | ``224.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod7_range<class_Terrain3DMeshAsset_property_lod7_range>`               | ``256.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod8_range<class_Terrain3DMeshAsset_property_lod8_range>`               | ``288.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``float``                                       | :ref:`lod9_range<class_Terrain3DMeshAsset_property_lod9_range>`               | ``320.0``         |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`lod_count<class_Terrain3DMeshAsset_property_lod_count>`                 | ``0``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``Material``                                    | :ref:`material_overlay<class_Terrain3DMeshAsset_property_material_overlay>`   |                   |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``Material``                                    | :ref:`material_override<class_Terrain3DMeshAsset_property_material_override>` |                   |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``String``                                      | :ref:`name<class_Terrain3DMeshAsset_property_name>`                           | ``\"New Mesh\"``    |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``PackedScene``                                 | :ref:`scene_file<class_Terrain3DMeshAsset_property_scene_file>`               |                   |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`shadow_impostor<class_Terrain3DMeshAsset_property_shadow_impostor>`     | ``0``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n   | ``int``                                         | :ref:`visibility_layers<class_Terrain3DMeshAsset_property_visibility_layers>` | ``1``             |\n   +-------------------------------------------------+-------------------------------------------------------------------------------+-------------------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | |void|        | :ref:`clear<class_Terrain3DMeshAsset_method_clear>`\\ (\\ )                                                      |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | ``Color``     | :ref:`get_highlight_color<class_Terrain3DMeshAsset_method_get_highlight_color>`\\ (\\ ) |const|                  |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | ``int``       | :ref:`get_instance_count<class_Terrain3DMeshAsset_method_get_instance_count>`\\ (\\ ) |const|                    |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | ``float``     | :ref:`get_lod_range<class_Terrain3DMeshAsset_method_get_lod_range>`\\ (\\ lod\\: ``int``\\ ) |const|               |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | ``Mesh``      | :ref:`get_mesh<class_Terrain3DMeshAsset_method_get_mesh>`\\ (\\ lod\\: ``int`` = 0\\ ) |const|                     |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | ``Texture2D`` | :ref:`get_thumbnail<class_Terrain3DMeshAsset_method_get_thumbnail>`\\ (\\ ) |const|                              |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | ``bool``      | :ref:`is_highlighted<class_Terrain3DMeshAsset_method_is_highlighted>`\\ (\\ ) |const|                            |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | |void|        | :ref:`set_highlighted<class_Terrain3DMeshAsset_method_set_highlighted>`\\ (\\ enabled\\: ``bool``\\ )              |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n   | |void|        | :ref:`set_lod_range<class_Terrain3DMeshAsset_method_set_lod_range>`\\ (\\ lod\\: ``int``, distance\\: ``float``\\ ) |\n   +---------------+----------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nSignals\n-------\n\n.. _class_Terrain3DMeshAsset_signal_id_changed:\n\n.. rst-class:: classref-signal\n\n**id_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DMeshAsset_signal_id_changed>`\n\nEmitted when :ref:`id<class_Terrain3DMeshAsset_property_id>` is changed.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_signal_instance_count_changed:\n\n.. rst-class:: classref-signal\n\n**instance_count_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DMeshAsset_signal_instance_count_changed>`\n\nEmitted when instances of this mesh asset have been added or removed.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_signal_instancer_setting_changed:\n\n.. rst-class:: classref-signal\n\n**instancer_setting_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DMeshAsset_signal_instancer_setting_changed>`\n\nEmitted when instancer specific settings are changed on this mesh asset, such as :ref:`cast_shadows<class_Terrain3DMeshAsset_property_cast_shadows>`, and triggers an instancer rebuild.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_signal_setting_changed:\n\n.. rst-class:: classref-signal\n\n**setting_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DMeshAsset_signal_setting_changed>`\n\nEmitted when settings are changed, other than those tracked by other signals.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DMeshAsset_GenType:\n\n.. rst-class:: classref-enumeration\n\nenum **GenType**: :ref:`🔗<enum_Terrain3DMeshAsset_GenType>`\n\n.. _class_Terrain3DMeshAsset_constant_TYPE_NONE:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`GenType<enum_Terrain3DMeshAsset_GenType>` **TYPE_NONE** = ``0``\n\nNo generated mesh is in use, likely because a scene file is used.\n\n.. _class_Terrain3DMeshAsset_constant_TYPE_TEXTURE_CARD:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`GenType<enum_Terrain3DMeshAsset_GenType>` **TYPE_TEXTURE_CARD** = ``1``\n\nGenerates a QuadMesh to be used as a texture card.\n\n.. _class_Terrain3DMeshAsset_constant_TYPE_MAX:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`GenType<enum_Terrain3DMeshAsset_GenType>` **TYPE_MAX** = ``2``\n\nMaximum value for this enum.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DMeshAsset_property_cast_shadows:\n\n.. rst-class:: classref-property\n\nRenderingServer.ShadowCastingSetting **cast_shadows** = ``1`` :ref:`🔗<class_Terrain3DMeshAsset_property_cast_shadows>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_cast_shadows**\\ (\\ value\\: RenderingServer.ShadowCastingSetting\\ )\n- RenderingServer.ShadowCastingSetting **get_cast_shadows**\\ (\\ )\n\nTells the renderer how to cast shadows from this mesh asset onto the terrain and other objects. This sets ``GeometryInstance3D.cast_shadow`` on all MultiMeshInstances used by this mesh.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_density:\n\n.. rst-class:: classref-property\n\n``float`` **density** = ``0.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_density>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_density**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_density**\\ (\\ )\n\nDensity is used to set the approximate default spacing between instances based on the size of the mesh. When painting meshes on the terrain, mesh density is multiplied by brush strength.\n\nThis value is not tied to any real world unit. It is calculated as ``10.f / mesh->get_aabb().get_volume()``, then clamped to a sane range. If the calculated amount is inappropriate, increase or decrease it here.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_enabled:\n\n.. rst-class:: classref-property\n\n``bool`` **enabled** = ``true`` :ref:`🔗<class_Terrain3DMeshAsset_property_enabled>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_enabled**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_enabled**\\ (\\ )\n\nIf enabled, MultiMeshInstance3Ds will be generated for this mesh asset.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_fade_margin:\n\n.. rst-class:: classref-property\n\n``float`` **fade_margin** = ``0.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_fade_margin>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_fade_margin**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_fade_margin**\\ (\\ )\n\nIf > 0, sets ``GeometryInstance3D.fade_mode = self``, sets this margin in ``GeometryInstance3D.visibility_range_begin_margin`` and ``GeometryInstance3D.visibility_range_end_margin`` and adjusts the ranges to allow fading between lods.\n\nLimited to the smaller of half the distance between LOD0 and LOD1, or 64m.\n\nCurrently broken and hidden in the inspector until `Godot issue #102799 <https://github.com/godotengine/godot/issues/102799>`__ is fixed.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_generated_faces:\n\n.. rst-class:: classref-property\n\n``int`` **generated_faces** = ``2`` :ref:`🔗<class_Terrain3DMeshAsset_property_generated_faces>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_generated_faces**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_generated_faces**\\ (\\ )\n\nSelect if you want the generated texture card to have a single QuadMesh, 2 meshes rotated 90° in a cross, or 3 rotated at 60°.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_generated_size:\n\n.. rst-class:: classref-property\n\n``Vector2`` **generated_size** = ``Vector2(1, 1)`` :ref:`🔗<class_Terrain3DMeshAsset_property_generated_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_generated_size**\\ (\\ value\\: ``Vector2``\\ )\n- ``Vector2`` **get_generated_size**\\ (\\ )\n\nSets the base size of the QuadMesh texture card. Increasing this size will expand from bottom, not the middle.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_generated_type:\n\n.. rst-class:: classref-property\n\n:ref:`GenType<enum_Terrain3DMeshAsset_GenType>` **generated_type** = ``0`` :ref:`🔗<class_Terrain3DMeshAsset_property_generated_type>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_generated_type**\\ (\\ value\\: :ref:`GenType<enum_Terrain3DMeshAsset_GenType>`\\ )\n- :ref:`GenType<enum_Terrain3DMeshAsset_GenType>` **get_generated_type**\\ (\\ )\n\nIf enabled, this mesh asset will be set to a generated QuadMesh to be used as a texture card.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_height_offset:\n\n.. rst-class:: classref-property\n\n``float`` **height_offset** = ``0.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_height_offset>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_height_offset**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_height_offset**\\ (\\ )\n\nVertically offsets the origin point of the mesh asset. For example, if you have a 2 meter diameter rock with the mesh origin point in the center, but you want all rocks to be sitting on the ground, you could enter 1 or 0.9 here and it will be placed near its edge. You can also adjust this when painting using the tool settings bar; both options are cummulative.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_id:\n\n.. rst-class:: classref-property\n\n``int`` **id** = ``0`` :ref:`🔗<class_Terrain3DMeshAsset_property_id>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_id**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_id**\\ (\\ )\n\nThe user settable ID of the mesh. You can change this to reorder meshes in the list.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_last_lod:\n\n.. rst-class:: classref-property\n\n``int`` **last_lod** = ``9`` :ref:`🔗<class_Terrain3DMeshAsset_property_last_lod>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_last_lod**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_last_lod**\\ (\\ )\n\nSets the farthest Level of Detail (LOD) that will be rendered. Farther LODs (greater than this number) will be ignored.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_last_shadow_lod:\n\n.. rst-class:: classref-property\n\n``int`` **last_shadow_lod** = ``9`` :ref:`🔗<class_Terrain3DMeshAsset_property_last_shadow_lod>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_last_shadow_lod**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_last_shadow_lod**\\ (\\ )\n\nSets the farthest Level of Detail (LOD) that will cast shadows. Farther LODs (greater than this number) won't cast shadows. If :ref:`shadow_impostor<class_Terrain3DMeshAsset_property_shadow_impostor>` is greater than this number, the shadow impostor will still cast shadows.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod0_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod0_range** = ``32.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod0_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod0_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod0_range**\\ (\\ )\n\nSets ``GeometryInstance3D.visibility_range_end`` on all MultiMeshInstances used by this mesh at the closest Level of Detail (LOD), the most detailed.\n\nAllows the renderer to cull MMIs beyond this distance. The next LOD level will use this value for its ``GeometryInstance3D.visibility_range_begin``.\n\nSet to 0 to disable culling and see this LOD at any distance.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod1_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod1_range** = ``64.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod1_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod1_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod1_range**\\ (\\ )\n\nSets the end visible range for LOD1. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod2_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod2_range** = ``96.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod2_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod2_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod2_range**\\ (\\ )\n\nSets the end visible range for LOD2. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod3_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod3_range** = ``128.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod3_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod3_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod3_range**\\ (\\ )\n\nSets the end visible range for LOD3. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod4_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod4_range** = ``160.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod4_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod4_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod4_range**\\ (\\ )\n\nSets the end visible range for LOD4. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod5_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod5_range** = ``192.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod5_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod5_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod5_range**\\ (\\ )\n\nSets the end visible range for LOD5. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod6_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod6_range** = ``224.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod6_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod6_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod6_range**\\ (\\ )\n\nSets the end visible range for LOD6. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod7_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod7_range** = ``256.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod7_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod7_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod7_range**\\ (\\ )\n\nSets the end visible range for LOD7. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod8_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod8_range** = ``288.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod8_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod8_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod8_range**\\ (\\ )\n\nSets the end visible range for LOD8. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod9_range:\n\n.. rst-class:: classref-property\n\n``float`` **lod9_range** = ``320.0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod9_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_lod9_range**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_lod9_range**\\ (\\ )\n\nSets the end visible range for LOD9. See :ref:`lod0_range<class_Terrain3DMeshAsset_property_lod0_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_lod_count:\n\n.. rst-class:: classref-property\n\n``int`` **lod_count** = ``0`` :ref:`🔗<class_Terrain3DMeshAsset_property_lod_count>`\n\n.. rst-class:: classref-property-setget\n\n- ``int`` **get_lod_count**\\ (\\ )\n\nProvides the detected number of Levels of Detail (LODs) found in the provided scene file or generated mesh. Read only.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_material_overlay:\n\n.. rst-class:: classref-property\n\n``Material`` **material_overlay** :ref:`🔗<class_Terrain3DMeshAsset_property_material_overlay>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_material_overlay**\\ (\\ value\\: ``Material``\\ )\n- ``Material`` **get_material_overlay**\\ (\\ )\n\nThis sets ``GeometryInstance3D.material_overlay``, which applies an overriding material using ``next_pass`` that overlays the base material.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_material_override:\n\n.. rst-class:: classref-property\n\n``Material`` **material_override** :ref:`🔗<class_Terrain3DMeshAsset_property_material_override>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_material_override**\\ (\\ value\\: ``Material``\\ )\n- ``Material`` **get_material_override**\\ (\\ )\n\nThis sets ``GeometryInstance3D.material_override``, which replaces the rendered mesh material with this one.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_name:\n\n.. rst-class:: classref-property\n\n``String`` **name** = ``\"New Mesh\"`` :ref:`🔗<class_Terrain3DMeshAsset_property_name>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_name**\\ (\\ value\\: ``String``\\ )\n- ``String`` **get_name**\\ (\\ )\n\nThe user specified name for this asset.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_scene_file:\n\n.. rst-class:: classref-property\n\n``PackedScene`` **scene_file** :ref:`🔗<class_Terrain3DMeshAsset_property_scene_file>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_scene_file**\\ (\\ value\\: ``PackedScene``\\ )\n- ``PackedScene`` **get_scene_file**\\ (\\ )\n\nSpecifies the PackedScene (.tscn, .scn, .glb, .fbx, etc) to load the mesh from. See the top description.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_shadow_impostor:\n\n.. rst-class:: classref-property\n\n``int`` **shadow_impostor** = ``0`` :ref:`🔗<class_Terrain3DMeshAsset_property_shadow_impostor>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_shadow_impostor**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_shadow_impostor**\\ (\\ )\n\nUses this lower quality Level of Detail (LOD) to calculate shadows (as an impostor) instead of the visible mesh.\n\ne.g. Normally each LOD casts its own shadows. Given LOD0-3, if ``shadow_impostor = 2`` then when LOD0-1 are visible, they are set to no shadows and LOD2 is set to cast shadows only. When LOD2-3 are visible, each casts their own shadow as normal.\n\nIncrease to improve performance by lowering shadow quality.\n\nShadow impostors are disabled if this is set to 0 or if :ref:`cast_shadows<class_Terrain3DMeshAsset_property_cast_shadows>` is set to shadows only.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_property_visibility_layers:\n\n.. rst-class:: classref-property\n\n``int`` **visibility_layers** = ``1`` :ref:`🔗<class_Terrain3DMeshAsset_property_visibility_layers>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_visibility_layers**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_visibility_layers**\\ (\\ )\n\nSets :ref:`VisualInstance3D.layers<class_VisualInstance3D_member_layers>`, which defines the rendering layers the MultiMeshInstance3Ds for this mesh asset are drawn on.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DMeshAsset_method_clear:\n\n.. rst-class:: classref-method\n\n|void| **clear**\\ (\\ ) :ref:`🔗<class_Terrain3DMeshAsset_method_clear>`\n\nResets this resource to default settings.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_get_highlight_color:\n\n.. rst-class:: classref-method\n\n``Color`` **get_highlight_color**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMeshAsset_method_get_highlight_color>`\n\nReturns the color of the current highlight material, if any.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_get_instance_count:\n\n.. rst-class:: classref-method\n\n``int`` **get_instance_count**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMeshAsset_method_get_instance_count>`\n\nReturns the number of instances on the ground for this mesh asset.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_get_lod_range:\n\n.. rst-class:: classref-method\n\n``float`` **get_lod_range**\\ (\\ lod\\: ``int``\\ ) |const| :ref:`🔗<class_Terrain3DMeshAsset_method_get_lod_range>`\n\nReturns the far visible distance for the specified Level of Detail (LOD). The near visible distance is the LOD range for the next closest LOD.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_get_mesh:\n\n.. rst-class:: classref-method\n\n``Mesh`` **get_mesh**\\ (\\ lod\\: ``int`` = 0\\ ) |const| :ref:`🔗<class_Terrain3DMeshAsset_method_get_mesh>`\n\nReturns the ``Mesh`` resource for the specified Level of Detail (LOD).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_get_thumbnail:\n\n.. rst-class:: classref-method\n\n``Texture2D`` **get_thumbnail**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMeshAsset_method_get_thumbnail>`\n\nReturns the thumbnail generated by :ref:`Terrain3DAssets<class_Terrain3DAssets>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_is_highlighted:\n\n.. rst-class:: classref-method\n\n``bool`` **is_highlighted**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DMeshAsset_method_is_highlighted>`\n\nReturns true if the instances for this mesh asset are currently highlighted on the ground. For editor use.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_set_highlighted:\n\n.. rst-class:: classref-method\n\n|void| **set_highlighted**\\ (\\ enabled\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DMeshAsset_method_set_highlighted>`\n\nEnables or disables adding a highlight material with a random color to the overlay slot for all instances of this mesh asset.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DMeshAsset_method_set_lod_range:\n\n.. rst-class:: classref-method\n\n|void| **set_lod_range**\\ (\\ lod\\: ``int``, distance\\: ``float``\\ ) :ref:`🔗<class_Terrain3DMeshAsset_method_set_lod_range>`\n\nSets the far visible distance for the specified Level of Detail (LOD).\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dregion.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DRegion.xml.\n\n.. _class_Terrain3DRegion:\n\nTerrain3DRegion\n===============\n\n**Inherits:** ``Resource``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis resource stores all map data for Terrain3D. See `Controlmap Format <https://terrain3d.readthedocs.io/en/stable/docs/controlmap_format.html>`__ and `Data Format Changelog <https://terrain3d.readthedocs.io/en/stable/docs/controlmap_format.html>`__.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``Image``      | :ref:`color_map<class_Terrain3DRegion_property_color_map>`           |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``Image``      | :ref:`control_map<class_Terrain3DRegion_property_control_map>`       |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``bool``       | :ref:`deleted<class_Terrain3DRegion_property_deleted>`               |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``bool``       | :ref:`edited<class_Terrain3DRegion_property_edited>`                 |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``Image``      | :ref:`height_map<class_Terrain3DRegion_property_height_map>`         |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``Vector2``    | :ref:`height_range<class_Terrain3DRegion_property_height_range>`     | ``Vector2(0, 0)`` |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``Dictionary`` | :ref:`instances<class_Terrain3DRegion_property_instances>`           | ``{}``            |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``Vector2i``   | :ref:`location<class_Terrain3DRegion_property_location>`             |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``bool``       | :ref:`modified<class_Terrain3DRegion_property_modified>`             |                   |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``int``        | :ref:`region_size<class_Terrain3DRegion_property_region_size>`       | ``0``             |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``float``      | :ref:`version<class_Terrain3DRegion_property_version>`               | ``0.8``           |\n   +----------------+----------------------------------------------------------------------+-------------------+\n   | ``float``      | :ref:`vertex_spacing<class_Terrain3DRegion_property_vertex_spacing>` | ``1.0``           |\n   +----------------+----------------------------------------------------------------------+-------------------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`calc_height_range<class_Terrain3DRegion_method_calc_height_range>`\\ (\\ )                                                                         |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`clear<class_Terrain3DRegion_method_clear>`\\ (\\ )                                                                                                 |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`dump<class_Terrain3DRegion_method_dump>`\\ (\\ verbose\\: ``bool`` = false\\ ) |const|                                                               |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Terrain3DRegion<class_Terrain3DRegion>` | :ref:`duplicate<class_Terrain3DRegion_method_duplicate>`\\ (\\ deep\\: ``bool`` = false\\ )                                                                |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Dictionary``                                | :ref:`get_data<class_Terrain3DRegion_method_get_data>`\\ (\\ ) |const|                                                                                   |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``                                     | :ref:`get_map<class_Terrain3DRegion_method_get_map>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const|                            |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | :ref:`Array<class_Array>`\\[``Image``\\]        | :ref:`get_maps<class_Terrain3DRegion_method_get_maps>`\\ (\\ ) |const|                                                                                   |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``                                     | :ref:`sanitize_map<class_Terrain3DRegion_method_sanitize_map>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, map\\: ``Image``\\ ) |const| |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`sanitize_maps<class_Terrain3DRegion_method_sanitize_maps>`\\ (\\ )                                                                                 |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | Error                                         | :ref:`save<class_Terrain3DRegion_method_save>`\\ (\\ path\\: ``String`` = \"\", save_16_bit\\: ``bool`` = false\\ )                                           |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`set_data<class_Terrain3DRegion_method_set_data>`\\ (\\ data\\: ``Dictionary``\\ )                                                                    |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`set_map<class_Terrain3DRegion_method_set_map>`\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, map\\: ``Image``\\ )                   |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`set_maps<class_Terrain3DRegion_method_set_maps>`\\ (\\ maps\\: :ref:`Array<class_Array>`\\[``Image``\\]\\ )                                            |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`update_height<class_Terrain3DRegion_method_update_height>`\\ (\\ height\\: ``float``\\ )                                                             |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | |void|                                        | :ref:`update_heights<class_Terrain3DRegion_method_update_heights>`\\ (\\ low_high\\: ``Vector2``\\ )                                                       |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``                                      | :ref:`validate_map_size<class_Terrain3DRegion_method_validate_map_size>`\\ (\\ map\\: ``Image``\\ ) |const|                                                |\n   +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nEnumerations\n------------\n\n.. _enum_Terrain3DRegion_MapType:\n\n.. rst-class:: classref-enumeration\n\nenum **MapType**: :ref:`🔗<enum_Terrain3DRegion_MapType>`\n\n.. _class_Terrain3DRegion_constant_TYPE_HEIGHT:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`MapType<enum_Terrain3DRegion_MapType>` **TYPE_HEIGHT** = ``0``\n\nHeight map - real values, eg. 10m, 44.5m.\n\n.. _class_Terrain3DRegion_constant_TYPE_CONTROL:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`MapType<enum_Terrain3DRegion_MapType>` **TYPE_CONTROL** = ``1``\n\nControl map - defines where textures and holes are placed.\n\n.. _class_Terrain3DRegion_constant_TYPE_COLOR:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`MapType<enum_Terrain3DRegion_MapType>` **TYPE_COLOR** = ``2``\n\nColor map - paints color on the terrain\n\n.. _class_Terrain3DRegion_constant_TYPE_MAX:\n\n.. rst-class:: classref-enumeration-constant\n\n:ref:`MapType<enum_Terrain3DRegion_MapType>` **TYPE_MAX** = ``3``\n\nThe number of elements in this enum. Often used to specify all map types for functions that request one.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DRegion_property_color_map:\n\n.. rst-class:: classref-property\n\n``Image`` **color_map** :ref:`🔗<class_Terrain3DRegion_property_color_map>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_color_map**\\ (\\ value\\: ``Image``\\ )\n- ``Image`` **get_color_map**\\ (\\ )\n\nThis map is used to paint color that blends in to the terrain textures.\n\nImage format: FORMAT_RGBA8, 32-bits per pixel as four 8-bit components.\n\n\\ **RGB** is used for color, which is multiplied by albedo in the shader. Multiply is a blend mode that only darkens.\n\n\\ **A** is used for a roughness modifier. A value of 0.5 means no change to the existing texture roughness. Higher than this value increases roughness, lower decreases it.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_control_map:\n\n.. rst-class:: classref-property\n\n``Image`` **control_map** :ref:`🔗<class_Terrain3DRegion_property_control_map>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_control_map**\\ (\\ value\\: ``Image``\\ )\n- ``Image`` **get_control_map**\\ (\\ )\n\nThis map tells the shader which textures to use where, how to blend, where to place holes, etc.\n\nImage format: FORMAT_RF, 32-bit per pixel as full-precision floating-point.\n\nHowever, we interpret these images as format: `RenderingDevice.DATA_FORMAT_R32_UINT <https://docs.godotengine.org/en/stable/classes/class_renderingdevice.html#class-renderingdevice-constant-data-format-r32-uint>`__ aka OpenGL RG32UI 32-bit per pixel as unsigned integer. See `Control map format <https://terrain3d.readthedocs.io/en/stable/docs/controlmap_format.html>`__.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_deleted:\n\n.. rst-class:: classref-property\n\n``bool`` **deleted** :ref:`🔗<class_Terrain3DRegion_property_deleted>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_deleted**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_deleted**\\ (\\ )\n\nThis region is marked for deletion. It won't be rendered once :ref:`Terrain3DData.update_maps()<class_Terrain3DData_method_update_maps>` rebuilds the map index. The file will be deleted from disk on :ref:`save()<class_Terrain3DRegion_method_save>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_edited:\n\n.. rst-class:: classref-property\n\n``bool`` **edited** :ref:`🔗<class_Terrain3DRegion_property_edited>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_edited**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_edited**\\ (\\ )\n\nThis region is marked for updating by :ref:`Terrain3DData.update_maps()<class_Terrain3DData_method_update_maps>` and for undo/redo tracking when set between :ref:`Terrain3DEditor.start_operation()<class_Terrain3DEditor_method_start_operation>` and :ref:`Terrain3DEditor.stop_operation()<class_Terrain3DEditor_method_stop_operation>`. The latter method clears the edited flag. This flag serves a different purpose than :ref:`modified<class_Terrain3DRegion_property_modified>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_height_map:\n\n.. rst-class:: classref-property\n\n``Image`` **height_map** :ref:`🔗<class_Terrain3DRegion_property_height_map>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_height_map**\\ (\\ value\\: ``Image``\\ )\n- ``Image`` **get_height_map**\\ (\\ )\n\nThis map contains the real value heights for the terrain.\n\nImage format: FORMAT_RF, 32-bit per pixel as full-precision floating-point.\n\nHeights sent to the vertex shader on the GPU which modifies the mesh in real-time.\n\nEditing is always done in 32-bit. We do provide an option to save as 16-bit, see :ref:`Terrain3D.save_16_bit<class_Terrain3D_property_save_16_bit>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_height_range:\n\n.. rst-class:: classref-property\n\n``Vector2`` **height_range** = ``Vector2(0, 0)`` :ref:`🔗<class_Terrain3DRegion_property_height_range>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_height_range**\\ (\\ value\\: ``Vector2``\\ )\n- ``Vector2`` **get_height_range**\\ (\\ )\n\nThe current minimum and maximum height range for this region, used to calculate the AABB of the terrain. Update it with :ref:`update_height()<class_Terrain3DRegion_method_update_height>`, and recalculate it with :ref:`calc_height_range()<class_Terrain3DRegion_method_calc_height_range>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_instances:\n\n.. rst-class:: classref-property\n\n``Dictionary`` **instances** = ``{}`` :ref:`🔗<class_Terrain3DRegion_property_instances>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_instances**\\ (\\ value\\: ``Dictionary``\\ )\n- ``Dictionary`` **get_instances**\\ (\\ )\n\nA Dictionary that stores the instancer transforms for this region.\n\nThe format is instances{mesh_id:int} -> cells{grid_location:Vector2i} -> ( Array:Transform3D, PackedColorArray, modified:bool ). That is:\n\n- A Dictionary keyed by mesh_id that returns:\n\n- A Dictionary keyed by the grid location of the 32 x 32m cell that returns:\n\n- A 3-item Array that contains:\n\n- 0: An Array of Transform3Ds\n\n- 1: A PackedColorArray with instance colors, same index as above\n\n- 2: A bool that tracks if this cell has been modified\n\nAfter changing this data, call :ref:`Terrain3DInstancer.update_mmis()<class_Terrain3DInstancer_method_update_mmis>` to rebuild the MMIs.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_location:\n\n.. rst-class:: classref-property\n\n``Vector2i`` **location** :ref:`🔗<class_Terrain3DRegion_property_location>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_location**\\ (\\ value\\: ``Vector2i``\\ )\n- ``Vector2i`` **get_location**\\ (\\ )\n\nThe location in region grid space ``(world space / region_size)`` coordinates. e.g. (-1, 1) equates to (-1024, 1024) in world space given a :ref:`region_size<class_Terrain3DRegion_property_region_size>` of 1024.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_modified:\n\n.. rst-class:: classref-property\n\n``bool`` **modified** :ref:`🔗<class_Terrain3DRegion_property_modified>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_modified**\\ (\\ value\\: ``bool``\\ )\n- ``bool`` **is_modified**\\ (\\ )\n\nThis region has been modified and will be saved to disk upon :ref:`save()<class_Terrain3DRegion_method_save>`. This serves a different purpose than the temporary :ref:`edited<class_Terrain3DRegion_property_edited>` setting.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_region_size:\n\n.. rst-class:: classref-property\n\n``int`` **region_size** = ``0`` :ref:`🔗<class_Terrain3DRegion_property_region_size>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_region_size**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_region_size**\\ (\\ )\n\nThe current region size for this region, calculated from the dimensions of the first loaded map. It should match :ref:`Terrain3D.region_size<class_Terrain3D_property_region_size>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_version:\n\n.. rst-class:: classref-property\n\n``float`` **version** = ``0.8`` :ref:`🔗<class_Terrain3DRegion_property_version>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_version**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_version**\\ (\\ )\n\nThe data file version. This is independent of the Terrain3D version, though they often align.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_property_vertex_spacing:\n\n.. rst-class:: classref-property\n\n``float`` **vertex_spacing** = ``1.0`` :ref:`🔗<class_Terrain3DRegion_property_vertex_spacing>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_vertex_spacing**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_vertex_spacing**\\ (\\ )\n\nStored instancer transforms are laterally scaled by this value. This value is manage by the instancer on loading or when :ref:`Terrain3D.vertex_spacing<class_Terrain3D_property_vertex_spacing>` is set, and shouldn't be manually adjusted.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DRegion_method_calc_height_range:\n\n.. rst-class:: classref-method\n\n|void| **calc_height_range**\\ (\\ ) :ref:`🔗<class_Terrain3DRegion_method_calc_height_range>`\n\nRecalculates the height range for this region by looking at every pixel in the heightmap.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_clear:\n\n.. rst-class:: classref-method\n\n|void| **clear**\\ (\\ ) :ref:`🔗<class_Terrain3DRegion_method_clear>`\n\nUnreferences the maps and resets all of the variables to default values.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_dump:\n\n.. rst-class:: classref-method\n\n|void| **dump**\\ (\\ verbose\\: ``bool`` = false\\ ) |const| :ref:`🔗<class_Terrain3DRegion_method_dump>`\n\nDumps information about the data in the region, including instance IDs, counts, and data pointers.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_duplicate:\n\n.. rst-class:: classref-method\n\n:ref:`Terrain3DRegion<class_Terrain3DRegion>` **duplicate**\\ (\\ deep\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DRegion_method_duplicate>`\n\nReturns a duplicate copy of this node, with references to the same image maps and multimeshes.\n\n- deep - Also make complete duplicates of the maps and multimeshes.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_get_data:\n\n.. rst-class:: classref-method\n\n``Dictionary`` **get_data**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DRegion_method_get_data>`\n\nReturns all data in this region in a dictionary.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_get_map:\n\n.. rst-class:: classref-method\n\n``Image`` **get_map**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`\\ ) |const| :ref:`🔗<class_Terrain3DRegion_method_get_map>`\n\nReturns the specified image map.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_get_maps:\n\n.. rst-class:: classref-method\n\n:ref:`Array<class_Array>`\\[``Image``\\] **get_maps**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DRegion_method_get_maps>`\n\nReturns an Array\\ ``Image`` with height, control, and color maps.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_sanitize_map:\n\n.. rst-class:: classref-method\n\n``Image`` **sanitize_map**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, map\\: ``Image``\\ ) |const| :ref:`🔗<class_Terrain3DRegion_method_sanitize_map>`\n\nValidates and adjusts the map size and format if possible, or creates a usable blank image in the right size and format.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_sanitize_maps:\n\n.. rst-class:: classref-method\n\n|void| **sanitize_maps**\\ (\\ ) :ref:`🔗<class_Terrain3DRegion_method_sanitize_maps>`\n\nSanitizes all map types. See :ref:`sanitize_map()<class_Terrain3DRegion_method_sanitize_map>`.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_save:\n\n.. rst-class:: classref-method\n\nError **save**\\ (\\ path\\: ``String`` = \"\", save_16_bit\\: ``bool`` = false\\ ) :ref:`🔗<class_Terrain3DRegion_method_save>`\n\nSaves this region to the current file name.\n\n- path - specifies a directory and file name to use from now on.\n\n- 16-bit - save this region with 16-bit height map instead of 32-bit. This process is lossy. Does not change the bit depth in memory.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_set_data:\n\n.. rst-class:: classref-method\n\n|void| **set_data**\\ (\\ data\\: ``Dictionary``\\ ) :ref:`🔗<class_Terrain3DRegion_method_set_data>`\n\nOverwrites all local variables with values in the dictionary.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_set_map:\n\n.. rst-class:: classref-method\n\n|void| **set_map**\\ (\\ map_type\\: :ref:`MapType<enum_Terrain3DRegion_MapType>`, map\\: ``Image``\\ ) :ref:`🔗<class_Terrain3DRegion_method_set_map>`\n\nAssigns the provided map to the desired map type.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_set_maps:\n\n.. rst-class:: classref-method\n\n|void| **set_maps**\\ (\\ maps\\: :ref:`Array<class_Array>`\\[``Image``\\]\\ ) :ref:`🔗<class_Terrain3DRegion_method_set_maps>`\n\nExpects an array with three images in it, and assigns them to the height, control, and color maps.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_update_height:\n\n.. rst-class:: classref-method\n\n|void| **update_height**\\ (\\ height\\: ``float``\\ ) :ref:`🔗<class_Terrain3DRegion_method_update_height>`\n\nWhen sculpting, this is called to provide the current height. It may expand the vertical bounds, which is used to calculate the terrain AABB.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_update_heights:\n\n.. rst-class:: classref-method\n\n|void| **update_heights**\\ (\\ low_high\\: ``Vector2``\\ ) :ref:`🔗<class_Terrain3DRegion_method_update_heights>`\n\nWhen sculpting the terrain, this is called to provide both a low and high height. It may expand the vertical bounds, which is used to calculate the terrain AABB.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DRegion_method_validate_map_size:\n\n.. rst-class:: classref-method\n\n``bool`` **validate_map_size**\\ (\\ map\\: ``Image``\\ ) |const| :ref:`🔗<class_Terrain3DRegion_method_validate_map_size>`\n\nThis validates the map size according to previously loaded maps.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dtextureasset.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DTextureAsset.xml.\n\n.. _class_Terrain3DTextureAsset:\n\nTerrain3DTextureAsset\n=====================\n\n**Inherits:** ``Resource``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nA set of texture files and settings that gets added to a :ref:`Terrain3DAssets<class_Terrain3DAssets>` resource. Textures must be prepared according to the `documentation <https://terrain3d.readthedocs.io/en/stable/docs/texture_prep.html>`__.\n\n.. rst-class:: classref-reftable-group\n\nProperties\n----------\n\n.. table::\n   :widths: auto\n\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``Color``     | :ref:`albedo_color<class_Terrain3DTextureAsset_property_albedo_color>`               | ``Color(1, 1, 1, 1)`` |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``Texture2D`` | :ref:`albedo_texture<class_Terrain3DTextureAsset_property_albedo_texture>`           |                       |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`ao_light_affect<class_Terrain3DTextureAsset_property_ao_light_affect>`         | ``0.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`ao_strength<class_Terrain3DTextureAsset_property_ao_strength>`                 | ``0.5``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`detiling_rotation<class_Terrain3DTextureAsset_property_detiling_rotation>`     | ``0.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`detiling_shift<class_Terrain3DTextureAsset_property_detiling_shift>`           | ``0.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`displacement_offset<class_Terrain3DTextureAsset_property_displacement_offset>` | ``0.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`displacement_scale<class_Terrain3DTextureAsset_property_displacement_scale>`   | ``0.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``int``       | :ref:`id<class_Terrain3DTextureAsset_property_id>`                                   | ``0``                 |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``String``    | :ref:`name<class_Terrain3DTextureAsset_property_name>`                               | ``\"New Texture\"``     |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`normal_depth<class_Terrain3DTextureAsset_property_normal_depth>`               | ``1.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``Texture2D`` | :ref:`normal_texture<class_Terrain3DTextureAsset_property_normal_texture>`           |                       |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`roughness<class_Terrain3DTextureAsset_property_roughness>`                     | ``0.0``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n   | ``float``     | :ref:`uv_scale<class_Terrain3DTextureAsset_property_uv_scale>`                       | ``0.1``               |\n   +---------------+--------------------------------------------------------------------------------------+-----------------------+\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +---------------+------------------------------------------------------------------------------------------------------+\n   | |void|        | :ref:`clear<class_Terrain3DTextureAsset_method_clear>`\\ (\\ )                                         |\n   +---------------+------------------------------------------------------------------------------------------------------+\n   | ``Color``     | :ref:`get_highlight_color<class_Terrain3DTextureAsset_method_get_highlight_color>`\\ (\\ ) |const|     |\n   +---------------+------------------------------------------------------------------------------------------------------+\n   | ``Texture2D`` | :ref:`get_thumbnail<class_Terrain3DTextureAsset_method_get_thumbnail>`\\ (\\ ) |const|                 |\n   +---------------+------------------------------------------------------------------------------------------------------+\n   | ``bool``      | :ref:`is_highlighted<class_Terrain3DTextureAsset_method_is_highlighted>`\\ (\\ ) |const|               |\n   +---------------+------------------------------------------------------------------------------------------------------+\n   | |void|        | :ref:`set_highlighted<class_Terrain3DTextureAsset_method_set_highlighted>`\\ (\\ enabled\\: ``bool``\\ ) |\n   +---------------+------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nSignals\n-------\n\n.. _class_Terrain3DTextureAsset_signal_file_changed:\n\n.. rst-class:: classref-signal\n\n**file_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DTextureAsset_signal_file_changed>`\n\nEmitted when :ref:`albedo_texture<class_Terrain3DTextureAsset_property_albedo_texture>` or :ref:`normal_texture<class_Terrain3DTextureAsset_property_normal_texture>` are changed.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_signal_id_changed:\n\n.. rst-class:: classref-signal\n\n**id_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DTextureAsset_signal_id_changed>`\n\nEmitted when :ref:`id<class_Terrain3DTextureAsset_property_id>` is changed.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_signal_setting_changed:\n\n.. rst-class:: classref-signal\n\n**setting_changed**\\ (\\ ) :ref:`🔗<class_Terrain3DTextureAsset_signal_setting_changed>`\n\nEmitted when any setting is changed, other than id, albedo_texture, or normal_texture.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nProperty Descriptions\n---------------------\n\n.. _class_Terrain3DTextureAsset_property_albedo_color:\n\n.. rst-class:: classref-property\n\n``Color`` **albedo_color** = ``Color(1, 1, 1, 1)`` :ref:`🔗<class_Terrain3DTextureAsset_property_albedo_color>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_albedo_color**\\ (\\ value\\: ``Color``\\ )\n- ``Color`` **get_albedo_color**\\ (\\ )\n\nThis color is multiplied by the albedo texture in the shader.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_albedo_texture:\n\n.. rst-class:: classref-property\n\n``Texture2D`` **albedo_texture** :ref:`🔗<class_Terrain3DTextureAsset_property_albedo_texture>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_albedo_texture**\\ (\\ value\\: ``Texture2D``\\ )\n- ``Texture2D`` **get_albedo_texture**\\ (\\ )\n\nThe texture file with albedo on RGB and height on A.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_ao_light_affect:\n\n.. rst-class:: classref-property\n\n``float`` **ao_light_affect** = ``0.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_ao_light_affect>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ao_light_affect**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_ao_light_affect**\\ (\\ )\n\nThis value is applied directly to the AO_LIGHT_AFFECT in the shader, which dictates how much ambient occlusion affects light from Light3Ds. 0 means AO only affects ambient light. 1 means it also affects lights.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_ao_strength:\n\n.. rst-class:: classref-property\n\n``float`` **ao_strength** = ``0.5`` :ref:`🔗<class_Terrain3DTextureAsset_property_ao_strength>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_ao_strength**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_ao_strength**\\ (\\ )\n\nThe strength of ambient occlusion, which darkens areas of this texture dictated by the ambient occlusion map. If you have not provided an AO texture, an AO value is approximated by the normal map automatically.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_detiling_rotation:\n\n.. rst-class:: classref-property\n\n``float`` **detiling_rotation** = ``0.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_detiling_rotation>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_detiling_rotation**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_detiling_rotation**\\ (\\ )\n\nThe shader rotates UV lookups in a detiling pattern based on this value.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_detiling_shift:\n\n.. rst-class:: classref-property\n\n``float`` **detiling_shift** = ``0.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_detiling_shift>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_detiling_shift**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_detiling_shift**\\ (\\ )\n\nThe shader laterally shifts UV lookups in a detiling pattern based on this value.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_displacement_offset:\n\n.. rst-class:: classref-property\n\n``float`` **displacement_offset** = ``0.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_displacement_offset>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_displacement_offset**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_displacement_offset**\\ (\\ )\n\nOffset that can be used to raise or lower the displaced surface height for this material.\n\nExample: slightly lowering a cobblestone texture so the tops of the cobbles match collision.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_displacement_scale:\n\n.. rst-class:: classref-property\n\n``float`` **displacement_scale** = ``0.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_displacement_scale>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_displacement_scale**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_displacement_scale**\\ (\\ )\n\nThe scale of the displaced surface height for this material.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_id:\n\n.. rst-class:: classref-property\n\n``int`` **id** = ``0`` :ref:`🔗<class_Terrain3DTextureAsset_property_id>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_id**\\ (\\ value\\: ``int``\\ )\n- ``int`` **get_id**\\ (\\ )\n\nThe user settable ID of the texture, between 0 and 31. You can change this to reorder textures in the list, however it won't change the ID painted on the terrain.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_name:\n\n.. rst-class:: classref-property\n\n``String`` **name** = ``\"New Texture\"`` :ref:`🔗<class_Terrain3DTextureAsset_property_name>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_name**\\ (\\ value\\: ``String``\\ )\n- ``String`` **get_name**\\ (\\ )\n\nA user specified name for this texture set.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_normal_depth:\n\n.. rst-class:: classref-property\n\n``float`` **normal_depth** = ``1.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_normal_depth>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_normal_depth**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_normal_depth**\\ (\\ )\n\nIncreases or decreases the strength of the normal texture.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_normal_texture:\n\n.. rst-class:: classref-property\n\n``Texture2D`` **normal_texture** :ref:`🔗<class_Terrain3DTextureAsset_property_normal_texture>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_normal_texture**\\ (\\ value\\: ``Texture2D``\\ )\n- ``Texture2D`` **get_normal_texture**\\ (\\ )\n\nThe texture file with normal on RGB and roughness on A.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_roughness:\n\n.. rst-class:: classref-property\n\n``float`` **roughness** = ``0.0`` :ref:`🔗<class_Terrain3DTextureAsset_property_roughness>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_roughness**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_roughness**\\ (\\ )\n\nIncreases or decreases the roughness texture values.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_property_uv_scale:\n\n.. rst-class:: classref-property\n\n``float`` **uv_scale** = ``0.1`` :ref:`🔗<class_Terrain3DTextureAsset_property_uv_scale>`\n\n.. rst-class:: classref-property-setget\n\n- |void| **set_uv_scale**\\ (\\ value\\: ``float``\\ )\n- ``float`` **get_uv_scale**\\ (\\ )\n\nThe scale of the textures.\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DTextureAsset_method_clear:\n\n.. rst-class:: classref-method\n\n|void| **clear**\\ (\\ ) :ref:`🔗<class_Terrain3DTextureAsset_method_clear>`\n\nClears the texture files and settings.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_method_get_highlight_color:\n\n.. rst-class:: classref-method\n\n``Color`` **get_highlight_color**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DTextureAsset_method_get_highlight_color>`\n\nReturns the color of the current highlight, if any.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_method_get_thumbnail:\n\n.. rst-class:: classref-method\n\n``Texture2D`` **get_thumbnail**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DTextureAsset_method_get_thumbnail>`\n\nReturns the generated thumbnail.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_method_is_highlighted:\n\n.. rst-class:: classref-method\n\n``bool`` **is_highlighted**\\ (\\ ) |const| :ref:`🔗<class_Terrain3DTextureAsset_method_is_highlighted>`\n\nReturns true if this texture is currently highlighted on the ground. For editor use.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DTextureAsset_method_set_highlighted:\n\n.. rst-class:: classref-method\n\n|void| **set_highlighted**\\ (\\ enabled\\: ``bool``\\ ) :ref:`🔗<class_Terrain3DTextureAsset_method_set_highlighted>`\n\nEnables or disables adding a random highlight color to this texture wherever placed on the ground.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/class_terrain3dutil.rst",
    "content": ":github_url: hide\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n.. XML source: https://github.com/godotengine/godot/tree/master/../_plugins/Terrain3D/doc/doc_classes/Terrain3DUtil.xml.\n\n.. _class_Terrain3DUtil:\n\nTerrain3DUtil\n=============\n\n**Inherits:** ``Object``\n\n.. rst-class:: classref-introduction-group\n\nDescription\n-----------\n\nThis class contains static utility functions. Reference them with the full class name. Eg. ``Terrain3DUtil.as_float()``.\n\nOr you can instance the class for a shorter alias:\n\n::\n\n    var util := Terrain3DUtil.new()\n    var my_float: float = util.as_float(my_int)\n\n\\ **Note on uints**: Various functions refer to unsigned integers as uint. Though GDScript doesn't support unsigned integers as a type, the C++ that receives and returns these values will interpret them all as unsigned.\n\n\\ **Note on bitwise-ORing**: To write back to a control map, encode your values and bitwise OR the results, then reinterpret that uint as a float. The shader will interpret the float as uint and extract the bits.\n\n::\n\n    var bits: int = util.enc_base(base_id) | util.enc_overlay(over_id) | \\\n    util.enc_blend(blend) | util.enc_uv_rotation(uvrotation) | \\\n    util.enc_uv_scale(uvscale) | util.enc_auto(autoshader) | \\\n    util.enc_nav(navigation) | util.enc_hole(hole)\n    var color: Color = Color(util.as_float(bits), 0., 0., 1.)\n    data.set_control(global_pos, color)\n\n.. rst-class:: classref-reftable-group\n\nMethods\n-------\n\n.. table::\n   :widths: auto\n\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``float``    | :ref:`as_float<class_Terrain3DUtil_method_as_float>`\\ (\\ value\\: ``int``\\ ) |static|                                                                                                                                                                                                             |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`as_uint<class_Terrain3DUtil_method_as_uint>`\\ (\\ value\\: ``float``\\ ) |static|                                                                                                                                                                                                             |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``    | :ref:`black_to_alpha<class_Terrain3DUtil_method_black_to_alpha>`\\ (\\ image\\: ``Image``\\ ) |static|                                                                                                                                                                                               |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_auto<class_Terrain3DUtil_method_enc_auto>`\\ (\\ pixel\\: ``bool``\\ ) |static|                                                                                                                                                                                                            |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_base<class_Terrain3DUtil_method_enc_base>`\\ (\\ base\\: ``int``\\ ) |static|                                                                                                                                                                                                              |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_blend<class_Terrain3DUtil_method_enc_blend>`\\ (\\ blend\\: ``int``\\ ) |static|                                                                                                                                                                                                           |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_hole<class_Terrain3DUtil_method_enc_hole>`\\ (\\ pixel\\: ``bool``\\ ) |static|                                                                                                                                                                                                            |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_nav<class_Terrain3DUtil_method_enc_nav>`\\ (\\ pixel\\: ``bool``\\ ) |static|                                                                                                                                                                                                              |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_overlay<class_Terrain3DUtil_method_enc_overlay>`\\ (\\ overlay\\: ``int``\\ ) |static|                                                                                                                                                                                                     |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_uv_rotation<class_Terrain3DUtil_method_enc_uv_rotation>`\\ (\\ rotation\\: ``int``\\ ) |static|                                                                                                                                                                                            |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`enc_uv_scale<class_Terrain3DUtil_method_enc_uv_scale>`\\ (\\ scale\\: ``int``\\ ) |static|                                                                                                                                                                                                     |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector2i`` | :ref:`filename_to_location<class_Terrain3DUtil_method_filename_to_location>`\\ (\\ filename\\: ``String``\\ ) |static|                                                                                                                                                                               |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`get_base<class_Terrain3DUtil_method_get_base>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                             |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`get_blend<class_Terrain3DUtil_method_get_blend>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                           |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``    | :ref:`get_filled_image<class_Terrain3DUtil_method_get_filled_image>`\\ (\\ size\\: ``Vector2i``, color\\: ``Color``, create_mipmaps\\: ``bool``, format\\: Image.Format\\ ) |static|                                                                                                                    |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Vector2``  | :ref:`get_min_max<class_Terrain3DUtil_method_get_min_max>`\\ (\\ image\\: ``Image``\\ ) |static|                                                                                                                                                                                                     |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`get_overlay<class_Terrain3DUtil_method_get_overlay>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                       |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``    | :ref:`get_thumbnail<class_Terrain3DUtil_method_get_thumbnail>`\\ (\\ image\\: ``Image``, size\\: ``Vector2i`` = Vector2i(256, 256)\\ ) |static|                                                                                                                                                       |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`get_uv_rotation<class_Terrain3DUtil_method_get_uv_rotation>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                               |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``int``      | :ref:`get_uv_scale<class_Terrain3DUtil_method_get_uv_scale>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                     |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``     | :ref:`is_auto<class_Terrain3DUtil_method_is_auto>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                               |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``     | :ref:`is_hole<class_Terrain3DUtil_method_is_hole>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                               |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``bool``     | :ref:`is_nav<class_Terrain3DUtil_method_is_nav>`\\ (\\ pixel\\: ``int``\\ ) |static|                                                                                                                                                                                                                 |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``    | :ref:`load_image<class_Terrain3DUtil_method_load_image>`\\ (\\ file_name\\: ``String``, cache_mode\\: ``int`` = 0, r16_height_range\\: ``Vector2`` = Vector2(0, 255), r16_size\\: ``Vector2i`` = Vector2i(0, 0)\\ ) |static|                                                                            |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``String``   | :ref:`location_to_filename<class_Terrain3DUtil_method_location_to_filename>`\\ (\\ region_location\\: ``Vector2i``\\ ) |static|                                                                                                                                                                      |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``    | :ref:`luminance_to_height<class_Terrain3DUtil_method_luminance_to_height>`\\ (\\ src_rgb\\: ``Image``\\ ) |static|                                                                                                                                                                                   |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n   | ``Image``    | :ref:`pack_image<class_Terrain3DUtil_method_pack_image>`\\ (\\ src_rgb\\: ``Image``, src_a\\: ``Image``, src_ao\\: ``Image``, invert_green\\: ``bool`` = false, invert_alpha\\: ``bool`` = false, normalize_alpha\\: ``bool`` = false, alpha_channel\\: ``int`` = 0, ao_channel\\: ``int`` = 0\\ ) |static| |\n   +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n.. rst-class:: classref-section-separator\n\n----\n\n.. rst-class:: classref-descriptions-group\n\nMethod Descriptions\n-------------------\n\n.. _class_Terrain3DUtil_method_as_float:\n\n.. rst-class:: classref-method\n\n``float`` **as_float**\\ (\\ value\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_as_float>`\n\nReturns a float typed variable with the contents of the memory stored in value, an integer typed variable.\n\nThis function does not convert integer values to float values (e.g. 4 -> 4.0). It reinterprets the memory block as if it were a float. If the data in value was a valid integer, it is now an invalid float.\n\n\\ ``my_float == util.as_float(util.as_uint(my_float))``\\ \n\nSee :ref:`as_uint()<class_Terrain3DUtil_method_as_uint>` for the opposite.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_as_uint:\n\n.. rst-class:: classref-method\n\n``int`` **as_uint**\\ (\\ value\\: ``float``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_as_uint>`\n\nReturns an integer typed variable with the contents of the memory stored in value, a float typed variable.\n\nThis function does not convert float values to integer values (e.g. 4.0 -> 4). It reinterprets the memory block as if it were an integer. If the data in value was a valid float, it is now a valid integer, but probably an unexepctedly large value.\n\n\\ ``my_int == util.as_uint(util.as_float(my_int))``\\ \n\nSee :ref:`as_float()<class_Terrain3DUtil_method_as_float>` for the opposite.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_black_to_alpha:\n\n.. rst-class:: classref-method\n\n``Image`` **black_to_alpha**\\ (\\ image\\: ``Image``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_black_to_alpha>`\n\nReceives an image with a black background and returns one with a transparent background, aka an alpha mask.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_auto:\n\n.. rst-class:: classref-method\n\n``int`` **enc_auto**\\ (\\ pixel\\: ``bool``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_auto>`\n\nReturns a control map uint with the auto shader bit set. See the top description for usage.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_base:\n\n.. rst-class:: classref-method\n\n``int`` **enc_base**\\ (\\ base\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_base>`\n\nReturns a control map uint with the base texture ID encoded. See the top description for usage.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_blend:\n\n.. rst-class:: classref-method\n\n``int`` **enc_blend**\\ (\\ blend\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_blend>`\n\nReturns a control map uint with the blend value encoded. See the top description for usage.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_hole:\n\n.. rst-class:: classref-method\n\n``int`` **enc_hole**\\ (\\ pixel\\: ``bool``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_hole>`\n\nReturns a control map uint with the hole bit set. See the top description for usage.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_nav:\n\n.. rst-class:: classref-method\n\n``int`` **enc_nav**\\ (\\ pixel\\: ``bool``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_nav>`\n\nReturns a control map uint with the nav bit set. See the top description for usage.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_overlay:\n\n.. rst-class:: classref-method\n\n``int`` **enc_overlay**\\ (\\ overlay\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_overlay>`\n\nReturns a control map uint with the overlay texture ID encoded. See the top description for usage.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_uv_rotation:\n\n.. rst-class:: classref-method\n\n``int`` **enc_uv_rotation**\\ (\\ rotation\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_uv_rotation>`\n\nReturns a control map uint with the texture rotation encoded. See the top description for usage.  See :ref:`get_uv_rotation()<class_Terrain3DUtil_method_get_uv_rotation>` for values.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_enc_uv_scale:\n\n.. rst-class:: classref-method\n\n``int`` **enc_uv_scale**\\ (\\ scale\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_enc_uv_scale>`\n\nReturns a control map uint with the texture scale encoded. See the top description for usage. See :ref:`get_uv_scale()<class_Terrain3DUtil_method_get_uv_scale>` for values.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_filename_to_location:\n\n.. rst-class:: classref-method\n\n``Vector2i`` **filename_to_location**\\ (\\ filename\\: ``String``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_filename_to_location>`\n\nConverts a file name string like ``terrain3d-01_02.res`` to a region location like ``(-1, 2)``. - is negative, \\_ is positive.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_base:\n\n.. rst-class:: classref-method\n\n``int`` **get_base**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_base>`\n\nReturns the base texture ID from a control map pixel.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_blend:\n\n.. rst-class:: classref-method\n\n``int`` **get_blend**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_blend>`\n\nReturns the blend value from a control map pixel.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_filled_image:\n\n.. rst-class:: classref-method\n\n``Image`` **get_filled_image**\\ (\\ size\\: ``Vector2i``, color\\: ``Color``, create_mipmaps\\: ``bool``, format\\: Image.Format\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_filled_image>`\n\nReturns an Image filled with a specified color and format.\n\nIf ``color.a < 0``, its filled with a checkered pattern multiplied by ``color.rgb``.\n\nThe behavior changes if a compressed format is requested:\n\n- If the editor is running and the format is DXT1, DXT5, or BPTC_RGBA, it returns a filled image in the requested color and format.\n\n- All other compressed formats return a blank image in that format.\n\nThe reason for this is the Image compression library is available only in the editor. And it is unreliable, offering little control over the output format, choosing automatically and often wrong. We have selected a few compressed formats it gets right.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_min_max:\n\n.. rst-class:: classref-method\n\n``Vector2`` **get_min_max**\\ (\\ image\\: ``Image``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_min_max>`\n\nReturns the minimum and maximum r channel values of an Image. Used for heightmaps.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_overlay:\n\n.. rst-class:: classref-method\n\n``int`` **get_overlay**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_overlay>`\n\nReturns the overlay texture ID from a control map pixel.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_thumbnail:\n\n.. rst-class:: classref-method\n\n``Image`` **get_thumbnail**\\ (\\ image\\: ``Image``, size\\: ``Vector2i`` = Vector2i(256, 256)\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_thumbnail>`\n\nReturns an Image normalized and converted to RGB8. Used for creating a human viewable image of a heightmap, at any size.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_uv_rotation:\n\n.. rst-class:: classref-method\n\n``int`` **get_uv_rotation**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_uv_rotation>`\n\nReturns the texture rotation from a control map pixel. Values are 0 - 15, which provides degrees when multiplied by 22.5. (360/16).\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_get_uv_scale:\n\n.. rst-class:: classref-method\n\n``int`` **get_uv_scale**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_get_uv_scale>`\n\nReturns the texture scale modification from a control map pixel. Values are an index into the array `{ 0, 20, 40, 60, 80, -60, -40, -20 }`. 0 indicates no scale modification. Index 2 indicates a 40% increase in texture scale at that pixel. Index -1 or 7 indicates a -20% texture scale change.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_is_auto:\n\n.. rst-class:: classref-method\n\n``bool`` **is_auto**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_is_auto>`\n\nReturns true if the control map pixel has the autoshader bit set.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_is_hole:\n\n.. rst-class:: classref-method\n\n``bool`` **is_hole**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_is_hole>`\n\nReturns true if the control map pixel has the hole bit set.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_is_nav:\n\n.. rst-class:: classref-method\n\n``bool`` **is_nav**\\ (\\ pixel\\: ``int``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_is_nav>`\n\nReturns true if the control map pixel has the nav bit set.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_load_image:\n\n.. rst-class:: classref-method\n\n``Image`` **load_image**\\ (\\ file_name\\: ``String``, cache_mode\\: ``int`` = 0, r16_height_range\\: ``Vector2`` = Vector2(0, 255), r16_size\\: ``Vector2i`` = Vector2i(0, 0)\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_load_image>`\n\nLoads a file from disk and returns an Image.\n\n\\ ``filename`` - The file name on disk to load. Loads EXR, R16/RAW, PNG, or a ResourceLoader format (jpg, res, tres, etc).\n\n\\ ``cache_mode`` - Send this flag to the resource loader to force caching or not.\n\n\\ ``height_range`` - Heights for R16 format. x=Min & y=Max value ranges. Required for R16 import.\n\n\\ ``size`` - Image dimensions for R16 format. Default (0,0) auto detects size, assuming square images. Required for non-square R16.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_location_to_filename:\n\n.. rst-class:: classref-method\n\n``String`` **location_to_filename**\\ (\\ region_location\\: ``Vector2i``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_location_to_filename>`\n\nConverts a region location like ``(-1, 2)`` to a file name string like ``terrain3d-01_02.res``. - is negative, \\_ is positive.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_luminance_to_height:\n\n.. rst-class:: classref-method\n\n``Image`` **luminance_to_height**\\ (\\ src_rgb\\: ``Image``\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_luminance_to_height>`\n\nGenerates a greyscale RGB8 height texture from the luminance values of the source image.\n\n.. rst-class:: classref-item-separator\n\n----\n\n.. _class_Terrain3DUtil_method_pack_image:\n\n.. rst-class:: classref-method\n\n``Image`` **pack_image**\\ (\\ src_rgb\\: ``Image``, src_a\\: ``Image``, src_ao\\: ``Image``, invert_green\\: ``bool`` = false, invert_alpha\\: ``bool`` = false, normalize_alpha\\: ``bool`` = false, alpha_channel\\: ``int`` = 0, ao_channel\\: ``int`` = 0\\ ) |static| :ref:`🔗<class_Terrain3DUtil_method_pack_image>`\n\nReturns an RGBA Image packed for terrain usage.\n\n- ``src_rgb`` - The source Image for the RGB channels.\n\n- ``src_a`` - The source image for the A channel.\n\n- ``invert_green`` - Inverts the green channel to convert between OpenGL and DirectX normal maps.\n\n- ``invert_alpha`` - Inverts the alpha channel to convert between Roughness and Smoothness maps.\n\n- ``normalize_alpha`` - Normalizes the alpha channel to use full range for height map.\n\n- ``alpha_channel`` - The channel index (0-3: R,G,B,A) to use from src_a for the alpha channel.\n\n- ``ao_channel`` - The channel index (0-3: R,G,B,A) to use from src_ao for the ambient occlusion channel.\n\n.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n.. |required| replace:: :abbr:`required (This method is required to be overridden when extending its base class.)`\n.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)`\n.. |void| replace:: :abbr:`void (No return value.)`\n"
  },
  {
    "path": "doc/api/index.rst",
    "content": ":github_url: hide\n:allow_comments: False\n\n.. DO NOT EDIT THIS FILE!!!\n.. Generated automatically from Godot engine sources.\n.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py.\n\n.. _doc_class_reference:\n\nAll classes\n===========\n\nVariant types\n=============\n\n.. toctree::\n    :maxdepth: 1\n    :name: toc-class-ref-variants\n\n    class_variant\n    class_terrain3d\n    class_terrain3dassets\n    class_terrain3dcollision\n    class_terrain3ddata\n    class_terrain3deditor\n    class_terrain3dinstancer\n    class_terrain3dmaterial\n    class_terrain3dmeshasset\n    class_terrain3dregion\n    class_terrain3dtextureasset\n    class_terrain3dutil\n\n"
  },
  {
    "path": "doc/build_docs.sh",
    "content": "#!/bin/bash\nGODOT=/c/gd/bin/Godot_v4.5.1-stable_win64.exe\nMAKERST=/c/gd/godot/doc/tools/make_rst.py\nREPO=`git rev-parse --show-toplevel`\n\npushd $REPO\n\necho --- Running Godot to dump XML files\ncd $REPO/project\n$GODOT --doctool ../doc --gdextension-docs\n\ncd $REPO/doc\n\necho --- Running make_rst.py to produce sphinx output\n$MAKERST --verbose --filter Terrain3D --output api path doc_classes/ 2>&1 | egrep -v 'Unresolved (type|enum)'\n\necho --- Generating html\nmake clean\nmake html 2>&1 | grep -Pv 'WARNING: undefined label: (?!'\\''class_terrain3d)' | egrep -v '(local id not found|copying images|writing output|reading sources|toctree contains reference .+api/class_variant)'\n\nstart _build/html/index.html\npopd\n"
  },
  {
    "path": "doc/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# For the full list of built-in configuration values, see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Project information -----------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information\n\nproject = 'Terrain3D'\ncopyright = '2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors'\nauthor = 'Cory Petkovsek, Roope Palmroos, and Contributors'\nrelease = '1.1.0'\n\n# -- General configuration ---------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration\n\n# TODO sphinx_tabs used in programming_languages.rst requires sphinx < 9. \n# Migrate to sphinx-design and sphinx 9+, then we can use tabs in markdown\nextensions = ['myst_parser', 'sphinx_rtd_dark_mode', 'sphinx_tabs.tabs']\n\nmyst_heading_anchors = 3\ndefault_dark_mode = False\n\ntemplates_path = ['_templates']\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n\n# -- Options for HTML output -------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output\n\nhtml_theme = 'sphinx_rtd_theme'\nhtml_static_path = ['_static']\nhtml_css_files = ['theme_overrides.css']"
  },
  {
    "path": "doc/doc_classes/Terrain3D.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3D\" inherits=\"Node3D\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tTerrain3D is a high performance, editable terrain system for Godot 4. It provides a clipmap based terrain that supports terrains from 64x64m up to 65.5x65.5km with multiple LODs, 32 textures, and editor tools for importing or creating terrains.\n\t\tThis class handles mesh generation, and management of the whole system. See [url=https://terrain3d.readthedocs.io/en/stable/docs/system_architecture.html]System Architecture[/url] for design details.\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"bake_mesh\" qualifiers=\"const\">\n\t\t\t<return type=\"Mesh\" />\n\t\t\t<param index=\"0\" name=\"lod\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"filter\" type=\"int\" enum=\"Terrain3DData.HeightFilter\" default=\"0\" />\n\t\t\t<description>\n\t\t\t\tGenerates a static ArrayMesh for the terrain.\n\t\t\t\t[code skip-lint]lod[/code] - Determines the granularity of the generated mesh. The range is 0-8. 4 is recommended.\n\t\t\t\t[code skip-lint]filter[/code] - Controls how vertex Y coordinates are generated from the height map. See [enum Terrain3DData.HeightFilter].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"generate_nav_mesh_source_geometry\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedVector3Array\" />\n\t\t\t<param index=\"0\" name=\"global_aabb\" type=\"AABB\" />\n\t\t\t<param index=\"1\" name=\"require_nav\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tGenerates source geometry faces for input to nav mesh baking. Geometry is only generated where there are no holes and the terrain has been painted as navigable.\n\t\t\t\t[code skip-lint]global_aabb[/code] - If non-empty, geometry will be generated only within this AABB. If empty, geometry will be generated for the entire terrain.\n\t\t\t\t[code skip-lint]require_nav[/code] - If true, this function will only generate geometry for terrain marked navigable. Otherwise, geometry is generated for the entire terrain within the AABB (which can be useful for dynamic and/or runtime nav mesh baking).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_camera\" qualifiers=\"const\">\n\t\t\t<return type=\"Camera3D\" />\n\t\t\t<description>\n\t\t\t\tReturns the camera the terrain is currently tracking for position, if not overridden by [member clipmap_target]. See [method set_camera].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_clipmap_target_position\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the position on which the terrain mesh is centered, which may be the camera or a target node. See [member clipmap_target].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_collision_target_position\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the position on which the terrain collision is centered, which may be the camera or a target node. See [member collision_target].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_editor\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3DEditor\" />\n\t\t\t<description>\n\t\t\t\tReturns the current Terrain3DEditor instance, if it has been set.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_intersection\">\n\t\t\t<return type=\"Vector3\" />\n\t\t\t<param index=\"0\" name=\"src_pos\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"direction\" type=\"Vector3\" />\n\t\t\t<param index=\"2\" name=\"gpu_mode\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tCasts a ray from [code skip-lint]src_pos[/code] pointing towards [code skip-lint]direction[/code], attempting to intersect the terrain. This operation is does not use physics and is not a typical raycast, so enabling collision is unnecessary. This function likely won't work if src_pos is below the terrain.\n\n\t\t\t\tThis function can operate in one of two modes selected by [code skip-lint]gpu_mode[/code]:\n\t\t\t\t- If gpu_mode is disabled (default), it raymarches from src_pos until the terrain is intersected, up to 4000m away. This works with one function call, and can only intersect the terrain where regions exist. It is slower than gpu_mode and gets increasingly slower the farther away the terrain is, though you may not notice.\n\n\t\t\t\t- If gpu_mode is enabled, it uses the GPU to detect the mouse. This works wherever the terrain is visible, even outside of regions, but may need to be called twice.\n\n\t\t\t\tGPU mode places a camera at the specified point and \"looks\" at the terrain. It uses the depth texture to determine how far away the intersection point is. It requires the use of an editor render layer, (default 32, set with [member mouse_layer]) while using this function.\n\n\t\t\t\tThe main caveats of using this mode is that the call to get_intersection() requests a viewport be drawn, but cannot wait for it to finish as there is no \"await\" in C++ and no force draw function in Godot. So the return value is one frame behind, and invalid on the first call. This also means the function cannot be used more than once per frame. This mode works well when used continuously, once per frame, where one frame of difference won't matter. The editor uses this mode to place the mouse cursor decal.\n\n\t\t\t\tThis mode can also be used by your plugins and games, such as a space ship firing lasers at the terrain and causing an explosion at the hit point. However if the calls aren't continuous, eg driven by the mouse, you'll need to call once to capture the viewport image, wait for it to be drawn, then call again to get the result:\n\t\t\t\t[codeblock]\n\t\t\t\tvar target_point = terrain.get_intersection(camera_pos, camera_dir, true)\n\t\t\t\tawait RenderingServer.frame_post_draw\n\t\t\t\ttarget_point = terrain.get_intersection(camera_pos, camera_dir, true)\n\t\t\t\t[/codeblock]\n\n\t\t\t\tPossible return values:\n\t\t\t\t- If the terrain is hit, the intersection point is returned.\n\t\t\t\t- If there is no intersection, eg. the ray points towards the sky, it returns the maximum double float value [code skip-lint]Vector3(3.402823466e+38F,...)[/code]. You can check this case with this code: [code skip-lint]if point.z &gt; 3.4e38:[/code]\n\t\t\t\t- On error, it returns [code skip-lint]Vector3(NAN, NAN, NAN)[/code] and prints a message to the console.\n\n\t\t\t\tAlso see [method get_raycast_result] and [method Terrain3DData.get_height] for alternative functions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_plugin\" qualifiers=\"const\">\n\t\t\t<return type=\"Object\" />\n\t\t\t<description>\n\t\t\t\tReturns the EditorPlugin Object connected to Terrain3D.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_raycast_result\" qualifiers=\"const\">\n\t\t\t<return type=\"Dictionary\" />\n\t\t\t<param index=\"0\" name=\"src_pos\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"direction\" type=\"Vector3\" />\n\t\t\t<param index=\"2\" name=\"collision_mask\" type=\"int\" default=\"4294967295\" />\n\t\t\t<param index=\"3\" name=\"exclude_terrain\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tThis is a helper function that creates a general physics-based raycast and returns the resulting dictionary; it's not limited to terrain use. Raycasts can only detect collision. It is used by our editor using the `on_collision` option to instance on non-terrain meshes.\n\t\t\t\tDirection is added to src_pos and includes magnitude. So to run a raycast from (100, 100, 100) to the ground 100m below, direction would be (0, -110, 0) with margin.\n\t\t\t\tCollision_mask has the physics layers the query will detect as a bitmask. By default, all collision layers are detected.\n\t\t\t\tSee [url=https://docs.godotengine.org/en/stable/classes/class_physicsdirectspacestate3d.html#class-physicsdirectspacestate3d-method-intersect-ray]PhysicsDirectSpaceState3D.intersect_ray[/url] for how to interpret the resulting dictionary.\n\t\t\t\tAlso see [method get_intersection] and [method Terrain3DData.get_height] for alternative functions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_camera\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"camera\" type=\"Camera3D\" />\n\t\t\t<description>\n\t\t\t\tSpecifies the camera on which the terrain centers. It attempts to aquire the camera from the active viewport.\n\t\t\t\tIf the camera is instanced in a sub scene or by code, Terrain3D might not be able to find it, will issue an error, and the terrain will center at (0,0,0) causing LODs to not update until a trackable node is set.\n\t\t\t\tEither specify the camera, or specify the clipmap and/or collision targets. It will use the targets first and fall back to the camera if they are null.\n\t\t\t\tSee [member clipmap_target] and [member collision_target].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_editor\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"editor\" type=\"Terrain3DEditor\" />\n\t\t\t<description>\n\t\t\t\tSets the current Terrain3DEditor instance.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_plugin\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"plugin\" type=\"Object\" />\n\t\t\t<description>\n\t\t\t\tSets the EditorPlugin Object connected to Terrain3D.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"snap\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tQueues the terrain mesh and collision to snap their positions to the target nodes on the next physics frame. Typically this only happens if the targets have moved sufficiently far. See [member clipmap_target] and [member collision_target].\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"assets\" type=\"Terrain3DAssets\" setter=\"set_assets\" getter=\"get_assets\">\n\t\t\tThe list of texture and mesh assets used by Terrain3D. You can optionally save this as an external [code skip-lint].tres[/code] text file if you wish to share it with Terrain3D nodes in other scenes.\n\t\t</member>\n\t\t<member name=\"buffer_shader_override\" type=\"Shader\" setter=\"set_buffer_shader_override\" getter=\"get_buffer_shader_override\">\n\t\t</member>\n\t\t<member name=\"buffer_shader_override_enabled\" type=\"bool\" setter=\"set_buffer_shader_override_enabled\" getter=\"is_buffer_shader_override_enabled\" default=\"false\">\n\t\t</member>\n\t\t<member name=\"cast_shadows\" type=\"int\" setter=\"set_cast_shadows\" getter=\"get_cast_shadows\" enum=\"RenderingServer.ShadowCastingSetting\" default=\"1\">\n\t\t\tTells the renderer how to cast shadows from the terrain onto other objects. This sets [code skip-lint]GeometryInstance3D.ShadowCastingSetting[/code] in the engine.\n\t\t</member>\n\t\t<member name=\"clipmap_target\" type=\"Node3D\" setter=\"set_clipmap_target\" getter=\"get_clipmap_target\">\n\t\t\tThe terrain clipmap mesh and lods will center itself at the position of this node. If null, or if in the editor, it will fall back to the camera position. See [method set_camera].\n\t\t</member>\n\t\t<member name=\"collision\" type=\"Terrain3DCollision\" setter=\"\" getter=\"get_collision\">\n\t\t\tThe active [Terrain3DCollision] object.\n\t\t</member>\n\t\t<member name=\"collision_layer\" type=\"int\" setter=\"set_collision_layer\" getter=\"get_collision_layer\" default=\"1\">\n\t\t\tThe physics layers the terrain lives on. Sets [code skip-lint]CollisionObject3D.collision_layer[/code].\n\t\t\tAlias for [member Terrain3DCollision.layer].\n\t\t\tAlso see [member collision_mask].\n\t\t</member>\n\t\t<member name=\"collision_mask\" type=\"int\" setter=\"set_collision_mask\" getter=\"get_collision_mask\" default=\"1\">\n\t\t\tThe physics layers the physics body scans for colliding objects. Sets [code skip-lint]CollisionObject3D.collision_mask[/code].\n\t\t\tAlias for [member Terrain3DCollision.mask].\n\t\t\tAlso see [member collision_layer].\n\t\t</member>\n\t\t<member name=\"collision_mode\" type=\"int\" setter=\"set_collision_mode\" getter=\"get_collision_mode\" enum=\"Terrain3DCollision.CollisionMode\" default=\"1\">\n\t\t\tThe selected mode determines if collision is generated and how. See [enum Terrain3DCollision.CollisionMode] for details.\n\t\t\tAlias for [member Terrain3DCollision.mode].\n\t\t</member>\n\t\t<member name=\"collision_priority\" type=\"float\" setter=\"set_collision_priority\" getter=\"get_collision_priority\" default=\"1.0\">\n\t\t\tThe priority with which the physics server uses to solve collisions. The higher the priority, the lower the penetration of a colliding object. Sets [code skip-lint]CollisionObject3D.collision_priority[/code].\n\t\t\tAlias for [member Terrain3DCollision.priority].\n\t\t</member>\n\t\t<member name=\"collision_radius\" type=\"int\" setter=\"set_collision_radius\" getter=\"get_collision_radius\" default=\"64\">\n\t\t\tIf [member collision_mode] is Dynamic, this is the distance range within which collision shapes will be generated.\n\t\t\tAlias for [member Terrain3DCollision.radius].\n\t\t</member>\n\t\t<member name=\"collision_shape_size\" type=\"int\" setter=\"set_collision_shape_size\" getter=\"get_collision_shape_size\" default=\"16\">\n\t\t\tIf [member collision_mode] is Dynamic, this is the size of each collision shape.\n\t\t\tAlias for [member Terrain3DCollision.shape_size].\n\t\t</member>\n\t\t<member name=\"collision_target\" type=\"Node3D\" setter=\"set_collision_target\" getter=\"get_collision_target\">\n\t\t\tIn dynamic mode, the terrain collision will center itself at the position of this node. If null, it will fall back to the [member clipmap_target] position and failing that will use the camera position. The camera is always used in the editor. See [method set_camera].\n\t\t</member>\n\t\t<member name=\"cull_margin\" type=\"float\" setter=\"set_cull_margin\" getter=\"get_cull_margin\" default=\"0.0\">\n\t\t\tThis margin is added to the vertical component of the terrain mesh bounding boxes (AABB). The terrain already sets its AABB from [method Terrain3DData.get_height_range], which is calculated while sculpting. This setting only needs to be used if the shader has expanded the terrain beyond the AABB and the terrain meshes are being culled at certain viewing angles. This might happen from using [member Terrain3DMaterial.world_background] with NOISE and a height value larger than the terrain heights. This setting is similar to [code skip-lint]GeometryInstance3D.extra_cull_margin[/code], but it only affects the Y axis.\n\t\t</member>\n\t\t<member name=\"data\" type=\"Terrain3DData\" setter=\"\" getter=\"get_data\">\n\t\t\tThis class manages loading, saving, adding, and removing of Terrain3DRegions and access to their content.\n\t\t</member>\n\t\t<member name=\"data_directory\" type=\"String\" setter=\"set_data_directory\" getter=\"get_data_directory\" default=\"&quot;&quot;\">\n\t\t\tThe directory where terrain data will be saved to and loaded from.\n\t\t</member>\n\t\t<member name=\"debug_level\" type=\"int\" setter=\"set_debug_level\" getter=\"get_debug_level\" enum=\"Terrain3D.DebugLevel\" default=\"0\">\n\t\t\tThe verbosity of debug messages printed to the console. Errors and warnings are always printed. This can also be set via command line using [code skip-lint]--terrain3d-debug=LEVEL[/code] where [code skip-lint]LEVEL[/code] is one of [code skip-lint]ERROR, INFO, DEBUG, EXTREME[/code]. The last includes continuously recurring messages like position updates for the mesh as the camera moves around.\n\t\t</member>\n\t\t<member name=\"displacement_scale\" type=\"float\" setter=\"set_displacement_scale\" getter=\"get_displacement_scale\" default=\"1.0\">\n\t\t\tA global multiplier for all displaced textures. This is the maximum distance that 2 adjacent verticies can be vertically seperated by. Setting this 1.0 would mean a maximum of + 0.5m, and -0.5m deviation from the collision mesh.\n\t\t\tAlias for [member Terrain3DMaterial.displacement_scale].\n\t\t</member>\n\t\t<member name=\"displacement_sharpness\" type=\"float\" setter=\"set_displacement_sharpness\" getter=\"get_displacement_sharpness\" default=\"0.25\">\n\t\t\tAdjusts the transition between textures. When set at `1.0`, the blending of displacment between textures will match the aldebo/normal blend sharpness exactly. Lower values will have a softer transition, avoiding harsh shapes, without compromising the abldeo and normal blend sharpness. If set at or very near to `0.0`, it is possible that more displaced textures can affect less displaced textures at low blend values even if not visible.\n\t\t\tAlias for [member Terrain3DMaterial.displacement_sharpness].\n\t\t</member>\n\t\t<member name=\"free_editor_textures\" type=\"bool\" setter=\"set_free_editor_textures\" getter=\"get_free_editor_textures\" default=\"true\">\n\t\t\tFrees ground textures used for editing in _ready(). These textures are used to generate the TextureArrays, so if you don't change any [Terrain3DTextureAsset] settings in game, this can be enabled. Also reloads the texture asset list in _enter_tree() in case you load scenes via code and need the textures again. Calls [method Terrain3DAssets.clear_textures].\n\t\t</member>\n\t\t<member name=\"gi_mode\" type=\"int\" setter=\"set_gi_mode\" getter=\"get_gi_mode\" enum=\"GeometryInstance3D.GIMode\" default=\"1\">\n\t\t\tTells the renderer which global illumination mode to use for the terrain mesh. This sets [code skip-lint]GeometryInstance3D.gi_mode[/code] in the engine.\n\t\t</member>\n\t\t<member name=\"instancer\" type=\"Terrain3DInstancer\" setter=\"\" getter=\"get_instancer\">\n\t\t\tThe active [Terrain3DInstancer] object.\n\t\t</member>\n\t\t<member name=\"instancer_mode\" type=\"int\" setter=\"set_instancer_mode\" getter=\"get_instancer_mode\" enum=\"Terrain3DInstancer.InstancerMode\" default=\"1\">\n\t\t\tNormal - Generates MultiMeshInstance3Ds and renders all instances as normal.\n\t\t\tDisabled - prevents the instancer from creating any MultiMeshInstance3Ds.\n\t\t\tAlias for [member Terrain3DInstancer.mode].\n\t\t</member>\n\t\t<member name=\"label_distance\" type=\"float\" setter=\"set_label_distance\" getter=\"get_label_distance\" default=\"0.0\">\n\t\t\tIf label_distance is non-zero (try 1024-4096) it will generate and display region coordinates in the viewport so you can identify the exact region files you are editing. This setting is the visible distance of the labels.\n\t\t</member>\n\t\t<member name=\"label_size\" type=\"int\" setter=\"set_label_size\" getter=\"get_label_size\" default=\"48\">\n\t\t\tSets the font size for region labels. See [member label_distance].\n\t\t</member>\n\t\t<member name=\"material\" type=\"Terrain3DMaterial\" setter=\"set_material\" getter=\"get_material\">\n\t\t\tA custom material for Terrain3D. You can optionally save this as an external [code skip-lint].tres[/code] text file if you wish to share it with instances of Terrain3D in other scenes. See [Terrain3DMaterial].\n\t\t</member>\n\t\t<member name=\"mesh_lods\" type=\"int\" setter=\"set_mesh_lods\" getter=\"get_mesh_lods\" default=\"7\">\n\t\t\tThe number of lods generated for the terrain meshes. Enable wireframe mode in the viewport to see them.\n\t\t</member>\n\t\t<member name=\"mesh_size\" type=\"int\" setter=\"set_mesh_size\" getter=\"get_mesh_size\" default=\"48\">\n\t\t\tThe correlated size of the terrain meshes. Lod0 has [code skip-lint]4*mesh_size + 2[/code] quads per side. E.g. when mesh_size=8, lod0 has 34 quads to a side, including 2 quads for seams.\n\t\t</member>\n\t\t<member name=\"mouse_layer\" type=\"int\" setter=\"set_mouse_layer\" getter=\"get_mouse_layer\" default=\"32\">\n\t\t\tGodot supports 32 render layers. For most objects, only layers 1-20 are available for selection in the inspector. 21-32 are settable via code, and are considered reserved for editor plugins.\n\t\t\tThis variable sets the editor render layer (21-32) to be used by [code skip-lint]get_intersection[/code], which the mouse cursor uses.\n\t\t\tYou may place other objects on this layer, however [code skip-lint]get_intersection[/code] will report intersections with them. So either dedicate this layer to Terrain3D, or if you must use all 32 layers, dedicate this one during editing or when using [code skip-lint]get_intersection[/code], and then you can use it during game play.\n\t\t\tSee [method get_intersection].\n\t\t</member>\n\t\t<member name=\"ocean_cast_shadows\" type=\"int\" setter=\"set_ocean_cast_shadows\" getter=\"get_ocean_cast_shadows\" enum=\"RenderingServer.ShadowCastingSetting\" default=\"0\">\n\t\t\tTells the renderer how to cast shadows from the ocean onto other objects. This sets [code skip-lint]GeometryInstance3D.ShadowCastingSetting[/code] in the engine.\n\t\t</member>\n\t\t<member name=\"ocean_cull_margin\" type=\"float\" setter=\"set_ocean_cull_margin\" getter=\"get_ocean_cull_margin\" default=\"20.0\">\n\t\t\tThis margin is added to the vertical component of the ocean mesh bounding boxes (AABB). When you set the height of your waves in the shader, this margin should be adjusted. If it's too small, the meshes will clip at certain camera angles. If you set it too large, the renderer may have slightly more work to do. This setting is similar to [code skip-lint]GeometryInstance3D.extra_cull_margin[/code], but it only affects the Y axis.\n\t\t</member>\n\t\t<member name=\"ocean_enabled\" type=\"bool\" setter=\"set_ocean_enabled\" getter=\"is_ocean_enabled\" default=\"false\">\n\t\t\tGenerates another clipmap mesh, which you can apply an ocean shader to and configure independently of the terrain mesh.\n\t\t</member>\n\t\t<member name=\"ocean_gi_mode\" type=\"int\" setter=\"set_ocean_gi_mode\" getter=\"get_ocean_gi_mode\" enum=\"GeometryInstance3D.GIMode\" default=\"0\">\n\t\t\tTells the renderer which global illumination mode to use for the ocean mesh. This sets [code skip-lint]GeometryInstance3D.gi_mode[/code] in the engine.\n\t\t</member>\n\t\t<member name=\"ocean_light_target\" type=\"Node3D\" setter=\"set_ocean_light_target\" getter=\"get_ocean_light_target\">\n\t\t\tThis sets the _light_direction and _light_color uniforms in the ocean shader, if they are present. You can use this for light scattering, detecting if the light is above the horizon, albedo coloring, etc.\n\t\t</member>\n\t\t<member name=\"ocean_material\" type=\"Material\" setter=\"set_ocean_material\" getter=\"get_ocean_material\">\n\t\t\tYou can assign a [code skip-lint]StandardMaterial[/code] here for testing, but you really need a [code skip-lint]ShaderMaterial[/code]. Start with the example in [code skip-lint]addons/terrain_3d/extras/shaders/M_ocean.tres[/code], which you can build on. Or use any of the ocean shaders available around the internet, provided you set `skip_vertex_transform` and copy the geomorphing code from our `vertex()` shader, which will properly handle the LOD transitions on the clipmap.\n\t\t</member>\n\t\t<member name=\"ocean_mesh_lods\" type=\"int\" setter=\"set_ocean_mesh_lods\" getter=\"get_ocean_mesh_lods\" default=\"7\">\n\t\t\tThe number of lods generated for the ocean meshes. Enable wireframe mode in the viewport to see them.\n\t\t</member>\n\t\t<member name=\"ocean_mesh_size\" type=\"int\" setter=\"set_ocean_mesh_size\" getter=\"get_ocean_mesh_size\" default=\"32\">\n\t\t\tThe correlated size of the ocean meshes. Lod0 has [code skip-lint]4*ocean_mesh_size + 2[/code] quads per side. E.g. when ocean_mesh_size=8, lod0 has 34 quads to a side, including 2 quads for seams.\n\t\t</member>\n\t\t<member name=\"ocean_render_layers\" type=\"int\" setter=\"set_ocean_render_layers\" getter=\"get_ocean_render_layers\" default=\"1\">\n\t\t\tThe render layers the ocean is drawn on. This sets [code skip-lint]VisualInstance3D.layers[/code] in the engine.\n\t\t</member>\n\t\t<member name=\"ocean_tessellation_level\" type=\"int\" setter=\"set_ocean_tessellation_level\" getter=\"get_ocean_tessellation_level\" default=\"0\">\n\t\t\tThis setting creates up to 6 additional subdivisions of the ocean mesh below LOD0, which provides more vertices for the vertex shader if desired. You can see it in wireframe mode.\n\t\t</member>\n\t\t<member name=\"ocean_vertex_spacing\" type=\"float\" setter=\"set_ocean_vertex_spacing\" getter=\"get_ocean_vertex_spacing\" default=\"4.0\">\n\t\t\tThe distance between vertices, settable up to 100. Godot units are typically considered to be meters. This laterally scales the ocean vertices on X and Z axes, but does not scale wave height.\n\t\t</member>\n\t\t<member name=\"physics_material\" type=\"PhysicsMaterial\" setter=\"set_physics_material\" getter=\"get_physics_material\">\n\t\t\tApplies a [code skip-lint]PhysicsMaterial[/code] override to the entire terrain StaticBody.\n\t\t\tAlias for [member Terrain3DCollision.physics_material] See that entry for details.\n\t\t</member>\n\t\t<member name=\"region_size\" type=\"int\" setter=\"change_region_size\" getter=\"get_region_size\" enum=\"Terrain3D.RegionSize\" default=\"256\">\n\t\t\tThe number of vertices in each region, and the number of pixels for each map in [Terrain3DRegion]. 1 pixel always corresponds to 1 vertex. [member Terrain3D.vertex_spacing] laterally scales regions, but does not change the number of vertices or pixels in each.\n\t\t\tThere is no undo for this operation. However you can apply it again to reslice, as long as your data doesn't hit the maximum boundaries.\n\t\t</member>\n\t\t<member name=\"render_layers\" type=\"int\" setter=\"set_render_layers\" getter=\"get_render_layers\" default=\"2147483649\">\n\t\t\tThe render layers the terrain is drawn on. This sets [code skip-lint]VisualInstance3D.layers[/code] in the engine. The defaults is layer 1 and 32 (for the mouse cursor). When you set this via code, make sure the layer for [member mouse_layer] is included, or set that variable again after this so that the mouse cursor and [method get_intersection] work.\n\t\t</member>\n\t\t<member name=\"save_16_bit\" type=\"bool\" setter=\"set_save_16_bit\" getter=\"get_save_16_bit\" default=\"false\">\n\t\t\tIf enabled, heightmaps are saved as 16-bit half-precision to reduce file size. Files are always loaded in 32-bit for editing. Upon save, a copy of the heightmap is converted to 16-bit for writing. It does not change what is currently in memory.\n\t\t\tThis process is lossy. 16-bit precision gets increasingly worse with every power of 2. At a height of 256m, the precision interval is .25m. At 512m it is .5m. At 1024m it is 1m. Saving a height of 1024.4m will be rounded down to 1024m.\n\t\t</member>\n\t\t<member name=\"show_autoshader\" type=\"bool\" setter=\"set_show_autoshader\" getter=\"get_show_autoshader\" default=\"false\">\n\t\t\tDisplays the area designated for use by the autoshader, which shows materials based upon slope.\n\t\t\tAlias for [member Terrain3DMaterial.show_autoshader].\n\t\t</member>\n\t\t<member name=\"show_checkered\" type=\"bool\" setter=\"set_show_checkered\" getter=\"get_show_checkered\" default=\"false\">\n\t\t\tShows a checkerboard display using a shader rendered pattern. This is turned on if the Texture List is empty.\n\t\t\tNote that when a blank texture slot is created, a 1k checkerboard texture is generated and stored in the texture slot. That takes VRAM. The two patterns have a slightly different scale.\n\t\t\tAlias for [member Terrain3DMaterial.show_checkered].\n\t\t</member>\n\t\t<member name=\"show_colormap\" type=\"bool\" setter=\"set_show_colormap\" getter=\"get_show_colormap\" default=\"false\">\n\t\t\tShows the color map in the albedo channel.\n\t\t\tAlias for [member Terrain3DMaterial.show_colormap].\n\t\t</member>\n\t\t<member name=\"show_contours\" type=\"bool\" setter=\"set_show_contours\" getter=\"get_show_contours\" default=\"false\">\n\t\t\tOverlays contour lines on the terrain. Customize the options in the material when enabled. Press `4` with the mouse in the viewport to toggle.\n\t\t\tAlias for [member Terrain3DMaterial.show_contours].\n\t\t</member>\n\t\t<member name=\"show_control_angle\" type=\"bool\" setter=\"set_show_control_angle\" getter=\"get_show_control_angle\" default=\"false\">\n\t\t\tAlbedo shows the painted angle. Orange means 0°, Yellow 270°, Cyan 180°, Violet 90°. Or warm colors towards -Z, cool colors +Z, greens/yellows +X, reds/blues -X. Draw all angles coming from the center of a circle for a better understanding.\n\t\t\tAlias for [member Terrain3DMaterial.show_control_angle].\n\t\t</member>\n\t\t<member name=\"show_control_blend\" type=\"bool\" setter=\"set_show_control_blend\" getter=\"get_show_control_blend\" default=\"false\">\n\t\t\tDisplays the values used to blend the textures. Blue shows the autoshader blending, red shows manually painted blending.\n\t\t\tAlias for [member Terrain3DMaterial.show_control_blend].\n\t\t</member>\n\t\t<member name=\"show_control_scale\" type=\"bool\" setter=\"set_show_control_scale\" getter=\"get_show_control_scale\" default=\"false\">\n\t\t\tAlbedo shows the painted scale. Larger scales are more red, smaller scales are more blue. 0.5 middle grey is the default 100% scale.\n\t\t\tAlias for [member Terrain3DMaterial.show_control_scale].\n\t\t</member>\n\t\t<member name=\"show_control_texture\" type=\"bool\" setter=\"set_show_control_texture\" getter=\"get_show_control_texture\" default=\"false\">\n\t\t\tAlbedo shows the base and overlay texture indices defined by the control map. Red pixels indicate the base texture, with brightness showing texture ids 0 to 31. Green pixels indicate the overlay texture. Yellow indicates both.\n\t\t\tAlias for [member Terrain3DMaterial.show_control_texture].\n\t\t</member>\n\t\t<member name=\"show_displacement_buffer\" type=\"bool\" setter=\"set_show_displacement_buffer\" getter=\"get_show_displacement_buffer\" default=\"false\">\n\t\t\tAlias for [member Terrain3DMaterial.show_displacement_buffer].\n\t\t</member>\n\t\t<member name=\"show_grey\" type=\"bool\" setter=\"set_show_grey\" getter=\"get_show_grey\" default=\"false\">\n\t\t\tAlbedo is set to 0.2 grey.\n\t\t\tAlias for [member Terrain3DMaterial.show_grey].\n\t\t</member>\n\t\t<member name=\"show_grid\" type=\"bool\" setter=\"set_show_region_grid\" getter=\"get_show_region_grid\" default=\"false\">\n\t\t\tAlias for [member Terrain3DMaterial.show_region_grid]. Press `1` with the mouse in the viewport to toggle.\n\t\t</member>\n\t\t<member name=\"show_heightmap\" type=\"bool\" setter=\"set_show_heightmap\" getter=\"get_show_heightmap\" default=\"false\">\n\t\t\tAlbedo is a white to black gradient depending on height. The gradient is scaled to a height of 300, so above that or far below 0 will be all white or black.\n\t\t\tAlias for [member Terrain3DMaterial.show_heightmap].\n\t\t</member>\n\t\t<member name=\"show_instancer_grid\" type=\"bool\" setter=\"set_show_instancer_grid\" getter=\"get_show_instancer_grid\" default=\"false\">\n\t\t\tOverlays the 32x32m instancer grid on the terrain, which shows how the instancer data is partitioned. Press `2` with the mouse in the viewport to toggle.\n\t\t\tAlias for [member Terrain3DMaterial.show_instancer_grid].\n\t\t</member>\n\t\t<member name=\"show_jaggedness\" type=\"bool\" setter=\"set_show_jaggedness\" getter=\"get_show_jaggedness\" default=\"false\">\n\t\t\tHighlights non-smooth areas of the terrain. Jagged peaks, troughs, or edges that are a bit rough with sharp angles between vertices.\n\t\t\tAlias for [member Terrain3DMaterial.show_jaggedness].\n\t\t</member>\n\t\t<member name=\"show_navigation\" type=\"bool\" setter=\"set_show_navigation\" getter=\"get_show_navigation\" default=\"false\">\n\t\t\tDisplays the area designated for generating the navigation mesh.\n\t\t\tAlias for [member Terrain3DMaterial.show_navigation].\n\t\t</member>\n\t\t<member name=\"show_region_grid\" type=\"bool\" setter=\"set_show_region_grid\" getter=\"get_show_region_grid\" default=\"false\">\n\t\t\tOverlays the region grid on the terrain. This is more accurate than the region grid gizmo for determining where the region border is when editing. Press `1` with the mouse in the viewport to toggle.\n\t\t\tAlias for [member Terrain3DMaterial.show_region_grid].\n\t\t</member>\n\t\t<member name=\"show_roughmap\" type=\"bool\" setter=\"set_show_roughmap\" getter=\"get_show_roughmap\" default=\"false\">\n\t\t\tAlbedo is set to the roughness modification map as grey scale. Middle grey, 0.5 means no roughness modification. Black would be high gloss while white is very rough.\n\t\t\tAlias for [member Terrain3DMaterial.show_roughmap].\n\t\t</member>\n\t\t<member name=\"show_texture_albedo\" type=\"bool\" setter=\"set_show_texture_albedo\" getter=\"get_show_texture_albedo\" default=\"false\">\n\t\t\tAlbedo textures are shown only. Other channels are excluded.\n\t\t\tAlias for [member Terrain3DMaterial.show_texture_albedo].\n\t\t</member>\n\t\t<member name=\"show_texture_ao\" type=\"bool\" setter=\"set_show_texture_ao\" getter=\"get_show_texture_ao\" default=\"false\">\n\t\t\tAlbedo is set to the painted Ambient Occlusion textures.\n\t\t\tAlias for [member Terrain3DMaterial.show_texture_ao].\n\t\t</member>\n\t\t<member name=\"show_texture_height\" type=\"bool\" setter=\"set_show_texture_height\" getter=\"get_show_texture_height\" default=\"false\">\n\t\t\tAlbedo is set to the painted Height textures.\n\t\t\tAlias for [member Terrain3DMaterial.show_texture_height].\n\t\t</member>\n\t\t<member name=\"show_texture_normal\" type=\"bool\" setter=\"set_show_texture_normal\" getter=\"get_show_texture_normal\" default=\"false\">\n\t\t\tAlbedo is set to the painted Normal textures.\n\t\t\tAlias for [member Terrain3DMaterial.show_texture_normal].\n\t\t</member>\n\t\t<member name=\"show_texture_rough\" type=\"bool\" setter=\"set_show_texture_rough\" getter=\"get_show_texture_rough\" default=\"false\">\n\t\t\tAlbedo is set to the painted Roughness textures. This is different from the roughness modification map above.\n\t\t\tAlias for [member Terrain3DMaterial.show_texture_rough].\n\t\t</member>\n\t\t<member name=\"show_vertex_grid\" type=\"bool\" setter=\"set_show_vertex_grid\" getter=\"get_show_vertex_grid\" default=\"false\">\n\t\t\tOverlays the vertex grid on the terrain, showing where each vertex is. Press `3` with the mouse in the viewport to toggle.\n\t\t\tAlias for [member Terrain3DMaterial.show_vertex_grid].\n\t\t</member>\n\t\t<member name=\"tessellation_level\" type=\"int\" setter=\"set_tessellation_level\" getter=\"get_tessellation_level\" default=\"0\">\n\t\t\tEnables displacement using texture heights for additional mesh detail when set above 0. This creates up to 6 additional subdivisions of the terrain mesh below LOD0, and adds a displacement buffer configurable in the material. You can see this in wireframe mode. Set to 0 to disable displacement. See [member Terrain3DMaterial.show_displacement_buffer] and look at the Displacement Buffer debug view.\n\t\t</member>\n\t\t<member name=\"version\" type=\"String\" setter=\"\" getter=\"get_version\" default=\"&quot;1.1.0-dev&quot;\">\n\t\t\tThe current version of Terrain3D.\n\t\t</member>\n\t\t<member name=\"vertex_spacing\" type=\"float\" setter=\"set_vertex_spacing\" getter=\"get_vertex_spacing\" default=\"1.0\">\n\t\t\tThe distance between vertices, settable up to 100. Godot units are typically considered to be meters. This laterally scales the terrain on X and Z axes.\n\t\t\tThis variable changes the global position of landscape features. A mountain peak might be at (512, 512), but with a vertex spacing of 2.0 it is now located at (1024, 1024). It retains the same height.\n\t\t\tAll Terrain3D functions with a global_position expect an absolute global value. If you would normally use [method Terrain3DData.import_images] to import an image in the region at (-1024, -1024), with a vertex_spacing of 2, you'll need to import that image at (-2048, -2048) to place it in the same region.\n\t\t\tTo scale heights, export the height map and reimport it with a new height scale.\n\t\t</member>\n\t</members>\n\t<signals>\n\t\t<signal name=\"assets_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when [member assets] is changed.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"material_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when [member material] is changed.\n\t\t\t</description>\n\t\t</signal>\n\t</signals>\n\t<constants>\n\t\t<constant name=\"ERROR\" value=\"0\" enum=\"DebugLevel\">\n\t\t\tErrors and warnings always print.\n\t\t</constant>\n\t\t<constant name=\"INFO\" value=\"1\" enum=\"DebugLevel\">\n\t\t\tTypically every function call and other important informational messages.\n\t\t</constant>\n\t\t<constant name=\"DEBUG\" value=\"2\" enum=\"DebugLevel\">\n\t\t\tDetailed steps within functions.\n\t\t</constant>\n\t\t<constant name=\"EXTREME\" value=\"3\" enum=\"DebugLevel\">\n\t\t\tMessages for continuous operations like snapping and editing.\n\t\t</constant>\n\t\t<constant name=\"SIZE_64\" value=\"64\" enum=\"RegionSize\">\n\t\t\tThe region size is 64 x 64 meters, vertices, and pixels on Image maps.\n\t\t</constant>\n\t\t<constant name=\"SIZE_128\" value=\"128\" enum=\"RegionSize\">\n\t\t\tThe region size is 128 x 128 meters, vertices, and pixels on Image maps.\n\t\t</constant>\n\t\t<constant name=\"SIZE_256\" value=\"256\" enum=\"RegionSize\">\n\t\t\tThe region size is 256 x 256 meters, vertices, and pixels on Image maps. (default)\n\t\t</constant>\n\t\t<constant name=\"SIZE_512\" value=\"512\" enum=\"RegionSize\">\n\t\t\tThe region size is 512 x 512 meters, vertices, and pixels on Image maps.\n\t\t</constant>\n\t\t<constant name=\"SIZE_1024\" value=\"1024\" enum=\"RegionSize\">\n\t\t\tThe region size is 1024 x 1024 meters, vertices, and pixels on Image maps.\n\t\t</constant>\n\t\t<constant name=\"SIZE_2048\" value=\"2048\" enum=\"RegionSize\">\n\t\t\tThe region size is 2048 x 2048 meters, vertices, and pixels on Image maps.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DAssets.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DAssets\" inherits=\"Resource\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis class contains arrays of [Terrain3DTextureAsset] and [Terrain3DMeshAsset] resources. It is a savable resource, so you can save it to disk and use the same asset list in multiple scenes that use Terrain3D. The amount of data is small, so it can be saved as a git-friendly, text based .tres file or left within the scene file.\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"clear_textures\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"update\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tAfter textures are loaded, they are combined into a TextureArray. The originals remain in VRAM and are only used if the [Terrain3DTextureAsset] settings are changed and regenerating the TextureArrays are necessary. Use this function to clear the originals if not needed. It removes all textures from the asset list, freeing them if they are not referenced by other objects.\n\t\t\t\tUpdate will regenerate the texture arrays housing the textures drawn on the terrain. This will remove all textures and turn the terrain checkerboard.\n\t\t\t\tA similar [code skip-lint]clear_meshes[/code] is less useful so hasn't been included. However you can do the same thing with [code skip-lint]get_mesh_list().clear()[/code].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"create_mesh_thumbnails\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"id\" type=\"int\" default=\"-1\" />\n\t\t\t<param index=\"1\" name=\"size\" type=\"Vector2i\" default=\"Vector2i(512, 512)\" />\n\t\t\t<param index=\"2\" name=\"force\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tGenerates mesh asset preview thumbnails for the asset dock, stored within each mesh asset. Specify id -1 to generate all. By default, mesh thumbnails are not recreated if they already exist. Specify [code skip-lint]force[/code] to regenerate existing thumbnails.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_albedo_array_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the resource ID of the TextureArray generated from combining all albedo and height textures.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_mesh_asset\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3DMeshAsset\" />\n\t\t\t<param index=\"0\" name=\"id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the specified Terrain3DMeshAsset resource.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_mesh_count\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the number of mesh assets in the list.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_normal_array_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the resource ID of the TextureArray generated from combining all normal and roughness textures.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_ao_light_affects\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedFloat32Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of ao light affects values each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_ao_strengths\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedFloat32Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of AO strengths for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_asset\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3DTextureAsset\" />\n\t\t\t<param index=\"0\" name=\"id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the Terrain3DTextureAsset with the specified ID.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_colors\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedColorArray\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of albedo tints for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_count\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the number of texture slots used.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_detiles\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedVector2Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of detiling values for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_displacements\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedVector2Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of displacement offset and scale values for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_normal_depths\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedFloat32Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of normal strengths for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_roughness_mods\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedFloat32Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of roughness modification values for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_uv_scales\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedFloat32Array\" />\n\t\t\t<description>\n\t\t\t\tReturns the array of uv scale values for each texture asset, indexed by asset id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"save\">\n\t\t\t<return type=\"int\" enum=\"Error\" />\n\t\t\t<param index=\"0\" name=\"path\" type=\"String\" default=\"&quot;&quot;\" />\n\t\t\t<description>\n\t\t\t\tSaves this texture list resource to disk, if saved as an external [code skip-lint].tres[/code] or [code skip-lint].res[/code] resource file.\n\t\t\t\tpath - specifies a directory and file name to use from now on.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_mesh_asset\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"id\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"mesh\" type=\"Terrain3DMeshAsset\" />\n\t\t\t<description>\n\t\t\t\tAssigns the Terrain3DMeshAsset to the specified ID slot. It can be null to clear the slot. See [method set_texture_asset].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_texture_asset\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"id\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"texture\" type=\"Terrain3DTextureAsset\" />\n\t\t\t<description>\n\t\t\t\tAdds a Terrain3DTextureAsset at the specified ID slot. The texture can be null to clear the slot, or remove it if its the last in the list. If the specified slot is full, it will be swapped with the source texture ID, or will find the next available ID.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_mesh_list\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tUpdates the internal list of meshes used by the instancer.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_texture_list\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tRegenerates the texture arrays from the list of texture assets, which is sent to the shader.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"mesh_list\" type=\"Terrain3DMeshAsset[]\" setter=\"set_mesh_list\" getter=\"get_mesh_list\" default=\"[]\">\n\t\t\tThe list of mesh assets.\n\t\t</member>\n\t\t<member name=\"texture_list\" type=\"Terrain3DTextureAsset[]\" setter=\"set_texture_list\" getter=\"get_texture_list\" default=\"[]\">\n\t\t\tThe list of texture assets.\n\t\t</member>\n\t</members>\n\t<signals>\n\t\t<signal name=\"meshes_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when the mesh list is updated, which happens as a result of a [Terrain3DMeshAsset] changing.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"textures_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when this list is updated due to changes in the texture slots, or the files or settings of any [Terrain3DTextureAsset].\n\t\t\t</description>\n\t\t</signal>\n\t</signals>\n\t<constants>\n\t\t<constant name=\"TYPE_TEXTURE\" value=\"0\" enum=\"AssetType\">\n\t\t\tAsset is type Terrain3DTextureAsset.\n\t\t</constant>\n\t\t<constant name=\"TYPE_MESH\" value=\"1\" enum=\"AssetType\">\n\t\t\tAsset is type Terrain3DMeshAsset.\n\t\t</constant>\n\t\t<constant name=\"MAX_TEXTURES\" value=\"32\">\n\t\t\tHard coded maximum number of textures, with IDs in the range of 0-31. Cannot easily be expanded.\n\t\t</constant>\n\t\t<constant name=\"MAX_MESHES\" value=\"256\">\n\t\t\tLimit of the maximum number of meshes. Arbitrary, easily expanded.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DCollision.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DCollision\" inherits=\"Object\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis class manages collision.\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"build\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tCreates collision shapes and calls [method update] to shape them. Calls [method destroy] first, so it is safe to call this to fully rebuild collision any time.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"destroy\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tRemoves all collision shapes and frees any memory used.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the RID of the active StaticBody.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_dynamic_mode\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if [member mode] is [code skip-lint]Dynamic / Editor[/code] or [code skip-lint]Dynamic / Game[/code].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_editor_mode\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if [member mode] is [code skip-lint]Full / Editor[/code] or [code skip-lint]Dynamic / Editor[/code].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_enabled\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if [member mode] is not [code skip-lint]Disabled[/code].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" default=\"Vector2i(2147483647, 2147483647)\" />\n\t\t\t<param index=\"1\" name=\"rebuild\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\t- If [member mode] is Full, recalculates the specified or all existing collision shapes. Specify a [code skip-lint]region_location[/code] to update only that region, or omit it or use `Vector2i.MAX` to update all regions. If regions have been added or removed, set [code skip-lint]rebuild[/code] to true or call [method build] instead. A full update can be slow.\n\t\t\t\t- If [member mode] is Dynamic, repositions collision shapes around the camera and recalculates ones that moved. Set [code skip-lint]rebuild[/code] to true to recalculate all shapes within [member radius]. [code skip-lint]region_location[/code] is ignored. This is very fast, and can be updated at 60fps for little cost.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"layer\" type=\"int\" setter=\"set_layer\" getter=\"get_layer\" default=\"1\">\n\t\t\tThe physics layers the terrain lives on. Sets [code skip-lint]CollisionObject3D.collision_layer[/code]. Also see [member mask].\n\t\t</member>\n\t\t<member name=\"mask\" type=\"int\" setter=\"set_mask\" getter=\"get_mask\" default=\"1\">\n\t\t\tThe physics layers the physics body scans for colliding objects. Sets [code skip-lint]CollisionObject3D.collision_mask[/code]. Also see [member layer].\n\t\t</member>\n\t\t<member name=\"mode\" type=\"int\" setter=\"set_mode\" getter=\"get_mode\" enum=\"Terrain3DCollision.CollisionMode\" default=\"1\">\n\t\t\tThe selected mode determines if collision is generated and how. See [enum CollisionMode] for details.\n\t\t</member>\n\t\t<member name=\"physics_material\" type=\"PhysicsMaterial\" setter=\"set_physics_material\" getter=\"get_physics_material\">\n\t\t\tApplies a [code skip-lint]PhysicsMaterial[/code] override to the entire terrain StaticBody.\n\t\t\tThere's no ability built into Godot to change physics material parameters based on texture or any other factor. However, it might be possible to extend `PhysicsMaterial` in order to inject code into the queries. It would need references to an object position and a terrain, and then it could run [method Terrain3DData.get_texture_id] based on the position and return different physics settings per texture. That would change the settings for the entire terrain for that moment.\n\t\t</member>\n\t\t<member name=\"priority\" type=\"float\" setter=\"set_priority\" getter=\"get_priority\" default=\"1.0\">\n\t\t\tThe priority with which the physics server uses to solve collisions. The higher the priority, the lower the penetration of a colliding object. Sets [code skip-lint]CollisionObject3D.collision_priority[/code].\n\t\t</member>\n\t\t<member name=\"radius\" type=\"int\" setter=\"set_radius\" getter=\"get_radius\" default=\"64\">\n\t\t\tIf [member mode] is Dynamic, this is the distance range within which collision shapes will be generated.\n\t\t</member>\n\t\t<member name=\"shape_size\" type=\"int\" setter=\"set_shape_size\" getter=\"get_shape_size\" default=\"16\">\n\t\t\tIf [member mode] is Dynamic, this is the size of each collision shape.\n\t\t</member>\n\t</members>\n\t<constants>\n\t\t<constant name=\"DISABLED\" value=\"0\" enum=\"CollisionMode\">\n\t\t\tNo collision shapes will be generated.\n\t\t</constant>\n\t\t<constant name=\"DYNAMIC_GAME\" value=\"1\" enum=\"CollisionMode\">\n\t\t\tCollision shapes are generated around the camera as it moves; in game only.\n\t\t</constant>\n\t\t<constant name=\"DYNAMIC_EDITOR\" value=\"2\" enum=\"CollisionMode\">\n\t\t\tCollision shapes are generated around the camera as it moves; in the editor and in game. Enable [code skip-lint]View Gizmos[/code] in the viewport menu to see them.\n\t\t</constant>\n\t\t<constant name=\"FULL_GAME\" value=\"3\" enum=\"CollisionMode\">\n\t\t\tCollision shapes are generated for all regions in game only. One shape per region.\n\t\t</constant>\n\t\t<constant name=\"FULL_EDITOR\" value=\"4\" enum=\"CollisionMode\">\n\t\t\tCollision shapes are generated for all regions in the editor and in game. One shape per region. This mode is necessary for some 3rd party plugins to detect the terrain using collision. Enable [code skip-lint]View Gizmos[/code] in the viewport menu to see the collision mesh.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DData.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DData\" inherits=\"Object\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tTerrain3D divides all data into regions which fit on a grid in the world. These coordinates are called region locations. The map data are stored in instances of [Terrain3DRegion], which are saved to individual files. This class manages region loading, unloading, data retreival and manipulation.\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"add_region\">\n\t\t\t<return type=\"int\" enum=\"Error\" />\n\t\t\t<param index=\"0\" name=\"region\" type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"1\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tAdds a region for sculpting and painting.\n\t\t\t\tThe region should already be configured with the desired location and maps before sending to this function.\n\t\t\t\tUpon saving, this region will be written to a data file stored in [member Terrain3D.data_directory].\n\t\t\t\t- update - regenerates the texture arrays if true. Set to false if bulk adding many regions, then true on the last one or use [method update_maps].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"add_region_blank\">\n\t\t\t<return type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tCreates and adds a blank region at the specified location. See [method add_region].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"add_region_blankp\">\n\t\t\t<return type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tCreates and adds a blank region at a region location encompassing the specified global position. See [method add_region].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"calc_height_range\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"recursive\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tRecalculates the master height range for the whole terrain by summing the height ranges of all active regions.\n\t\t\t\tRecursive mode does the same, but has each region recalculate heights from each heightmap pixel. See [method Terrain3DRegion.calc_height_range].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"change_region_size\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_size\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReslices terrain data to fit the new region size. This is a destructive process for which there is no undo. However Godot does make an undo entry, which will reslice in reverse. Files on disk are not added or removed until the scene is saved.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"do_for_regions\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"area\" type=\"Rect2i\" />\n\t\t\t<param index=\"1\" name=\"callback\" type=\"Callable\" />\n\t\t\t<description>\n\t\t\t\tCalls the callback function for every region within the given area. If using vertex_spacing, area values should be descaled.\n\t\t\t\tThe callable receives: source Terrain3DRegion, source Rect2i, dest Rect2i, (bindings)\n\t\t\t\tYou may wish to append .bind() to the callback to pass along variables. For instance internally this function is called when changing region size. We bind the destination Terrain3DRegion, then use do_for_regions to copy segments of source regions to segments of destination regions. See the code for change_region_size() for more.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"dump\" qualifiers=\"const\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"verbose\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tCalls [method Terrain3DRegion.dump] for all regions loaded, active and inactive.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"export_image\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" enum=\"Error\" />\n\t\t\t<param index=\"0\" name=\"file_name\" type=\"String\" />\n\t\t\t<param index=\"1\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<description>\n\t\t\t\tExports the specified map type as one of r16/raw, exr, jpg, png, webp, res, tres. \n\t\t\t\tR16 or exr are recommended for roundtrip external editing.\n\t\t\t\tR16 can be edited by Krita, however you must know the dimensions and min/max before reimporting. This information is printed to the console.\n\t\t\t\tRes/tres stores in Godot's native data format.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_color\" qualifiers=\"const\">\n\t\t\t<return type=\"Color\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the associated pixel on the color map at the requested position.\n\t\t\t\tReturns [code skip-lint]Color(NAN, NAN, NAN, NAN)[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_color_maps_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the resource ID of the generated height map Texture Array sent to the shader. You can use this RID with the RenderingServer to set it as a shader parameter for a sampler2DArray uniform in your own shader. See [url=https://terrain3d.readthedocs.io/en/stable/docs/tips.html#using-the-generated-height-map-in-other-shaders]Tips[/url] for an example.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the associated pixel on the control map at the requested position.\n\t\t\t\tReturns [code skip-lint]4,294,967,295[/code] aka [code skip-lint]UINT32_MAX[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_angle\" qualifiers=\"const\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the angle, aka uv rotation, on the control map at the requested position. Values are fixed to 22.5 degree intervals, for a maximum of 16 angles. 360 / 16 = 22.5.\n\t\t\t\tReturns [code skip-lint]NAN[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_auto\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns whether the autoshader is enabled on the control map at the requested position.\n\t\t\t\tReturns [code skip-lint]false[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_base_id\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the base texture ID on the control map at the requested position. Values are 0 - 31, which matches the ID of the texture asset in the asset dock.\n\t\t\t\tReturns [code skip-lint]4,294,967,295[/code] aka [code skip-lint]UINT32_MAX[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_blend\" qualifiers=\"const\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the blend value between the base texture ID and the overlay texture ID. The value is clamped between 0.0 - 1.0 where 0.0 shows only the base texture, and 1.0 shows only the overlay texture.\n\t\t\t\tReturns [code skip-lint]NAN[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_hole\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns whether there is a hole on the control map at the requested position.\n\t\t\t\tReturns [code skip-lint]false[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_maps_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the resource ID of the generated control map Texture Array sent to the shader. You can use this RID with the RenderingServer to set it as a shader parameter for a sampler2DArray uniform in your own shader. See [url=https://terrain3d.readthedocs.io/en/stable/docs/tips.html#using-the-generated-height-map-in-other-shaders]Tips[/url] for an example.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_navigation\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns whether navigation is enabled on the control map at the requested position.\n\t\t\t\tReturns [code skip-lint]false[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_overlay_id\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the overlay texture ID on the control map at the requested position. Values are 0 - 31, which matches the ID of the texture asset in the asset dock.\n\t\t\t\tReturns [code skip-lint]4,294,967,295[/code] aka [code skip-lint]UINT32_MAX[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_control_scale\" qualifiers=\"const\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the uv scale on the control map at the requested position. The value is rounded to the nearest 20% difference from 100%, ranging between -60% to +80%. Eg. +20% or -40%.\n\t\t\t\tReturns [code skip-lint]NAN[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_height\" qualifiers=\"const\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the height at the requested position. If the position is close to a vertex, the pixel height on the heightmap is returned. Otherwise the value is interpolated from the 4 vertices surrounding the position.\n\t\t\t\tReturns [code skip-lint]NAN[/code] if the requested position is a hole or outside of defined regions.\n\t\t\t\tAlso see [method Terrain3D.get_raycast_result] and [method Terrain3D.get_intersection] for alternative functions\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_height_maps_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the resource ID of the generated height map texture array sent to the shader. You can use this RID with the RenderingServer to set it as a shader parameter for a sampler2DArray uniform in your own shader. See [url=https://terrain3d.readthedocs.io/en/stable/docs/tips.html#using-the-generated-height-map-in-other-shaders]Tips[/url] for an example.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_height_range\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector2\" />\n\t\t\t<description>\n\t\t\t\tReturns the highest and lowest heights for the sculpted terrain used to set the world AABB. See [method calc_height_range].\n\t\t\t\tAny [member Terrain3DMaterial.world_background] used that extends the mesh outside of this range will not change this variable. You need to set [member Terrain3D.cull_margin] or the renderer will clip meshes.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_maps\" qualifiers=\"const\">\n\t\t\t<return type=\"Image[]\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<description>\n\t\t\t\tReturns an Array of Images from all regions of the specified map type.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_mesh_vertex\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector3\" />\n\t\t\t<param index=\"0\" name=\"lod\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"filter\" type=\"int\" enum=\"Terrain3DData.HeightFilter\" />\n\t\t\t<param index=\"2\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the position of a terrain vertex at a certain LOD. If the position is outside of defined regions or there is a hole, it returns [code skip-lint]NAN[/code] in the vector's Y coordinate.\n\t\t\t\t[code skip-lint]lod[/code] - Determines how many heights around the given global position will be sampled. Range 0 - 8.\n\t\t\t\t[code skip-lint]filter[/code] - Specifies how samples are filtered. See [enum HeightFilter].\n\t\t\t\t[code skip-lint]global_position[/code] - X and Z coordinates of the vertex. Heights will be sampled around these coordinates.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_normal\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector3\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the terrain normal at the specified position. This function uses [method get_height].\n\t\t\t\tReturns [code skip-lint]Vector3(NAN, NAN, NAN)[/code] if the requested position is a hole or outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_pixel\" qualifiers=\"const\">\n\t\t\t<return type=\"Color\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<param index=\"1\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the pixel for the map type associated with the specified position.\n\t\t\t\tReturns [code skip-lint]Color(NAN, NAN, NAN, NAN)[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tReturn the [Terrain3DRegion] at the specified location. This will return inactive regions marked for deletion. Check with [member Terrain3DRegion.deleted].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region_count\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the number of active regions; those not marked for deletion.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region_id\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tReturns -1 if no region or out of bounds at the given location, otherwise returns the current region id.\n\t\t\t\tThe region_id is the index into the TextureArrays sent to the shader, and can change at any time. Gamedevs should generally index regions by location. However, this function is useful to determine if the location is a valid region.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region_idp\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the region id at a global position. See [method get_region_id].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region_location\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector2i\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the calculated region location for the given global position. This is just a calculation and does no bounds checking or verification that a region exists. See [method get_region_map_index] for bounds checking, or [method has_region] for checking existance.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region_map\" qualifiers=\"const\">\n\t\t\t<return type=\"PackedInt32Array\" />\n\t\t\t<description>\n\t\t\t\tReturns a fully populated 32 x 32 array. The array location contains the region id + 1, or 0, which means no region.\n\t\t\t\tSee [method get_region_map_index].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_region_map_index\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tGiven a region location, returns the index into the region map array. See [method get_region_map].\n\t\t\t\tYou can use this function to quickly determine if a location is within the greater world bounds (-16,-16) to (15, 15). It returns -1 if not.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_regionp\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the region at the specified global position. This will return inactive regions marked for deletion. Check with [member Terrain3DRegion.deleted].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_regions_active\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3DRegion[]\" />\n\t\t\t<param index=\"0\" name=\"copy\" type=\"bool\" default=\"false\" />\n\t\t\t<param index=\"1\" name=\"deep\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tReturns an array of active regions not marked for deletion. Each region knows its own location. See [member Terrain3DRegion.location].\n\t\t\t\t- copy - returns a shallow copy of the regions; region map references are copied.\n\t\t\t\t- deep - returns a deep copy of the regions; region maps are full duplicates.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_regions_all\" qualifiers=\"const\">\n\t\t\t<return type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tReturns all regions in a dictionary indexed by region location. Some regions may be marked for deletion.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_roughness\" qualifiers=\"const\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the roughness modifier (wetness) on the color map alpha channel associated with the specified position.\n\t\t\t\tReturns [code skip-lint]Color(NAN, NAN, NAN, NAN)[/code] if the position is outside of defined regions.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_texture_id\" qualifiers=\"const\">\n\t\t\t<return type=\"Vector3\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns [code skip-lint]Vector3(base texture id, overlay id, blend value)[/code].\n\t\t\t\tReturns [code skip-lint]Vector3(NAN, NAN, NAN)[/code] if the position is a hole or outside of defined regions.\n\t\t\t\tThis is often used for playing sounds on footsteps. It's up to the gamedev to determine which is visually apparent based on shader settings.\n\t\t\t\tDue to blending, it won't be pixel perfect. Try having your player controller print this value while walking around to see how the blending values look. Perhaps you'll find that the overlay texture is visible starting at a blend value of .3 to .5, otherwise the base is visible. You can also observe the control blend debug view with [member Terrain3DMaterial.show_control_blend].\n\t\t\t\tObserving how this is done in The Witcher 3, there are only about 6 sounds used (snow, foliage, dirt, gravel, rock, wood), and except for wood, they are not pixel perfect. Wood is easy to do by detecting if the player is walking on wood meshes. The other 5 sounds are played when the player is in an area where the textures are blending. So it might play rock while over a dirt area. This shows pixel perfect accuracy is not important. It will still provide a seamless audio visual experience.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"has_region\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the specified region location has an active region.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"has_regionp\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the specified global position has an active region.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"import_images\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"images\" type=\"Image[]\" />\n\t\t\t<param index=\"1\" name=\"global_position\" type=\"Vector3\" default=\"Vector3(0, 0, 0)\" />\n\t\t\t<param index=\"2\" name=\"offset\" type=\"float\" default=\"0.0\" />\n\t\t\t<param index=\"3\" name=\"scale\" type=\"float\" default=\"1.0\" />\n\t\t\t<description>\n\t\t\t\tImports an Image set (Height, Control, Color) into this resource. It does NOT normalize values to 0-1. You must do that using get_min_max() and adjusting scale and offset.\n\t\t\t\t[code skip-lint]images[/code] - MapType.TYPE_MAX sized array of Images for Height, Control, Color. Images can be blank or null.\n\t\t\t\t[code skip-lint]global_position[/code] - X,0,Z position on the region map. Valid range is [member Terrain3D.vertex_spacing] * [member Terrain3D.region_size] * (+/-16, +/-16).\n\t\t\t\t[code skip-lint]offset[/code] - Add this factor to all height values, can be negative.\n\t\t\t\t[code skip-lint]scale[/code] - Scale all height values by this factor (applied after offset).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_in_slope\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"slope_range\" type=\"Vector2\" />\n\t\t\t<param index=\"2\" name=\"normal\" type=\"Vector3\" default=\"Vector3(0, 0, 0)\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the slope of the terrain at the given position is within the slope range. If normal is provided it will use that instead of querying the terrain.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_region_deleted\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the region at the  location exists and is marked as deleted. Syntactic sugar for [member Terrain3DRegion.deleted].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_region_modified\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the region at the location exists and is marked as modified. Syntactic sugar for [member Terrain3DRegion.modified].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"layered_to_image\" qualifiers=\"const\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<description>\n\t\t\t\tReturns an Image of the given map type that contains all regions in one large image. If the world has multiple islands, this function will return an image large enough to encompass all used regions, with black areas in between the islands.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"load_directory\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"directory\" type=\"String\" />\n\t\t\t<description>\n\t\t\t\tLoads all of the Terrain3DRegion files found in the specified directory. Then it rebuilds all map arrays.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"load_region\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"directory\" type=\"String\" />\n\t\t\t<param index=\"2\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tLoads the specified region location file.\n\t\t\t\t- update - rebuild maps if true.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"remove_region\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region\" type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"1\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tMarks the specified region as deleted. This deactivates it so it won't render it on screen once maps are updated, unless marked not deleted. The file will be deleted from disk upon saving.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"remove_regionl\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tRemoves the region at the specified location. See [method remove_region].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"remove_regionp\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tRemoves the region at the specified global_position. See [method remove_region].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"save_directory\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"directory\" type=\"String\" />\n\t\t\t<description>\n\t\t\t\tThis saves all active regions into the specified directory.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"save_region\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"directory\" type=\"String\" />\n\t\t\t<param index=\"2\" name=\"save_16_bit\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tSaves the specified active region to the directory. See [method Terrain3DRegion.save].\n\t\t\t\t- region_location - the region to save.\n\t\t\t\t- 16_bit - converts the edited 32-bit heightmap to 16-bit. This is a lossy operation.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_color\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"color\" type=\"Color\" />\n\t\t\t<description>\n\t\t\t\tSets the color on the color map pixel associated with the specified position. See [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"control\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tSets the value on the control map pixel associated with the specified position. See [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_angle\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"degrees\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tSets the angle, aka uv rotation, on the control map at the requested position. Values are rounded to the nearest 22.5 degree interval, for a maximum of 16 angles. 360 / 16 = 22.5.\n\t\t\t\tSee [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_auto\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"enable\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tSets if the material should render the autoshader or manual texturing on the control map at the requested position.\n\t\t\t\tSee [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_base_id\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"texture_id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tSets the base texture ID on the control map at the requested position. Values are clamped to 0 - 31, matching the ID of the texture asset in the asset dock.\n\t\t\t\tSee [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_blend\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"blend_value\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tSets the blend value between the base texture ID, and the overlay texture ID. The value is clamped between 0.0 - 1.0 where 0.0 shows only the base texture, and 1.0 shows only the overlay texture.\n\t\t\t\tSee [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_hole\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"enable\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tSets if a hole should be rendered on the control map at the requested position. See [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_navigation\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"enable\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tSets if navigation generation is enabled on the control map at the requested position. See [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_overlay_id\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"texture_id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tSets the overlay texture ID on the control map at the requested position. Values are clamped to 0 - 31, matching the ID of the texture asset in the asset dock.\n\t\t\t\tSee [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_control_scale\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"percentage_modifier\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tSets the uv scale on the control map at the requested position. The value is rounded to the nearest 20% difference from 100%, ranging between -60% to +80%.\n\t\t\t\tSee [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_height\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"height\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tSets the height value on the heightmap pixel associated with the specified position. See [method set_pixel] for important information.\n\t\t\t\tUnlike [method get_height], which interpolates between vertices, this function does not and will set the pixel at floored coordinates.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_pixel\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<param index=\"1\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"2\" name=\"pixel\" type=\"Color\" />\n\t\t\t<description>\n\t\t\t\tSets the pixel for the map type associated with the specified position. This method is fine for setting a few pixels, but if you wish to modify thousands of pixels quickly, you should get the region and use [method Terrain3DRegion.get_map], then edit the images directly.\n\t\t\t\tAfter setting pixels you need to call [method update_maps]. You may also need to regenerate collision if you don't have dynamic collision enabled.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_region_deleted\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"deleted\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tMarks a region as deleted. It will stop displaying when maps are updated. The file will be removed on save.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_region_modified\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"modified\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tSets the region as modified. It will be written to disk when saved. Syntactic sugar for [member Terrain3DRegion.modified].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_roughness\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"roughness\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tSets the roughness modifier (wetness) on the color map alpha channel associated with the specified position. See [method set_pixel] for important information.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_maps\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" default=\"3\" />\n\t\t\t<param index=\"1\" name=\"all_regions\" type=\"bool\" default=\"true\" />\n\t\t\t<param index=\"2\" name=\"generate_mipmaps\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tRegenerates the region map and the TextureArrays that combine the requested map types. This function needs to be called after editing any of the maps.\n\t\t\t\tBy default, this function rebuilds all maps for all regions.\n\t\t\t\t- map_type - Regenerate only maps of this type.\n\t\t\t\t- all_regions - Regenerate all regions if true, otherwise only those marked with [member Terrain3DRegion.edited].\n\t\t\t\t- generate_mipmaps - Regenerate mipmaps if map_type is color or all (max), for the regions specified above. This can also be done on individual regions before calling this function with [code skip-lint]region.get_color_map().generate_mipmaps()[/code].\n\t\t\t\tFor frequent editing, rather than enabling all_regions, it is more optimal to only update changed regions as follows:\n\t\t\t\t[codeblock]\n\t\t\t\tterrain.data.set_height(global_position, 10.0)\n\t\t\t\tvar region:Terrain3DRegion = terrain.data.get_regionp(global_position)\n\t\t\t\tregion.set_edited(true)\n\t\t\t\tterrain.data.update_maps(Terrain3DRegion.TYPE_HEIGHT, false)\n\t\t\t\tregion.set_edited(false)\n\t\t\t\t[/codeblock]\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"color_maps\" type=\"Image[]\" setter=\"\" getter=\"get_color_maps\" default=\"[]\">\n\t\t\tAn Array[Image] containing references to all of the color maps in all regions. See [member Terrain3DRegion.color_map].\n\t\t</member>\n\t\t<member name=\"control_maps\" type=\"Image[]\" setter=\"\" getter=\"get_control_maps\" default=\"[]\">\n\t\t\tAn Array[Image] containing references to all of the control maps in all regions. See [member Terrain3DRegion.control_map].\n\t\t</member>\n\t\t<member name=\"height_maps\" type=\"Image[]\" setter=\"\" getter=\"get_height_maps\" default=\"[]\">\n\t\t\tAn Array[Image] containing references to all of the height maps in all regions. See [member Terrain3DRegion.height_map].\n\t\t</member>\n\t\t<member name=\"region_locations\" type=\"Vector2i[]\" setter=\"set_region_locations\" getter=\"get_region_locations\" default=\"[]\">\n\t\t\tThe array of all active region locations; those not marked for deletion.\n\t\t</member>\n\t</members>\n\t<signals>\n\t\t<signal name=\"color_maps_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when the color maps array is regenerated.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"control_maps_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when the control maps array is regenerated.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"height_maps_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when the height maps array is regenerated.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"maps_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when the region map or any map array has been regenerated.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"maps_edited\">\n\t\t\t<param index=\"0\" name=\"edited_area\" type=\"AABB\" />\n\t\t\t<description>\n\t\t\t\tThis signal is emitted whenever the editor ([Terrain3DEditor]) is used to:\n\t\t\t\t- add or remove a region\n\t\t\t\t- alter a region map with a brush tool\n\t\t\t\t- undo or redo any of the above operations\n\t\t\t\tThe parameter contains the axis-aligned bounding box of the area edited.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"region_map_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when the region map is regenerated.\n\t\t\t</description>\n\t\t</signal>\n\t</signals>\n\t<constants>\n\t\t<constant name=\"HEIGHT_FILTER_NEAREST\" value=\"0\" enum=\"HeightFilter\">\n\t\t\tSamples the height map at the exact coordinates given.\n\t\t</constant>\n\t\t<constant name=\"HEIGHT_FILTER_MINIMUM\" value=\"1\" enum=\"HeightFilter\">\n\t\t\tSamples (1 &lt;&lt; lod) * 2 heights around the given coordinates and returns the lowest.\n\t\t</constant>\n\t\t<constant name=\"REGION_MAP_SIZE\" value=\"32\">\n\t\t\tHard coded number of regions on a side. The total number of regions is this squared.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DEditor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DEditor\" inherits=\"Object\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis class handles all of the sculpting and painting operations for Terrain3D.\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"apply_undo\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"data\" type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tUndo the previous changes, with the provided data. Used by Godot, not gamedevs.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"backup_region\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region\" type=\"Terrain3DRegion\" />\n\t\t\t<description>\n\t\t\t\tAdds a region to the currently pending operation undo snapshot. [method is_operating] must be true.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_operation\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" enum=\"Terrain3DEditor.Operation\" />\n\t\t\t<description>\n\t\t\t\tReturns the current selected tool operation (eg. add, subtract).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_terrain\" qualifiers=\"const\">\n\t\t\t<return type=\"Terrain3D\" />\n\t\t\t<description>\n\t\t\t\tReturns the instance of Terrain3D this class is conneced to.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_tool\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" enum=\"Terrain3DEditor.Tool\" />\n\t\t\t<description>\n\t\t\t\tReturns the current tool selected in the editor plugin.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_operating\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if currently in the middle of a brushing operation.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"operate\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"camera_direction\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tStart brushing.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_brush_data\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"data\" type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tSets all brush settings used in the editor plugin.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_operation\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"operation\" type=\"int\" enum=\"Terrain3DEditor.Operation\" />\n\t\t\t<description>\n\t\t\t\tSets the tool operation used in the editor plugin.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_terrain\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"terrain\" type=\"Terrain3D\" />\n\t\t\t<description>\n\t\t\t\tSets the instance of Terrain3D this class is connected to.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_tool\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"tool\" type=\"int\" enum=\"Terrain3DEditor.Tool\" />\n\t\t\t<description>\n\t\t\t\tSets the tool selected in the editor plugin.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"start_operation\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tBegin a sculpting or painting operation. Prepares to create an undo/redo commit.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"stop_operation\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tEnd a sculpting or painting operation. Commits any regions marked with [member Terrain3DRegion.edited] in the undo/redo system and clears that flag.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<constants>\n\t\t<constant name=\"ADD\" value=\"0\" enum=\"Operation\">\n\t\t\tAdditive operations.\n\t\t</constant>\n\t\t<constant name=\"SUBTRACT\" value=\"1\" enum=\"Operation\">\n\t\t\tSubtractive operations.\n\t\t</constant>\n\t\t<constant name=\"REPLACE\" value=\"2\" enum=\"Operation\">\n\t\t\tReplacing operations.\n\t\t</constant>\n\t\t<constant name=\"AVERAGE\" value=\"3\" enum=\"Operation\">\n\t\t\tAveraging operations.\n\t\t</constant>\n\t\t<constant name=\"GRADIENT\" value=\"4\" enum=\"Operation\">\n\t\t\tGradient operations.\n\t\t</constant>\n\t\t<constant name=\"OP_MAX\" value=\"5\" enum=\"Operation\">\n\t\t\tThe number of elements in this enum.\n\t\t</constant>\n\t\t<constant name=\"SCULPT\" value=\"1\" enum=\"Tool\">\n\t\t</constant>\n\t\t<constant name=\"HEIGHT\" value=\"2\" enum=\"Tool\">\n\t\t\tSculpt heights.\n\t\t</constant>\n\t\t<constant name=\"TEXTURE\" value=\"3\" enum=\"Tool\">\n\t\t\tPaint textures.\n\t\t</constant>\n\t\t<constant name=\"COLOR\" value=\"4\" enum=\"Tool\">\n\t\t\tPaint on the color map.\n\t\t</constant>\n\t\t<constant name=\"ROUGHNESS\" value=\"5\" enum=\"Tool\">\n\t\t\tPaint a roughness modifier, aka wetness.\n\t\t</constant>\n\t\t<constant name=\"ANGLE\" value=\"10\" enum=\"Tool\">\n\t\t\tPaint textures rotated by an angle.\n\t\t</constant>\n\t\t<constant name=\"SCALE\" value=\"11\" enum=\"Tool\">\n\t\t\tPaint textures scaled by a value.\n\t\t</constant>\n\t\t<constant name=\"AUTOSHADER\" value=\"6\" enum=\"Tool\">\n\t\t\tPaint where the shader automatically textures.\n\t\t</constant>\n\t\t<constant name=\"HOLES\" value=\"7\" enum=\"Tool\">\n\t\t\tPaint where vertices will be invalidated to leave holes.\n\t\t</constant>\n\t\t<constant name=\"NAVIGATION\" value=\"8\" enum=\"Tool\">\n\t\t\tPaint where navigation will be generated.\n\t\t</constant>\n\t\t<constant name=\"INSTANCER\" value=\"9\" enum=\"Tool\">\n\t\t\tPaint MultiMesh instances on the ground.\n\t\t</constant>\n\t\t<constant name=\"REGION\" value=\"0\" enum=\"Tool\">\n\t\t\tAdd/remove regions.\n\t\t</constant>\n\t\t<constant name=\"TOOL_MAX\" value=\"12\" enum=\"Tool\">\n\t\t\tThe number of elements in this enum.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DInstancer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DInstancer\" inherits=\"Object\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis class places mesh instances defined in the Terrain3D asset dock into MultiMeshInstance3Ds on the ground.\n\t\tData is currently stored in [member Terrain3DRegion.instances] and loaded into MultiMeshInstances, which are attached to the scene tree and managed by this class.\n\t\t[b]The methods available for adding instances are:[/b]\n\t\t- [method add_transforms] - Accepts your list of transforms and parses them by region and cell location and stores in our data storage. Recommended for general API instancing.\n\t\t- [method add_multimesh] - Pulls the transforms out of your MultiMesh and calls add_transforms.\n\t\t- [method add_instances] - A feature rich function designed for hand editing via Terrain3DEditor.\n\t\t- Creating your own instance data and inserting it directly into [member Terrain3DRegion.instances]. It's not difficult to do this in GDScript, but a thorough understanding of the C++ code in this class is recommended.\n\t\t[b]The methods available for removing instances are:[/b]\n\t\t- [method remove_instances] - Like add_instances, this is can be used procedurally but is designed for hand editing.\n\t\t- [method clear_by_mesh], [method clear_by_location] - To erase large sections of instances.\n\t\t- Editing [member Terrain3DRegion.instances] directly.\n\t\tAfter modifying region data, run [method update_mmis] to rebuild the MultiMeshInstance3Ds.\n\t\t[b]Read More:[/b]\n\t\t- [b]Tutorial:[/b] [url=https://terrain3d.readthedocs.io/en/stable/docs/instancer.html]Foliage Instancing[/url]\n\t\t- [b]Godot Reference:[/b] [url=https://docs.godotengine.org/en/stable/classes/class_multimesh.html]MultiMesh[/url]\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"add_instances\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"params\" type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tUsed by Terrain3DEditor to place instances given many brush parameters. In addition to the brush position, it also uses the following parameters: asset_id:int, size:float, strength:float, fixed_scale:float, random_scale:float, fixed_spin:float, random_spin:float, fixed_tilt:float, random_tilt:float, align_to_normal:bool, height_offset:float, random_height:float, vertex_color:Color, random_hue:float, random_darken:float, slope:Vector2, on_collision:bool, raycast_height:float. All of these settings are set in the editor via tool_settings.gd.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"add_multimesh\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"multimesh\" type=\"MultiMesh\" />\n\t\t\t<param index=\"2\" name=\"transform\" type=\"Transform3D\" default=\"Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)\" />\n\t\t\t<param index=\"3\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tAllows procedural placement of meshes, or importing from another MultiMeshInstancer placement tool. The specified mesh_id should already be setup as a [Terrain3DMeshAsset] in the asset dock. This function extracts the instance transforms and colors from a multimesh and passes it to [method add_transforms].\n\t\t\t\tUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"add_transforms\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"transforms\" type=\"Transform3D[]\" />\n\t\t\t<param index=\"2\" name=\"colors\" type=\"PackedColorArray\" default=\"PackedColorArray()\" />\n\t\t\t<param index=\"3\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tAllows procedural placement of meshes. The mesh_id should already be setup as a [Terrain3DMeshAsset] in the asset dock. You provide the array of Transform3Ds and optional Colors, which will be parsed into our data storage.\n\t\t\t\tThis function adds the [member Terrain3DMeshAsset.height_offset] to the transform along its local Y axis.\n\t\t\t\tUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"append_location\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<param index=\"2\" name=\"transforms\" type=\"Transform3D[]\" />\n\t\t\t<param index=\"3\" name=\"colors\" type=\"PackedColorArray\" />\n\t\t\t<param index=\"4\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tAppends new transforms to the existing data within a region location. The mesh_id should already be setup as a [Terrain3DMeshAsset] in the asset dock.\n\t\t\t\tUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"append_region\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region\" type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"1\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<param index=\"2\" name=\"transforms\" type=\"Transform3D[]\" />\n\t\t\t<param index=\"3\" name=\"colors\" type=\"PackedColorArray\" />\n\t\t\t<param index=\"4\" name=\"update\" type=\"bool\" default=\"true\" />\n\t\t\t<description>\n\t\t\t\tAppends new transforms to the existing data within a region location. The mesh_id should already be setup as a [Terrain3DMeshAsset] in the asset dock.\n\t\t\t\tUpdate will regenerate the MultiMeshInstances. Disable for bulk adding, then call at the end.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"clear_by_location\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tRemoves all instancer data and MultiMeshInstance nodes attached to the tree for the specified region location and mesh id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"clear_by_mesh\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tRemoves all instancer data and MultiMeshInstance nodes attached to the tree for all regions for the specified mesh id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"clear_by_region\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"region\" type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"1\" name=\"mesh_id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tRemoves all instancer data and MultiMeshInstance nodes attached to the tree for the specified region and mesh id.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_closest_mesh_id\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<description>\n\t\t\t\tReturns the mesh instance ID closest to the specified global position on the ground.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_enabled\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if [member mode] is not disabled.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"remove_instances\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"global_position\" type=\"Vector3\" />\n\t\t\t<param index=\"1\" name=\"params\" type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tUses parameters asset_id:int, size:float, strength:float, slope:Vector2, on_collision:bool, raycast_height:float to randomly remove instances within the indicated brush position and size.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"swap_ids\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"src_id\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"dest_id\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tSwaps the ID of two meshes without changing the mesh instances on the ground.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_mmis\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"mesh_id\" type=\"int\" default=\"-1\" />\n\t\t\t<param index=\"1\" name=\"region_location\" type=\"Vector2i\" default=\"Vector2i(2147483647, 2147483647)\" />\n\t\t\t<param index=\"2\" name=\"rebuild_all\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tQueues a request to rebuild the MMIs for the specified IDs and regions on the next RenderingServer.frame_pre_draw signal. This is safe to call multiple times per frame and it will de-duplicate and do the most general requests. So if you first call it for region (0,0), mesh 52, then later in the frame ask for all regions, mesh 52, it will do only the latter.\n\t\t\t\t- mesh_id - rebuild MMIs for this mesh id. Use `-1` for all IDs.\n\t\t\t\t- region_location - rebuild MMIs for this region. Use `Vector2i.MAX` for all regions.\n\t\t\t\t- rebuild_all - destroy all MMIs first before rebuilding.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_transforms\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"aabb\" type=\"AABB\" />\n\t\t\t<description>\n\t\t\t\tReviews all existing instance transforms within an AABB and adjusts their heights to match the terrain.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"mode\" type=\"int\" setter=\"set_mode\" getter=\"get_mode\" enum=\"Terrain3DInstancer.InstancerMode\" default=\"1\">\n\t\t\tNormal - Generates MMIs and renders  all instances as normal.\n\t\t\tDisabled - prevents the instancer from creating any MultiMeshInstance3Ds.\n\t\t</member>\n\t</members>\n\t<constants>\n\t\t<constant name=\"NORMAL\" value=\"1\" enum=\"InstancerMode\">\n\t\t\tCreate MultiMeshInstance3Ds and render instances as normal.\n\t\t</constant>\n\t\t<constant name=\"DISABLED\" value=\"0\" enum=\"InstancerMode\">\n\t\t\tDisables creation of MultiMeshInstance3Ds and instances.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DMaterial.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DMaterial\" inherits=\"Resource\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t\tA custom shader material resource for Terrain3D.\n\t</brief_description>\n\t<description>\n\t\tThis class handles options for both the built-in shader and any custom override shader. It collects compiled texture data from the other classes and sends all of it to the shader via the RenderingServer.\n\t\tIt is a savable resource, so you can save it to disk and use the same material settings in multiple scenes that use Terrain3D. The amount of data is small, assuming you have saved your shader parameter textures to disk, so it can be saved as a git-friendly, text based .tres file or left within the scene file.\n\t\tWhile it does mimic some of the functionality of ShaderMaterial, it does not derive from any of the Godot Material classes. It will fail any [code skip-lint]is Material[/code] checks. It is a [code skip-lint]Resource[/code].\n\t\tInspector settings above `Custom Shader` and [member shader_override] are used to determine what code is used in the current shader. Inspector settings in `Shader Uniforms` are the public uniforms (not prefaced with `_`) available in the current shader.\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"get_buffer_material_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the RID of the displacement buffer material used with the Rendering Server. This is set per instance of this class.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_buffer_shader_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the RID of the displacement buffer shader used with the Rendering Server.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_material_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the RID of the material used with the Rendering Server. This is set per instance of this class.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_shader_param\" qualifiers=\"const\">\n\t\t\t<return type=\"Variant\" />\n\t\t\t<param index=\"0\" name=\"name\" type=\"StringName\" />\n\t\t\t<description>\n\t\t\t\tRetrieve a parameter from the active shader (built-in or override shader).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_shader_rid\" qualifiers=\"const\">\n\t\t\t<return type=\"RID\" />\n\t\t\t<description>\n\t\t\t\tReturns the RID of the built in shader used with the Rendering Server. This is different from any shader override which has its own RID.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"save\">\n\t\t\t<return type=\"int\" enum=\"Error\" />\n\t\t\t<param index=\"0\" name=\"path\" type=\"String\" default=\"&quot;&quot;\" />\n\t\t\t<description>\n\t\t\t\tSaves this material resource to disk, if saved as an external [code skip-lint].tres[/code] or [code skip-lint].res[/code] resource file.\n\t\t\t\tpath - specifies a directory and file name to use from now on.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_shader_param\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"name\" type=\"StringName\" />\n\t\t\t<param index=\"1\" name=\"value\" type=\"Variant\" />\n\t\t\t<description>\n\t\t\t\tSet a parameter in the active shader (built-in or override shader).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"flags\" type=\"int\" default=\"0\" />\n\t\t\t<description>\n\t\t\t\tSends uniform values to the shader. See [enum UpdateFlags] for options.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"_shader_parameters\" type=\"Dictionary\" setter=\"_set_shader_parameters\" getter=\"_get_shader_parameters\" default=\"{}\">\n\t\t\tThis private dictionary stores all of the shader parameters in the resource. It is not a cache.\n\t\t</member>\n\t\t<member name=\"auto_shader_enabled\" type=\"bool\" setter=\"set_auto_shader_enabled\" getter=\"get_auto_shader_enabled\" default=\"false\">\n\t\t\tEnables selecting two texture IDs that will automatically be applied to the terrain based upon slope.\n\t\t</member>\n\t\t<member name=\"buffer_shader_override\" type=\"Shader\" setter=\"set_buffer_shader_override\" getter=\"get_buffer_shader_override\">\n\t\t\tIf buffer_shader_override_enabled is true and this Shader is valid, the displacement buffer material will use this custom shader code. If this is blank when you enable the override, the system will generate a shader with the current settings. A visual shader will also work here. However we only generate a text based shader so currently a visual shader needs to be constructed with the base code before it can work.\n\t\t</member>\n\t\t<member name=\"buffer_shader_override_enabled\" type=\"bool\" setter=\"set_buffer_shader_override_enabled\" getter=\"is_buffer_shader_override_enabled\" default=\"false\">\n\t\t\tEnables use of the [member buffer_shader_override] shader code. Generates default code if shader_override is blank.\n\t\t</member>\n\t\t<member name=\"displacement_scale\" type=\"float\" setter=\"set_displacement_scale\" getter=\"get_displacement_scale\" default=\"1.0\">\n\t\t\tA global multiplier for all displaced textures. This is the maximum distance that 2 adjacent verticies can be vertically seperated by. Setting this 1.0 would mean a maximum of + 0.5m, and -0.5m deviation from the collision mesh.\n\t\t</member>\n\t\t<member name=\"displacement_sharpness\" type=\"float\" setter=\"set_displacement_sharpness\" getter=\"get_displacement_sharpness\" default=\"0.5\">\n\t\t\tAdjusts the transition between textures. When set at `1.0`, the blending of displacment between textures will match the aldebo/normal blend sharpness exactly. Lower values will have a softer transition, avoiding harsh shapes, without compromising the abldeo and normal blend sharpness. If set at or very near to `0.0`, it is possible that more displaced textures can affect less displaced textures at low blend values even if not visible.\n\t\t</member>\n\t\t<member name=\"dual_scaling_enabled\" type=\"bool\" setter=\"set_dual_scaling_enabled\" getter=\"get_dual_scaling_enabled\" default=\"false\">\n\t\t\tEnables selecting one texture ID that will have multiple scales applied based upon camera distance. Use it for something like a rock texture so up close it will be nicely detailed, and far away mountains can be covered in the same rock texture without looking tiled. The two blend together at a specified distance.\n\t\t</member>\n\t\t<member name=\"macro_variation_enabled\" type=\"bool\" setter=\"set_macro_variation_enabled\" getter=\"get_macro_variation_enabled\" default=\"false\">\n\t\t\tAllows you to add a couple of noise patterns at different scales and colors to add variation to your terrain to avoid tiled textures.\n\t\t</member>\n\t\t<member name=\"output_albedo\" type=\"bool\" setter=\"set_output_albedo_enabled\" getter=\"get_output_albedo_enabled\" default=\"true\">\n\t\t\tEnables the Albedo, aka Base Color or Diffuse, output channel in the shader.\n\t\t</member>\n\t\t<member name=\"output_ambient_occlusion\" type=\"bool\" setter=\"set_output_ambient_occlusion_enabled\" getter=\"get_output_ambient_occlusion_enabled\" default=\"true\">\n\t\t\tEnables the Ambient Occlusion output channel in the shader.\n\t\t</member>\n\t\t<member name=\"output_normal_map\" type=\"bool\" setter=\"set_output_normal_map_enabled\" getter=\"get_output_normal_map_enabled\" default=\"true\">\n\t\t\tEnables the Normal Map output channel in the shader.\n\t\t</member>\n\t\t<member name=\"output_roughness\" type=\"bool\" setter=\"set_output_roughness_enabled\" getter=\"get_output_roughness_enabled\" default=\"true\">\n\t\t\tEnables the Roughness output channel in the shader.\n\t\t</member>\n\t\t<member name=\"projection_enabled\" type=\"bool\" setter=\"set_projection_enabled\" getter=\"get_projection_enabled\" default=\"false\">\n\t\t\tEnables textures to be projected vertically when placed on slopes above 45 degrees. This is useful for mapping textures on cliff faces without stretching, even though the polygons are stretched.\n\t\t</member>\n\t\t<member name=\"shader_override\" type=\"Shader\" setter=\"set_shader_override\" getter=\"get_shader_override\">\n\t\t\tIf shader_override_enabled is true and this Shader is valid, the material will use this custom shader code. If this is blank when you enable the override, the system will generate a shader with the current settings. So if you have a debug view enabled, the generated shader will have all of that code. A visual shader will also work here. However we only generate a text based shader so currently a visual shader needs to be constructed with the base code before it can work.\n\t\t</member>\n\t\t<member name=\"shader_override_enabled\" type=\"bool\" setter=\"set_shader_override_enabled\" getter=\"is_shader_override_enabled\" default=\"false\">\n\t\t\tEnables using the [member shader_override] shader. An editable shader is generated from the current one if shader_override is blank.\n\t\t\tThe inspector settings above this group determine the code that is used in the current shader. The settings below are uniforms for the current shader.\n\t\t</member>\n\t\t<member name=\"show_autoshader\" type=\"bool\" setter=\"set_show_autoshader\" getter=\"get_show_autoshader\" default=\"false\">\n\t\t\tDisplays the area designated for use by the autoshader, which shows materials based upon slope.\n\t\t</member>\n\t\t<member name=\"show_checkered\" type=\"bool\" setter=\"set_show_checkered\" getter=\"get_show_checkered\" default=\"false\">\n\t\t\tShows a checkerboard display using a shader rendered pattern. This is turned on if the Texture List is empty.\n\t\t\tNote that when a blank texture slot is created, a 1k checkerboard texture is generated and stored in the texture slot. That takes VRAM. The two patterns have a slightly different scale.\n\t\t</member>\n\t\t<member name=\"show_colormap\" type=\"bool\" setter=\"set_show_colormap\" getter=\"get_show_colormap\" default=\"false\">\n\t\t\tShows the color map in the albedo channel.\n\t\t</member>\n\t\t<member name=\"show_contours\" type=\"bool\" setter=\"set_show_contours\" getter=\"get_show_contours\" default=\"false\">\n\t\t\tOverlays contour lines on the terrain. Customize the options in the material when enabled. Press `4` with the mouse in the viewport to toggle.\n\t\t</member>\n\t\t<member name=\"show_control_angle\" type=\"bool\" setter=\"set_show_control_angle\" getter=\"get_show_control_angle\" default=\"false\">\n\t\t\tAlbedo shows the painted angle. Orange means 0°, Yellow 270°, Cyan 180°, Violet 90°. Or warm colors towards -Z, cool colors +Z, greens/yellows +X, reds/blues -X. Draw all angles coming from the center of a circle for a better understanding.\n\t\t</member>\n\t\t<member name=\"show_control_blend\" type=\"bool\" setter=\"set_show_control_blend\" getter=\"get_show_control_blend\" default=\"false\">\n\t\t\tDisplays the values used to blend the textures. Blue shows the autoshader blending, red shows manually painted blending.\n\t\t</member>\n\t\t<member name=\"show_control_scale\" type=\"bool\" setter=\"set_show_control_scale\" getter=\"get_show_control_scale\" default=\"false\">\n\t\t\tAlbedo shows the painted scale. Larger scales are more red, smaller scales are more blue. 0.5 middle grey is the default 100% scale.\n\t\t</member>\n\t\t<member name=\"show_control_texture\" type=\"bool\" setter=\"set_show_control_texture\" getter=\"get_show_control_texture\" default=\"false\">\n\t\t\tAlbedo shows the base and overlay texture indices defined by the control map. Red pixels indicate the base texture, with brightness showing texture ids 0 to 31. Green pixels indicate the overlay texture. Yellow indicates both.\n\t\t</member>\n\t\t<member name=\"show_displacement_buffer\" type=\"bool\" setter=\"set_show_displacement_buffer\" getter=\"get_show_displacement_buffer\" default=\"false\">\n\t\t\tShows the resulting displacement buffer vertex differential from 0. Black matches collision exactly. Green shows extrusions, Red for depressions into the terrain.\n\t\t</member>\n\t\t<member name=\"show_grey\" type=\"bool\" setter=\"set_show_grey\" getter=\"get_show_grey\" default=\"false\">\n\t\t\tAlbedo is set to 0.2 grey.\n\t\t</member>\n\t\t<member name=\"show_heightmap\" type=\"bool\" setter=\"set_show_heightmap\" getter=\"get_show_heightmap\" default=\"false\">\n\t\t\tAlbedo is a white to black gradient depending on height. The gradient is scaled to a height of 300, so above that or far below 0 will be all white or black.\n\t\t</member>\n\t\t<member name=\"show_instancer_grid\" type=\"bool\" setter=\"set_show_instancer_grid\" getter=\"get_show_instancer_grid\" default=\"false\">\n\t\t\tOverlays the 32x32m instancer grid on the terrain, which shows how the instancer data is partitioned. Press `2` with the mouse in the viewport to toggle.\n\t\t</member>\n\t\t<member name=\"show_jaggedness\" type=\"bool\" setter=\"set_show_jaggedness\" getter=\"get_show_jaggedness\" default=\"false\">\n\t\t\tHighlights non-smooth areas of the terrain. Jagged peaks, troughs, or edges that are a bit rough with sharp angles between vertices.\n\t\t</member>\n\t\t<member name=\"show_navigation\" type=\"bool\" setter=\"set_show_navigation\" getter=\"get_show_navigation\" default=\"false\">\n\t\t\tDisplays the area designated for generating the navigation mesh.\n\t\t</member>\n\t\t<member name=\"show_region_grid\" type=\"bool\" setter=\"set_show_region_grid\" getter=\"get_show_region_grid\" default=\"false\">\n\t\t\tOverlays the region grid on the terrain. This is more accurate than the region grid gizmo for determining where the region border is when editing. Press `1` with the mouse in the viewport to toggle.\n\t\t</member>\n\t\t<member name=\"show_roughmap\" type=\"bool\" setter=\"set_show_roughmap\" getter=\"get_show_roughmap\" default=\"false\">\n\t\t\tAlbedo is set to the roughness modification map as grey scale. Middle grey, 0.5 means no roughness modification. Black would be high gloss while white is very rough.\n\t\t</member>\n\t\t<member name=\"show_texture_albedo\" type=\"bool\" setter=\"set_show_texture_albedo\" getter=\"get_show_texture_albedo\" default=\"false\">\n\t\t\tAlbedo textures are shown only. Other channels are excluded.\n\t\t</member>\n\t\t<member name=\"show_texture_ao\" type=\"bool\" setter=\"set_show_texture_ao\" getter=\"get_show_texture_ao\" default=\"false\">\n\t\t\tAlbedo is set to the painted Ambient Occlusion textures.\n\t\t</member>\n\t\t<member name=\"show_texture_height\" type=\"bool\" setter=\"set_show_texture_height\" getter=\"get_show_texture_height\" default=\"false\">\n\t\t\tAlbedo is set to the painted Height textures.\n\t\t</member>\n\t\t<member name=\"show_texture_normal\" type=\"bool\" setter=\"set_show_texture_normal\" getter=\"get_show_texture_normal\" default=\"false\">\n\t\t\tAlbedo is set to the painted Normal textures.\n\t\t</member>\n\t\t<member name=\"show_texture_rough\" type=\"bool\" setter=\"set_show_texture_rough\" getter=\"get_show_texture_rough\" default=\"false\">\n\t\t\tAlbedo is set to the painted Roughness textures. This is different from the roughness modification map above.\n\t\t</member>\n\t\t<member name=\"show_vertex_grid\" type=\"bool\" setter=\"set_show_vertex_grid\" getter=\"get_show_vertex_grid\" default=\"false\">\n\t\t\tOverlays the vertex grid on the terrain, showing where each vertex is. Press `3` with the mouse in the viewport to toggle.\n\t\t</member>\n\t\t<member name=\"texture_filtering\" type=\"int\" setter=\"set_texture_filtering\" getter=\"get_texture_filtering\" enum=\"Terrain3DMaterial.TextureFiltering\" default=\"0\">\n\t\t\tSets how the renderer should filter textures. See [enum TextureFiltering] for options.\n\t\t</member>\n\t\t<member name=\"world_background\" type=\"int\" setter=\"set_world_background\" getter=\"get_world_background\" enum=\"Terrain3DMaterial.WorldBackground\" default=\"1\">\n\t\t\tSets how the mesh outside of defined regions behave. See [enum WorldBackground] for options.\n\t\t</member>\n\t</members>\n\t<constants>\n\t\t<constant name=\"NONE\" value=\"0\" enum=\"WorldBackground\">\n\t\t\tOutside of the defined regions, hide the mesh.\n\t\t</constant>\n\t\t<constant name=\"FLAT\" value=\"1\" enum=\"WorldBackground\">\n\t\t\tOutside of the defined regions, show a flat terrain.\n\t\t</constant>\n\t\t<constant name=\"NOISE\" value=\"2\" enum=\"WorldBackground\">\n\t\t\tOutside of the defined regions, generate visual-only hills.\n\t\t</constant>\n\t\t<constant name=\"LINEAR_ANISOTROPIC\" value=\"0\" enum=\"TextureFiltering\">\n\t\t\tTextures are filtered using a blend of 4 adjacent pixels, with anisotropic filtering which improves the sharpness of distant terrain off axis from the camera. Use this for most cases for high quality renders.\n\t\t</constant>\n\t\t<constant name=\"LINEAR\" value=\"1\" enum=\"TextureFiltering\">\n\t\t\tTextures are filtered using a blend of 4 adjacent pixels. Use this for most cases for high quality renders.\n\t\t</constant>\n\t\t<constant name=\"NEAREST_ANISOTROPIC\" value=\"2\" enum=\"TextureFiltering\">\n\t\t\tTextures are filtered using the nearest pixel only with anisotropic filtering which improves the sharpness of distant terrain off axis from the camera. It is faster than LINEAR, but the texture will look pixelated. Use this for a low-poly look, with a very low uv_scale.\n\t\t</constant>\n\t\t<constant name=\"NEAREST\" value=\"3\" enum=\"TextureFiltering\">\n\t\t\tTextures are filtered using the nearest pixel only. It is faster than LINEAR, but the texture will look pixelated. Use this for a low-poly look, with a very low uv_scale.\n\t\t</constant>\n\t\t<constant name=\"UNIFORMS_ONLY\" value=\"0\" enum=\"UpdateFlags\">\n\t\t\tNon-texture array values are assigned to the shader. This is the default and is always done.\n\t\t</constant>\n\t\t<constant name=\"TEXTURE_ARRAYS\" value=\"1\" enum=\"UpdateFlags\">\n\t\t\tThe ground texture arrays are assigned to the shader, along with the values in `UNIFORMS_ONLY`.\n\t\t</constant>\n\t\t<constant name=\"REGION_ARRAYS\" value=\"2\" enum=\"UpdateFlags\">\n\t\t\tThe region data texture arrays are assigned to the shader, along with the values in `UNIFORMS_ONLY`.\n\t\t</constant>\n\t\t<constant name=\"UPDATE_ARRAYS\" value=\"3\" enum=\"UpdateFlags\">\n\t\t\tValues in `TEXTURE_ARRAYS` and `REGION_ARRAYS` are assigned to the shader.\n\t\t</constant>\n\t\t<constant name=\"FULL_REBUILD\" value=\"7\" enum=\"UpdateFlags\">\n\t\t\tThe shader is rebuilt, then all values in `UPDATE_ARRAYS` are assigned to the shader.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DMeshAsset.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DMeshAsset\" inherits=\"Resource\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis class manages meshes used for instancing. There are two broad types of meshes it can host.\n\t\t1. Generated Texture Card - this class will generate a QuadMesh. The typical use for this is to create a material in the override material, place a 2D grass texture in the `albedo texture` slot, and enable alpha scissor. This will generate low poly grass.\n\t\t2. Scene File - you can provide your own mesh in a scene file, which is specifically a PackedScene (.tscn, .scn, .glb, .fbx, etc). You can override the material if desired. MultiMeshes only support one mesh object, so complex objects like tree trunks and leaves, or a door frame and door either need to be combined into one object with multiple materials, or placed by another method. The system will look for MeshInstance3D nodes in the file to use as Levels of Detail (LODs). Ideally they have suffixes like `LOD0`, `LOD1`. We support up to 10 LODs, but recommend no more than 4.\n\t\t[b]Read More:[/b]\n\t\t- [b]Tutorial:[/b] [url=https://terrain3d.readthedocs.io/en/stable/docs/instancer.html]Foliage Instancing[/url]\n\t\t- [b]Godot Reference:[/b] [url=https://docs.godotengine.org/en/stable/classes/class_multimesh.html]MultiMesh[/url], [url=https://docs.godotengine.org/en/stable/classes/class_meshinstance3d.html#class-meshinstance3d]MultiMeshInstance3D[/url]\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"clear\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tResets this resource to default settings.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_highlight_color\" qualifiers=\"const\">\n\t\t\t<return type=\"Color\" />\n\t\t\t<description>\n\t\t\t\tReturns the color of the current highlight material, if any.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_instance_count\" qualifiers=\"const\">\n\t\t\t<return type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the number of instances on the ground for this mesh asset.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_lod_range\" qualifiers=\"const\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"lod\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the far visible distance for the specified Level of Detail (LOD). The near visible distance is the LOD range for the next closest LOD.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_mesh\" qualifiers=\"const\">\n\t\t\t<return type=\"Mesh\" />\n\t\t\t<param index=\"0\" name=\"lod\" type=\"int\" default=\"0\" />\n\t\t\t<description>\n\t\t\t\tReturns the [code skip-lint]Mesh[/code] resource for the specified Level of Detail (LOD).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_thumbnail\" qualifiers=\"const\">\n\t\t\t<return type=\"Texture2D\" />\n\t\t\t<description>\n\t\t\t\tReturns the thumbnail generated by [Terrain3DAssets].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_highlighted\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the instances for this mesh asset are currently highlighted on the ground. For editor use.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_highlighted\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"enabled\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tEnables or disables adding a highlight material with a random color to the overlay slot for all instances of this mesh asset.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_lod_range\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"lod\" type=\"int\" />\n\t\t\t<param index=\"1\" name=\"distance\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tSets the far visible distance for the specified Level of Detail (LOD).\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"cast_shadows\" type=\"int\" setter=\"set_cast_shadows\" getter=\"get_cast_shadows\" enum=\"RenderingServer.ShadowCastingSetting\" default=\"1\">\n\t\t\tTells the renderer how to cast shadows from this mesh asset onto the terrain and other objects. This sets [code skip-lint]GeometryInstance3D.cast_shadow[/code] on all MultiMeshInstances used by this mesh.\n\t\t</member>\n\t\t<member name=\"density\" type=\"float\" setter=\"set_density\" getter=\"get_density\" default=\"0.0\">\n\t\t\tDensity is used to set the approximate default spacing between instances based on the size of the mesh. When painting meshes on the terrain, mesh density is multiplied by brush strength.\n\t\t\tThis value is not tied to any real world unit. It is calculated as [code skip-lint]10.f / mesh-&gt;get_aabb().get_volume()[/code], then clamped to a sane range. If the calculated amount is inappropriate, increase or decrease it here.\n\t\t</member>\n\t\t<member name=\"enabled\" type=\"bool\" setter=\"set_enabled\" getter=\"is_enabled\" default=\"true\">\n\t\t\tIf enabled, MultiMeshInstance3Ds will be generated for this mesh asset.\n\t\t</member>\n\t\t<member name=\"fade_margin\" type=\"float\" setter=\"set_fade_margin\" getter=\"get_fade_margin\" default=\"0.0\">\n\t\t\tIf &gt; 0, sets [code skip-lint]GeometryInstance3D.fade_mode = self[/code], sets this margin in [code skip-lint]GeometryInstance3D.visibility_range_begin_margin[/code] and [code skip-lint]GeometryInstance3D.visibility_range_end_margin[/code] and adjusts the ranges to allow fading between lods.\n\t\t\tLimited to the smaller of half the distance between LOD0 and LOD1, or 64m.\n\t\t\tCurrently broken and hidden in the inspector until [url=https://github.com/godotengine/godot/issues/102799]Godot issue #102799[/url] is fixed.\n\t\t</member>\n\t\t<member name=\"generated_faces\" type=\"int\" setter=\"set_generated_faces\" getter=\"get_generated_faces\" default=\"2\">\n\t\t\tSelect if you want the generated texture card to have a single QuadMesh, 2 meshes rotated 90° in a cross, or 3 rotated at 60°.\n\t\t</member>\n\t\t<member name=\"generated_size\" type=\"Vector2\" setter=\"set_generated_size\" getter=\"get_generated_size\" default=\"Vector2(1, 1)\">\n\t\t\tSets the base size of the QuadMesh texture card. Increasing this size will expand from bottom, not the middle.\n\t\t</member>\n\t\t<member name=\"generated_type\" type=\"int\" setter=\"set_generated_type\" getter=\"get_generated_type\" enum=\"Terrain3DMeshAsset.GenType\" default=\"0\">\n\t\t\tIf enabled, this mesh asset will be set to a generated QuadMesh to be used as a texture card.\n\t\t</member>\n\t\t<member name=\"height_offset\" type=\"float\" setter=\"set_height_offset\" getter=\"get_height_offset\" default=\"0.0\">\n\t\t\tVertically offsets the origin point of the mesh asset. For example, if you have a 2 meter diameter rock with the mesh origin point in the center, but you want all rocks to be sitting on the ground, you could enter 1 or 0.9 here and it will be placed near its edge. You can also adjust this when painting using the tool settings bar; both options are cummulative.\n\t\t</member>\n\t\t<member name=\"id\" type=\"int\" setter=\"set_id\" getter=\"get_id\" default=\"0\">\n\t\t\tThe user settable ID of the mesh. You can change this to reorder meshes in the list.\n\t\t</member>\n\t\t<member name=\"last_lod\" type=\"int\" setter=\"set_last_lod\" getter=\"get_last_lod\" default=\"9\">\n\t\t\tSets the farthest Level of Detail (LOD) that will be rendered. Farther LODs (greater than this number) will be ignored.\n\t\t</member>\n\t\t<member name=\"last_shadow_lod\" type=\"int\" setter=\"set_last_shadow_lod\" getter=\"get_last_shadow_lod\" default=\"9\">\n\t\t\tSets the farthest Level of Detail (LOD) that will cast shadows. Farther LODs (greater than this number) won't cast shadows. If [member shadow_impostor] is greater than this number, the shadow impostor will still cast shadows.\n\t\t</member>\n\t\t<member name=\"lod0_range\" type=\"float\" setter=\"set_lod0_range\" getter=\"get_lod0_range\" default=\"32.0\">\n\t\t\tSets [code skip-lint]GeometryInstance3D.visibility_range_end[/code] on all MultiMeshInstances used by this mesh at the closest Level of Detail (LOD), the most detailed.\n\t\t\tAllows the renderer to cull MMIs beyond this distance. The next LOD level will use this value for its [code skip-lint]GeometryInstance3D.visibility_range_begin[/code].\n\t\t\tSet to 0 to disable culling and see this LOD at any distance.\n\t\t</member>\n\t\t<member name=\"lod1_range\" type=\"float\" setter=\"set_lod1_range\" getter=\"get_lod1_range\" default=\"64.0\">\n\t\t\tSets the end visible range for LOD1. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod2_range\" type=\"float\" setter=\"set_lod2_range\" getter=\"get_lod2_range\" default=\"96.0\">\n\t\t\tSets the end visible range for LOD2. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod3_range\" type=\"float\" setter=\"set_lod3_range\" getter=\"get_lod3_range\" default=\"128.0\">\n\t\t\tSets the end visible range for LOD3. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod4_range\" type=\"float\" setter=\"set_lod4_range\" getter=\"get_lod4_range\" default=\"160.0\">\n\t\t\tSets the end visible range for LOD4. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod5_range\" type=\"float\" setter=\"set_lod5_range\" getter=\"get_lod5_range\" default=\"192.0\">\n\t\t\tSets the end visible range for LOD5. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod6_range\" type=\"float\" setter=\"set_lod6_range\" getter=\"get_lod6_range\" default=\"224.0\">\n\t\t\tSets the end visible range for LOD6. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod7_range\" type=\"float\" setter=\"set_lod7_range\" getter=\"get_lod7_range\" default=\"256.0\">\n\t\t\tSets the end visible range for LOD7. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod8_range\" type=\"float\" setter=\"set_lod8_range\" getter=\"get_lod8_range\" default=\"288.0\">\n\t\t\tSets the end visible range for LOD8. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod9_range\" type=\"float\" setter=\"set_lod9_range\" getter=\"get_lod9_range\" default=\"320.0\">\n\t\t\tSets the end visible range for LOD9. See [member lod0_range].\n\t\t</member>\n\t\t<member name=\"lod_count\" type=\"int\" setter=\"\" getter=\"get_lod_count\" default=\"0\">\n\t\t\tProvides the detected number of Levels of Detail (LODs) found in the provided scene file or generated mesh. Read only.\n\t\t</member>\n\t\t<member name=\"material_overlay\" type=\"Material\" setter=\"set_material_overlay\" getter=\"get_material_overlay\">\n\t\t\tThis sets [code skip-lint]GeometryInstance3D.material_overlay[/code], which applies an overriding material using [code skip-lint]next_pass[/code] that overlays the base material.\n\t\t</member>\n\t\t<member name=\"material_override\" type=\"Material\" setter=\"set_material_override\" getter=\"get_material_override\">\n\t\t\tThis sets [code skip-lint]GeometryInstance3D.material_override[/code], which replaces the rendered mesh material with this one.\n\t\t</member>\n\t\t<member name=\"name\" type=\"String\" setter=\"set_name\" getter=\"get_name\" default=\"&quot;New Mesh&quot;\">\n\t\t\tThe user specified name for this asset.\n\t\t</member>\n\t\t<member name=\"scene_file\" type=\"PackedScene\" setter=\"set_scene_file\" getter=\"get_scene_file\">\n\t\t\tSpecifies the PackedScene (.tscn, .scn, .glb, .fbx, etc) to load the mesh from. See the top description.\n\t\t</member>\n\t\t<member name=\"shadow_impostor\" type=\"int\" setter=\"set_shadow_impostor\" getter=\"get_shadow_impostor\" default=\"0\">\n\t\t\tUses this lower quality Level of Detail (LOD) to calculate shadows (as an impostor) instead of the visible mesh.\n\t\t\te.g. Normally each LOD casts its own shadows. Given LOD0-3, if [code skip-lint]shadow_impostor = 2[/code] then when LOD0-1 are visible, they are set to no shadows and LOD2 is set to cast shadows only. When LOD2-3 are visible, each casts their own shadow as normal.\n\t\t\tIncrease to improve performance by lowering shadow quality.\n\t\t\tShadow impostors are disabled if this is set to 0 or if [member cast_shadows] is set to shadows only.\n\t\t</member>\n\t\t<member name=\"visibility_layers\" type=\"int\" setter=\"set_visibility_layers\" getter=\"get_visibility_layers\" default=\"1\">\n\t\t\tSets [member VisualInstance3D.layers], which defines the rendering layers the MultiMeshInstance3Ds for this mesh asset are drawn on.\n\t\t</member>\n\t</members>\n\t<signals>\n\t\t<signal name=\"id_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when [member id] is changed.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"instance_count_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when instances of this mesh asset have been added or removed.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"instancer_setting_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when instancer specific settings are changed on this mesh asset, such as [member cast_shadows], and triggers an instancer rebuild.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"setting_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when settings are changed, other than those tracked by other signals.\n\t\t\t</description>\n\t\t</signal>\n\t</signals>\n\t<constants>\n\t\t<constant name=\"TYPE_NONE\" value=\"0\" enum=\"GenType\">\n\t\t\tNo generated mesh is in use, likely because a scene file is used.\n\t\t</constant>\n\t\t<constant name=\"TYPE_TEXTURE_CARD\" value=\"1\" enum=\"GenType\">\n\t\t\tGenerates a QuadMesh to be used as a texture card.\n\t\t</constant>\n\t\t<constant name=\"TYPE_MAX\" value=\"2\" enum=\"GenType\">\n\t\t\tMaximum value for this enum.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DRegion.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DRegion\" inherits=\"Resource\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis resource stores all map data for Terrain3D. See [url=https://terrain3d.readthedocs.io/en/stable/docs/controlmap_format.html]Controlmap Format[/url] and [url=https://terrain3d.readthedocs.io/en/stable/docs/controlmap_format.html]Data Format Changelog[/url].\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"calc_height_range\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tRecalculates the height range for this region by looking at every pixel in the heightmap.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"clear\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tUnreferences the maps and resets all of the variables to default values.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"dump\" qualifiers=\"const\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"verbose\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tDumps information about the data in the region, including instance IDs, counts, and data pointers.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"duplicate\">\n\t\t\t<return type=\"Terrain3DRegion\" />\n\t\t\t<param index=\"0\" name=\"deep\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tReturns a duplicate copy of this node, with references to the same image maps and multimeshes.\n\t\t\t\t- deep - Also make complete duplicates of the maps and multimeshes.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_data\" qualifiers=\"const\">\n\t\t\t<return type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tReturns all data in this region in a dictionary.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_map\" qualifiers=\"const\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<description>\n\t\t\t\tReturns the specified image map.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_maps\" qualifiers=\"const\">\n\t\t\t<return type=\"Image[]\" />\n\t\t\t<description>\n\t\t\t\tReturns an Array[Image] with height, control, and color maps.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"sanitize_map\" qualifiers=\"const\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<param index=\"1\" name=\"map\" type=\"Image\" />\n\t\t\t<description>\n\t\t\t\tValidates and adjusts the map size and format if possible, or creates a usable blank image in the right size and format.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"sanitize_maps\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tSanitizes all map types. See [method sanitize_map].\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"save\">\n\t\t\t<return type=\"int\" enum=\"Error\" />\n\t\t\t<param index=\"0\" name=\"path\" type=\"String\" default=\"&quot;&quot;\" />\n\t\t\t<param index=\"1\" name=\"save_16_bit\" type=\"bool\" default=\"false\" />\n\t\t\t<description>\n\t\t\t\tSaves this region to the current file name.\n\t\t\t\t- path - specifies a directory and file name to use from now on.\n\t\t\t\t- 16-bit - save this region with 16-bit height map instead of 32-bit. This process is lossy. Does not change the bit depth in memory.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_data\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"data\" type=\"Dictionary\" />\n\t\t\t<description>\n\t\t\t\tOverwrites all local variables with values in the dictionary.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_map\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"map_type\" type=\"int\" enum=\"Terrain3DRegion.MapType\" />\n\t\t\t<param index=\"1\" name=\"map\" type=\"Image\" />\n\t\t\t<description>\n\t\t\t\tAssigns the provided map to the desired map type.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_maps\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"maps\" type=\"Image[]\" />\n\t\t\t<description>\n\t\t\t\tExpects an array with three images in it, and assigns them to the height, control, and color maps.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_height\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"height\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tWhen sculpting, this is called to provide the current height. It may expand the vertical bounds, which is used to calculate the terrain AABB.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"update_heights\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"low_high\" type=\"Vector2\" />\n\t\t\t<description>\n\t\t\t\tWhen sculpting the terrain, this is called to provide both a low and high height. It may expand the vertical bounds, which is used to calculate the terrain AABB.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"validate_map_size\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"map\" type=\"Image\" />\n\t\t\t<description>\n\t\t\t\tThis validates the map size according to previously loaded maps.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"color_map\" type=\"Image\" setter=\"set_color_map\" getter=\"get_color_map\">\n\t\t\tThis map is used to paint color that blends in to the terrain textures.\n\t\t\tImage format: FORMAT_RGBA8, 32-bits per pixel as four 8-bit components.\n\t\t\t[b]RGB[/b] is used for color, which is multiplied by albedo in the shader. Multiply is a blend mode that only darkens.\n\t\t\t[b]A[/b] is used for a roughness modifier. A value of 0.5 means no change to the existing texture roughness. Higher than this value increases roughness, lower decreases it.\n\t\t</member>\n\t\t<member name=\"control_map\" type=\"Image\" setter=\"set_control_map\" getter=\"get_control_map\">\n\t\t\tThis map tells the shader which textures to use where, how to blend, where to place holes, etc.\n\t\t\tImage format: FORMAT_RF, 32-bit per pixel as full-precision floating-point.\n\t\t\tHowever, we interpret these images as format: [url=https://docs.godotengine.org/en/stable/classes/class_renderingdevice.html#class-renderingdevice-constant-data-format-r32-uint]RenderingDevice.DATA_FORMAT_R32_UINT[/url] aka OpenGL RG32UI 32-bit per pixel as unsigned integer. See [url=https://terrain3d.readthedocs.io/en/stable/docs/controlmap_format.html]Control map format[/url].\n\t\t</member>\n\t\t<member name=\"deleted\" type=\"bool\" setter=\"set_deleted\" getter=\"is_deleted\">\n\t\t\tThis region is marked for deletion. It won't be rendered once [method Terrain3DData.update_maps] rebuilds the map index. The file will be deleted from disk on [method save].\n\t\t</member>\n\t\t<member name=\"edited\" type=\"bool\" setter=\"set_edited\" getter=\"is_edited\">\n\t\t\tThis region is marked for updating by [method Terrain3DData.update_maps] and for undo/redo tracking when set between [method Terrain3DEditor.start_operation] and [method Terrain3DEditor.stop_operation]. The latter method clears the edited flag. This flag serves a different purpose than [member modified].\n\t\t</member>\n\t\t<member name=\"height_map\" type=\"Image\" setter=\"set_height_map\" getter=\"get_height_map\">\n\t\t\tThis map contains the real value heights for the terrain.\n\t\t\tImage format: FORMAT_RF, 32-bit per pixel as full-precision floating-point.\n\t\t\tHeights sent to the vertex shader on the GPU which modifies the mesh in real-time.\n\t\t\tEditing is always done in 32-bit. We do provide an option to save as 16-bit, see [member Terrain3D.save_16_bit].\n\t\t</member>\n\t\t<member name=\"height_range\" type=\"Vector2\" setter=\"set_height_range\" getter=\"get_height_range\" default=\"Vector2(0, 0)\">\n\t\t\tThe current minimum and maximum height range for this region, used to calculate the AABB of the terrain. Update it with [method update_height], and recalculate it with [method calc_height_range].\n\t\t</member>\n\t\t<member name=\"instances\" type=\"Dictionary\" setter=\"set_instances\" getter=\"get_instances\" default=\"{}\">\n\t\t\tA Dictionary that stores the instancer transforms for this region.\n\t\t\tThe format is instances{mesh_id:int} -&gt; cells{grid_location:Vector2i} -&gt; ( Array:Transform3D, PackedColorArray, modified:bool ). That is:\n\t\t\t- A Dictionary keyed by mesh_id that returns:\n\t\t\t- A Dictionary keyed by the grid location of the 32 x 32m cell that returns:\n\t\t\t- A 3-item Array that contains:\n\t\t\t- 0: An Array of Transform3Ds\n\t\t\t- 1: A PackedColorArray with instance colors, same index as above\n\t\t\t- 2: A bool that tracks if this cell has been modified\n\t\t\tAfter changing this data, call [method Terrain3DInstancer.update_mmis] to rebuild the MMIs.\n\t\t</member>\n\t\t<member name=\"location\" type=\"Vector2i\" setter=\"set_location\" getter=\"get_location\">\n\t\t\tThe location in region grid space [code skip-lint](world space / region_size)[/code] coordinates. e.g. (-1, 1) equates to (-1024, 1024) in world space given a [member region_size] of 1024.\n\t\t</member>\n\t\t<member name=\"modified\" type=\"bool\" setter=\"set_modified\" getter=\"is_modified\">\n\t\t\tThis region has been modified and will be saved to disk upon [method save]. This serves a different purpose than the temporary [member edited] setting.\n\t\t</member>\n\t\t<member name=\"region_size\" type=\"int\" setter=\"set_region_size\" getter=\"get_region_size\" default=\"0\">\n\t\t\tThe current region size for this region, calculated from the dimensions of the first loaded map. It should match [member Terrain3D.region_size].\n\t\t</member>\n\t\t<member name=\"version\" type=\"float\" setter=\"set_version\" getter=\"get_version\" default=\"0.8\">\n\t\t\tThe data file version. This is independent of the Terrain3D version, though they often align.\n\t\t</member>\n\t\t<member name=\"vertex_spacing\" type=\"float\" setter=\"set_vertex_spacing\" getter=\"get_vertex_spacing\" default=\"1.0\">\n\t\t\tStored instancer transforms are laterally scaled by this value. This value is manage by the instancer on loading or when [member Terrain3D.vertex_spacing] is set, and shouldn't be manually adjusted.\n\t\t</member>\n\t</members>\n\t<constants>\n\t\t<constant name=\"TYPE_HEIGHT\" value=\"0\" enum=\"MapType\">\n\t\t\tHeight map - real values, eg. 10m, 44.5m.\n\t\t</constant>\n\t\t<constant name=\"TYPE_CONTROL\" value=\"1\" enum=\"MapType\">\n\t\t\tControl map - defines where textures and holes are placed.\n\t\t</constant>\n\t\t<constant name=\"TYPE_COLOR\" value=\"2\" enum=\"MapType\">\n\t\t\tColor map - paints color on the terrain\n\t\t</constant>\n\t\t<constant name=\"TYPE_MAX\" value=\"3\" enum=\"MapType\">\n\t\t\tThe number of elements in this enum. Often used to specify all map types for functions that request one.\n\t\t</constant>\n\t</constants>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DTextureAsset.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DTextureAsset\" inherits=\"Resource\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tA set of texture files and settings that gets added to a [Terrain3DAssets] resource. Textures must be prepared according to the [url=https://terrain3d.readthedocs.io/en/stable/docs/texture_prep.html]documentation[/url].\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"clear\">\n\t\t\t<return type=\"void\" />\n\t\t\t<description>\n\t\t\t\tClears the texture files and settings.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_highlight_color\" qualifiers=\"const\">\n\t\t\t<return type=\"Color\" />\n\t\t\t<description>\n\t\t\t\tReturns the color of the current highlight, if any.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_thumbnail\" qualifiers=\"const\">\n\t\t\t<return type=\"Texture2D\" />\n\t\t\t<description>\n\t\t\t\tReturns the generated thumbnail.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_highlighted\" qualifiers=\"const\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns true if this texture is currently highlighted on the ground. For editor use.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"set_highlighted\">\n\t\t\t<return type=\"void\" />\n\t\t\t<param index=\"0\" name=\"enabled\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tEnables or disables adding a random highlight color to this texture wherever placed on the ground.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n\t<members>\n\t\t<member name=\"albedo_color\" type=\"Color\" setter=\"set_albedo_color\" getter=\"get_albedo_color\" default=\"Color(1, 1, 1, 1)\">\n\t\t\tThis color is multiplied by the albedo texture in the shader.\n\t\t</member>\n\t\t<member name=\"albedo_texture\" type=\"Texture2D\" setter=\"set_albedo_texture\" getter=\"get_albedo_texture\">\n\t\t\tThe texture file with albedo on RGB and height on A.\n\t\t</member>\n\t\t<member name=\"ao_light_affect\" type=\"float\" setter=\"set_ao_light_affect\" getter=\"get_ao_light_affect\" default=\"0.0\">\n\t\t\tThis value is applied directly to the AO_LIGHT_AFFECT in the shader, which dictates how much ambient occlusion affects light from Light3Ds. 0 means AO only affects ambient light. 1 means it also affects lights.\n\t\t</member>\n\t\t<member name=\"ao_strength\" type=\"float\" setter=\"set_ao_strength\" getter=\"get_ao_strength\" default=\"0.5\">\n\t\t\tThe strength of ambient occlusion, which darkens areas of this texture dictated by the ambient occlusion map. If you have not provided an AO texture, an AO value is approximated by the normal map automatically.\n\t\t</member>\n\t\t<member name=\"detiling_rotation\" type=\"float\" setter=\"set_detiling_rotation\" getter=\"get_detiling_rotation\" default=\"0.0\">\n\t\t\tThe shader rotates UV lookups in a detiling pattern based on this value.\n\t\t</member>\n\t\t<member name=\"detiling_shift\" type=\"float\" setter=\"set_detiling_shift\" getter=\"get_detiling_shift\" default=\"0.0\">\n\t\t\tThe shader laterally shifts UV lookups in a detiling pattern based on this value.\n\t\t</member>\n\t\t<member name=\"displacement_offset\" type=\"float\" setter=\"set_displacement_offset\" getter=\"get_displacement_offset\" default=\"0.0\">\n\t\t\tOffset that can be used to raise or lower the displaced surface height for this material.\n\t\t\tExample: slightly lowering a cobblestone texture so the tops of the cobbles match collision.\n\t\t</member>\n\t\t<member name=\"displacement_scale\" type=\"float\" setter=\"set_displacement_scale\" getter=\"get_displacement_scale\" default=\"0.0\">\n\t\t\tThe scale of the displaced surface height for this material.\n\t\t</member>\n\t\t<member name=\"id\" type=\"int\" setter=\"set_id\" getter=\"get_id\" default=\"0\">\n\t\t\tThe user settable ID of the texture, between 0 and 31. You can change this to reorder textures in the list, however it won't change the ID painted on the terrain.\n\t\t</member>\n\t\t<member name=\"name\" type=\"String\" setter=\"set_name\" getter=\"get_name\" default=\"&quot;New Texture&quot;\">\n\t\t\tA user specified name for this texture set.\n\t\t</member>\n\t\t<member name=\"normal_depth\" type=\"float\" setter=\"set_normal_depth\" getter=\"get_normal_depth\" default=\"1.0\">\n\t\t\tIncreases or decreases the strength of the normal texture.\n\t\t</member>\n\t\t<member name=\"normal_texture\" type=\"Texture2D\" setter=\"set_normal_texture\" getter=\"get_normal_texture\">\n\t\t\tThe texture file with normal on RGB and roughness on A.\n\t\t</member>\n\t\t<member name=\"roughness\" type=\"float\" setter=\"set_roughness\" getter=\"get_roughness\" default=\"0.0\">\n\t\t\tIncreases or decreases the roughness texture values.\n\t\t</member>\n\t\t<member name=\"uv_scale\" type=\"float\" setter=\"set_uv_scale\" getter=\"get_uv_scale\" default=\"0.1\">\n\t\t\tThe scale of the textures.\n\t\t</member>\n\t</members>\n\t<signals>\n\t\t<signal name=\"file_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when [member albedo_texture] or [member normal_texture] are changed.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"id_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when [member id] is changed.\n\t\t\t</description>\n\t\t</signal>\n\t\t<signal name=\"setting_changed\">\n\t\t\t<description>\n\t\t\t\tEmitted when any setting is changed, other than id, albedo_texture, or normal_texture.\n\t\t\t</description>\n\t\t</signal>\n\t</signals>\n</class>\n"
  },
  {
    "path": "doc/doc_classes/Terrain3DUtil.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<class name=\"Terrain3DUtil\" inherits=\"Object\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd\">\n\t<brief_description>\n\t</brief_description>\n\t<description>\n\t\tThis class contains static utility functions. Reference them with the full class name. Eg. [code skip-lint]Terrain3DUtil.as_float()[/code].\n\t\tOr you can instance the class for a shorter alias:\n\t\t[codeblock]\n\t\tvar util := Terrain3DUtil.new()\n\t\tvar my_float: float = util.as_float(my_int)\n\t\t[/codeblock]\n\t\t[b]Note on uints[/b]: Various functions refer to unsigned integers as uint. Though GDScript doesn't support unsigned integers as a type, the C++ that receives and returns these values will interpret them all as unsigned.\n\t\t[b]Note on bitwise-ORing[/b]: To write back to a control map, encode your values and bitwise OR the results, then reinterpret that uint as a float. The shader will interpret the float as uint and extract the bits.\n\t\t[codeblock]\n\t\tvar bits: int = util.enc_base(base_id) | util.enc_overlay(over_id) | \\\n\t\tutil.enc_blend(blend) | util.enc_uv_rotation(uvrotation) | \\\n\t\tutil.enc_uv_scale(uvscale) | util.enc_auto(autoshader) | \\\n\t\tutil.enc_nav(navigation) | util.enc_hole(hole)\n\t\tvar color: Color = Color(util.as_float(bits), 0., 0., 1.)\n\t\tdata.set_control(global_pos, color)\n\t\t[/codeblock]\n\t</description>\n\t<tutorials>\n\t</tutorials>\n\t<methods>\n\t\t<method name=\"as_float\" qualifiers=\"static\">\n\t\t\t<return type=\"float\" />\n\t\t\t<param index=\"0\" name=\"value\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns a float typed variable with the contents of the memory stored in value, an integer typed variable.\n\t\t\t\tThis function does not convert integer values to float values (e.g. 4 -&gt; 4.0). It reinterprets the memory block as if it were a float. If the data in value was a valid integer, it is now an invalid float.\n\t\t\t\t[code skip-lint]my_float == util.as_float(util.as_uint(my_float))[/code]\n\t\t\t\tSee [method as_uint] for the opposite.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"as_uint\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"value\" type=\"float\" />\n\t\t\t<description>\n\t\t\t\tReturns an integer typed variable with the contents of the memory stored in value, a float typed variable.\n\t\t\t\tThis function does not convert float values to integer values (e.g. 4.0 -&gt; 4). It reinterprets the memory block as if it were an integer. If the data in value was a valid float, it is now a valid integer, but probably an unexepctedly large value.\n\t\t\t\t[code skip-lint]my_int == util.as_uint(util.as_float(my_int))[/code]\n\t\t\t\tSee [method as_float] for the opposite.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"black_to_alpha\" qualifiers=\"static\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"image\" type=\"Image\" />\n\t\t\t<description>\n\t\t\t\tReceives an image with a black background and returns one with a transparent background, aka an alpha mask.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_auto\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the auto shader bit set. See the top description for usage.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_base\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"base\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the base texture ID encoded. See the top description for usage.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_blend\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"blend\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the blend value encoded. See the top description for usage.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_hole\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the hole bit set. See the top description for usage.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_nav\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"bool\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the nav bit set. See the top description for usage.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_overlay\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"overlay\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the overlay texture ID encoded. See the top description for usage.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_uv_rotation\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"rotation\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the texture rotation encoded. See the top description for usage.  See [method get_uv_rotation] for values.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"enc_uv_scale\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"scale\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns a control map uint with the texture scale encoded. See the top description for usage. See [method get_uv_scale] for values.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"filename_to_location\" qualifiers=\"static\">\n\t\t\t<return type=\"Vector2i\" />\n\t\t\t<param index=\"0\" name=\"filename\" type=\"String\" />\n\t\t\t<description>\n\t\t\t\tConverts a file name string like [code skip-lint]terrain3d-01_02.res[/code] to a region location like [code skip-lint](-1, 2)[/code]. - is negative, _ is positive.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_base\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the base texture ID from a control map pixel.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_blend\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the blend value from a control map pixel.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_filled_image\" qualifiers=\"static\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"size\" type=\"Vector2i\" />\n\t\t\t<param index=\"1\" name=\"color\" type=\"Color\" />\n\t\t\t<param index=\"2\" name=\"create_mipmaps\" type=\"bool\" />\n\t\t\t<param index=\"3\" name=\"format\" type=\"int\" enum=\"Image.Format\" />\n\t\t\t<description>\n\t\t\t\tReturns an Image filled with a specified color and format.\n\t\t\t\tIf [code skip-lint]color.a &lt; 0[/code], its filled with a checkered pattern multiplied by [code skip-lint]color.rgb[/code].\n\t\t\t\tThe behavior changes if a compressed format is requested:\n\t\t\t\t- If the editor is running and the format is DXT1, DXT5, or BPTC_RGBA, it returns a filled image in the requested color and format.\n\t\t\t\t- All other compressed formats return a blank image in that format.\n\t\t\t\tThe reason for this is the Image compression library is available only in the editor. And it is unreliable, offering little control over the output format, choosing automatically and often wrong. We have selected a few compressed formats it gets right.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_min_max\" qualifiers=\"static\">\n\t\t\t<return type=\"Vector2\" />\n\t\t\t<param index=\"0\" name=\"image\" type=\"Image\" />\n\t\t\t<description>\n\t\t\t\tReturns the minimum and maximum r channel values of an Image. Used for heightmaps.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_overlay\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the overlay texture ID from a control map pixel.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_thumbnail\" qualifiers=\"static\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"image\" type=\"Image\" />\n\t\t\t<param index=\"1\" name=\"size\" type=\"Vector2i\" default=\"Vector2i(256, 256)\" />\n\t\t\t<description>\n\t\t\t\tReturns an Image normalized and converted to RGB8. Used for creating a human viewable image of a heightmap, at any size.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_uv_rotation\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the texture rotation from a control map pixel. Values are 0 - 15, which provides degrees when multiplied by 22.5. (360/16).\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"get_uv_scale\" qualifiers=\"static\">\n\t\t\t<return type=\"int\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns the texture scale modification from a control map pixel. Values are an index into the array `{ 0, 20, 40, 60, 80, -60, -40, -20 }`. 0 indicates no scale modification. Index 2 indicates a 40% increase in texture scale at that pixel. Index -1 or 7 indicates a -20% texture scale change.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_auto\" qualifiers=\"static\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the control map pixel has the autoshader bit set.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_hole\" qualifiers=\"static\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the control map pixel has the hole bit set.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"is_nav\" qualifiers=\"static\">\n\t\t\t<return type=\"bool\" />\n\t\t\t<param index=\"0\" name=\"pixel\" type=\"int\" />\n\t\t\t<description>\n\t\t\t\tReturns true if the control map pixel has the nav bit set.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"load_image\" qualifiers=\"static\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"file_name\" type=\"String\" />\n\t\t\t<param index=\"1\" name=\"cache_mode\" type=\"int\" default=\"0\" />\n\t\t\t<param index=\"2\" name=\"r16_height_range\" type=\"Vector2\" default=\"Vector2(0, 255)\" />\n\t\t\t<param index=\"3\" name=\"r16_size\" type=\"Vector2i\" default=\"Vector2i(0, 0)\" />\n\t\t\t<description>\n\t\t\t\tLoads a file from disk and returns an Image.\n\t\t\t\t[code skip-lint]filename[/code] - The file name on disk to load. Loads EXR, R16/RAW, PNG, or a ResourceLoader format (jpg, res, tres, etc).\n\t\t\t\t[code skip-lint]cache_mode[/code] - Send this flag to the resource loader to force caching or not.\n\t\t\t\t[code skip-lint]height_range[/code] - Heights for R16 format. x=Min &amp; y=Max value ranges. Required for R16 import.\n\t\t\t\t[code skip-lint]size[/code] - Image dimensions for R16 format. Default (0,0) auto detects size, assuming square images. Required for non-square R16.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"location_to_filename\" qualifiers=\"static\">\n\t\t\t<return type=\"String\" />\n\t\t\t<param index=\"0\" name=\"region_location\" type=\"Vector2i\" />\n\t\t\t<description>\n\t\t\t\tConverts a region location like [code skip-lint](-1, 2)[/code] to a file name string like [code skip-lint]terrain3d-01_02.res[/code]. - is negative, _ is positive.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"luminance_to_height\" qualifiers=\"static\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"src_rgb\" type=\"Image\" />\n\t\t\t<description>\n\t\t\t\tGenerates a greyscale RGB8 height texture from the luminance values of the source image.\n\t\t\t</description>\n\t\t</method>\n\t\t<method name=\"pack_image\" qualifiers=\"static\">\n\t\t\t<return type=\"Image\" />\n\t\t\t<param index=\"0\" name=\"src_rgb\" type=\"Image\" />\n\t\t\t<param index=\"1\" name=\"src_a\" type=\"Image\" />\n\t\t\t<param index=\"2\" name=\"src_ao\" type=\"Image\" />\n\t\t\t<param index=\"3\" name=\"invert_green\" type=\"bool\" default=\"false\" />\n\t\t\t<param index=\"4\" name=\"invert_alpha\" type=\"bool\" default=\"false\" />\n\t\t\t<param index=\"5\" name=\"normalize_alpha\" type=\"bool\" default=\"false\" />\n\t\t\t<param index=\"6\" name=\"alpha_channel\" type=\"int\" default=\"0\" />\n\t\t\t<param index=\"7\" name=\"ao_channel\" type=\"int\" default=\"0\" />\n\t\t\t<description>\n\t\t\t\tReturns an RGBA Image packed for terrain usage.\n\t\t\t\t- [code skip-lint]src_rgb[/code] - The source Image for the RGB channels.\n\t\t\t\t- [code skip-lint]src_a[/code] - The source image for the A channel.\n\t\t\t\t- [code skip-lint]invert_green[/code] - Inverts the green channel to convert between OpenGL and DirectX normal maps.\n\t\t\t\t- [code skip-lint]invert_alpha[/code] - Inverts the alpha channel to convert between Roughness and Smoothness maps.\n\t\t\t\t- [code skip-lint]normalize_alpha[/code] - Normalizes the alpha channel to use full range for height map.\n\t\t\t\t- [code skip-lint]alpha_channel[/code] - The channel index (0-3: R,G,B,A) to use from src_a for the alpha channel.\n\t\t\t\t- [code skip-lint]ao_channel[/code] - The channel index (0-3: R,G,B,A) to use from src_ao for the ambient occlusion channel.\n\t\t\t</description>\n\t\t</method>\n\t</methods>\n</class>\n"
  },
  {
    "path": "doc/docs/authors.rst",
    "content": "Authors\n============\n.. include:: ../../AUTHORS.md\n   :parser: myst_parser.sphinx_"
  },
  {
    "path": "doc/docs/building_from_source.md",
    "content": "Building from Source\n====================\n\nIf you wish to use more recent builds without building from source, see [Nightly Builds](nightly_builds.md).\n\n## 1. Install dependencies\n\nFollow Godot's instructions to set up your system to build Godot. You don't need the Godot source code, so stop before then. You only need the build tools, specifically `scons`, `python`, a compiler, and any other tools these pages identify. They provide easy installation instructions.\n\n* [Windows](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html)\n* [Linux/BSD](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_linuxbsd.html)\n* [macOS](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_macos.html)\n\n\n## 2. Download this repository\n\nYou can either grab the zip file, or clone it on the command line. Only type in the commands after the $ prompts.\n\n```\n$ git clone git@github.com:TokisanGames/Terrain3D.git\n\nCloning into 'Terrain3D'...\nEnter passphrase for key:\nremote: Enumerating objects: 125, done.\nremote: Counting objects: 100% (125/125), done.\nremote: Compressing objects: 100% (79/79), done.\nremote: Total 125 (delta 56), reused 94 (delta 36), pack-reused 0\nReceiving objects: 100% (125/125), 42.20 KiB | 194.00 KiB/s, done.\nResolving deltas: 100% (56/56), done.\n```\n\n\n## 3. Initialize the submodule repository\n\n```\n$ cd Terrain3D\n\nTerrain3D$ git submodule init\nSubmodule 'godot-cpp' (https://github.com/godotengine/godot-cpp.git) registered for path 'godot-cpp'\n\nTerrain3D$ git submodule update\nCloning into 'C:/GD/Terrain3D/godot-cpp'...\nSubmodule path 'godot-cpp': checked out '9d1c396c54fc3bdfcc7da4f3abcb52b14f6cce8f'\n\n```\n\nNote the version it checked out: **9d1c396**...\n\nThis hash number is important for the next section.\n\n\n## 4. Identify the appropriate godot-cpp version\n\nThe checked out version of the godot-cpp submodule needs to match the version of your Godot engine build. e.g. Godot Engine 4.0.2 official build with godot-cpp checked out to a 4.0.2 branch. The early days of Godot 4.x were very strict and required the exact same major, minor, and patch versions. Since then, the requirements have loosened. For instance we've matched godot-cpp 4.1.3 with Godot engine 4.1.3 through 4.2.1 without issue.\n\nIn the repository, we leave godot-cpp linked to an older version for broad compatiblity. For your individual needs, you may chose to keep the version we have currently linked, or update it to the version of the engine build you are using.\n\n**What is important is that you are aware of which version of godot-cpp you have, which you wish to use, and know how to change it.**\n\nYou can check the version of your godot-cpp by changing to that directory, typing `git log`, and finding the most recent tag.\n\n```\nTerrain3D/godot-cpp$ git log\ncommit 631cd5fe37d4e6df6e5eb66eb4435feca12708cc (HEAD, tag: godot-4.1.3-stable, 4.1)\n...\n\n```\n\nUse one of these steps below to find and select the desired tag or commit hash, then move on to step 4.\n\nYou may need to update your godot-cpp before it can find or checkout the latest tags:\n\n```\nTerrain3D/godot-cpp$ git fetch\nFrom https://github.com/godotengine/godot-cpp\n * [new tag]         godot-4.0.1-stable -> godot-4.0.1-stable\n * [new tag]         godot-4.0.2-stable -> godot-4.0.2-stable\n```\n\nNow we can search for or checkout more recent tags and commits.\n\n\n### Using tags\nOn the [Godot-cpp repository page](https://github.com/godotengine/godot-cpp), click the branch selector, then `Tags` to identify available tags that match the Godot engine binary you wish to use. If your engine version is in this list, e.g. `godot-4.0.2-stable`, great, move on to step 4. Otherwise explore the commit history on the website or command line as shown below.\n\n```{image} images/build_tags.png\n:target: ../_images/build_tags.png\n```\n\n\n### Using the commit history\nIf your engine version doesn't have a tag assigned, because it's a custom build, you can look at the [godot-cpp commit history](https://github.com/godotengine/godot-cpp/commits/master) for a commit that syncs the repository to the upstream engine version. Search for entries named `Sync with upstream commit...`.\n\nEg, from Godot 4.0-stable.\n\n```{image} images/build_commit_history.png\n:target: ../_images/build_commit_history.png\n```\n\nClicking the `...` in the middle expands the description which shows that this commit syncs godot-cpp with Godot engine `4.0-stable`. To use this commit, copy the commit string on the right in blue. Click the two overlapping squares on the right to copy the commit hash (`9d1c396`).\n\n\n### Using the command line\nAlternatively, you can use git to search on the command line. Make sure to fetch to update the submodule (step 3). This will search the server (origin) for all commit messages with the string `stable`, allowing you to find the commits. Make sure to grab the commit hash on top (`9d1c396..`), not the upstream commit shown at the bottom, which is from the Godot repository.\n\n```\nTerrain3D/godot-cpp$ git log origin -Gstable\ncommit 9d1c396c54fc3bdfcc7da4f3abcb52b14f6cce8f (HEAD -> master, tag: godot-4.0-stable, origin/master, origin/HEAD, origin/4.0)\nAuthor: R mi Verschelde <rverschelde@gmail.com>\nDate:   Wed Mar 1 15:32:44 2023 +0100\n\n    gdextension: Sync with upstream commit 92bee43adba8d2401ef40e2480e53087bcb1eaf1 (4.0-stable)\n\n```\n\n\n## 5. Check out the correct version\nOnce you have identified the proper tag or commit string, and you have updated the godot-cpp submodule (step 3), you just need to check it out. If using a commit string, you may use either the full hash or just the first 6-8 characters, so `9d1c396` would also match 4.0-stable.\n\nThese examples will change the godot-cpp repository to 4.0-stable and 4.02-stable, respectively:\n\n```\nTerrain3D$ cd godot-cpp\n\nTerrain3D/godot-cpp$ git checkout 9d1c396c54fc3bdfcc7da4f3abcb52b14f6cce8f\nHEAD is now at 9d1c396 gdextension: Sync with upstream commit 92bee43adba8d2401ef40e2480e53087bcb1eaf1 (4.0-stable)\n\nTerrain3D/godot-cpp$ git checkout godot-4.0.2-stable\nPrevious HEAD position was 9d1c396 gdextension: Sync with upstream commit 92bee43adba8d2401ef40e2480e53087bcb1eaf1 (4.0-stable)\nHEAD is now at 7fb46e9 gdextension: Sync with upstream commit 7a0977ce2c558fe6219f0a14f8bd4d05aea8f019 (4.0.2-stable)\n\n```\n\n\n## 6. Build the extension\n\nBy default `scons` will build the debug library which works for the editor and debug exports. You can add `target=template_release` to build the release version.\n\n```\nTerrain3D/godot-cpp$ cd ..\n\n# To build the debug library\nTerrain3D$ scons\n\n# To build both debug and release versions sequentially (bash command line)\nTerrain3D$ scons && scons target=template_release\n\n```\n\nUpon success you should see something like this at the end:\n\n```\nCreating library project\\addons\\terrain_3d\\bin\\libterrain.windows.debug.x86_64.lib and object project\\addons\\terrain_3d\\bin\\libterrain.windows.debug.x86_64.exp\nscons: done building targets.\n```\n\n\n## 7. Set up the extension in Godot\n\n1. Build Terrain3D, then ensure binary libraries exist in `project/addons/terrain_3d/bin`.\n2. Run Godot with the console executable so you can see error messages.\n3. In the Project Manager, import the project found in `project` and open it. Restart when it prompts.\n4. In `Project / Project Settings / Plugins`, ensure that Terrain3D is enabled.\n5. Select `Project / Reload Current Project` to restart once more.\n6. If the demo scene doesn't open automatically, open `demo/Demo.tscn`. You should see a terrain. Run the scene by pressing `F6`. \n\nClose godot before rebuilding again.\n\nContinue with the remaining [installation instructions](installation.md#in-your-own-scene).\n\n\n## Other Build Options\n\nThe `scons` build system has additional useful options. These come from the GDExtension template we are using, so some options may not be supported or work properly for this particular plugin. e.g. The platform.\n\n\n### Debug Symbols\n\nBuild the extension with debug symbols. See [debugging](#debugging-the-source-code) below.\n```\nscons dev_build=yes\n```\n\n\n### Clean up build files\n```\n# Linux, other Unix, Git bash on Windows\nscons --clean\nrm project/addons/terrain_3d/bin/*\nfind . -iregex '.+\\.\\(a\\|lib\\|o\\|obj\\|os\\)' -delete\n\n# Windows\nscons --clean\ndel /q project\\addons\\terrain_3d\\bin\\*.*\ndel /s /q *.a *.lib *.o *.obj *.os\n```\n\n\n### Manually specify the target platform\nThis plugin supports Windows, Linux and macOS. See [Platform Support](platforms.md) for the status of other platforms.\n\n```\n# platform: Target platform (linux|macos|windows|android|ios|javascript)\n\nscons platform=linux\n```\n\n\n### Using C++20\nThe C++ standard used in Godot and Godot-cpp is C++17. However you may use C++20 for building GDExtensions if desired.\n\nOur [SConstruct](https://github.com/TokisanGames/Terrain3D/blob/main/SConstruct#L52-L56) file has some commented code towards the bottom that will replace the standard.\n\n\n### See all options\n```\n# Godot custom build options\nscons --help\n\n# Scons application options\nscons -H\n```\n\n\n## Troubleshooting\n\n### Debugging the source code\nIn addition to the [debug logs](troubleshooting.md#debug-logs) dumped to the console, it is possible to debug Godot and Terrain3D, stepping through the full source code for both projects, viewing the callstack, and watching variables. We do it in MSVC regularly with these steps.\n\n* Build Terrain3D with `scons dev_build=yes`.\n* Build Godot with `scons debug_symbols=true`.\n* Start Godot with the debugger from within the Godot source project, rather than the Terrain3D project.\n* The debugger will attach to the Project Manager. After you load your project in the editor, or your game scene, `Debug/Attach to Process...` to reattach the debugger to the new process. Alternatively, adjust your debug startup command so Godot loads your project in the editor or runs your game scene directly and the debugger will attach to it on startup.\n* Since you started in the Godot project, upon hitting a breakpoint in Terrain3D, it will ask for the location of the file. Once found, it should have no problem loading the source code.\n\nIf you have problems, use `Debug/Windows/Modules` to display the dependent libraries and ensure the symbols for Godot and Terrain3D are loaded.\n\nYou can also debug only Terrain3D, using the official Godot binary. You'll be able to view and step through Terrain3D code. Any calls to Godot will be processed, but you won't be able to step through the code or watch variables without the symbols.\n\n\n### When running scons, I get these errors:\n\n```\nTerrain3D$ scons\nscons: Reading SConscript files ...\n\nscons: warning: Calling missing SConscript without error is deprecated.\nTransition by adding must_exist=False to SConscript calls.\nMissing SConscript 'godot-cpp\\SConstruct'\nFile \"C:\\gd\\Terrain3D\\SConstruct\", line 6, in <module>\nAttributeError: 'NoneType' object has no attribute 'Append':\n  File \"C:\\gd\\Terrain3D\\SConstruct\", line 9:\n    env.Append(CPPPATH=[\"src/\"])\n\n```\n\nYour godot-cpp directory is probably empty. Review the instructions above for updating the submodule.\n\n\n### I can build the plugin, however Godot instantly crashes. \nYour godot-cpp version probably does not match your engine version. See section 3 above to learn how to identify and change versions. Test the example project in the next question.\n\n\n### How can I make sure godot-cpp is the right version and working?\nYou'll find a test project in `godot-cpp/test/`. Make sure this test project works with your Godot version first, then come back and try Terrain3D again.\n  * Build the example plugin by typing `scons` while in the `godot-cpp/test/` directory.\n  * Copy `example.gdextension` and `bin` into the root folder of your project.\n  * Run Godot. If it crashes, you're on the wrong version, or Godot-cpp has a problem that the maintainers will need to resolve.\n  * Create a new scene.\n  * Add a new `Example` node. When clicking the node, you should see an `Example` section and `Property From List` and various `Dproperty#` variables in the inspector.\n"
  },
  {
    "path": "doc/docs/collision.md",
    "content": "Collision\n=======================\n\nOne of the most important things about a terrain is knowing where it is. Using physics based collision is not the only way, nor even the best or fastest way. There are at least 5 ways to detect terrain height: Physics based raycasting on collision, raymarching, the GPU depth texture, get_height(), and reading the heightmap directly.\n\nYou should use raycasting only when you don't already know the X, Z of the collision point (eg not vertical).\n\n\n## Physics Based Collision & Raycasting\n\nTo enable physics based collision, we must generate a StaticBody and CollisionShapes that match the shape of the terrain. This means collision is only generated where you have defined regions. Out in the WorldNoise background, there is no terrain data, so no collision.\n\nCollision generation can be slow and consume a lot of memory, so we offer five options:\n* `Dynamic / Game` is the default, which only generates around the camera while in game. It is node-less and the fastest option.\n* `Dynamic / Editor` generates around the camera in editor or in game. It attaches nodes to the tree, so is slightly slower, but this allows the shapes to be [visualized](#visualizing-collision).\n* `Full / Game` generates collision for the entire terrain at game start, using node-less shapes. It consumes a lot of memory on large terrains.\n* `Full / Editor` does the above with viewable shapes in the editor.\n* `Disabled` is self explanatory.\n\nSome addons or other activities need collision in the editor, which can be enabled with an `Editor` mode above. You can run the game with any option, but the default is recommended for the best performance. See [set_mode](../api/class_terrain3dcollision.rst#class-terrain3dcollision-property-mode) and [CollisionMode](../api/class_terrain3dcollision.rst#enum-terrain3dcollision-collisionmode).\n\nOnce the desired collision mode is set, to detect ground height with physics, you can use a [ray cast](https://docs.godotengine.org/en/stable/tutorials/physics/ray-casting.html). The colliding object will either be the [Terrain3D](../api/class_terrain3d.rst) node if in a `Game` mode. Or it will be a StaticBody if using an `Editor` mode. In the latter case, the StaticBody is a child of Terrain3D. Below is an example that will handle either scenario.\n\n```gdscript\n\tvar space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state\n\tvar query := PhysicsRayQueryParameters3D.create(position, position + Vector3(0, -500, 0))\n\tquery.exclude = [ self ]\n\tvar result: Dictionary = space_state.intersect_ray(query)\n\tif result:\n\t\tvar node: Node = result[\"collider\"]\n\t\t# Change node to StaticBody parent in `Editor` collision modes\n\t\tif node is StaticBody3D and node.get_parent() is Terrain3D:\n\t\t\tnode = node.get_parent()\n\t\t# Detect Terrain3D with `is`, since printing the node returns `[Wrapped:0]`\n\t\tif node is Terrain3D:\n\t\t\tprint(\"Hit: Terrain3D\") \n\t\telse:\n\t\t\tprint(\"Hit: \", node)\n```\n\nAlternatively, you can use our helper function:\n\n```gdscript\n\tvar result: Dictionary = $Terrain3D.get_raycast_result(position, Vector3(0,-500,0)))\n```\n\nGodot Physics is far from perfect. If you have issues with raycasts or other physics calculations, try switching to Jolt. If you have trouble with a perfectly vertical raycast, try angling it ever so slightly. Finally, consider using an alternate method below.\n\n\n## Raycasting Without Physics\n\nIt is possible to cast a ray from any position and direction to detect the collision point on the terrain without using the physics engine. We have two methods for that: raymarching and \"looking\" at it with the GPU.\n\nSending the source point and ray direction to [Terrain3D.get_intersection()](../api/class_terrain3d.rst#class-terrain3d-method-get-intersection) will return the intersection point. This function has two modes:\n\nIn raymarching mode it iterates over get_height() until an intersection is reached. This only works within regions, and is a bit heavy compared to the other mode.\n\nIn GPU mode, it \"looks\" at the terrain using a camera and the GPU depth texture. This works outside of regions, even on the WorldNoise. However there are caveats. It returns values for the previous frame, so can only used continuously or used with `await`, and no more than once per frame.\n\nBe sure to read the link above to understand all caveats. Review [editor_plugin.gd:_forward_3d_gui_input](https://github.com/TokisanGames/Terrain3D/blob/main/project/addons/terrain_3d/src/editor_plugin.gd) to see an example of using this function to project the mouse position onto the terrain.\n\n\n## Query Height At Any Position\n\nIf you already know the X, Z position, use `get_height()`:\n\n```gdscript\n     var height: float = terrain.data.get_height(global_position)\n```\n\n`NAN` is returned if the position is a hole, or outside of regions.\n\nThis is ideal for one lookup. Use the next option for greater efficiency.\n\n\n### Retreiving The Normal\n\nAfter getting the height, you may also wish to get the normal with `Terrain3DData.get_normal(global_position)`. The normal is a Vector3 that points perpendulcar to the terrain face.\n\n\n## Query Many Heights\n\nIf you wish to look up thousands of heights, it will be faster to retrieve the heightmap Image for the region and query the data directly. \n\nHowever, note that `get_height()` above will [interpolate between vertices](https://github.com/TokisanGames/Terrain3D/blob/5bab86ff311159356dd4d837ea2c340f59d139b6/src/terrain_3d_storage.cpp#L493-L502), while this code will not.\n\n```gdscript\n     var region: Terrain3DRegion = terrain.data.get_regionp(global_position)\n     if region and not region.is_deleted():\n         var img: Image = region.get_height_map()\n         for y in img.get_height():\n              for x in img.get_width():\n                   var height: float = img.get_pixel(x, y).r\n```\n\n----\n\n## Additional Tips\n\n\n### Visualizing Collision\n\nTo see the collision shape, first set `Terrain3D/Collision/Collision Mode` to `Full / Editor` or `Dynamic / Editor`.\n\nTo see it in the editor, in the Godot `Perspective` menu, enable `View Gizmos`. Avoid using the Full option on slow systems.\n\nTo see debug collision in game, in the Godot `Debug` menu, enable `Visible Collision Shapes` and run the scene.\n\n\n### Enemy Collision\n\nThe easy approach is to give every enemy a capsule collision shape, and start creating your level. As you fill your level with terrain, rocks, caves, canyons, and dungeons, you'll quickly learn that this approach is terrible for performance.\n\nYour system will be brought to its knees when you have 5-10 enemies follow the player into a tight area with many faceted collision shapes. The physics server will need to calculate collisions against hundreds of faces in the area for each of the 10 character bodies.\n\nA better alternative is to use physics collision only for the player, and use raycasts for the enemies. This allows you to limit the collision checks to a few per enemy, regardless of how many collision faces there are.\n\nHowever both methods above require collision shapes where the enemy is. What if you want to have enemies stay on the ground when far from the camera, while using a dynamic collision mode?\n\nFor one, you could disable all enemy processing when away from the camera. Do you really need them moving, playing animations, and applying gravity if they aren't even on screen?\n\nIf so, you can have each enemy query `Terrain3DData.get_height()`, either in conjunction with checking height with a raycast or physics, or in place of. It could come right after applying gravity so it will snap back to the surface if it dips below.\n\ne.g.\n```\n    global_position.y -= gravity * p_delta\n    global_position.y = maxf(global_position.y, terrain.data.get_height(global_position))\n```\n\n"
  },
  {
    "path": "doc/docs/contributing.rst",
    "content": "Contributing\n============\n.. include:: ../../CONTRIBUTING.md\n   :parser: myst_parser.sphinx_"
  },
  {
    "path": "doc/docs/controlmap_format.md",
    "content": "Control Map Format\n=====================\n\nThe control map defines how the terrain is textured using unsigned, 32-bit integer data. Godot doesn't fully support integer Image or Texture formats, so we use FORMAT_RF. See further below for details.\n\nWe process each uint32 pixel as a bit field with the following definition, starting with the left most bits:\n\n| Description | Range | # Bits | Bit #s | Encode | Decode\n|-|-|-|-|-|-|\n| Base texture id | 0-31 | 5 | 32-28 | `(x & 0x1F) <<27` | `x >>27 & 0x1F`\n| Overlay texture id | 0-31 | 5 | 27-23 | `(x & 0x1F) <<22` | `x >>22 & 0x1F`\n| Texture blend | 0-255 | 8 | 22-15 | `(x & 0xFF) <<14` | `x >>14 & 0xFF`\n| UV angle | 0-15 | 4 | 14-11 | `(x & 0xF) <<10` | `x >>10 & 0xF`\n| UV scale | 0-7 | 3 | 10-8 | `(x & 0x7) <<7` | `x >>7 & 0x7`\n| ... reserved ... | | | \n| Hole: 0 terrain, 1 hole | 0-1 | 1 | 3 | `(x & 0x1) <<2` | `x >>2 & 0x1`\n| Navigation: 0 no, 1 yes | 0-1 | 1 | 2 | `(x & 0x1) <<1` | `x >>1 & 0x1`\n| Autoshader: 0 manual, 1 auto | 0-1 | 1 | 1 | `x & 0x1` | `x & 0x1`\n\n* The encode/decode formulas work in both C++ or GLSL, though may need a `u` at the end of literals when working with an unsigned integer. e.g. `x >> 14u & 0xFFu`.\n* We use a FORMAT_RF 32-bit float Image or Texture to allocate the memory. Then in C++, we read or write each uint32 pixel directly into the \"float\" memory. The values are meaningless when interpreted as floats. We don't convert the integer values to float, so there is no precision loss. Godot shaders support usamplers so we can interpret the memory directly as uint32, without requiring any conversion.\n* Gamedevs can use the conversion and testing functions found in Terrain3DUtil defined in [C++](https://github.com/TokisanGames/Terrain3D/blob/main/src/terrain_3d_util.h) and [GDScript](../api/class_terrain3dutil.rst).\n* Possible future plans for reserved bits:\n  * 2 bits - Hole and Navigation might be stored elsewhere, freeing up 2 bits\n  * 5 bits - 32 paintable particles\n  * 3 bits - paintable slope array index+\n  * 2 bits - 4 layers (including Hole above, eg water, non-destructive, hole, normal mesh) \n  * 1 bit - future use (maybe added to particles)\n  * 2 bits - Texture blend weight array index+\n  * Array for all + marked 3-bit indices above: `{ 0.0f, .125f, .25f, .334f, .5f, .667f, .8f, 1.0f }`;\n\nThe 3 bit indices above are used to select a value from an 8-index array of values between 0-1, such as `{ 0.0f, .125f, .25f, .334f, .5f, .667f, .8f, 1.0f };` This allows us to store full range 0-1 values that would normally require 8 bits (256 values) in only 3 bits (8 values), since the fine gradations are not important. This idea came from the Witcher 3 presentation.\n\n"
  },
  {
    "path": "doc/docs/data_format.md",
    "content": "Data Format Changelog\n==========================\nThe Terrain3DRegion resource files have their own version, independent of the version of Terrain3D, shown at the top of the inspector. Generally this is updated to the latest format upon saving. This information is more relevant for developers, while the [upgrade path](installation.md#upgrade-path) is relevant for all users.\n\nThe data format version is found as [Terrain3DData.version](../api/class_terrain3ddata.rst#class-terrain3ddata-property-version) and is visible in the inspector under `Version` after double clicking a data file.\n\n| Version | Description |\n|---------|-------------------|\n| 0.93 | The monolithic storage file was split into one file per region [#374](https://github.com/TokisanGames/Terrain3D/pull/374), [#476](https://github.com/TokisanGames/Terrain3D/pull/476)\n| 0.92 | Add `Terrain3DInstancer` data [#340](https://github.com/TokisanGames/Terrain3D/pull/340)\n| 0.842 | Control map changed from FORMAT_RGB to 32-bit packed integer (encoded in FORMAT_RF) [#234](https://github.com/TokisanGames/Terrain3D/pull/234/)\n| 0.841 | Colormap painted/stored as srgb and converted to linear in the shader (prev painted/stored as linear). [64dc3e4](https://github.com/TokisanGames/Terrain3D/commit/64dc3e4b5e71c11ac3f2cd4fedf9aeb7d235f45c)\n| 0.84 | Separated material processing from Storage as a `Terrain3DMaterial` resource. [#224](https://github.com/TokisanGames/Terrain3D/pull/224/)\n| 0.83 | Separated Surfaces (textures) from Storage as a `Terrain3DTextureList` resource. [#188](https://github.com/TokisanGames/Terrain3D/pull/188/)\n| 0.8 | Initial version\n"
  },
  {
    "path": "doc/docs/displacement.md",
    "content": "Displacement\n====================\n\nFor a more detailed terrain mesh, you can enable texture height based displacement. This will subdivide the clipmap mesh, greatly increasing the vertex density around the camera. This feature is compatible with all platforms, including mobile and web, however it does come with a potentially significant performance cost.\n\n\n```{image} images/displacement_example.jpg\n:target: ../_images/displacement_example.jpg\n```\n\n```{image} images/displacement_wireframe.jpg\n:target: ../_images/displacement_wireframe.jpg\n```\n\n## Enabling Displacement\n\nYou can enable displacement in the `Terrain Mesh` group, by increasing `Tessellation Level` from 0 (disabled, default) up to 6. This will subdivide the terrain around the camera. Generally, it is recommended to keep `Tessellation Level` low, `Mesh Size` as high as performance allows, and `Vertex Spacing` at `1.0` for the best results. All three parameters will have a noticable effect on the subdivision density.\n\nWhen `Tessellation Level` is greater than 0, the `Displacement` subgroup appears with global options. Enable displacement, then setup your textures, then come back to these global options.\n\nTo setup your texture assets, you need a clear view of the terrain and an understanding of where the collision surface is. Explore any of these options:\n* Disable instances by setting `Rendering/Instancer Mode` to `Disabled`.\n* Hide your nodes in the scene that obscure the terrain surface.\n* Change `Collision/Collision Mode` to editor collision, and enable view gizmos, which will show the collision mesh for direct comparison. \n* Enable the `Debug Views/Displacement Buffer`. Black areas match collision exactly. Red shows depressions into the terrain, and Green shows extrusions above the terrain.\n\n```{image} images/displacement_buffer.jpg\n:target: ../_images/displacement_buffer.jpg\n```\n\n\n## Configuring Textures\n\nIn order to use displacement, you must have textures packed with height textures as described in [Texture Prep](texture_prep.md).\n\nWhen you configure your `Terrain3DTextureAsset`, you'll find `Displacement Offset` and `Displacement Scale` options.\n\nFirst, adjust `Displacement Scale` until the dimensions of features in the textures are correctly proportioned. Eg. branches are not overly elongated, and pebbles appear round, rather than spiky.\n\nNext, adjust `Normal Depth` so that any displaced areas are lit correctly. The normal map normals are used for the higher detail geometry.\n\nFinally, adjust `Displacement Offset` if necessary to minimize the amount of colision mismatch. Most of the time, the default `0.0` is adequate. As an example, a cobblestone texture might have the tops of the cobbles protrude above the terrain, but a small negative offset can align the tops of the cobbles with collision.\n\n\n## Displacement Global Settings\n\nOnce the texture assets are configured, the global settings can be adjusted in `Terrain Mesh/Displacement`. \n\nUse `Displacement Scale` for a global multiplier on all textures. This is the maximum distance that 2 adjacent verticies can be vertically seperated by. Setting this 1.0 would mean a maximum of + 0.5m, and -0.5m deviation from the collision mesh.\n\n`Displacement Sharpness` adjusts the transition between textures. When set at `1.0`, the blending of displacment between textures will match the aldebo and normal blend sharpness exactly. Lower values will have a softer transition, avoiding harsh shapes, without compromising the albedo and normal blend sharpness. If set at or very near to `0.0`, it is possible that more displaced textures can affect less displaced textures at low blend values even if not visible.\n\n\n## Displacement Buffer\n\nTo calculate displacement, an atlas texture buffer is created via a viewport and canvas_item shader. This buffer updates only when the terrain mesh moves to save on computational cost.\n\nYou can access the buffer shader by enabling `Terrain Mesh/Displacement/Buffer Shader Override Enabled`, which will generate the default for you. Though primarily for development use, it could be modified to read from a persistent buffer for real-time effects like footsteps in sand or mud, or to change how the blending of height textures is handled.\n\nCustom uniforms can be added within the `group_uniforms displacement` block. These will show up in the `Material/Displacement` group.\n"
  },
  {
    "path": "doc/docs/double_precision.md",
    "content": "Double Precision\n=================\n\nWhen the player and camera move 10s to 100s of thousands, or millions of units away from the origin using a single precision float engine, things start to break down. Movements, positions, and rendering starts to become jittery or corrupted as the interval between valid values gets larger and larger.\n\nBuilding Terrain3D with double precision (aka 64-bit) floats allows high precision even at large numbers, but there are some caveats.\n\nFor a more detailed explanation, see [Large World Coordinates](https://docs.godotengine.org/en/stable/tutorials/physics/large_world_coordinates.html) in the Godot documentation.\n\n\n## Caveats\n\n* This feature is experimental and has had only one user give a positive report so far.\n* There are many caveats listed in the link above. You should read them all before beginning this process.\n* You must build Godot and Terrain3D from source.\n* Terrain3D currently supports a maximum world size of 65.5x65.5km. Although with `vertex_spacing`, you can expand this up to 100x. You can also have Terrain3D regions around the origin, then have your own meshes or a shader generated terrain outside of that maximum world space.\n* Shaders do not support double precision. Clayjohn wrote an article demonstrating how to [Emulate Double Precision](https://godotengine.org/article/emulating-double-precision-gpu-render-large-worlds/) in shaders. He wrote that the camera and model transform matrices needed to be emulated to support double precision. This is now done automatically in the engine when building it with double precision. There may be other cases where shaders will need this emulation.\n\n\n## Setup\n\nTo get Terrain3D and Godot working with double precision floats, you must:\n\n1. [Build Godot from source](https://docs.godotengine.org/en/latest/engine_details/development/compiling/index.html) with `scons precision=double` along with your other parameters like target, platform, etc.\n2. Generate custom godot-cpp bindings using this new executable. This will generate a JSON file you will use in the next step.\n\t- `godot --dump-extension-api`, which gives you an extension_api.json\n\t- `scons custom_api_file=path_to/extension_api.json`\n3. [Build Terrain3D from source](building_from_source.md) (which includes godot-cpp) using `scons precision=double custom_api_file=path_to/extension_api.json`\n\nAfter that, you can run the double version of Godot with the double version of Terrain3D.\n\n\n## Further Reading\n\nSee [Support Doubles #30](https://github.com/TokisanGames/Terrain3D/issues/30) for our Issue that documents implementing support for doubles in Terrain3D."
  },
  {
    "path": "doc/docs/games.md",
    "content": "Games Using Terrain3D\n=======================\n\nTerrain3D is being used in the following games. To add yours, submit it to the #game-dev channel on [our discord server](https://tokisan.com/discord).\n\n| Game | Studio | Description |\n|------|--------|-------------|\n| [Out of the Ashes](https://store.steampowered.com/app/2296950/Out_of_the_Ashes/) | [Tokisan Games](https://x.com/TokisanGames) | Story driven medieval adventure\n| [Gunship Origins](https://store.steampowered.com/app/4055780/Gunship_Origins/) | [Jammin Games](https://jammin.games/) | Hellicopter combat\n| [Black Pellet](https://www.kickstarter.com/projects/raiseledwards/black-pellet) | [BlackPelletGame](https://x.com/BlackPelletGame) | Claymation, Western, TPS, Open world, Action-adventure\n| [Memora Wanderer](https://store.steampowered.com/app/2937690/Memora_Wanderer/) | [Maytch](https://x.com/Maytch) | Cute nostalgic RPG\n| [No Gasoline](https://store.steampowered.com/app/2835350/No_Gasoline/) | [Mount Retro](https://x.com/mountretro) | Co-Op/Solo, Adventure-Simulation-Puzzle\n| [RotorSim](https://store.steampowered.com/app/3376070/RotorSim_Helicopter_Simulator/) | [Immaculate Lift](https://immaculate-lift-studio.github.io/studio-site/) | Retro helicopter simulation\n| [B&E Ski](https://www.youtube.com/watch?v=pD8Ea3utz9o) | [Penguin Milk](https://bande.ski/) | Skiing game\n| [Sacred Forest](https://store.steampowered.com/app/2864350/Sacred_Forest/) | [Blekoh](https://www.youtube.com/@sacredforestgame) | Open world 3D pixel art RPG\n| [Pest Apocalypse](https://store.steampowered.com/app/2506810/Pest_Apocalypse/) | [Kikimora Games](https://x.com/KikimoraGames) | Post-apocalyptic pizza delivery\n| [Forg](https://store.steampowered.com/app/2807130/Forg/) | [Crow Games](https://www.youtube.com/@crowgamesdev) | FPS tower defense\n| [open-fpsz](https://gitlab.com/open-fpsz/open-fpsz) | [anyreso](https://mastodon.gamedev.place/@anyreso) | Open-source, Tribes-like FPS multiplayer shooter\n| [Element](https://devanew.itch.io/element) | [Luke Aaron](https://www.youtube.com/watch?v=b18jDnY1YS4) | Gamejam FPS tactial shooter\n| [Castaway Cove](https://boolburg.itch.io/castaway-cove) | [Boolburg](https://boolburg.itch.io/) | Tropical island exploration\n\n## Demos\n\n| Game | Studio | Description |\n|------|--------|-------------|\n| [Island Demo](https://github.com/OverfortGames/LandscapeDemo) | [Overfort Games](https://x.com/OverfortGames) | Island demo w/ source\n| [Jungle Demo](https://wrobot.itch.io/jungledemo) | [WrobotGames](https://x.com/wrobot123) | Godot rendering demo in a jungle\n"
  },
  {
    "path": "doc/docs/generating_csharp_bindings.md",
    "content": "Generating C# Bindings\n==========================\n\nC# Bindings need to be rebuilt after building Terrain3D. At this point in time, C# and GDScript don't share their ClassDB. You can call the functions through reflection, but providing C# Bindings makes coding in C# much easier. As of Terrain3D 1.1, these bindings are generated and included in release builds by a maintainer.\n\nHow To Generate Bindings\n------------------------\n\n1. Compile Terrain3D as shown in [Building from Source](building_from_source.md).\n1. Clone the [CSharp-Wrapper-Generator-for-GDExtension](https://github.com/Delsin-Yu/CSharp-Wrapper-Generator-for-GDExtension) repo.\n1. Load the CSharp Wrapper Generator project in the .NET version of Godot.\n1. Build the C# project.\n1. Ensure the plugin is enabled in project settings and restart.\n1. You should see a Wrapper Generator panel in a side panel. Set up the namespace `TokisanGames`, save path `addons/terrain_3d/csharp`, and indentation `tabs`.\n1. Add your built `addons/terrain_3d/` folder to this project. I made a link on my filesystem between my Terrain3D repo and this one so I could read from new builds, and generate C# without moving files.\n1. Generate, and look for errors or warnings to address.\n1. Move the generated C# files to `project/addons/terrain_3d/csharp` in your project.\n1. In your project, build the C# project.\n"
  },
  {
    "path": "doc/docs/getting_help.md",
    "content": "Getting Help\n===================\n\n**Video Tutorials**: Make sure to watch the [tutorial videos](tutorial_videos.md) which show proper installation, setup, and how to use most of the tools.\n\n**Read the docs**: Many questions have already been addressed in this documentation, especially in [Troubleshooting](troubleshooting.md) and [Setting Up Textures](texture_prep.md).\n\n**Discord**: For questions or technical issues, join the [Tokisan discord server](https://tokisan.com/discord) and ask in the `#terrain-help` channel.\n\n**Issues**: For technical issues or bug reports, search through the [issues](https://github.com/TokisanGames/Terrain3D/issues) to ensure it hasn't already been reported, then create a new one. Use discord for questions.\n\n## Help Us Help You\n\nTerrain3D has been stable for thousands of users for more than a year, so we know it works fine when setup properly. If you're having trouble, there's a 90% chance it's not installed or setup properly. We can help you get setup, but only if you provide adequate information.\n\nAt a minimum we need the following:\n\n* A concise description of the problem, what you expect to happen, and what is happening.\n* The exact version of Godot and of Terrain3D you're using.\n    * Or, click your Terrain3D node and provide a full screenshot, which will include that and other information.\n* Your Operating system and GPU.\n* Confirmation of, or a screenshot of your Project Settings / Plugins screen to show that Terrain3D is enabled, and what other plugins you have.\n* A screenshot or copy of all text in your [console](troubleshooting.md#using-the-console), beginning with the initial Godot Engine startup message.\n\nIf you have trouble with textures, we also need:\n* Double click a texture file and show the file format and size as reported by the Godot inspector. Select either the first texture, or a relevant problem texture.\n* A shot of your import tab with that same texture selected.\n\nFinally, be ready to provide [debugging logs](troubleshooting.md#debug-logs), and what steps you tried, and the results of those steps.\n\n\n"
  },
  {
    "path": "doc/docs/heightmaps.md",
    "content": "Heightmaps\n===========\n\nTerrain3D can be used with pre-made heightmaps. They can be found online, created from heightmap generators, or downloaded from real world data.\n\nOnce you have your heightmap source, ensure it is 16 or 32-bit, [scaled properly](#scaling), and converted to `exr` or `r16`. Then read [Importing Data](import_export.md) to learn how to import it. What is not covered here is how to use Photoshop, Krita, or Gimp, but you can find tutorials on YouTube.\n\n**Table of Contents**\n* [Pre-made Heightmaps](#pre-made-heightmaps)\n* [Heightmap Generators](#heightmap-generators)\n* [Real World Data](#real-world-data)\n* [Converting Data](#converting-data)\n* [Scaling](#scaling)\n\n\n## Pre-made Heightmaps\n\nA simple web search for `download terrain heightmaps` will allow you to find heightmaps in a few minutes. Some pages might say `free heightmaps for Unity` or `Unreal Engine`. All of these will work. You need to understand what format they are giving you, ensure it is at least 16 or 32-bit, and [convert](#converting-data) that to `exr` for import. \n\nThere are also asset packs for game engines like Unreal Engine or Unity that come with premade heightmaps. Both engines provide the ability to export these to a format you can use elsewhere. Be sure to verify the license of the content you're using, but generally anything you've bought can be modified and used elsewhere.\n\n\n## Heightmap Generators\n\nYou can use software to generate heightmaps, and in some cases, texture layout maps (aka splat maps, index maps, or our word: control maps). You must look through the documentation of your program to determine what formats and bit depth they export, and convert if necessary.\n\n### Free or Open Source\n\nSearch github for `heightmap generator` or `noise generator` to see the current projects. Anything that can produce *and* export a 16-bit grayscale image will work. Here are some examples.\n\n* [wgen](https://github.com/jice-nospam/wgen) - MIT License. Written in Rust. Exports to 16-bit `png` or `exr`.\n\n* [HTerrain](https://github.com/Zylann/godot_heightmap_plugin) - MIT License. A GDScript based terrain system for Godot. It includes a terrain generator that can save a heightmap as a native Godot .res file, or you can export to `exr`. Both will work for Terrain3D.\n\n* In the future, Terrain3D will include a generator. Follow [Issue 101](https://github.com/TokisanGames/Terrain3D/issues/101) for progress.\n\n\n### Commercial Software\n\nSome of these tools are free for non-commercial use. Generally they generate both heightmaps and control maps. Currently you can only import the heightmaps into Terrain3D.\n\nImporting a control map is possible, but requires you or a contributor to 1) research and document the proprietary control map format available in each tool, 2) create a conversion script for our importer. As programming tasks go, this is pretty easy to do. See [issue 135](https://github.com/TokisanGames/Terrain3D/issues/135) if you'd like to help with one or both.\n\nYou could export a baked image of the textures, like a satellite image and import that on our color map, and enable `Debug Views / Colormap`. This could aid you in manual texturing, or left for display purposes.\n\n* [Gaea](https://quadspinner.com/)\n\n* [World Machine](https://www.world-machine.com/)\n\n* [World Creator](https://www.world-creator.com/)\n\n* [Terragen](https://planetside.co.uk/)\n\n* [Houdini](https://www.sidefx.com/products/houdini/) - General procedural modeling software that includes terrain generation.\n\n* [Instant Terrain](https://www.wysilab.com/) - Terrain generation plugin for UE or Houdini.\n\n \n## Real World Data\n\nGeographic Information System (GIS) professionals use real world height data from satellite and aircraft surveys. Much of it is available for free from government websites. This data is often referred to as a Digital Elevation Model (DEM), or Surface (DSM) or Terrain (DTM). A DSM might contain buildings, DTM only the ground, while DEM is the umbrella term. This data may come in a variety of formats.\n\nThere are many, many sources available online. Here are a few examples:\n\n* [USGS](https://www.usgs.gov/the-national-map-data-delivery/gis-data-download) provides several tools to download DEMs and Tiffs for the US down to 1m resolution.\n* [European Space Agency Copernicus program](https://ec.europa.eu/eurostat/web/gisco/geodata/digital-elevation-model/copernicus) provides DEMs for the whole world.\n* [EU-DEM](https://ec.europa.eu/eurostat/web/gisco/geodata/digital-elevation-model/eu-dem)\n* [http://www.terrainmap.com/rm39.html](http://www.terrainmap.com/rm39.html) - A list of other websites with GIS data.\n\nSome GIS data might include sattelite imagery. You can import that into our color map, and enable `Debug Views / Colormap`.\n\nThere are some websites that allow you to download a heightmap from real world data, however often they are unusable 8-bit heightmaps. The sites below are some examples that might have higher quality, usable data. But government GIS survey data is probably superior.\n\n* [https://manticorp.github.io/unrealheightmap/](https://manticorp.github.io/unrealheightmap/) - exports 16-bit `png`\n* [https://touchterrain.geol.iastate.edu/](https://touchterrain.geol.iastate.edu/) - exports GeoTiff\n\n\n## Converting Data\n\nConverting data is not trivial. Understanding how your data is stored and how to convert it to another format is an essential skill for you to learn. I promise that any time spent learning this will be useful for your entire life working with computers.\n\nUltimately we want the data stored as an `exr` or `r16` for import into Terrain3D. See [supported formats](import_export.md#supported-formats) for more details. \n\nHow we get there depends on your source data and tools available. Photoshop, Krita, or Gimp are the most common tools, but you might find other useful tools or even write your own scripts to convert data. How to use these tools is out of scope for this documentation. You might need to work through multiple tools to get the right format and workflow. \n\n\n### File Format\n\nHopefully your source data is a 16-bit `png` or Tiff/GeoTiff file, which may allow you to convert it directly just by saving the file as an `exr` or `r16`. GeoTiff is a `tiff` file with geospatial metadata embedded.\n\nSome Tiff formats are newer and not supported by all image editing apps, and may require experimentation with multiple tools. \n\nYou might be able to convert it with a python script such as the following, which produces an `r16` file (named raw):\n```\n\tfrom PIL import Image\n\timport numpy\n\n\tim = Image.open('image.tif')\n\timarray = numpy.array(im)\n\timarray.astype('int16').tofile(\"image.raw\")\n```\n\nIf your GIS source data is in a non-image format, you can try converting it with [VTBuilder](http://vterrain.org/). Drag the file into the window, and if it loads, use `Elevation/Export To` and save it as 16-bit `png` or GeoTiff.\n\n\n### Normalized Data\n\nOnce the source file is opened in an image editing program, the map might show only an outline of your landscape, with the land in solid white or red, and the ocean in black. You can use the dropper to look at the values, which should show expected values for heights in meters. This is a map with real world values. You can use tone mapping to view the image as a gradient heightmap, however you don't want to save it this way.\n\nIf the file shows a smooth gradient heightmap, then it's normalized, with all values between 0 and 1. Ensure the image is 16 or 32-bit. 8-bit will give you an ugly terraced terrain and will require a lot of smoothing to be usable.\n\nEither normalized values or real values are fine, as long as you understand how your data is formatted and that you must [scale](#scaling) normalized values on import. That means knowing how much to scale by, and that info should have been provided with the data source.\n\n\n### Conversion Examples\n\n* **Ex 1:** I exported a 16-bit `png` from a commercial tool. I opened the file in Photoshop and exported it as `exr`. Or I could have opened it in Krita and exported as `exr` or `r16`.\n\n* **Ex 2:** I downloaded a Digital Elevation Model (DEM) from a govt website and received GeoTiff files. \n  * Though I've opened other GeoTiff files in Photoshop before, it couldn't read these. Krita opened them but only displayed black. \n  * I was able open them in Gimp, and reviewing the pixel data showed the ocean at `-inf`, and the land at real world values. I exported as `exr` and imported to Terrain3D, but the land was extremely high. \n  * I was able to open the `exr` in Photoshop and compare with my other known working `exr` files. The landscape shape appeared, but the height values were extremely large. I experimented with various color management and bit depth conversions within Gimp to see if I could get a correct looking `exr` in Photoshop, but couldn't. \n  * In Gimp, I exported as `tiff`, which gave me a file I could open in Photoshop and gave me the heights I was looking for, except the ocean still showed `-inf`. This was a problem as it produced holes in Terrain3D, but not our kind of holes that can be filled in. \n  * In Gimp I found `Colors / RGBClip` in the menu and used it to limit `-inf` to 0, while retaining height values above 1.\n  * Then I exported the Tiff to Photoshop, confirmed the values looked good, exported that to `exr` for import and finally got the desired result.\n\n\n## Scaling\n\nTerrain3D generally expects a ratio of 1px = 1m lateral space with real world heights, and provides options for manipulating these. In order to get expected results on import, there are two important characteristics you need to understand about your data:\n\n1. **Vertical Scale**. If your data is not normalized, your data has real values and vertical scale should remain 1 on import. Most likely 0 is defined as sea level. This is how Terrain3D stores data internally. If your data is normalized with values 0-1, you can multiply by a vertical scale in the import tool to set the peak height according to what your source defines. You can also apply an offset to adjust how your data aligns with 0.\n\n2. **Vertical Aspect Ratio (VAR)**. This is often called `resolution` in GIS and terrain documentation, but there is a crucial distinction from image resolution. Your data has an inherent ratio of lateral space to height, or a 3D aspect ratio of XZ to Y (up). Terrain3D expects 1px = 1m with real world heights, giving a VAR of 1m lateral to 1m vertical. If your data is 1px = 10m with real world heights, the VAR is 10:1. That means your data is 10x wider than it is high and you need to either adjust the data or adjust Terrain3D settings to account for this difference to get an accurate result. This means scaling the heightmap image in Photoshop before import, scaling the heights on import, or adjusting [Mesh / Vertex Spacing](../api/class_terrain3d.rst#class-terrain3d-property-vertex-spacing) on or after import. Which one you choose depends on your needs, as described below.\n\n\n### Scaling Examples\n\nHere are some examples of adjusting settings to account for vertical scale and VAR. These examples will be more meaningful if you've read the [import doc](import_export.md) and have imported at least one heightmap first.\n\n* **Ex 1**: I downloaded GIS data that is a 20m resolution DEM and received a GeoTiff. Upon loading it in Photoshop, the dropper tool revealed that sea level is at 0 and other points on land have real world values in meters. This means the vertical scale is built in to the data, and every 1px on the map represents 20m of lateral space, giving us a VAR of 20:1. I want to work with this map in Terrain3D at the default resolution of 1px = 1m.\n  * **Option 1**: In Photoshop I crop the image down to a small 500x500px area that I want to import, then scale the image 20x to 10k x 10k. \n\t* Given the large image size, in Terrain3D I increase the region size to 512 (10k / 32 = 312.5 minimum). (Read [why 32](introduction.md#regions)). \n\t* Next I import with a scale of 1, offset of 0, and leave vertex_spacing at 1. \n\t* I now have an accurate representation of the data within Terrain3D that I can sculpt and refine with the standard vertex spacing. This consumes the VRAM required for a 10k x 10k heightmap. A lot of that is wasted if I do nothing else, since I duplicated the pixels 20x without adding any new data. However this process has given me an accurate base to start sculpting and add my own detail at a reasonable resolution. This is likely my preferred path for real world data.\n  * **Option 2**: I import the original image with scale set to 0.05 (1/20th). Now in Terrain3D I have a visually accurate representation of the data, given that the VAR is correct. However if I were to measure the heights, they would be 1/20th their real world values. 1px = 20m and height = 1/20th. There's no wasted VRAM due to duplication of pixels. This is a good path if I only want to visualize the data.\n  * **Option 3**: I import the image with a scale of 1. In Terrain3D, it will look very spiky. I increase vertex_spacing to 20. Like the option above, there's no wasted VRAM. This gives I accurate heights, but the world might look a bit low poly.\n\n* **Ex 2**: I exported a heightmap from a commercial tool and received 4 tiled 1024x1024 heightmaps. Upon opening them in Photoshop, they look like gradient heightmaps and the dropper tool confirms the data is normalized with values between 0-1. I was informed the lowest trough is -500m, highest peak 1500m, sea level is 0, and the resolution is 4px to 1m.\n  * **Option 1**: In Photoshop, I combine the four tiles into one 4k image. \n\t* Then I scale the whole image by 4 to 16k. \n\t* On import to Terrain3D, I change region size to 512, set offset to -500m, and scale to 2000m (1500m - -500m). I change vertex_spacing to 0.25. \n\t* I now have an accurate representation of the data, and am consuming the VRAM for a 16k x 16k map for a high resolution 4k x 4k world. 0.25m is probably overkill and the increased vertex density does have a performance cost.\n  * **Option 2**: After combining and scaling the images into one 16k image, I scale the resolution down to 4k. This erases data between each meter. \n\t* On import, I repeat the region size, offset, and scale as above, but leave vertex_spacing at 1. \n\t* I still have a 4k x 4k world, with an accurate scale and VAR, but now my terrain resolution is a more optimal 1m. I could also split the difference with a 0.5 or 0.667 vertex_spacing.\n\nRead [Importing Data](import_export.md) to learn how to import your `exr`.\n"
  },
  {
    "path": "doc/docs/import_export.md",
    "content": "Importing & Exporting Data\n===========================\n\nThis page describes how to get data into or out of our tool. You should read [Heightmaps](heightmaps.md) to learn about preparing files from various data sources.\n\nCurrently importing and exporting is possible via code or our import tool. We will [make a UI](https://github.com/TokisanGames/Terrain3D/issues/81) eventually. In the meantime, we have written a script that uses the Godot Inspector as a makeshift UI. You can use it to make a data file for your other scenes.\n\n**Table of Contents**\n* [Supported Formats](#supported-formats)\n* [Importing Data](#importing-data)\n* [Exporting Data](#exporting-data)\n* [Exporting GLTF](#exporting-gltf)\n\n\n## Supported Formats\n\nTerrain3D has three map types you can import or export: [Height](../api/class_terrain3dregion.rst#class-terrain3dregion-property-height-map), [Control](../api/class_terrain3dregion.rst#class-terrain3dregion-property-control-map), and [Color](../api/class_terrain3dregion.rst#class-terrain3dregion-property-color-map).\n\nWe can import any supported image format that Godot can read, however we have recomendations below for specific formats to use for the different maps.\n\nGodot can read these file formats:\n* [Godot supported Image file formats](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_images.html#supported-image-formats): `bmp`, `dds`, `exr`, `hdr`, `jpg`, `jpeg`, `png` 8-bit, `tga`, `svg`, `webp`\n* [Godot resource files](https://docs.godotengine.org/en/stable/classes/class_image.html#enum-image-format): Any data format listed at the link stored as a `tres` or `res`.\n\nGodot can write these file formats:\n* [Godot supported Image save functions](https://docs.godotengine.org/en/stable/classes/class_image.html#class-image-method-save-exr): `exr`, `png`, `jpg`, `webp`\n* `res`: Godot binary resource file with `ResourceSaver::FLAG_COMPRESS` enabled. The format contained inside is defined in our API linked at the top of this section for each map type.\n* `tres`: Godot text resource file. Not recommended.\n\n\n### Height Map Recommendations\n\nUse `exr` or `r16/raw` for import / export.\n\n* `exr`: Values should be real heights, not normalized (0.0 - 1.0). RGB, not greyscale. 16 or 32-bit float, no transparency. Older versions of Photoshop can use [exr-io](https://www.exr-io.com/).\n* `r16`: Values are normalized to 0 - 65,535 (maximum 16-bit unsigned int value). Can be read/written by Krita. Min/max heights and image dimensions are not stored in the file, so you must keep track of them elsewhere, such as in the filename.\n* `raw`: This is not a format specification! It just means the file contains a dump of values. But what format are the values? They could be 8, 16, or 32-bit, signed or unsigned ints or floats, little or big endian byte order (aka Windows or macOS, Intel or Arm/Motorola). In order to read this file you need to know all of these, *and* the dimensions. `Photoshop Raw` only supports 8/16/32-bit float, little or big endian. **Terrain3D interprets a .raw extension as r16**, as does Unity, Unreal Engine, and various commercial software.\n\nOther notes:\n* `png`: Godot only supports 8-bit PNGs. This works fine for the colormap, but not for heightmaps. If you have a 16-bit `png`, convert it to `exr` with an image editor.\n* Only use 16 or 32-bit height data. If your data is 8-bit, it will look terraced and require a lot of smoothing to be useable. You could convert the image to 16-bit and blur it in Photoshop.\n* [Zylann's HTerrain](https://github.com/Zylann/godot_heightmap_plugin/) stores height data in a `res` file which we can import directly. No need to export it first, though his tool also exports `exr` and `r16`.\n\n \n### Control Map Recommendations\n\nOur control maps use a [proprietary format](controlmap_format.md). We currently only import our own format. Use `exr` to export and reimport only from this tool. This is only for transferring the data to another Terrain3D data file.\n\n\n### Color Map Recommendations\n\n* Any regular color format is fine. \n* `png` or `webp` are recommended as they are lossless, unlike `jpg`.\n* The alpha channel is interpretted as a [roughness modifier](../api/class_terrain3ddata.rst#class-terrain3ddata-property-color-maps) for wetness. So if you wish to edit the color map in an external program, you may need to disable or separate the alpha channel first.\n\n\n## Importing Data\n\n1. Open `addons/terrain_3d/tools/importer.tscn`.\n\n2. Click Importer in the scene tree.\n\n```{image} images/io_importer.png\n:target: ../_images/io_importer.png\n```\n\n3. If you're importing a large file, you need to adjust `Terrain3D / Regions / Region Size`. You only get [1024 regions (32x32)](introduction.md#regions) so you need a region size greater than `image width / 32`. E.g., importing 10k x 10k means 10,000/32 = 312.5, so a minimum region size of 512 is required.\n\n4. In the inspector, select a file for height, control, and/or color maps. See [formats](#supported-formats) above. File type is determined by extension.\n\n5. Specify the `import_position` of where in the world you want to import. Values are rounded to the nearest `region_size` (defaults to 256). So a location of (-2000, 1000) will be imported at (-2048, 1024).\n\n     Notes:\n     * You can import multiple times into the greater world map by specifying different positions. So you could import multiple maps as separate islands or combined regions.\n     * It will slice and pad odd sized images into region sized chunks (default is 256x256). e.g. You could import a 4k x 2k, several 1k x 1ks, and a 5123 x 3769 and position them so they are adjacent.\n     * You can also reimport to the same location to overwrite anything there using individual maps or a complete set of height, control, and/or color.\n\n6. Specify any desired `height_offset` or `import_scale`. The scale gets applied first. (eg. 100, -100 would scale the terrain by 100, then lower the whole terrain by 100).\n\n     * We store full range values. If you sculpt a hill to a height of 50, that's what goes into the data file. Your heightmap values (esp w/ `exr`) may be normalized to the range of 0-1. If you import and the terrain is still flat, try scaling the height up by 300-500.\n     * See [Scaling](heightmaps.md#scale) for more details on proper scaling and offset.\n\n7. If you have a RAW or R16 file (same thing), it should have an extension of `r16` or `raw`. You can specify the height range and dimensions next. These are not stored in the file so you must know them. I prefer to place them in the filename.\n\n8. Click `Run Import` and wait 10-30 seconds. Look at the console for activity or errors. If the `Terrain3D.debug_level` is set to `debug`, you'll also see progress.\n\n9. When you are happy with the import, scroll down in the inspector until you see `Terrain3D / Data Directory`. Specify an empty directory and save.\n\n\n```{image} images/io_data_directory.png\n:target: ../_images/io_data_directory.png\n```\n\nYou can now load this directory into Terrain3D in any of your scenes. You can also load an existing data directory in the importer, then import more data into it and save it again.\n\n\n## Exporting Data\n\n1. Open `addons/terrain_3d/tools/importer.tscn`.\n\n2. Click Importer in the scene tree.\n\n3. Scroll the inspector down to `Terrain3D / Data Directory`, and load the directory you wish to export from.\n\n```{image} images/io_data_directory.png\n:target: ../_images/io_data_directory.png\n```\n\n4. Scroll the inspector to `Export File`.\n\n```{image} images/io_exporter.png\n:target: ../_images/io_exporter.png\n```\n\n5. Select the type of map you wish to extract: Height (32-bit floats), Color (rgba), Control (proprietary).\n\n6. Specify the full path and file name to save. The file type is determined based upon the extension. You can enter any location on your hard drive, or preface the file name with `res://` to save it in your Godot project folder. See [formats](#supported-formats) for recommendations.\n\n7. Click `Run Export` and wait. 10-30s is normal. Look at your file system or the console for status.\n\nNotes:\n\n* The exporter takes the smallest rectangle that will fit around all active regions in the world and export that as an image. So, if you have a 1k x 1k island in the NW corner, and a 2k x 3k island in the center, with a 1k strait between them, the resulting export image will be something like 4k x 5k. You'll need to specify the location (rounded to `region_size`) when reimporting to have a perfect round trip.\n\n* The exporter tool does not offer region by region export, but there is an API where you can retrieve any given region, then you can use `Image` to save it externally yourself.\n\n* Upon export, the console reports the image size and minimum/maximum heights, which is necessary for r16 heightmap exports.\n\n## Exporting GLTF\n\nYou can export the terrain as a mesh, without texturing.\n\n1. Create a new empty scene and add a Terrain3D node.\n2. Load your terrain `Data Directory`.\n3. Select the Terrain3D node.\n4. At the top of the viewport, select `Terrain3D / Bake ArrayMesh...` and bake at the desired LOD.\n5. Delete the Terrain3D and other nodes, leaving only the generated MeshInstance3D node in this scene.\n6. In the Godot menu, select `Scene / Export As... / GLTF 2.0 Scene...`.\n\nYou can then use this mesh in Blender or other tools. It's fine for reference, but isn't an optimal mesh as there is a vertex every square meter. You can decimate or remesh it if you need a more optimal version.\n"
  },
  {
    "path": "doc/docs/installation.md",
    "content": "Installation & Upgrades\n==========================\n\n**Table of Contents**\n* [Requirements](#requirements)\n* [Installing Terrain3D](#installing-terrain3d)\n* [Upgrading Terrain3D](#upgrading-terrain3d)\n\n## Requirements\n* Terrain3D 1.0.1 supports Godot 4.4+. Use 1.0.0 for 4.3.\n* Supports Windows, Linux, and [macOS (read more)](platforms.md#macos).\n* Some platforms and renderers are experimental or unsupported. See [Supported Platforms](platforms.md).\n\n## Installing Terrain3D\n\n### From The Asset Library\nTerrain3D is [listed in the Asset Library](https://godotengine.org/asset-library/asset/3134), so you can download it directly within Godot.\n1. Run Godot using the console executable so you can see error messages.\n2. Setup a new project within Godot.\n3. Click `AssetLib` at the top of the Godot window.\n4. Search for `Terrain3D`, and click the entry from `TokisanGames` shown for your Godot version.\n5. Click `Download`.\n6. Godot will ask you to install files into `addons` and `demo`. Demo is optional, but highly recommended for troubleshooting. Click `Install`.\n7. Restart when Godot prompts.\n8. In `Project / Project Settings / Plugins`, ensure that Terrain3D is enabled.\n9. Select `Project / Reload Current Project` to restart once more.\n10. Open `demo/Demo.tscn`. You should see a terrain. Run the scene by pressing `F6`.\n\nIf the demo isn't working for you, watch the [tutorial videos](tutorial_videos.md) and see [Troubleshooting](troubleshooting.md) and [Getting Help](getting_help.md).\n\nContinue below to [In Your Own Scene](#in-your-own-scene).\n\n### From Github\n1. Download the [latest binary release](https://github.com/TokisanGames/Terrain3D/releases) and extract the files, or [build the plugin from source](building_from_source.md).\n2. Run Godot using the console executable so you can see error messages.\n3. In the Project Manager, import the demo project and open it. Restart when it prompts.\n4. In `Project / Project Settings / Plugins`, ensure that Terrain3D is enabled.\n5. Select `Project / Reload Current Project` to restart once more.\n6. If the demo scene doesn't open automatically, open `demo/Demo.tscn`. You should see a terrain. Run the scene by pressing `F6`. \n\nIf the demo isn't working for you, watch the [tutorial videos](tutorial_videos.md) and see [Troubleshooting](troubleshooting.md) and [Getting Help](getting_help.md).\n\nContinue below.\n\n### In Your Own Scene\n* To use Terrain3D in your own project, copy `addons/terrain_3d` to your project folder as `addons/terrain_3d`. Create the directories if they are missing.\n* When making a new 3D scene, add a Terrain3D node to your Scene panel. In the Inspector, find `Data Directory` and click the folder icon to specify an empty directory in which to store your data. You can share this directory with other scenes that will load the same terrain map. Different terrain maps need separate directories.\n* Optionally, click the arrow to the right of `Material` and `Assets` and save these as `.tres` files should you wish to share your material settings and asset dock resources (textures and meshes) with other scenes. This is recommended. Saving these in the data directory is fine.\n\nNext, review the [user interface](user_interface.md) or learn how to [prepare your textures](texture_prep.md) if you're ready to start creating.\n\n\n## Upgrading Terrain3D\n\nTo update Terrain3D: \n1. Close Godot.\n2. Remove `addons/terrain_3d` from your project folder.\n3. Copy `addons/terrain_3d` from the new release download or build directory into your project addons folder.\n\nDon't just copy the new folder over the old, as this won't remove any files that we may have intentionally removed.\n\n### Upgrade Path\n\nWhile later versions of Terrain3D can generally open previous versions, not all data will be transfered unless the supported upgrade path is followed. We occasionally deprecate or rename classes and provide upgrade paths to convert data for a limited time. \n\nIf upgrading from a very old version, you may need to go through multiple steps to upgrade to the latest version.\n\n| Starting Version | Can Upgrade w/ Data Conversion |\n|------------------|-------------------|\n| 1.0.1 | 1.1.0 |\n| 1.0.0 | 1.0.1 - 1.1.0 |\n| 0.9.3 | 1.0.0 - 1.1.0 |\n| 0.9.2 | 0.9.3* |\n| 0.9.1 | 0.9.2 - 0.9.3* |\n| 0.9.0 | 0.9.2 - 0.9.3* |\n| 0.8.4 | 0.9.2 - 0.9.3* |\n| 0.8.3 | 0.8.4 - 0.9.0 |\n| 0.8.2 | 0.8.4 - 0.9.0 |\n| 0.8.1 | 0.8.4 - 0.9.0 |\n| 0.8.0 | 0.8.4 - 0.9.0 |\n\n* 0.9.3 - Data storage changed from a single .res file to one file per region saved in a directory.\n"
  },
  {
    "path": "doc/docs/instancer.md",
    "content": "Foliage Instancing\n====================\n\nTerrain3D provides two types of instancing systems that can be used to render not only grass, but also rocks, trees, pinecones, debris, or anything else you want.\n\n1. A Particle Shader allows the GPU to automatically generate meshes around the camera. We have provided an example that you can modify and extend for your own needs in `extras/particle_example`. We refer to these meshes as `particles`.\n\n2. [Terrain3DInstancer](../api/class_terrain3dinstancer.rst) optimally renders hundreds of thousands of meshes that have been intentionally placed either by code or by hand using Godot's [MultiMesh](https://docs.godotengine.org/en/stable/classes/class_multimesh.html) class. See this link for capabilities and engine tutorials. We refer to these meshes as `instances`.\n\nSee [Procedural Placement](#procedural-placement) for a comparision between these two methods. The rest of this page is dedicated to learning how to use the instancer for manual and code placement.\n\n\n**Table of Contents**\n* [How To Use The Instancer](#how-to-use-the-instancer)\n* [Limitations](#limitations)\n* [LOD Support](#lod-support)\n* [Wind and Player Interaction](#wind-and-player-interaction)\n* [Procedural Placement](#procedural-placement)\n* [Importing From Other Tools](#importing-from-other-tools)\n\n\n## How To Use The Instancer\n\n### 1. Open the Asset Dock Meshes\n\nClick the `Meshes` tab in the Asset Dock.\n\nUse the icons to en/disable, edit, or clear an asset, or use these mouse buttons:\n\n* <kbd>LMB</kbd> - Select.\n* <kbd>RMB</kbd> - Edit.\n* <kbd>MMB</kbd> - Clear the asset and remove all instances.\n\nYou can only remove entries from the end of the list. You can edit any of them, change the ID to the end, and then remove it.\n\n```{image} images/mesh_asset_dock.png\n:target: ../_images/mesh_asset_dock.png\n```\n\n### 2. Set Up A Mesh Asset\n\nClick the large plus button to add a new mesh asset. This will `Edit` the asset and display its settings in the inspector.\n\n```{image} images/mesh_asset_inspector.png\n:target: ../_images/mesh_asset_inspector.png\n```\n\nEach mesh asset can be a generated texture card or a scene file (.tscn, .scn, .glb, .fbx). \n\nChanging the ID will reorder the assets in the list, allowing you to place one at the end for removal. Unlike the texture list, changing IDs won't change what is dispalyed on the ground because it also changes the mesh ID in the data.\n\nIf your mesh doesn't have a material, add or customize the override material as needed. If you are using a generated texture card, add your texture file to the `albedo_texture` slot in the material and enable transparency.\n\nThe generated texture card is 1, 2, or 3 QuadMeshes, on which you can apply a 2D texture of a plant as is commonly done in older or low poly games.\n\nSee the [Terrain3DMeshAsset API](../api/class_terrain3dmeshasset.rst) for a description of the parameters.\n\n### 3. Select The Mesh Asset\n\nClick the desired mesh in the Asset Dock. This should also enable the foliage tool in the toolbar, but also select that if not.\n\n### 4. Adjust Placement Options\n\n```{image} images/instancer_options.png\n:target: ../_images/instancer_options.png\n```\n\nThe instancer has many options for adjusting the height, scale, rotation, and color shifts while painting. There are options for fixed adjustments and random variances. For instance, using fixed_scale to increase all instances by 200%, and using random_scale to vary each by +/- 20%.\n\nThe paintable height offset on the tool settings bar is cummulative with the mesh asset height offset in the inspector. This allows you to specify the default on the asset, and override it while painting.\n\nMost of the options should be self explanatory.\n\nAdjusting the vertex color requires that `vertex_color_use_as_albedo` is enabled in your material. The hue shift applies to the specified vertex color, which should have some saturation to see any effect. e.g. Hue shift on white will not be visible. Hue shift on red will be.\n\n\n### 4. Paint On The Ground\n\nPaint instances on the terrain. You can remove instances of the selected mesh by holding <kbd>Ctrl</kbd> while painting.\n\nPress <kbd>Ctrl + Shift + LMB</kbd> to remove mesh instances of any type.\n\nSee [User Interface](user_interface.md) for additional keys.\n\n## Limitations\n\nThere are some caveats and limitations built into the engine that you should be aware of.\n\n### Simple Objects\n\nSome 3D assets are complex objects with multiple, separate meshes, such as a tree trunk and leaves, a chest and lid, or a door frame and door. MultiMeshes supports only one mesh. If you give it a complex object with separate trunk and leaves, it won't work as expected.\n\nEither combine your complex objects into one (easy to do with the Join operation in Blender, while maintaining separate materials), or use another method of placement, such as AssetPlacer, Scatter, or manual placement.\n\nIf you use a mesh with multiple materials, make sure they are connected to the Mesh resource on import into Godot, or in the MeshInstance3D override slots in the scene. We currently provide only a single material override.\n\n### No Individual Culling\n\nA MultiMesh renders all instances in one draw call and does not cull individual instances via frustum, occlusion, nor distance.\n\nWe mitigate this by generating multiple MultiMeshes. Each region is divided into 32x32m cells so that these MultiMeshes can be culled by frustum or occlusion. We expose visibility ranges in each mesh asset settings so they can be culled by distance as well.\n\n\n### No Collision\n\nMultimeshes are generated and rendered on the GPU. The physics engine is on the CPU, and doesn't know anything about the placed instances. For now use this only for instances where collision is unnecessary like grass.\n\nIn the future, instance collision will be generated using the collision shapes stored in your scene file. See [PR 699](https://github.com/TokisanGames/Terrain3D/pull/699).\n\n\n### No Scene Transforms\n\nCurrently, the instancer uses the first Mesh resource it finds in the scene file and uses it as is. It ignores all transforms in the file, as they are not stored in the Mesh resource.\n\nIf you've built and imported your object with a non-zero transform, and have used the position, rotation, or scale in the scene file to fix your placement, then your instanced objects are going to have strange transforms. e.g. Your tree might be laying flat or be extremely large or small.\n\nFix your object in blender by setting the origin point in the center or the bottom of the mesh. Move the mesh origin to (0, 0, 0). Ensure the scale is appropriate to real world units. Apply your transforms, so you have neutral transforms: position (0,0,0), rotation (0,0,0), scale (1,1,1). Be cognizant of your export and import settings. In the past, exporting via Blender FBX and importing into Godot produced a scene file where the mesh was scaled 0.01 and the parent node was scaled to 100 (or the opposite?). We use GLB/GLTF and don't have this issue. Ideally the object in Blender and all nodes in your scene file in Godot have neutral transforms, and your mesh vertex positions are scaled to real world coordinates.\n\n\n### No VRAM Overrun Protection\n\nGodot currently has no protection against filling up your VRAM. You could do so if you have a simple mesh asset, say grass, with hundreds of thousands of instances on the ground, and then replace that mesh in the asset dock with a much larger mesh. That could instantly fill your VRAM. Your console will fill with Vulkan errors complaining about running out of VRAM and you'll have to force Godot to quit and restart. Your system may also crash if you have flakey drivers or hardware.\n\n----------------------------------\n\n## LOD Support\n\nMeshes are often created with multiple Levels of Detail (LODs). These are separate meshes, usually combined in the same file, which are the same mesh at higher and lower vertex counts. They usually use the same material. Often assets have as the last LOD, a single quad mesh with a flat 2D billboard texture with a picture of the mesh.\n\nThere is a quirk of the LOD numbering convention to keep in mind. LOD0 is the one closest to the camera and the highest detail. LOD4 is farther away, and lower detail. Confusingly, people might refer to LOD0 as higher than LOD4, even though 4 is greater than 0. When refering to a \"higher\" LOD, does this mean higher detail (LOD0) or higher LOD ID (LOD4)? We prefer using the terms near/far or first/last and avoid the terms higher/lower or greater/lesser.\n\n\n### Automatic LOD Generation\n\nBy default, Godot automatically generates LODs on imported meshes. Godot MultiMeshes do work with these auto generated LODs. If that is sufficient for you, ensure [last_lod](../api/class_terrain3dmeshasset.rst#class-terrain3dmeshasset-property-last-lod) and [lod0_range](../api/class_terrain3dmeshasset.rst#class-terrain3dmeshasset-property-lod0-range) are both `0` on the [Terrain3DMeshAsset](../api/class_terrain3dmeshasset.rst) and our LOD system will be disabled for this mesh. \n\nYou may find that the generated meshes are sub par. If so, the only way to fix it is by disabling the auto LOD generation on the mesh import settings. This can be done with existing mesh instances on the ground.\n\nIf using assets with artist created lods, you should also disable LOD generation in the import settings. \n\nGodot also automatically generates a shadow mesh, which you may desire to disable on import and use an artist created LOD as a [shadow impostor](#shadow-performance) instead.\n\n### Artist Created LODs\n\nYour scene file can contain artist created LODs and we will use up to 10. Though we recommend no more than 4, as we will be generating MultiMesh3D nodes for every mesh type, every 32m square that is in use.\n\nThe recommended tree structure looks like this. Have the MeshInstance3Ds use LOD# suffixes, and be siblings on the same level somewhere below the root node:\n\n```{image} images/mesh_asset_lod_setup.png\n:target: ../_images/mesh_asset_lod_setup.png\n```\n\nOur system scans the provided scene file for _reasonable_ LOD structures.\n\n1. It will first search for meshes that match `*LOD?`. e.g. `MyMeshInstanceLOD0`, `MyMeshInstanceLOD1`. If found, it will sort by the last digit and use the first 10.\n2. Otherwise, it will find all meshes and use the first 10 in the order provided.\n3. Finally, if the root node is a mesh, it will use that as `LOD0`.\n\nAfter linking your scene file, you can specify the visible distance range for each LOD or use the defaults, spacing every 32m.\n\nInstances are drawn using a 32x32m grid of MMIs, so LODs are switched a grid cell at a time, not per instance. If your LODs are fairly similar, pop-in shouldn't be terrible unlike the extreme example shown below.\n\n```{image} images/mesh_asset_lods.jpg\n:target: ../_images/mesh_asset_lods.jpg\n```\n\n\n### Shadow Performance\n\nShadow calculation is expensive, especially when rendering a tree with tens of thousands of leaves. In most cases, this detail is far more than you need, especially when rendering on a detailed terrain.\n\nWe provide two options to improve shadow calculation performance.\n\n1. [last_shadow_lod](../api/class_terrain3dmeshasset.rst#class-terrain3dmeshasset-property-last-shadow-lod) - This setting defines the farthest LOD that will cast shadows. LODs beyond this will render without shadows. DirectionalLight3Ds also provide a shadow distance option, but you may wish to have the DL and tree shadows rendered farther out while grass shadows stop much closer.\n\n2. [shadow_impostor](../api/class_terrain3dmeshasset.rst#class-terrain3dmeshasset-property-shadow-impostor) - We provide the option of using a shadow impostor, which uses a lower resolution mesh to calculate the shadows while rendering a higher resolution mesh. e.g. Setting this to 2 means when an instance is rendered at LOD0, it is rendered without shadows, while its LOD2 is rendered with only shadows. When LOD2 or LOD3 are visible, they use their own shadows.\n\nImpostors are only usable if you have more than one LOD mesh. However Godot automatically generates a shadow mesh on import of your meshes. You may wish to use it, or disable it and rely on the artist created LOD.\n\nYou can see both settings applied in the picture below. Compare with the original. The meshes are 3m above the ground so we can clearly see the shadows. `last_shadow_lod = 1`, disabling shadows for LOD2 (green) and LOD3 (blue). And `shadow_impostor = 1`, making the LOD0 sphere use the LOD1 cube shadow. \n\n```{image} images/mesh_asset_shadow_lods.jpg\n:target: ../_images/mesh_asset_shadow_lods.jpg\n```\n\nOriginal: \n\n```{image} images/mesh_asset_lods.jpg\n:target: ../_images/mesh_asset_lods.jpg\n```\n\n\n----------------------------------\n\n## Wind And Player Interaction\n\nThese features can be implemented by having a wind shader or player interaction (grass flattening) shader in a ShaderMaterial attached to your mesh. We don't currently provide these shaders, but you can find both online. We may provide these shaders in the future. The instancer will use whatever material you've attached to the mesh or placed in the override slot, and the MultiMesh will automatically apply it to all instances.\n\n## Procedural Placement\n\nPlacing instances via code is possible. See the [Terrain3DInstancer API](../api/class_terrain3dinstancer.rst) for available functions. Also read this and the next section.\n\nOne thing you must consider is if it makes sense to use the MultiMesh based instancer, or if it's more efficient to use a particle shader, such as the example included in our `extras/particle_example` directory.\n\n**MultiMesh Pros & Cons:**\n* Designed for hand painting and manual control\n* Can combine hand painting and API placement\n* API placement is possible, but a bit tricky\n* More control over placement as you can introduce more logic and code that considers many instances\n* Must place, store, and load all transforms which can be cumbersome for very large or procedural worlds\n* More optimal for the same number of instances on screen since Particle Shaders use MultiMeshes under the hood\n\n**Particle Shader Pros & Cons:**\n* All automatic placement, no manual control\n* Less control over placement that can only consider one instance in the logic\n* No data stored, so the number of instances in memory can be significantly less. For very large or procedural worlds this is much more efficient when loading and running.\n\nPerhaps it makes sense to use both, such as particles for grass, and instances for rocks, bushes and trees. You'll need to test and determine which methods will help you achieve your goal best.\n\n\n## Importing From Other Tools\n\nYou can find a sample script that will import data from SimpleGrassTextured in `project/addons/terrain_3d/extras/3rd_party/import_sgt.gd`. SGT is another MultiMesh management tool, so the only data that we need from it are the transforms. You could do something similar for other tools.\n\n**To use it:**\n1. Setup the mesh asset you wish to use in the asset dock.\n1. Select your Terrain3D node.\n1. In the inspector, click Script (very bottom) and Quick Load `import_sgt.gd`.\n1. At the very top, assign your SimpleGrassTextured node.\n1. Input the desired mesh asset ID.\n1. Click import. The output window and console will report when finished.\n1. Clear the script from your Terrain3D node, and save your scene. \n\nThe instance transforms are now stored in your region files.\n\nThis script also serves as an example to learn how to use the API for procedural placement. Though this script uses add_multimesh(), you could manually iterate through the SGT multimesh, pull out the transforms, modify them, then send them to the instancer with add_transforms().\n\n"
  },
  {
    "path": "doc/docs/introduction.md",
    "content": "Introduction\n=====================\n\nTerrain3D is an editable **clipmap terrain** system divided into **regions**. Data can be hand edited or manipulated through the **API** in realtime. This page summarizes the core concepts of the whole system.\n\n\n## Clipmap Terrain\n\nThis is a Geomorphing Geometric Clipmap Mesh Terrain, as used in The Witcher 3. The terrain is made of several flat meshes, which have a higher density towards the center, and lower farther away. This provides the infrastructure for automatic Level of Detail (LOD) handling.\n\n```{image} images/mesh_lods_flat.jpg\n:target: ../_images/mesh_lods_flat.jpg\n```\n\nThe LODs are then blended together in a circular pattern. \n\n```{image} images/mesh_circular_lods.png\n:target: ../_images/mesh_circular_lods.png\n```\n\nAs the camera moves, the terrain meshes are centered on the camera, keeping higher LODs near the camera and lower LODs far away. The meshes and height data are sent to the GPU which updates the vertices of the mesh every frame. Even though the meshes are constantly moving, the terrain appears stable because the height data remains fixed in place.\n\nSee [System Architecture](system_architecture.md) for more details.\n\n\n## Regions\n\nThe terrain is divided into regions, which represent both physical space, and containers for terrain data.\n\nBy default, regions are 256m x 256m, but can range between 64m and 2048m. The region size defines a grid in the world with borders at -256, 0, 256, 512, 768, 1024, etc. This region size also corresponds to the 256x256 pixel size of the images and textures used to store terrain data. These sizes are independent of your ground texture sizes.\n\nYou pay in memory and VRAM only for the regions you allocate. Space between the regions can be set to empty, flat, or shader generated noise (See `Terrain3D / Material / WorldBackground`). No collisions are generated outside of regions.\n\n```{image} images/regions_used.jpg\n:target: ../_images/regions_used.jpg\n```\n\nThere is currently a limit of 1024 regions or 32 x 32. So the maximum dimensions of the your world are `32 * region_size`, the maximum being 32 * 2048 or 65,536m per side.\n\nRegion files are stored in the data directory as individual files, with their location coordinates in the filename. e.g. terrain3d_01-02.res, which represents region (+01, -02).\n\nThe region grid is visible if `View Gizmos` is enabled in the Godot `Perspective` menu. Or if `Terrain3D / Regions / Show Grid` is enabled.\n\n\n## Region Location\n\nRegion locations are the grid coordinates that regions fit into. Represented as a Vector2i, eg (-1, 0), this is the primary key used for identifying regions in the API.\n\nConverting a global position to a region location is calculated by `floor(global position / region size)`, or by calling the function [Terrain3DData.get_region_location()](../api/class_terrain3ddata.rst#class-terrain3ddata-method-get-region-location). e.g. If region size is 1024, a global position of (2500, 0, 3700) would convert to (2, 3). It gets more complicated as [region_size](../api/class_terrain3d.rst#class-terrain3d-property-region-size) changes.\n\nSet `Terrain3D / Regions / Label Distance` to 1024-4096 to see region coordinates in the viewport.\n\n\n## Vertex Painting\n\nThis system is a vertex painter, not a pixel painter. In order to make a high performance terrain, we are spreading out say 1024 x 1024 pixels over 1024m x 1024m. Each square meter is influenced by only 4 pixels of data in the corners. \n\nWe use sophisticated algorithms that allow natural blending of *quality* textures between the vertices. However, it is not magic. Pixel perfect painting is not practical. Texture artists place 4k or 8k textures on a human sized rock to acheive adequate texel density. How much larger than a rock is a 1024m x 1024m terrain, let alone 16km x 16km? Achieving the same texel density on a pixel painted terrain would consume far more VRAM than anyone has.\n\nThe system we have works well for producing natural environments and is modeled off of the Witcher 3 terrain system. It will most likely work for your game as well. You'll read more about selecting and preparing quality textures in [Texture Prep](texture_prep.md) and [Texture Painting](texture_painting.md).\n\n\n## API\n\nAplication Programming Interface. This is the [list of variables and functions](../api/index.rst) available to you via code.\n\nLike Godot, this documentation is separated in two parts in the sidebar. The first lists various free form tutorial pages describing each aspect of the terrain system, such as what you are reading now. \n\nThe second section is the API. You can find it at the very bottom left of the document titles. The API is also built into the Godot editor help system, once Terrain3D is installed.\n\nFinally, this documentation is versioned. Select the version that matches your version of the plugin in the menu.\n"
  },
  {
    "path": "doc/docs/keyboard_shortcuts.md",
    "content": "Keyboard Shortcuts\n=================\n\nThe following mouse and keyboard shortcuts or hotkeys are available.\n\n\n**Table of Contents**\n* [General Keys](#general-keys)\n* [Overlays](#overlays)\n* [Tool Selection](#tool-selection)\n* [Tool Specific Keys](#tool-specific-keys)\n* [Special Cases](#special-cases)\n\n\n## General Keys\n\n* <kbd>LMB</kbd> - **Apply** the current tool to the terrain.\n* <kbd>Ctrl + LMB</kbd> - **Inverse** the current tool. Use <kbd>Cmd</kbd> on macOS.\n* <kbd>Shift + LMB</kbd> - **Smooth** height, texture blend, color, wetness.\n* <kbd>Alt + LMB</kbd> - **Alternate mode**, where applicable.\n* <kbd>T</kbd> - **Invert the slope filter** on the tool settings bar.\n* <kbd>Ctrl + Z</kbd> - **Undo**. View the entries in the Godot `History` panel.\n* <kbd>Ctrl + Shift + Z</kbd> - **Redo**\n* <kbd>Ctrl + S</kbd> - **Save** scene & modified terrain data. Saved regions are printed to the [console](troubleshooting.md#using-the-console).\n\n\n## Overlays\n\nThese toggle the Overlays found in the inspector. The mouse must be in the 3D Viewport with Terrain3D selected for these to work. \n\n* <kbd>1</kbd> - Toggle **Region Grid**.\n* <kbd>2</kbd> - Toggle **Region Label Distance** between 0 and 4096.\n* <kbd>3</kbd> - Toggle **Contour Lines**. Customize in the material when enabled.\n* <kbd>4</kbd> - Toggle **Instancer Grid**.\n* <kbd>5</kbd> - Toggle **Vertex Grid**.\n\n\n## Tool Selection\n\nThe mouse must be in the 3D Viewport with Terrain3D selected for these to work.\n\n* <kbd>E</kbd> - Add / remove **rEgion**.\n* <kbd>R</kbd> - Sculpt **Raise** or lower.\n* <kbd>H</kbd> - Sculpt **Height**.\n* <kbd>S</kbd> - Sculpt **Slope**.\n* <kbd>B</kbd> - Paint **Base** texture.\n* <kbd>V</kbd> - Spray **oVerlay** texture.\n* <kbd>A</kbd> - Paint **Autoshader**.\n* <kbd>C</kbd> - Paint **Color**.\n* <kbd>W</kbd> - Paint **Wetness**.\n* <kbd>N</kbd> - Paint **Navigation**.\n* <kbd>X</kbd> - Paint **Holes**.\n* <kbd>I</kbd> - Add mesh **Instances**.\n\n\n## Tool Specific Keys\n\n### Region Tool\n\n* <kbd>LMB</kbd> - **Add** a region.\n* <kbd>Ctrl + LMB</kbd> - **Remove** a region.\n\n### Raise / Lower Tool\n\n* <kbd>LMB</kbd> - **Raise** the terrain.\n* <kbd>Ctrl + LMB</kbd> - **Lower** the terrain.\n* <kbd>Shift + LMB</kbd> - **Smooth** the terrain height.\n* <kbd>Alt + LMB</kbd> - **Lift floors**: Lifts up lower portions of the terrain without affecting higher terrain. Use it along the bottom of cliff faces. See [videos demonstrating before and after](https://github.com/TokisanGames/Terrain3D/pull/409). \n* <kbd>Ctrl + Alt + LMB</kbd> - **Flatten peaks**: Reduces peaks and ridges without affecting lower terrain around it.\n\n### Height Tool\n\n* <kbd>LMB</kbd> - **Flatten** the terrain at the height set on the [settings bar](user_interface.md#tool-settings-bar).\n* <kbd>Ctrl + LMB</kbd> - **Pick & Flatten**: Flattens the terrain at the height first clicked.\n* <kbd>Shift + LMB</kbd> - **Smooth** the terrain height.\n\n### Slope Tool\n\n*This is not to be confused* with the slope range filter on the [settings bar](user_interface.md#tool-settings-bar).\n\n* <kbd>LMB</kbd> - Set points to automatically create a slope, or if `Drawable` is checked, manually sculpt between points.\n* <kbd>Shift + LMB</kbd> - **Smooth** the terrain height.\n\n### Paint Tool\n\nAll operations are performed within the slope range on the [settings bar](user_interface.md#tool-settings-bar).\n\n* <kbd>LMB</kbd> - **Paint** the base texture.\n* <kbd>Shift + LMB</kbd> - **Smooth** the overlay texture blend value, averaging towards 0.5.\n* <kbd>Alt + Shift + LMB</kbd> - **Smooth** the blend value with a true average.\n\n### Spray Tool\n\nAll operations are performed within the slope range on the [settings bar](user_interface.md#tool-settings-bar).\n\n* <kbd>LMB</kbd> - **Spray** the overlay texture, increase the blend value, and once over a certain threshold, set overlay ID.\n* <kbd>Alt + LMB</kbd> - **Spray** the overlay texture, increase the blend value, and set the overlay ID immediately.\n* <kbd>Ctrl + LMB</kbd> - **Remove** the overlay texture. Reduces the blend value, thus showing more of the base.\n* <kbd>Shift + LMB</kbd> - **Smooth** the blend value, averaging towards 0.5.\n* <kbd>Alt + Shift + LMB</kbd> - **Smooth** the blend value with a true average.\n\nNote: Yes this is a ridiculous amount of options. But our lead environment artist uses all of them in different ways so we'll keep them for now. In the future we anticipate adding a 3rd texture layer for improved blending, which will likely consolidate all Paint and Spray options into one tool.\n\n\n### Autoshader Tool\n\n* <kbd>LMB</kbd> - **Add** areas to the autoshader.\n* <kbd>Ctrl + LMB</kbd> - **Remove** areas from the autoshader.\n\n### Color Tool\n\nAll operations are performed within the slope range on the [settings bar](user_interface.md#tool-settings-bar).\n\n* <kbd>LMB</kbd> - **Paint** color on the terrain.\n* <kbd>Ctrl + LMB</kbd> - **Remove** painted color from the terrain.\n* <kbd>Shift + LMB</kbd> - **Smooth** the painted colors.\n\n### Wetness Tool\n\nAll operations are performed within the slope range on the [settings bar](user_interface.md#tool-settings-bar).\n\n* <kbd>LMB</kbd> - **Add** wet areas to the terrain.\n* <kbd>Ctrl + LMB</kbd> - **Remove** wet areas from the terrain.\n* <kbd>Shift + LMB</kbd> - **Smooth** the wetness values.\n\n### Navigation Tool\n\n* <kbd>LMB</kbd> - **Add** navigable areas to the terrain.\n* <kbd>Ctrl + LMB</kbd> - **Remove** navigable areas.\n\n### Holes Tool\n\n* <kbd>LMB</kbd> - **Add** holes.\n* <kbd>Ctrl + LMB</kbd> - **Remove** holes.\n* <kbd>Shift + LMB</kbd> - **Smooth** the terrain height.\n\n### Instancer Tool\n\nAll operations *except smoothing* are performed within the slope range on the [settings bar](user_interface.md#tool-settings-bar).\n\n* <kbd>LMB</kbd> - **Add** the selected mesh instances to the terrain.\n* <kbd>Ctrl + LMB</kbd> - **Remove** instances of the *selected* mesh asset.\n* <kbd>Ctrl + Shift + LMB</kbd> - **Remove** instances of *any* mesh asset.\n* <kbd>Shift + LMB</kbd> - **Smooth** the terrain height.\n\n---\n\n## Special Cases\n\n**macOS Users:** Use <kbd>Cmd</kbd> instead of <kbd>Ctrl</kbd>.\n\n**Touchscreen Users:** You'll see an `Invert` checkbox on the settings bar which acts like <kbd>Ctrl</kbd> to inverse operations.\n\n**Maya Users:** The <kbd>Alt</kbd> key can be changed to Space, Meta (Windows key), or Capslock in `Editor Settings / Terrain3D / Config / Alt Key Bind` so it does not conflict with Maya input settings `Editor Settings / 3D / Navigation / Navigation Scheme`.\n"
  },
  {
    "path": "doc/docs/license.rst",
    "content": "License\n==========\n.. include:: ../../LICENSE.txt"
  },
  {
    "path": "doc/docs/navigation.md",
    "content": "# Navigation\n\nNavigation in games is quite a broad and sometimes complex topic. A full description can't be provided here, so it's recommended to familiarize yourself with the basic concepts in the [official Godot documentation](https://docs.godotengine.org/en/stable/tutorials/navigation/navigation_introduction_3d.html) first.\n\nThis page describes how to bake a nav mesh (navigation mesh) for your terrain.\n\n\n## Setting Up Navigation\n\nNav meshes take a long time to bake, and in most games, it would be wasteful to generate vast amounts of navigation data for areas that navigation agents aren't going to use. So by default, all terrain is un-navigable. To make parts of it navigable, you must use the Navigable terrain tool. Navigable areas appear in dark magenta when this tool is selected.\n\n```{image} images/nav_painting.png\n:target: ../_images/nav_painting.png\n```\n\nNext, you will need a `NavigationRegion3D` node. If you don't already have one, Terrain3D provides a convenient tool to set one up for you. Select your `Terrain3D` node, then in the `Terrain3D` menu on top, click `Set up Navigation`.\n\n```{image} images/terrain3d_menu.png\n:target: ../_images/terrain3d_menu.png\n```\n\nThe same steps can be performed manually if you prefer:\n\n1. Create a `NavigationRegion3D` node.\n2. Assign it a blank `NavigationMesh` resource. Review and adjust the settings on it if you need to.\n3. If using the default source geometry mode, move the `Terrain3D` node to be a child of the new `NavigationRegion3D` node. Otherwise, if you selected one of the group-based modes, add the Terrain3D node to the group.\n\n\n## Baking a Nav Mesh\n\nOnce navigation has been set up, baking and re-baking it is straight-forward:\n\n1. Select the `Terrain3D` node.\n2. In Terrain3D menu, click `Bake NavMesh`. This can take a long time to complete.\n\nNote that the standard `Bake NavMesh` button that `NavigationRegion3D` provides will not generate a nav mesh for Terrain3D (see [godot-proposals#5138](https://github.com/godotengine/godot-proposals/issues/5138)). Only use the Terrain3D baker, which appears whenever you click the `Terrain3D` node or any `NavigationRegion3D` nodes. \n\n```{image} images/nav_baking.png\n:target: ../_images/nav_baking.png\n```\n\nNote: After loading a scene and clicking a NavigationRegion3D, the menu won't appear until after Terrain3D has been clicked.\n\nIf this is your first time setting up and baking a nav mesh, the only thing left to do is add your navigation agents. See [Godot's very clear and thorough documentation on navigation agents](https://docs.godotengine.org/en/stable/tutorials/navigation/navigation_using_navigationagents.html), which provides several handy template scripts you can use.\n\nYou can also play with the `NavigationDemo.tscn` and `CodeGeneratedDemo.tscn` scenes which both demonstrate navigation.\n\n<figure class=\"video_container\">\n <video width=\"600px\" controls=\"true\" allowfullscreen=\"true\">\n <source src=\"../_static/video/nav_demo.mp4\" type=\"video/mp4\">\n </video>\n</figure>\n\n\n## Tips\n\n### Enable visible navigation for debugging\n\nThis option enables a blue overlay mesh that displays where the navigation mesh exists.\n\n```{image} images/nav_debugging.png\n:target: ../_images/nav_debugging.png\n```\n\n\n### How to remove navigation or hide the purple indication\n\nYou can remove navigation from the terrain by painting while holding down <kbd>Ctrl</kbd>\n\nYou can hide the purple indication for where navigation is by clicking any tool in the toolbar other than Navigation.\n\n\n### Save NavigationMesh resources to disk\n\nNavigationMesh resources can bloat the size of your scene. It's recommended to save these resources to disk in binary format with the `.res` extension.\n\n\n### Use multiple nav meshes in large scenes\n\nAs mentioned, in many games, large areas of terrain are generally unreachable to agents. The 'Navigable Areas' tool is used to reduce the amount of geometry coming from the `Terrain3D` node, however having lots of other meshes in unreachable areas can also lead to long bake times.\n\nIf you have a very large scene in, for example, an open world RPG, it's better to have multiple small nav meshes that cover only what you need, rather than one giant one covering the entire world. In said example, each RPG town could have its own nav mesh. To do this, you would need to:\n\n1. Create a NavigationRegion3D node for each town, each with their own NavigationMesh resources (i.e. unique, not shared).\n2. Define the [`filter_baking_aabb`](https://docs.godotengine.org/en/stable/classes/class_navigationmesh.html#class-navigationmesh-property-filter-baking-aabb) on each nav mesh, so that it only bakes objects within its own area.\n3. To use the same Terrain3D node with multiple NavigationRegion3Ds, change the nav meshes to use one of the group modes [`SOURCE_GEOMETRY_GROUPS_*` modes](https://docs.godotengine.org/en/stable/classes/class_navigationmesh.html#class-navigationmesh-property-geometry-source-geometry-mode), add the Terrain3D node to that group and bake. Alternatively, using the default `SOURCE_GEOMETRY_ROOT_NODE_CHILDREN` mode, add Terrain3D as a child of one NavigationRegion3D and bake navigation with the Terrain3D menu. Then move it as a child of the next and bake.\n\n\n## Common Issues\n\n### Navigation won't generate where foliage instances have been placed.\n\nChange [NavigationMesh.parsed_geometry_type](https://docs.godotengine.org/en/stable/classes/class_navigationmesh.html#class-navigationmesh-property-geometry-parsed-geometry-type) from `Mesh Instance` (visual) to `Static Colliders`.\n\n\n### NavigationMeshSourceGeometryData3D is empty. Parse source geometry first.\n\nThe engine produces this error if there's nothing for a NavigationRegion3D to generate a nav mesh from. The most likely cause, if you're using Terrain3D, is that you haven't painted any parts of the terrain as navigable.\n\n\n### Navigation map synchronization error\n\n`Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'`\n\nThere are several possible causes for this. If the `cell_size` of your nav mesh matches the `cell_size` in your project settings, it's currently believed to be caused by [an engine bug](https://github.com/godotengine/godot/issues/85548). This error message shouldn't affect the usability of your nav meshes.\n\n\n### The Nav mesh is broken over steep slopes\n\nThe NavigationRegion3D has settings for adjusting the slope you wish to allow in your nav mesh. Adjust those settings, and review the other settings available in Godot's documentation.\n\n\n### Agents get stuck on collisions, run in circles, go off the nav mesh, or fail to find obvious paths\n\nDeveloping good path-following behaviors is a very complex topic, far beyond the scope of this article. In general, make sure your NavigationMesh settings, NavigationAgent3D settings, and collisions are all consistent with each other. If a NavigationAgent3D is using a NavigationMesh that was baked for smaller agents than itself, for instance, then it's going to get stuck.\n\nYou can try repainting an area and regenerating the nav mesh.\n\nMaking reasonable fallback behaviors, when you're able to detect in a script that something has gone awry, can also help.\n\n\n## Baking a Nav Mesh at Runtime\n\nIf your project has dynamic, or generated terrain, or if the traversable area of your terrain is so gigantic that it can't be baked in the editor, then you might need to use runtime navmesh baking.\n\nTerrain3D contains an example script that shows how to bake terrain nav meshes at runtime, which you can find in the `CodeGeneratedDemo.tscn` scene. \n\n<figure class=\"video_container\">\n <video width=\"600px\" controls=\"true\" allowfullscreen=\"true\">\n <source src=\"../_static/video/nav_code_demo.mp4\" type=\"video/mp4\">\n </video>\n</figure>\n\nThe script periodically re-bakes a nav mesh in the area around the player as it moves through the scene. Adjust `bake_cooldown` if you wish the baker to update more frequently, at the cost of CPU cycles. \n\nYou can also adjust how frequently the navigation agent updates its path by adjusting `Enemy.gd:RETARGET_COOLDOWN`. If you have a lot of agents, that's going to come with a performance hit.\n\n### Performance Tips\n\nNavigation baking is slow. Editor or compile-time baking navigation is usually a better option for games. Runtime baking can still be usable however. There are a few things you can do to speed it up, or work around the slowness:\n\n* Create a set of fallback behaviors that get used when proper navigation isn't possible. In this way, you can make the AI degrade without completely failing while waiting for the nav server, or when out of range of the nav mesh. For instance the enemy could simply move straight towards the player if navigation is not ready yet. It could have rudimentary ability to detect cliffs and obstacles with raycasts.\n* Reduce the speed of the player character. Delays happen frequently in the demo because the player can move across the mesh so rapidly.\n* Reduce the size of the baked mesh with `mesh_size` to make it cheaper to bake.\n* Increase the size of the cells (i.e. reduce the resolution of the navmesh) to reduce the amount of work to do. Change `cell_size` in the `template` NavigationMesh. If you increase it too far, obstacles may hide within a cell and break your navigation. You can write fallback behaviors for that as well, or ensure that your obstacles are all larger than the cell size.\n"
  },
  {
    "path": "doc/docs/nightly_builds.md",
    "content": "Nightly Builds\n====================\n\nTraditionally, \"nightly builds\" are automatically built from the main development tree every night. \n\nOur Github repository is configured to build automatically on every commit and PR push. If you want to test more recent versions of Terrain3D than the releases, you can download these \"push builds\" or \"nightly builds\". \n\nThey are exactly the same construction as the releases except for the commit used. However, they are inherently less tested as new commits have more recently been merged in, and may have more bugs.\n\n1. [Click here](https://github.com/TokisanGames/Terrain3D/actions/workflows/build.yml?query=branch%3Amain) for `Github Actions`, `Build All` workflow, `main` branch.\n\n2. Click the most recent successful build:\n\n```{image} images/build_workflow.png\n:target: ../_images/build_workflow.png\n```\n\n3. Download the artifact, which is just a zip file with a nightly build. You must be logged in to github to download it.\n\n```{image} images/build_artifact.png\n:target: ../_images/build_artifact.png\n```\n\nIf have trouble with the unsigned macOS released builds or wish to contribute, learn how to [Build from Source](building_from_source.md) on your own system.\n\n\n## PR Builds\n\nYou can also test builds of PRs. Instead of specifying the `main` branch above, select the branch listed at the top of the PR. Or click the `Checks` tab, then `Build All` to see the summary page, which has the artifact.\n"
  },
  {
    "path": "doc/docs/occlusion_culling.md",
    "content": "Occlusion Culling\n===================\n\nOcclusion culling allows the renderer to hide objects that are behind the terrain. See the example below. For more information about occlusion culling in Godot, see [the official docs](https://docs.godotengine.org/en/stable/tutorials/3d/occlusion_culling.html).\n\n<figure class=\"video_container\">\n <video width=\"600px\" controls=\"true\" allowfullscreen=\"true\">\n <source src=\"../_static/video/oc_demo.mp4\" type=\"video/mp4\">\n </video>\n</figure>\n\n## Baking Terrain Occlusion\n\nFirst, enable `use_occlusion_culling` in the project settings. \nThen in the editor:\n\n* Select Terrain3D.\n* Click `Terrain3D`, then `Bake Occluder3D` in the menu above the viewport. \n* On the popup window accept the default, LOD 4.\n\n```{image} images/terrain3d_menu.png\n:target: ../_images/terrain3d_menu.png\n```\n\n* Select the OccluderInstance3D child node.\n* In the inspector, click the arrow to the right of the ArrayOccluder resource and choose save. Save the file as a binary `.occ`.\n\n```{image} images/oc_save.png\n:target: ../_images/oc_save.png\n```\n\n### More Information\nThe LOD value determines the granularity of the occlusion mesh, and therefore the number of vertices used. Baking an occluder at a lower level of detail (higher number) will reduce opportunities for culling, but make occlusion testing quicker.\n\nBaking pauses the editor for about 5 seconds per region at LOD4. It has to read every pixel on the height map once to make sure that the generated occluder doesn't extend above or outside the clipmap (at any level of detail).\n\nAfter baking completes, an OccluderInstance3D node is created as a child of the Terrain3D node with an Occluder3D resource in it containing the baked occlusion mesh.\n\nThe generated Occluder3D resource can be quite large. It's more efficient to store this in binary format than text format, so you should always save this resource to a `.occ` file after it has been baked.\n\nThe occluder has to be manually baked again each time the terrain is altered.\n\n## Baking Occlusion For An Entire Scene\n\nGodot has a built-in tool for baking occlusion for all meshes. It is visible when you add an OccluderInstance3D to the scene tree and select it.\n\n```{image} images/oc_oc_menu.png\n:target: ../_images/oc_oc_menu.png\n```\n\nThis tool doesn't know about Terrain3D, so it will bake all MeshInstances and ignore Terrain3D. To get a complete bake, you will need to use our menu to bake the terrain occluder, then add a separate OccluderInstance3D to your scene and bake all of your other meshes.\n"
  },
  {
    "path": "doc/docs/platforms.md",
    "content": "Supported Platforms\n=========================\n\nThis page documents the status of various platforms and renderers supported by Godot.\n\n## Table of Contents\n\n**Operating Systems**\n* [Windows](#windows)\n* [Linux](#linux)\n* [macOS](#macos)\n* [IOS](#ios)\n* [Android](#android)\n* [Steam Deck](#steam-deck)\n* [HTML / WebGL](#webgl)\n\n**Renderers**\n* [Forward+ / Vulkan](#vulkan)\n* [Forward+ / Direct3D 12](#d3d12)\n* [Forward+ / Metal](#metal)\n* [Mobile / Vulkan](#mobile)\n* [Compatibility / OpenGLES 3](#compatibility)\n\n## Windows\n\nFully supported. See [renderers](#supported-renderers).\n\n## Linux\n\nFully supported. See [renderers](#supported-renderers).\n\n## macOS\n\nGodot and Terrain3D work fine on macOS, however Apple security is overly aggressive when using our release binaries.\n\nUsers have reported errors like this:\n\n`\"libterrain.macos.debug\" cannot be opened because the developer cannot be verified. macOS cannot verify that this app is free from malware.`\n\nRunning the following commands within the downloaded and unzipped directory appears to resolve the issue.\n\n```\n$ xattr -dr com.apple.quarantine addons/terrain_3d/bin/libterrain.macos.debug.framework/libterrain.macos.debug\n$ xattr -dr com.apple.quarantine addons/terrain_3d/bin/libterrain.macos.release.framework/libterrain.macos.release\n```\n\nYou can also [read comments and workarounds](https://github.com/TokisanGames/Terrain3D/issues/227)\nfrom other users. \n\nIf bypassing Apple security is not working, or if approaching a release date, macOS users should [build from source](building_from_source.md) so you can sign the binaries with your own developer account.\n\n\n## IOS\n\nAs of Terrain3D 0.9.1 and Godot 4.2, iOS is reported to work with the following setup:\n\n* Use textures that Godot imports (converts) such as PNG or TGA, not DDS.\n* Enable `Project Settings/Rendering/Textures/VRAM Compression/Import ETC2 ASTC`.\n* Set `Project Settings/Application/Config/Icon` to a valid file (eg `res://icon.png` or svg).\n* The Terrain3D release includes iOS builds, however they aren't signed and may not work.\n* If needed, build the iOS library and make sure the binaries are placed where identified in `terrain.gdextension`:\n```\n     scons platform=ios target=template_debug\n     scons platform=ios target=template_release\n```\n\n* Select `Project/Export`, Add the iOS export preset and configure with `App Store Team ID` and `Bundle Identifier`, then export.\n\n```{image} images/ios_export.png\n:target: ../_images/ios_export.png\n```\n\nOnce it has been exported, you can open it in XCode, run locally, or on your device.\n\nFurther reading:\n* [Issue 218](https://github.com/TokisanGames/Terrain3D/issues/218)\n* [PR 219](https://github.com/TokisanGames/Terrain3D/pull/219)\n* [PR 295](https://github.com/TokisanGames/Terrain3D/pull/295)\n\n\n## Android\n\nAs of Terrain3D 0.9.1 and Godot 4.2, Android is reported to work. It is still a bit experimental.\n\n* Use textures that Godot imports (converts) such as PNG or TGA, not DDS.\n* Enable `Project Settings/Rendering/Textures/VRAM Compression/Import ETC2 ASTC`.\n\nThe release builds include binaries for arm32 and arm64.\n\nSome mobile devices appear to not fully support texture arrays. Or perhaps they need more testing of different texture formats. \n\nFurther reading:\n* [Issue 668](https://github.com/TokisanGames/Terrain3D/issues/668)\n* [Issue 137](https://github.com/TokisanGames/Terrain3D/issues/137)\n* [Issue 197](https://github.com/TokisanGames/Terrain3D/issues/197)\n\n\n## Steam Deck\n\nAs of Terrain3D v0.9.1 and Godot 4.2, the first generation Steam Deck is reported working, running the demo at 200+ fps.\n\nThe user got it working with the following:\n* Use SteamOS 3.5.7\n* Install `glibc` and `linux-api-headers` in addition to the standard Godot dependencies\n* [Build from source](building_from_source.md)\n\nFurther reading:\n* [Issue 220](https://github.com/TokisanGames/Terrain3D/issues/220#issuecomment-1837552459)\n\n\n## WebGL\n\nThe releases and nightly builds include a web build, but web exports are very experimental. We have had success on some platforms. See the progress and setup instructions in [Issue 502](https://github.com/TokisanGames/Terrain3D/issues/502).\n\n\nSupported Renderers\n====================\n\n* [Forward+ / Vulkan](#vulkan)\n* [Forward+ / Direct3D 12](#d3d12)\n* [Forward+ / Metal](#metal)\n* [Forwad Mobile](#mobile)\n* [Compatibility / OpenGLES 3](#compatibility)\n\n## Vulkan\n\nThe Forward+ Vulkan renderer is fully supported.\n\n## D3D12\n\nThe Forward+ Direct3D 12 support should be fully supported as of Godot 4.6.\n\n## Metal\n\nSupport for Apple's Metal for iOS and macOS was merged into Godot 4.4-dev1. No testing has been done, and Terrain3D support is unknown.\n\n## Mobile\n\nThe Forward Vulkan Mobile renderer is fully supported.\n\n\n## Compatibility\n\nThe OpenGLES 3.0 Compatibility renderer is fully supported since Terrain3D 1.0 and Godot 4.4. A small set of shader pre-processor statements are used to override fma() and dFdxCoarse(). This allows the shader to work with the compatibility renderer without intrusive changes.\n"
  },
  {
    "path": "doc/docs/press.md",
    "content": "# Press\n\nTerrain3D has been featured in the following media:\n\n| Organization | Published | Link 1 | Link 2|\n|---|---|---|---|\n| 80 Level | January 31, 2025 | [Dynamic Collision Mode](https://80.lv/articles/godot-engine-s-terrain3d-received-dynamic-collision-mode/) | [Tweet](https://x.com/80Level/status/1885274326930194553)\n| 80 Level | July 19, 2024 | [Paint w/ Texture Rotation & Scale](https://80.lv/articles/you-can-now-paint-texture-rotation-scale-with-godot-s-terrain3d/) | [Tweet](https://x.com/80Level/status/1814253864545042946)\n| Blips | Dec 26, 2023 | [Terrain System Enters Beta](https://blog.blips.fm/articles/terrain3d-a-terrain-system-for-godot-4-enters-beta-phase)\n| 80 Level | Dec 19, 2023 | [Terrain System Enters Beta](https://80.lv/articles/this-free-terrain-system-for-godot-engine-enters-beta/) | [Tweet](https://twitter.com/80Level/status/1736937052946543084)\n| 80 Level | July 27, 2023 | [Free Terrain For Godot](https://80.lv/articles/terrain3d-a-free-terrain-system-for-godot-engine/) | [Tweet](https://twitter.com/80Level/status/1684473704972177409)\n| Blips | July 24, 2023 | [New Terrain For Godot](https://blog.blips.fm/articles/terrain3d-a-new-terrain-system-for-godot-4)\n| Godot Engine | July 19, 2023 | [Godot 4.2dev1 & Terrain3D](https://godotengine.org/article/dev-snapshot-godot-4-2-dev-1/)\n| GameFromScratch | July 18, 2023 | [New Terrain Engine for Godot (video)](https://www.youtube.com/watch?v=NwJEXOglBrQ) | [Article](https://gamefromscratch.com/terrain3d-a-new-terrain-engine-for-godot/)\n\n\n\t\n\t\n\n"
  },
  {
    "path": "doc/docs/programming_languages.rst",
    "content": "Programming Languages\n=====================\n\nAny language Godot supports should be able to work with Terrain3D via\nthe GDExtension interface. This includes\n`C# <https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/index.html>`__,\nand `several\nothers <https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/what_is_gdextension.html#supported-languages>`__.\n\nHere are some tips for integrating with Terrain3D.\n\n.. image:: images/integrating_gdextension.jpg\n   :target: ../_images/integrating_gdextension.jpg\n\nDetecting If Terrain3D Is Installed\n-----------------------------------\n\nTo determine if Terrain3D is installed and active, `ask\nGodot <https://docs.godotengine.org/en/stable/classes/class_editorinterface.html#class-editorinterface-method-is-plugin-enabled>`__. This works only in the editor for tool scripts and editor plugins.\n\nC# might be different depending if you're using the generated bindings :doc:`Generating C# Bindings <generating_csharp_bindings>`.\n\n.. tabs::\n   .. tab:: GDScript\n        .. code:: gdscript\n\n            print(\"Terrain3D enabled: \", EditorInterface.is_plugin_enabled(\"terrain_3d\"))\n\n   .. tab:: C#\n        .. code:: c#\n\n            GD.Print(\"Terrain3D enabled: \", EditorInterface.Singleton.IsPluginEnabled(\"Terrain3D\"));\n\n   .. tab:: C# (Bindings)\n        .. code:: c#\n\n            using TokisanGames;\n            ...\n            GD.Print(\"Terrain3D enabled: \", EditorInterface.Singleton.IsPluginEnabled(nameof(Terrain3D)));\n\n\nYou can also ask ClassDB if the class exists:\n\n.. tabs::\n   .. tab:: GDScript\n        .. code:: gdscript\n\n            ClassDB.class_exists(\"Terrain3D\")\n            ClassDB.can_instantiate(\"Terrain3D\")\n\n   .. tab:: C#\n        .. code:: c#\n\n            ClassDB.ClassExists(\"Terrain3D\");\n            ClassDB.CanInstantiate(\"Terrain3D\");\n\n   .. tab:: C# (Bindings)\n        .. code:: c#\n\n            using TokisanGames;\n            ...\n            ClassDB.ClassExists(nameof(Terrain3D));\n            ClassDB.CanInstantiate(nameof(Terrain3D));\n\n\nInstantiating & Calling Terrain3D\n---------------------------------\n\nTerrain3D is instantiated and referenced like any other object.\n\nSee the ``CodeGeneratedDemo.tscn`` or `CodeGeneratedCSDemo.tscn` demos for examples of initiating\nTerrain3D from script.\n\n.. tabs::\n   .. tab:: GDScript\n        .. code:: gdscript\n\n            var terrain: Terrain3D = Terrain3D.new()\n            print(terrain.get_version())\n            terrain.assets = Terrain3DAssets.new()\n\n   .. tab:: C#\n        .. code:: c#\n\n            var terrain = ClassDB.Instantiate(\"Terrain3D\");\n            GD.Print(\"Terrain3D version: \", terrain.AsGodotObject().Call(\"get_version\"));\n            terrain.AsGodotObject().Set(\"assets\", ClassDB.Instantiate(\"Terrain3DAssets\"));\n\n   .. tab:: C# (Bindings)\n        .. code:: c#\n\n            using TokisanGames;\n            ...\n            Terrain3D terrain = Terrain3D.Instantiate();\n            GD.Print(\"Terrain3D version: \", terrain.Version);\n            terrain.Assets = Terrain3DAssets.Instantiate();\n\nYou can also check if a node is a Terrain3D object:\n\n.. tabs::\n   .. tab:: GDScript\n        .. code:: gdscript\n\n            if node is Terrain3D:\n\n   .. tab:: C#\n        .. code:: c#\n\n            if (myNode.IsClass(\"Terrain3D\")) {\n\n   .. tab:: C# (Bindings)\n        .. code:: c#\n\n            using TokisanGames;\n            ...\n            if (myNode.IsClass(nameof(Terrain3D))) {\n\n\nFor more information on C# and other languages, read `Cross-language\nscripting <https://docs.godotengine.org/en/stable/tutorials/scripting/cross_language_scripting.html>`__\nin the Godot docs.\n\nFinding the Terrain3D Instance\n------------------------------\n\nThese options are for programming scenarios where a user action is\nintented to provide your code with the Terrain3D instance.\n\n-  If collision is enabled in game (default) or in the editor (debug\n   only), you can run a raycast and if it hits, it will return a\n   ``Terrain3D`` object. See more in the\n   `raycasting <collision.md#physics-based-collision-raycasting>`__\n   section.\n\n-  Your script can provide a NodePath and allow the user to select their\n   Terrain3D node.\n\n-  You can search the current scene tree for `nodes of\n   type <https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-method-find-children>`__\n   “Terrain3D”.\n\n.. tabs::\n   .. tab:: GDScript\n        .. code:: gdscript\n\n            var terrain: Terrain3D # or Node if you aren't sure if it's installed\n            if Engine.is_editor_hint(): # In editor\n                terrain = get_tree().get_edited_scene_root().find_children(\"*\", \"Terrain3D\").front()\n            else: # In game\n                terrain = get_tree().get_current_scene().find_children(\"*\", \"Terrain3D\").front()\n            if terrain:\n                print(\"Found terrain\")\n\n   .. tab:: C# (Bindings)\n        .. code:: c#\n\n            using System.Linq;\n            using TokisanGames;\n            ...\n            Terrain3D terrain;\n            Node terrainNode;\n            if (Engine.IsEditorHint())\n                terrainNode = GetTree().GetEditedSceneRoot().FindChildren(\"*\", nameof(Terrain3D)).FirstOrDefault();\n            else\n                terrainNode = GetTree().GetCurrentScene().FindChildren(\"*\", nameof(Terrain3D)).FirstOrDefault();\n            if (terrainNode != null)\n            {\n                terrain = Terrain3D.Bind(terrainNode);\n                GD.Print(\"Found terrain: \", terrain);\n            }\n\nDetecting Terrain Height\n------------------------\n\nSee `Collision <collision.md>`__ for several methods.\n\nGetting Updates on Terrain Changes\n----------------------------------\n\n``Terrain3DData`` has\n`signals <../api/class_terrain3ddata.rst#signals>`__ that fire when\nupdates occur. You can connect to them to receive updates.\n"
  },
  {
    "path": "doc/docs/shader_design.md",
    "content": "Shader Design\n==============\n\nOur shader combines a lot of ideas and code from [cdxntchou's IndexMapTerrain](https://github.com/cdxntchou/IndexMapTerrain) for Unity, [Zylann's HTerrain](https://github.com/Zylann/godot_heightmap_plugin/) for Godot, the Witcher 3 talk linked in the System Design page, and our own thoughts and optimizations.\n\nIn the material, you can enable `shader_override_enabled` with an empty `shader overide` slot and it will generate the default shader code so you can follow along with this document. You can also find the minimum shader needed to enable the terrain height functionality without texturing in `addons/terrain_3D/extras/shaders/minimum.gdshader`.\n\nAt its core, the current texture painting and rendering system is a vertex painter, not a pixel painter. We paint codes at each vertex, 1m apart by default, represented as a pixel on the [Control map](controlmap_format.md). The shader uses its many parameters to control how each pixel between the vertices blend together. For an artist, it's not as nice to use as a multi-layer, pixel based painter you might find in Photoshop, but the dynamic nature of the system does afford other benefits.\n\nThe following describes the various elements of the shader in a linear fashion to help you understand how it works.\n\n\n## Texture Lookup Methods\n\nFirst some terminology and notes about the various methods used to retreive a texture value. \n\nA `pixel` is a colored dot on your screen (aka `picture element`). A `texel` is a colored dot on a texture in memory (aka a `texture pixel`). When a grey value is read from a rock texture, it's a texel. When it is projected on a rock mesh with lighting and rendered on your screen, it's a pixel.\n\nThe GPU does a lot of work for gamedevs when using the standard lookup function `texture()`, such as calculating which mipmap level to use and automatically interpolating surrounding texels. A lot of this work we don't want done automatically and instead do it ourselves so we can optimize or reuse some of the process. \n\nHere's a quick summary of potential operations we might use to retreive a texel:\n\n* `texture()` - We provide UVs. The GPU calculates UV derivatives and mipmap LOD, then returns an interpolated value.\n* `textureGrad()` - We provide UV derivatives. The GPU calculates mipmap LOD and returns an interpolated value.\n* `textureLod()` - We provide UVs and mipmap LOD. The GPU returns an interpolated value.\n* `texelFetch()` - We provide UVs and mipmap LOD. The GPU returns the texel.\n\n`texture*()` functions interpolate from multiple samples of the texture map if linear filtering is enabled. Using either nearest filtering or `texelFetch()` disables interpolation.\n\n\n## Uniforms\n\n[Terrain3DMaterial](../api/class_terrain3dmaterial.rst) exposes uniforms found in the shader, including any you have added. Uniforms that begin with `_` are considered private and are hidden, but you can still access them via code. See [Tips](tips_technical.md#accessing-private-shader-variables).\n\nThese notable [Terrain3DData](../api/class_terrain3ddata.rst) arrays are passed in as uniforms. The API has more information on each.\n* [_region_map](../api/class_terrain3ddata.rst#class-terrain3ddata-method-get-region-map), [_region_locations](../api/class_terrain3ddata.rst#class-terrain3ddata-property-region-locations) store the location and ID of each region\n* [_height_maps](../api/class_terrain3ddata.rst#class-terrain3ddata-property-height-maps), [_control_maps](../api/class_terrain3ddata.rst#class-terrain3ddata-property-control-maps), and [_color_maps](../api/class_terrain3ddata.rst#class-terrain3ddata-property-color-maps) store the elevation, texture layout, and colors of the terrain, indexed by region ID\n* [_texture_array_albedo](../api/class_terrain3dassets.rst#class-terrain3dassets-method-get-albedo-array-rid), [_texture_array_normal](../api/class_terrain3dassets.rst#class-terrain3dassets-method-get-normal-array-rid) store the ground textures, indexed by texture ID\n\n\n## Vertex() & Supporting Functions\n\nThe CPU has already created flat mesh components that make up the clipmap mesh, and collision shapes with heights. The vertex shader adjusts the mesh to match the collision shape defined by the heightmap. `vertex()` is run for every vertex on these mesh components.\n\nNoteworthy supporting functions include `get_index_coord()` and `get_index_uv` which take in world space XZ coordinates and return region coordinates, either real or normalized. They also return the region ID, which indexes into the texture arrays.\n\nWithin `vertex()`, the controlmap is read to determine if a vertex is a hole, the heightmap is read if valid, and if world noise should be calculated. The values are accumulated to determine the final height, and vertex normal.\n\nIf the optional world noise is enabled, it generates fractal brownian noise which can be used as background hills outside of your regions. It's a visual only effect, can be costly at high octaves, and does not generate collision.\n\nAs `render_mode skip_vertex_transform` is used, we apply the necessary matrix transforms to set the final `VERTEX` position, matching the collision mesh.\n\n\n## Fragment()\n\n`fragment()` is run for every screen pixel in which the terrain mesh appears on screen. This is many more times than the number of vertices.\n\n\n### Grid Offsets, Weights and Derivatives\n\nFeatures like UV rotation, UV scale, detiling, and projection break the continuity of texture UVs. So we must use `textureGrad()` and provide the derivatives for it. We take 1 set of `dfdx(uv)` and `dFdy(uv)` saved in `base_derivatives` and then scale them as needed.\n\nThe lookup grid and blend weights are initially calculated here, as they are used for both normals, and material lookups. \n\nTo see the grid, add this at the end of the shader `ALBEDO *= vec3(round(weight), 0.0) + .5;` which shows horizontal stripes in red, and vertical stripes in green. The inverse is stored in `invert`, so where horizontal stripes alternate `red, black, red`, this has `black, red, black`. You can see the vertices if you enable `Debug Views / Vertex Grid`, as shown.\n\n```{image} images/sh_mirror.png\n:target: ../_images/sh_mirror.png\n```\n\nA determination is made with the base derivatives, of whether it is reasonable to skip all additional lookups required to do the bilinear blend. Skipping this can save a significant amount of bandwidth and processing for the GPU depending on how much of the screen is occupied by distant terrain. It's worth noting that as this is calculated from screen space derivatives, it is independent of screen resolution.\n\n\n### Normal Calculation\n\nThe next step is calculating the terrain normals. Clipmap terrain vertices are farther apart at lower LODs, causing certain things like normals to look strange when viewed in the distance. Because of this, we calculate normals by taking derivatives from the heightmap in `fragment()`.\n\nWe use `texelFetch()` to read the height values on each vertex without any automatic interpolation. These values are used to generate a set of normals per-index, and an interpolated value for smooth normals. Using `texture()` here would not only trigger many additional lookups of adjacent vertices for interpolation, but also create artifacts when interpolating across region boundaries.\n\nGenerating normals in the shader works fine, and modern GPUs can easily handle the load of the additional height lookups and the on-the-fly calculations. Doing this saves 3-4MB VRAM per region (sized at 1024) instead of pre-generating a normal map and passing it to the shader.\n\n\n### Material Creation\n\nThe control maps are queried for each of the 4 adjacent grid points (aka vertices and indices) and stored in `control[0]`-`control[3]`.\n\nThe control map bits, are decoded when needed, as defined in [Controlmap Format](controlmap_format.md).\n\nIf the Autoshader is enabled, the control_data for each of the 4 grid points is overwritten with the autoshader ids and blend value calculated from the terrain normal and height.\n\nWe then iterate over the point control_data to calculate the `texture_weight` of each `texture_id`. This takes into account how many points have a given texture, and the bilinear weight. Eg if an ID is present at only 3 of 4 index points, then its weight would be:\n\n`weights[0] * blend_weight[0] + weights[2] * blend_weight[2] + weights[3] * blend_weight[3]`\n\nThis allows smooth interpolation and better blend shaping across the points during the material accumulation.\n\nThe textures at each vertex are looked up and accumulated, the weight for each lookup is modified by the texture height value, sharpness, and potentially the world normal. The world space normal blend adjustment takes the normal map value of the texture in the base layer and uses it to modify the blend weight of the overlay layer of the same point. \n\nWhere possible, texture lookups are branched and in some cases only 2 samples are required, bringing VRAM bandwith requirements to a minimum.\n\n\n### Texture Sampling - Splat Map vs Index Map\n\nThis analysis compares the *splat map* method used by many other terrain systems with an *index map* method, used by Terrain3D and [cdxntchou's IndexMapTerrain](https://github.com/cdxntchou/IndexMapTerrain).\n\nAt their core, all height map based terrain tools are just fancy painting applications. For texturing, the \"reasonable\" approach would be to define a strength value for each texture ID at each pixel and blend them together as occurs when painting in Photoshop. Subtly brushing with red gradually increases the R in the RGB value of the pixel. This is how a splat map works, but instead of painting with just RGBA, it paints with RGBACDEFHIJKLMNO (for 16 textures). The \"unreasonable\" approach would be to use an entirely different methodology in order to reduce memory or increase speed.\n\nThe **Splat map approach** specifies an 8-bit strength value for each texture. 16 textures fits into 4 splat maps each made up of 32-bit RGBA values, for a total of 16 bytes. Double for 32 textures.  \n\nWhen rendering, all splat maps are sampled, and of the 16-32 values, the 4 strongest are blended together, per terrain pixel. The blending of textures for pixels drawn between vertices is handled by the GPU's linear interpolated texture filter during texture lookups.\n\nThe **Index map approach** samples a control map at 4 fixed grid points surrounding the current terrain pixel. The 4 surrounding samples have two texture IDs, and a blending value. The texture values range from 0-31, each stored in 5 bits. The blend value is stored in 8-bits.\n\nThe position of the pixel within its grid square is used to bilinearly interpolate the values of the 4 surrounding samples. We disable the default GPU interpolation on texture lookups and interpolate ourselves here. At distances where the the bilinear blend would occur across only 1 pixel in sceen space, the bilinear interpolation is skipped, requiring only 1/4 of the normal samples.\n\n**Comparing the two methods:**\n\n* **Texture lookups** - Considering only lookups for which ground texture to use and loading the texture data:\n  * Splat maps use 12-16 lookups per pixel depending on 16 or 32 textures:\n    * 4-8 to get the 4 strongest texture IDs. 4 for 16 textures, 8 for 32. This retreives the texture ID for the closest vertex point.\n    * 8 for the strongest 4 albedo_height textures and the 4 normal_rough textures\n  * Terrain3D uses 5-20 lookups per pixel depending on terrain distance:\n    * 1-4 for the surrounding 4 grid points on the control map.\n    * 4-16 for the 2-4 albedo_height & normal_rough for the base and overlay textures, for each of the 4 grid points.\n\n* **VRAM consumed** \n  * Splat maps store 16 texture strength values in 16 bytes per pixel, or 32 in 32 bytes per pixel. On a 4096k terrain with 16M pixels, splat maps consume 256MB for 16 textures, 512MB for 32.\n  * Terrain3D stores 32 texture strengths in 18-bits. 5-bit base ID, 5-bit overlay ID, 8-bit blend value. We can store texture layout for 32 textures on a 4k terrain in only 36MB, for a 93% reduction in VRAM. \n \n\nThe calculations above consider only the portion of lookups and VRAM used by the data that defines where textures are place on the terrain. In practical use there are many other features that greatly adjust both.\n\nAs for usage of the two techniques:\n\n* Splat maps - 4 textures can be blended intuitively as one would paint in Photoshop. Some systems might introduce artifacts when 3-4 textures are blended in an area.\n\n* Terrain3D - Only 2 textures can stored in a vertex. However pixels are interpolated between the 4 adjacent vertices, so can easily blend between up to 4 textures based on painted blend value, height textures, and material settings. Thus getting a natural looking blend is easily doable if textures are properly setup with heights, using [the right technique](texture_painting.md#manual-painting-technique).\n\n\n### Calculating Weights & Applying PBR\n\nSince each terrain pixel exists within four points on a grid, we can use bilinear interpolation to calculate weights based on how close we are to each grid point. e.g. The current pixel is 75% to the next X and 33% to the next Y, which gives us a weighted strength for the texture values from each adjacent point. We lookup the 4 adjacent textures, take the weighted average, and apply height blending to calculate our final value.\n\n\nThe color map and macro variation are multiplied onto the albedo channel. Then all PBR values are sent to the GPU.\n\n"
  },
  {
    "path": "doc/docs/system_architecture.md",
    "content": "System Architecture\n=====================\n\n## Geometry Clipmap Terrain\nSome other terrain systems generate a grid of mesh chunks. As the camera moves forward, old meshes far behind are destroyed and new meshes in front are created. Our approach is different.\n\nLike The Witcher 3, this system uses a geometry clipmap, where the mesh components are generated once, and at periodic intervals are centered on the camera location. On each update, the vertex heights of the mesh components are adjusted by the GPU vertex shader reading from the terrain heightmap. Levels of Detail (LODs) are built into the mesh generated on startup, so don't require any additional consideration once placed. Lower detail levels are automatically placed far away once all mesh components are recentered on the camera. See Mike Savage's excellent blog below for visual examples and further explanations on the technique.\n\nWe provide a system where one can allocate regions for sculpting and texturing and only pay the VRAM and storage costs for only the areas used. Think of a world that takes up 16k x 16k, but has multiple small islands. Rather than pay for 16k, our system requires only allocates memory for the regions that contain the islands. The space in between can be flat, hidden, or have collision-less shader generated noise.\n\n\n### Reference Material\n* Mike J. Savage: [Geometry clipmaps: simple terrain rendering with level of detail](https://mikejsavage.co.uk/blog/geometry-clipmaps.html). Earlier versions of Terrain3D used Mike's implementation. Blog and repository code released under the MIT license, clarified per email communication with Mike. \n\n* NVidia GPU Gems 2: [Terrain Rendering Using GPU-Based Geometry Clipmaps](https://developer.nvidia.com/gpugems/gpugems2/part-i-geometric-complexity/chapter-2-terrain-rendering-using-gpu-based-geometry)\n\n* GDC 2014: [The Witcher 3 Clipmap Terrain and Texturing](https://archive.org/details/GDC2014Gollent) - [Slides](https://ubm-twvideo01.s3.amazonaws.com/o1/vault/GDC2014/Presentations/Gollent_Marcin_Landscape_Creation_and.pdf)\n\n## Architecture\nHere is a diagram showing what the classes do and how they communicate.\n\n```{image} images/sa_uml.png\n:target: ../_images/sa_uml.png\n```\n\n## Architectural Design Principles\n\n### 1 Pixel == 1 Vertex\n\nCurrently, we maintain a constant ratio where 1 pixel on height, control, and color maps correlates to 1 world vertex, on LOD0. \n\nWe provide variable region sizes which limit the number of vertices allocated, and `Terrain3D.vertex_spacing` to allow devs to spread out or condense the spacing between vertices for higher and lower poly worlds. However this 1 pixel == 1 vertex principle is maintained. With a vertex scaling of 2.0, a 1024px^2 map represents a 2048m^2 world, using a 1024 region size.\n\n### Global Positions are Absolute\n\nMany functions in the API receive a global position. Before `vertex_spacing`, there was an easy translation to regions, vertex positions, and image coordinates. Now that the user can laterally scale the terrain, landscape features like mountain peaks change their global position, this introduced a design challenge.\n\nTo make this managable, we've adopted the principle that all functions in the API that take a `global_position` parameter expect an absolute global position from the users perspective. That position is generally descaled for operations internally in local or image coordinates. If a function calls other functions, it will need to send the global position. Care will need to be taken by devs to ensure the descaled and global positions are used at the right time.\n"
  },
  {
    "path": "doc/docs/texture_painting.md",
    "content": "Texturing the Terrain\n=========================\n\n## Texture List\n\nOnce your texture files have been prepared, this document describes how to use them to texture your terrain.\n\n\n### Adding a Texture Set\n1. Once you've [created your textures](texture_prep.md), place them in your Godot project folder.\n2. Set the appropriate Import settings for them as defined in [compression formats](texture_prep.md#compression-format).\n3. Make a new texture slot in the `Textures` section of the [Asset Dock](user_interface.md#asset-dock) by clicking `Add New`. \n4. Drag your texture file for albedo+height from the `FileSystem` panel into the albedo slot. Drag your normal+roughness texture into the normal slot. \n5. In the inspector, name the texture and adjust the other settings as needed.\n\n\n### Managing the Texture List\n* Unused texture slots take up memory with the default generated textures. Remove unused slots.\n* Right-click any texture slot in the Asset Dock to bring it into edit mode.\n* Middle-click any texture slot to clear or delete it. You can only delete the last texture in the list.\n* Reorder textures by changing the texture id. This will change the texture rendered in the viewport as it does not change the values painted on the control map. In the future we'll add image processing tools that will allow changing texture ids painted on the terrain.\n\n\n## Texture Painting\n\nTextures are painted on to the terrain in three values: a base texture ID, an overlay texture ID, and a blending value. Our shader then determines the best way considering a variety of factors including the height texture from each set, to determine which texture to show. In areas where autoshading has been enabled, the painted textures are ignored.\n\nThe general idea for texturing the terrain is to enable the autoshader to automatically texture the terrain in most areas, then manually paint textures only where you need it.\n\nThere are a handful of tools to familiarize yourself with:\n* The toolbar has `Paint Texture`, `Spray Texture`, and `Autoshader` tools to paint where the terrain is manually or automatically textured.\n* The material has an option to enable or disable the autoshader for the whole terrain.\n* The `Debug Views/Autoshader` displays where the terrain is automatically or manually textured.\n\n\n### Manual Painting Technique\n\nPainting with natural mixing is easy to do as long as your texture sets have height textures, they are quality textures, and you use the correct technique as follows.\n\n* Use the `Paint Texture` tool to cover large sections with a single texture. This tool sets the blend value to `0.0` and both base and overlay IDs to the selected texture.\n* Start Painting your terrain in large sections with this tool.\n* You can and should Paint similar but different textures in an area for a natural variety. e.g. gravel and dirt; mud, dirt, and rocks. Do what blending you can with Paint first.\n* Use the `Spray Texture` tool to blend the edges of the Paint work, or lightly in the center of the Painted areas to give it a natural look. This gradually increases the weight of the selected texture. See the [keyboard shortcuts](keyboard_shortcuts.md) for options you can do while Spraying.\n* Example: Use the Paint tool for both a grass field and a dirt pathway. Then use the Spray tool and repeatedly switch between grass and dirt to blend the edges of the path randomly until it looks realistic.\n* Use the [control texture](../api/class_terrain3dmaterial.rst#class-terrain3dmaterial-property-show-control-texture) and [control blend](../api/class_terrain3dmaterial.rst#class-terrain3dmaterial-property-show-control-blend) debug views to understand how your textures are painted and blended. \n\n\n### Autoshading\nNew regions are set to enable the autoshader by default. If you started using Terrain3D before the autoshader was added, all of your regions are set to manual shading. To enable it:\n* In the material, enable the autoshader.\n* Specify the base and overlay texture IDs the autoshader should use.\n* Use the `Autoshader` tool and paint over the areas you want to be autoshaded.\n\nEnabling the autoshader will not change your manually painted textures. In autoshaded regions, the shader will ignore your manual painting, but it's still there and will be visible any time you disable the autoshader in the material or by painting.\n\n\n### Mixing Manual Painting & the Autoshader\nSince the paint brushes will disable the autoshader, it's easy to make artifacts appear without the right process. This technique will allow you to achieve seamless painting using both the autoshader and manual painting. \n\nLet's say we want to paint a pathway through grass, surrounded by autoshaded hills:\n\n* Find a flat or evenly sloped area with a uniform texture. Avoid corners where the autoshader transitions between textures. In our example, find a flat grassy area.\n* Use the `Paint Texture` tool to paint the same grass texture the autoshader is using in the flat area. The changes won't be visible unless you disable the autoshader in the material, or enable the autoshader debug view. But you will be simultanously disabling the painted autoshader, and painting the same texture.\n* Use the same tool to paint a pathway texture.\n* Use the `Spray Texture` tool to blend the edges.\n\nYou can see the manual painting technique is the same as above. The key step here is invisibly carving out a manually painted section by laying down a base texture the same as the autoshader. If you go too far out with the manual painting, you can use the `Autoshader` tool to bring it back in.\n\n\n## Painting Angle & Scale\n\nBoth the `Paint Base Texture` and `Spray Overlay Texture` tools have Angle and Scale modifiers, which allow painting textures at different angles and scales. This can be useful for creating paths, water embankments, and generally having textures more closely follow terrain features.\n\nThese tools have pickers allowing you to select current values from the terrain. Angle also has a `Dynamic` mode which causes the angle to change based upon mouse movement while painting. This ignores any value set by the slider.\n\nPaint/Spray brushes have toggleable options for Texture, Angle, and Scale, meaning they can be independently applied. This allows you to disable Texture and Scale in order to repaint an area using only Angle modification. This will change texture rotation without affecting the other parameters.\n\nYou can paint Angle and Scale on top of the autoshader.\n\n\n## Color Painting\n\nIn addition to painting textures, you can also paint colors on the terrain. There are two primary uses for the colormap.\n\n\n### 1. GIS Applications\n\nYou can import a full image such as a satellite photo, and enable the color map debug view for GIS visualization.\n\n```{image} images/gis.png\n:target: ../_images/gis.png\n```\n\n\n### 2. Color Variation\n\nYou can use the `Paint Color` tool to paint colors on the terrain. This is useful to add variation. Colors are multiplied on to painted textures, which is a blend mode that only darkens.\n\nTry painting your terrain with subtle light grays, greens and browns to add depth, contours, and variation. Subtlety is key.\n\nPaint white to reset.\n\nYou can paint with the colormap on top of the autoshader. You can also use the picker to select a colormap value from the terrain.\n\n\n## Painting Wetness\n\nUse the `Paint Wetness` tool to modify the roughness of the textures. Reduce the roughness percentage to say -30% and wherever you paint the textures will become more glossy.\n\nIf you wish to turn dirt into mud, try painting a light/medium grey on the colormap to darken it, then paint a -30% on the wetness.\n\nPaint 0 to reset.\n\nYou can paint wetness on top of the autoshader. You can also use the picker to select a wetness value from the terrain.\n"
  },
  {
    "path": "doc/docs/texture_prep.md",
    "content": "﻿Preparing Textures\n=========================\n\nTerrain3D supports up to 32 texture sets using albedo, height, normal, and roughness textures, each set are channel packed into 2 files. This page describes everything you need to know to prepare your texture files. Continue on to [Texture Painting](texture_painting.md) to learn how to use them.\n\n**Table of Contents**\n* [Texture Requirements](#texture-requirements)\n* [Texture Content](#texture-content)\n* [Channel Pack Textures in Terrain3D](#channel-pack-textures-in-terrain3d)\n* [Channel Pack Textures with Gimp](#channel-pack-textures-with-gimp)\n* [Where to Get Textures](#where-to-get-textures)\n* [Frequently Asked Questions](#faq)\n\n\n## Texture Requirements\n\n### Texture Files\n\nTypically \"a texture\" say rock comes as a pack of individual texture files: albedo/diffuse/base color, height, normal, smoothness/roughness, ambient occlusion (AO). For maximum efficiency we provide the option of packing these 5 separate files into 2.\n\nTerrain3D is designed for texture sets that are channel packed as follows:\n\n| Name | Format |\n| - | - |\n| albedo_texture | RGB: Albedo, A: Height\n| normal_texture| RGB: Normal map ([OpenGL](#normal-map-format)), RGB: AO, A: Roughness\n\nThe terrain can work without the height, normal, ao, or roughness maps. But then you won't have height blending, roughness, or the other features. That may be fine for a low-poly or stylized terrain, but not for a realistic one.\n\nTextures can be channel packed using the `Pack Textures...` option in the Terrain3D menu at the top of the viewport (recommended), or in [Gimp](https://www.gimp.org/). Photoshop or [Krita](https://krita.org/) are possible, but working with alpha channels can be a bit challenging.\n\n### Texture Sizes\n\nAll albedo textures must be the same size, and all normal textures must be the same size. Each type gets combined into separate Texture2DArrays, so their sizes of the two arrays can differ.\n\nDouble click any texture file and the inspector will show you the size. The demo textures are 1024x1024.\n\nFor GPU efficiency, it is recommended that all of your textures have dimensions that are a power of 2 (128, 256, 512, 1024, 2048, 4096, 8192), but this isn't required.\n\n### Compression Format\n\nAll albedo textures must be the same format, and all normal textures must be the same format. Albedo and Normals are combined into separate Texture2DArrays, so the two can have different formats.\n\nDouble-clicking a texture in the FileSystem panel will display it in the Inspector with the current converted format of the file, size, and mipmaps. Settings may be adjustable on the Godot Import tab.\n\n| Type | Supports | Format |\n| - | - | - |\n| **PNG** | Desktop, Mobile | RGBA, converts to DXT5 or BPTC (HQ). In Godot you must go to the Import tab and select: `Mode: VRAM Compressed`, `Normal Map: Disabled`, `Mipmaps Generate: On`, optionally check `High Quality`, then reimport each file. \n| **DDS** | Desktop | BC3 / DXT5, linear (intel plugin), Color + alpha, mipmaps generated. These files are used directly by Godot and are not converted, so there are no import settings.|\n| **Others** | | Other [Godot supported formats](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_images.html#supported-image-formats) like KTX, TGA, JPG, WEBP should work as long as you match similar settings to PNG.\n| **EXR** | | While EXRs can work, they store color data as 16/32-bit float, not 8-bit integer. Don't use them for terrain textures unless you know what you're doing.\n\nTo get the highest quality compression on desktop, use either:\n* Use PNG with the high quality option in Godot (BC6/BPTC). Pack with our channnel packer and mark HQ. Godot does not currently support importing anything higher than BC3/DXT5 in DDS files.\n* DDS (BC3/DXT5) made in Gimp are recommended over using the default PNG settings, which produces poor quality BC3/DXT5 files. When creating DDS files in Gimp you have a lot more conversion options, such as different mipmaps filtering algorithms which can be helpful to remove artifacts in reflections (eg try Mitchell). \n\nThe demo textures are PNG imported as HQ which are converted to BPTC.\n\nYou can create DDS files by:\n  * Exporting directly from Gimp\n  * Exporting from Photoshop with [Intel's DDS plugin](https://www.intel.com/content/www/us/en/developer/articles/tool/intel-texture-works-plugin.html)\n  * Converting RGBA PNGs using [NVidia's Texture Tools](https://developer.nvidia.com/nvidia-texture-tools-exporter)\n\nYou can create KTX files with [Khronos' KTX tools](https://github.com/KhronosGroup/KTX-Software/releases).\n\n## Texture Content\n\n### Seamless Textures\n\nMake sure you have seamless textures that can be repeated without an obvious seam.\n\n### Height Textures\n\nIf creating your own height textures, aim for a central point of grey (0.5) with bumps and divots above and below that value. Adjust the contrast so that troughs and peaks reach just about 0 and 1.\n\n### Normal Map Format\n\nNormal maps come in two formats: DirectX with -Y, and OpenGL with +Y. Both formats should be normalized.\n\nDirectX can be converted to OpenGL and vice versa by inverting the green channel in a photo editing app, or within our texture packing tool.\n\nThey can often be identified visually by whether bumps appear to stick out (OpenGL) or appear pushed in (DirectX). The sphere and pyramid on the left in the image below are the clearest examples. \n\n```{image} images/tex_normalmap.png\n:target: ../_images/tex_normalmap.png\n```\n\nNatural textures like rock or grass can be very difficult to tell. However if you get assets made for a certain engine like Unreal or Unity, you can generally assume their format and convert as needed. On occasion artists get it wrong though, so if the lighting looks off on your object, try inverting the normal map.\n\n| Software | DirectX | OpenGL |\n|----------|---------|--------|\n| 3DS Max\t\t\t\t| ✓ | |\n| Blender\t\t\t\t| | ✓ |\n| Cinema 4D\t\t\t\t| | ✓ |\n| CryEngine\t\t\t\t| ✓ | |\n| Godot Engine\t\t\t| | ✓ |\n| Houdini\t\t\t\t| | ✓ |\n| Marmoset Toolbag\t\t| | ✓ |\n| Maya\t\t\t\t\t| | ✓ |\n| Substance Painter\t\t| ✓ | |\n| Unity\t\t\t\t\t| | ✓ |\n| Unreal Engine\t\t\t| ✓ | |\n| Zbrush\t\t\t\t| | ✓ |\n\n### Roughness vs Smoothness\n\nSome \"roughness\" textures are actually smoothness or gloss textures. You can convert between them by inverting the image in an image editor, or in our texture packing tool.\n\nYou can tell which is which just by looking at distinctive textures and thinking about the material. If it's glass it should be glossy, so on a roughness texture values will be near 0 and the texture will appear mostly black. If it's dry rock or dirt, it should be mostly white, which is near 1 roughness. A smoothness texture would show the opposite.\n\n\n### Ambient Occlusion\n\nOur built in texture packing tool allows you to easily combine AO texture maps into your normal texture set. This is done by a clever technique of expecting your normal map is normalized, then scaling the vector by the AO value.\n\nAO maps are not required for any texture. You can even mix and match on different textures, unlike with sizes or formats. If you haven't included an AO texture, AO will be approximated from the normal map.\n\nIf you wish to apply AO to your textures manually or in another tool, ensure your normal map is normalized, then pack AO with: `unpacked_normal_vector * (sqrt(ao) * 0.5 + 0.5)`.\n\n\n## Channel Pack Textures in Terrain3D\n\nWe recommend you use our built in tool to pack textures.\n\n```{image} images/terrain3d_menu.png\n:target: ../_images/terrain3d_menu.png\n```\n\n1. At the top of your viewport, click the `Terrain3D` menu, then `Pack Textures`.\n2. Select your textures for albedo and height.\n3. Optionally, select textures for normal, roughness, and if desired ambient occlusion\n4. Optionally, convert a DirectX normal map to OpenGL, or smoothness to roughness map.\n5. Optionally, enable Orthogonalise normals if you see a reflective checkerboard pattern appear when using detiling.\n6. Click `Pack Textures As...` and save the resulting PNG files to disk.\n7. Go to the Import tab and one at a time, select your new PNG files, specify the following settings and click `reimport`. \n\t* `Mode: VRAM Compressed`\n\t* Optional: `High Quality: On` if you wish BPTC instead of DXT5.\n\t* `Normal Map: Disabled`\n\t* `Mipmaps Generate: On`\n\nMake sure to reimport both files. Double click each file in the filesystem and ensure the inspector reveals the expected format and size. All of the files you put in your texture list must match.\n\n\n## Channel Pack Textures with Gimp\n\n> Note: AO packing normals manually is complex and not reccomended. The formula used is: `unpacked_normal_vector * (sqrt(ao) * 0.5 + 0.5)`\n\n1. Open your RGB Albedo and greyscale Height files (or Normal and Roughness).\n\n2. On the RGB file select `Colors/Components/Decompose`. Select `RGB`. Keep `Decompose to layers` checked. On the resulting image you have three greyscale layers for RGB. \n\n3. Copy the greyscale Height (or Roughness) file and paste it as a new layer into this decomposed file. Name the new layer `alpha`.\n\nThis would be a good time to invert the green channel if you need to convert a Normalmap from DirectX to OpenGL, or to invert the alpha channel if you need to convert a smoothness texture to a roughness texture.\n\n4. Select `Colors/Components/Compose`. Select `RGBA` and ensure each named layer connects to the correct channel.\n\n5. Now export the file with the following settings. DDS is highly recommended. \n\nAlso recommended is to export directly into your Godot project folder. Then drag the files from the FileSystem panel into the appropriate texture slots. With this setup, you can make adjustments in Gimp and export again, and Godot will automatically update with any file changes.\n\n### Exporting As DDS\n* Change `Compression` to `BC3 / DXT5`\n* `Mipmaps` to `Generate Mipmaps`. \n* Optionally, change the `Mipmap Options` `Filter`, such as to Mitchell if you get reflection artifacts in your normal+roughness texture.\n* Insert into Godot and you're done.\n\n```{image} images/io_gimp_dds_export.png\n:target: ../_images/io_gimp_dds_export.png\n```\n\n### Exporting As PNG\n* Change `automatic pixel format` to `8bpc RGBA`. \n* In Godot you must go to the Import tab and select: `Mode: VRAM Compressed`, `Normal Map: Disabled`, `Mipmaps Generate: On`, then click `Reimport`.\n\n```{image} images/io_gimp_png_export.png\n:target: ../_images/io_gimp_png_export.png\n```\n\n## Where to Get Textures\n\n### Texture Creation Software\n\nYou can make textures in dedicated texture software, such as those below. There are many other tools and ai texture generators to be found online. You can also paint textures in applications like krita/gimp/photoshop.\n \n* [Materialize](http://boundingboxsoftware.com/materialize/) - Great free tool for generating missing maps. e.g. you only have an albedo texture that you love and want to generate a normal and height map\n* [Material Maker](https://www.materialmaker.org/) - Free, open source material maker made in Godot\n* [ArmorLab](https://armorpaint.org/) - Free & open source texture creator\n* Substance Designer - Commercial, \"industry standard\" texture maker\n\n\n### Download Textures\n\nThere are numerous websites where you can download high quality, royalty free textures for free or pay. These textures come as individual maps, with the expectation that you will download only the maps you need and then channel pack them. Here are just a few:\n\n* [PolyHaven](https://polyhaven.com/textures) - many free textures (Download PNG, not EXR)\n* [AmbientCG](https://ambientcg.com/) - many free textures\n* [Poliigon](https://www.poliigon.com/textures/free) - free and commercial\n* [GameTextures](https://gametextures.com/) - commercial\n* [Textures](https://www.textures.com/) - commercial\n\n## FAQ\n\n### Why do we have to channel pack textures? Why is this so difficult?\n\nYou don't have to. You can use just the albedo map, or also the normal map, without the others. However if you want a realistic terrain with height blending and roughness, you need all of the maps. You could have 5 different texture maps in memory, or pack that down to 2 maps and save precious VRAM.\n\nChannel packing is a very common task done by professional game developers. Every pro asset pack you've used has channel packed textures. When you download texture packs from websites, they provide individual textures so you can pack them how you want. They are not intended to be used individually!\n\nWe offer a built in `Pack Textures` tool, found in the Terrain3D menu at the top of the viewport that facilitates the texture creation process within Godot. Packing can be done in 30 seconds.\n\nFinally, we provide easy, 5-step instructions for packing textures with Gimp, which takes less than 2 minutes once you're familiar with the process. \n\nIf we want high performance games, we need to optimize our games for graphics hardware. A shader can retrieve four channels RGBA from a texture at once. Albedo and normal textures only have RGB. Thus, reading Alpha is free, and a waste if not used. So, we put height / roughness in the Alpha channel.\n\nWe could have the software let you specify individual maps and we pack textures for you at startup, however that would mean processing up to 160 images every time any scene with Terrain3D loads, both in the editor and running games. Exported games may not even work since Godot's image compression libraries only exist in the editor. The most reasonable path is for gamedevs to learn a simple process that they'll use for their entire career and use it to set up terrain textures one time.\n\n### What about Emissive, Metal, and other texture maps?\n\nMost terrain textures like grass, rock, and dirt do not need these. \n\nOccasional textures do need additional texture maps. Lava rock might need emissive, or rock with gold veins might need metallic, or some unique texture might need both. These are most likely only 1-2 textures out of the possible 32, so setting up these additional options for all textures is a waste of memory. You can add a [custom shader](tips_technical.md#add-a-custom-texture-map) to add the individual texture map.\n\n### Why not use Standard Godot materials?\n\nAll materials in Godot are just shaders. The standard shader is both overly complex, and inadequate for our needs. Dirt does not need SSS, refraction, or backlighting for instance. See [a more thorough explanation](https://github.com/TokisanGames/Terrain3D/issues/199).\n\n### What about displacement?\n\nGodot doesn't support texture displacement via tessellation or geometry shaders in the renderer. However, we provide the option of subdividing the terrain mesh, to allow textures to displace verteices. For further details see [Displacement](displacement.md).\n\nEffects like depth parallax or occlusion mapping etc require many samples in fragment, which can be prohibitivley expensive when applied to already complex terrain shaders. There are [alternatives](https://github.com/TokisanGames/Terrain3D/issues/175) that might prove useful in the future.\n\n### What about...\n\nWe provide a base texture with the most commonly needed terrain options. Then we provide the option for a custom shader so you can explore `what about` on your own. Any of the options in the Godot StandardMaterial can be converted to a shader, and then you can insert that code into a custom shader. You could experiment with Godot's standard depth parallax technique, or any of the alternatives above. Or anything else you can imagine, like a sinewave that ripples the vertices outward for a VFX ground ripple effect, or ripples on a puddle ground texture.\n\n\n"
  },
  {
    "path": "doc/docs/tips_environment.md",
    "content": "﻿Environment Tips\n============================\n\n**Table of Contents**\n* [Introduction](#introduction)\n* [Lighting](#lighting)\n* [Foliage Assets](#foliage-assets)\n* [Foliage Material](#foliage-material)\n\n\n## Introduction\n\nNo matter how good of assets you have, your landscape will not look good without a proper rendering environment.\n\nThe Godot renderer has greatly improved its lighting over previous generations, but the default material shader is not good at rendering foliage.\n\nTerrain3D provides the ground mesh with a sophisticated shader, an instancer, and a particle shader. But sourcing, using, and setting up the textures, foliage assets, materials, lighting, and environment settings are your responsibility. Your choices and artistic technique will dramatically affect what your world looks like. Prepare to spend countless hours tweaking your materials, lighting, and environment settings.\n\nTo get an attractive environment you need the following, **in order of priority**:\n\n1. White balanced and properly exposed lighting. Use pure white light. Don't use colored light unless you really know what you're doing.\n2. Quality [ground textures with heights](texture_prep.md), and [good technique in painting](texture_painting.md#texture-painting).\n3. Quality foliage mesh and texture assets.\n4. A good foliage shader (below). The default is inadequate.\n5. Suitable `WorldEnvironment` settings to balance and polish your look.\n\n\n## Lighting\n\nLighting is the most important. To be able to adjust your world accurately, you need to ensure the photons hitting your eyes are accurate. If your view of the world is off, your interpretation of it will be off.\n\n\n### Room Lighting\n\nThere is a reason every major feature film finishes off their post production with a professional colorist. This person or team has spend tens of thousands of dollars to create a viewing studio with calibrated equipment in order to be able to work in the environment of the ideal movie theater. They can then fix issues **they might not have been able to see** in other environments, such as over or under exposure, inaccurate colors, crushed blacks, blown out highlights, flickering, etc.\n\nThe goal is not to have your game look good on all monitors. That's an impossible task. \n\nWhat a movie studio aims for is the best possible experience on reference hardware in the ideal environment. They want to meet industry standard specifications defined by THX, Dolby, etc. Then they rely on equipment manufacturers and those building professional and home theaters to also build for those specifications. The closer the end user gets to those standards, the more accurate their experience of the product will be.\n\nYou don't have to match this level of expense or effort. But you should look at your office and monitors, and fix the worst issues so that you can work in an environment closer to how gamers will consume your product.\n\nIt's going to be very difficult to produce an attractive game if your screen looks like the left:\n```{image} images/monitor_calibration.jpg\n:target: images/monitor_calibration.jpg\n```\n\n**Tips:**\n\n* Ensure you don't have light shining on your screen. If you have a glossy screen, make sure there are no reflections on it.\n* If you have colored lights around your office or computer, turn them off. Turn off keyboard and case LEDs.\n* Adjust your room lighting for neutrality:\n\t* The color of room light should be neutral - close to overcast outdoor lighting. Your monitor is most likely slightly blue like outdoor light, incandescent lights are orange, cool white bulbs are too blue.\n\t* The overall brightness of the room should be slightly dim, but not dark. You want your monitor at near full brightness so it can render colors as designed, without competing with other lights or windows. But you don't want the room so dark the bright screen blows out your eyes.\n* Before buying a screen or laptop:\n\t* Make sure it's a matte screen. Never buy glossy screens for graphics work as you'll always be fighting reflections.\n\t* Look at it in person. Open a web browser at the store, load up an monitor calibration image, and try to adjust the screen in the store to ensure you can get an accurate result at home.\n\t* Look up the monitor on a review website that focuses on screen testing for color and brightness uniformity and rendering.\n\t* Lenovo laptop monitors have been very good in recent years (through 2024 at least).\n* Calibrate your monitor.\n\n\n### Monitor Calibration\n\nThe easy way is to buy a device to calibrate your monitor like something from Calibrite or Datacolor.\n\nHowever you can do it manually, and with practice you'll get better at it and train your eye.\n\n* Find your screen controls built into your monitor and adjust those first with the tests below. Even laptop screens have brightness controls, which should probably be at or near max. This is the first pass.\n* Repeat the adjustments with the color calibration software built in to your OS. Windows has `Display Color Calibration` control panel with a wizard.\n* Work through the pages in [LCD Test](http://www.lagom.nl/lcd-test/) as you adjust the physical, and then later, the software controls.\n* View a `monitor calibration image` (web search) as you adjust the controls, such as [this one](https://webtransformer.com/calibrate/).\n* When adjusting, think about what you're looking at on the image; the fruit, skin color, etc. Ask yourself, \"Is that what it's supposed to look like?\" \"Is this what it would look like if it were sitting on my desk in front of me?\" Look away, out the window, then look back at it and ask yourself again. Adjust the controls. Make it too saturated. Then make it desaturated. Then find the right spot. Do that with all of the controls. Too bright, too dark, just right.\n* Color is the hardest. You must find neutral white, which is comprised of equal parts of red, blue, and green as your screen displays them, not what is fed from your video card. Look at white on the reference image or the background of a web browser. Is it too blue? Probably. Most factory shipped monitors and TVs are. Is it too red? Is it too green? Adjust, look away, look, adjust. Make it too red, too blue, too green, just right.\n* Over many years of color and photography work, you will stop accepting what your brain interprets you're seeing (a white wall) and will perceive the raw input from your eyes (a slightly yellow-grey wall, dark grey in the shadows, a subtle orange tint around the incandescent light, and a soft blue tint from the window).\n\n\n### Game Environment\n\nIn your virtual world, the most important aspects are your sky shader, directional light, and camera. The sky shader illuminates the world with reflective light, giving your world a base exposure. The directional light gives your world three-dimensional form through light and shadow. The camera is the eye that perceives the virtual light rays.\n\nWe won't discuss Global Illumination here. There aren't any good world GI solutions in Godot currently. Even if you do use it, you need to setup your base lighting properly first. Also see [Faking GI](https://docs.godotengine.org/en/stable/tutorials/3d/global_illumination/faking_global_illumination.html).\n\nYou should familiarize yourself with the Godot docs on [WorldEnvironment](https://docs.godotengine.org/en/stable/classes/class_worldenvironment.html), [Environment](https://docs.godotengine.org/en/stable/classes/class_environment.html), [DirectionalLight3D](https://docs.godotengine.org/en/stable/classes/class_directionallight3d.html), [Camera3D](https://docs.godotengine.org/en/stable/classes/class_camera3d.html), and [CameraAttributes](https://docs.godotengine.org/en/stable/classes/class_cameraattributes.html) and the Practical and Physical child classes. There are many options that need to be tweaked for a good looking setup, which you can learn over time.\n\nFor now though start with the sky. The built in procedural skies in Godot 4 are sub par. Use either an HDRI, a custom sky shader, or Sky3D.\n\n\n#### HDRIs\n\nHigh Dynamic Range Image. Use 32-bit HDR or EXR images as these have full dynamic range, which will give the highest quality reflective light. A web search will turn up many websites with paid and free options. Here are some sites with free HDRIs:\n\n* [AllSkyFree](https://github.com/rpgwhitelock/AllSkyFree_Godot) - 8-bit PNGs, which may be fine for some projects, but they are attractive, game-centric and free; with paid options.\n* [Polyhaven](https://polyhaven.com/hdris)\n* [HDRI-Skies](https://hdri-skies.com/free-hdris/)\n* [HDRMaps](https://hdrmaps.com/freebies/)\n\nOnce you have the file, resize it according to how much VRAM you want to consume, and how high of quality you want. 2k-4k is most likely all you need. Don't use it at 16k.\n\nPlace it in `WorldEnvironment/Environment/Sky/PanoramaSkyMaterial/Panorama`. See [PanoramaSkyMaterial](https://docs.godotengine.org/en/stable/classes/class_panoramaskymaterial.html).\n\n\n#### Sky Shader\n\nYou can write your own sky material or look online for already written sky shaders. Place your `ShaderMaterial` in `WorldEnvironment/Environment/Sky`. For a shader developer there's no limit to what you can do. You could have clouds, stars, multiple suns, multiple moons. All of these features can provide reflective light that you'll see in reflections and psuedo bounce lighting.\n\n\n#### Sky3D\n\nWe use use, maintain, and recommend [Sky3D](https://github.com/TokisanGames/Sky3D), which is a feature rich sky shader. It provides:\n* Consolidated lighting & environmental controls\n* A day/night cycle\n* A game time manager\n\nSky3D renders a sun, moon, clouds, and fog. It's compatible with Godot's basic and volumetric fogs, plus has its own screen space fog. We've consolidated all of Godot's various lighting and environmental controls into one panel, and provide a great number of customizable options.\n\n**Tips:**\n\n* The Sky3D fog uses a `render_priority` of 100. This is relevant here because you want your foliage to render lower than this. However if you have text on screen with a Label3D, you want it to have a higher priority so it renders on top.\n\n\n\n------------\n\n## Foliage Assets\n\nThere are lots of ways to get foliage assets. You need to look around, acquire, and import into Godot, which is all out of scope for this document.\n\n**Options:**\n\n* Download countless available assets from other engine Asset stores. Almost all assets you have a license for can be legally exported for use and distribution in your Godot project. Check the license for your asset and considering how you want to use it.\n\t* FAB / Unreal Engine\n\t* Unity Engine\n* Trees can be made with [Treeit](https://www.evolved-software.com/treeit/treeit).\n* Make assets yourself in Blender.\n\n\n------------\n\n## Foliage Material\n\nThe default material in Godot is sub par for foliage. Here are some tips to improve rendering. However **[setup your lighting first!](#lighting)** You cannot properly adjust your materials if you aren't seeing objective \"reality\".\n\nMany of the options below can only be done in a custom shader. But you can setup a StandardMaterial, place it in a material slot, then right-click and choose `Convert To ShaderMaterial`. This will turn the existing configuration into a shader you can edit.\n\nThe shader snippets below are incomplete. Where you see `void fragment() {` or `vertex()` it is assumed you will add the subsequent lines into that function; typically at the end. Study some shader tutorials to learn what you're doing if it's not working right.\n\n\n### Texture Cards\n\nFoliage is made up of texture cards: 2D textures UV mapped onto a flat polygon. This is also how hair works.\n\nMany games place a grass texture on a QuadMesh or PlaneMesh, often using 2 or 3 cards in a cross or asterisk shape, like the default Terrain3DMeshAsset. \n\nWith 3D foliage, the leaves are 2D textures UV mapped onto flat polygon cards distributed around 3D polygon branches.\n\nFor all of these cards, only one side of each polygon is the front face, the other is the back. By default, the back face is culled.\n\n* If you want to see the back face, change `cull_mode` to disabled. In the shader, change the render mode at the top from `cull_back` to:\n```glsl\nrender_mode cull_disabled;\n```\n* The above will make the back face dark in certain lighting conditions. Enable `Back Lighting` to allow light from the front side to shine through. In the shader:\n\n```glsl\nuniform vec3 backlight : source_color = vec3(.5, .5, .5);\n\nvoid fragment() {\n\tBACKLIGHT = backlight.rgb;\n}\n```\n\n\n### Normals\n\nA normal is the perpendular vector of any given point of an object. This helps the renderer figure out the appropriate direction to bounce a light ray. \n\nEach face has a normal. Each vertex has a normal. Each pixel of the normal map texture has a normal.\n\n#### Face Normals\n\nYou've likely read about \"recalculating your normals in Blender\". That typically means recalculating the vector perpendicular to each **front face**. That needs to be done for proper rendering of a mesh or light reflects off of it will look weird. Typically once calculated in Blender, this doesn't need to be accessed in the shader.\n\nHowever, you can reverse the normals of back faces so that all faces will render like they are front faces. This is good for your foliage leaves made of cards. We want to see above and below leaves without the lighting reversed because of the face normal. Note NORMAL, not NORMAL_MAP.\n```glsl\nvoid fragment() {\n\tNORMAL = !FRONT_FACING ? -NORMAL : NORMAL;\n}\n```\n\n#### Vertex Normals\n\nLike faces, each vertex also has a normal. If they are messed up, you might see odd lighting in the corners of faces instead of on the whole mesh. These can also be reset in Blender. \n\nVertex normals have a unique caveat when rendering foliage like thick tree canopies and bushes. First, let's look at real life. Notice how the canopies are made up of many individual elements, yet the overall lighting is very consistent with light on top and darkness on bottom.\n\n```{image} images/vertex_normals_photo.jpg\n:target: ../_images/vertex_normals_photo.jpg\n```\n\nUnfortunately, if you render foliage in the same way as other 3D objects, some meshes will be lit inconsistently. Observe this Poplar tree below with the default lighting on the left compared to the above real trees and a more desirable rendering.\n\n```{image} images/vertex_normals_3d.jpg\n:target: ../_images/vertex_normals_3d.jpg\n```\n\nTo achieve the effect on the right, edit your mesh in Blender or another tool and reorient the vertex normals of the canopy as if they are extending outward from the origin of the mesh like an expanding sphere or dome. You will have to learn how your DCC can transfer vertex normals from one object to another, as a Blender tutorial is out of scope for this documentation. But you can learn more about the concept and applying it in DCCs here:\n* [https://ericchadwick.com/img/tree_shading_examples.html](https://ericchadwick.com/img/tree_shading_examples.html)\n* [http://wiki.polycount.com/wiki/VertexNormal#Transfer_Attributes](http://wiki.polycount.com/wiki/VertexNormal#Transfer_Attributes)\n\nYou can achieve a similar affect in the shader by calculating vertex normals as they extend outward from the origin of the mesh. You can apply this to the shader on your leaves. This is how the above image on the right was created.\n\n```glsl\nvoid vertex() {\n\tNORMAL = normalize(VERTEX); // Recalculate vertex normals from model origin\n\tTANGENT = normalize(cross(NORMAL, vec3(0., 0., 1.)));\n\tBINORMAL = normalize(cross(NORMAL, TANGENT));\n}\n```\n\n#### Texture Normals\n\nAs the renderer displays each pixel of a mesh on screen, it uses the texture normal map to determine how light will bounce off of the material. \n\nNormal maps are either in a format for DirectX systems, others are in OpenGL format. You need to be able to convert between them. Sometimes the map you got is mislabeled, and needs to be corrected.\n\nYou can convert between OpenGL and DirectX by inverting the green channel either in Photoshop, in the Godot import settings, or with the shader code below. The latter is useful to be able to quickly compare how the light reflects off of the leaves. Unlike with rocks, it's often much harder to visually tell them apart on foliage. Make sure you're looking at the front face of the texture card (see above). Add the line referencing `NORMAL_MAP.g` after your NORMAL_MAP texture lookup, then you can toggle the uniform to switch.\n\n\n```glsl\nuniform bool directx_normals = false;\n\nvoid fragment() {\n\tNORMAL_MAP = texture(texture_normal, base_uv).rgb;\n\tNORMAL_MAP.g += (1.0 - 2.0 * NORMAL_MAP.g) * float(directx_normals); // Invert Green if DirectX\n}\n```\n\n### Thin Trees\n\nThe Godot renderer will quickly discard pixels of thin foliage as the camera moves away. To combat this, prefer broad leaf foliage over thin leaves like pine needles. Of course this isn't ideal for all environments. These shader snippets will also help.\n\nFor a deeper explanation, this is partially caused by mipmap averaged alpha values. As the mip level increases, the alpha can decrease due to more and more 0 alpha pixels being included. Say 51% of a leaf texture has alpha 0, with a clip threshold of 0.5. Mip0 will look as desired, but as the mip level increases the edges get blended under the clip threshold. This makes the foliage textures shrink aggressively. Disabling mipmaps for alpha clip textures will stop this happening, but results garbage noisy pixels. A lot of games use a MAX comparison when generating alpha clip texture mipmaps to help with this, though Godot doesn't currently have this option. We compensate for this in the snippets below.\n\n* This code makes trees maintain volume at a distance. You can change `log` to `log2` or even remove it for greater thickness.  This boosts the alpha value closer to 1.0 as the distance increases, which counteracts the issue with mipmap averaging against 0 alpha.\n\n\n```glsl\nuniform float alpha_scissor_threshold : hint_range(0,1) = 0.5;\nuniform float alpha_antialiasing_edge : hint_range(0,1) = 0.3;\n\nvoid fragment() {\n\tALPHA = clamp(albedo_tex.a * max(log(-VERTEX.z), 1.0), 0.0, 1.0);\n\tALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;\n\tALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge;\n}\n```\n\n\n* Alpha-to-coverage adjusts anti aliasing, which further helps foliage maintain density at a distance. Set the value to the size of your albedo texture, though there doesn't seem to be an issue if hard coded larger. e.g. 16384.\n\n```glsl\nrender_mode alpha_to_coverage;\n\nvoid fragment() {\n\tALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size);\n}\n```\n\n\n### Artifacts\n\n* If your mesh has weird colors or darkness that shouldn't be there. Disable `Vertex Color / Use as albedo`. In the shader, look for and remove `COLOR`, e.g. `ALBEDO = albedo_tex.rgb * COLOR.rgb`. If that wasn't the cause, the darkness might be due to normals. Read [Normals](#normals) above.\n\n* If your leaves and branches are in reverse order, don't use `Transparency/Alpha`. Use only `Depth Pre-Pass`, or `Alpha Scissor`. The former initially appears to look better, but it puts the material in the slower transparency pipeline, and still has rendering and Z-order issues. We recommend using only `Alpha Scissors`. Also enable `Alpha Antialiasing Mode`. Then adjust `alpha_scissor_threshold` and `alpha_antialiasing_edge` to suit. Read [Thin Trees](#thin-trees) above.\n\n* Specular and Roughness for reflective light are appropriate up close, but they can produce ugly artifacts when far away. If one or more of your plants are too shiny at a distance, fade out specular.\n\n```glsl\nuniform float specular_distance_fade : hint_range(0,1000,.1) = 15.;\n\nvoid fragment() {\n\tvec3 camera_pos = INV_VIEW_MATRIX[3].xyz;\n\tvec3 pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;\n\tSPECULAR = mix(specular, 0., clamp(length(pixel_pos - camera_pos) / specular_distance_fade, 0., 1.));\n}\n```\n\n* Some foliage renders with a white edge even though it's not visible on the texture.\n\t* First, ensure all of your texture samplers have the repeat_disable flag:\n\t```glsl\n\tuniform sampler2D texture_albedo : source_color, repeat_disable;\n\t``` \n\t* Next, confirm if the issue is caused by the roughness or specular channels, and adjust as above.\n\t* Finally, if you're sure the issue is in ALBEDO, place this line *after* assigning the texture to ALPHA, and it will allow you to clip off white pixels.\n\t```glsl\n\tuniform float alpha_white_filter : hint_range(0,1) = 0.0;\n\n\tvoid fragment() {\n\t\tALPHA *= step(alpha_white_filter, 1.0 - max(max(ALBEDO.r, ALBEDO.g), ALBEDO.b));\n\t}\n\t```\n\n* Usually you want to keep your values appropriately clamped or normalized or you will get rendering artifacts. E.g. `ROUGHNESS = clamp(roughness, 0., 1.); NORMAL_MAP = normalize(normal);`. Other times, it's useful to use values higher than the typical range. E.g. `ALBEDO` and `BACKLIGHT`. Don't be afraid to experiment to find appropriate value ranges. But test extreme values on multiple cards and drivers before shipping your game or they might give you more artifacts.\n\n\n### Conserve VRAM\n\nSave as much VRAM as you can. The less data transfer across the bus, the faster your game will be. The smaller your texture files, the less data transfer. The more efficient your shader and fewer texture lookups, the less traffic on the bus.\n\n* A lot of foliage leaves don't need metal/roughness/AO maps. If you get an asset with these maps, ensure they make a difference before using them. You can adjust roughness and specular without a map. Using only albedo and normal maps are often adequate for foliage.\n\n* Material textures don't have to be the same resolution. You could have an 8k albedo texture that covers a lot of plants, and find that a 2k or 4k normal map is sufficient. Zoom your camera as close to the plant leaves as your player will be. If you can't tell a difference between a 2k, 4k, or 8k normal map or roughness texture, then use the smallest that makes a difference.\n"
  },
  {
    "path": "doc/docs/tips_technical.md",
    "content": "Technical Tips\n====================\n\n## Are Certain Features Supported?\n\nThis list are for items that don't already have dedicated pages in the documentation.\n\n| Feature | Status | \n| ------------- | ------------- | \n| Destructibility | Real-time modification is possible by changing the data and updating the maps and collision. You can sculpt heights, change textures, or make holes. If you want tunnels or caves though you need to add your own meshes or use [Zylann's Voxel Terrain](https://github.com/Zylann/godot_voxel).\n| GPU Sculpting| [Pending](https://github.com/TokisanGames/Terrain3D/issues/174). Currently painting occurs on the CPU in C++. It's reasonably fast, but we have a soft limit of 200 on the brush size, as larger sizes lag.\n| Holes | Holes work for both visual and collision.\n| Jolt | [Godot-Jolt](https://github.com/godot-jolt/godot-jolt) was merged into Godot. Terrain3D works with both Godot and Jolt physics. Collision is generated where regions are defined.\n| Non-destructive layers | Used for things like river beds, roads or paths that follow a curve and tweak the terrain. It's [possible](https://github.com/TokisanGames/Terrain3D/issues/129) in the future.\n| Object placement | The [instancer](instancer.md) supports placing foliage. Placing objects that shouldn't be in a MultiMeshInstance node is [out of scope](https://github.com/TokisanGames/Terrain3D/issues/47). See 3rd party tools below.\n| Streaming | There is no streaming built in to Godot. Region Streaming is [in progress](https://github.com/TokisanGames/Terrain3D/pull/675).\n| Roads | Look at [Godot Road Generator](https://github.com/TheDuckCow/godot-road-generator/).\n| Water | Use [WaterWays](https://github.com/Arnklit/Waterways) for rivers, or [Realistic Water Shader](https://github.com/godot-extended-libraries/godot-realistic-water/) or a variety of other water shaders available online for lakes or oceans.\n|**Rendering**|\n| Frustum Culling | The terrain is made up of several meshes, so half can be culled if the camera is near the ground.\n| SDFGI | Works fine.\n| VoxelGI | Works fine.\n| Lightmaps | Not possible. There is no static mesh, nor UV2 channel to bake lightmaps on to.\n| **3rd Party Tools** |\n| [Scatter](https://github.com/HungryProton/scatter) | For placing MeshInstance3D objects algorithmically, with or without collision. We provide [a script](https://github.com/TokisanGames/Terrain3D/blob/main/project/addons/terrain_3d/extras/3rd_party/project_on_terrain3d.gd) that allows Scatter to detect our terrain. Or you can change collision mode to `Full / Editor` and use the default `Project on Colliders`. Don't use it for MultiMeshInstances, use our built-in instancer.\n| [AssetPlacer](https://cookiebadger.itch.io/assetplacer) | A level design tool for placing MeshInstance3D assets manually. Works on Terrain3D with placement mode set to Terrain3D or using the default mode and collision mode set to `Full / Editor`.\n\n\n## Regions\n\nOutside of regions, there is no collision. Raycasts won't hit anything. Querying terrain heights or other data will result in NANs or INF. Look through the API for specific return values. See [Collision] for more.\n\nYou can determine if a given location is within a region by using `Terrain3DData.has_regionp(global_position)`. It will return -1 if the XZ location is not within a region. Y is ignored.\n\n\n## Terrain3DObjects\n\nJust as the instancer keeps foliage stuck to the ground when sculpting, we provide a special node that does the same for regular MeshInstance3D objects.\n\nObjects that are children of this node will maintain the same vertical offset relative to the terrain as they are moved laterally or as the terrain is sculpted.\n\nFor example you can place a sphere on the ground, move it laterally where a hill exists, and it will snap up to the new ground height. Or you can lower the ground and the sphere will drop with the changes. \n\nYou can then adjust the vertical position of the sphere so it is half embedded in the ground, then repeat either of the above and the sphere will snap with the same vertical offset, half embedded in the ground.\n\nTo use it:\n* Create a new node of type Terrain3DObjects\n* Add your MeshInstance3D objects as children of this node\n\n\n## Performance\n* The Terrain3DMaterial shader has some advanced features that look nice but consume some performance. You can get better performance by disabling them:\n    * Set `WorldBackground` to `Flat` or `None`\n\t* Disable `Auto Shader`\n\t* Disable `Dual Scaling`\n* `WorldBackground` as `Noise` exposes additional shader settings, such as octaves and LOD. You can adjust these settings for performance. However this world generating noise is expensive. Consider not using it at all in a commercial game, and instead obscure your background with meshes, or use an HDR skybox with mountains built in.\n* Reduce the size of the mesh and levels of detail by reducing `Terrain Mesh/Size` or `Terrain Mesh/Lods` in the `Terrain3D` node.\n* Reduce `Terrain Mesh/Tessellation Level` or set to 0 (default) to completely disable texture displacement.\n* Increase `Terrain Mesh/Vertex Spacing`, which increases the lateral scale and gives you a more low-poly terrain. Preferrably do this before you sculpt, but if done after, you can export the heightmap and manipulate it in Photoshop to rescale it.\n* Don't use `Renderer/Cull Margin`. It should only be needed if using the noise background. Otherwise the AABB should be correctly calculated via editing, so there is no need to expand the cull margin. Keeping it enabled can cost more processing time.\n* Experiment with `Renderer/free_editor_textures`, which is enabled by default. It saves VRAM by removing the initial textures used to generate the texture arrays.\n* For cases where performance is paramount, an example `lightweight` shader is provided in `extras/shaders`. This shader is designed to do the minimum possible amount of texture lookups, while still providing basic texturing, including height blending. Normals are also fully calculated in `vertex()`. This shader removes advanced features like projection, detiling, and paintable rotation and scale for significant performance gains on low-end hardware, mobile, and VR applications. Or you can use the `minimum` shader and craft your own texturing and coloring without any extra features.\n\n\n## Shaders\n\n### Minimal Shaders\n\nThis terrain is driven by the GPU, and controlled by our shader. We provide a minimal shader that has only the code needed to shape the terrain mesh without any texturing that you can use as a base to build your own. There's also versions that use the color map, and have a low-poly look with flat normals. Find them all in `extras/shaders/minimum.gdshader`.\n\nLoad this shader into the override shader slot and enable it. It includes no texturing so you can create your own.\n\n### Low-poly & PS1 Styles\n\nOlder style asthetics has a few different looks:\n\n**PS1 style** often has blocky textures, which can be achieved by:\n* Use low res textures\n* In the Terrain3DTextureAsset, decreasing UV Scale\n* In the material, change Texture Filtering to Nearest. If you make your own shader, make sure to change your samplers to use Nearest filtering instead of Linear.\n\n**Low-poly Style** often has large, flat shaded polygons. To get the best results:\n* Increase `vertex_spacing` to a large value like 10\n* Enable `Flat Terrain Normals` in the material settings.\n\n\n### Day/Night cycles & light under the terrain\n\nIf you have a day/night cycle and the sun sets below the horizon, it might shine through the terrain. There are a few options you can try:\n\n* Check `Material / Shader Override Enabled` and change the second line of the generated shader to `cull_disabled`. The terrain shader is set to `cull_back` by default, meaning back faces are neither rendered, nor do they block light. This will change that, though it does cost some performance. \n* You can also try changing `Rendering / cast_shadows` to double sided.\n* Turn off your light when it sinks below the horizon. Changing the number of visible DirectionalLight3Ds will likely cause a shader recompile in the engine as it's a different lighting configuration. Instead you can tween light energy to 0, or you could turn off the sun light and turn on a moon light as long as it is done simultaneously in the same frame.\n\n\n### Accessing private shader variables\nVariables in the shader prefaced with `_` are considered private and not saved to disk. They can be accessed externally via the Rendering Server:\n\n```gdscript\nRenderingServer.material_get_param(terrain.get_material().get_material_rid(), \"_background_mode\")\n```\n\n\n### Using the generated height map in other shaders\nHere we get the resource ID of a material on a mesh. We assign the RID of the generated heightmap to texture slot in that material.\n\n```gdscript\nvar mat: RID = $MeshInstance3D.mesh.surface_get_material(0).get_rid()\nRenderingServer.material_set_param(mat, \"texture_albedo\", get_data().get_height_maps_rid())\n```\n\nThis is a quick demonstration that shows results. However the generated texture arrays should be accessed with sampler2DArray in a shader, not the regular sampler which is what will will happen here.\n\nThis also works with the control and color maps. \n\n\n### Add a custom texture map\n\nHere's an example of using a custom texture map for one texture, such as adding an emissive texture for lava. Add in this code and add an emissive texture, then adjust the emissive ID to match the lava texture, and adjust the strength.\n\nAdd these uniforms at the top of the file with the other uniforms:\n```glsl\nuniform int emissive_id : hint_range(0, 31) = 0;\nuniform float emissive_strength = 1.0;\nuniform sampler2D emissive_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;\n```\n\nAdd a variable to store emissive value in the Material struct.\n\n```glsl\nstruct material {\n\t...\n\tvec3 emissive;\n};\n```\n\nModify `accumulate_material()` to read the emissive texture with the next several options. \n\nAdd the initial value for emissive by adding a vec3 at the end:\n```glsl\n\t// Struct to accumulate all texture data.\n\tmaterial mat = material(vec4(0.0), vec4(0.0), 0., 0., 0., vec3(0.));\n```\n\nNear the bottom of `accumulate_material()`:\n```glsl\n\tmat.normal_rough += nrm * id_weight;\n\tmat.normal_map_depth += _texture_normal_depth_array[id] * id_weight;\n\tmat.ao_strength += _texture_ao_strength_array[id] * id_weight;\n\tmat.total_weight += id_weight;\n```\n\non the next line add:\n```glsl\n\tif(id == emissive_id) {\n\t\tmat.emissive += textureGrad(emissive_tex, vec3(id_uv, float(id)), id_dd.xy, id_dd.zw).rgb *= id_weight;\n\t}\n```\n\nFind this in `fragment()`, before the final `}`, apply the weighting and send it to the GPU.\n```glsl\n\t// normalize accumulated values back to 0.0 - 1.0 range.\n\tfloat weight_inv = 1.0 / total_weight;\n\tmat.albedo_height *= weight_inv;\n\t...\n\tmat.emissive *= weight_inv;\n\tEMISSION = mat.emissive * emissive_strength;\n```\n\nNext, add your emissive texture to the texture sampler and adjust the values on the newly exposed uniforms.\n\n\n### Avoid sub branches\n\nAvoid placing an if statement within an if statement. Enable your FPS counter so you can test as you build your code. Some branch configurations may be free, some may be very expensive, or even more performant than you expect. Always test.\n\nSometimes it's faster to always calculate than it is to branch.\n\nSometimes you can do tricks like this to avoid sub branching:\n\n```glsl\nuniform bool auto_shader;\nif (height > 256) {\n   if (auto_shader) {\n     albedo = snow_color;\n   }\n}\n```\n\n```glsl\nuniform bool auto_shader;\nif (height > 256) {\n  albedo = float(!auto_shader)*albedo + float(auto_shader)*snow_color;\n}\n```\n\nThese two are equivalent, and avoids the sub branch by always calculating. If auto_shader is true, the line is `albedo = 0.*albedo + 1.*snow_color`.\n"
  },
  {
    "path": "doc/docs/troubleshooting.md",
    "content": "Troubleshooting\n=================\n\nTerrain3D is working for thousands of users. If you're having trouble you've likely missed a step or an important piece of documentation. Ensure you are using the [console version of Godot](#using-the-console), and have reviewed the [installation instructions](installation.md) before continuing.\n\nYou can also watch the [tutorial videos](tutorial_videos.md) which show proper installation and setup, and read [Technical Tips](tips_technical.md) which may have other helpful information.\n\n**Table of Contents**\n* [Installation](#installation)\n* [Textures](#textures)\n* [Usage](#usage)\n* [Crashing](#crashing)\n* [Using the Console](#using-the-console)\n* [Debug Logs](#debug-logs)\n\n\n## Installation\n\n### Unable to load addon script from path\n\n`Unable to load addon script from path: xxxxxxxxxxx. This might be due to a code error in that script. Disabling the addon at 'res://addons/terrain_3d/plugin.cfg' to prevent further errors.\"`\n\nMost certainly you've installed the plugin improperly. These are the common causes:\n\n1) You downloaded the repository code, not a [binary release](https://github.com/TokisanGames/Terrain3D/releases).\n\n2) You moved the files into the wrong directory. The Terrain3D files should be in `project/addons/terrain_3d`. `Editor.gd` should be found at `res://addons/terrain_3d/editor/editor.gd`. [See an example issue here](https://github.com/TokisanGames/Terrain3D/issues/200).  \n\nBasically, the binary library file isn't where it's supposed to be. The error messages in your [console](#using-the-console) (not the output panel) will tell you exactly the file name and path it's looking for. View that location on your hard drive. On windows you might be looking for `libterrain.windows.debug.x86_64.dll`. Does that file exist where it's looking in the logs? Probably not. Download the correct package, and review the instructions to install the files in the right location.\n\n### There are no tools. The texture list is blank\n\nEnsure the plugin is [installed properly](installation.md) and enabled in the project settings. \n\nThen, click the Terrain3D node in the Scene panel to activate the tools and asset dock.\n\n\n### The editor tools panel is there, but the buttons are blank or missing\n\nRestart Godot twice before using it. Review the [installation instructions](installation.md).\n\n\n### Start up is very slow\n\nYou probably have a large terrain and are generating collision for all of it. Set your Collision Mode to `Disabled` to test. You can dynamically create only a small collision area around the camera by setting your Collision Mode to `Dynamic / Game` (default) or `Dynamic / Editor`. Read more about [collision modes here](collision.md#physics-based-collision-raycasting).\n\nOr you've added a bunch of the generated textures, which are binary data saved as text in the scene. Or you've disconnected your textures from the files on disk, which has done the same thing. If your .tscn scene file (or .tres asset list if you've saved it off) is very large, and has lots of binary data as text, this is the case. Review your textures and ensure each is linked to a file saved on disk. Terrain3D should also report problem textures to your [terminal](#using-the-console).\n\n---\n\n## Textures\n\n### Added a texture, now the terrain is white\n\nYour [console](#using-the-console) also reports something like:\n\n`Texture 1 albedo / normal, size / format... doesn't match size of first texture... They must be identical.`\n\nThe new texture doesn't match the format or size of the existing ones. [Texture Preparation](texture_prep.md) descibes the requirements, which includes the same format and size for each. Double click a texture in the filesystem and Godot will tell you what it is. You can also click the texture in the inspector when editing an entry in the asset dock to see the same thing.\n\nIf adding textures to the demo, the format is PNG marked HQ so it converts to BPTC, which you can read about on the link above.\n\n\n### The terrain is all black\n\nCheck with the default shader by disabling any override shader you have created.\n\nOtherwise, this is usually incompatibilities with textures or texture arrays. \n\nWe have seen this when using the Compatibility Renderer in older versions where texture import settings need to be set to VRAM Uncompressed. In 4.4, the Compatibility Renderer hasn't had this problem.\n\nWe've seen it running on mobile and switching to another phone works. This might be caused by poor support in the phone driver or Godot for textures in the format you're using, texture arrays in general, or texture arrays of the size we are using (1024). We've seen all three. It will take experimentation on your end, or updated drivers from the manufacturer or better support from Godot to fix it.\n\n\n### The terrain is checkered\n\nIf starting up for the first time, this is normal. Add a texture to the asset dock and paint with it. See [Texture Preparation](texture_prep.md).\n\nIf loading scenes via code, this is because `free_editor_textures` is enabled by default and is conserving memory and VRAM. When running in game, Terrain3D combines the textures into texture arrays for the shader. It then clears the texture list so Godot can unload the original textures if they aren't used by other systems.\n\nTo load them in all scenes, you can either:\n* Disable `free_editor_textures`\n* Make your asset list unique to each scene. (Think about this one. A shared texture list is useful.)\n* Reload the textures when loading a scene with Terrain3D:\n\n```\nterrain.assets = ResourceLoader.load(\"res://scenes/terrains/asset_list.tres\", \"\", ResourceLoader.CACHE_MODE_IGNORE)\n```\n\nTerrain3D will continue to clear the textures on `_ready()` after compiling the arrays. You can clear textures at other times with `terrain.assets.clear_textures()`.\n\n---\n\n## Usage\n\n### Collision is offset from the mesh and it's showing lower LODs near the camera\n\nIf you're using multiple cameras, or viewports, you need to tell Terrain3D which camera you want to use with `Terrain3D.set_camera()`. You can update it every frame if you like.\n\n\n### Textures flicker\n\nThis is caused by temporal effects: FSR, TAA, and motion blur shaders. The Terrain3D mesh is constantly moving, which causes Godot to unnecessarily generate motion vectors for the terrain.\n\nTo fix the flickering, disable the effects above, or use Godot 4.5+ and Terrain3D v1.0.2+, v1.1.0+, or source built against the godot-cpp 4.5 branch or higher.\n\n\n### Intersecting meshes flicker\n\nWhen another mesh intersects with Terrain3D far away from the camera, such as in the case of water on a beach, the two meshes can flicker as the renderer can't decide which mesh should be displayed in front. This is called Z-fighting. You can greatly reduce it by increasing `Camera3D.near` to 0.25. You can also set it for the editor camera in the main viewport by adjusting `View/Settings/View Z-Near`.\n\n\n### Segments of the terrain disappear on camera movement\n\nYou can increase `Renderer/Cull Margin`. For sculpted regions this should not be necessary as sculpting sets the AABB for the meshes already. However for shader based backgrounds like WorldBackground, or Flat with ground level, there is no AABB detection. You need to specify an increased cull margin if the mesh goes higher or lower than the sculpted areas. This consumes some performance so it is ideal if not used.\n\n---\n\n## Crashing\n\n### Godot crashes on load\n\nIf this is the first startup after installing the plugin, this is normal due to a bug in the engine currently. Restart Godot.\n\nIf it still crashes, try the demo scene. \n\nIf that doesn't work, most likely the Terrain3D library version does not match the engine version. If you downloaded a release binary, download the exactly matching engine version. If you built from source review the [instructions](building_from_source.md) to make sure your `godot-cpp` directory exactly matches the engine version you want to use. \n\nIf the demo scene does work, you have an issue in your project. It could be a setting or file given to Terrain3D, or it could be anywhere else in your project. Divide and conquer. Copy your project and start ripping things out until you find the cause.\n\n### Exported game crashes on startup\n\nFirst make sure your game works running in the editor. Then ensure it works as a debug export with the console open. If there are challenges, you can enable [Terrain3D debugging](#debug-logs) before exporting with debug so you can see activity. Only then, test in release mode.\n\nMake sure you have both the debug and release binaries on your system, or have built Terrain3D in [both debug and release mode](building_from_source.md#5-build-the-extension), and that upon export both libraries are in the export directory (eg. `libterrain.windows.debug.x86_64.dll` and `libterrain.windows.release.x86_64.dll`). These libraries must be in the same directory as the executable, or somewhere in the library search path. Every OS has this option. If Godot can't open the libraries you linked to, your game will close instantly upon startup.\n\n---\n\n## Using the Console\n\nAs a gamedev, you should always be running with the console or terminal window open. This means you ran `Godot_v4.*_console.exe` or ran Godot in a terminal window.\n\n```{image} images/console_exec.png\n:target: ../_images/console_exec.png\n```\n\nThis is what it looks like this when you have the console open. \n\n```{image} images/console_window.png\n:target: ../_images/console_window.png\n```\n\nGodot, Terrain3D, and every other addon gives you additional information here, and when things don't work properly, these messages often tell you exactly why.\n\nGodot also has an `Output` panel at the bottom of the screen, but it is slow, will skip messages if it's busy, and not all messages appear there.\n\n\n## Debug Logs\n\nTerrain3D has debug logs for everything, which it can dump to the [console](#using-the-console). These logs *may* also be saved to Godot's log files on disk.\n\nSet `Terrain3D.debug_level` to `Info` or `Debug` and you'll get copious activity logs that will help troubleshoot problems.\n\nYou can also enable debugging from the command line by running Godot with `--terrain3d-debug=<LEVEL>` where `<LEVEL>` is one of `ERROR`, `INFO`, `DEBUG`, `EXTREME`. Extreme dumps everything including repetitive messages such as those that appear on camera movement.\n\nTo run the demo from the command line with debugging, open a terminal, and change to the project folder (where `project.godot` is):\n\nAdjust the file paths to your system. The console executable is not needed since you're already running these commands in a terminal window.\n\n```\n# To run the demo with debug messages\ncd /c/gd/Terrain3D/project\n/c/gd/bin/Godot_v4.1.3-stable_win64.exe --terrain3d-debug=DEBUG\n\n# To run the editor with debug messages\n/c/gd/bin/Godot_v4.1.3-stable_win64.exe -e --terrain3d-debug=DEBUG\n```\n\nWhen asking for help on anything you can't solve yourself, you'll need to provide a full log from your console or file system. As long as Godot doesn't crash, it saves the log files on your drive. In Godot select, `Editor, Open Editor Data/Settings Menu`. On windows this opens `%appdata%\\Godot` (e.g. `C:\\Users\\%username%\\AppData\\Roaming\\Godot`). Look under `app_userdata\\<your_project_name>\\logs`. Otherwise, you can copy and paste messages from the console window above.\n\n"
  },
  {
    "path": "doc/docs/tutorial_videos.md",
    "content": "Tutorial Videos\n==================\n\n*Note: Some instructions in the videos may be out of date. Be sure to review the documentation for the latest info. Be aware you can select the appropriate documentation version in the menu.*\n\n## Using Terrain3D Part 1\n\nCovers:\n* Installation from Github\n* Setup (storage is out of date, specify a directory now)\n* Basic Usage\n* Preparing textures\n* Importing/Exporting data\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/oV8c9alXVwU?si=0kTuPwnHHwBmHsss\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n\n---\n\n## Using Terrain3D Part 2\n\nCovers:\n* Texture Painting\n* Autoshader\n* Material Settings\n* Occlusion Culling\n* Holes\n* Navigation\n* Mesh intersection flickering (e.g. for ocean planes)\n* Advanced Usage\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/YtiAI2F6Xkk?si=9wVwk3ZLd7CmxfR5\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n\n---\n\n## Terrain3D v0.9.3a New Features - Tutorial 3\n\nCovers:\n* Asset library installation\n* Platform updates\n* Region features like splitting and grid\n* Asset Dock\n* Detiling\n* Instancer\n* Tool bar changes and tool settings\n* Alpha stamping\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/fsigwCL0m3U?si=sQThKVVN3At-3jkE\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>"
  },
  {
    "path": "doc/docs/user_interface.md",
    "content": "User Interface\n=================\n\nThis document describes the various UI components of Terrain3D.\n\n**Table of Contents**\n* [Main Toolbar](#main-toolbar)\n* [Terrain3D Menu](#terrain3d-menu)\n* [Tool Settings Bar](#tool-settings-bar)\n* [Asset Dock](#asset-dock)\n\n\n## Main Toolbar\n\nAfter properly installing and enabling the plugin, add a Terrain3D node to your Scene and select it. This will enable the toolbar.\n\n```{image} images/ui_tools.png\n:target: ../_images/ui_tools.png\n```\n\nThe tools provide options for sculpting, texturing, and other features. Each button has a tooltip that describes what it does.\n\nFirst, select the Region Tool (first one: square with a cross), and click the ground. This allocates space for you to sculpt and paint.\n\n\n## Terrain3D Menu\n\nAfter selecting the Terrain3D node, the Terrain3D menu appears at the top of the viewport. This provides a variety of utilities such as our channel packer, mesh baker, occlusion baker, etc. These are documented elsewhere.\n\n```{image} images/terrain3d_menu.png\n:target: ../_images/terrain3d_menu.png\n```\n\n---\n\n## Tool Settings Bar\n\nDepending on which tool is selected on the toolbar, various tool settings will appear on the bottom bar.\n\n```{image} images/ui_tool_settings.jpg\n:target: ../_images/ui_tool_settings.jpg\n```\n\nMany tool settings offer a slider with a fixed range, and an input box where you can manually enter a much larger setting.\n\nClick the label of any setting to reset the value to its default, or checkboxes to toggle.\n\nThe settings are saved across sessions in `Editor Settings / Terrain3D / Tool Settings`. \n\nSome tools like `Paint`, `Spray`, and `Color` have options to disable some features. e.g. Disabling `Texture` on `Paint` means it will only apply scale or angle. Enabling `Texture` on `Color` will filter color painting to the selected texture.\n\nThe three dots button on the right is the advanced options menu. One noteworthy setting is `Brush Spin Speed`, which is what causes the brush to spin while painting. Reduce it to zero if you don't want this.\n\nBrushes can be edited in the `addons/terrain_3d/brushes` directory, using your OS folder explorer. The folder is hidden to Godot. The files are 100x100 alpha masks saved as EXR. Larger sizes should work fine, but will be slow if too big. We scale all brushes up to a minimum of 1024px on selection.\n\nMost other options are self explanatory. See [Foliage Instancing](instancer.md) for specific details on its settings.\n\n### Slope Range Filter\n\nThe slope filter on the settings bar allows you to paint by slope. If the slope filter is 0-45 degrees, then it will paint if the slope of the ground is 45 degrees or less. \n\nDon't confuse this with the slope sculpting tool on the left toolbar.\n\nThese tools work with the slope filter: **Paint**, **Spray**, **Color**, **Wetness**, **Instancer**.\n\nSee the [keyboard shortcuts](keyboard_shortcuts.md) for usage, including the option to inverse the slope.\n\n\n---\n\n## Asset Dock\n\n\nThe asset dock houses the list of textures and meshes available for use in Terrain3D. The contents are stored in the [Terrain3DAssets](../api/class_terrain3dassets.rst) resource shown in the inspector when the Terrain3D node is selected.\n\nClick `Textures` or `Meshes` to switch between the asset types.\n\nMesh thumbnail generation is currently unreliable. Hover over a mesh or click `Meshes` to regenerate them.\n\n```{image} images/ui_asset_dock_bottom.png\n:target: ../_images/ui_asset_dock_bottom.png\n```\n\n### Dock Position\n\nThe dropdown option allows you to select the dock position. Shown above, it is docked to the bottom panel. Below, it is docked to the right sidebar, in the [bottom right position](https://docs.godotengine.org/en/stable/classes/class_editorplugin.html#class-editorplugin-constant-dock-slot-left-ul).\n\n```{image} images/ui_asset_dock_sidebar.png\n:target: ../_images/ui_asset_dock_sidebar.png\n```\n\nThe icon with white and grey boxes in the top right can be used to pop out the dock into its own window. While in this state, there is a pin button that allows you to enable or disable always-on-top.\n\nNext the slider will resize the thumbnails.\n\nFinally, when the dock is in the sidebar, there are three vertical, grey dots, shown in the image above, in the top right. This also allows you to change the sidebar position, however setting it here won't save. Ignore this and use our dropdown instead.\n\n\n### Adding Assets\n\nYou can add resources by dragging a texture onto the `Add Texture` icon, a mesh (a packed scene: tscn, scn, fbx, glb) onto the `Add Mesh` icon, or by clicking either `Add` button and setting them up manually.\n\nIf you add a new texture and the terrain turns white, see [Troubleshooting](troubleshooting.md#added-a-texture-now-the-terrain-is-white).\n\nEach asset resource type has their own settings described in the API docs for [Terrain3DTextureAsset](../api/class_terrain3dtextureasset.rst) and [Terrain3DMeshAsset](../api/class_terrain3dmeshasset.rst).\n\nYou can read more about mesh setup on the [Foliage Instancer page](instancer.md#how-to-use-the-instancer).\n\n### Dock Operations\n\n* <kbd>LMB</kbd> - Select the asset to paint with.\n* <kbd>RMB</kbd> - Edit the asset in the inspector. You can also click the pencil on the thumbnail.\n* <kbd>MMB</kbd> - Clear the asset. You can also click the X on the thumbnail. If this asset is at the end of the list, this will also remove it. You can clear and reuse this asset, or change its ID to move it to the end for removal. When using the instancer, this will remove all instances painted on the ground. It will ask for confirmation first.\n\n\n\n"
  },
  {
    "path": "doc/dump_contributors.py",
    "content": "# This scripts dumps all contributors names, handles, and urls for a repository.\n# To use modify the settings below.\n\npat_file = \"/c/Users/Cory/github_terrain3d_pat.txt\"  # Github Personal Access Token to expand rate limiting\nrepo_url = \"https://api.github.com/repos/TokisanGames/Terrain3D/contributors\"\n\nimport requests\nimport sys\nimport unicodedata\nimport time\nimport pathlib\n\n# Set UTF-8 encoding for stdout on all platforms\nsys.stdout.reconfigure(encoding='utf-8')\n\n# Function to normalize Unicode characters to ASCII\ndef normalize_name(name):\n    if not name:\n        return \"\"\n    try:\n        # Normalize Unicode to ASCII, removing diacritics\n        normalized = unicodedata.normalize('NFKD', name).encode('ascii', 'ignore').decode('ascii')\n        return normalized.strip()\n    except Exception:\n        return \"\"\n\n# Function to normalize Git Bash /c/ paths to Windows C:\\ paths\ndef normalize_gitbash_path(path):\n    if sys.platform == \"win32\" and path.startswith(\"/c/\"):\n        return \"C:\\\\\" + path[3:].replace(\"/\", \"\\\\\")\n    return path\n\n# Read GitHub PAT from file\npat_file = normalize_gitbash_path(pat_file)\npat_path = pathlib.Path(pat_file)\nprint(f\"Reading PAT file from: {pat_path}\")\ntry:\n    with pat_path.open(\"r\", encoding=\"utf-8\") as f:\n        github_pat = f.read().strip()\nexcept FileNotFoundError:\n    print(f\"Error: Could not find PAT file at {pat_path}\")\n    sys.exit(1)\nexcept Exception as e:\n    print(f\"Error reading PAT file at {pat_path}: {e}\")\n    sys.exit(1)\n\n# GitHub API endpoint for contributors\nheaders = {\n    \"Accept\": \"application/vnd.github.v3+json\",\n    \"User-Agent\": \"Python script\",  # Avoid rate-limiting\n    \"Authorization\": f\"token {github_pat}\"  # Use PAT from file\n}\nparams = {\"per_page\": 100}  # Max per page\n\n# Fetch all contributors with pagination\ncontributors = []\npage = 1\nwhile True:\n    response = requests.get(repo_url, headers=headers, params={**params, \"page\": page})\n    if response.status_code != 200:\n        print(f\"API error fetching contributors: {response.status_code} - {response.text}\")\n        break\n\n    try:\n        data = response.json()\n    except ValueError:\n        print(f\"Failed to parse JSON response for contributors page {page}\")\n        break\n\n    # Handle API errors\n    if not isinstance(data, list):\n        print(f\"API error: {data.get('message', 'Unknown error')}\")\n        break\n\n    if not data:\n        break\n    contributors.extend(data)\n    page += 1\n\n# Process each contributor\nfor contributor in contributors:\n    username = contributor.get(\"login\", \"Unknown\")\n    if username == \"Unknown\":\n        print(f\"Skipping contributor with missing login\")\n        continue\n\n    # Fetch user profile\n    profile_url = f\"https://api.github.com/users/{username}\"\n    try:\n        profile_response = requests.get(profile_url, headers=headers)\n        time.sleep(0.5)  # Delay to avoid rate-limiting\n        if profile_response.status_code != 200:\n            print(f\"Failed to fetch profile for {username}: {profile_response.status_code}\")\n            name = \"\"\n        else:\n            try:\n                profile = profile_response.json()\n                # Ensure profile is valid and has data\n                if not isinstance(profile, dict):\n                    print(f\"Invalid profile data for {username}\")\n                    name = \"\"\n                else:\n                    # Get published name (leave blank if None or empty)\n                    name = profile.get(\"name\", \"\").strip() if profile.get(\"name\") else \"\"\n                    if name.lower() == \"none\" or not name:\n                        name = \"\"\n                    else:\n                        name = normalize_name(name) + \" \"\n            except ValueError:\n                print(f\"Failed to parse JSON profile for {username}\")\n                name = \"\"\n    except requests.RequestException as e:\n        print(f\"Error fetching profile for {username}: {e}\")\n        name = \"\"\n\n    # Print formatted output\n    try:\n        print(f\"* {name}[@{username}](https://github.com/{username})\")\n    except UnicodeEncodeError:\n        # Fallback: print without name if encoding fails\n        print(f\"* [@{username}](https://github.com/{username})\")\n\n"
  },
  {
    "path": "doc/index.rst",
    "content": "﻿.. Terrain3D documentation master file, created by\n   sphinx-quickstart on Wed Nov 29 15:57:36 2023.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\n.. |logo| image:: docs/images/terrain3d.jpg\n   :target: _images/terrain3d.jpg\n\n|logo|\n\nTerrain3D Documentation\n===============================\n\n`Terrain3D <https://github.com/TokisanGames/Terrain3D>`_ is a high performance, editable terrain system for Godot 4.\n\nStart with :doc:`Introduction <docs/introduction>` and the other pages in the Getting Started section in the left sidebar to learn how to use this system.\n\n\nCredit\n-------------\nDeveloped for the Godot community by:\n\n+------------------------------------+-------------------------------------+\n| **Cory Petkovsek, Tokisan Games**  | |t-x| |t-gh| |t-web| |t-ds| |t-yt|  |\n+------------------------------------+-------------------------------------+\n| **Roope Palmroos, Outobugi Games** | |o-x| |o-gh| |o-web| |o-yt|         |\n+------------------------------------+-------------------------------------+\n\n.. |t-x| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/twitter.png?raw=true\n   :target: https://twitter.com/TokisanGames\n   :width: 24\n\n.. |t-gh| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/github.png?raw=true\n   :target: https://github.com/TokisanGames\n   :width: 24\n\n.. |t-web| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true\n   :target: https://tokisan.com/\n   :width: 24\n\n.. |t-ds| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/discord.png?raw=true\n   :target: https://tokisan.com/discord\n   :width: 24\n\n.. |t-yt| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/youtube.png?raw=true\n   :target: https://www.youtube.com/@TokisanGames\n   :width: 24\n\n.. |o-x| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/twitter.png?raw=true\n   :target: https://twitter.com/outobugi\n   :width: 24\n\n.. |o-gh| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/github.png?raw=true\n   :target: https://github.com/outobugi\n   :width: 24\n\n.. |o-web| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true\n   :target: https://outobugi.com/\n   :width: 24\n\n.. |o-yt| image:: https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/youtube.png?raw=true\n   :target: https://www.youtube.com/@outobugi\n   :width: 24\n\nAnd the contribution team in :doc:`docs/authors` and displayed on `github contributors <https://github.com/TokisanGames/Terrain3D/graphs/contributors>`_.\n\n.. toctree::\n   :maxdepth: 1\n   :caption: About\n\n   docs/games\n   docs/press\n   docs/authors\n   docs/license\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Getting Started\n\n   docs/introduction\n   docs/tutorial_videos\n   docs/installation\n   docs/platforms\n   docs/user_interface\n   docs/keyboard_shortcuts\n   docs/troubleshooting\n   docs/getting_help\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Basic Usage\n\n   docs/texture_prep\n   docs/texture_painting\n   docs/heightmaps\n   docs/import_export\n   docs/instancer\n   docs/tips_technical\n   docs/tips_environment\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Advanced Usage\n\n   docs/collision\n   docs/displacement\n   docs/double_precision\n   docs/navigation\n   docs/occlusion_culling\n   docs/programming_languages\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Development\n\n   docs/nightly_builds\n   docs/building_from_source\n   docs/system_architecture\n   docs/shader_design\n   docs/controlmap_format\n   docs/data_format\n   docs/generating_csharp_bindings\n   docs/contributing\n\n\n.. toctree::\n   :maxdepth: 2\n   :caption: API\n\n   api/index\n"
  },
  {
    "path": "doc/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=.\nset BUILDDIR=_build\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.https://www.sphinx-doc.org/\n\texit /b 1\n)\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\n\n:end\npopd\n"
  },
  {
    "path": "doc/requirements.txt",
    "content": "docutils>=0.17\nmyst-parser>=3.0.0\nsphinx_rtd_theme>=1.1.1\nsphinx-rtd-dark-mode\nsphinx-tabs>=3.4.7\nsphinx<9.0.0\n"
  },
  {
    "path": "project/Terrain3D.csproj",
    "content": "<Project Sdk=\"Godot.NET.Sdk/4.4.1\">\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <EnableDynamicLoading>true</EnableDynamicLoading>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "project/Terrain3D.sln",
    "content": "﻿Microsoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 2012\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Terrain3D\", \"Terrain3D.csproj\", \"{E3C3EDF1-6587-443E-B932-EBA7704DB53E}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\tDebug|Any CPU = Debug|Any CPU\n\tExportDebug|Any CPU = ExportDebug|Any CPU\n\tExportRelease|Any CPU = ExportRelease|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{E3C3EDF1-6587-443E-B932-EBA7704DB53E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E3C3EDF1-6587-443E-B932-EBA7704DB53E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E3C3EDF1-6587-443E-B932-EBA7704DB53E}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU\n\t\t{E3C3EDF1-6587-443E-B932-EBA7704DB53E}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU\n\t\t{E3C3EDF1-6587-443E-B932-EBA7704DB53E}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU\n\t\t{E3C3EDF1-6587-443E-B932-EBA7704DB53E}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "project/addons/terrain_3d/brushes/.gdignore",
    "content": ""
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3D.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3D : Node3D\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3D\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3D object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3D() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3D\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3D\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3D\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3D\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3D Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3D wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3D);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3D).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3D)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3D\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3D\" type.</returns>\n\tpublic new static Terrain3D Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum DebugLevelEnum\n\t{\n\t\tError = 0,\n\t\tInfo = 1,\n\t\tDebug = 2,\n\t\tExtreme = 3,\n\t}\n\n\tpublic enum RegionSizeEnum\n\t{\n\t\tSize64 = 64,\n\t\tSize128 = 128,\n\t\tSize256 = 256,\n\t\tSize512 = 512,\n\t\tSize1024 = 1024,\n\t\tSize2048 = 2048,\n\t}\n\n\tpublic new static class GDExtensionSignalName\n\t{\n\t\tpublic new static readonly StringName MaterialChanged = \"material_changed\";\n\t\tpublic new static readonly StringName AssetsChanged = \"assets_changed\";\n\t}\n\n\tpublic new delegate void MaterialChangedSignalHandler();\n\tprivate MaterialChangedSignalHandler _materialChangedSignal;\n\tprivate Callable _materialChangedSignalCallable;\n\tpublic event MaterialChangedSignalHandler MaterialChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_materialChangedSignal is null)\n\t\t\t{\n\t\t\t\t_materialChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_materialChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.MaterialChanged, _materialChangedSignalCallable);\n\t\t\t}\n\t\t\t_materialChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_materialChangedSignal -= value;\n\t\t\tif (_materialChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.MaterialChanged, _materialChangedSignalCallable);\n\t\t\t_materialChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void AssetsChangedSignalHandler();\n\tprivate AssetsChangedSignalHandler _assetsChangedSignal;\n\tprivate Callable _assetsChangedSignalCallable;\n\tpublic event AssetsChangedSignalHandler AssetsChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_assetsChangedSignal is null)\n\t\t\t{\n\t\t\t\t_assetsChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_assetsChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.AssetsChanged, _assetsChangedSignalCallable);\n\t\t\t}\n\t\t\t_assetsChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_assetsChangedSignal -= value;\n\t\t\tif (_assetsChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.AssetsChanged, _assetsChangedSignalCallable);\n\t\t\t_assetsChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName Version = \"version\";\n\t\tpublic new static readonly StringName DebugLevel = \"debug_level\";\n\t\tpublic new static readonly StringName DataDirectory = \"data_directory\";\n\t\tpublic new static readonly StringName Material = \"material\";\n\t\tpublic new static readonly StringName Assets = \"assets\";\n\t\tpublic new static readonly StringName Data = \"data\";\n\t\tpublic new static readonly StringName Collision = \"collision\";\n\t\tpublic new static readonly StringName Instancer = \"instancer\";\n\t\tpublic new static readonly StringName RegionSize = \"region_size\";\n\t\tpublic new static readonly StringName Save16Bit = \"save_16_bit\";\n\t\tpublic new static readonly StringName LabelDistance = \"label_distance\";\n\t\tpublic new static readonly StringName LabelSize = \"label_size\";\n\t\tpublic new static readonly StringName ShowGrid = \"show_grid\";\n\t\tpublic new static readonly StringName CollisionMode = \"collision_mode\";\n\t\tpublic new static readonly StringName CollisionShapeSize = \"collision_shape_size\";\n\t\tpublic new static readonly StringName CollisionRadius = \"collision_radius\";\n\t\tpublic new static readonly StringName CollisionTarget = \"collision_target\";\n\t\tpublic new static readonly StringName CollisionLayer = \"collision_layer\";\n\t\tpublic new static readonly StringName CollisionMask = \"collision_mask\";\n\t\tpublic new static readonly StringName CollisionPriority = \"collision_priority\";\n\t\tpublic new static readonly StringName PhysicsMaterial = \"physics_material\";\n\t\tpublic new static readonly StringName ClipmapTarget = \"clipmap_target\";\n\t\tpublic new static readonly StringName MeshLods = \"mesh_lods\";\n\t\tpublic new static readonly StringName TessellationLevel = \"tessellation_level\";\n\t\tpublic new static readonly StringName MeshSize = \"mesh_size\";\n\t\tpublic new static readonly StringName VertexSpacing = \"vertex_spacing\";\n\t\tpublic new static readonly StringName CullMargin = \"cull_margin\";\n\t\tpublic new static readonly StringName CastShadows = \"cast_shadows\";\n\t\tpublic new static readonly StringName GiMode = \"gi_mode\";\n\t\tpublic new static readonly StringName RenderLayers = \"render_layers\";\n\t\tpublic new static readonly StringName DisplacementScale = \"displacement_scale\";\n\t\tpublic new static readonly StringName DisplacementSharpness = \"displacement_sharpness\";\n\t\tpublic new static readonly StringName BufferShaderOverrideEnabled = \"buffer_shader_override_enabled\";\n\t\tpublic new static readonly StringName BufferShaderOverride = \"buffer_shader_override\";\n\t\tpublic new static readonly StringName OceanEnabled = \"ocean_enabled\";\n\t\tpublic new static readonly StringName OceanMeshLods = \"ocean_mesh_lods\";\n\t\tpublic new static readonly StringName OceanTessellationLevel = \"ocean_tessellation_level\";\n\t\tpublic new static readonly StringName OceanMeshSize = \"ocean_mesh_size\";\n\t\tpublic new static readonly StringName OceanVertexSpacing = \"ocean_vertex_spacing\";\n\t\tpublic new static readonly StringName OceanCullMargin = \"ocean_cull_margin\";\n\t\tpublic new static readonly StringName OceanCastShadows = \"ocean_cast_shadows\";\n\t\tpublic new static readonly StringName OceanGiMode = \"ocean_gi_mode\";\n\t\tpublic new static readonly StringName OceanRenderLayers = \"ocean_render_layers\";\n\t\tpublic new static readonly StringName OceanMaterial = \"ocean_material\";\n\t\tpublic new static readonly StringName OceanLightTarget = \"ocean_light_target\";\n\t\tpublic new static readonly StringName MouseLayer = \"mouse_layer\";\n\t\tpublic new static readonly StringName FreeEditorTextures = \"free_editor_textures\";\n\t\tpublic new static readonly StringName InstancerMode = \"instancer_mode\";\n\t\tpublic new static readonly StringName ShowRegionGrid = \"show_region_grid\";\n\t\tpublic new static readonly StringName ShowInstancerGrid = \"show_instancer_grid\";\n\t\tpublic new static readonly StringName ShowVertexGrid = \"show_vertex_grid\";\n\t\tpublic new static readonly StringName ShowContours = \"show_contours\";\n\t\tpublic new static readonly StringName ShowNavigation = \"show_navigation\";\n\t\tpublic new static readonly StringName ShowCheckered = \"show_checkered\";\n\t\tpublic new static readonly StringName ShowGrey = \"show_grey\";\n\t\tpublic new static readonly StringName ShowHeightmap = \"show_heightmap\";\n\t\tpublic new static readonly StringName ShowJaggedness = \"show_jaggedness\";\n\t\tpublic new static readonly StringName ShowAutoshader = \"show_autoshader\";\n\t\tpublic new static readonly StringName ShowControlTexture = \"show_control_texture\";\n\t\tpublic new static readonly StringName ShowControlBlend = \"show_control_blend\";\n\t\tpublic new static readonly StringName ShowControlAngle = \"show_control_angle\";\n\t\tpublic new static readonly StringName ShowControlScale = \"show_control_scale\";\n\t\tpublic new static readonly StringName ShowColormap = \"show_colormap\";\n\t\tpublic new static readonly StringName ShowRoughmap = \"show_roughmap\";\n\t\tpublic new static readonly StringName ShowDisplacementBuffer = \"show_displacement_buffer\";\n\t\tpublic new static readonly StringName ShowTextureAlbedo = \"show_texture_albedo\";\n\t\tpublic new static readonly StringName ShowTextureHeight = \"show_texture_height\";\n\t\tpublic new static readonly StringName ShowTextureNormal = \"show_texture_normal\";\n\t\tpublic new static readonly StringName ShowTextureRough = \"show_texture_rough\";\n\t\tpublic new static readonly StringName ShowTextureAo = \"show_texture_ao\";\n\t}\n\n\tpublic new string Version\n\t{\n\t\tget => Get(GDExtensionPropertyName.Version).As<string>();\n\t}\n\n\tpublic new Variant DebugLevel\n\t{\n\t\tget => Get(GDExtensionPropertyName.DebugLevel).As<Variant>();\n\t\tset => Set(GDExtensionPropertyName.DebugLevel, value);\n\t}\n\n\tpublic new string DataDirectory\n\t{\n\t\tget => Get(GDExtensionPropertyName.DataDirectory).As<string>();\n\t\tset => Set(GDExtensionPropertyName.DataDirectory, value);\n\t}\n\n\tpublic new Terrain3DMaterial Material\n\t{\n\t\tget => Terrain3DMaterial.Bind(Get(GDExtensionPropertyName.Material).As<Resource>());\n\t\tset => Set(GDExtensionPropertyName.Material, value);\n\t}\n\n\tpublic new Terrain3DAssets Assets\n\t{\n\t\tget => Terrain3DAssets.Bind(Get(GDExtensionPropertyName.Assets).As<Resource>());\n\t\tset => Set(GDExtensionPropertyName.Assets, value);\n\t}\n\n\tpublic new Terrain3DData Data\n\t{\n\t\tget => Terrain3DData.Bind(Get(GDExtensionPropertyName.Data).As<GodotObject>());\n\t}\n\n\tpublic new Terrain3DCollision Collision\n\t{\n\t\tget => Terrain3DCollision.Bind(Get(GDExtensionPropertyName.Collision).As<GodotObject>());\n\t}\n\n\tpublic new Terrain3DInstancer Instancer\n\t{\n\t\tget => Terrain3DInstancer.Bind(Get(GDExtensionPropertyName.Instancer).As<GodotObject>());\n\t}\n\n\tpublic new Variant RegionSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.RegionSize).As<Variant>();\n\t\tset => Set(GDExtensionPropertyName.RegionSize, value);\n\t}\n\n\tpublic new bool Save16Bit\n\t{\n\t\tget => Get(GDExtensionPropertyName.Save16Bit).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.Save16Bit, value);\n\t}\n\n\tpublic new double LabelDistance\n\t{\n\t\tget => Get(GDExtensionPropertyName.LabelDistance).As<double>();\n\t\tset => Set(GDExtensionPropertyName.LabelDistance, value);\n\t}\n\n\tpublic new long LabelSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.LabelSize).As<long>();\n\t\tset => Set(GDExtensionPropertyName.LabelSize, value);\n\t}\n\n\tpublic new bool ShowGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowGrid, value);\n\t}\n\n\tpublic new Variant CollisionMode\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionMode).As<Variant>();\n\t\tset => Set(GDExtensionPropertyName.CollisionMode, value);\n\t}\n\n\tpublic new long CollisionShapeSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionShapeSize).As<long>();\n\t\tset => Set(GDExtensionPropertyName.CollisionShapeSize, value);\n\t}\n\n\tpublic new long CollisionRadius\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionRadius).As<long>();\n\t\tset => Set(GDExtensionPropertyName.CollisionRadius, value);\n\t}\n\n\tpublic new Node3D CollisionTarget\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionTarget).As<Node3D>();\n\t\tset => Set(GDExtensionPropertyName.CollisionTarget, value);\n\t}\n\n\tpublic new long CollisionLayer\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionLayer).As<long>();\n\t\tset => Set(GDExtensionPropertyName.CollisionLayer, value);\n\t}\n\n\tpublic new long CollisionMask\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionMask).As<long>();\n\t\tset => Set(GDExtensionPropertyName.CollisionMask, value);\n\t}\n\n\tpublic new double CollisionPriority\n\t{\n\t\tget => Get(GDExtensionPropertyName.CollisionPriority).As<double>();\n\t\tset => Set(GDExtensionPropertyName.CollisionPriority, value);\n\t}\n\n\tpublic new PhysicsMaterial PhysicsMaterial\n\t{\n\t\tget => Get(GDExtensionPropertyName.PhysicsMaterial).As<PhysicsMaterial>();\n\t\tset => Set(GDExtensionPropertyName.PhysicsMaterial, value);\n\t}\n\n\tpublic new Node3D ClipmapTarget\n\t{\n\t\tget => Get(GDExtensionPropertyName.ClipmapTarget).As<Node3D>();\n\t\tset => Set(GDExtensionPropertyName.ClipmapTarget, value);\n\t}\n\n\tpublic new long MeshLods\n\t{\n\t\tget => Get(GDExtensionPropertyName.MeshLods).As<long>();\n\t\tset => Set(GDExtensionPropertyName.MeshLods, value);\n\t}\n\n\tpublic new long TessellationLevel\n\t{\n\t\tget => Get(GDExtensionPropertyName.TessellationLevel).As<long>();\n\t\tset => Set(GDExtensionPropertyName.TessellationLevel, value);\n\t}\n\n\tpublic new long MeshSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.MeshSize).As<long>();\n\t\tset => Set(GDExtensionPropertyName.MeshSize, value);\n\t}\n\n\tpublic new double VertexSpacing\n\t{\n\t\tget => Get(GDExtensionPropertyName.VertexSpacing).As<double>();\n\t\tset => Set(GDExtensionPropertyName.VertexSpacing, value);\n\t}\n\n\tpublic new double CullMargin\n\t{\n\t\tget => Get(GDExtensionPropertyName.CullMargin).As<double>();\n\t\tset => Set(GDExtensionPropertyName.CullMargin, value);\n\t}\n\n\tpublic new long CastShadows\n\t{\n\t\tget => Get(GDExtensionPropertyName.CastShadows).As<long>();\n\t\tset => Set(GDExtensionPropertyName.CastShadows, value);\n\t}\n\n\tpublic new long GiMode\n\t{\n\t\tget => Get(GDExtensionPropertyName.GiMode).As<long>();\n\t\tset => Set(GDExtensionPropertyName.GiMode, value);\n\t}\n\n\tpublic new long RenderLayers\n\t{\n\t\tget => Get(GDExtensionPropertyName.RenderLayers).As<long>();\n\t\tset => Set(GDExtensionPropertyName.RenderLayers, value);\n\t}\n\n\tpublic new double DisplacementScale\n\t{\n\t\tget => Get(GDExtensionPropertyName.DisplacementScale).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DisplacementScale, value);\n\t}\n\n\tpublic new double DisplacementSharpness\n\t{\n\t\tget => Get(GDExtensionPropertyName.DisplacementSharpness).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DisplacementSharpness, value);\n\t}\n\n\tpublic new bool BufferShaderOverrideEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.BufferShaderOverrideEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.BufferShaderOverrideEnabled, value);\n\t}\n\n\tpublic new Shader BufferShaderOverride\n\t{\n\t\tget => Get(GDExtensionPropertyName.BufferShaderOverride).As<Shader>();\n\t\tset => Set(GDExtensionPropertyName.BufferShaderOverride, value);\n\t}\n\n\tpublic new bool OceanEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.OceanEnabled, value);\n\t}\n\n\tpublic new long OceanMeshLods\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanMeshLods).As<long>();\n\t\tset => Set(GDExtensionPropertyName.OceanMeshLods, value);\n\t}\n\n\tpublic new long OceanTessellationLevel\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanTessellationLevel).As<long>();\n\t\tset => Set(GDExtensionPropertyName.OceanTessellationLevel, value);\n\t}\n\n\tpublic new long OceanMeshSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanMeshSize).As<long>();\n\t\tset => Set(GDExtensionPropertyName.OceanMeshSize, value);\n\t}\n\n\tpublic new double OceanVertexSpacing\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanVertexSpacing).As<double>();\n\t\tset => Set(GDExtensionPropertyName.OceanVertexSpacing, value);\n\t}\n\n\tpublic new double OceanCullMargin\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanCullMargin).As<double>();\n\t\tset => Set(GDExtensionPropertyName.OceanCullMargin, value);\n\t}\n\n\tpublic new long OceanCastShadows\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanCastShadows).As<long>();\n\t\tset => Set(GDExtensionPropertyName.OceanCastShadows, value);\n\t}\n\n\tpublic new long OceanGiMode\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanGiMode).As<long>();\n\t\tset => Set(GDExtensionPropertyName.OceanGiMode, value);\n\t}\n\n\tpublic new long OceanRenderLayers\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanRenderLayers).As<long>();\n\t\tset => Set(GDExtensionPropertyName.OceanRenderLayers, value);\n\t}\n\n\tpublic new Material OceanMaterial\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanMaterial).As<Material>();\n\t\tset => Set(GDExtensionPropertyName.OceanMaterial, value);\n\t}\n\n\tpublic new Node3D OceanLightTarget\n\t{\n\t\tget => Get(GDExtensionPropertyName.OceanLightTarget).As<Node3D>();\n\t\tset => Set(GDExtensionPropertyName.OceanLightTarget, value);\n\t}\n\n\tpublic new long MouseLayer\n\t{\n\t\tget => Get(GDExtensionPropertyName.MouseLayer).As<long>();\n\t\tset => Set(GDExtensionPropertyName.MouseLayer, value);\n\t}\n\n\tpublic new bool FreeEditorTextures\n\t{\n\t\tget => Get(GDExtensionPropertyName.FreeEditorTextures).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.FreeEditorTextures, value);\n\t}\n\n\tpublic new Terrain3DInstancer.InstancerMode InstancerMode\n\t{\n\t\tget => Get(GDExtensionPropertyName.InstancerMode).As<Terrain3DInstancer.InstancerMode>();\n\t\tset => Set(GDExtensionPropertyName.InstancerMode, Variant.From(value));\n\t}\n\n\tpublic new bool ShowRegionGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowRegionGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowRegionGrid, value);\n\t}\n\n\tpublic new bool ShowInstancerGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowInstancerGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowInstancerGrid, value);\n\t}\n\n\tpublic new bool ShowVertexGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowVertexGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowVertexGrid, value);\n\t}\n\n\tpublic new bool ShowContours\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowContours).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowContours, value);\n\t}\n\n\tpublic new bool ShowNavigation\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowNavigation).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowNavigation, value);\n\t}\n\n\tpublic new bool ShowCheckered\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowCheckered).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowCheckered, value);\n\t}\n\n\tpublic new bool ShowGrey\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowGrey).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowGrey, value);\n\t}\n\n\tpublic new bool ShowHeightmap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowHeightmap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowHeightmap, value);\n\t}\n\n\tpublic new bool ShowJaggedness\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowJaggedness).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowJaggedness, value);\n\t}\n\n\tpublic new bool ShowAutoshader\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowAutoshader).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowAutoshader, value);\n\t}\n\n\tpublic new bool ShowControlTexture\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlTexture).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlTexture, value);\n\t}\n\n\tpublic new bool ShowControlBlend\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlBlend).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlBlend, value);\n\t}\n\n\tpublic new bool ShowControlAngle\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlAngle).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlAngle, value);\n\t}\n\n\tpublic new bool ShowControlScale\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlScale).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlScale, value);\n\t}\n\n\tpublic new bool ShowColormap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowColormap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowColormap, value);\n\t}\n\n\tpublic new bool ShowRoughmap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowRoughmap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowRoughmap, value);\n\t}\n\n\tpublic new bool ShowDisplacementBuffer\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowDisplacementBuffer).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowDisplacementBuffer, value);\n\t}\n\n\tpublic new bool ShowTextureAlbedo\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureAlbedo).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureAlbedo, value);\n\t}\n\n\tpublic new bool ShowTextureHeight\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureHeight).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureHeight, value);\n\t}\n\n\tpublic new bool ShowTextureNormal\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureNormal).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureNormal, value);\n\t}\n\n\tpublic new bool ShowTextureRough\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureRough).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureRough, value);\n\t}\n\n\tpublic new bool ShowTextureAo\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureAo).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureAo, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName SetEditor = \"set_editor\";\n\t\tpublic new static readonly StringName GetEditor = \"get_editor\";\n\t\tpublic new static readonly StringName SetPlugin = \"set_plugin\";\n\t\tpublic new static readonly StringName GetPlugin = \"get_plugin\";\n\t\tpublic new static readonly StringName SetCamera = \"set_camera\";\n\t\tpublic new static readonly StringName GetCamera = \"get_camera\";\n\t\tpublic new static readonly StringName GetClipmapTargetPosition = \"get_clipmap_target_position\";\n\t\tpublic new static readonly StringName GetCollisionTargetPosition = \"get_collision_target_position\";\n\t\tpublic new static readonly StringName Snap = \"snap\";\n\t\tpublic new static readonly StringName GetIntersection = \"get_intersection\";\n\t\tpublic new static readonly StringName GetRaycastResult = \"get_raycast_result\";\n\t\tpublic new static readonly StringName BakeMesh = \"bake_mesh\";\n\t\tpublic new static readonly StringName GenerateNavMeshSourceGeometry = \"generate_nav_mesh_source_geometry\";\n\t}\n\n\tpublic new void SetEditor(Terrain3DEditor editor) => \n\t\tCall(GDExtensionMethodName.SetEditor, [editor]);\n\n\tpublic new Terrain3DEditor GetEditor() => \n\t\tTerrain3DEditor.Bind(Call(GDExtensionMethodName.GetEditor, []).As<GodotObject>());\n\n\tpublic new void SetPlugin(GodotObject plugin) => \n\t\tCall(GDExtensionMethodName.SetPlugin, [plugin]);\n\n\tpublic new GodotObject GetPlugin() => \n\t\tCall(GDExtensionMethodName.GetPlugin, []).As<GodotObject>();\n\n\tpublic new void SetCamera(Camera3D camera) => \n\t\tCall(GDExtensionMethodName.SetCamera, [camera]);\n\n\tpublic new Camera3D GetCamera() => \n\t\tCall(GDExtensionMethodName.GetCamera, []).As<Camera3D>();\n\n\tpublic new Vector3 GetClipmapTargetPosition() => \n\t\tCall(GDExtensionMethodName.GetClipmapTargetPosition, []).As<Vector3>();\n\n\tpublic new Vector3 GetCollisionTargetPosition() => \n\t\tCall(GDExtensionMethodName.GetCollisionTargetPosition, []).As<Vector3>();\n\n\tpublic new void Snap() => \n\t\tCall(GDExtensionMethodName.Snap, []);\n\n\tpublic new Vector3 GetIntersection(Vector3 srcPos, Vector3 direction, bool gpuMode = false) => \n\t\tCall(GDExtensionMethodName.GetIntersection, [srcPos, direction, gpuMode]).As<Vector3>();\n\n\tpublic new Godot.Collections.Dictionary GetRaycastResult(Vector3 srcPos, Vector3 direction, long collisionMask = 4294967295, bool excludeTerrain = false) => \n\t\tCall(GDExtensionMethodName.GetRaycastResult, [srcPos, direction, collisionMask, excludeTerrain]).As<Godot.Collections.Dictionary>();\n\n\tpublic new Mesh BakeMesh(long lod, Terrain3DData.HeightFilter filter = Terrain3DData.HeightFilter.Nearest) => \n\t\tCall(GDExtensionMethodName.BakeMesh, [lod, Variant.From(filter)]).As<Mesh>();\n\n\tpublic new Vector3[] GenerateNavMeshSourceGeometry(Aabb globalAabb, bool requireNav = true) => \n\t\tCall(GDExtensionMethodName.GenerateNavMeshSourceGeometry, [globalAabb, requireNav]).As<Vector3[]>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3D.cs.uid",
    "content": "uid://des50dgjni8ga\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DAssets.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DAssets : Resource\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DAssets\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DAssets object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DAssets() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DAssets\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DAssets\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DAssets\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DAssets\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DAssets Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DAssets wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DAssets);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DAssets).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DAssets)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DAssets\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DAssets\" type.</returns>\n\tpublic new static Terrain3DAssets Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum AssetType\n\t{\n\t\tTexture = 0,\n\t\tMesh = 1,\n\t}\n\n\tpublic new static class GDExtensionSignalName\n\t{\n\t\tpublic new static readonly StringName MeshesChanged = \"meshes_changed\";\n\t\tpublic new static readonly StringName TexturesChanged = \"textures_changed\";\n\t}\n\n\tpublic new delegate void MeshesChangedSignalHandler();\n\tprivate MeshesChangedSignalHandler _meshesChangedSignal;\n\tprivate Callable _meshesChangedSignalCallable;\n\tpublic event MeshesChangedSignalHandler MeshesChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_meshesChangedSignal is null)\n\t\t\t{\n\t\t\t\t_meshesChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_meshesChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.MeshesChanged, _meshesChangedSignalCallable);\n\t\t\t}\n\t\t\t_meshesChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_meshesChangedSignal -= value;\n\t\t\tif (_meshesChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.MeshesChanged, _meshesChangedSignalCallable);\n\t\t\t_meshesChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void TexturesChangedSignalHandler();\n\tprivate TexturesChangedSignalHandler _texturesChangedSignal;\n\tprivate Callable _texturesChangedSignalCallable;\n\tpublic event TexturesChangedSignalHandler TexturesChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_texturesChangedSignal is null)\n\t\t\t{\n\t\t\t\t_texturesChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_texturesChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.TexturesChanged, _texturesChangedSignalCallable);\n\t\t\t}\n\t\t\t_texturesChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_texturesChangedSignal -= value;\n\t\t\tif (_texturesChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.TexturesChanged, _texturesChangedSignalCallable);\n\t\t\t_texturesChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName MeshList = \"mesh_list\";\n\t\tpublic new static readonly StringName TextureList = \"texture_list\";\n\t}\n\n\tpublic new Godot.Collections.Array MeshList\n\t{\n\t\tget => Get(GDExtensionPropertyName.MeshList).As<Godot.Collections.Array>();\n\t\tset => Set(GDExtensionPropertyName.MeshList, value);\n\t}\n\n\tpublic new Godot.Collections.Array TextureList\n\t{\n\t\tget => Get(GDExtensionPropertyName.TextureList).As<Godot.Collections.Array>();\n\t\tset => Set(GDExtensionPropertyName.TextureList, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName SetTextureAsset = \"set_texture_asset\";\n\t\tpublic new static readonly StringName GetTextureAsset = \"get_texture_asset\";\n\t\tpublic new static readonly StringName GetTextureCount = \"get_texture_count\";\n\t\tpublic new static readonly StringName GetAlbedoArrayRid = \"get_albedo_array_rid\";\n\t\tpublic new static readonly StringName GetNormalArrayRid = \"get_normal_array_rid\";\n\t\tpublic new static readonly StringName GetTextureColors = \"get_texture_colors\";\n\t\tpublic new static readonly StringName GetTextureNormalDepths = \"get_texture_normal_depths\";\n\t\tpublic new static readonly StringName GetTextureAoStrengths = \"get_texture_ao_strengths\";\n\t\tpublic new static readonly StringName GetTextureAoLightAffects = \"get_texture_ao_light_affects\";\n\t\tpublic new static readonly StringName GetTextureRoughnessMods = \"get_texture_roughness_mods\";\n\t\tpublic new static readonly StringName GetTextureUvScales = \"get_texture_uv_scales\";\n\t\tpublic new static readonly StringName GetTextureDetiles = \"get_texture_detiles\";\n\t\tpublic new static readonly StringName GetTextureDisplacements = \"get_texture_displacements\";\n\t\tpublic new static readonly StringName ClearTextures = \"clear_textures\";\n\t\tpublic new static readonly StringName UpdateTextureList = \"update_texture_list\";\n\t\tpublic new static readonly StringName SetMeshAsset = \"set_mesh_asset\";\n\t\tpublic new static readonly StringName GetMeshAsset = \"get_mesh_asset\";\n\t\tpublic new static readonly StringName GetMeshCount = \"get_mesh_count\";\n\t\tpublic new static readonly StringName CreateMeshThumbnails = \"create_mesh_thumbnails\";\n\t\tpublic new static readonly StringName UpdateMeshList = \"update_mesh_list\";\n\t\tpublic new static readonly StringName Save = \"save\";\n\t}\n\n\tpublic new void SetTextureAsset(long id, Terrain3DTextureAsset texture) => \n\t\tCall(GDExtensionMethodName.SetTextureAsset, [id, texture]);\n\n\tpublic new Terrain3DTextureAsset GetTextureAsset(long id) => \n\t\tTerrain3DTextureAsset.Bind(Call(GDExtensionMethodName.GetTextureAsset, [id]).As<Resource>());\n\n\tpublic new long GetTextureCount() => \n\t\tCall(GDExtensionMethodName.GetTextureCount, []).As<long>();\n\n\tpublic new Rid GetAlbedoArrayRid() => \n\t\tCall(GDExtensionMethodName.GetAlbedoArrayRid, []).As<Rid>();\n\n\tpublic new Rid GetNormalArrayRid() => \n\t\tCall(GDExtensionMethodName.GetNormalArrayRid, []).As<Rid>();\n\n\tpublic new Color[] GetTextureColors() => \n\t\tCall(GDExtensionMethodName.GetTextureColors, []).As<Color[]>();\n\n\tpublic new float[] GetTextureNormalDepths() => \n\t\tCall(GDExtensionMethodName.GetTextureNormalDepths, []).As<float[]>();\n\n\tpublic new float[] GetTextureAoStrengths() => \n\t\tCall(GDExtensionMethodName.GetTextureAoStrengths, []).As<float[]>();\n\n\tpublic new float[] GetTextureAoLightAffects() => \n\t\tCall(GDExtensionMethodName.GetTextureAoLightAffects, []).As<float[]>();\n\n\tpublic new float[] GetTextureRoughnessMods() => \n\t\tCall(GDExtensionMethodName.GetTextureRoughnessMods, []).As<float[]>();\n\n\tpublic new float[] GetTextureUvScales() => \n\t\tCall(GDExtensionMethodName.GetTextureUvScales, []).As<float[]>();\n\n\tpublic new Vector2[] GetTextureDetiles() => \n\t\tCall(GDExtensionMethodName.GetTextureDetiles, []).As<Vector2[]>();\n\n\tpublic new Vector2[] GetTextureDisplacements() => \n\t\tCall(GDExtensionMethodName.GetTextureDisplacements, []).As<Vector2[]>();\n\n\tpublic new void ClearTextures(bool update = false) => \n\t\tCall(GDExtensionMethodName.ClearTextures, [update]);\n\n\tpublic new void UpdateTextureList() => \n\t\tCall(GDExtensionMethodName.UpdateTextureList, []);\n\n\tpublic new void SetMeshAsset(long id, Terrain3DMeshAsset mesh) => \n\t\tCall(GDExtensionMethodName.SetMeshAsset, [id, mesh]);\n\n\tpublic new Terrain3DMeshAsset GetMeshAsset(long id) => \n\t\tTerrain3DMeshAsset.Bind(Call(GDExtensionMethodName.GetMeshAsset, [id]).As<Resource>());\n\n\tpublic new long GetMeshCount() => \n\t\tCall(GDExtensionMethodName.GetMeshCount, []).As<long>();\n\n\tpublic new void CreateMeshThumbnails(long id = -1, Vector2I size = default, bool force = false) => \n\t\tCall(GDExtensionMethodName.CreateMeshThumbnails, [id, size, force]);\n\n\tpublic new void UpdateMeshList() => \n\t\tCall(GDExtensionMethodName.UpdateMeshList, []);\n\n\tpublic new Error Save(string path = \"\") => \n\t\tCall(GDExtensionMethodName.Save, [path]).As<Error>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DAssets.cs.uid",
    "content": "uid://myg4xtgeyn1n\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DCollision.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DCollision : GodotObject\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DCollision\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DCollision object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DCollision() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DCollision\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DCollision\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DCollision\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DCollision\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DCollision Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DCollision wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DCollision);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DCollision).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DCollision)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DCollision\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DCollision\" type.</returns>\n\tpublic new static Terrain3DCollision Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum CollisionMode\n\t{\n\t\tDisabled = 0,\n\t\tDynamicGame = 1,\n\t\tDynamicEditor = 2,\n\t\tFullGame = 3,\n\t\tFullEditor = 4,\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName Mode = \"mode\";\n\t\tpublic new static readonly StringName ShapeSize = \"shape_size\";\n\t\tpublic new static readonly StringName Radius = \"radius\";\n\t\tpublic new static readonly StringName Layer = \"layer\";\n\t\tpublic new static readonly StringName Mask = \"mask\";\n\t\tpublic new static readonly StringName Priority = \"priority\";\n\t\tpublic new static readonly StringName PhysicsMaterial = \"physics_material\";\n\t}\n\n\tpublic new Variant Mode\n\t{\n\t\tget => Get(GDExtensionPropertyName.Mode).As<Variant>();\n\t\tset => Set(GDExtensionPropertyName.Mode, value);\n\t}\n\n\tpublic new long ShapeSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShapeSize).As<long>();\n\t\tset => Set(GDExtensionPropertyName.ShapeSize, value);\n\t}\n\n\tpublic new long Radius\n\t{\n\t\tget => Get(GDExtensionPropertyName.Radius).As<long>();\n\t\tset => Set(GDExtensionPropertyName.Radius, value);\n\t}\n\n\tpublic new long Layer\n\t{\n\t\tget => Get(GDExtensionPropertyName.Layer).As<long>();\n\t\tset => Set(GDExtensionPropertyName.Layer, value);\n\t}\n\n\tpublic new long Mask\n\t{\n\t\tget => Get(GDExtensionPropertyName.Mask).As<long>();\n\t\tset => Set(GDExtensionPropertyName.Mask, value);\n\t}\n\n\tpublic new double Priority\n\t{\n\t\tget => Get(GDExtensionPropertyName.Priority).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Priority, value);\n\t}\n\n\tpublic new PhysicsMaterial PhysicsMaterial\n\t{\n\t\tget => Get(GDExtensionPropertyName.PhysicsMaterial).As<PhysicsMaterial>();\n\t\tset => Set(GDExtensionPropertyName.PhysicsMaterial, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName Build = \"build\";\n\t\tpublic new static readonly StringName Update = \"update\";\n\t\tpublic new static readonly StringName Destroy = \"destroy\";\n\t\tpublic new static readonly StringName IsEnabled = \"is_enabled\";\n\t\tpublic new static readonly StringName IsEditorMode = \"is_editor_mode\";\n\t\tpublic new static readonly StringName IsDynamicMode = \"is_dynamic_mode\";\n\t\tpublic new static readonly StringName GetRid = \"get_rid\";\n\t}\n\n\tpublic new void Build() => \n\t\tCall(GDExtensionMethodName.Build, []);\n\n\tpublic new void Update(bool rebuild = false) => \n\t\tCall(GDExtensionMethodName.Update, [rebuild]);\n\n\tpublic new void Destroy() => \n\t\tCall(GDExtensionMethodName.Destroy, []);\n\n\tpublic new bool IsEnabled() => \n\t\tCall(GDExtensionMethodName.IsEnabled, []).As<bool>();\n\n\tpublic new bool IsEditorMode() => \n\t\tCall(GDExtensionMethodName.IsEditorMode, []).As<bool>();\n\n\tpublic new bool IsDynamicMode() => \n\t\tCall(GDExtensionMethodName.IsDynamicMode, []).As<bool>();\n\n\tpublic new Rid GetRid() => \n\t\tCall(GDExtensionMethodName.GetRid, []).As<Rid>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DCollision.cs.uid",
    "content": "uid://yab31a0ysqa3\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DData.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DData : GodotObject\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DData\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DData object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DData() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DData\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DData\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DData\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DData\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DData Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DData wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DData);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DData).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DData)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DData\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DData\" type.</returns>\n\tpublic new static Terrain3DData Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum HeightFilter\n\t{\n\t\tNearest = 0,\n\t\tMinimum = 1,\n\t}\n\n\tpublic new static class GDExtensionSignalName\n\t{\n\t\tpublic new static readonly StringName MapsChanged = \"maps_changed\";\n\t\tpublic new static readonly StringName RegionMapChanged = \"region_map_changed\";\n\t\tpublic new static readonly StringName HeightMapsChanged = \"height_maps_changed\";\n\t\tpublic new static readonly StringName ControlMapsChanged = \"control_maps_changed\";\n\t\tpublic new static readonly StringName ColorMapsChanged = \"color_maps_changed\";\n\t\tpublic new static readonly StringName MapsEdited = \"maps_edited\";\n\t}\n\n\tpublic new delegate void MapsChangedSignalHandler();\n\tprivate MapsChangedSignalHandler _mapsChangedSignal;\n\tprivate Callable _mapsChangedSignalCallable;\n\tpublic event MapsChangedSignalHandler MapsChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_mapsChangedSignal is null)\n\t\t\t{\n\t\t\t\t_mapsChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_mapsChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.MapsChanged, _mapsChangedSignalCallable);\n\t\t\t}\n\t\t\t_mapsChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_mapsChangedSignal -= value;\n\t\t\tif (_mapsChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.MapsChanged, _mapsChangedSignalCallable);\n\t\t\t_mapsChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void RegionMapChangedSignalHandler();\n\tprivate RegionMapChangedSignalHandler _regionMapChangedSignal;\n\tprivate Callable _regionMapChangedSignalCallable;\n\tpublic event RegionMapChangedSignalHandler RegionMapChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_regionMapChangedSignal is null)\n\t\t\t{\n\t\t\t\t_regionMapChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_regionMapChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.RegionMapChanged, _regionMapChangedSignalCallable);\n\t\t\t}\n\t\t\t_regionMapChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_regionMapChangedSignal -= value;\n\t\t\tif (_regionMapChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.RegionMapChanged, _regionMapChangedSignalCallable);\n\t\t\t_regionMapChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void HeightMapsChangedSignalHandler();\n\tprivate HeightMapsChangedSignalHandler _heightMapsChangedSignal;\n\tprivate Callable _heightMapsChangedSignalCallable;\n\tpublic event HeightMapsChangedSignalHandler HeightMapsChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_heightMapsChangedSignal is null)\n\t\t\t{\n\t\t\t\t_heightMapsChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_heightMapsChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.HeightMapsChanged, _heightMapsChangedSignalCallable);\n\t\t\t}\n\t\t\t_heightMapsChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_heightMapsChangedSignal -= value;\n\t\t\tif (_heightMapsChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.HeightMapsChanged, _heightMapsChangedSignalCallable);\n\t\t\t_heightMapsChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void ControlMapsChangedSignalHandler();\n\tprivate ControlMapsChangedSignalHandler _controlMapsChangedSignal;\n\tprivate Callable _controlMapsChangedSignalCallable;\n\tpublic event ControlMapsChangedSignalHandler ControlMapsChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_controlMapsChangedSignal is null)\n\t\t\t{\n\t\t\t\t_controlMapsChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_controlMapsChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.ControlMapsChanged, _controlMapsChangedSignalCallable);\n\t\t\t}\n\t\t\t_controlMapsChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_controlMapsChangedSignal -= value;\n\t\t\tif (_controlMapsChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.ControlMapsChanged, _controlMapsChangedSignalCallable);\n\t\t\t_controlMapsChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void ColorMapsChangedSignalHandler();\n\tprivate ColorMapsChangedSignalHandler _colorMapsChangedSignal;\n\tprivate Callable _colorMapsChangedSignalCallable;\n\tpublic event ColorMapsChangedSignalHandler ColorMapsChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_colorMapsChangedSignal is null)\n\t\t\t{\n\t\t\t\t_colorMapsChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_colorMapsChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.ColorMapsChanged, _colorMapsChangedSignalCallable);\n\t\t\t}\n\t\t\t_colorMapsChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_colorMapsChangedSignal -= value;\n\t\t\tif (_colorMapsChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.ColorMapsChanged, _colorMapsChangedSignalCallable);\n\t\t\t_colorMapsChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void MapsEditedSignalHandler(Aabb editedArea);\n\tprivate MapsEditedSignalHandler _mapsEditedSignal;\n\tprivate Callable _mapsEditedSignalCallable;\n\tpublic event MapsEditedSignalHandler MapsEditedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_mapsEditedSignal is null)\n\t\t\t{\n\t\t\t\t_mapsEditedSignalCallable = Callable.From((Variant editedArea) => \n\t\t\t\t\t_mapsEditedSignal?.Invoke(editedArea.As<Aabb>()));\n\t\t\t\tConnect(GDExtensionSignalName.MapsEdited, _mapsEditedSignalCallable);\n\t\t\t}\n\t\t\t_mapsEditedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_mapsEditedSignal -= value;\n\t\t\tif (_mapsEditedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.MapsEdited, _mapsEditedSignalCallable);\n\t\t\t_mapsEditedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName RegionLocations = \"region_locations\";\n\t\tpublic new static readonly StringName HeightMaps = \"height_maps\";\n\t\tpublic new static readonly StringName ControlMaps = \"control_maps\";\n\t\tpublic new static readonly StringName ColorMaps = \"color_maps\";\n\t}\n\n\tpublic new Godot.Collections.Array RegionLocations\n\t{\n\t\tget => Get(GDExtensionPropertyName.RegionLocations).As<Godot.Collections.Array>();\n\t\tset => Set(GDExtensionPropertyName.RegionLocations, value);\n\t}\n\n\tpublic new Godot.Collections.Array HeightMaps\n\t{\n\t\tget => Get(GDExtensionPropertyName.HeightMaps).As<Godot.Collections.Array>();\n\t}\n\n\tpublic new Godot.Collections.Array ControlMaps\n\t{\n\t\tget => Get(GDExtensionPropertyName.ControlMaps).As<Godot.Collections.Array>();\n\t}\n\n\tpublic new Godot.Collections.Array ColorMaps\n\t{\n\t\tget => Get(GDExtensionPropertyName.ColorMaps).As<Godot.Collections.Array>();\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName GetRegionCount = \"get_region_count\";\n\t\tpublic new static readonly StringName GetRegionsActive = \"get_regions_active\";\n\t\tpublic new static readonly StringName GetRegionsAll = \"get_regions_all\";\n\t\tpublic new static readonly StringName GetRegionMap = \"get_region_map\";\n\t\tpublic new static readonly StringName GetRegionMapIndex = \"get_region_map_index\";\n\t\tpublic new static readonly StringName DoForRegions = \"do_for_regions\";\n\t\tpublic new static readonly StringName ChangeRegionSize = \"change_region_size\";\n\t\tpublic new static readonly StringName GetRegionLocation = \"get_region_location\";\n\t\tpublic new static readonly StringName GetRegionId = \"get_region_id\";\n\t\tpublic new static readonly StringName GetRegionIdp = \"get_region_idp\";\n\t\tpublic new static readonly StringName HasRegion = \"has_region\";\n\t\tpublic new static readonly StringName HasRegionp = \"has_regionp\";\n\t\tpublic new static readonly StringName GetRegion = \"get_region\";\n\t\tpublic new static readonly StringName GetRegionp = \"get_regionp\";\n\t\tpublic new static readonly StringName SetRegionModified = \"set_region_modified\";\n\t\tpublic new static readonly StringName IsRegionModified = \"is_region_modified\";\n\t\tpublic new static readonly StringName SetRegionDeleted = \"set_region_deleted\";\n\t\tpublic new static readonly StringName IsRegionDeleted = \"is_region_deleted\";\n\t\tpublic new static readonly StringName AddRegionBlankp = \"add_region_blankp\";\n\t\tpublic new static readonly StringName AddRegionBlank = \"add_region_blank\";\n\t\tpublic new static readonly StringName AddRegion = \"add_region\";\n\t\tpublic new static readonly StringName RemoveRegionp = \"remove_regionp\";\n\t\tpublic new static readonly StringName RemoveRegionl = \"remove_regionl\";\n\t\tpublic new static readonly StringName RemoveRegion = \"remove_region\";\n\t\tpublic new static readonly StringName SaveDirectory = \"save_directory\";\n\t\tpublic new static readonly StringName SaveRegion = \"save_region\";\n\t\tpublic new static readonly StringName LoadDirectory = \"load_directory\";\n\t\tpublic new static readonly StringName LoadRegion = \"load_region\";\n\t\tpublic new static readonly StringName GetMaps = \"get_maps\";\n\t\tpublic new static readonly StringName UpdateMaps = \"update_maps\";\n\t\tpublic new static readonly StringName GetHeightMapsRid = \"get_height_maps_rid\";\n\t\tpublic new static readonly StringName GetControlMapsRid = \"get_control_maps_rid\";\n\t\tpublic new static readonly StringName GetColorMapsRid = \"get_color_maps_rid\";\n\t\tpublic new static readonly StringName SetPixel = \"set_pixel\";\n\t\tpublic new static readonly StringName GetPixel = \"get_pixel\";\n\t\tpublic new static readonly StringName SetHeight = \"set_height\";\n\t\tpublic new static readonly StringName GetHeight = \"get_height\";\n\t\tpublic new static readonly StringName SetColor = \"set_color\";\n\t\tpublic new static readonly StringName GetColor = \"get_color\";\n\t\tpublic new static readonly StringName SetControl = \"set_control\";\n\t\tpublic new static readonly StringName GetControl = \"get_control\";\n\t\tpublic new static readonly StringName SetRoughness = \"set_roughness\";\n\t\tpublic new static readonly StringName GetRoughness = \"get_roughness\";\n\t\tpublic new static readonly StringName SetControlBaseId = \"set_control_base_id\";\n\t\tpublic new static readonly StringName GetControlBaseId = \"get_control_base_id\";\n\t\tpublic new static readonly StringName SetControlOverlayId = \"set_control_overlay_id\";\n\t\tpublic new static readonly StringName GetControlOverlayId = \"get_control_overlay_id\";\n\t\tpublic new static readonly StringName SetControlBlend = \"set_control_blend\";\n\t\tpublic new static readonly StringName GetControlBlend = \"get_control_blend\";\n\t\tpublic new static readonly StringName SetControlAngle = \"set_control_angle\";\n\t\tpublic new static readonly StringName GetControlAngle = \"get_control_angle\";\n\t\tpublic new static readonly StringName SetControlScale = \"set_control_scale\";\n\t\tpublic new static readonly StringName GetControlScale = \"get_control_scale\";\n\t\tpublic new static readonly StringName SetControlHole = \"set_control_hole\";\n\t\tpublic new static readonly StringName GetControlHole = \"get_control_hole\";\n\t\tpublic new static readonly StringName SetControlNavigation = \"set_control_navigation\";\n\t\tpublic new static readonly StringName GetControlNavigation = \"get_control_navigation\";\n\t\tpublic new static readonly StringName SetControlAuto = \"set_control_auto\";\n\t\tpublic new static readonly StringName GetControlAuto = \"get_control_auto\";\n\t\tpublic new static readonly StringName GetNormal = \"get_normal\";\n\t\tpublic new static readonly StringName IsInSlope = \"is_in_slope\";\n\t\tpublic new static readonly StringName GetTextureId = \"get_texture_id\";\n\t\tpublic new static readonly StringName GetMeshVertex = \"get_mesh_vertex\";\n\t\tpublic new static readonly StringName GetHeightRange = \"get_height_range\";\n\t\tpublic new static readonly StringName CalcHeightRange = \"calc_height_range\";\n\t\tpublic new static readonly StringName ImportImages = \"import_images\";\n\t\tpublic new static readonly StringName ExportImage = \"export_image\";\n\t\tpublic new static readonly StringName LayeredToImage = \"layered_to_image\";\n\t\tpublic new static readonly StringName Dump = \"dump\";\n\t}\n\n\tpublic new long GetRegionCount() => \n\t\tCall(GDExtensionMethodName.GetRegionCount, []).As<long>();\n\n\tpublic new Godot.Collections.Array GetRegionsActive(bool copy = false, bool deep = false) => \n\t\tCall(GDExtensionMethodName.GetRegionsActive, [copy, deep]).As<Godot.Collections.Array>();\n\n\tpublic new Godot.Collections.Dictionary GetRegionsAll() => \n\t\tCall(GDExtensionMethodName.GetRegionsAll, []).As<Godot.Collections.Dictionary>();\n\n\tpublic new int[] GetRegionMap() => \n\t\tCall(GDExtensionMethodName.GetRegionMap, []).As<int[]>();\n\n\tpublic new static long GetRegionMapIndex(Vector2I regionLocation) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetRegionMapIndex, [regionLocation]).As<long>();\n\n\tpublic new void DoForRegions(Rect2I area, Callable callback) => \n\t\tCall(GDExtensionMethodName.DoForRegions, [area, callback]);\n\n\tpublic new void ChangeRegionSize(long regionSize) => \n\t\tCall(GDExtensionMethodName.ChangeRegionSize, [regionSize]);\n\n\tpublic new Vector2I GetRegionLocation(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetRegionLocation, [globalPosition]).As<Vector2I>();\n\n\tpublic new long GetRegionId(Vector2I regionLocation) => \n\t\tCall(GDExtensionMethodName.GetRegionId, [regionLocation]).As<long>();\n\n\tpublic new long GetRegionIdp(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetRegionIdp, [globalPosition]).As<long>();\n\n\tpublic new bool HasRegion(Vector2I regionLocation) => \n\t\tCall(GDExtensionMethodName.HasRegion, [regionLocation]).As<bool>();\n\n\tpublic new bool HasRegionp(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.HasRegionp, [globalPosition]).As<bool>();\n\n\tpublic new Terrain3DRegion GetRegion(Vector2I regionLocation) => \n\t\tTerrain3DRegion.Bind(Call(GDExtensionMethodName.GetRegion, [regionLocation]).As<Resource>());\n\n\tpublic new Terrain3DRegion GetRegionp(Vector3 globalPosition) => \n\t\tTerrain3DRegion.Bind(Call(GDExtensionMethodName.GetRegionp, [globalPosition]).As<Resource>());\n\n\tpublic new void SetRegionModified(Vector2I regionLocation, bool modified) => \n\t\tCall(GDExtensionMethodName.SetRegionModified, [regionLocation, modified]);\n\n\tpublic new bool IsRegionModified(Vector2I regionLocation) => \n\t\tCall(GDExtensionMethodName.IsRegionModified, [regionLocation]).As<bool>();\n\n\tpublic new void SetRegionDeleted(Vector2I regionLocation, bool deleted) => \n\t\tCall(GDExtensionMethodName.SetRegionDeleted, [regionLocation, deleted]);\n\n\tpublic new bool IsRegionDeleted(Vector2I regionLocation) => \n\t\tCall(GDExtensionMethodName.IsRegionDeleted, [regionLocation]).As<bool>();\n\n\tpublic new Terrain3DRegion AddRegionBlankp(Vector3 globalPosition, bool update = true) => \n\t\tTerrain3DRegion.Bind(Call(GDExtensionMethodName.AddRegionBlankp, [globalPosition, update]).As<Resource>());\n\n\tpublic new Terrain3DRegion AddRegionBlank(Vector2I regionLocation, bool update = true) => \n\t\tTerrain3DRegion.Bind(Call(GDExtensionMethodName.AddRegionBlank, [regionLocation, update]).As<Resource>());\n\n\tpublic new Error AddRegion(Terrain3DRegion region, bool update = true) => \n\t\tCall(GDExtensionMethodName.AddRegion, [region, update]).As<Error>();\n\n\tpublic new void RemoveRegionp(Vector3 globalPosition, bool update = true) => \n\t\tCall(GDExtensionMethodName.RemoveRegionp, [globalPosition, update]);\n\n\tpublic new void RemoveRegionl(Vector2I regionLocation, bool update = true) => \n\t\tCall(GDExtensionMethodName.RemoveRegionl, [regionLocation, update]);\n\n\tpublic new void RemoveRegion(Terrain3DRegion region, bool update = true) => \n\t\tCall(GDExtensionMethodName.RemoveRegion, [region, update]);\n\n\tpublic new void SaveDirectory(string directory) => \n\t\tCall(GDExtensionMethodName.SaveDirectory, [directory]);\n\n\tpublic new void SaveRegion(Vector2I regionLocation, string directory, bool save16Bit = false) => \n\t\tCall(GDExtensionMethodName.SaveRegion, [regionLocation, directory, save16Bit]);\n\n\tpublic new void LoadDirectory(string directory) => \n\t\tCall(GDExtensionMethodName.LoadDirectory, [directory]);\n\n\tpublic new void LoadRegion(Vector2I regionLocation, string directory, bool update = true) => \n\t\tCall(GDExtensionMethodName.LoadRegion, [regionLocation, directory, update]);\n\n\tpublic new Godot.Collections.Array GetMaps(Terrain3DRegion.MapType mapType) => \n\t\tCall(GDExtensionMethodName.GetMaps, [Variant.From(mapType)]).As<Godot.Collections.Array>();\n\n\tpublic new void UpdateMaps(Terrain3DRegion.MapType mapType = Terrain3DRegion.MapType.Max, bool allRegions = true, bool generateMipmaps = false) => \n\t\tCall(GDExtensionMethodName.UpdateMaps, [Variant.From(mapType), allRegions, generateMipmaps]);\n\n\tpublic new Rid GetHeightMapsRid() => \n\t\tCall(GDExtensionMethodName.GetHeightMapsRid, []).As<Rid>();\n\n\tpublic new Rid GetControlMapsRid() => \n\t\tCall(GDExtensionMethodName.GetControlMapsRid, []).As<Rid>();\n\n\tpublic new Rid GetColorMapsRid() => \n\t\tCall(GDExtensionMethodName.GetColorMapsRid, []).As<Rid>();\n\n\tpublic new void SetPixel(Terrain3DRegion.MapType mapType, Vector3 globalPosition, Color pixel) => \n\t\tCall(GDExtensionMethodName.SetPixel, [Variant.From(mapType), globalPosition, pixel]);\n\n\tpublic new Color GetPixel(Terrain3DRegion.MapType mapType, Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetPixel, [Variant.From(mapType), globalPosition]).As<Color>();\n\n\tpublic new void SetHeight(Vector3 globalPosition, double height) => \n\t\tCall(GDExtensionMethodName.SetHeight, [globalPosition, height]);\n\n\tpublic new double GetHeight(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetHeight, [globalPosition]).As<double>();\n\n\tpublic new void SetColor(Vector3 globalPosition, Color color) => \n\t\tCall(GDExtensionMethodName.SetColor, [globalPosition, color]);\n\n\tpublic new Color GetColor(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetColor, [globalPosition]).As<Color>();\n\n\tpublic new void SetControl(Vector3 globalPosition, long control) => \n\t\tCall(GDExtensionMethodName.SetControl, [globalPosition, control]);\n\n\tpublic new long GetControl(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControl, [globalPosition]).As<long>();\n\n\tpublic new void SetRoughness(Vector3 globalPosition, double roughness) => \n\t\tCall(GDExtensionMethodName.SetRoughness, [globalPosition, roughness]);\n\n\tpublic new double GetRoughness(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetRoughness, [globalPosition]).As<double>();\n\n\tpublic new void SetControlBaseId(Vector3 globalPosition, long textureId) => \n\t\tCall(GDExtensionMethodName.SetControlBaseId, [globalPosition, textureId]);\n\n\tpublic new long GetControlBaseId(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlBaseId, [globalPosition]).As<long>();\n\n\tpublic new void SetControlOverlayId(Vector3 globalPosition, long textureId) => \n\t\tCall(GDExtensionMethodName.SetControlOverlayId, [globalPosition, textureId]);\n\n\tpublic new long GetControlOverlayId(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlOverlayId, [globalPosition]).As<long>();\n\n\tpublic new void SetControlBlend(Vector3 globalPosition, double blendValue) => \n\t\tCall(GDExtensionMethodName.SetControlBlend, [globalPosition, blendValue]);\n\n\tpublic new double GetControlBlend(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlBlend, [globalPosition]).As<double>();\n\n\tpublic new void SetControlAngle(Vector3 globalPosition, double degrees) => \n\t\tCall(GDExtensionMethodName.SetControlAngle, [globalPosition, degrees]);\n\n\tpublic new double GetControlAngle(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlAngle, [globalPosition]).As<double>();\n\n\tpublic new void SetControlScale(Vector3 globalPosition, double percentageModifier) => \n\t\tCall(GDExtensionMethodName.SetControlScale, [globalPosition, percentageModifier]);\n\n\tpublic new double GetControlScale(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlScale, [globalPosition]).As<double>();\n\n\tpublic new void SetControlHole(Vector3 globalPosition, bool enable) => \n\t\tCall(GDExtensionMethodName.SetControlHole, [globalPosition, enable]);\n\n\tpublic new bool GetControlHole(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlHole, [globalPosition]).As<bool>();\n\n\tpublic new void SetControlNavigation(Vector3 globalPosition, bool enable) => \n\t\tCall(GDExtensionMethodName.SetControlNavigation, [globalPosition, enable]);\n\n\tpublic new bool GetControlNavigation(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlNavigation, [globalPosition]).As<bool>();\n\n\tpublic new void SetControlAuto(Vector3 globalPosition, bool enable) => \n\t\tCall(GDExtensionMethodName.SetControlAuto, [globalPosition, enable]);\n\n\tpublic new bool GetControlAuto(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetControlAuto, [globalPosition]).As<bool>();\n\n\tpublic new Vector3 GetNormal(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetNormal, [globalPosition]).As<Vector3>();\n\n\tpublic new bool IsInSlope(Vector3 globalPosition, Vector2 slopeRange, Vector3 normal = default) => \n\t\tCall(GDExtensionMethodName.IsInSlope, [globalPosition, slopeRange, normal]).As<bool>();\n\n\tpublic new Vector3 GetTextureId(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetTextureId, [globalPosition]).As<Vector3>();\n\n\tpublic new Vector3 GetMeshVertex(long lod, Terrain3DData.HeightFilter filter, Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetMeshVertex, [lod, Variant.From(filter), globalPosition]).As<Vector3>();\n\n\tpublic new Vector2 GetHeightRange() => \n\t\tCall(GDExtensionMethodName.GetHeightRange, []).As<Vector2>();\n\n\tpublic new void CalcHeightRange(bool recursive = false) => \n\t\tCall(GDExtensionMethodName.CalcHeightRange, [recursive]);\n\n\tpublic new void ImportImages(Godot.Collections.Array images, Vector3 globalPosition = default, double offset = 0, double scale = 1) => \n\t\tCall(GDExtensionMethodName.ImportImages, [images, globalPosition, offset, scale]);\n\n\tpublic new Error ExportImage(string fileName, Terrain3DRegion.MapType mapType) => \n\t\tCall(GDExtensionMethodName.ExportImage, [fileName, Variant.From(mapType)]).As<Error>();\n\n\tpublic new Image LayeredToImage(Terrain3DRegion.MapType mapType) => \n\t\tCall(GDExtensionMethodName.LayeredToImage, [Variant.From(mapType)]).As<Image>();\n\n\tpublic new void Dump(bool verbose = false) => \n\t\tCall(GDExtensionMethodName.Dump, [verbose]);\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DData.cs.uid",
    "content": "uid://ct5j1v1bbb4x3\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DEditor.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DEditor : GodotObject\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DEditor\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DEditor object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DEditor() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DEditor\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DEditor\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DEditor\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DEditor\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DEditor Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DEditor wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DEditor);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DEditor).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DEditor)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DEditor\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DEditor\" type.</returns>\n\tpublic new static Terrain3DEditor Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum Operation\n\t{\n\t\tAdd = 0,\n\t\tSubtract = 1,\n\t\tReplace = 2,\n\t\tAverage = 3,\n\t\tGradient = 4,\n\t\tOpMax = 5,\n\t}\n\n\tpublic enum Tool\n\t{\n\t\tSculpt = 1,\n\t\tHeight = 2,\n\t\tTexture = 3,\n\t\tColor = 4,\n\t\tRoughness = 5,\n\t\tAngle = 10,\n\t\tScale = 11,\n\t\tAutoshader = 6,\n\t\tHoles = 7,\n\t\tNavigation = 8,\n\t\tInstancer = 9,\n\t\tRegion = 0,\n\t\tMax = 12,\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName SetTerrain = \"set_terrain\";\n\t\tpublic new static readonly StringName GetTerrain = \"get_terrain\";\n\t\tpublic new static readonly StringName SetBrushData = \"set_brush_data\";\n\t\tpublic new static readonly StringName SetTool = \"set_tool\";\n\t\tpublic new static readonly StringName GetTool = \"get_tool\";\n\t\tpublic new static readonly StringName SetOperation = \"set_operation\";\n\t\tpublic new static readonly StringName GetOperation = \"get_operation\";\n\t\tpublic new static readonly StringName StartOperation = \"start_operation\";\n\t\tpublic new static readonly StringName IsOperating = \"is_operating\";\n\t\tpublic new static readonly StringName Operate = \"operate\";\n\t\tpublic new static readonly StringName BackupRegion = \"backup_region\";\n\t\tpublic new static readonly StringName StopOperation = \"stop_operation\";\n\t\tpublic new static readonly StringName ApplyUndo = \"apply_undo\";\n\t}\n\n\tpublic new void SetTerrain(Terrain3D terrain) => \n\t\tCall(GDExtensionMethodName.SetTerrain, [terrain]);\n\n\tpublic new Terrain3D GetTerrain() => \n\t\tTerrain3D.Bind(Call(GDExtensionMethodName.GetTerrain, []).As<Node3D>());\n\n\tpublic new void SetBrushData(Godot.Collections.Dictionary data) => \n\t\tCall(GDExtensionMethodName.SetBrushData, [data]);\n\n\tpublic new void SetTool(Terrain3DEditor.Tool tool) => \n\t\tCall(GDExtensionMethodName.SetTool, [Variant.From(tool)]);\n\n\tpublic new Terrain3DEditor.Tool GetTool() => \n\t\tCall(GDExtensionMethodName.GetTool, []).As<Terrain3DEditor.Tool>();\n\n\tpublic new void SetOperation(Terrain3DEditor.Operation operation) => \n\t\tCall(GDExtensionMethodName.SetOperation, [Variant.From(operation)]);\n\n\tpublic new Terrain3DEditor.Operation GetOperation() => \n\t\tCall(GDExtensionMethodName.GetOperation, []).As<Terrain3DEditor.Operation>();\n\n\tpublic new void StartOperation(Vector3 position) => \n\t\tCall(GDExtensionMethodName.StartOperation, [position]);\n\n\tpublic new bool IsOperating() => \n\t\tCall(GDExtensionMethodName.IsOperating, []).As<bool>();\n\n\tpublic new void Operate(Vector3 position, double cameraDirection) => \n\t\tCall(GDExtensionMethodName.Operate, [position, cameraDirection]);\n\n\tpublic new void BackupRegion(Terrain3DRegion region) => \n\t\tCall(GDExtensionMethodName.BackupRegion, [region]);\n\n\tpublic new void StopOperation() => \n\t\tCall(GDExtensionMethodName.StopOperation, []);\n\n\tpublic new void ApplyUndo(Godot.Collections.Dictionary data) => \n\t\tCall(GDExtensionMethodName.ApplyUndo, [data]);\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DEditor.cs.uid",
    "content": "uid://bhdx3ry8edaq5\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DInstancer.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DInstancer : GodotObject\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DInstancer\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DInstancer object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DInstancer() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DInstancer\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DInstancer\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DInstancer\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DInstancer\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DInstancer Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DInstancer wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DInstancer);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DInstancer).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DInstancer)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DInstancer\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DInstancer\" type.</returns>\n\tpublic new static Terrain3DInstancer Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum InstancerMode\n\t{\n\t\tNormal = 1,\n\t\tDisabled = 0,\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName Mode = \"mode\";\n\t}\n\n\tpublic new Terrain3DInstancer.InstancerMode Mode\n\t{\n\t\tget => Get(GDExtensionPropertyName.Mode).As<Terrain3DInstancer.InstancerMode>();\n\t\tset => Set(GDExtensionPropertyName.Mode, Variant.From(value));\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName ClearByMesh = \"clear_by_mesh\";\n\t\tpublic new static readonly StringName ClearByLocation = \"clear_by_location\";\n\t\tpublic new static readonly StringName ClearByRegion = \"clear_by_region\";\n\t\tpublic new static readonly StringName IsEnabled = \"is_enabled\";\n\t\tpublic new static readonly StringName AddInstances = \"add_instances\";\n\t\tpublic new static readonly StringName RemoveInstances = \"remove_instances\";\n\t\tpublic new static readonly StringName AddMultimesh = \"add_multimesh\";\n\t\tpublic new static readonly StringName AddTransforms = \"add_transforms\";\n\t\tpublic new static readonly StringName AppendLocation = \"append_location\";\n\t\tpublic new static readonly StringName AppendRegion = \"append_region\";\n\t\tpublic new static readonly StringName UpdateTransforms = \"update_transforms\";\n\t\tpublic new static readonly StringName GetClosestMeshId = \"get_closest_mesh_id\";\n\t\tpublic new static readonly StringName UpdateMmis = \"update_mmis\";\n\t\tpublic new static readonly StringName SwapIds = \"swap_ids\";\n\t}\n\n\tpublic new void ClearByMesh(long meshId) => \n\t\tCall(GDExtensionMethodName.ClearByMesh, [meshId]);\n\n\tpublic new void ClearByLocation(Vector2I regionLocation, long meshId) => \n\t\tCall(GDExtensionMethodName.ClearByLocation, [regionLocation, meshId]);\n\n\tpublic new void ClearByRegion(Terrain3DRegion region, long meshId) => \n\t\tCall(GDExtensionMethodName.ClearByRegion, [region, meshId]);\n\n\tpublic new bool IsEnabled() => \n\t\tCall(GDExtensionMethodName.IsEnabled, []).As<bool>();\n\n\tpublic new void AddInstances(Vector3 globalPosition, Godot.Collections.Dictionary @params) => \n\t\tCall(GDExtensionMethodName.AddInstances, [globalPosition, @params]);\n\n\tpublic new void RemoveInstances(Vector3 globalPosition, Godot.Collections.Dictionary @params) => \n\t\tCall(GDExtensionMethodName.RemoveInstances, [globalPosition, @params]);\n\n\tpublic new void AddMultimesh(long meshId, MultiMesh multimesh, Transform3D transform = default, bool update = true) => \n\t\tCall(GDExtensionMethodName.AddMultimesh, [meshId, multimesh, transform, update]);\n\n\tpublic new void AddTransforms(long meshId, Godot.Collections.Array transforms, Color[] colors = default, bool update = true) => \n\t\tCall(GDExtensionMethodName.AddTransforms, [meshId, transforms, colors, update]);\n\n\tpublic new void AppendLocation(Vector2I regionLocation, long meshId, Godot.Collections.Array transforms, Color[] colors, bool update = true) => \n\t\tCall(GDExtensionMethodName.AppendLocation, [regionLocation, meshId, transforms, colors, update]);\n\n\tpublic new void AppendRegion(Terrain3DRegion region, long meshId, Godot.Collections.Array transforms, Color[] colors, bool update = true) => \n\t\tCall(GDExtensionMethodName.AppendRegion, [region, meshId, transforms, colors, update]);\n\n\tpublic new void UpdateTransforms(Aabb aabb) => \n\t\tCall(GDExtensionMethodName.UpdateTransforms, [aabb]);\n\n\tpublic new long GetClosestMeshId(Vector3 globalPosition) => \n\t\tCall(GDExtensionMethodName.GetClosestMeshId, [globalPosition]).As<long>();\n\n\tpublic new void UpdateMmis(long meshId = -1, Vector2I regionLocation = default, bool rebuildAll = false) => \n\t\tCall(GDExtensionMethodName.UpdateMmis, [meshId, regionLocation, rebuildAll]);\n\n\tpublic new void SwapIds(long srcId, long destId) => \n\t\tCall(GDExtensionMethodName.SwapIds, [srcId, destId]);\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DInstancer.cs.uid",
    "content": "uid://v155pladupha\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DMaterial.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DMaterial : Resource\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DMaterial\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DMaterial object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DMaterial() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DMaterial\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DMaterial\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DMaterial\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DMaterial\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DMaterial Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DMaterial wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DMaterial);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DMaterial).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DMaterial)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DMaterial\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DMaterial\" type.</returns>\n\tpublic new static Terrain3DMaterial Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum WorldBackgroundEnum\n\t{\n\t\tNone = 0,\n\t\tFlat = 1,\n\t\tNoise = 2,\n\t}\n\n\tpublic enum TextureFilteringEnum\n\t{\n\t\tLinear = 0,\n\t\tNearest = 1,\n\t}\n\n\tpublic enum UpdateFlags\n\t{\n\t\tUniformsOnly = 0,\n\t\tTextureArrays = 1,\n\t\tRegionArrays = 2,\n\t\tArrays = 3,\n\t\tFullRebuild = 7,\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName WorldBackground = \"world_background\";\n\t\tpublic new static readonly StringName TextureFiltering = \"texture_filtering\";\n\t\tpublic new static readonly StringName AutoShaderEnabled = \"auto_shader_enabled\";\n\t\tpublic new static readonly StringName DualScalingEnabled = \"dual_scaling_enabled\";\n\t\tpublic new static readonly StringName MacroVariationEnabled = \"macro_variation_enabled\";\n\t\tpublic new static readonly StringName ProjectionEnabled = \"projection_enabled\";\n\t\tpublic new static readonly StringName OutputAlbedo = \"output_albedo\";\n\t\tpublic new static readonly StringName OutputRoughness = \"output_roughness\";\n\t\tpublic new static readonly StringName OutputNormalMap = \"output_normal_map\";\n\t\tpublic new static readonly StringName OutputAmbientOcclusion = \"output_ambient_occlusion\";\n\t\tpublic new static readonly StringName ShaderOverrideEnabled = \"shader_override_enabled\";\n\t\tpublic new static readonly StringName ShaderOverride = \"shader_override\";\n\t\tpublic new static readonly StringName ShowRegionGrid = \"show_region_grid\";\n\t\tpublic new static readonly StringName ShowInstancerGrid = \"show_instancer_grid\";\n\t\tpublic new static readonly StringName ShowVertexGrid = \"show_vertex_grid\";\n\t\tpublic new static readonly StringName ShowContours = \"show_contours\";\n\t\tpublic new static readonly StringName ShowNavigation = \"show_navigation\";\n\t\tpublic new static readonly StringName DisplacementScale = \"displacement_scale\";\n\t\tpublic new static readonly StringName DisplacementSharpness = \"displacement_sharpness\";\n\t\tpublic new static readonly StringName BufferShaderOverrideEnabled = \"buffer_shader_override_enabled\";\n\t\tpublic new static readonly StringName BufferShaderOverride = \"buffer_shader_override\";\n\t\tpublic new static readonly StringName ShowCheckered = \"show_checkered\";\n\t\tpublic new static readonly StringName ShowGrey = \"show_grey\";\n\t\tpublic new static readonly StringName ShowHeightmap = \"show_heightmap\";\n\t\tpublic new static readonly StringName ShowJaggedness = \"show_jaggedness\";\n\t\tpublic new static readonly StringName ShowAutoshader = \"show_autoshader\";\n\t\tpublic new static readonly StringName ShowControlTexture = \"show_control_texture\";\n\t\tpublic new static readonly StringName ShowControlBlend = \"show_control_blend\";\n\t\tpublic new static readonly StringName ShowControlAngle = \"show_control_angle\";\n\t\tpublic new static readonly StringName ShowControlScale = \"show_control_scale\";\n\t\tpublic new static readonly StringName ShowColormap = \"show_colormap\";\n\t\tpublic new static readonly StringName ShowRoughmap = \"show_roughmap\";\n\t\tpublic new static readonly StringName ShowTextureAlbedo = \"show_texture_albedo\";\n\t\tpublic new static readonly StringName ShowTextureHeight = \"show_texture_height\";\n\t\tpublic new static readonly StringName ShowTextureNormal = \"show_texture_normal\";\n\t\tpublic new static readonly StringName ShowTextureAo = \"show_texture_ao\";\n\t\tpublic new static readonly StringName ShowTextureRough = \"show_texture_rough\";\n\t\tpublic new static readonly StringName ShowDisplacementBuffer = \"show_displacement_buffer\";\n\t}\n\n\tpublic new Terrain3DMaterial.WorldBackgroundEnum WorldBackground\n\t{\n\t\tget => Get(GDExtensionPropertyName.WorldBackground).As<Terrain3DMaterial.WorldBackgroundEnum>();\n\t\tset => Set(GDExtensionPropertyName.WorldBackground, Variant.From(value));\n\t}\n\n\tpublic new long TextureFiltering\n\t{\n\t\tget => Get(GDExtensionPropertyName.TextureFiltering).As<long>();\n\t\tset => Set(GDExtensionPropertyName.TextureFiltering, value);\n\t}\n\n\tpublic new bool AutoShaderEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.AutoShaderEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.AutoShaderEnabled, value);\n\t}\n\n\tpublic new bool DualScalingEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.DualScalingEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.DualScalingEnabled, value);\n\t}\n\n\tpublic new bool MacroVariationEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.MacroVariationEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.MacroVariationEnabled, value);\n\t}\n\n\tpublic new bool ProjectionEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.ProjectionEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ProjectionEnabled, value);\n\t}\n\n\tpublic new bool OutputAlbedo\n\t{\n\t\tget => Get(GDExtensionPropertyName.OutputAlbedo).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.OutputAlbedo, value);\n\t}\n\n\tpublic new bool OutputRoughness\n\t{\n\t\tget => Get(GDExtensionPropertyName.OutputRoughness).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.OutputRoughness, value);\n\t}\n\n\tpublic new bool OutputNormalMap\n\t{\n\t\tget => Get(GDExtensionPropertyName.OutputNormalMap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.OutputNormalMap, value);\n\t}\n\n\tpublic new bool OutputAmbientOcclusion\n\t{\n\t\tget => Get(GDExtensionPropertyName.OutputAmbientOcclusion).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.OutputAmbientOcclusion, value);\n\t}\n\n\tpublic new bool ShaderOverrideEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShaderOverrideEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShaderOverrideEnabled, value);\n\t}\n\n\tpublic new Shader ShaderOverride\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShaderOverride).As<Shader>();\n\t\tset => Set(GDExtensionPropertyName.ShaderOverride, value);\n\t}\n\n\tpublic new bool ShowRegionGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowRegionGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowRegionGrid, value);\n\t}\n\n\tpublic new bool ShowInstancerGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowInstancerGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowInstancerGrid, value);\n\t}\n\n\tpublic new bool ShowVertexGrid\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowVertexGrid).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowVertexGrid, value);\n\t}\n\n\tpublic new bool ShowContours\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowContours).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowContours, value);\n\t}\n\n\tpublic new bool ShowNavigation\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowNavigation).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowNavigation, value);\n\t}\n\n\tpublic new double DisplacementScale\n\t{\n\t\tget => Get(GDExtensionPropertyName.DisplacementScale).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DisplacementScale, value);\n\t}\n\n\tpublic new double DisplacementSharpness\n\t{\n\t\tget => Get(GDExtensionPropertyName.DisplacementSharpness).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DisplacementSharpness, value);\n\t}\n\n\tpublic new bool BufferShaderOverrideEnabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.BufferShaderOverrideEnabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.BufferShaderOverrideEnabled, value);\n\t}\n\n\tpublic new Variant BufferShaderOverride\n\t{\n\t\tget => Get(GDExtensionPropertyName.BufferShaderOverride).As<Variant>();\n\t\tset => Set(GDExtensionPropertyName.BufferShaderOverride, value);\n\t}\n\n\tpublic new bool ShowCheckered\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowCheckered).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowCheckered, value);\n\t}\n\n\tpublic new bool ShowGrey\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowGrey).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowGrey, value);\n\t}\n\n\tpublic new bool ShowHeightmap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowHeightmap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowHeightmap, value);\n\t}\n\n\tpublic new bool ShowJaggedness\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowJaggedness).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowJaggedness, value);\n\t}\n\n\tpublic new bool ShowAutoshader\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowAutoshader).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowAutoshader, value);\n\t}\n\n\tpublic new bool ShowControlTexture\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlTexture).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlTexture, value);\n\t}\n\n\tpublic new bool ShowControlBlend\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlBlend).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlBlend, value);\n\t}\n\n\tpublic new bool ShowControlAngle\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlAngle).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlAngle, value);\n\t}\n\n\tpublic new bool ShowControlScale\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowControlScale).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowControlScale, value);\n\t}\n\n\tpublic new bool ShowColormap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowColormap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowColormap, value);\n\t}\n\n\tpublic new bool ShowRoughmap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowRoughmap).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowRoughmap, value);\n\t}\n\n\tpublic new bool ShowTextureAlbedo\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureAlbedo).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureAlbedo, value);\n\t}\n\n\tpublic new bool ShowTextureHeight\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureHeight).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureHeight, value);\n\t}\n\n\tpublic new bool ShowTextureNormal\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureNormal).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureNormal, value);\n\t}\n\n\tpublic new bool ShowTextureAo\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureAo).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureAo, value);\n\t}\n\n\tpublic new bool ShowTextureRough\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowTextureRough).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowTextureRough, value);\n\t}\n\n\tpublic new bool ShowDisplacementBuffer\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShowDisplacementBuffer).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.ShowDisplacementBuffer, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName Update = \"update\";\n\t\tpublic new static readonly StringName GetMaterialRid = \"get_material_rid\";\n\t\tpublic new static readonly StringName GetShaderRid = \"get_shader_rid\";\n\t\tpublic new static readonly StringName GetBufferMaterialRid = \"get_buffer_material_rid\";\n\t\tpublic new static readonly StringName GetBufferShaderRid = \"get_buffer_shader_rid\";\n\t\tpublic new static readonly StringName SetShaderParam = \"set_shader_param\";\n\t\tpublic new static readonly StringName GetShaderParam = \"get_shader_param\";\n\t\tpublic new static readonly StringName Save = \"save\";\n\t}\n\n\tpublic new void Update(long flags = 0) => \n\t\tCall(GDExtensionMethodName.Update, [flags]);\n\n\tpublic new Rid GetMaterialRid() => \n\t\tCall(GDExtensionMethodName.GetMaterialRid, []).As<Rid>();\n\n\tpublic new Rid GetShaderRid() => \n\t\tCall(GDExtensionMethodName.GetShaderRid, []).As<Rid>();\n\n\tpublic new Rid GetBufferMaterialRid() => \n\t\tCall(GDExtensionMethodName.GetBufferMaterialRid, []).As<Rid>();\n\n\tpublic new Rid GetBufferShaderRid() => \n\t\tCall(GDExtensionMethodName.GetBufferShaderRid, []).As<Rid>();\n\n\tpublic new void SetShaderParam(StringName name, Variant value) => \n\t\tCall(GDExtensionMethodName.SetShaderParam, [name, value]);\n\n\tpublic new void GetShaderParam(StringName name) => \n\t\tCall(GDExtensionMethodName.GetShaderParam, [name]);\n\n\tpublic new Error Save(string path = \"\") => \n\t\tCall(GDExtensionMethodName.Save, [path]).As<Error>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DMaterial.cs.uid",
    "content": "uid://cq4wvyd0gl2do\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DMeshAsset.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DMeshAsset : Resource\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DMeshAsset\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DMeshAsset object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DMeshAsset() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DMeshAsset\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DMeshAsset\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DMeshAsset\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DMeshAsset\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DMeshAsset Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DMeshAsset wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DMeshAsset);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DMeshAsset).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DMeshAsset)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DMeshAsset\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DMeshAsset\" type.</returns>\n\tpublic new static Terrain3DMeshAsset Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum GenType\n\t{\n\t\tNone = 0,\n\t\tTextureCard = 1,\n\t\tMax = 2,\n\t}\n\n\tpublic new static class GDExtensionSignalName\n\t{\n\t\tpublic new static readonly StringName IdChanged = \"id_changed\";\n\t\tpublic new static readonly StringName SettingChanged = \"setting_changed\";\n\t\tpublic new static readonly StringName InstancerSettingChanged = \"instancer_setting_changed\";\n\t\tpublic new static readonly StringName InstanceCountChanged = \"instance_count_changed\";\n\t}\n\n\tpublic new delegate void IdChangedSignalHandler();\n\tprivate IdChangedSignalHandler _idChangedSignal;\n\tprivate Callable _idChangedSignalCallable;\n\tpublic event IdChangedSignalHandler IdChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_idChangedSignal is null)\n\t\t\t{\n\t\t\t\t_idChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_idChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.IdChanged, _idChangedSignalCallable);\n\t\t\t}\n\t\t\t_idChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_idChangedSignal -= value;\n\t\t\tif (_idChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.IdChanged, _idChangedSignalCallable);\n\t\t\t_idChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void SettingChangedSignalHandler();\n\tprivate SettingChangedSignalHandler _settingChangedSignal;\n\tprivate Callable _settingChangedSignalCallable;\n\tpublic event SettingChangedSignalHandler SettingChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_settingChangedSignal is null)\n\t\t\t{\n\t\t\t\t_settingChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_settingChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.SettingChanged, _settingChangedSignalCallable);\n\t\t\t}\n\t\t\t_settingChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_settingChangedSignal -= value;\n\t\t\tif (_settingChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.SettingChanged, _settingChangedSignalCallable);\n\t\t\t_settingChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void InstancerSettingChangedSignalHandler();\n\tprivate InstancerSettingChangedSignalHandler _instancerSettingChangedSignal;\n\tprivate Callable _instancerSettingChangedSignalCallable;\n\tpublic event InstancerSettingChangedSignalHandler InstancerSettingChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_instancerSettingChangedSignal is null)\n\t\t\t{\n\t\t\t\t_instancerSettingChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_instancerSettingChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.InstancerSettingChanged, _instancerSettingChangedSignalCallable);\n\t\t\t}\n\t\t\t_instancerSettingChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_instancerSettingChangedSignal -= value;\n\t\t\tif (_instancerSettingChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.InstancerSettingChanged, _instancerSettingChangedSignalCallable);\n\t\t\t_instancerSettingChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void InstanceCountChangedSignalHandler();\n\tprivate InstanceCountChangedSignalHandler _instanceCountChangedSignal;\n\tprivate Callable _instanceCountChangedSignalCallable;\n\tpublic event InstanceCountChangedSignalHandler InstanceCountChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_instanceCountChangedSignal is null)\n\t\t\t{\n\t\t\t\t_instanceCountChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_instanceCountChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.InstanceCountChanged, _instanceCountChangedSignalCallable);\n\t\t\t}\n\t\t\t_instanceCountChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_instanceCountChangedSignal -= value;\n\t\t\tif (_instanceCountChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.InstanceCountChanged, _instanceCountChangedSignalCallable);\n\t\t\t_instanceCountChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName Name = \"name\";\n\t\tpublic new static readonly StringName Id = \"id\";\n\t\tpublic new static readonly StringName Enabled = \"enabled\";\n\t\tpublic new static readonly StringName SceneFile = \"scene_file\";\n\t\tpublic new static readonly StringName GeneratedType = \"generated_type\";\n\t\tpublic new static readonly StringName HeightOffset = \"height_offset\";\n\t\tpublic new static readonly StringName Density = \"density\";\n\t\tpublic new static readonly StringName CastShadows = \"cast_shadows\";\n\t\tpublic new static readonly StringName VisibilityLayers = \"visibility_layers\";\n\t\tpublic new static readonly StringName MaterialOverride = \"material_override\";\n\t\tpublic new static readonly StringName MaterialOverlay = \"material_overlay\";\n\t\tpublic new static readonly StringName GeneratedFaces = \"generated_faces\";\n\t\tpublic new static readonly StringName GeneratedSize = \"generated_size\";\n\t\tpublic new static readonly StringName LodCount = \"lod_count\";\n\t\tpublic new static readonly StringName LastLod = \"last_lod\";\n\t\tpublic new static readonly StringName LastShadowLod = \"last_shadow_lod\";\n\t\tpublic new static readonly StringName ShadowImpostor = \"shadow_impostor\";\n\t\tpublic new static readonly StringName Lod0Range = \"lod0_range\";\n\t\tpublic new static readonly StringName Lod1Range = \"lod1_range\";\n\t\tpublic new static readonly StringName Lod2Range = \"lod2_range\";\n\t\tpublic new static readonly StringName Lod3Range = \"lod3_range\";\n\t\tpublic new static readonly StringName Lod4Range = \"lod4_range\";\n\t\tpublic new static readonly StringName Lod5Range = \"lod5_range\";\n\t\tpublic new static readonly StringName Lod6Range = \"lod6_range\";\n\t\tpublic new static readonly StringName Lod7Range = \"lod7_range\";\n\t\tpublic new static readonly StringName Lod8Range = \"lod8_range\";\n\t\tpublic new static readonly StringName Lod9Range = \"lod9_range\";\n\t\tpublic new static readonly StringName FadeMargin = \"fade_margin\";\n\t}\n\n\tpublic new string Name\n\t{\n\t\tget => Get(GDExtensionPropertyName.Name).As<string>();\n\t\tset => Set(GDExtensionPropertyName.Name, value);\n\t}\n\n\tpublic new long Id\n\t{\n\t\tget => Get(GDExtensionPropertyName.Id).As<long>();\n\t\tset => Set(GDExtensionPropertyName.Id, value);\n\t}\n\n\tpublic new bool Enabled\n\t{\n\t\tget => Get(GDExtensionPropertyName.Enabled).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.Enabled, value);\n\t}\n\n\tpublic new PackedScene SceneFile\n\t{\n\t\tget => Get(GDExtensionPropertyName.SceneFile).As<PackedScene>();\n\t\tset => Set(GDExtensionPropertyName.SceneFile, value);\n\t}\n\n\tpublic new Variant GeneratedType\n\t{\n\t\tget => Get(GDExtensionPropertyName.GeneratedType).As<Variant>();\n\t\tset => Set(GDExtensionPropertyName.GeneratedType, value);\n\t}\n\n\tpublic new double HeightOffset\n\t{\n\t\tget => Get(GDExtensionPropertyName.HeightOffset).As<double>();\n\t\tset => Set(GDExtensionPropertyName.HeightOffset, value);\n\t}\n\n\tpublic new double Density\n\t{\n\t\tget => Get(GDExtensionPropertyName.Density).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Density, value);\n\t}\n\n\tpublic new long CastShadows\n\t{\n\t\tget => Get(GDExtensionPropertyName.CastShadows).As<long>();\n\t\tset => Set(GDExtensionPropertyName.CastShadows, value);\n\t}\n\n\tpublic new long VisibilityLayers\n\t{\n\t\tget => Get(GDExtensionPropertyName.VisibilityLayers).As<long>();\n\t\tset => Set(GDExtensionPropertyName.VisibilityLayers, value);\n\t}\n\n\tpublic new Material MaterialOverride\n\t{\n\t\tget => Get(GDExtensionPropertyName.MaterialOverride).As<Material>();\n\t\tset => Set(GDExtensionPropertyName.MaterialOverride, value);\n\t}\n\n\tpublic new Material MaterialOverlay\n\t{\n\t\tget => Get(GDExtensionPropertyName.MaterialOverlay).As<Material>();\n\t\tset => Set(GDExtensionPropertyName.MaterialOverlay, value);\n\t}\n\n\tpublic new long GeneratedFaces\n\t{\n\t\tget => Get(GDExtensionPropertyName.GeneratedFaces).As<long>();\n\t\tset => Set(GDExtensionPropertyName.GeneratedFaces, value);\n\t}\n\n\tpublic new Vector2 GeneratedSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.GeneratedSize).As<Vector2>();\n\t\tset => Set(GDExtensionPropertyName.GeneratedSize, value);\n\t}\n\n\tpublic new long LodCount\n\t{\n\t\tget => Get(GDExtensionPropertyName.LodCount).As<long>();\n\t}\n\n\tpublic new long LastLod\n\t{\n\t\tget => Get(GDExtensionPropertyName.LastLod).As<long>();\n\t\tset => Set(GDExtensionPropertyName.LastLod, value);\n\t}\n\n\tpublic new long LastShadowLod\n\t{\n\t\tget => Get(GDExtensionPropertyName.LastShadowLod).As<long>();\n\t\tset => Set(GDExtensionPropertyName.LastShadowLod, value);\n\t}\n\n\tpublic new long ShadowImpostor\n\t{\n\t\tget => Get(GDExtensionPropertyName.ShadowImpostor).As<long>();\n\t\tset => Set(GDExtensionPropertyName.ShadowImpostor, value);\n\t}\n\n\tpublic new double Lod0Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod0Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod0Range, value);\n\t}\n\n\tpublic new double Lod1Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod1Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod1Range, value);\n\t}\n\n\tpublic new double Lod2Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod2Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod2Range, value);\n\t}\n\n\tpublic new double Lod3Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod3Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod3Range, value);\n\t}\n\n\tpublic new double Lod4Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod4Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod4Range, value);\n\t}\n\n\tpublic new double Lod5Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod5Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod5Range, value);\n\t}\n\n\tpublic new double Lod6Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod6Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod6Range, value);\n\t}\n\n\tpublic new double Lod7Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod7Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod7Range, value);\n\t}\n\n\tpublic new double Lod8Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod8Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod8Range, value);\n\t}\n\n\tpublic new double Lod9Range\n\t{\n\t\tget => Get(GDExtensionPropertyName.Lod9Range).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Lod9Range, value);\n\t}\n\n\tpublic new double FadeMargin\n\t{\n\t\tget => Get(GDExtensionPropertyName.FadeMargin).As<double>();\n\t\tset => Set(GDExtensionPropertyName.FadeMargin, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName Clear = \"clear\";\n\t\tpublic new static readonly StringName SetHighlighted = \"set_highlighted\";\n\t\tpublic new static readonly StringName IsHighlighted = \"is_highlighted\";\n\t\tpublic new static readonly StringName GetHighlightColor = \"get_highlight_color\";\n\t\tpublic new static readonly StringName GetThumbnail = \"get_thumbnail\";\n\t\tpublic new static readonly StringName GetMesh = \"get_mesh\";\n\t\tpublic new static readonly StringName SetLodRange = \"set_lod_range\";\n\t\tpublic new static readonly StringName GetLodRange = \"get_lod_range\";\n\t\tpublic new static readonly StringName GetInstanceCount = \"get_instance_count\";\n\t}\n\n\tpublic new void Clear() => \n\t\tCall(GDExtensionMethodName.Clear, []);\n\n\tpublic new void SetHighlighted(bool enabled) => \n\t\tCall(GDExtensionMethodName.SetHighlighted, [enabled]);\n\n\tpublic new bool IsHighlighted() => \n\t\tCall(GDExtensionMethodName.IsHighlighted, []).As<bool>();\n\n\tpublic new Color GetHighlightColor() => \n\t\tCall(GDExtensionMethodName.GetHighlightColor, []).As<Color>();\n\n\tpublic new Texture2D GetThumbnail() => \n\t\tCall(GDExtensionMethodName.GetThumbnail, []).As<Texture2D>();\n\n\tpublic new Mesh GetMesh(long lod = 0) => \n\t\tCall(GDExtensionMethodName.GetMesh, [lod]).As<Mesh>();\n\n\tpublic new void SetLodRange(long lod, double distance) => \n\t\tCall(GDExtensionMethodName.SetLodRange, [lod, distance]);\n\n\tpublic new double GetLodRange(long lod) => \n\t\tCall(GDExtensionMethodName.GetLodRange, [lod]).As<double>();\n\n\tpublic new long GetInstanceCount() => \n\t\tCall(GDExtensionMethodName.GetInstanceCount, []).As<long>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DMeshAsset.cs.uid",
    "content": "uid://c1mg74hkhl5m2\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DRegion.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DRegion : Resource\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DRegion\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DRegion object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DRegion() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DRegion\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DRegion\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DRegion\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DRegion\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DRegion Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DRegion wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DRegion);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DRegion).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DRegion)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DRegion\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DRegion\" type.</returns>\n\tpublic new static Terrain3DRegion Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic enum MapType\n\t{\n\t\tHeight = 0,\n\t\tControl = 1,\n\t\tColor = 2,\n\t\tMax = 3,\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName Version = \"version\";\n\t\tpublic new static readonly StringName RegionSize = \"region_size\";\n\t\tpublic new static readonly StringName VertexSpacing = \"vertex_spacing\";\n\t\tpublic new static readonly StringName HeightRange = \"height_range\";\n\t\tpublic new static readonly StringName HeightMap = \"height_map\";\n\t\tpublic new static readonly StringName ControlMap = \"control_map\";\n\t\tpublic new static readonly StringName ColorMap = \"color_map\";\n\t\tpublic new static readonly StringName Instances = \"instances\";\n\t\tpublic new static readonly StringName Edited = \"edited\";\n\t\tpublic new static readonly StringName Deleted = \"deleted\";\n\t\tpublic new static readonly StringName Modified = \"modified\";\n\t\tpublic new static readonly StringName Location = \"location\";\n\t}\n\n\tpublic new double Version\n\t{\n\t\tget => Get(GDExtensionPropertyName.Version).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Version, value);\n\t}\n\n\tpublic new long RegionSize\n\t{\n\t\tget => Get(GDExtensionPropertyName.RegionSize).As<long>();\n\t\tset => Set(GDExtensionPropertyName.RegionSize, value);\n\t}\n\n\tpublic new double VertexSpacing\n\t{\n\t\tget => Get(GDExtensionPropertyName.VertexSpacing).As<double>();\n\t\tset => Set(GDExtensionPropertyName.VertexSpacing, value);\n\t}\n\n\tpublic new Vector2 HeightRange\n\t{\n\t\tget => Get(GDExtensionPropertyName.HeightRange).As<Vector2>();\n\t\tset => Set(GDExtensionPropertyName.HeightRange, value);\n\t}\n\n\tpublic new Image HeightMap\n\t{\n\t\tget => Get(GDExtensionPropertyName.HeightMap).As<Image>();\n\t\tset => Set(GDExtensionPropertyName.HeightMap, value);\n\t}\n\n\tpublic new Image ControlMap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ControlMap).As<Image>();\n\t\tset => Set(GDExtensionPropertyName.ControlMap, value);\n\t}\n\n\tpublic new Image ColorMap\n\t{\n\t\tget => Get(GDExtensionPropertyName.ColorMap).As<Image>();\n\t\tset => Set(GDExtensionPropertyName.ColorMap, value);\n\t}\n\n\tpublic new Godot.Collections.Dictionary Instances\n\t{\n\t\tget => Get(GDExtensionPropertyName.Instances).As<Godot.Collections.Dictionary>();\n\t\tset => Set(GDExtensionPropertyName.Instances, value);\n\t}\n\n\tpublic new bool Edited\n\t{\n\t\tget => Get(GDExtensionPropertyName.Edited).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.Edited, value);\n\t}\n\n\tpublic new bool Deleted\n\t{\n\t\tget => Get(GDExtensionPropertyName.Deleted).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.Deleted, value);\n\t}\n\n\tpublic new bool Modified\n\t{\n\t\tget => Get(GDExtensionPropertyName.Modified).As<bool>();\n\t\tset => Set(GDExtensionPropertyName.Modified, value);\n\t}\n\n\tpublic new Vector2I Location\n\t{\n\t\tget => Get(GDExtensionPropertyName.Location).As<Vector2I>();\n\t\tset => Set(GDExtensionPropertyName.Location, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName Clear = \"clear\";\n\t\tpublic new static readonly StringName SetMap = \"set_map\";\n\t\tpublic new static readonly StringName GetMap = \"get_map\";\n\t\tpublic new static readonly StringName SetMaps = \"set_maps\";\n\t\tpublic new static readonly StringName GetMaps = \"get_maps\";\n\t\tpublic new static readonly StringName SanitizeMaps = \"sanitize_maps\";\n\t\tpublic new static readonly StringName SanitizeMap = \"sanitize_map\";\n\t\tpublic new static readonly StringName ValidateMapSize = \"validate_map_size\";\n\t\tpublic new static readonly StringName UpdateHeight = \"update_height\";\n\t\tpublic new static readonly StringName UpdateHeights = \"update_heights\";\n\t\tpublic new static readonly StringName CalcHeightRange = \"calc_height_range\";\n\t\tpublic new static readonly StringName Save = \"save\";\n\t\tpublic new static readonly StringName SetData = \"set_data\";\n\t\tpublic new static readonly StringName GetData = \"get_data\";\n\t\tpublic new static readonly StringName Duplicate = \"duplicate\";\n\t\tpublic new static readonly StringName Dump = \"dump\";\n\t}\n\n\tpublic new void Clear() => \n\t\tCall(GDExtensionMethodName.Clear, []);\n\n\tpublic new void SetMap(Terrain3DRegion.MapType mapType, Image map) => \n\t\tCall(GDExtensionMethodName.SetMap, [Variant.From(mapType), map]);\n\n\tpublic new Image GetMap(Terrain3DRegion.MapType mapType) => \n\t\tCall(GDExtensionMethodName.GetMap, [Variant.From(mapType)]).As<Image>();\n\n\tpublic new void SetMaps(Godot.Collections.Array maps) => \n\t\tCall(GDExtensionMethodName.SetMaps, [maps]);\n\n\tpublic new Godot.Collections.Array GetMaps() => \n\t\tCall(GDExtensionMethodName.GetMaps, []).As<Godot.Collections.Array>();\n\n\tpublic new void SanitizeMaps() => \n\t\tCall(GDExtensionMethodName.SanitizeMaps, []);\n\n\tpublic new Image SanitizeMap(Terrain3DRegion.MapType mapType, Image map) => \n\t\tCall(GDExtensionMethodName.SanitizeMap, [Variant.From(mapType), map]).As<Image>();\n\n\tpublic new bool ValidateMapSize(Image map) => \n\t\tCall(GDExtensionMethodName.ValidateMapSize, [map]).As<bool>();\n\n\tpublic new void UpdateHeight(double height) => \n\t\tCall(GDExtensionMethodName.UpdateHeight, [height]);\n\n\tpublic new void UpdateHeights(Vector2 lowHigh) => \n\t\tCall(GDExtensionMethodName.UpdateHeights, [lowHigh]);\n\n\tpublic new void CalcHeightRange() => \n\t\tCall(GDExtensionMethodName.CalcHeightRange, []);\n\n\tpublic new Error Save(string path = \"\", bool save16Bit = false) => \n\t\tCall(GDExtensionMethodName.Save, [path, save16Bit]).As<Error>();\n\n\tpublic new void SetData(Godot.Collections.Dictionary data) => \n\t\tCall(GDExtensionMethodName.SetData, [data]);\n\n\tpublic new Godot.Collections.Dictionary GetData() => \n\t\tCall(GDExtensionMethodName.GetData, []).As<Godot.Collections.Dictionary>();\n\n\tpublic new Terrain3DRegion Duplicate(bool deep = false) => \n\t\tTerrain3DRegion.Bind(Call(GDExtensionMethodName.Duplicate, [deep]).As<Resource>());\n\n\tpublic new void Dump(bool verbose = false) => \n\t\tCall(GDExtensionMethodName.Dump, [verbose]);\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DRegion.cs.uid",
    "content": "uid://bcby10sji7q8i\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DTextureAsset.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DTextureAsset : Resource\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DTextureAsset\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DTextureAsset object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DTextureAsset() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DTextureAsset\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DTextureAsset\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DTextureAsset\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DTextureAsset\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DTextureAsset Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DTextureAsset wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DTextureAsset);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DTextureAsset).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DTextureAsset)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DTextureAsset\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DTextureAsset\" type.</returns>\n\tpublic new static Terrain3DTextureAsset Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic new static class GDExtensionSignalName\n\t{\n\t\tpublic new static readonly StringName IdChanged = \"id_changed\";\n\t\tpublic new static readonly StringName FileChanged = \"file_changed\";\n\t\tpublic new static readonly StringName SettingChanged = \"setting_changed\";\n\t}\n\n\tpublic new delegate void IdChangedSignalHandler();\n\tprivate IdChangedSignalHandler _idChangedSignal;\n\tprivate Callable _idChangedSignalCallable;\n\tpublic event IdChangedSignalHandler IdChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_idChangedSignal is null)\n\t\t\t{\n\t\t\t\t_idChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_idChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.IdChanged, _idChangedSignalCallable);\n\t\t\t}\n\t\t\t_idChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_idChangedSignal -= value;\n\t\t\tif (_idChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.IdChanged, _idChangedSignalCallable);\n\t\t\t_idChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void FileChangedSignalHandler();\n\tprivate FileChangedSignalHandler _fileChangedSignal;\n\tprivate Callable _fileChangedSignalCallable;\n\tpublic event FileChangedSignalHandler FileChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_fileChangedSignal is null)\n\t\t\t{\n\t\t\t\t_fileChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_fileChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.FileChanged, _fileChangedSignalCallable);\n\t\t\t}\n\t\t\t_fileChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_fileChangedSignal -= value;\n\t\t\tif (_fileChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.FileChanged, _fileChangedSignalCallable);\n\t\t\t_fileChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new delegate void SettingChangedSignalHandler();\n\tprivate SettingChangedSignalHandler _settingChangedSignal;\n\tprivate Callable _settingChangedSignalCallable;\n\tpublic event SettingChangedSignalHandler SettingChangedSignal\n\t{\n\t\tadd\n\t\t{\n\t\t\tif (_settingChangedSignal is null)\n\t\t\t{\n\t\t\t\t_settingChangedSignalCallable = Callable.From(() => \n\t\t\t\t\t_settingChangedSignal?.Invoke());\n\t\t\t\tConnect(GDExtensionSignalName.SettingChanged, _settingChangedSignalCallable);\n\t\t\t}\n\t\t\t_settingChangedSignal += value;\n\t\t}\n\t\tremove\n\t\t{\n\t\t\t_settingChangedSignal -= value;\n\t\t\tif (_settingChangedSignal is not null) return;\n\t\t\tDisconnect(GDExtensionSignalName.SettingChanged, _settingChangedSignalCallable);\n\t\t\t_settingChangedSignalCallable = default;\n\t\t}\n\t}\n\n\tpublic new static class GDExtensionPropertyName\n\t{\n\t\tpublic new static readonly StringName Name = \"name\";\n\t\tpublic new static readonly StringName Id = \"id\";\n\t\tpublic new static readonly StringName AlbedoColor = \"albedo_color\";\n\t\tpublic new static readonly StringName AlbedoTexture = \"albedo_texture\";\n\t\tpublic new static readonly StringName NormalTexture = \"normal_texture\";\n\t\tpublic new static readonly StringName NormalDepth = \"normal_depth\";\n\t\tpublic new static readonly StringName AoStrength = \"ao_strength\";\n\t\tpublic new static readonly StringName AoLightAffect = \"ao_light_affect\";\n\t\tpublic new static readonly StringName Roughness = \"roughness\";\n\t\tpublic new static readonly StringName DisplacementScale = \"displacement_scale\";\n\t\tpublic new static readonly StringName DisplacementOffset = \"displacement_offset\";\n\t\tpublic new static readonly StringName UvScale = \"uv_scale\";\n\t\tpublic new static readonly StringName DetilingRotation = \"detiling_rotation\";\n\t\tpublic new static readonly StringName DetilingShift = \"detiling_shift\";\n\t}\n\n\tpublic new string Name\n\t{\n\t\tget => Get(GDExtensionPropertyName.Name).As<string>();\n\t\tset => Set(GDExtensionPropertyName.Name, value);\n\t}\n\n\tpublic new long Id\n\t{\n\t\tget => Get(GDExtensionPropertyName.Id).As<long>();\n\t\tset => Set(GDExtensionPropertyName.Id, value);\n\t}\n\n\tpublic new Color AlbedoColor\n\t{\n\t\tget => Get(GDExtensionPropertyName.AlbedoColor).As<Color>();\n\t\tset => Set(GDExtensionPropertyName.AlbedoColor, value);\n\t}\n\n\tpublic new Texture2D AlbedoTexture\n\t{\n\t\tget => Get(GDExtensionPropertyName.AlbedoTexture).As<Texture2D>();\n\t\tset => Set(GDExtensionPropertyName.AlbedoTexture, value);\n\t}\n\n\tpublic new Texture2D NormalTexture\n\t{\n\t\tget => Get(GDExtensionPropertyName.NormalTexture).As<Texture2D>();\n\t\tset => Set(GDExtensionPropertyName.NormalTexture, value);\n\t}\n\n\tpublic new double NormalDepth\n\t{\n\t\tget => Get(GDExtensionPropertyName.NormalDepth).As<double>();\n\t\tset => Set(GDExtensionPropertyName.NormalDepth, value);\n\t}\n\n\tpublic new double AoStrength\n\t{\n\t\tget => Get(GDExtensionPropertyName.AoStrength).As<double>();\n\t\tset => Set(GDExtensionPropertyName.AoStrength, value);\n\t}\n\n\tpublic new double AoLightAffect\n\t{\n\t\tget => Get(GDExtensionPropertyName.AoLightAffect).As<double>();\n\t\tset => Set(GDExtensionPropertyName.AoLightAffect, value);\n\t}\n\n\tpublic new double Roughness\n\t{\n\t\tget => Get(GDExtensionPropertyName.Roughness).As<double>();\n\t\tset => Set(GDExtensionPropertyName.Roughness, value);\n\t}\n\n\tpublic new double DisplacementScale\n\t{\n\t\tget => Get(GDExtensionPropertyName.DisplacementScale).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DisplacementScale, value);\n\t}\n\n\tpublic new double DisplacementOffset\n\t{\n\t\tget => Get(GDExtensionPropertyName.DisplacementOffset).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DisplacementOffset, value);\n\t}\n\n\tpublic new double UvScale\n\t{\n\t\tget => Get(GDExtensionPropertyName.UvScale).As<double>();\n\t\tset => Set(GDExtensionPropertyName.UvScale, value);\n\t}\n\n\tpublic new double DetilingRotation\n\t{\n\t\tget => Get(GDExtensionPropertyName.DetilingRotation).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DetilingRotation, value);\n\t}\n\n\tpublic new double DetilingShift\n\t{\n\t\tget => Get(GDExtensionPropertyName.DetilingShift).As<double>();\n\t\tset => Set(GDExtensionPropertyName.DetilingShift, value);\n\t}\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName Clear = \"clear\";\n\t\tpublic new static readonly StringName SetHighlighted = \"set_highlighted\";\n\t\tpublic new static readonly StringName IsHighlighted = \"is_highlighted\";\n\t\tpublic new static readonly StringName GetHighlightColor = \"get_highlight_color\";\n\t\tpublic new static readonly StringName GetThumbnail = \"get_thumbnail\";\n\t}\n\n\tpublic new void Clear() => \n\t\tCall(GDExtensionMethodName.Clear, []);\n\n\tpublic new void SetHighlighted(bool enabled) => \n\t\tCall(GDExtensionMethodName.SetHighlighted, [enabled]);\n\n\tpublic new bool IsHighlighted() => \n\t\tCall(GDExtensionMethodName.IsHighlighted, []).As<bool>();\n\n\tpublic new Color GetHighlightColor() => \n\t\tCall(GDExtensionMethodName.GetHighlightColor, []).As<Color>();\n\n\tpublic new Texture2D GetThumbnail() => \n\t\tCall(GDExtensionMethodName.GetThumbnail, []).As<Texture2D>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DTextureAsset.cs.uid",
    "content": "uid://bbee5m52swmhh\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DUtil.cs",
    "content": "#pragma warning disable CS0109\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing Godot;\nusing Godot.Collections;\n\nnamespace TokisanGames;\n\n[Tool]\npublic partial class Terrain3DUtil : GodotObject\n{\n\n\tprivate new static readonly StringName NativeName = new StringName(\"Terrain3DUtil\");\n\n\t[Obsolete(\"Wrapper types cannot be constructed with constructors (it only instantiate the underlying Terrain3DUtil object), please use the Instantiate() method instead.\")]\n\tprotected Terrain3DUtil() { }\n\n\tprivate static CSharpScript _wrapperScriptAsset;\n\n\t/// <summary>\n\t/// Try to cast the script on the supplied <paramref name=\"godotObject\"/> to the <see cref=\"Terrain3DUtil\"/> wrapper type,\n\t/// if no script has attached to the type, or the script attached to the type does not inherit the <see cref=\"Terrain3DUtil\"/> wrapper type,\n\t/// a new instance of the <see cref=\"Terrain3DUtil\"/> wrapper script will get attaches to the <paramref name=\"godotObject\"/>.\n\t/// </summary>\n\t/// <remarks>The developer should only supply the <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</remarks>\n\t/// <param name=\"godotObject\">The <paramref name=\"godotObject\"/> that represents the correct underlying GDExtension type.</param>\n\t/// <returns>The existing or a new instance of the <see cref=\"Terrain3DUtil\"/> wrapper script attached to the supplied <paramref name=\"godotObject\"/>.</returns>\n\tpublic new static Terrain3DUtil Bind(GodotObject godotObject)\n\t{\n\t\tif (!IsInstanceValid(godotObject))\n\t\t\treturn null;\n\n\t\tif (godotObject is Terrain3DUtil wrapperScriptInstance)\n\t\t\treturn wrapperScriptInstance;\n\n#if DEBUG\n\t\tvar expectedType = typeof(Terrain3DUtil);\n\t\tvar currentObjectClassName = godotObject.GetClass();\n\t\tif (!ClassDB.IsParentClass(expectedType.Name, currentObjectClassName))\n\t\t\tthrow new InvalidOperationException($\"The supplied GodotObject ({currentObjectClassName}) is not the {expectedType.Name} type.\");\n#endif\n\n\t\tif (_wrapperScriptAsset is null)\n\t\t{\n\t\t\tvar scriptPathAttribute = typeof(Terrain3DUtil).GetCustomAttributes<ScriptPathAttribute>().FirstOrDefault();\n\t\t\tif (scriptPathAttribute is null) throw new UnreachableException();\n\t\t\t_wrapperScriptAsset = ResourceLoader.Load<CSharpScript>(scriptPathAttribute.Path);\n\t\t}\n\n\t\tvar instanceId = godotObject.GetInstanceId();\n\t\tgodotObject.SetScript(_wrapperScriptAsset);\n\t\treturn (Terrain3DUtil)InstanceFromId(instanceId);\n\t}\n\n\t/// <summary>\n\t/// Creates an instance of the GDExtension <see cref=\"Terrain3DUtil\"/> type, and attaches a wrapper script instance to it.\n\t/// </summary>\n\t/// <returns>The wrapper instance linked to the underlying GDExtension \"Terrain3DUtil\" type.</returns>\n\tpublic new static Terrain3DUtil Instantiate() => Bind(ClassDB.Instantiate(NativeName).As<GodotObject>());\n\n\tpublic new static class GDExtensionMethodName\n\t{\n\t\tpublic new static readonly StringName AsFloat = \"as_float\";\n\t\tpublic new static readonly StringName AsUint = \"as_uint\";\n\t\tpublic new static readonly StringName GetBase = \"get_base\";\n\t\tpublic new static readonly StringName EncBase = \"enc_base\";\n\t\tpublic new static readonly StringName GetOverlay = \"get_overlay\";\n\t\tpublic new static readonly StringName EncOverlay = \"enc_overlay\";\n\t\tpublic new static readonly StringName GetBlend = \"get_blend\";\n\t\tpublic new static readonly StringName EncBlend = \"enc_blend\";\n\t\tpublic new static readonly StringName GetUvRotation = \"get_uv_rotation\";\n\t\tpublic new static readonly StringName EncUvRotation = \"enc_uv_rotation\";\n\t\tpublic new static readonly StringName GetUvScale = \"get_uv_scale\";\n\t\tpublic new static readonly StringName EncUvScale = \"enc_uv_scale\";\n\t\tpublic new static readonly StringName IsHole = \"is_hole\";\n\t\tpublic new static readonly StringName EncHole = \"enc_hole\";\n\t\tpublic new static readonly StringName IsNav = \"is_nav\";\n\t\tpublic new static readonly StringName EncNav = \"enc_nav\";\n\t\tpublic new static readonly StringName IsAuto = \"is_auto\";\n\t\tpublic new static readonly StringName EncAuto = \"enc_auto\";\n\t\tpublic new static readonly StringName FilenameToLocation = \"filename_to_location\";\n\t\tpublic new static readonly StringName LocationToFilename = \"location_to_filename\";\n\t\tpublic new static readonly StringName BlackToAlpha = \"black_to_alpha\";\n\t\tpublic new static readonly StringName GetMinMax = \"get_min_max\";\n\t\tpublic new static readonly StringName GetThumbnail = \"get_thumbnail\";\n\t\tpublic new static readonly StringName GetFilledImage = \"get_filled_image\";\n\t\tpublic new static readonly StringName LoadImage = \"load_image\";\n\t\tpublic new static readonly StringName PackImage = \"pack_image\";\n\t\tpublic new static readonly StringName LuminanceToHeight = \"luminance_to_height\";\n\t}\n\n\tpublic new static double AsFloat(long value) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.AsFloat, [value]).As<double>();\n\n\tpublic new static long AsUint(double value) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.AsUint, [value]).As<long>();\n\n\tpublic new static long GetBase(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetBase, [pixel]).As<long>();\n\n\tpublic new static long EncBase(long @base) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncBase, [@base]).As<long>();\n\n\tpublic new static long GetOverlay(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetOverlay, [pixel]).As<long>();\n\n\tpublic new static long EncOverlay(long overlay) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncOverlay, [overlay]).As<long>();\n\n\tpublic new static long GetBlend(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetBlend, [pixel]).As<long>();\n\n\tpublic new static long EncBlend(long blend) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncBlend, [blend]).As<long>();\n\n\tpublic new static long GetUvRotation(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetUvRotation, [pixel]).As<long>();\n\n\tpublic new static long EncUvRotation(long rotation) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncUvRotation, [rotation]).As<long>();\n\n\tpublic new static long GetUvScale(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetUvScale, [pixel]).As<long>();\n\n\tpublic new static long EncUvScale(long scale) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncUvScale, [scale]).As<long>();\n\n\tpublic new static bool IsHole(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.IsHole, [pixel]).As<bool>();\n\n\tpublic new static long EncHole(bool pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncHole, [pixel]).As<long>();\n\n\tpublic new static bool IsNav(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.IsNav, [pixel]).As<bool>();\n\n\tpublic new static long EncNav(bool pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncNav, [pixel]).As<long>();\n\n\tpublic new static bool IsAuto(long pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.IsAuto, [pixel]).As<bool>();\n\n\tpublic new static long EncAuto(bool pixel) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.EncAuto, [pixel]).As<long>();\n\n\tpublic new static Vector2I FilenameToLocation(string filename) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.FilenameToLocation, [filename]).As<Vector2I>();\n\n\tpublic new static string LocationToFilename(Vector2I regionLocation) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.LocationToFilename, [regionLocation]).As<string>();\n\n\tpublic new static Image BlackToAlpha(Image image) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.BlackToAlpha, [image]).As<Image>();\n\n\tpublic new static Vector2 GetMinMax(Image image) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetMinMax, [image]).As<Vector2>();\n\n\tpublic new static Image GetThumbnail(Image image, Vector2I size = default) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetThumbnail, [image, size]).As<Image>();\n\n\tpublic new static Image GetFilledImage(Vector2I size, Color color, bool createMipmaps, Image.Format format) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.GetFilledImage, [size, color, createMipmaps, Variant.From(format)]).As<Image>();\n\n\tpublic new static Image LoadImage(string fileName, long cacheMode = 0, Vector2 r16HeightRange = default, Vector2I r16Size = default) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.LoadImage, [fileName, cacheMode, r16HeightRange, r16Size]).As<Image>();\n\n\tpublic new static Image PackImage(Image srcRgb, Image srcA, Image srcAo, bool invertGreen = false, bool invertAlpha = false, bool normalizeAlpha = false, long alphaChannel = 0, long aoChannel = 0) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.PackImage, [srcRgb, srcA, srcAo, invertGreen, invertAlpha, normalizeAlpha, alphaChannel, aoChannel]).As<Image>();\n\n\tpublic new static Image LuminanceToHeight(Image srcRgb) => \n\t\tClassDB.ClassCallStatic(NativeName, GDExtensionMethodName.LuminanceToHeight, [srcRgb]).As<Image>();\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/csharp/Terrain3DUtil.cs.uid",
    "content": "uid://b5712pl5vpmet\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/3rd_party/import_sgt.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Import From SimpleGrassTextured\n# \n# This script demonstrates how to import transforms from SimpleGrassTextured. To use it:\n#\n# 1. Setup the mesh asset you wish to use in the asset dock.\n# 1. Select your Terrain3D node.\n# 1. In the inspector, click Script (very bottom) and Quick Load import_sgt.gd.\n# 1. At the very top, assign your SimpleGrassTextured node.\n# 1. Input the desired mesh asset ID.\n# 1. Click import. The output window and console will report when finished.\n# 1. Clear the script from your Terrain3D node, and save your scene. \n#\n# The instance transforms are now stored in your region files.\n#\n# Use clear_instances to erase all instances that match the assign_mesh_id.\n#\n# The add_transforms function (called by add_multimesh) applies the height_offset specified in the \n# Terrain3DMeshAsset. \n# Once the transforms are imported, you can reassign any mesh you like into this mesh slot.\n\n@tool\nextends Terrain3D\n\n@export var simple_grass_textured: MultiMeshInstance3D\n@export var assign_mesh_id: int\n@export_tool_button(\"Import\") var import = import_sgt\n@export_tool_button(\"Clear Instances\") var clear_instances = clear_multimeshes\n\n\nfunc clear_multimeshes() -> void:\n\tget_instancer().clear_by_mesh(assign_mesh_id)\n\n\nfunc import_sgt() -> void:\n\tvar sgt_mm: MultiMesh = simple_grass_textured.multimesh\n\tvar global_xform: Transform3D = simple_grass_textured.global_transform\t\n\tprint(\"Starting to import %d instances from SimpleGrassTextured using mesh id %d\" % [ sgt_mm.instance_count, assign_mesh_id])\n\tvar time: int = Time.get_ticks_msec()\n\tget_instancer().add_multimesh(assign_mesh_id, sgt_mm, simple_grass_textured.global_transform)\t\n\tprint(\"Import complete in %.2f seconds\" % [ float(Time.get_ticks_msec() - time)/1000. ])\n\t\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/3rd_party/import_sgt.gd.uid",
    "content": "uid://bllcuwetve45k\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/3rd_party/project_on_terrain3d.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter\n# It provides a `Project on Terrain3D` modifier, which allows Scatter \n# to detect the terrain height from Terrain3D without using collision.\n#\n# Copy this file into /addons/proton_scatter/src/modifiers\n# Then uncomment everything below (select, press CTRL+K)\n# In the editor, add this modifier to Scatter, then set your Terrain3D node\n\n#@tool\n#extends \"base_modifier.gd\"\n#\n#\n#signal projection_completed\n#\n#\n#@export var terrain_node : NodePath\n#@export var align_with_collision_normal : bool = false\n#@export_range(0.0, 90.0, 0.1) var max_slope : float = 90.0\n#@export var enable_texture_filtering : bool = false\n#@export_range(0, 31) var target_texture_id : int = 0\n#@export var not_target_texture : bool = false\n#@export_range(0.0, 1.0, 0.01) var texture_threshold : float = 0.8\n#\n#var _terrain: Terrain3D\n#\n#\n#func _init() -> void:\n\t#display_name = \"Project On Terrain3D\"\n\t#category = \"Edit\"\n\t#can_restrict_height = false\n\t#global_reference_frame_available = true\n\t#local_reference_frame_available = true\n\t#individual_instances_reference_frame_available = true\n\t#use_global_space_by_default()\n#\n\t#documentation.add_paragraph(\n\t\t#\"This is a modified version of `Project on Colliders` that queries Terrain3D\n\t\t#for heights without using collision. It constrains placement by slope or texture.\n#\n\t\t#This modifier must have terrain_node set to a Terrain3D node.\")\n#\n\t#var p := documentation.add_parameter(\"Terrain Node\")\n\t#p.set_type(\"NodePath\")\n\t#p.set_description(\"Set your Terrain3D node.\")\n\t\t#\n\t#p = documentation.add_parameter(\"Align with collision normal\")\n\t#p.set_type(\"bool\")\n\t#p.set_description(\n\t\t#\"Rotate the transform to align it with the collision normal in case\n\t\t#the ray cast hit a collider.\")\n\t\t#\n\t#p = documentation.add_parameter(\"Enable Texture Filtering\")\n\t#p.set_type(\"bool\")\n\t#p.set_description(\n\t\t#\"If enabled, objects will only be placed based on the ground texture specified.\")\n\t\t#\n\t#p = documentation.add_parameter(\"Target Texture ID\")\n\t#p.set_type(\"int\")\n\t#p.set_description(\n\t\t#\"The ID of the texture to place objects on (0-31). Objects will only be placed on this texture.\")\n\t\t#\n\t#p = documentation.add_parameter(\"Not Target Texture\")\n\t#p.set_type(\"bool\") \n\t#p.set_description(\n\t\t#\"If true, objects will be placed on all textures EXCEPT the target texture.\")\n\t\t#\n\t#p = documentation.add_parameter(\"Texture Threshold\")\n\t#p.set_type(\"float\") \n\t#p.set_description(\"The blend value required for placement on the texture.\")\n#\n#\n#func _process_transforms(transforms, domain, _seed) -> void:\n\t#if transforms.is_empty():\n\t\t#return\n#\n\t#if terrain_node:\n\t\t#_terrain = domain.get_root().get_node_or_null(terrain_node)\n#\n\t#if not _terrain:\n\t\t#warning += \"\"\"No Terrain3D node found\"\"\"\n\t\t#return\n#\n\t#if not _terrain.data:\n\t\t#warning += \"\"\"Terrain3DData is not initialized\"\"\"\n\t\t#return\n#\n\t## Review transforms\n\t#var gt: Transform3D = domain.get_global_transform()\n\t#var gt_inverse := gt.affine_inverse()\n\t#var new_transforms_array: Array[Transform3D] = []\n\t#var remapped_max_slope: float = remap(max_slope, 0.0, 90.0, 0.0, 1.0)\n\t#for i in transforms.list.size():\n\t\t#var t: Transform3D = transforms.list[i]\n\t\t#\n\t\t#var location: Vector3 = (gt * t).origin\n\t\t#var height: float = _terrain.data.get_height(location)\t\t\n\t\t#if is_nan(height):\n\t\t\t#continue\n\t\t#\n\t\t#var normal: Vector3 = _terrain.data.get_normal(location)\n\t\t#if not abs(Vector3.UP.dot(normal)) >= (1.0 - remapped_max_slope):\n\t\t\t#continue\n\t\t#\n\t\t#if enable_texture_filtering:\n\t\t\t#var texture_info: Vector3 = _terrain.data.get_texture_id(location)\n\t\t\t#var base_id: int = int(texture_info.x)\n\t\t\t#var overlay_id: int = int(texture_info.y)\n\t\t\t#var blend_value: float = texture_info.z\n\t\t\t## Skip if overlay or blend != target texture, unless inverted\n\t\t\t#if ((overlay_id != target_texture_id or blend_value < texture_threshold) and \\\n\t\t\t\t#(base_id != target_texture_id or blend_value >= texture_threshold)) != not_target_texture:\n\t\t\t\t\t#continue\n#\n\t\t#if align_with_collision_normal and not is_nan(normal.x):\n\t\t\t#t.basis.y = normal\n\t\t\t#t.basis.x = -t.basis.z.cross(normal)\n\t\t\t#t.basis = t.basis.orthonormalized()\n#\n\t\t#t.origin.y = height - gt.origin.y\n\t\t#new_transforms_array.push_back(t)\n#\n\t#transforms.list.clear()\n\t#transforms.list.append_array(new_transforms_array)\n#\n\t#if transforms.is_empty():\n\t\t#warning += \"\"\"All transforms have been removed. Possible reasons include: \\n\"\"\"\n\t\t#if enable_texture_filtering:\n\t\t\t#warning += \"\"\"+ No matching texture found at any position.\n\t\t\t#+ Texture threshold may be too high.\n\t\t\t#\"\"\"\n\t\t#warning += \"\"\"+ No collider is close enough to the shapes.\n\t\t#+ Ray length is too short.\n\t\t#+ Ray direction is incorrect.\n\t\t#+ Collision mask is not set properly.\n\t\t#+ Max slope is too low.\n\t\t#\"\"\"\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/3rd_party/project_on_terrain3d.gd.uid",
    "content": "uid://g3opjh3m3iww\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/Terrain3DParticles.tscn",
    "content": "[gd_scene load_steps=5 format=3 uid=\"uid://d3sr0a7dxfkr8\"]\n\n[ext_resource type=\"Script\" uid=\"uid://bp7r4ppgq1m0g\" path=\"res://addons/terrain_3d/extras/particle_example/terrain_3D_particles.gd\" id=\"1_gl3qg\"]\n[ext_resource type=\"Material\" uid=\"uid://el5y10hnh13g\" path=\"res://addons/terrain_3d/extras/particle_example/process_material.tres\" id=\"2_2gon1\"]\n[ext_resource type=\"Material\" uid=\"uid://ljo1wt61kbkq\" path=\"res://addons/terrain_3d/extras/particle_example/grass_material.tres\" id=\"3_qyjnw\"]\n\n[sub_resource type=\"RibbonTrailMesh\" id=\"RibbonTrailMesh_fwrtk\"]\nshape = 0\nsection_length = 0.18\nsection_segments = 1\n\n[node name=\"Terrain3DParticles\" type=\"Node3D\"]\nscript = ExtResource(\"1_gl3qg\")\ninstance_spacing = 0.25\ncell_width = 24.0\ngrid_width = 5\nrows = 96\namount = 9216\nprocess_material = ExtResource(\"2_2gon1\")\nmesh = SubResource(\"RibbonTrailMesh_fwrtk\")\nshadow_mode = 0\nmesh_material_override = ExtResource(\"3_qyjnw\")\nmin_draw_distance = 60.0\nparticle_count = 230400\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/grass.gdshader",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nshader_type spatial;\n\nrender_mode skip_vertex_transform, cull_disabled, blend_mix;\n\nuniform vec2 wind_direction = vec2(1.0, 1.0);\n\nvarying vec4 data;\n\n// A lot of hard coded things here atm.\nvoid vertex() {\n\t// Wind effect from model data, in this case no vertex colors,\n\t// so just use vertex Y component, including mesh offset\n\tdata[2] = (VERTEX.y + 0.55);\n\tdata[2] *= data[2]; // make non-linear\n\n\t// Ribbon used as a grass mesh.. so pinch the top.\n\tVERTEX.xz *= (1.0 - data[2]);\n\n\t// Brighten tips\n\tCOLOR = mix(COLOR, vec4(1.0), smoothstep(0.9, 1.0, data[2]));\n\t// Darken base, skip is scale is less than threshold, as this means \"grow in\" is occuring.\n\tCOLOR *= INSTANCE_CUSTOM[3] < 0.35 ? 1. : mix(1.0, 0.75, smoothstep(0.35, 0.0, data[2]));\n\t// Save red/green shift for fragment\n\tdata.rg = INSTANCE_CUSTOM.rg;\n\n\t// World space vertex\n\tvec3 w_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;\n\t// Get wind force and scale from process()\n\tfloat scale = pow(INSTANCE_CUSTOM[3] * INSTANCE_CUSTOM[3], 0.707);\n\tfloat force = INSTANCE_CUSTOM[2] * data[2] * scale;\n\t// Add some cheap jitter at high wind values\n\tforce -= fract(force * 256.0) * force * 0.05;\n\t// Curve the result\n\tforce = pow(force, 0.707);\n\n\t// These 2 combined result in a decent bend without resorting to matrices or pivot data.\n\t// Lateral move and wobble\n\tfloat lateral_wobble = sin(TIME * 2.0 * (1.0 + data.r + data.g)) * 0.25 * (1.0 - INSTANCE_CUSTOM[2]);\n\tvec2 direction = normalize(wind_direction);\n\tw_vertex.xz -= (vec2(-direction.y, direction.x) * lateral_wobble + direction) * force;\n\t// Flatten\n\tw_vertex.y -= INSTANCE_CUSTOM[2] * force * data[2];\n\n\t// Save final wind force value for fragment.\n\tdata[3] = force;\n\n\tVERTEX = (VIEW_MATRIX * vec4(w_vertex, 1.0)).xyz;\n\tNORMAL = MODELVIEW_NORMAL_MATRIX * NORMAL;\n\tBINORMAL = MODELVIEW_NORMAL_MATRIX * BINORMAL;\n\tTANGENT = MODELVIEW_NORMAL_MATRIX * TANGENT;\n}\n\nvoid fragment() {\n\t// Hard coded color.\n\tALBEDO = vec3(0.20, 0.22, 0.05) * (data[2] * 0.5 + 0.5);\n\tALBEDO.rg *= (data.rg * 0.3 + 0.9);\n\tALBEDO *= pow(COLOR.rgb, vec3(2.2));\n\t// Modify roughness / specular based on wind force for added detail\n\tfloat spec_rough = clamp(max(data[2], data[3]), 0., 1.);\n\tROUGHNESS = 1. - spec_rough;\n\tSPECULAR = clamp(spec_rough * 0.25, 0., .15);\n\n\tBACKLIGHT = vec3(0.33);\n\t#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n\tALBEDO = pow(ALBEDO, vec3(0.4));\n\t#endif\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/grass.gdshader.uid",
    "content": "uid://dq3lfyp3u5oxt\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/grass_material.tres",
    "content": "[gd_resource type=\"ShaderMaterial\" load_steps=2 format=3 uid=\"uid://ljo1wt61kbkq\"]\n\n[ext_resource type=\"Shader\" uid=\"uid://dq3lfyp3u5oxt\" path=\"res://addons/terrain_3d/extras/particle_example/grass.gdshader\" id=\"1_nkru0\"]\n\n[resource]\nrender_priority = 0\nshader = ExtResource(\"1_nkru0\")\nshader_parameter/wind_direction = Vector2(1, 1)\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/particles.gdshader",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// This is an example particle shader designed to procedurally place\n// particles like grass, small rocks, and other ground effects, on the terrain\n// surface by reading the Terrain3D data maps. It works in tandem with the\n// provided GDScript.\n\nshader_type particles;\nrender_mode disable_velocity, disable_force;\n\ngroup_uniforms options;\nuniform sampler2D main_noise;\nuniform float main_noise_scale = 0.01;\nuniform vec3 position_offset = vec3(0.);\nuniform bool align_to_normal = true;\nuniform float normal_strength : hint_range(0.01, 1.0, 0.01) = 0.3;\nuniform bool random_rotation = true;\nuniform float random_spacing : hint_range(0.0, 1.0, 0.01) = 0.5;\nuniform vec3 min_scale = vec3(1.0);\nuniform vec3 max_scale = vec3(1.0);\n\ngroup_uniforms wind;\nuniform float noise_scale = 0.0041;\nuniform float wind_speed = 0.025;\nuniform float wind_strength : hint_range(0.0, 1.0, 0.01) = 1.0;\nuniform float wind_dithering = 4.0;\nuniform vec2 wind_direction = vec2(1.0,1.0);\n\ngroup_uniforms shaping;\nuniform float clod_scale_boost = 3.0;\nuniform float clod_min_threshold : hint_range(0.0, 1.0, 0.001) = 0.2;\nuniform float clod_max_threshold : hint_range(0.0, 1.0, 0.001) = 0.5;\nuniform float patch_min_threshold : hint_range(0.0, 1.0, 0.001) = 0.025;\nuniform float patch_max_threshold : hint_range(0.0, 1.0, 0.001) = 0.2;\n\ngroup_uniforms filtering;\nuniform float condition_dither_range : hint_range(0.0, 1.0, 0.01) = 0.15;\nuniform float surface_slope_min : hint_range(0.0, 1.0, 0.01) = 0.87;\nuniform float distance_fade_ammount : hint_range(0.0, 1.0, 0.01) = 0.5;\n\ngroup_uniforms private;\nuniform float max_dist = 1.;\nuniform vec3 camera_position = vec3(0.);\nuniform uint instance_rows = 1;\nuniform float instance_spacing = 0.5;\nuniform uint _background_mode = 0u;\nuniform float _vertex_spacing = 1.0;\nuniform float _vertex_density = 1.0; // = 1/_vertex_spacing\nuniform float _region_size = 1024.0;\nuniform float _region_texel_size = 0.0009765625; // = 1/REGION_SIZE\nuniform int _region_map_size = 32;\nuniform int _region_map[1024];\nuniform vec2 _region_locations[1024];\nuniform highp sampler2DArray _height_maps : repeat_disable;\nuniform highp sampler2DArray _control_maps : repeat_disable;\nuniform highp sampler2DArray _color_maps : repeat_disable;\n\n// Defined Constants\n#define SKIP_PASS 0\n#define VERTEX_PASS 1\n#define FRAGMENT_PASS 2\n\n// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)\n// Returns ivec3 with:\n// XY: (0 to _region_size - 1) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nivec3 get_index_coord(const vec2 uv, const int search) {\n\tvec2 r_uv = round(uv);\n\tvec2 o_uv = mod(r_uv,_region_size);\n\tivec2 pos;\n\tint bounds, layer_index = -1;\n\tfor (int i = -1; i < 0; i++) {\n\t\tif ((layer_index == -1 && _background_mode == 0u) || i < 0) {\n\t\t\tr_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));\n\t\t\tpos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);\n\t\t\tbounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\t\t\tlayer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);\n\t\t}\n\t}\n\treturn ivec3(ivec2(mod(r_uv,_region_size)), layer_index);\n}\n\n#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n\t#define fma(a, b, c) ((a) * (b) + (c))\n#endif\nfloat random(vec2 v) {\n\treturn fract(1e4 * sin(fma(17.0, v.x, v.y * 0.1)) * (0.1 + abs(sin(fma(v.y, 13.0, v.x)))));\n}\n\nmat3 rotation_matrix(vec3 axis, float angle) {\n\tfloat c = cos(angle);\n\tfloat s = sin(angle);\n\tfloat t = 1.0 - c;\n\tvec3 n = normalize(axis);\n\tfloat x = n.x;\n\tfloat y = n.y;\n\tfloat z = n.z;\n\n\treturn mat3(\n\t\tvec3(t * x * x + c, t * x * y - z * s, t * x * z + y * s),\n\t\tvec3(t * x * y + z * s, t * y * y + c, t * y * z - x * s),\n\t\tvec3(t * x * z - y * s, t * y * z + x * s, t * z * z + c));\n}\n\nmat3 align_to_vector(vec3 normal) {\n\tvec3 up = vec3(0.0, 1.0, 0.0);\n\tif (abs(dot(normal, up)) > 0.9999) { // Avoid singularity\n\t\tup = vec3(1.0, 0.0, 0.0);\n\t}\n\tvec3 tangent = normalize(cross(up, normal));\n\tvec3 bitangent = normalize(cross(tangent, normal));\n\treturn mat3(tangent, normal, bitangent);\n}\n\nvoid start() {\n\t// Create centered a grid\n\tvec3 pos = vec3(float(INDEX % instance_rows), 0.0, float(INDEX / instance_rows)) - float(instance_rows >>1u);\n\n\t// Apply spcaing\n\tpos *= instance_spacing;\n\t// Move the grid to the emitter, snapping is handled CPU side\n\tpos.xz += EMISSION_TRANSFORM[3].xz;\n\n\t// Create random values per-instance, incorporating the seed, mask bits to avoid NAN/INF\n\tfloat seed = fract(uintBitsToFloat(RANDOM_SEED & 0x7EFFFFFFu));\n\tvec3 r = fract(vec3(random(pos.xz), random(pos.xz + vec2(0.5)), random(pos.xz - vec2(0.5))) + seed);\n\t// Randomize instance spacing\n\tpos.x += ((r.x * 2.0) - 1.0) * random_spacing * instance_spacing;\n\tpos.z += ((r.z * 2.0) - 1.0) * random_spacing * instance_spacing;\n\n\t// Lookup offsets, ID and blend weight\n\tconst vec3 offsets = vec3(0, 1, 2);\n\tvec2 index_id = floor(pos.xz * _vertex_density);\n\tvec2 weight = fract(pos.xz * _vertex_density);\n\tvec2 invert = 1.0 - weight;\n\tvec4 weights = vec4(\n\t\tinvert.x * weight.y, // 0\n\t\tweight.x * weight.y, // 1\n\t\tweight.x * invert.y, // 2\n\t\tinvert.x * invert.y  // 3\n\t);\n\n\tivec3 index[4];\n\t// Map lookups\n\tindex[0] = get_index_coord(index_id + offsets.xy, VERTEX_PASS);\n\tindex[1] = get_index_coord(index_id + offsets.yy, VERTEX_PASS);\n\tindex[2] = get_index_coord(index_id + offsets.yx, VERTEX_PASS);\n\tindex[3] = get_index_coord(index_id + offsets.xx, VERTEX_PASS);\n\n\thighp float h[8];\n\th[0] = texelFetch(_height_maps, index[0], 0).r; // 0 (0,1)\n\th[1] = texelFetch(_height_maps, index[1], 0).r; // 1 (1,1)\n\th[2] = texelFetch(_height_maps, index[2], 0).r; // 2 (1,0)\n\th[3] = texelFetch(_height_maps, index[3], 0).r; // 3 (0,0)\n\th[4] = texelFetch(_height_maps, get_index_coord(index_id + offsets.yz, VERTEX_PASS), 0).r; // 4 (1,2)\n\th[5] = texelFetch(_height_maps, get_index_coord(index_id + offsets.zy, VERTEX_PASS), 0).r; // 5 (2,1)\n\th[6] = texelFetch(_height_maps, get_index_coord(index_id + offsets.zx, VERTEX_PASS), 0).r; // 6 (2,0)\n\th[7] = texelFetch(_height_maps, get_index_coord(index_id + offsets.xz, VERTEX_PASS), 0).r; // 7 (0,2)\n\tvec3 index_normal[4];\n\tindex_normal[0] = vec3(h[0] - h[1], _vertex_spacing, h[0] - h[7]);\n\tindex_normal[1] = vec3(h[1] - h[5], _vertex_spacing, h[1] - h[4]);\n\tindex_normal[2] = vec3(h[2] - h[6], _vertex_spacing, h[2] - h[1]);\n\tindex_normal[3] = vec3(h[3] - h[2], _vertex_spacing, h[3] - h[0]);\n\tvec3 w_normal = normalize(\n\t\tindex_normal[0] * weights[0] +\n\t\tindex_normal[1] * weights[1] +\n\t\tindex_normal[2] * weights[2] +\n\t\tindex_normal[3] * weights[3]);\n\n\t// Set the height according to the heightmap data\n\tpos.y =\n\t\th[0] * weights[0] +\n\t\th[1] * weights[1] +\n\t\th[2] * weights[2] +\n\t\th[3] * weights[3] ;\n\n\t// Offset, Rotation, Alignment.\n\tTRANSFORM = mat4(1.0);\n\tvec3 orientation = vec3(0., 1., 0.);\n\n\tvec2 uv = (pos.xz) * main_noise_scale;\n\tfloat noise = textureLod(main_noise, uv, 0.0).r;\n\tfloat clods = smoothstep(clod_min_threshold, clod_max_threshold, noise) * clod_scale_boost;\n\tfloat patch = smoothstep(patch_min_threshold, patch_max_threshold, noise);\n\tfloat width_modifier = 1.0 + 3.0 * smoothstep(0., max_dist, length(camera_position - pos));\n\n\t// Calculate scale\n\tvec3 scale = vec3(\n\t\tmix(min_scale.x, max_scale.x, r.x) * width_modifier,\n\t\tmix(min_scale.y, max_scale.y, r.y) + clods,\n\t\tmix(min_scale.z, max_scale.z, r.z) * width_modifier) * patch;\n\n\t// Apply scale to offset\n\tvec3 offset = position_offset * scale;\n\n\t// Apply normal orientation\n\tif (align_to_normal) {\n\t\torientation = mix(orientation, w_normal, normal_strength);\n\t\tmat3 alignment = align_to_vector(orientation);\n\t\toffset = alignment * offset;\n\t\tTRANSFORM = mat4(alignment);\n\t}\n\n\t// Apply rotation around orientation\n\tif (random_rotation) {\n\t\tmat3 rotation = rotation_matrix(orientation, r.x * TAU);\n\t\tTRANSFORM = mat4(rotation) * TRANSFORM;\n\t}\n\n\t// Filtering - Causes some particles to be rendered as degenerate triangles\n\t// via 0./0. - Particles filtered this way are still processed by the GPU.\n\t// For compatibility it seems we must shift as well.\n\t// Surface slope filtering\n\tif (surface_slope_min > w_normal.y + (r.y - 0.5) * condition_dither_range) {\n\t\tpos.y = 0. / 0.;\n\t\tpos.xz = vec2(100000.0);\n\t}\n\n\t// Read color map\n\thighp vec4 c[4];\n\t#define COLOR_MAP vec4(1., 1., 1., 0.5)\n\tc[0] = index[0].z >= 0 ? texelFetch(_color_maps, index[0], 0) : COLOR_MAP; // 0 (0,1)\n\tc[1] = index[1].z >= 0 ? texelFetch(_color_maps, index[1], 0) : COLOR_MAP; // 1 (1,1)\n\tc[2] = index[2].z >= 0 ? texelFetch(_color_maps, index[2], 0) : COLOR_MAP; // 2 (1,0)\n\tc[3] = index[3].z >= 0 ? texelFetch(_color_maps, index[3], 0) : COLOR_MAP; // 3 (0,0)\n\tvec4 color_map =\n\t\tc[0] * weights[0] +\n\t\tc[1] * weights[1] +\n\t\tc[2] * weights[2] +\n\t\tc[3] * weights[3] ;\n\n\tCOLOR = color_map;\n\n\t// Read control maps\n\tuvec4 control = uvec4(\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[0], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[1], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[2], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[3], 0).r));\n\tbool hole = any(bvec4(control >> uvec4(2u) & uvec4(0x1u)));\n\tbool auto = any(bvec4(control & uvec4(0x1u)));\n\tint base = int(control[3] >> 27u & 0x1Fu);\n\tint over = int(control[3] >> 22u & 0x1Fu);\n\tfloat blend = float(control[3] >> 14u & 0xFFu) * 0.003921568627450; // 1. / 255.\n\n\t// Filter out holes\n\tif (hole) {\n\t\tpos.y = 0. / 0.;\n\t\tpos.xz = vec2(100000.0);\n\t}\n\n\t// Hardcoded example, hand painted texture id 0 is filtered out.\n\tif (!auto && ((base == 0 && blend < 0.7) || (over == 0 && blend >= 0.3))) {\n\t\tpos.y = 0. / 0.;\n\t\tpos.xz = vec2(100000.0);\n\t}\n\n\tif (length(camera_position - pos) > max_dist) {\n\t\tpos.y = 0. / 0.;\n\t\tpos.xz = vec2(100000.0);\n\t} else {\n\t\tfloat fade_factor = 1.0 - smoothstep(max_dist * distance_fade_ammount, max_dist + 0.0001, length(camera_position - pos)) + 0.001;\n\t\tscale.y *= fade_factor;\n\t\toffset *= fade_factor;\n\t}\n\n\t// Apply scale\n\tTRANSFORM[0] *= scale.x;\n\tTRANSFORM[1] *= scale.y;\n\tTRANSFORM[2] *= scale.z;\n\n\t// Apply the position\n\tTRANSFORM[3].xyz = pos.xyz + offset;\n\n\t// Save Fixed 2 Random values for Reg/Green color randomness\n\tCUSTOM.rg = r.rg;\n\t// Save Y component scale pre-rotation\n\tCUSTOM[3] = scale.y;\n}\n\nvoid process() {\n\t// Extract world space UV from Transform Matrix\n\tvec2 uv = (TRANSFORM[3].xz + CUSTOM.rg * wind_dithering) * noise_scale;\n\t// Scaled wind noise, updated per instance, at process FPS. Passed to Vertex()\n\tCUSTOM[2] = textureLod(main_noise, uv + TIME * wind_speed * normalize(wind_direction), 0.0).r * wind_strength;\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/particles.gdshader.uid",
    "content": "uid://dce675i014xcn\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/process_material.tres",
    "content": "[gd_resource type=\"ShaderMaterial\" load_steps=4 format=3 uid=\"uid://el5y10hnh13g\"]\n\n[ext_resource type=\"Shader\" uid=\"uid://dce675i014xcn\" path=\"res://addons/terrain_3d/extras/particle_example/particles.gdshader\" id=\"1_t55me\"]\n\n[sub_resource type=\"FastNoiseLite\" id=\"FastNoiseLite_544iv\"]\nnoise_type = 2\nfrequency = 0.0145\ncellular_return_type = 4\n\n[sub_resource type=\"NoiseTexture2D\" id=\"NoiseTexture2D_544iv\"]\nnoise = SubResource(\"FastNoiseLite_544iv\")\nseamless = true\n\n[resource]\nshader = ExtResource(\"1_t55me\")\nshader_parameter/main_noise = SubResource(\"NoiseTexture2D_544iv\")\nshader_parameter/main_noise_scale = 0.01\nshader_parameter/position_offset = Vector3(0, 0.45, 0)\nshader_parameter/align_to_normal = true\nshader_parameter/normal_strength = 0.3\nshader_parameter/random_rotation = true\nshader_parameter/random_spacing = 0.5\nshader_parameter/min_scale = Vector3(0.125, 0.5, 0.125)\nshader_parameter/max_scale = Vector3(0.125, 1, 0.125)\nshader_parameter/noise_scale = 0.0041\nshader_parameter/wind_speed = 0.025\nshader_parameter/wind_strength = 1.0\nshader_parameter/wind_dithering = 4.0\nshader_parameter/wind_direction = Vector2(1, 1)\nshader_parameter/clod_scale_boost = 2.0\nshader_parameter/clod_min_threshold = 0.2\nshader_parameter/clod_max_threshold = 0.8\nshader_parameter/patch_min_threshold = 0.025\nshader_parameter/patch_max_threshold = 0.2\nshader_parameter/condition_dither_range = 0.15\nshader_parameter/surface_slope_min = 0.87\nshader_parameter/distance_fade_ammount = 0.66\nshader_parameter/max_dist = 1.0\nshader_parameter/camera_position = Vector3(0, 0, 0)\nshader_parameter/instance_rows = 1\nshader_parameter/instance_spacing = 0.5\nshader_parameter/_background_mode = 0\nshader_parameter/_vertex_spacing = 1.0\nshader_parameter/_vertex_density = 1.0\nshader_parameter/_region_size = 1024.0\nshader_parameter/_region_texel_size = 0.000976563\nshader_parameter/_region_map_size = 32\nshader_parameter/_region_map = PackedInt32Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\nshader_parameter/_region_locations = PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/terrain_3D_particles.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n#\n# This is an example of using a particle shader with Terrain3D.\n# To use it, add `Terrain3DParticles.tscn` to your scene and assign the terrain.\n# Then customize the settings, materials and shader to extend it and make it your own.\n\n@tool\nextends Node3D\n\n\n#region settings\n## Auto set if attached as a child of a Terrain3D node\n@export var terrain: Terrain3D:\n\tset(value):\n\t\tterrain = value\n\t\t_create_grid()\n\n\n## Distance between instances\n@export_range(0.125, 2.0, 0.015625) var instance_spacing: float = 0.5:\n\tset(value):\n\t\tinstance_spacing = clamp(round(value * 64.0) * 0.015625, 0.125, 2.0)\n\t\trows = maxi(int(cell_width / instance_spacing), 1)\n\t\tamount = rows * rows\n\t\t_set_offsets()\n\n\n## Width of an individual cell of the grid\n@export_range(8.0, 256.0, 1.0) var cell_width: float = 32.0:\n\tset(value):\n\t\tcell_width = clamp(value, 8.0, 256.0)\n\t\trows = maxi(int(cell_width / instance_spacing), 1)\n\t\tamount = rows * rows\n\t\tmin_draw_distance = 1.0\n\t\t# Have to update aabb\n\t\tif terrain and terrain.data:\n\t\t\tvar height_range: Vector2 = terrain.data.get_height_range()\n\t\t\tvar height: float = height_range[0] - height_range[1]\n\t\t\tvar aabb: AABB = AABB()\n\t\t\taabb.size = Vector3(cell_width, height, cell_width)\n\t\t\taabb.position = aabb.size * -0.5\n\t\t\taabb.position.y = height_range[1]\n\t\t\tfor p in particle_nodes:\n\t\t\t\tp.custom_aabb = aabb\n\t\t_set_offsets()\n\n\n## Grid width. Must be odd. \n## Higher values cull slightly better, draw further out.\n@export_range(1, 15, 2) var grid_width: int = 9:\n\tset(value):\n\t\tgrid_width = value\n\t\tparticle_count = 1\n\t\tmin_draw_distance = 1.0\n\t\t_create_grid()\n\n\n@export_storage var rows: int = 1\n\n@export_storage var amount: int = 1:\n\tset(value):\n\t\tamount = value\n\t\tparticle_count = value\n\t\tlast_pos = Vector3.ZERO\n\t\tfor p in particle_nodes:\n\t\t\tp.amount = amount\n\n\n@export_range(1, 256, 1) var process_fixed_fps: int = 30:\n\tset(value):\n\t\tprocess_fixed_fps = maxi(value, 1)\n\t\tfor p in particle_nodes:\n\t\t\tp.fixed_fps = process_fixed_fps\n\t\t\tp.preprocess = 1.0 / float(process_fixed_fps)\n\n\n## Access to process material parameters\n@export var process_material: ShaderMaterial\n\n## The mesh that each particle will render\n@export var mesh: Mesh\n\n@export var shadow_mode: GeometryInstance3D.ShadowCastingSetting = (\n\t\tGeometryInstance3D.ShadowCastingSetting.SHADOW_CASTING_SETTING_ON):\n\tset(value):\n\t\tshadow_mode = value\n\t\tfor p in particle_nodes:\n\t\t\tp.cast_shadow = value\n\n\n## Override material for the particle mesh\n@export_custom(\n\tPROPERTY_HINT_RESOURCE_TYPE,\n\t\"BaseMaterial3D,ShaderMaterial\") var mesh_material_override: Material:\n\tset(value):\n\t\tmesh_material_override = value\n\t\tfor p in particle_nodes:\n\t\t\tp.material_override = mesh_material_override\n\n\n@export_group(\"Info\")\n## The minimum distance that particles will be drawn upto\n## If using fade out effects like pixel alpha this is the limit to use.\n@export var min_draw_distance: float = 1.0:\n\tset(value):\n\t\tmin_draw_distance = float(cell_width * grid_width) * 0.5\n\n\n## Displays current total particle count based on Cell Width and Instance Spacing\n@export var particle_count: int = 1:\n\tset(value):\n\t\tparticle_count = amount * grid_width * grid_width\n\n#endregion\n\n\nvar offsets: Array[Vector3]\nvar last_pos: Vector3 = Vector3.ZERO\nvar particle_nodes: Array[GPUParticles3D]\n\n\nfunc _ready() -> void:\n\tif not terrain:\n\t\tvar parent: Node = get_parent()\n\t\tif parent is Terrain3D:\n\t\t\tterrain = parent\n\t_create_grid()\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\t_destroy_grid()\n\n\nfunc _physics_process(delta: float) -> void:\n\tif terrain:\n\t\tvar camera: Camera3D = terrain.get_camera()\n\t\tif camera:\n\t\t\tif last_pos.distance_squared_to(camera.global_position) > 1.0:\n\t\t\t\tvar pos: Vector3 = camera.global_position.snapped(Vector3.ONE)\n\t\t\t\t_position_grid(pos)\n\t\t\t\tRenderingServer.material_set_param(process_material.get_rid(), \"camera_position\", pos )\n\t\t\t\tlast_pos = camera.global_position\n\t\t_update_process_parameters()\n\telse:\n\t\tset_physics_process(false)\n\n\nfunc _create_grid() -> void:\n\t_destroy_grid()\n\tif not terrain:\n\t\treturn\n\tset_physics_process(true)\n\t_set_offsets()\n\tvar hr: Vector2 = terrain.data.get_height_range()\n\tvar height: float = hr.x - hr.y\n\tvar aabb: AABB = AABB()\n\taabb.size = Vector3(cell_width, height, cell_width)\n\taabb.position = aabb.size * -0.5\n\taabb.position.y = hr.y\n\tvar half_grid: int = grid_width / 2\n\t# Iterating the array like this allows identifying grid position, in case setting\n\t# different mesh or materials is desired for LODs etc.\n\tfor x in range(-half_grid, half_grid + 1):\n\t\tfor z in range(-half_grid, half_grid + 1):\n\t\t\t#var ring: int = maxi(maxi(absi(x), absi(z)), 0)\n\t\t\tvar particle_node = GPUParticles3D.new()\n\t\t\tparticle_node.lifetime = 600.0\n\t\t\tparticle_node.amount = amount\n\t\t\tparticle_node.explosiveness = 1.0\n\t\t\tparticle_node.amount_ratio = 1.0\n\t\t\tparticle_node.process_material = process_material\n\t\t\tparticle_node.draw_pass_1 = mesh\n\t\t\tparticle_node.speed_scale = 1.0\n\t\t\tparticle_node.custom_aabb = aabb\n\t\t\tparticle_node.cast_shadow = shadow_mode\n\t\t\tparticle_node.fixed_fps = process_fixed_fps\n\t\t\t# This prevent minor grid alignment errors when the camera is moving very fast\n\t\t\tparticle_node.preprocess = 1.0 / float(process_fixed_fps)\n\t\t\tif mesh_material_override:\n\t\t\t\tparticle_node.material_override = mesh_material_override\n\t\t\tparticle_node.use_fixed_seed = true\n\t\t\tif (x > -half_grid and z > -half_grid): # Use the same seed across all nodes\n\t\t\t\tparticle_node.seed = particle_nodes[0].seed\n\t\t\tself.add_child(particle_node)\n\t\t\tparticle_node.emitting = true\n\t\t\tparticle_nodes.push_back(particle_node)\n\tlast_pos = Vector3.ZERO\n\n\nfunc _set_offsets() -> void:\n\tvar half_grid: int = grid_width / 2\n\toffsets.clear()\n\tfor x in range(-half_grid, half_grid + 1):\n\t\tfor z in range(-half_grid, half_grid + 1):\n\t\t\tvar offset := Vector3(\n\t\t\t\tfloat(x * rows) * instance_spacing,\n\t\t\t\t0.0,\n\t\t\t\tfloat(z * rows) * instance_spacing\n\t\t\t)\n\t\t\toffsets.append(offset)\n\n\nfunc _destroy_grid() -> void:\n\tfor node: GPUParticles3D in particle_nodes:\n\t\tif is_instance_valid(node):\n\t\t\tnode.queue_free()\n\tparticle_nodes.clear()\n\n\nfunc _position_grid(pos: Vector3) -> void:\n\tfor i in particle_nodes.size():\n\t\tvar node: GPUParticles3D = particle_nodes[i]\n\t\tvar snap = Vector3(pos.x, 0, pos.z).snapped(Vector3.ONE) + offsets[i]\n\t\tnode.global_position = (snap / instance_spacing).round() * instance_spacing\n\t\tnode.reset_physics_interpolation()\n\t\tnode.restart(true) # keep the same seed.\n\n\nfunc _update_process_parameters() -> void:\n\tif process_material:\n\t\tvar process_rid: RID = process_material.get_rid()\n\t\tif terrain and process_rid.is_valid():\n\t\t\tRenderingServer.material_set_param(process_rid, \"_background_mode\", terrain.material.world_background)\n\t\t\tRenderingServer.material_set_param(process_rid, \"_vertex_spacing\", terrain.vertex_spacing)\n\t\t\tRenderingServer.material_set_param(process_rid, \"_vertex_density\", 1.0 / terrain.vertex_spacing)\n\t\t\tRenderingServer.material_set_param(process_rid, \"_region_size\", terrain.region_size)\n\t\t\tRenderingServer.material_set_param(process_rid, \"_region_texel_size\", 1.0 / terrain.region_size)\n\t\t\tRenderingServer.material_set_param(process_rid, \"_region_map_size\", 32)\n\t\t\tRenderingServer.material_set_param(process_rid, \"_region_map\", terrain.data.get_region_map())\n\t\t\tRenderingServer.material_set_param(process_rid, \"_region_locations\", terrain.data.get_region_locations())\n\t\t\tRenderingServer.material_set_param(process_rid, \"_height_maps\", terrain.data.get_height_maps_rid())\n\t\t\tRenderingServer.material_set_param(process_rid, \"_control_maps\", terrain.data.get_control_maps_rid())\n\t\t\tRenderingServer.material_set_param(process_rid, \"_color_maps\", terrain.data.get_color_maps_rid())\n\t\t\tRenderingServer.material_set_param(process_rid, \"instance_spacing\", instance_spacing)\n\t\t\tRenderingServer.material_set_param(process_rid, \"instance_rows\", rows)\n\t\t\tRenderingServer.material_set_param(process_rid, \"max_dist\", min_draw_distance)\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/particle_example/terrain_3D_particles.gd.uid",
    "content": "uid://bp7r4ppgq1m0g\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/M_ocean.tres",
    "content": "[gd_resource type=\"ShaderMaterial\" load_steps=4 format=3 uid=\"uid://cgk4glwmc4vk2\"]\n\n[ext_resource type=\"Shader\" uid=\"uid://biawd3fhk5weg\" path=\"res://addons/terrain_3d/extras/shaders/ocean_shader.gdshader\" id=\"1_t7ju5\"]\n\n[sub_resource type=\"FastNoiseLite\" id=\"FastNoiseLite_m6kh4\"]\nnoise_type = 2\nfrequency = 0.029\nfractal_type = 2\nfractal_octaves = 3\nfractal_gain = 0.195\ncellular_return_type = 3\n\n[sub_resource type=\"NoiseTexture2D\" id=\"NoiseTexture2D_i2r35\"]\ngenerate_mipmaps = false\nnoise = SubResource(\"FastNoiseLite_m6kh4\")\nseamless = true\n\n[resource]\nrender_priority = 0\nshader = ExtResource(\"1_t7ju5\")\nshader_parameter/sea_level = 5.000000819565997\nshader_parameter/water_color = Color(0.1058, 0.1568, 0.2117, 1)\nshader_parameter/visible_depth = 256.0\nshader_parameter/depth_curve = 0.1\nshader_parameter/shore_blend = 0.999999977648\nshader_parameter/refraction_strength = 0.02\nshader_parameter/time_scale = 8.0\nshader_parameter/height_scale = 3.0\nshader_parameter/ocean_scale = 2.0\nshader_parameter/fresnel_scale = 0.5\nshader_parameter/fresnel_power = 1.0\nshader_parameter/specular = 1.0\nshader_parameter/foam_enabled = true\nshader_parameter/foam_height = 1.350000064125\nshader_parameter/foam_top_intensity = 2.0\nshader_parameter/foam_distance = 3072.00014591674\nshader_parameter/foam_shore_enabled = true\nshader_parameter/foam_shore_intensity = 1.3000000584901201\nshader_parameter/foam_noise_texture = SubResource(\"NoiseTexture2D_i2r35\")\nshader_parameter/foam_noise_scale = 1.5\nshader_parameter/light_scattering_enabled = true\nshader_parameter/height_scattering = 6.000000285\nshader_parameter/height_threshold = 0.1\nshader_parameter/height_angle_threshold = 0.21\nshader_parameter/fersnel_scattering = 20.0\nshader_parameter/direct_scattering = 10.0\nshader_parameter/_mesh_size = 32.0\nshader_parameter/_subdiv = 1.0\nshader_parameter/_vertex_spacing = 4.0\nshader_parameter/_vertex_density = 0.25\nshader_parameter/_target_pos = Vector3(0, 0, 0)\nshader_parameter/_light_direction = Vector3(0, 0.7671652, 0.64144963)\nshader_parameter/_light_color = Color(1, 1, 1, 1)\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/hex_grid.gdshaderinc",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n// This shader snippet draws a hex grid\n\n// To use it, add this line to the top of your shader:\n// #include \"res://addons/terrain_3d/extras/shaders/hex_grid.gdshaderinc\"\n\n// And this line at the bottom of your shader:\n// \tdraw_hex_grid(uv2, _region_texel_size, w_normal, ALBEDO);\n\nmat2 rotate2d(float _angle) {\n\treturn mat2(vec2(cos(_angle),-sin(_angle)), vec2(sin(_angle), cos(_angle)));\n}\n\nvoid draw_hex_grid(vec2 uv, float texel_size, vec3 normal, inout vec3 albedo) {\n\tfloat hex_size = 0.02;\n\tfloat line_thickness = 0.04;\n\n\tvec2 guv = (uv - vec2(0.5 * texel_size)) / hex_size;\n\n\t// Convert UV to axial hex coordinates\n\tfloat q = (sqrt(3.0) / 3.0 * guv.x - 1.0 / 3.0 * guv.y);\n\tfloat r = (2.0 / 3.0 * guv.y);\n\n\t// Cube coordinates for the hex (q, r, -q-r)\n\tfloat x = q;\n\tfloat z = r;\n\tfloat y = -x - z;\n\n\t// Round to the nearest hex center\n\tvec3 rounded = round(vec3(x, y, z));\n\tvec3 diff = abs(vec3(x, y, z) - rounded);\n\n\t// Fix rounding errors\n\tif (diff.x > diff.y && diff.x > diff.z) {\n\t\trounded.x = -rounded.y - rounded.z;\n\t} else if (diff.y > diff.z) {\n\t\trounded.y = -rounded.x - rounded.z;\n\t} else {\n\t\trounded.z = -rounded.x - rounded.y;\n\t}\n\n\t// Find the hex center in UV space\n\tvec2 hex_center = vec2(\n\t    sqrt(3.0) * rounded.x + sqrt(3.0) / 2.0 * rounded.z,\n\t    3.0 / 2.0 * rounded.z\n    );\n\n\t// Relative position within the hex\n\tvec2 local_pos = guv - hex_center;\n\tvec2 lines_uv = local_pos;\n\tfloat line = 1.0;\n\n\tfor (int i = 0; i < 6; i++) {\n\t\tvec2 luv = lines_uv * rotate2d(radians(60.0 * float(i) + 30.0));\n\t\tfloat dist = abs(dot(luv + vec2(0.90), vec2(0.0, 1.0)));\n\t\tline = min(line, dist);\n\t}\n\n\t// Filter lines by slope\n\tfloat slope = 4.; // Can also assign to (auto_slope * 4.) to match grass placement\n\tfloat slope_factor = clamp(dot(vec3(0., 1., 0.), slope * (normal - 1.) + 1.), 0., 1.);\n\n\t// Draw hex grid\n\talbedo = mix(albedo, vec3(1.0), smoothstep(line_thickness + 0.02, line_thickness, line) * slope_factor);\n\t// Draw Hex center dot\n\talbedo = mix(albedo, vec3(0.0, 0.5, 0.5), smoothstep(0.11, 0.10, length(local_pos)) * slope_factor);\n}"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/hex_grid.gdshaderinc.uid",
    "content": "uid://mri8pfoj2mfk\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/lightweight.gdshader",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n// This shader is the minimum needed to allow the terrain to function, without any texturing.\n\nshader_type spatial;\nrender_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;\n\n/* This is an example stripped down shader with maximum performance in mind.\n * Only Autoshader/Base/Over/Blend/Holes/Colormap are supported.\n * Displacement is not enabled. Mesh Tesselation level must be set to 0.\n * All terrain normal calculations take place in vertex().\n *\n * Control map indices are processed such that each ID only requires reading ONCE.\n * The following features: projection, detiling, and paintable rotation / scale\n * cannot work with this method, without the additional samples required for blending\n * between same ID textures with different values across indices.\n */\n\n// Defined Constants\n#define COLOR_MAP vec4(1.0, 1.0, 1.0, 0.5)\n#define DIV_255 0.003921568627450 // 1. / 255.\n\n// Inline Functions\n#define DECODE_BLEND(control) float(control >>14u & 0xFFu) * DIV_255\n#define DECODE_AUTO(control) bool(control & 0x1u)\n#define DECODE_BASE(control) int(control >>27u & 0x1Fu)\n#define DECODE_OVER(control) int(control >>22u & 0x1Fu)\n#define DECODE_HOLE(control) bool(control >>2u & 0x1u)\n\n#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n    #define fma(a, b, c) ((a) * (b) + (c))\n    #define dFdxCoarse(a) dFdx(a)\n    #define dFdyCoarse(a) dFdy(a)\n#endif\n\n// Private uniforms\ngroup_uniforms shader_uniforms;\nuniform vec3 _target_pos = vec3(0.f);\nuniform float _mesh_size = 48.f;\nuniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2\nuniform uint _mouse_layer = 0x80000000u; // Layer 32\nuniform float _vertex_spacing = 1.0;\nuniform float _vertex_density = 1.0; // = 1./_vertex_spacing\nuniform float _subdiv = 1.0;\nuniform float _region_size = 1024.0;\nuniform float _region_texel_size = 0.0009765625; // = 1./region_size\nuniform int _region_map_size = 32;\nuniform int _region_map[1024];\nuniform vec2 _region_locations[1024];\nuniform float _texture_normal_depth_array[32];\nuniform float _texture_ao_strength_array[32];\nuniform float _texture_ao_affect_array[32];\nuniform float _texture_roughness_mod_array[32];\nuniform float _texture_uv_scale_array[32];\nuniform vec4 _texture_color_array[32];\nuniform highp sampler2DArray _height_maps : repeat_disable;\nuniform highp sampler2DArray _control_maps : repeat_disable;\nuniform highp sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;\nuniform highp sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;\nuniform highp sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;\ngroup_uniforms;\n\n// Public uniforms\ngroup_uniforms shader_uniforms.general;\nuniform float ground_level : hint_range(-1000., 1000.) = 0.0;\nuniform float region_blend : hint_range(.001, 1., 0.001) = 0.25;\nuniform bool flat_terrain_normals = false;\nuniform bool textures_enabled = true;\nuniform float blend_sharpness : hint_range(0, 1) = 0.5;\ngroup_uniforms;\n\ngroup_uniforms shader_uniforms.auto_shader;\nuniform float auto_slope : hint_range(0, 10) = 1.0;\nuniform float auto_height_reduction : hint_range(0, 1) = 0.1;\nuniform int auto_base_texture : hint_range(0, 31) = 0;\nuniform int auto_overlay_texture : hint_range(0, 31) = 1;\ngroup_uniforms;\n\ngroup_uniforms shader_uniforms.macro_variation;\nuniform bool macro_variation_enabled = true;\nuniform vec3 macro_variation1 : source_color = vec3(1.);\nuniform vec3 macro_variation2 : source_color = vec3(1.);\nuniform float macro_variation_slope : hint_range(0., 1.)  = 0.333;\nuniform highp sampler2D noise_texture : source_color, filter_linear_mipmap_anisotropic, repeat_enable;\nuniform float noise1_scale : hint_range(0.001, 1.) = 0.04; // Used for macro variation 1. Scaled up 10x\nuniform float noise1_angle : hint_range(0, 6.283) = 0.;\nuniform vec2 noise1_offset = vec2(0.5);\nuniform float noise2_scale : hint_range(0.001, 1.) = 0.076; // Used for macro variation 2. Scaled up 10x\ngroup_uniforms;\n\n// Varyings & Types\nvarying vec3 v_vertex;\nvarying float v_vertex_xz_dist;\nvarying vec3 v_normal;\nvarying vec3 v_camera_pos;\nvarying mat3 TBN;\n\n////////////////////////\n// Vertex\n////////////////////////\n\n// Takes in world space XZ (UV) coordinates\n// Returns ivec3 with:\n// XY: (0 to _region_size - 1) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nivec3 get_index_coord(const vec2 uv) {\n\tvec2 r_uv = round(uv);\n\tivec2 pos = ivec2(floor(r_uv * _region_texel_size)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[pos.y * _region_map_size + pos.x] * bounds - 1;\n\treturn ivec3(ivec2(mod(r_uv, _region_size)), layer_index);\n}\n\n// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:\n// XY: (0. to 1.) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nvec3 get_index_uv(const vec2 uv2) {\n\tivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;\n\treturn vec3(uv2 - _region_locations[layer_index], float(layer_index));\n}\n\n// Takes in world space XZ (UV) and returns if the coordinate should be part of the NONE background.\nbool is_none_bg(const vec2 uv) {\n\tivec4 regions = ivec4(\n\t\tget_index_coord(uv - vec2(0.5, 0.0)).z,\n\t\tget_index_coord(uv - vec2(0.0, 0.5)).z,\n\t\tget_index_coord(uv + vec2(1.0, 0.0)).z,\n\t\tget_index_coord(uv + vec2(0.0, 1.0)).z);\n\treturn any(equal(regions, ivec4(-1)));\n}\n\n// Takes in UV2 region space coordinates, returns 1.0 or 0.0 if a region is present or not.\nfloat check_region(const vec2 uv2) {\n\tivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);\n\tint layer_index = 0;\n\tif (uint(pos.x | pos.y) < uint(_region_map_size)) {\n\t\tlayer_index = clamp(_region_map[ pos.y * _region_map_size + pos.x ] - 1, -1, 0) + 1;\n\t}\n\treturn float(layer_index);\n}\n\n// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions\nfloat get_region_blend(vec2 uv2) {\n\tuv2 -= 0.5;\n\tconst vec2 offset = vec2(0.0, 1.0);\n\tfloat a = check_region(uv2 + offset.xy);\n\tfloat b = check_region(uv2 + offset.yy);\n\tfloat c = check_region(uv2 + offset.yx);\n\tfloat d = check_region(uv2 + offset.xx);\n\tvec2 w = smoothstep(vec2(0.0), vec2(1.0), fract(uv2));\n\tfloat blend = mix(mix(d, c, w.x), mix(a, b, w.x), w.y);\n    return 1.0 - blend;\n}\n\nfloat interpolated_height(vec2 pos) {\n\tconst vec2 offsets = vec2(0, 1);\n\tvec2 index_id = floor(pos);\n\tivec3 index[4];\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\tfloat h0 = texelFetch(_height_maps, index[0], 0).r;\n\tfloat h1 = texelFetch(_height_maps, index[1], 0).r;\n\tfloat h2 = texelFetch(_height_maps, index[2], 0).r;\n\tfloat h3 = texelFetch(_height_maps, index[3], 0).r;\n\tvec2 f = fract(pos);\n\tvec2 i = 1.0 - f;\n\tvec4 w = vec4(i.x * f.y, f.x * f.y, f.x * i.y, i.x * i.y);\n\tfloat h = h0 * w[0] + h1 * w[1] + h2 * w[2] + h3 * w[3];\n\treturn h;\n}\n\nvoid vertex() {\n\t// Save Camera Position to varying for access in later functions\n\tv_camera_pos = MAIN_CAM_INV_VIEW_MATRIX[3].xyz;\n\n\t// Get vertex of flat plane in world coordinates and set world UV\n\tv_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;\n\n\t// Distance from target node to vertex on a flat plane\n\tv_vertex_xz_dist = length(v_vertex.xz - _target_pos.xz);\n\n\t// Geomorph vertex across clipmap LODs, set end and start for linear height interpolate\n\tfloat scale = MODEL_MATRIX[0][0];\n\tfloat inv_scale = 1.0 / scale;\n\tfloat max_xz = max(abs(v_vertex.x - _target_pos.x), abs(v_vertex.z - _target_pos.z));\n\tfloat vertex_lerp = smoothstep(0.0, 1.0, (max_xz * inv_scale - _mesh_size - 4.0) / (_mesh_size - 4.0));\n\tvec2 vertex_fract = fract(VERTEX.xz * 0.5) * 2.0;\n\t// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+\n\tvec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not\n\t\t// Shift from regular to symmetric\n\t\tmix(vertex_fract, vec2(vertex_fract.x, -vertex_fract.y),\n\t\t\tround(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *\n\t\t\tround(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :\n\t\t// Symmetric shift\n\t\tvertex_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);\n\tvec2 start_pos = v_vertex.xz * _vertex_density;\n\tvec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;\n\tv_vertex.xz -= shift * scale * vertex_lerp;\n\n\t// UV coordinates in region space. 0-1 covers 1 region, 1-2 is the next region, etc.\n\tUV = v_vertex.xz * _vertex_density;\n\n\t// UV coordinates in region space + texel offset. Values are 0 to 1 within regions\n\tUV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));\n\n\t// Discard vertices for Holes. 1 lookup\n\tivec3 v_region = get_index_coord(start_pos);\n\tuint control = floatBitsToUint(texelFetch(_control_maps, v_region, 0)).r;\n\tbool hole = DECODE_HOLE(control);\n\n\t// Show holes to all cameras except mouse camera (on exactly 1 layer)\n\tif ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&\n\t\t\t(hole || (_background_mode == 0u && is_none_bg(UV)))) {\n\t\tv_vertex.x = 0. / 0.;\n\t} else {\n\t\t// Set final vertex height, and vertex normal.\n\t\tfloat h, u, v;\n\t\t// This branch is static for each of the clipmap segments\n\t\t// Interpolated reads only occur where sub-texel values are required.\n\t\tif (scale < _vertex_spacing) {\n\t\t\th = interpolated_height(UV);\n\t\t\tu = interpolated_height(UV + vec2(1, 0));\n\t\t\tv = interpolated_height(UV + vec2(0, 1));\n\t\t} else {\n\t\t\tivec3 coord_a = get_index_coord(start_pos);\n\t\t\tivec3 coord_b = get_index_coord(end_pos);\n\t\t\tivec3 coord_ua = get_index_coord(start_pos + vec2(1, 0));\n\t\t\tivec3 coord_ub = get_index_coord(end_pos + vec2(1, 0));\n\t\t\tivec3 coord_va = get_index_coord(start_pos + vec2(0, 1));\n\t\t\tivec3 coord_vb = get_index_coord(end_pos + vec2(0, 1));\n\t\t\th = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);\n\t\t\tu = mix(texelFetch(_height_maps, coord_ua, 0).r, texelFetch(_height_maps, coord_ub, 0).r, vertex_lerp);\n\t\t\tv = mix(texelFetch(_height_maps, coord_va, 0).r, texelFetch(_height_maps, coord_vb, 0).r, vertex_lerp);\n\t\t}\n\n\t\t// Apply background ground level and region blend\n\t\tvec2 ruv = UV * _region_texel_size;\n\t\th += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(ruv));\n\t\tu += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(ruv + vec2(_region_texel_size, 0.)));\n\t\tv += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(ruv + vec2(0., _region_texel_size)));\n\n\t\tv_vertex.y = h;\n\t\tv_normal = normalize(vec3(h - u, _vertex_spacing, h - v));\n\t}\n\n\t// Convert model space to view space w/ skip_vertex_transform render mode\n\tVERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;\n\n\t// Apply terrain normals\n\tvec3 w_tangent = normalize(cross(v_normal, vec3(0.0, 0.0, 1.0)));\n\tvec3 w_binormal = normalize(cross(v_normal, w_tangent));\n\tTBN = mat3(w_tangent, w_binormal, v_normal);\n\n\tNORMAL = normalize((VIEW_MATRIX * vec4(v_normal, 0.0)).xyz);\n\tBINORMAL = normalize((VIEW_MATRIX * vec4(w_binormal, 0.0)).xyz);\n\tTANGENT = normalize((VIEW_MATRIX * vec4(w_tangent, 0.0)).xyz);\n}\n\n////////////////////////\n// Fragment\n////////////////////////\n\nmat2 rotate_plane(float angle) {\n\tfloat c = cos(angle), s = sin(angle);\n\treturn mat2(vec2(c, s), vec2(-s, c));\n}\n\nvoid fragment() {\n\t// Recover UVs\n\tvec2 uv = UV;\n\tvec2 uv2 = UV2;\n\n\t// Lookup offsets, ID and blend weight\n\tvec3 region_uv = get_index_uv(uv2);\n\tconst vec3 offsets = vec3(0, 1, 2);\n\tvec2 index_id = floor(uv);\n\tvec2 weight = fract(uv);\n\tvec2 invert = 1.0 - weight;\n\tvec4 weights = vec4(\n\t\tinvert.x * weight.y, // 0\n\t\tweight.x * weight.y, // 1\n\t\tweight.x * invert.y, // 2\n\t\tinvert.x * invert.y  // 3\n\t);\n\n\tivec3 index[4];\n\t// control map lookups, used for some normal lookups as well\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\n\tvec3 base_ddx = dFdxCoarse(v_vertex);\n\tvec3 base_ddy = dFdyCoarse(v_vertex);\n\tvec4 base_dd = vec4(base_ddx.xz, base_ddy.xz);\n\t// Calculate the effective mipmap for regionspace\n\tfloat region_mip = log2(max(length(base_ddx.xz), length(base_ddy.xz)) * _vertex_density);\n\n\t// Color map\n\tvec4 color_map = region_uv.z > -1.0 ? textureLod(_color_maps, region_uv, region_mip) : COLOR_MAP;\n\n\tif (flat_terrain_normals) {\n\t\tNORMAL = normalize(cross(dFdyCoarse(VERTEX),dFdxCoarse(VERTEX)));\n\t\tTANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));\n\t\tBINORMAL = normalize(cross(NORMAL, TANGENT));\n\t}\n\n\t// defaults\n\tvec4 normal_rough = vec4(0., 1., 0., 0.7);\n\tvec4 albedo_height = vec4(1.);\n\tfloat normal_map_depth = 1.;\n\tfloat ao = 1.0;\n\tfloat ao_affect = 0.;\n\n\tif (textures_enabled) {\n\t\t// set to zero before accumulation\n\t\talbedo_height = vec4(0.);\n\t\tnormal_rough = vec4(0.);\n\t\tnormal_map_depth = 0.;\n\t\tao = 0.;\n\t\tfloat total_weight = 0.;\n\t\tfloat sharpness = fma(56., blend_sharpness, 8.);\n\n\t\t// Get index control data\n\t\t// 1 - 4 lookups\n\t\tuvec4 control = floatBitsToUint(vec4(\n\t\t\ttexelFetch(_control_maps, index[0], 0).r,\n\t\t\ttexelFetch(_control_maps, index[1], 0).r,\n\t\t\ttexelFetch(_control_maps, index[2], 0).r,\n\t\t\ttexelFetch(_control_maps, index[3], 0).r));\n\n\t\t{\n\t\t\t// Auto blend calculation\n\t\t\tfloat auto_blend = clamp(fma(auto_slope * 2.0, (v_normal.y - 1.0), 1.0)\n\t\t\t\t- auto_height_reduction * 0.01 * v_vertex.y, 0.0, 1.0);\n\t\t\t// Enable Autoshader if outside regions or painted in regions, otherwise manual painted\n\t\t\tuvec4 is_auto = (control & uvec4(0x1u)) |\n\t\t\t\tuvec4(lessThan(ivec4(index[0].z, index[1].z, index[2].z, index[3].z), ivec4(0)));\n\t\t\tuint u_auto =\n\t\t\t\t((uint(auto_base_texture) & 0x1Fu) << 27u) |\n\t\t\t\t((uint(auto_overlay_texture) & 0x1Fu) << 22u) |\n\t\t\t\t((uint(fma(auto_blend, 255.0 , 0.5)) & 0xFFu) << 14u);\n\t\t\tcontrol = control * (1u - is_auto) + u_auto * is_auto;\n\t\t}\n\n\n\t\t// Texture weights\n\t\t// Vectorised Deocode of all texture IDs, then swizzle to per index mapping.\n\t\tivec4 t_id[2] = {ivec4(control >> uvec4(27u) & uvec4(0x1Fu)),\n\t\t\tivec4(control >> uvec4(22u) & uvec4(0x1Fu))};\n\t\tivec2 texture_ids[4] = ivec2[4](\n\t\t\tivec2(t_id[0].x, t_id[1].x),\n\t\t\tivec2(t_id[0].y, t_id[1].y),\n\t\t\tivec2(t_id[0].z, t_id[1].z),\n\t\t\tivec2(t_id[0].w, t_id[1].w));\n\n\t\t// interpolated weights.\n\t\tvec4 weights_id_1 = vec4(control >> uvec4(14u) & uvec4(0xFFu)) * DIV_255 * weights;\n\t\tvec4 weights_id_0 = weights - weights_id_1;\n\t\tvec2 t_weights[4] = {vec2(0), vec2(0), vec2(0), vec2(0)};\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\t\tvec2 w_0 = vec2(weights_id_0[i]);\n\t\t\t\tvec2 w_1 = vec2(weights_id_1[i]);\n\t\t\t\tivec2 id_0 = texture_ids[i].xx;\n\t\t\t\tivec2 id_1 = texture_ids[i].yy;\n\t\t\t\tt_weights[0] += fma(w_0, vec2(equal(texture_ids[0], id_0)), w_1 * vec2(equal(texture_ids[0], id_1)));\n\t\t\t\tt_weights[1] += fma(w_0, vec2(equal(texture_ids[1], id_0)), w_1 * vec2(equal(texture_ids[1], id_1)));\n\t\t\t\tt_weights[2] += fma(w_0, vec2(equal(texture_ids[2], id_0)), w_1 * vec2(equal(texture_ids[2], id_1)));\n\t\t\t\tt_weights[3] += fma(w_0, vec2(equal(texture_ids[3], id_0)), w_1 * vec2(equal(texture_ids[3], id_1)));\n\t\t}\n\n\n\t\t// Process control data to determine each texture ID present, so that only\n\t\t// a single sample will be needed later, as all id are contiguous when features\n\t\t// like detiling, scale, rotation, and projection are not present.\n\t\t// 2 to 16 lookups\n\t\tuint id_read = 0u; // 1 bit per possible ID\n\t\t// world normal adjustment requires acess to previous id during next iteration\n\t\tvec4 nrm = vec4(0.0, 1.0, 0.0, 1.0);\n\n\t\tvec2 world_uv = v_vertex.xz;\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tfor (int t = 0; t < 2; t++) {\n\t\t\t\tint id = texture_ids[i][t];\n\t\t\t\tuint mask = 1u << uint(id);\n\t\t\t\tif ((id_read & mask) == 0u) {\n\t\t\t\t\t// Set this id bit\n\t\t\t\t\tid_read |= mask;\n\t\t\t\t\tfloat id_w = t_weights[i][t];\n\t\t\t\t\tfloat id_scale = _texture_uv_scale_array[id] * 0.5;\n\t\t\t\t\tvec2 id_uv = fma(world_uv, vec2(id_scale), vec2(0.5));\n\t\t\t\t\tvec4 i_dd = base_dd * id_scale;\n\t\t\t\t\tvec4 alb = textureGrad(_texture_array_albedo, vec3(id_uv, float(id)), i_dd.xy, i_dd.zw);\n\t\t\t\t\tfloat world_normal = clamp(fma(TBN[0], vec3(nrm.x), fma(TBN[1], vec3(nrm.z), v_normal * vec3(nrm.y))).y, 0., 1.);\n\t\t\t\t\tnrm = textureGrad(_texture_array_normal, vec3(id_uv, float(id)), i_dd.xy, i_dd.zw);\n\t\t\t\t\talb.rgb *= _texture_color_array[id].rgb;\n\t\t\t\t\tnrm.a = clamp(nrm.a + _texture_roughness_mod_array[id], 0., 1.);\n\t\t\t\t\t// Unpack normal map for blending.\n\t\t\t\t\tnrm.xyz = fma(nrm.xzy, vec3(2.0), vec3(-1.0));\n\t\t\t\t\tfloat id_ao = length(nrm.xyz) * 2.0 - 1.0;\n\t\t\t\t\tid_ao = mix(id_ao * id_ao * _texture_ao_strength_array[id] + 1.0 - _texture_ao_strength_array[id], 1.0, alb.a * alb.a);\n\t\t\t\t\t// height weight modifier.\n\t\t\t\t\tfloat id_weight = exp2(sharpness * log2(id_w + alb.a * world_normal));\n\t\t\t\t\talbedo_height += alb * id_weight;\n\t\t\t\t\tnormal_rough += nrm * id_weight;\n\t\t\t\t\tnormal_map_depth += _texture_normal_depth_array[id] * id_weight;\n\t\t\t\t\tao_affect += _texture_ao_affect_array[id] * id_weight;\n\t\t\t\t\tao += id_ao * id_weight;\n\t\t\t\t\ttotal_weight += id_weight;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// normalize accumulated values back to 0.0 - 1.0 range.\n\t\tfloat weight_inv = 1.0 / max(total_weight, 1e-8);\n\t\talbedo_height *= weight_inv;\n\t\tnormal_rough *= weight_inv;\n\t\tnormal_map_depth *= weight_inv;\n\t\tao_affect *= weight_inv;\n\t\tao *= weight_inv;\n\t}\n\n\t// Macro variation. 2 lookups\n\tvec3 macrov = vec3(1.);\n\tif (macro_variation_enabled) {\n\t\tfloat noise1 = texture(noise_texture, (uv * noise1_scale * .1 + noise1_offset) * rotate_plane(noise1_angle)).r;\n\t\tfloat noise2 = texture(noise_texture, uv * noise2_scale * .1).r;\n\t\tmacrov = mix(macro_variation1, vec3(1.), noise1);\n\t\tmacrov *= mix(macro_variation2, vec3(1.), noise2);\n\t\tmacrov = mix(vec3(1.0), macrov, clamp(v_normal.y + macro_variation_slope, 0., 1.));\n\t}\n\n\t// Wetness/roughness modifier, converting 0 - 1 range to -1 to 1 range, clamped to Godot roughness values\n\tfloat roughness = clamp(fma(color_map.a - 0.5, 2.0, normal_rough.a), 0., 1.);\n\n\t// Apply PBR\n\tALBEDO = albedo_height.rgb * color_map.rgb * macrov;\n\tROUGHNESS = roughness;\n\tSPECULAR = 1. - normal_rough.a;\n\t// Repack final normal map value.\n\tNORMAL_MAP = fma(normalize(normal_rough.xzy), vec3(0.5), vec3(0.5));\n\tNORMAL_MAP_DEPTH = normal_map_depth;\n\n\tAO = clamp(ao, 0., 1.);\n\tAO_LIGHT_AFFECT = ao_affect;\n\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/lightweight.gdshader.uid",
    "content": "uid://bbx2xhanpq5l3\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/minimum.gdshader",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n// This shader is the minimum needed to allow the terrain to function, without any texturing.\n\nshader_type spatial;\nrender_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;\n\n#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n    #define fma(a, b, c) ((a) * (b) + (c))\n    #define dFdxCoarse(a) dFdx(a)\n    #define dFdyCoarse(a) dFdy(a)\n#endif\n\n// Private uniforms\n// Commented uniforms aren't needed for this shader, but are available for your own needs.\ngroup_uniforms shader_uniforms;\nuniform vec3 _target_pos = vec3(0.f);\nuniform float _mesh_size = 48.f;\nuniform float _subdiv = 1.f;\nuniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2\nuniform uint _mouse_layer = 0x80000000u; // Layer 32\nuniform float _vertex_spacing = 1.0;\nuniform float _vertex_density = 1.0; // = 1./_vertex_spacing\nuniform float _region_size = 1024.0;\nuniform float _region_texel_size = 0.0009765625; // = 1./region_size\nuniform int _region_map_size = 32;\nuniform int _region_map[1024];\n//uniform vec2 _region_locations[1024];\n//uniform float _texture_normal_depth_array[32];\n//uniform float _texture_ao_strength_array[32];\n//uniform float _texture_ao_affect_array[32];\n//uniform float _texture_roughness_mod_array[32];\n//uniform float _texture_uv_scale_array[32];\n//uniform vec2 _texture_detile_array[32];\n//uniform vec4 _texture_color_array[32];\nuniform highp sampler2DArray _height_maps : repeat_disable;\nuniform highp sampler2DArray _control_maps : repeat_disable;\n//uniform highp sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;\n//uniform highp sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;\n//uniform highp sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;\ngroup_uniforms;\n\n// Public uniforms\ngroup_uniforms shader_uniforms.general;\nuniform bool flat_terrain_normals = false;\ngroup_uniforms;\n\n// Varyings & Types\n// Some are required for editor functions\nvarying vec3 v_vertex;\nvarying float v_vertex_xz_dist;\nvarying vec3 v_camera_pos;\n\n////////////////////////\n// Vertex\n////////////////////////\n\n// Takes in world space XZ (UV) coordinates\n// Returns ivec3 with:\n// XY: (0 to _region_size - 1) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nivec3 get_index_coord(const vec2 uv) {\n\tvec2 r_uv = round(uv);\n\tivec2 pos = ivec2(floor(r_uv * _region_texel_size)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[pos.y * _region_map_size + pos.x] * bounds - 1;\n\treturn ivec3(ivec2(mod(r_uv, _region_size)), layer_index);\n}\n\n// Takes in world space XZ (UV) and returns if the coordinate should be part of the NONE background.\nbool is_none_bg(const vec2 uv) {\n\tivec4 regions = ivec4(\n\t\tget_index_coord(uv - vec2(0.5, 0.0)).z,\n\t\tget_index_coord(uv - vec2(0.0, 0.5)).z,\n\t\tget_index_coord(uv + vec2(1.0, 0.0)).z,\n\t\tget_index_coord(uv + vec2(0.0, 1.0)).z);\n\treturn any(equal(regions, ivec4(-1)));\n}\n\nfloat interpolated_height(vec2 pos) {\n\tconst vec2 offsets = vec2(0, 1);\n\tvec2 index_id = floor(pos);\n\tivec3 index[4];\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\tfloat h0 = texelFetch(_height_maps, index[0], 0).r;\n\tfloat h1 = texelFetch(_height_maps, index[1], 0).r;\n\tfloat h2 = texelFetch(_height_maps, index[2], 0).r;\n\tfloat h3 = texelFetch(_height_maps, index[3], 0).r;\n\tvec2 f = fract(pos);\n\tvec2 i = 1.0 - f;\n\tvec4 w = vec4(i.x * f.y, f.x * f.y, f.x * i.y, i.x * i.y);\n\tfloat h = h0 * w[0] + h1 * w[1] + h2 * w[2] + h3 * w[3];\n\treturn h;\n}\n\nvoid vertex() {\n\t// Save Camera Position to varying for access in later functions\n\tv_camera_pos = MAIN_CAM_INV_VIEW_MATRIX[3].xyz;\n\n\t// Get vertex of flat plane in world coordinates and set world UV\n\tv_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;\n\n\t// Distance from target node to vertex on a flat plane\n\tv_vertex_xz_dist = length(v_vertex.xz - _target_pos.xz);\n\n\t// Geomorph vertex across clipmap LODs, set end and start for linear height interpolate\n\tfloat scale = MODEL_MATRIX[0][0];\n\tfloat inv_scale = 1.0 / scale;\n\tfloat max_xz = max(abs(v_vertex.x - _target_pos.x), abs(v_vertex.z - _target_pos.z));\n\tfloat vertex_lerp = smoothstep(0.0, 1.0, (max_xz * inv_scale - _mesh_size - 4.0) / (_mesh_size - 4.0));\n\tvec2 vertex_fract = fract(VERTEX.xz * 0.5) * 2.0;\n\t// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+\n\tvec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not\n\t\t// Shift from regular to symmetric\n\t\tmix(vertex_fract, vec2(vertex_fract.x, -vertex_fract.y),\n\t\t\tround(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *\n\t\t\tround(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :\n\t\t// Symmetric shift\n\t\tvertex_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);\n\tvec2 start_pos = v_vertex.xz * _vertex_density;\n\tvec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;\n\tv_vertex.xz -= shift * scale * vertex_lerp;\n\n\t// UV coordinates in region space. 0-1 covers 1 region, 1-2 is the next region, etc.\n\tUV = v_vertex.xz * _vertex_density;\n\n\t// UV coordinates in region space + texel offset. Values are 0 to 1 within regions\n\tUV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));\n\n\t// Discard vertices for Holes. 1 lookup\n\tivec3 v_region = get_index_coord(start_pos);\n\tuint control = floatBitsToUint(texelFetch(_control_maps, v_region, 0)).r;\n\tbool hole = bool(control >>2u & 0x1u);\n\n\t// Show holes to all cameras except mouse camera (on exactly 1 layer)\n\tif ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&\n\t\t\t(hole || (_background_mode == 0u && is_none_bg(UV)))) {\n\t\tv_vertex.x = 0. / 0.;\n\t} else {\n\t\t// Set final vertex height.\n\t\tfloat h;\n\t\t// This branch is static for each of the clipmap segments\n\t\t// Interpolated reads only occur where sub-texel values are required.\n\t\tif (scale < _vertex_spacing) {\n\t\t\th = interpolated_height(UV);\n\t\t} else {\n\t\t\tivec3 coord_a = get_index_coord(start_pos);\n\t\t\tivec3 coord_b = get_index_coord(end_pos);\n\t\t\th = mix(texelFetch(_height_maps, coord_a, 0).r,texelFetch(_height_maps, coord_b, 0).r,vertex_lerp);\n\t\t}\n\t\tv_vertex.y = h;\n\t}\n\n\t// Convert model space to view space w/ skip_vertex_transform render mode\n\tVERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;\n\tNORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);\n\tBINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);\n\tTANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);\n}\n\n////////////////////////\n// Fragment\n////////////////////////\n\nvoid fragment() {\n\t// Recover UVs\n\tvec2 uv = UV;\n\t//vec2 uv2 = UV2;\n\n\t// Lookup offsets, ID and blend weight\n\tconst vec3 offsets = vec3(0, 1, 2);\n\tvec2 index_id = floor(uv);\n\tvec2 weight = fract(uv);\n\tvec2 invert = 1.0 - weight;\n\tvec4 weights = vec4(\n\t\tinvert.x * weight.y, // 0\n\t\tweight.x * weight.y, // 1\n\t\tweight.x * invert.y, // 2\n\t\tinvert.x * invert.y  // 3\n\t);\n\n\tvec3 base_ddx = dFdxCoarse(v_vertex);\n\tvec3 base_ddy = dFdyCoarse(v_vertex);\n\t//vec4 base_derivatives = vec4(base_ddx.xz, base_ddy.xz);\n\t// Calculate the effective mipmap for regionspace, and if less than 0,\n\t// skip all extra lookups required for bilinear blend.\n\tfloat region_mip = log2(max(length(base_ddx.xz), length(base_ddy.xz)) * _vertex_density);\n\tbool bilerp = region_mip < 4.0;\n\n\tivec3 index[4];\n\t// control map lookups, used for some normal lookups as well\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\n\t// Terrain normals\n\tvec3 index_normal[4];\n\tfloat h[4];\n\t// allows additional derivatives, eg world noise, brush previews etc\n\tfloat u = 0.0;\n\tfloat v = 0.0;\n\n\t// Re-use index[] for the first lookups, skipping some math. 3 lookups\n\th[3] = texelFetch(_height_maps, index[3], 0).r; // 0 (0,0)\n\th[2] = texelFetch(_height_maps, index[2], 0).r; // 1 (1,0)\n\th[0] = texelFetch(_height_maps, index[0], 0).r; // 2 (0,1)\n\tindex_normal[3] = normalize(vec3(h[3] - h[2] + u, _vertex_spacing, h[3] - h[0] + v));\n\n\t// Set flat world normal - overriden if bilerp is true\n\tvec3 w_normal = index_normal[3];\n\n\t// Branching smooth normals must be done seperatley for correct normals at all 4 index ids\n\tif (bilerp) {\n\t\t// 5 lookups\n\t\t// Fetch the additional required height values for smooth normals\n\t\th[1] = texelFetch(_height_maps, index[1], 0).r; // 3 (1,1)\n\t\tfloat h_4 = texelFetch(_height_maps, get_index_coord(index_id + offsets.yz), 0).r; // 4 (1,2)\n\t\tfloat h_5 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zy), 0).r; // 5 (2,1)\n\t\tfloat h_6 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zx), 0).r; // 6 (2,0)\n\t\tfloat h_7 = texelFetch(_height_maps, get_index_coord(index_id + offsets.xz), 0).r; // 7 (0,2)\n\n\t\t// Calculate the normal for the remaining index ids.\n\t\tindex_normal[0] = normalize(vec3(h[0] - h[1] + u, _vertex_spacing, h[0] - h_7 + v));\n\t\tindex_normal[1] = normalize(vec3(h[1] - h_5 + u, _vertex_spacing, h[1] - h_4 + v));\n\t\tindex_normal[2] = normalize(vec3(h[2] - h_6 + u, _vertex_spacing, h[2] - h[1] + v));\n\n\t\t// Set interpolated world normal\n\t\tw_normal =\n\t\t\tindex_normal[0] * weights[0] +\n\t\t\tindex_normal[1] * weights[1] +\n\t\t\tindex_normal[2] * weights[2] +\n\t\t\tindex_normal[3] * weights[3] ;\n\t}\n\n\t// Apply terrain normals\n\tvec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));\n\tvec3 w_binormal = normalize(cross(w_normal, w_tangent));\n\tNORMAL = mat3(VIEW_MATRIX) * w_normal;\n\tTANGENT = mat3(VIEW_MATRIX) * w_tangent;\n\tBINORMAL = mat3(VIEW_MATRIX) * w_binormal;\n\n\tif (flat_terrain_normals) {\n\t\tNORMAL = normalize(cross(dFdyCoarse(VERTEX),dFdxCoarse(VERTEX)));\n\t\tTANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));\n\t\tBINORMAL = normalize(cross(NORMAL, TANGENT));\n\t}\n\n\t// Apply PBR\n\tALBEDO = vec3(.2);\n\tROUGHNESS = .7;\n}\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/minimum.gdshader.uid",
    "content": "uid://01qauauvd8aa\n"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/ocean_shader.gdshader",
    "content": "﻿// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n// This file is an example ocean shader. You should be able to use any ocean shader with slight modification\n// for the clipmap. See the note above vertex().\n\nshader_type spatial;\nrender_mode cull_back, depth_draw_always, diffuse_burley, specular_schlick_ggx, shadows_disabled, skip_vertex_transform;\n\n//////////////////////////////\n// Constants\n//////////////////////////////\nconst int ITERATIONS_VERTEX = 1;\nconst int ITERATIONS_FRAGMENT = 3;\n\n//////////////////////////////\n// Uniforms\n//////////////////////////////\ngroup_uniforms water;\nuniform float sea_level : hint_range(-50.0, 50.0, 0.001) = 1.0;\nuniform vec3 water_color : source_color = vec3(.1058, .1568, .2117); // This is a common deep blue\n//uniform vec3 water_color : source_color = vec3(0.102, 0.212, 0.235); // This is a more green ocean\nuniform float visible_depth : hint_range(0.001, 10000., .1) = 256.;\nuniform float depth_curve : hint_range(0.001, 5.0) = .1;\nuniform float shore_blend : hint_range(0.0, 10.0, 0.001) = 1.;\nuniform float refraction_strength : hint_range(0.0, .2, 0.001) = 0.02;\ngroup_uniforms;\n\ngroup_uniforms waves;\nuniform float time_scale : hint_range(0.0, 64.0) = 8.0;\nuniform float height_scale : hint_range(0.0, 16.0) = 3.0;\nuniform float ocean_scale : hint_range(0.1, 16.0) = 2.0;\ngroup_uniforms;\n\ngroup_uniforms reflections;\nuniform float fresnel_scale : hint_range(0., 1.) = .5;\nuniform float fresnel_power : hint_range(0., 10.) = 1.;\nuniform float specular : hint_range(0., 10.) = 1.;\ngroup_uniforms;\n\ngroup_uniforms foam;\nuniform bool foam_enabled = true;\nuniform float foam_height : hint_range(0., 5.0) = 2.5;\nuniform float foam_top_intensity : hint_range(.1, 100.0) = 2.0;\nuniform float foam_distance : hint_range(0.1, 16384.0) = 2048.;\nuniform bool foam_shore_enabled = true;\nuniform float foam_shore_intensity : hint_range(0.1, 100.0) = 2.;\nuniform sampler2D foam_noise_texture;\nuniform float foam_noise_scale : hint_range(0.1, 5.0) = 1.5;\ngroup_uniforms;\n\ngroup_uniforms light_scattering;\nuniform bool light_scattering_enabled = true;\nuniform float height_scattering : hint_range(0.0, 15.0) = 3.0;\nuniform float height_threshold : hint_range(0.0, 1.0) = 0.1;\nuniform float height_angle_threshold : hint_range(0.0, 1.0) = 0.21;\nuniform float fersnel_scattering : hint_range(0.0, 70.0) = 20.0;\nuniform float direct_scattering : hint_range(0.0, 70.0) = 10.0;\ngroup_uniforms;\n\n// Uniforms set by C++\ngroup_uniforms private_set_by_terrain;\nuniform float _mesh_size = 32.;\nuniform float _subdiv = 1.;\nuniform float _vertex_spacing = 1.;\nuniform float _vertex_density = 1.;\nuniform vec3 _target_pos = vec3(0.,0.,0.);\nuniform vec3 _light_direction = vec3(-0.196, 0.980, 0);\nuniform vec3 _light_color : source_color = vec3(1.0, 1.0, .735);\ngroup_uniforms;\n\nuniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;\nuniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;\n\nvarying vec3 v_vertex;\nvarying float v_vertex_distance;\n\n//////////////////////////////\n// Ocean Noise\n//////////////////////////////\n\nfloat hash(vec2 p) {\n\tuvec2 q = uvec2(ivec2(p)) * uvec2(1597334673u, 3812015801u);\n    uint n = (q.x ^ q.y) * 1597334673u;\n    return float(n) * (1.0 / float(0xffffffffu));\n}\n\nfloat noise(vec2 p) {\n    vec2 i = floor(p);\n    vec2 f = fract(p);\n    vec2 u = smoothstep(0., 1., f);\n    vec2 i1 = i + 1.0;\n\n    float a = hash(i);\n    float b = hash(vec2(i1.x, i.y));\n    float c = hash(vec2(i.x, i1.y));\n    float d = hash(i1);\n\n    return 2.0 * mix(mix(a, b, u.x), mix(c, d, u.x), u.y) - 1.0;\n}\n\nfloat octave(vec2 uv) {\n\tuv += noise((uv) * 0.5) * 2.;\n\tvec2 wave = 1.0 - abs(sin(uv));\n\t// Shaping\n\tfloat v = wave.x * wave.y + 1e-6;\n\tfloat s = v * inversesqrt(v);\n\treturn 1.0 - 2.0 * s + v;\n}\n\nfloat ocean_height(vec2 pos, int iterations) {\n\tfloat freq = 0.03 * ocean_scale;\n\tfloat amp = 4.0;\n\tvec2 uv = pos;\n\tfloat h = 0.0;\n\tfloat t = TIME * time_scale;\n\n\tfor (int i = 0; i < iterations; i++) {\n\t    h += (octave((uv + t) * freq) + octave((uv - t) * freq)) * amp;\n\t    uv *= mat2(vec2(1.8, 0.8), vec2(-0.8, 1.8));\n\t    freq *= 1.9;\n\t    amp *= 0.2;\n\t}\n    return h * 0.1 * height_scale;\n}\n\n//////////////////////////////\n// Vertex\n//\n// vertex() positions the vertices of the clipmap mesh across the geomorphed lods. You'll need the\n// first two blocks for any other water shader, from the start to setting v_vertex.xz. After that\n// your own shader can set the UV and v_vertex.Y however you like.\n//////////////////////////////\n\nvoid vertex() {\n\t// Get vertex of flat plane in world coordinates and set world UV\n    v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;\n\n\t// Geomorph vertex across clipmap LODs\n\tfloat scale = MODEL_MATRIX[0][0];\n\tfloat inv_scale = 1.0 / scale;\n\tfloat max_xz = max(abs(v_vertex.x - _target_pos.x), abs(v_vertex.z - _target_pos.z));\n\tfloat vertex_lerp = smoothstep(0.0, 1.0, (max_xz * inv_scale - _mesh_size - 4.0) / (_mesh_size - 4.0));\n\tvec2 vertex_fract = fract(VERTEX.xz * 0.5) * 2.0;\n\t// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+\n\tvec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not\n\t\t// Shift from regular to symmetric\n\t\tmix(vertex_fract, vec2(vertex_fract.x, -vertex_fract.y),\n\t\t\tround(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *\n\t\t\tround(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :\n\t\t// Symmetric shift\n\t\tvertex_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);\n    v_vertex.xz -= shift * scale * vertex_lerp;\n\n\t// UV coordinates in region space. 0-1 covers 1 region, 1-2 is the next region, etc.\n    UV = v_vertex.xz;\n\n\t// Set height according to waves\n    v_vertex.y = sea_level + ocean_height(UV, ITERATIONS_VERTEX);\n    VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;\n\n\tv_vertex_distance = length(v_vertex - _target_pos);\n}\n\n//////////////////////////////\n// Fragment\n//////////////////////////////\n\nvoid fragment() {\n\t// Normals\n\tfloat wave_height, u, v;\n\t// Adjust normal domain as screen space distance increases to reduce high frequency values across pixels\n\tfloat dv = max(0.25, length(fwidth(VERTEX)));\n\tvec2  n_uv = UV + dv * -0.66;\n\twave_height = ocean_height(n_uv, ITERATIONS_FRAGMENT);\n\tu = ocean_height(n_uv + vec2(dv, 0.0), ITERATIONS_FRAGMENT);\n\tv = ocean_height(n_uv + vec2(0.0, dv), ITERATIONS_FRAGMENT);\n\tconst float wave_distance = 512.;\n\tvec3 world_normal = normalize(vec3(wave_height - u, max(.1, v_vertex_distance/wave_distance), wave_height - v));\n\tNORMAL = mat3(VIEW_MATRIX) * world_normal;\n\tTANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));\n\tBINORMAL = normalize(cross(NORMAL, TANGENT));\n\n\t// Horizon mask: 1.0 when light is above horizon w/ fade at edge\n\tfloat daytime_mask = smoothstep(0., .1, _light_direction.y);\n\n\t// Reflections\n\tfloat cos_theta = max(0.0, dot(NORMAL, VIEW)); // 1.0 when perpendicular (looking straight down), ~0.0 grazing\n\tfloat fresnel_base = pow(1.0 - cos_theta, fresnel_power); // high at grazing, low straight down\n\tfloat fresnel = clamp(fresnel_scale * fresnel_base, 0.0, 1.0);\n\tROUGHNESS = mix(0.02, 0.8, fresnel) * daytime_mask;\n\tSPECULAR = specular * fresnel;\n\tSPECULAR = clamp (SPECULAR - .5 *  (1. - daytime_mask), 0.15, 1.); // Mask far distance when sun is below horizon\n\n\t// Terrain pixel position\n\tfloat depth_raw = textureLod(depth_texture, SCREEN_UV, 0.0).r;\n\tvec4 clip_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth_raw, 1.0);\n\tvec3 view_pos = clip_pos.xyz / clip_pos.w;\n\tvec4 world_pos_hom = INV_VIEW_MATRIX * vec4(view_pos, 1.0);\n\tvec3 terrain_position = world_pos_hom.xyz / world_pos_hom.w;\n\n\t// Shore mask: 1.0 near the shore line\n\tfloat shore_mask = 1.0 - clamp(1.0 - smoothstep(view_pos.z + 2. * shore_blend, view_pos.z, VERTEX.z), 0.0, 1.0);\n\tALPHA = 1.0 - shore_mask;\n\n\t// Albedo accumulation: starting with water and sunlight colors\n\tvec3 light_mult = light_scattering_enabled ? _light_color : vec3(1.0);\n\tALBEDO = water_color * light_mult;\n\n\t// Refraction offset\n\tvec2 refract_offset = NORMAL.xz * refraction_strength;\n\tfloat vp_y_mask = 1.0 - smoothstep(0.0, length(refract_offset) * 2.0, 1.0 - SCREEN_UV.y);\n\trefract_offset = mix(refract_offset, vec2(0.0), max(shore_mask, vp_y_mask));\n\tvec2 refract_uv = SCREEN_UV + refract_offset;\n\trefract_uv = clamp(refract_uv, vec2(0.001, 0.001), vec2(0.999, 0.999));\n\n\t// Sample refracted terrain depth & pixel position\n\tfloat refract_depth_raw = textureLod(depth_texture, refract_uv, 0.0).r;\n\tvec4 refract_clip_pos = INV_PROJECTION_MATRIX * vec4(refract_uv * 2.0 - 1.0, refract_depth_raw, 1.0);\n\tvec3 refract_view_pos = refract_clip_pos.xyz / refract_clip_pos.w;\n\tvec4 refract_world_hom = INV_VIEW_MATRIX * vec4(refract_view_pos, 1.0);\n\tvec3 refract_world_pos = refract_world_hom.xyz / refract_world_hom.w;\n\n\t// Filter out pixels above the water surface (ie player's head), or beyond the visible depth\n\tbool pixel_above_water = refract_world_pos.y >= v_vertex.y;\n\tbool in_refraction_depth = VERTEX.z < refract_view_pos.z + 2. * visible_depth;\n\tbool use_refracted = !pixel_above_water && in_refraction_depth;\n\tvec2 final_refract_uv = use_refracted ? refract_uv : SCREEN_UV;\n\n\t// Sample undersea meshes\n\tvec3 background_color = texture(screen_texture, final_refract_uv).rgb;\n\n\t// Depth fade based on vertical depth, with curve\n\t// The minimum amount helps to avoid ghosting around the player under the water\n\tfloat water_depth = max(20., sea_level + v_vertex.y - terrain_position.y);\n\tfloat normalized_depth = water_depth / visible_depth;\n\tfloat depth_fade = pow(clamp(normalized_depth, 0.0, 1.0), depth_curve);\n\tALBEDO = mix(background_color, ALBEDO, depth_fade);\n\n\t// Foam\n\tfloat foam_mask = 0.0;\n\tif (foam_enabled) {\n\t\tfloat detail_wave = wave_height * 0.005 ;\n\t\tfloat foam_noise = texture(foam_noise_texture, UV * .1 * foam_noise_scale).r;\n\t\tfoam_mask = clamp(wave_height * 0.5 + (detail_wave * foam_top_intensity + float(foam_shore_enabled) * shore_mask * foam_shore_intensity) * foam_noise * 1.5 - foam_height, 0.0, 1.0);\n\t\tfloat dist_fade = 1. - smoothstep(0., 1., v_vertex_distance / foam_distance);\n\t\tfoam_mask *= dist_fade;\n\t\tALBEDO = mix(ALBEDO, vec3(0.8), foam_mask);\n\t}\n\n\t// Light scattering\n\tif (light_scattering_enabled) {\n\t\tvec3 ray_dir = normalize(mat3(INV_VIEW_MATRIX) * VIEW);\n\t\t// Light scattering based on height and sun angle\n\t\tvec3 light_factor = height_scattering * max(wave_height, height_threshold)\n\t\t\t* max(pow(dot(_light_direction, -ray_dir), 4.0), height_angle_threshold)\n\t\t\t* _light_color\n\t\t\t* pow(0.5 - 0.5 * dot(_light_direction, world_normal), 3.0);\n\t\t// Light scattering based on view angle\n\t\tlight_factor += fersnel_scattering * pow(dot(ray_dir, world_normal), 2.0) * _light_color * water_color;\n\t\t// Direct light scattering\n\t\tlight_factor += direct_scattering * dot(_light_direction, world_normal) * water_color * _light_color;\n\t\t// Apply horizon fade to the whole scattering\n    \tBACKLIGHT = light_factor * 4.0 * clamp(depth_fade, 0.4, 1.0) * max(1.0 - foam_mask, 0.5) * daytime_mask;\n\t}\n\n}"
  },
  {
    "path": "project/addons/terrain_3d/extras/shaders/ocean_shader.gdshader.uid",
    "content": "uid://biawd3fhk5weg\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/autoshader.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bdwolwswwy8wr\"\npath=\"res://.godot/imported/autoshader.svg-9998e61bbc6afd5b134b767acd17a425.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/autoshader.svg\"\ndest_files=[\"res://.godot/imported/autoshader.svg-9998e61bbc6afd5b134b767acd17a425.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/color_paint.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://krrmpalen8xu\"\npath=\"res://.godot/imported/color_paint.svg-2a416ebf35da04135017e5c6ef53ea57.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/color_paint.svg\"\ndest_files=[\"res://.godot/imported/color_paint.svg-2a416ebf35da04135017e5c6ef53ea57.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_add.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bcmbqryggekg1\"\npath=\"res://.godot/imported/height_add.svg-9e680ce71fa4c541748e081b99167369.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_add.svg\"\ndest_files=[\"res://.godot/imported/height_add.svg-9e680ce71fa4c541748e081b99167369.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_div.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://danh7tb2v6rx7\"\npath=\"res://.godot/imported/height_div.svg-449a465f9fdd11ab59f2f1c78815408c.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_div.svg\"\ndest_files=[\"res://.godot/imported/height_div.svg-449a465f9fdd11ab59f2f1c78815408c.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_flat.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://crj0xfyiyr45u\"\npath=\"res://.godot/imported/height_flat.svg-be726a006bf06e05a7a8867510f3996e.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_flat.svg\"\ndest_files=[\"res://.godot/imported/height_flat.svg-be726a006bf06e05a7a8867510f3996e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_mul.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bu3q0645kb3el\"\npath=\"res://.godot/imported/height_mul.svg-2dca20fa42a85408713e9bfe411f3c79.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_mul.svg\"\ndest_files=[\"res://.godot/imported/height_mul.svg-2dca20fa42a85408713e9bfe411f3c79.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_slope.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://0cd7so4kw7da\"\npath=\"res://.godot/imported/height_slope.svg-e20540c5538d0c57a9d229a772b3d1b3.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_slope.svg\"\ndest_files=[\"res://.godot/imported/height_slope.svg-e20540c5538d0c57a9d229a772b3d1b3.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_smooth.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://chrbx4xnxyiel\"\npath=\"res://.godot/imported/height_smooth.svg-d8fc43572f5984eef64c886a49988c06.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_smooth.svg\"\ndest_files=[\"res://.godot/imported/height_smooth.svg-d8fc43572f5984eef64c886a49988c06.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/height_sub.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://mo3hnbk3ffjs\"\npath=\"res://.godot/imported/height_sub.svg-1a14a9bb856f3db0faa02dba3c807b50.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/height_sub.svg\"\ndest_files=[\"res://.godot/imported/height_sub.svg-1a14a9bb856f3db0faa02dba3c807b50.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/holes.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bsmaxekrmnuy2\"\npath=\"res://.godot/imported/holes.svg-a7cb97bb50d7879cd274646e207b9213.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/holes.svg\"\ndest_files=[\"res://.godot/imported/holes.svg-a7cb97bb50d7879cd274646e207b9213.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/layers.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cs1la1mashf2e\"\npath=\"res://.godot/imported/layers.svg-4a679bb626c5179d3773f33e77e4a5e4.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/layers.svg\"\ndest_files=[\"res://.godot/imported/layers.svg-4a679bb626c5179d3773f33e77e4a5e4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/multimesh.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cjlcl5lf20ve0\"\npath=\"res://.godot/imported/multimesh.svg-5487b93b04ddbaae37b5d3e91f10750b.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/multimesh.svg\"\ndest_files=[\"res://.godot/imported/multimesh.svg-5487b93b04ddbaae37b5d3e91f10750b.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/navigation.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://f3po5pogkv2b\"\npath=\"res://.godot/imported/navigation.svg-1e4cf210c589be8d2911c522d4a17d78.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/navigation.svg\"\ndest_files=[\"res://.godot/imported/navigation.svg-1e4cf210c589be8d2911c522d4a17d78.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/picker_checked.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bg8x6o32ggt88\"\npath=\"res://.godot/imported/picker_checked.svg-81f35b6ae38bccc8aa9e7ae22b530168.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/picker_checked.svg\"\ndest_files=[\"res://.godot/imported/picker_checked.svg-81f35b6ae38bccc8aa9e7ae22b530168.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/region_add.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c0tn453fsckv5\"\npath=\"res://.godot/imported/region_add.svg-a05dc161a452dd3e024f9835a737d9f0.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/region_add.svg\"\ndest_files=[\"res://.godot/imported/region_add.svg-a05dc161a452dd3e024f9835a737d9f0.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/region_remove.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cbpo5eamf3bx2\"\npath=\"res://.godot/imported/region_remove.svg-5710e8aeb34f1eaa06e637634f4a7d16.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/region_remove.svg\"\ndest_files=[\"res://.godot/imported/region_remove.svg-5710e8aeb34f1eaa06e637634f4a7d16.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/terrain3d.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bnsydn4jkyeyn\"\npath=\"res://.godot/imported/terrain3d.svg-eb45756f1a003759fda81eaa1db10769.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/terrain3d.svg\"\ndest_files=[\"res://.godot/imported/terrain3d.svg-eb45756f1a003759fda81eaa1db10769.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/texture_paint.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://duo8valena3a2\"\npath=\"res://.godot/imported/texture_paint.svg-72da4fd2096377e625a8fe09cdacb0e4.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/texture_paint.svg\"\ndest_files=[\"res://.godot/imported/texture_paint.svg-72da4fd2096377e625a8fe09cdacb0e4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/texture_spray.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://16yfxe7xe703\"\npath=\"res://.godot/imported/texture_spray.svg-326fee11cf418653e621bc222a470861.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/texture_spray.svg\"\ndest_files=[\"res://.godot/imported/texture_spray.svg-326fee11cf418653e621bc222a470861.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/icons/wetness.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bcgg0srmqsh3n\"\npath=\"res://.godot/imported/wetness.svg-9b2ddec096ab7734492b77b20c75c82b.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/terrain_3d/icons/wetness.svg\"\ndest_files=[\"res://.godot/imported/wetness.svg-9b2ddec096ab7734492b77b20c75c82b.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/bake_lod_dialog.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Bake LOD Dialog for Terrain3D\n@tool\nextends ConfirmationDialog\n\nvar lod: int = 0\nvar description: String = \"\"\n\n\nfunc _ready() -> void:\n\tset_unparent_when_invisible(true)\n\tabout_to_popup.connect(_on_about_to_popup)\n\tvisibility_changed.connect(_on_visibility_changed)\n\t%LodBox.value_changed.connect(_on_lod_box_value_changed)\n\n\nfunc _on_about_to_popup() -> void:\n\tlod = %LodBox.value\n\n\nfunc _on_visibility_changed() -> void:\n\t# Change text on the autowrap label only when the popup is visible.\n\t# Works around Godot issue #47005:\n\t# https://github.com/godotengine/godot/issues/47005\n\tif visible:\n\t\t%DescriptionLabel.text = description\n\n\nfunc _on_lod_box_value_changed(p_value: float) -> void:\n\tlod = %LodBox.value\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/bake_lod_dialog.gd.uid",
    "content": "uid://cqmt8f5x5c2ad\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/bake_lod_dialog.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://bhvrrmb8bk1bt\"]\n\n[ext_resource type=\"Script\" uid=\"uid://cqmt8f5x5c2ad\" path=\"res://addons/terrain_3d/menu/bake_lod_dialog.gd\" id=\"1_sf76d\"]\n\n[node name=\"bake_lod_dialog\" type=\"ConfirmationDialog\"]\ntitle = \"Bake Terrain3D Mesh\"\nposition = Vector2i(0, 36)\nsize = Vector2i(400, 155)\nvisible = true\nscript = ExtResource(\"1_sf76d\")\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\".\"]\noffset_left = 8.0\noffset_top = 8.0\noffset_right = 392.0\noffset_bottom = 106.0\ntheme_override_constants/margin_left = 10\ntheme_override_constants/margin_top = 10\ntheme_override_constants/margin_right = 10\ntheme_override_constants/margin_bottom = 10\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"MarginContainer/VBoxContainer\"]\nlayout_mode = 2\ntheme_override_constants/separation = 20\n\n[node name=\"Label\" type=\"Label\" parent=\"MarginContainer/VBoxContainer/HBoxContainer\"]\nlayout_mode = 2\ntext = \"LOD:\"\n\n[node name=\"LodBox\" type=\"SpinBox\" parent=\"MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nmax_value = 8.0\nvalue = 4.0\n\n[node name=\"DescriptionLabel\" type=\"Label\" parent=\"MarginContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nautowrap_mode = 2\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/baker.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Baker for Terrain3D\nextends Node\n\nconst BakeLodDialog: PackedScene = preload(\"res://addons/terrain_3d/menu/bake_lod_dialog.tscn\")\nconst BAKE_MESH_DESCRIPTION: String = \"This will create a child MeshInstance3D. LOD4+ is recommended. LOD0 is slow and dense with vertices every 1 unit. It is not an optimal mesh.\"\nconst BAKE_OCCLUDER_DESCRIPTION: String = \"This will create a child OccluderInstance3D. LOD4+ is recommended and will take 5+ seconds per region to generate. LOD0 is unnecessarily dense and slow.\"\nconst SET_UP_NAVIGATION_DESCRIPTION: String = \"This operation will:\n\n- Create a NavigationRegion3D node,\n- Assign it a blank NavigationMesh resource,\n- Move the Terrain3D node to be a child of the new node,\n- And bake the nav mesh.\n\nOnce setup is complete, you can modify the settings on your nav mesh, and rebake\nwithout having to run through the setup again.\n\nIf preferred, this setup can be canceled and the steps performed manually. For\nthe best results, adjust the settings on the NavigationMesh resource to match\nthe settings of your navigation agents and collisions.\"\n\nvar plugin: EditorPlugin\nvar bake_method: Callable\nvar bake_lod_dialog: ConfirmationDialog\nvar confirm_dialog: ConfirmationDialog\n\n\nfunc _enter_tree() -> void:\n\tbake_lod_dialog = BakeLodDialog.instantiate()\n\tbake_lod_dialog.hide()\n\tbake_lod_dialog.confirmed.connect(func(): bake_method.call())\n\tbake_lod_dialog.set_unparent_when_invisible(true)\n\t\n\tconfirm_dialog = ConfirmationDialog.new()\n\tconfirm_dialog.hide()\n\tconfirm_dialog.confirmed.connect(func(): bake_method.call())\n\tconfirm_dialog.set_unparent_when_invisible(true)\n\n\nfunc _exit_tree() -> void:\n\tbake_lod_dialog.queue_free()\n\tconfirm_dialog.queue_free()\n\n\nfunc bake_mesh_popup() -> void:\n\tif plugin.terrain:\n\t\tbake_method = _bake_mesh\n\t\tbake_lod_dialog.description = BAKE_MESH_DESCRIPTION\n\t\tEditorInterface.popup_dialog_centered(bake_lod_dialog)\n\n\nfunc _bake_mesh() -> void:\n\tif plugin.terrain.data.get_region_count() == 0:\n\t\tpush_error(\"Terrain3D has no active regions to bake\")\n\t\treturn\n\tvar mesh: Mesh = plugin.terrain.bake_mesh(bake_lod_dialog.lod, Terrain3DData.HEIGHT_FILTER_NEAREST)\n\tif !mesh:\n\t\tpush_error(\"Failed to bake mesh from Terrain3D\")\n\t\treturn\n\n\tvar undo: EditorUndoRedoManager = plugin.get_undo_redo()\n\tundo.create_action(\"Terrain3D Bake ArrayMesh\")\n\t\n\tvar mesh_instance := plugin.terrain.get_node_or_null(^\"MeshInstance3D\") as MeshInstance3D\n\tif !mesh_instance:\n\t\tmesh_instance = MeshInstance3D.new()\n\t\tmesh_instance.name = &\"MeshInstance3D\"\n\t\tmesh_instance.set_skeleton_path(NodePath())\n\t\tmesh_instance.mesh = mesh\n\t\t\n\t\tundo.add_do_method(plugin.terrain, &\"add_child\", mesh_instance, true)\n\t\tundo.add_undo_method(plugin.terrain, &\"remove_child\", mesh_instance)\n\t\tundo.add_do_property(mesh_instance, &\"owner\", EditorInterface.get_edited_scene_root())\n\t\tundo.add_do_reference(mesh_instance)\n\t\n\telse:\n\t\tundo.add_do_property(mesh_instance, &\"mesh\", mesh)\n\t\tundo.add_undo_property(mesh_instance, &\"mesh\", mesh_instance.mesh)\n\t\t\n\t\tif mesh_instance.mesh.resource_path:\n\t\t\tvar path := mesh_instance.mesh.resource_path\n\t\t\tundo.add_do_method(mesh, &\"take_over_path\", path)\n\t\t\tundo.add_undo_method(mesh_instance.mesh, &\"take_over_path\", path)\n\t\t\tundo.add_do_method(ResourceSaver, &\"save\", mesh)\n\t\t\tundo.add_undo_method(ResourceSaver, &\"save\", mesh_instance.mesh)\n\t\n\tundo.commit_action()\n\n\nfunc bake_occluder_popup() -> void:\n\tif plugin.terrain:\n\t\tbake_method = _bake_occluder\n\t\tbake_lod_dialog.description = BAKE_OCCLUDER_DESCRIPTION\n\t\tEditorInterface.popup_dialog_centered(bake_lod_dialog)\n\n\nfunc _bake_occluder() -> void:\n\tif plugin.terrain.data.get_region_count() == 0:\n\t\tpush_error(\"Terrain3D has no active regions to bake\")\n\t\treturn\n\tvar mesh: Mesh = plugin.terrain.bake_mesh(bake_lod_dialog.lod, Terrain3DData.HEIGHT_FILTER_MINIMUM)\n\tif !mesh:\n\t\tpush_error(\"Failed to bake mesh from Terrain3D\")\n\t\treturn\n\tassert(mesh.get_surface_count() == 1)\n\n\tvar undo: EditorUndoRedoManager = plugin.get_undo_redo()\n\tundo.create_action(\"Terrain3D Bake Occluder3D\")\n\n\tvar occluder := ArrayOccluder3D.new()\n\tvar arrays: Array = mesh.surface_get_arrays(0)\n\tassert(arrays.size() > Mesh.ARRAY_INDEX)\n\tassert(arrays[Mesh.ARRAY_INDEX] != null)\n\toccluder.set_arrays(arrays[Mesh.ARRAY_VERTEX], arrays[Mesh.ARRAY_INDEX])\n\n\tvar occluder_instance := plugin.terrain.get_node_or_null(^\"OccluderInstance3D\") as OccluderInstance3D\n\tif !occluder_instance:\n\t\toccluder_instance = OccluderInstance3D.new()\n\t\toccluder_instance.name = &\"OccluderInstance3D\"\n\t\toccluder_instance.occluder = occluder\n\n\t\tundo.add_do_method(plugin.terrain, &\"add_child\", occluder_instance, true)\n\t\tundo.add_undo_method(plugin.terrain, &\"remove_child\", occluder_instance)\n\t\tundo.add_do_property(occluder_instance, &\"owner\", EditorInterface.get_edited_scene_root())\n\t\tundo.add_do_reference(occluder_instance)\n\t\n\telse:\n\t\tundo.add_do_property(occluder_instance, &\"occluder\", occluder)\n\t\tundo.add_undo_property(occluder_instance, &\"occluder\", occluder_instance.occluder)\n\t\t\n\t\tif occluder_instance.occluder.resource_path:\n\t\t\tvar path := occluder_instance.occluder.resource_path\n\t\t\tundo.add_do_method(occluder, &\"take_over_path\", path)\n\t\t\tundo.add_undo_method(occluder_instance.occluder, &\"take_over_path\", path)\n\t\t\tundo.add_do_method(ResourceSaver, &\"save\", occluder)\n\t\t\tundo.add_undo_method(ResourceSaver, &\"save\", occluder_instance.occluder)\n\t\n\tundo.commit_action()\n\n\nfunc find_nav_region_terrains(p_nav_region: NavigationRegion3D) -> Array[Terrain3D]:\n\tvar result: Array[Terrain3D] = []\n\tif not p_nav_region.navigation_mesh:\n\t\treturn result\n\t\n\tvar source_mode: NavigationMesh.SourceGeometryMode\n\tsource_mode = p_nav_region.navigation_mesh.geometry_source_geometry_mode\n\tif source_mode == NavigationMesh.SOURCE_GEOMETRY_ROOT_NODE_CHILDREN:\n\t\tresult.append_array(p_nav_region.find_children(\"\", \"Terrain3D\", true, true))\n\t\treturn result\n\t\n\tvar group_nodes: Array = p_nav_region.get_tree().get_nodes_in_group(p_nav_region.navigation_mesh.geometry_source_group_name)\n\tfor node in group_nodes:\n\t\tif node is Terrain3D:\n\t\t\tresult.push_back(node)\n\t\tif source_mode == NavigationMesh.SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN:\n\t\t\tresult.append_array(node.find_children(\"\", \"Terrain3D\", true, true))\n\t\n\treturn result\n\n\nfunc find_terrain_nav_regions(p_terrain: Terrain3D) -> Array[NavigationRegion3D]:\n\tvar result: Array[NavigationRegion3D] = []\n\tvar root: Node = EditorInterface.get_edited_scene_root()\n\tif not root:\n\t\treturn result\n\tfor nav_region in root.find_children(\"\", \"NavigationRegion3D\", true, true):\n\t\tif find_nav_region_terrains(nav_region).has(p_terrain):\n\t\t\tresult.push_back(nav_region)\n\treturn result\n\n\nfunc bake_nav_mesh() -> void:\n\tif plugin.nav_region:\n\t\t# A NavigationRegion3D is selected. We only need to bake that one navmesh.\n\t\t_bake_nav_region_nav_mesh(plugin.nav_region)\n\t\tprint(\"Terrain3DNavigation: Finished baking 1 NavigationMesh.\")\n\t\n\telif plugin.terrain:\n\t\tif plugin.terrain.data.get_region_count() == 0:\n\t\t\tpush_error(\"Terrain3D has no active regions to bake\")\n\t\t\treturn\n\t\t# A Terrain3D is selected. There are potentially multiple navmeshes to bake and we need to\n\t\t# find them all. (The multiple navmesh use-case is likely on very large scenes with lots of\n\t\t# geometry. Each navmesh in this case would define its own, non-overlapping, baking AABB, to\n\t\t# cut down on the amount of geometry to bake. In a large open-world RPG, for instance, there\n\t\t# could be a navmesh for each town.)\n\t\tvar nav_regions: Array[NavigationRegion3D] = find_terrain_nav_regions(plugin.terrain)\n\t\tfor nav_region in nav_regions:\n\t\t\t_bake_nav_region_nav_mesh(nav_region)\n\t\tprint(\"Terrain3DNavigation: Finished baking %d NavigationMesh(es).\" % nav_regions.size())\n\n\nfunc _bake_nav_region_nav_mesh(p_nav_region: NavigationRegion3D) -> void:\n\tvar nav_mesh: NavigationMesh = p_nav_region.navigation_mesh\n\tassert(nav_mesh != null)\n\t\n\tvar source_geometry_data := NavigationMeshSourceGeometryData3D.new()\n\tNavigationServer3D.parse_source_geometry_data(nav_mesh, source_geometry_data, p_nav_region)\n\t\n\tfor terrain in find_nav_region_terrains(p_nav_region):\n\t\tvar aabb: AABB = nav_mesh.filter_baking_aabb\n\t\taabb.position += nav_mesh.filter_baking_aabb_offset\n\t\taabb = p_nav_region.global_transform * aabb\n\t\tvar faces: PackedVector3Array = terrain.generate_nav_mesh_source_geometry(aabb)\n\t\tif not faces.is_empty():\n\t\t\tsource_geometry_data.add_faces(faces, Transform3D.IDENTITY)\n\t\n\tNavigationServer3D.bake_from_source_geometry_data(nav_mesh, source_geometry_data)\n\t\n\t_postprocess_nav_mesh(nav_mesh)\n\t\n\t# Assign null first to force the debug display to actually update:\n\tp_nav_region.set_navigation_mesh(null)\n\tp_nav_region.set_navigation_mesh(nav_mesh)\n\t\n\t# Trigger save to disk if it is saved as an external file\n\tif not nav_mesh.get_path().is_empty():\n\t\tResourceSaver.save(nav_mesh, nav_mesh.get_path(), ResourceSaver.FLAG_COMPRESS)\n\t\n\t# Let other editor plugins and tool scripts know the nav mesh was just baked:\n\tp_nav_region.bake_finished.emit()\n\n\nfunc _postprocess_nav_mesh(p_nav_mesh: NavigationMesh) -> void:\n\t# Post-process the nav mesh to work around Godot issue #85548\n\t\n\t# Round all the vertices in the nav_mesh to the nearest cell_size/cell_height so that it doesn't\n\t# contain any edges shorter than cell_size/cell_height (one cause of #85548).\n\tvar vertices: PackedVector3Array = _postprocess_nav_mesh_round_vertices(p_nav_mesh)\n\t\n\t# Rounding vertices can collapse some edges to 0 length. We remove these edges, and any polygons\n\t# that have been reduced to 0 area.\n\tvar polygons: Array[PackedInt32Array] = _postprocess_nav_mesh_remove_empty_polygons(p_nav_mesh, vertices)\n\t\n\t# Another cause of #85548 is baking producing overlapping polygons. We remove these.\n\t_postprocess_nav_mesh_remove_overlapping_polygons(p_nav_mesh, vertices, polygons)\n\t\n\tp_nav_mesh.clear_polygons()\n\tp_nav_mesh.set_vertices(vertices)\n\tfor polygon in polygons:\n\t\tp_nav_mesh.add_polygon(polygon)\n\n\nfunc _postprocess_nav_mesh_round_vertices(p_nav_mesh: NavigationMesh) -> PackedVector3Array:\n\tassert(p_nav_mesh != null)\n\tassert(p_nav_mesh.cell_size > 0.0)\n\tassert(p_nav_mesh.cell_height > 0.0)\n\t\n\tvar cell_size: Vector3 = Vector3(p_nav_mesh.cell_size, p_nav_mesh.cell_height, p_nav_mesh.cell_size)\n\t\n\t# Round a little harder to avoid rounding errors with non-power-of-two cell_size/cell_height\n\t# causing the navigation map to put two non-matching edges in the same cell:\n\tvar round_factor := cell_size * 1.001\n\t\n\tvar vertices: PackedVector3Array = p_nav_mesh.get_vertices()\n\tfor i in range(vertices.size()):\n\t\tvertices[i] = (vertices[i] / round_factor).floor() * round_factor\n\treturn vertices\n\n\nfunc _postprocess_nav_mesh_remove_empty_polygons(p_nav_mesh: NavigationMesh, p_vertices: PackedVector3Array) -> Array[PackedInt32Array]:\n\tvar polygons: Array[PackedInt32Array] = []\n\t\n\tfor i in range(p_nav_mesh.get_polygon_count()):\n\t\tvar old_polygon: PackedInt32Array = p_nav_mesh.get_polygon(i)\n\t\tvar new_polygon: PackedInt32Array = []\n\t\t\n\t\t# Remove duplicate vertices (introduced by rounding) from the polygon:\n\t\tvar polygon_vertices: PackedVector3Array = []\n\t\tfor index in old_polygon:\n\t\t\tvar vertex: Vector3 = p_vertices[index]\n\t\t\tif polygon_vertices.has(vertex):\n\t\t\t\tcontinue\n\t\t\tpolygon_vertices.push_back(vertex)\n\t\t\tnew_polygon.push_back(index)\n\t\t\n\t\t# If we removed some vertices, we might be able to remove the polygon too:\n\t\tif new_polygon.size() <= 2:\n\t\t\tcontinue\n\t\tpolygons.push_back(new_polygon)\n\t\t\n\treturn polygons\n\n\nfunc _postprocess_nav_mesh_remove_overlapping_polygons(p_nav_mesh: NavigationMesh, p_vertices: PackedVector3Array, p_polygons: Array[PackedInt32Array]) -> void:\n\t# Occasionally, a baked nav mesh comes out with overlapping polygons:\n\t# https://github.com/godotengine/godot/issues/85548#issuecomment-1839341071\n\t# Until the bug is fixed in the engine, this function attempts to detect and remove overlapping\n\t# polygons.\n\t\n\t# This function has to make a choice of which polygon to remove when an overlap is detected,\n\t# because in this case the nav mesh is ambiguous. To do this it uses a heuristic:\n\t# (1) an 'overlap' is defined as an edge that is shared by 3 or more polygons.\n\t# (2) a 'bad polygon' is defined as a polygon that contains 2 or more 'overlaps'.\n\t# The function removes the 'bad polygons', which in practice seems to be enough to remove all\n\t# overlaps without creating holes in the nav mesh.\n\t\n\tvar cell_size: Vector3 = Vector3(p_nav_mesh.cell_size, p_nav_mesh.cell_height, p_nav_mesh.cell_size)\n\t\n\t# `edges` is going to map edges (vertex pairs) to arrays of polygons that contain that edge.\n\tvar edges: Dictionary = {}\n\t\n\tfor polygon_index in range(p_polygons.size()):\n\t\tvar polygon: PackedInt32Array = p_polygons[polygon_index]\n\t\tfor j in range(polygon.size()):\n\t\t\tvar vertex: Vector3 = p_vertices[polygon[j]]\n\t\t\tvar next_vertex: Vector3 = p_vertices[polygon[(j + 1) % polygon.size()]]\n\t\t\t\n\t\t\t# edge_key is a key we can use in the edges dictionary that uniquely identifies the\n\t\t\t# edge. We use cell coordinates here (Vector3i) because with a non-power-of-two\n\t\t\t# cell_size, rounding errors can cause Vector3 vertices to not be equal.\n\t\t\t# Array.sort IS defined for vector types - see the Godot docs. It's necessary here\n\t\t\t# because polygons that share an edge can have their vertices in a different order.\n\t\t\tvar edge_key: Array = [Vector3i(vertex / cell_size), Vector3i(next_vertex / cell_size)]\n\t\t\tedge_key.sort()\n\t\t\t\n\t\t\tif !edges.has(edge_key):\n\t\t\t\tedges[edge_key] = []\n\t\t\tedges[edge_key].push_back(polygon_index)\n\t\n\tvar overlap_count: Dictionary = {}\n\tfor connections in edges.values():\n\t\tif connections.size() <= 2:\n\t\t\tcontinue\n\t\tfor polygon_index in connections:\n\t\t\toverlap_count[polygon_index] = overlap_count.get(polygon_index, 0) + 1\n\t\n\tvar bad_polygons: Array = []\n\tfor polygon_index in overlap_count.keys():\n\t\tif overlap_count[polygon_index] >= 2:\n\t\t\tbad_polygons.push_back(polygon_index)\n\t\n\tbad_polygons.sort()\n\tfor i in range(bad_polygons.size() - 1, -1, -1):\n\t\tp_polygons.remove_at(bad_polygons[i])\n\n\nfunc set_up_navigation_popup() -> void:\n\tif plugin.terrain:\n\t\tbake_method = _set_up_navigation\n\t\tconfirm_dialog.dialog_text = SET_UP_NAVIGATION_DESCRIPTION\n\t\tEditorInterface.popup_dialog_centered(confirm_dialog)\n\n\nfunc _set_up_navigation() -> void:\n\tassert(plugin.terrain)\n\tif plugin.terrain == EditorInterface.get_edited_scene_root():\n\t\tpush_error(\"Terrain3D Navigation setup not possible if Terrain3D node is scene root\")\n\t\treturn\n\tif plugin.terrain.data.get_region_count() == 0:\n\t\tpush_error(\"Terrain3D has no active regions\")\n\t\treturn\n\tvar terrain: Terrain3D = plugin.terrain\n\t\n\tvar nav_region := NavigationRegion3D.new()\n\tnav_region.name = &\"NavigationRegion3D\"\n\tnav_region.navigation_mesh = NavigationMesh.new()\n\t\n\tvar undo_redo: EditorUndoRedoManager = plugin.get_undo_redo()\n\t\n\tundo_redo.create_action(\"Terrain3D Set up Navigation\")\n\tundo_redo.add_do_method(self, &\"_do_set_up_navigation\", nav_region, terrain)\n\tundo_redo.add_undo_method(self, &\"_undo_set_up_navigation\", nav_region, terrain)\n\tundo_redo.add_do_reference(nav_region)\n\tundo_redo.commit_action()\n\n\tEditorInterface.inspect_object(nav_region)\n\tassert(plugin.nav_region == nav_region)\n\t\n\tbake_nav_mesh()\n\n\nfunc _do_set_up_navigation(p_nav_region: NavigationRegion3D, p_terrain: Terrain3D) -> void:\n\tvar parent: Node = p_terrain.get_parent()\n\tvar index: int = p_terrain.get_index()\n\tvar t_owner: Node = p_terrain.owner\n\t\n\tparent.add_child(p_nav_region, true)\n\tp_terrain.reparent(p_nav_region)\n\tparent.move_child(p_nav_region, index)\n\t\n\tp_nav_region.owner = t_owner\n\tp_terrain.owner = t_owner\n\n\nfunc _undo_set_up_navigation(p_nav_region: NavigationRegion3D, p_terrain: Terrain3D) -> void:\n\tassert(p_terrain.get_parent() == p_nav_region)\n\t\n\tvar parent: Node = p_nav_region.get_parent()\n\tvar index: int = p_nav_region.get_index()\n\tvar t_owner: Node = p_nav_region.get_owner()\n\t\n\tp_terrain.reparent(parent)\n\tparent.remove_child(p_nav_region)\n\tparent.move_child(p_terrain, index)\n\t\n\tp_terrain.owner = t_owner\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/baker.gd.uid",
    "content": "uid://4ulaeevj5jvi\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/channel_packer.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Channel Packer for Terrain3D\nextends RefCounted\n\nconst WINDOW_SCENE: String = \"res://addons/terrain_3d/menu/channel_packer.tscn\"\nconst TEMPLATE_PATH: String = \"res://addons/terrain_3d/menu/channel_packer_import_template.txt\"\nconst DRAG_DROP_SCRIPT: String = \"res://addons/terrain_3d/menu/channel_packer_dragdrop.gd\"\nenum { \n\tINFO,\n\tWARN,\n\tERROR,\n}\n\nenum {\n\tIMAGE_ALBEDO,\n\tIMAGE_HEIGHT,\n\tIMAGE_NORMAL,\n\tIMAGE_ROUGHNESS,\n\tIMAGE_AO\n}\n\nvar plugin: EditorPlugin\nvar window: Window\nvar save_file_dialog: EditorFileDialog\nvar open_file_dialog: EditorFileDialog\nvar invert_green_checkbox: CheckBox\nvar invert_smooth_checkbox: CheckBox\nvar invert_height_checkbox: CheckBox\nvar normalize_height_checkbox: CheckBox\nvar lumin_height_button: Button\nvar generate_mipmaps_checkbox: CheckBox\nvar high_quality_checkbox: CheckBox\nvar align_normals_checkbox: CheckBox\nvar resize_toggle_checkbox: CheckBox\nvar resize_option_box: SpinBox\nvar height_channel: Array[Button]\nvar height_channel_selected: int = 0\nvar roughness_channel: Array[Button]\nvar roughness_channel_selected: int = 0\nvar occlusion_channel: Array[Button]\nvar occlusion_channel_selected: int = 0\nvar last_opened_directory: String\nvar last_saved_directory: String\nvar packing_albedo: bool = false\nvar queue_pack_normal_roughness: bool = false\nvar images: Array[Image] = [null, null, null, null, null]\nvar status_label: Label\nvar no_op: Callable = func(): pass\nvar last_file_selected_fn: Callable = no_op\nvar normal_vector: Vector3\n\n\nfunc pack_textures_popup() -> void:\n\tif window != null:\n\t\twindow.show()\n\t\twindow.grab_focus()\n\t\twindow.move_to_center()\n\t\treturn\n\t\n\t# Set background color to match theme\n\twindow = (load(WINDOW_SCENE) as PackedScene).instantiate()\n\tvar base_color: Color = plugin.editor_settings.get_setting(\"interface/theme/base_color\")\n\tvar panel_style := StyleBoxFlat.new()\n\tpanel_style.bg_color = base_color\n\twindow.get_node(\"PanelContainer\").set(\"theme_override_styles/panel\", panel_style)\n\n\t# Connect button signals\n\tlumin_height_button = window.get_node(\"%LuminanceAsHeightButton\") as Button\n\t\n\tinvert_height_checkbox = window.get_node(\"%ConvertDepthToHeight\") as CheckBox\n\tnormalize_height_checkbox = window.get_node(\"%NormalizeHeight\") as CheckBox\n\n\tinvert_green_checkbox = window.get_node(\"%InvertGreenChannelCheckBox\") as CheckBox\n\talign_normals_checkbox = window.get_node(\"%AlignNormalsCheckBox\") as CheckBox\n\n\tinvert_smooth_checkbox = window.get_node(\"%InvertSmoothCheckBox\") as CheckBox\n\n\tresize_toggle_checkbox = window.get_node(\"%ResizeToggle\") as CheckBox\n\tresize_option_box = window.get_node(\"%ResizeOptionBox\") as SpinBox\n\tresize_toggle_checkbox.toggled.connect(func(val:bool) -> void: resize_option_box.set_visible(val))\n\tgenerate_mipmaps_checkbox = window.get_node(\"%GenerateMipmapsCheckBox\") as CheckBox\n\thigh_quality_checkbox = window.get_node(\"%HighQualityCheckBox\") as CheckBox\n\n\twindow.get_node(\"%PackButton\").pressed.connect(_on_pack_button_pressed)\n\tstatus_label = window.get_node(\"%StatusLabel\") as Label\n\twindow.get_node(\"%CloseButton\").pressed.connect(_on_close_requested)\n\twindow.close_requested.connect(_on_close_requested)\n\twindow.window_input.connect(func(event:InputEvent):\n\t\tif event is InputEventKey:\n\t\t\tif event.pressed and event.keycode == KEY_ESCAPE:\n\t\t\t\t_on_close_requested()\n\t\t)\n\t\t\n\theight_channel = [\n\t\twindow.get_node(\"%HeightChannelR\") as Button,\n\t\twindow.get_node(\"%HeightChannelG\") as Button,\n\t\twindow.get_node(\"%HeightChannelB\") as Button,\n\t\twindow.get_node(\"%HeightChannelA\") as Button\n\t]\n\troughness_channel = [\n\t\twindow.get_node(\"%RoughnessChannelR\") as Button,\n\t\twindow.get_node(\"%RoughnessChannelG\") as Button,\n\t\twindow.get_node(\"%RoughnessChannelB\") as Button,\n\t\twindow.get_node(\"%RoughnessChannelA\") as Button\n\t]\n\tocclusion_channel = [\n\t\twindow.get_node(\"%AOChannelR\") as Button,\n\t\twindow.get_node(\"%AOChannelG\") as Button,\n\t\twindow.get_node(\"%AOChannelB\") as Button,\n\t\twindow.get_node(\"%AOChannelA\") as Button\n\t]\n\t\n\theight_channel[0].pressed.connect(func() -> void: height_channel_selected = 0)\n\theight_channel[1].pressed.connect(func() -> void: height_channel_selected = 1)\n\theight_channel[2].pressed.connect(func() -> void: height_channel_selected = 2)\n\theight_channel[3].pressed.connect(func() -> void: height_channel_selected = 3)\n\t\n\troughness_channel[0].pressed.connect(func() -> void: roughness_channel_selected = 0)\n\troughness_channel[1].pressed.connect(func() -> void: roughness_channel_selected = 1)\n\troughness_channel[2].pressed.connect(func() -> void: roughness_channel_selected = 2)\n\troughness_channel[3].pressed.connect(func() -> void: roughness_channel_selected = 3)\n\t\n\tocclusion_channel[0].pressed.connect(func() -> void: occlusion_channel_selected = 0)\n\tocclusion_channel[1].pressed.connect(func() -> void: occlusion_channel_selected = 1)\n\tocclusion_channel[2].pressed.connect(func() -> void: occlusion_channel_selected = 2)\n\tocclusion_channel[3].pressed.connect(func() -> void: occlusion_channel_selected = 3)\n\t\n\tplugin.add_child(window)\n\t_init_file_dialogs()\n\t\n\t# the dialog disables the parent window \"on top\" so, restore it after 1 frame to alow the dialog to clear.\n\tvar set_on_top_fn: Callable = func(_file: String = \"\") -> void:\n\t\tawait RenderingServer.frame_post_draw\n\t\twindow.always_on_top = true\n\tsave_file_dialog.file_selected.connect(set_on_top_fn)\n\tsave_file_dialog.canceled.connect(set_on_top_fn)\n\topen_file_dialog.file_selected.connect(set_on_top_fn)\n\topen_file_dialog.canceled.connect(set_on_top_fn)\n\t\n\t_init_texture_picker(window.get_node(\"%AlbedoVBox\"), IMAGE_ALBEDO)\n\t_init_texture_picker(window.get_node(\"%HeightVBox\"), IMAGE_HEIGHT)\n\t_init_texture_picker(window.get_node(\"%NormalVBox\"), IMAGE_NORMAL)\n\t_init_texture_picker(window.get_node(\"%RoughnessVBox\"), IMAGE_ROUGHNESS)\n\t_init_texture_picker(window.get_node(\"%AOVBox\"), IMAGE_AO)\n\n\nfunc _on_close_requested() -> void:\n\tlast_file_selected_fn = no_op\n\timages = [null, null, null, null, null]\n\twindow.queue_free()\n\twindow = null\n\n\nfunc _init_file_dialogs() -> void:\n\tsave_file_dialog = EditorFileDialog.new()\n\tsave_file_dialog.set_filters(PackedStringArray([\"*.png\"]))\n\tsave_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_SAVE_FILE)\n\tsave_file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM\n\tsave_file_dialog.file_selected.connect(_on_save_file_selected)\n\tsave_file_dialog.ok_button_text = \"Save\"\n\tsave_file_dialog.size = Vector2i(550, 550)\n\t#save_file_dialog.transient = false\n\t#save_file_dialog.exclusive = false\n\t#save_file_dialog.popup_window = true\n\t\n\topen_file_dialog = EditorFileDialog.new()\n\topen_file_dialog.set_filters(PackedStringArray(\n\t\t[\"*.png\", \"*.bmp\", \"*.exr\", \"*.hdr\", \"*.jpg\", \"*.jpeg\", \"*.tga\", \"*.svg\", \"*.webp\", \"*.ktx\", \"*.dds\"]))\n\topen_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_OPEN_FILE)\n\topen_file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM\n\topen_file_dialog.ok_button_text = \"Open\"\n\topen_file_dialog.size = Vector2i(550, 550)\n\t#open_file_dialog.transient = false\n\t#open_file_dialog.exclusive = false\n\t#open_file_dialog.popup_window = true\n\t\n\twindow.add_child(save_file_dialog)\n\twindow.add_child(open_file_dialog)\n\n\nfunc _init_texture_picker(p_parent: Node, p_image_index: int) -> void:\n\tvar line_edit: LineEdit = p_parent.find_child(\"LineEdit\") as LineEdit\n\tvar file_pick_button: Button = p_parent.find_child(\"PickButton\") as Button\n\tvar clear_button: Button = p_parent.find_child(\"ClearButton\") as Button\n\tvar texture_rect: TextureRect = p_parent.find_child(\"TextureRect\") as TextureRect\n\tvar texture_button: Button = p_parent.find_child(\"TextureButton\") as Button\n\ttexture_button.set_script(load(DRAG_DROP_SCRIPT) as GDScript)\n\t\n\tvar set_channel_fn: Callable = func(used_channels: int) -> void:\n\t\tvar channel_count: int = 4\n\t\t# enum Image.UsedChannels\n\t\tmatch used_channels:\n\t\t\tImage.USED_CHANNELS_L, Image.USED_CHANNELS_R: channel_count = 1\n\t\t\tImage.USED_CHANNELS_LA, Image.USED_CHANNELS_RG: channel_count = 2\n\t\t\tImage.USED_CHANNELS_RGB: channel_count = 3\n\t\t\tImage.USED_CHANNELS_RGBA: channel_count = 4\n\t\tif p_image_index == IMAGE_HEIGHT:\n\t\t\tfor i in 4:\n\t\t\t\theight_channel[i].visible = i < channel_count\n\t\t\theight_channel[0].button_pressed = true\n\t\t\theight_channel[0].pressed.emit()\n\t\telif p_image_index == IMAGE_ROUGHNESS:\n\t\t\tfor i in 4:\n\t\t\t\troughness_channel[i].visible = i < channel_count\n\t\t\troughness_channel[0].button_pressed = true\n\t\t\troughness_channel[0].pressed.emit()\n\t\telif p_image_index == IMAGE_AO:\n\t\t\tfor i in 4:\n\t\t\t\tocclusion_channel[i].visible = i < channel_count\n\t\t\tocclusion_channel[0].button_pressed = true\n\t\t\tocclusion_channel[0].pressed.emit()\n\t\n\tvar load_image_fn: Callable = func(path: String):\n\t\tvar image: Image = Image.new()\n\t\tvar error: int = OK\n\t\t# Special case for dds files\n\t\tif path.get_extension() == \"dds\":\n\t\t\timage = ResourceLoader.load(path).get_image()\n\t\t\tif not image.is_empty():\n\t\t\t\t# if the dds file is loaded, we must clear any mipmaps and\n\t\t\t\t# decompress if needed in order to do per pixel operations.\n\t\t\t\timage.clear_mipmaps()\n\t\t\t\timage.decompress()\n\t\t\telse:\n\t\t\t\terror = FAILED\n\t\telse:\n\t\t\terror = image.load(path)\n\t\tif error != OK:\n\t\t\t_show_message(ERROR, \"Failed to load texture '\" + path + \"'\")\n\t\t\ttexture_rect.texture = null\n\t\t\timages[p_image_index] = null\n\t\telse:\n\t\t\t_show_message(INFO, \"Loaded texture '\" + path + \"'\")\n\t\t\ttexture_rect.texture = ImageTexture.create_from_image(image)\n\t\t\timages[p_image_index] = image\n\t\t\t_set_wh_labels(p_image_index, image.get_width(), image.get_height())\n\t\t\tif p_image_index == IMAGE_NORMAL:\n\t\t\t\t_set_normal_vector(image)\n\t\t\tif p_image_index in [ IMAGE_HEIGHT, IMAGE_ROUGHNESS, IMAGE_AO ]:\n\t\t\t\tset_channel_fn.call(image.detect_used_channels())\n\t\n\tvar os_drop_fn: Callable = func(files: PackedStringArray) -> void:\n\t\t# OS drag drop holds mouse focus until released,\n\t\t# Get mouse pos and check directly if inside texture_rect\n\t\tvar rect = texture_button.get_global_rect()\n\t\tvar mouse_position = texture_button.get_global_mouse_position()\n\t\tif rect.has_point(mouse_position):\n\t\t\tif files.size() != 1:\n\t\t\t\t_show_message(ERROR, \"Cannot load multiple files\")\n\t\t\telse:\n\t\t\t\tline_edit.text = files[0]\n\t\t\t\tload_image_fn.call(files[0])\n\t\n\tvar godot_drop_fn: Callable = func(path: String) -> void:\n\t\tpath = ProjectSettings.globalize_path(path)\n\t\tline_edit.text = path\n\t\tload_image_fn.call(path)\n\t\n\tvar open_fn: Callable = func() -> void:\n\t\topen_file_dialog.current_path = last_opened_directory\n\t\tif last_file_selected_fn != no_op:\n\t\t\topen_file_dialog.file_selected.disconnect(last_file_selected_fn)\n\t\tlast_file_selected_fn = func(path: String) -> void: \n\t\t\tline_edit.text = path\n\t\t\tload_image_fn.call(path)\n\t\topen_file_dialog.file_selected.connect(last_file_selected_fn)\n\t\topen_file_dialog.popup_centered_ratio()\n\t\n\tvar line_edit_submit_fn: Callable = func(path: String) -> void:\n\t\tline_edit.text = path\n\t\tload_image_fn.call(path)\n\t\n\tvar clear_fn: Callable = func() -> void:\n\t\tline_edit.text = \"\"\n\t\ttexture_rect.texture = null\n\t\timages[p_image_index] = null\n\t\t_set_wh_labels(p_image_index, -1, -1)\n\t\n\tline_edit.text_submitted.connect(line_edit_submit_fn)\n\tfile_pick_button.pressed.connect(open_fn)\n\ttexture_button.pressed.connect(open_fn)\n\tclear_button.pressed.connect(clear_fn)\n\ttexture_button.dropped.connect(godot_drop_fn)\n\twindow.files_dropped.connect(os_drop_fn)\n\t\n\tif p_image_index == IMAGE_HEIGHT:\n\t\tvar lumin_fn: Callable = func() -> void:\n\t\t\tif !images[IMAGE_ALBEDO]:\n\t\t\t\t_show_message(ERROR, \"Albedo Image Required for Operation\")\n\t\t\telse:\n\t\t\t\tline_edit.text = \"Generated Height\"\n\t\t\t\tvar height_texture: Image = Terrain3DUtil.luminance_to_height(images[IMAGE_ALBEDO])\n\t\t\t\tif height_texture.is_empty():\n\t\t\t\t\t_show_message(ERROR, \"Height Texture Generation error\")\n\t\t\t\t# blur the image by resizing down and back..\n\t\t\t\tvar w: int = height_texture.get_width()\n\t\t\t\tvar h: int = height_texture.get_height()\n\t\t\t\theight_texture.resize(w / 4, h / 4)\n\t\t\t\theight_texture.resize(w, h, Image.INTERPOLATE_CUBIC)\n\t\t\t\t# \"Load\" the height texture\n\t\t\t\timages[IMAGE_HEIGHT] = height_texture\n\t\t\t\ttexture_rect.texture = ImageTexture.create_from_image(images[IMAGE_HEIGHT])\n\t\t\t\t_set_wh_labels(IMAGE_HEIGHT, height_texture.get_width(), height_texture.get_height())\n\t\t\t\tset_channel_fn.call(Image.USED_CHANNELS_R)\n\t\t\t\t_show_message(INFO, \"Height Texture generated sucsessfully\")\n\t\tlumin_height_button.pressed.connect(lumin_fn)\n\tplugin.ui.set_button_editor_icon(file_pick_button, \"Folder\")\n\tplugin.ui.set_button_editor_icon(clear_button, \"Remove\")\n\n\nfunc _set_wh_labels(p_image_index: int, width: int, height: int) -> void:\n\tmatch p_image_index:\n\t\t0:\n\t\t\twindow.get_node(\"%AlbedoSize\").text = \"(%d, %d)\" % [ width, height ]\n\t\t1:\n\t\t\twindow.get_node(\"%HeightSize\").text = \"(%d, %d)\" % [ width, height ]\n\t\t2:\n\t\t\twindow.get_node(\"%NormalSize\").text = \"(%d, %d)\" % [ width, height ]\n\t\t3:\n\t\t\twindow.get_node(\"%RoughnessSize\").text = \"(%d, %d)\" % [ width, height ]\n\t\t4:\n\t\t\twindow.get_node(\"%AOSize\").text = \"(%d, %d)\" % [ width, height ]\n\n\nfunc _show_message(p_level: int, p_text: String) -> void:\n\tstatus_label.text = p_text\n\tmatch p_level:\n\t\tINFO:\n\t\t\tprint(\"Terrain3DChannelPacker: \" + p_text)\n\t\t\tstatus_label.add_theme_color_override(\"font_color\", Color(0, 0.82, 0.14))\n\t\tWARN:\n\t\t\tpush_warning(\"Terrain3DChannelPacker: \" + p_text)\n\t\t\tstatus_label.add_theme_color_override(\"font_color\", Color(0.9, 0.9, 0))\n\t\tERROR,_:\n\t\t\tpush_error(\"Terrain3DChannelPacker: \" + p_text)\n\t\t\tstatus_label.add_theme_color_override(\"font_color\", Color(0.9, 0, 0))\n\n\nfunc _create_import_file(png_path: String) -> void:\n\tvar dst_import_path: String = png_path + \".import\"\n\tvar file: FileAccess = FileAccess.open(TEMPLATE_PATH, FileAccess.READ)\n\tvar template_content: String = file.get_as_text()\n\tfile.close()\n\ttemplate_content = template_content.replace(\n\t\t\"$SOURCE_FILE\", png_path).replace(\n\t\t\"$HIGH_QUALITY\", str(high_quality_checkbox.button_pressed)).replace(\n\t\t\"$GENERATE_MIPMAPS\", str(generate_mipmaps_checkbox.button_pressed)\n\t)\n\tvar import_content: String = template_content\n\tfile = FileAccess.open(dst_import_path, FileAccess.WRITE)\n\tfile.store_string(import_content)\n\tfile.close()\n\n\nfunc _on_pack_button_pressed() -> void:\n\tpacking_albedo = images[IMAGE_ALBEDO] != null and images[IMAGE_HEIGHT] != null\n\tvar packing_normal_roughness: bool = images[IMAGE_NORMAL] != null and images[IMAGE_ROUGHNESS] != null\n\t\n\tif not packing_albedo and not packing_normal_roughness:\n\t\t_show_message(WARN, \"Please select an albedo and height texture or a normal and roughness texture\")\n\t\treturn\n\tif packing_albedo:\n\t\tsave_file_dialog.current_path = last_saved_directory + \"packed_albedo_height\"\n\t\tsave_file_dialog.title = \"Save Packed Albedo/Height Texture\"\n\t\tsave_file_dialog.popup_centered_ratio()\n\t\tif packing_normal_roughness:\n\t\t\tqueue_pack_normal_roughness = true\n\t\treturn\n\tif packing_normal_roughness:\n\t\tsave_file_dialog.current_path = last_saved_directory + \"packed_normal_roughness\"\n\t\tsave_file_dialog.title = \"Save Packed Normal/Roughness Texture\"\n\t\tsave_file_dialog.popup_centered_ratio()\n\n\nfunc _on_save_file_selected(p_dst_path) -> void:\n\tlast_saved_directory = p_dst_path.get_base_dir() + \"/\"\n\tvar error: int\n\tif packing_albedo:\n\t\terror = _pack_textures(images[IMAGE_ALBEDO], images[IMAGE_HEIGHT], null, p_dst_path, false,\n\t\tinvert_height_checkbox.button_pressed, false, normalize_height_checkbox.button_pressed, height_channel_selected)\n\telse:\n\t\terror = _pack_textures(images[IMAGE_NORMAL], images[IMAGE_ROUGHNESS], images[IMAGE_AO], p_dst_path,\n\t\t\tinvert_green_checkbox.button_pressed, invert_smooth_checkbox.button_pressed,\n\t\t\talign_normals_checkbox.button_pressed, false, roughness_channel_selected, occlusion_channel_selected)\n\t\n\tif error == OK:\n\t\tEditorInterface.get_resource_filesystem().scan()\n\t\tif window.visible:\n\t\t\twindow.hide()\n\t\tawait EditorInterface.get_resource_filesystem().resources_reimported\n\t\t# wait 1 extra frame, to ensure the UI is responsive.\n\t\tawait RenderingServer.frame_post_draw\n\t\twindow.show()\n\t\n\tif queue_pack_normal_roughness:\n\t\tqueue_pack_normal_roughness = false\n\t\tpacking_albedo = false\n\t\tsave_file_dialog.current_path = last_saved_directory + \"packed_normal_roughness\"\n\t\tsave_file_dialog.title = \"Save Packed Normal/Roughness Texture\"\n\t\t\n\t\tsave_file_dialog.call_deferred(\"popup_centered_ratio\")\n\t\tsave_file_dialog.call_deferred(\"grab_focus\")\n\n\nfunc _alignment_basis(normal: Vector3) -> Basis:\n\tvar up: Vector3 = Vector3(0, 0, 1)\n\tvar v: Vector3 = normal.cross(up)\n\tvar c: float = normal.dot(up)\n\tvar k: float = 1.0 / (1.0 + c)\n\t\n\tvar vxy: float = v.x * v.y * k\n\tvar vxz: float = v.x * v.z * k\n\tvar vyz: float = v.y * v.z * k\n\t\n\treturn Basis(Vector3(v.x * v.x * k + c, vxy - v.z, vxz + v.y),\n\t\tVector3(vxy + v.z, v.y * v.y * k + c, vyz - v.x),\n\t\tVector3(vxz - v.y, vyz + v.x, v.z * v.z * k + c)\n\t)\n\n\nfunc _set_normal_vector(source: Image, quiet: bool = false) -> void:\n\t# Calculate texture normal sum direction\n\tvar normal: Image = source\n\tvar sum: Color = Color(0.0, 0.0, 0.0, 0.0)\n\tfor x in normal.get_width():\n\t\tfor y in normal.get_height():\n\t\t\tsum += normal.get_pixel(x, y)\n\tvar div: float = normal.get_height() * normal.get_width()\n\tsum /= Color(div, div, div)\n\tsum *= 2.0\n\tsum -= Color(1.0, 1.0, 1.0)\n\tnormal_vector = Vector3(sum.r, sum.g, sum.b).normalized()\n\tif normal_vector.dot(Vector3(0.0, 0.0, 1.0)) < 0.999 && !quiet:\n\t\t_show_message(WARN, \"Normal Texture Not Orthoganol to UV plane.\\nFor Compatability with Detiling and Rotation, Select Orthoganolize Normals\")\n\n\nfunc _align_normals(source: Image, iteration: int = 0) -> void:\n\t# generate matrix to re-align the normalmap\n\tvar mat3: Basis = _alignment_basis(normal_vector)\n\t# re-align the normal map pixels\n\tfor x in source.get_width():\n\t\tfor y in source.get_height():\n\t\t\tvar old_pixel: Color = source.get_pixel(x, y)\n\t\t\tvar vector_pixel: Vector3 = Vector3(old_pixel.r, old_pixel.g, old_pixel.b)\n\t\t\tvector_pixel *= 2.0\n\t\t\tvector_pixel -= Vector3.ONE\n\t\t\tvector_pixel = vector_pixel.normalized()\n\t\t\tvector_pixel = vector_pixel * mat3\n\t\t\tvector_pixel += Vector3.ONE\n\t\t\tvector_pixel *= 0.5\n\t\t\tvar new_pixel: Color = Color(vector_pixel.x, vector_pixel.y, vector_pixel.z, old_pixel.a)\n\t\t\tsource.set_pixel(x, y, new_pixel)\n\t_set_normal_vector(source, true)\n\tif normal_vector.dot(Vector3(0.0, 0.0, 1.0)) < 0.999 && iteration < 3:\n\t\t++iteration\n\t\t_align_normals(source, iteration)\n\n\nfunc _pack_textures(p_rgb_image: Image, p_a_image: Image, p_ao_image: Image, p_dst_path: String, p_invert_green: bool,\n\tp_invert_smooth: bool, p_align_normals: bool, p_normalize_height: bool, p_alpha_channel: int, p_occlusion_channel: int = 0) -> Error:\n\tif p_rgb_image and p_a_image:\n\t\tif p_rgb_image.get_size() != p_a_image.get_size() and !resize_toggle_checkbox.button_pressed:\n\t\t\t_show_message(ERROR, \"Textures must be the same size.\\nEnable resize to override image dimensions\")\n\t\t\treturn FAILED\n\t\tif p_ao_image:\n\t\t\tif p_rgb_image.get_size() != p_ao_image.get_size() and !resize_toggle_checkbox.button_pressed:\n\t\t\t\t_show_message(ERROR, \"Textures must be the same size.\\nEnable resize to override image dimensions\")\n\t\t\t\treturn FAILED\n\t\n\t\tif resize_toggle_checkbox.button_pressed:\n\t\t\tvar size: int = max(128, resize_option_box.value)\n\t\t\tp_rgb_image.resize(size, size, Image.INTERPOLATE_CUBIC)\n\t\t\tp_a_image.resize(size, size, Image.INTERPOLATE_CUBIC)\n\t\t\tif p_ao_image:\n\t\t\t\tp_ao_image.resize(size, size, Image.INTERPOLATE_CUBIC)\n\t\n\t\tif p_align_normals and normal_vector.dot(Vector3(0.0, 0.0, 1.0)) < 0.999:\n\t\t\t_align_normals(p_rgb_image)\n\t\telif p_align_normals:\n\t\t\t_show_message(INFO, \"Alignment OK, skipping Normal Orthogonalization\")\n\t\n\t\tvar output_image: Image = Terrain3DUtil.pack_image(p_rgb_image, p_a_image, p_ao_image,\n\t\t\tp_invert_green, p_invert_smooth, p_normalize_height, p_alpha_channel, p_occlusion_channel)\n\t\n\t\tif not output_image:\n\t\t\t_show_message(ERROR, \"Failed to pack textures\")\n\t\t\treturn FAILED\n\t\tif output_image.detect_alpha() != Image.ALPHA_BLEND:\n\t\t\t_show_message(WARN, \"Warning, Alpha channel empty\")\n\n\t\toutput_image.save_png(p_dst_path)\n\t\t_create_import_file(p_dst_path)\n\t\t_show_message(INFO, \"Packed to \" + p_dst_path + \".\")\n\t\treturn OK\n\telse:\n\t\t_show_message(ERROR, \"Failed to load one or more textures\")\n\t\treturn FAILED\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/channel_packer.gd.uid",
    "content": "uid://bwldx4itd58o7\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/channel_packer.tscn",
    "content": "[gd_scene load_steps=12 format=3 uid=\"uid://nud6dwjcnj5v\"]\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_mbo40\"]\ncontent_margin_left = 10.0\ncontent_margin_top = 10.0\ncontent_margin_right = 10.0\ncontent_margin_bottom = 10.0\ndraw_center = false\nborder_width_left = 3\nborder_width_top = 3\nborder_width_right = 3\nborder_width_bottom = 3\nborder_color = Color(0.28, 0.28, 0.28, 1)\nborder_blend = true\ncorner_radius_top_left = 5\ncorner_radius_top_right = 5\ncorner_radius_bottom_right = 5\ncorner_radius_bottom_left = 5\n\n[sub_resource type=\"LabelSettings\" id=\"LabelSettings_pxdc5\"]\nfont_size = 24\n\n[sub_resource type=\"LabelSettings\" id=\"LabelSettings_mbo40\"]\nfont_color = Color(1, 1, 1, 0.705882)\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_cb0xf\"]\nbg_color = Color(0.147, 0.168, 0.203, 1)\ndraw_center = false\nborder_width_left = 3\nborder_width_top = 3\nborder_width_right = 3\nborder_width_bottom = 3\nborder_color = Color(0.5655, 0.582, 0.6095, 1)\ncorner_radius_top_left = 5\ncorner_radius_top_right = 5\ncorner_radius_bottom_right = 5\ncorner_radius_bottom_left = 5\n\n[sub_resource type=\"StyleBoxEmpty\" id=\"StyleBoxEmpty_7qdas\"]\n\n[sub_resource type=\"LabelSettings\" id=\"LabelSettings_2eo1k\"]\nshadow_color = Color(0, 0, 0, 1)\n\n[sub_resource type=\"ButtonGroup\" id=\"ButtonGroup_wnxik\"]\n\n[sub_resource type=\"LabelSettings\" id=\"LabelSettings_nt754\"]\nfont_size = 24\n\n[sub_resource type=\"ButtonGroup\" id=\"ButtonGroup_47nos\"]\n\n[sub_resource type=\"ButtonGroup\" id=\"ButtonGroup_mbo40\"]\n\n[sub_resource type=\"LabelSettings\" id=\"LabelSettings_3c3a7\"]\nfont_size = 24\n\n[node name=\"Window\" type=\"Window\"]\ntitle = \"Terrain3D Channel Packer\"\ninitial_position = 1\nsize = Vector2i(1446, 730)\nwrap_controls = true\nalways_on_top = true\n\n[node name=\"PanelContainer\" type=\"PanelContainer\" parent=\".\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 0\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"PanelContainer\"]\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"PanelContainer/ScrollContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_constants/margin_left = 5\ntheme_override_constants/margin_top = 5\ntheme_override_constants/margin_right = 5\ntheme_override_constants/margin_bottom = 0\n\n[node name=\"FlowContainer\" type=\"HFlowContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"Albedo_Height\" type=\"PanelContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_mbo40\")\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height\"]\nlayout_mode = 2\ntheme_override_constants/separation = 10\n\n[node name=\"AlbHtLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox\"]\nlayout_mode = 2\ntext = \"1. Albedo + Height Texture\"\nlabel_settings = SubResource(\"LabelSettings_pxdc5\")\n\n[node name=\"HSeparator\" type=\"HSeparator\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox\"]\nlayout_mode = 2\n\n[node name=\"AlbedoVBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"AlbedoLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox\"]\nlayout_mode = 2\ntext = \"Albedo Texture\"\nlabel_settings = SubResource(\"LabelSettings_mbo40\")\n\n[node name=\"AlbedoFileHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox\"]\nlayout_mode = 2\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoFileHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"PickButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoFileHBox\"]\nlayout_mode = 2\n\n[node name=\"ClearButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoFileHBox\"]\nlayout_mode = 2\n\n[node name=\"AlbedoMapHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_constants/separation = 30\n\n[node name=\"Panel\" type=\"Panel\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoMapHBox\"]\ncustom_minimum_size = Vector2(110, 110)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_cb0xf\")\n\n[node name=\"TextureRect\" type=\"TextureRect\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -50.0\noffset_top = -50.0\noffset_right = 50.0\noffset_bottom = 50.0\ngrow_horizontal = 2\ngrow_vertical = 2\nexpand_mode = 1\n\n[node name=\"TextureButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_styles/normal = SubResource(\"StyleBoxEmpty_7qdas\")\n\n[node name=\"AlbedoSize\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoMapHBox/Panel\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 7\nanchor_left = 0.5\nanchor_top = 1.0\nanchor_right = 0.5\nanchor_bottom = 1.0\noffset_left = -18.5\noffset_top = -23.0\noffset_right = 18.5\ngrow_horizontal = 2\ngrow_vertical = 0\ntheme_override_colors/font_shadow_color = Color(0, 0, 0, 1)\ntext = \"(0, 0)\"\nlabel_settings = SubResource(\"LabelSettings_2eo1k\")\nhorizontal_alignment = 1\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoMapHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_constants/separation = 5\n\n[node name=\"LuminanceAsHeightButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/AlbedoVBox/AlbedoMapHBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \" Generate Height from Luminance\"\nicon_alignment = 2\n\n[node name=\"HSeparator2\" type=\"HSeparator\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox\"]\nlayout_mode = 2\ntheme_override_constants/separation = 20\n\n[node name=\"HeightVBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"HeightLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox\"]\nlayout_mode = 2\ntext = \"Height Texture\"\nlabel_settings = SubResource(\"LabelSettings_mbo40\")\n\n[node name=\"HeightFileHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox\"]\nlayout_mode = 2\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightFileHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"PickButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightFileHBox\"]\nlayout_mode = 2\n\n[node name=\"ClearButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightFileHBox\"]\nlayout_mode = 2\n\n[node name=\"HeightMapHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_constants/separation = 30\n\n[node name=\"Panel\" type=\"Panel\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox\"]\ncustom_minimum_size = Vector2(110, 110)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_cb0xf\")\n\n[node name=\"TextureRect\" type=\"TextureRect\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -50.0\noffset_top = -50.0\noffset_right = 50.0\noffset_bottom = 50.0\ngrow_horizontal = 2\ngrow_vertical = 2\nexpand_mode = 1\n\n[node name=\"TextureButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_styles/normal = SubResource(\"StyleBoxEmpty_7qdas\")\n\n[node name=\"HeightSize\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/Panel\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 7\nanchor_left = 0.5\nanchor_top = 1.0\nanchor_right = 0.5\nanchor_bottom = 1.0\noffset_left = -40.0\noffset_top = -23.0\noffset_right = 40.0\ngrow_horizontal = 2\ngrow_vertical = 0\ntheme_override_colors/font_shadow_color = Color(0, 0, 0, 1)\ntext = \"(0, 0)\"\nlabel_settings = SubResource(\"LabelSettings_2eo1k\")\nhorizontal_alignment = 1\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_constants/separation = 5\n\n[node name=\"ChannelHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nalignment = 1\n\n[node name=\"HeightChannelLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox/ChannelHBox\"]\nlayout_mode = 2\ntext = \" Source Channel: \"\nhorizontal_alignment = 2\n\n[node name=\"HeightChannelR\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_pressed = true\nbutton_group = SubResource(\"ButtonGroup_wnxik\")\ntext = \"R\"\n\n[node name=\"HeightChannelB\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_wnxik\")\ntext = \"G\"\n\n[node name=\"HeightChannelG\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_wnxik\")\ntext = \"B\"\n\n[node name=\"HeightChannelA\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_wnxik\")\ntext = \"A\"\n\n[node name=\"ConvertDepthToHeight\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \" Convert Depth to Height\"\nicon_alignment = 2\n\n[node name=\"NormalizeHeight\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox/HeightMapHBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Normalize Height\"\nicon_alignment = 2\n\n[node name=\"Spacer\" type=\"Control\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Albedo_Height/VBox/HeightVBox\"]\nlayout_mode = 2\n\n[node name=\"Normal_Roughness\" type=\"PanelContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_mbo40\")\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness\"]\nlayout_mode = 2\ntheme_override_constants/separation = 10\n\n[node name=\"NrmRghLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nlayout_mode = 2\ntext = \"2. Normal + Roughness Texture\"\nlabel_settings = SubResource(\"LabelSettings_nt754\")\n\n[node name=\"HSeparator\" type=\"HSeparator\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nlayout_mode = 2\n\n[node name=\"NormalVBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"NormalLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox\"]\nlayout_mode = 2\ntext = \"Normal Texture\"\nlabel_settings = SubResource(\"LabelSettings_mbo40\")\n\n[node name=\"NormalFileHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox\"]\nlayout_mode = 2\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalFileHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"PickButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalFileHBox\"]\nlayout_mode = 2\n\n[node name=\"ClearButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalFileHBox\"]\nlayout_mode = 2\n\n[node name=\"NormalMapHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_constants/separation = 30\n\n[node name=\"Panel\" type=\"Panel\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox\"]\ncustom_minimum_size = Vector2(110, 110)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_cb0xf\")\n\n[node name=\"TextureRect\" type=\"TextureRect\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -50.0\noffset_top = -50.0\noffset_right = 50.0\noffset_bottom = 50.0\ngrow_horizontal = 2\ngrow_vertical = 2\nexpand_mode = 1\n\n[node name=\"TextureButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_styles/normal = SubResource(\"StyleBoxEmpty_7qdas\")\n\n[node name=\"NormalSize\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox/Panel\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 7\nanchor_left = 0.5\nanchor_top = 1.0\nanchor_right = 0.5\nanchor_bottom = 1.0\noffset_left = -18.5\noffset_top = -23.0\noffset_right = 18.5\ngrow_horizontal = 2\ngrow_vertical = 0\ntheme_override_colors/font_shadow_color = Color(0, 0, 0, 1)\ntext = \"(0, 0)\"\nlabel_settings = SubResource(\"LabelSettings_2eo1k\")\nhorizontal_alignment = 1\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_constants/separation = 5\n\n[node name=\"InvertGreenChannelCheckBox\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"This setting determines if the normal map is +Y up (OpenGL) or down (DirectX) on the green channel. Some engines like Unreal Engine use DirectX normal maps. Godot uses OpenGL. This option inverts the green channel.\"\ntext = \" Convert DirectX to OpenGL\"\n\n[node name=\"AlignNormalsCheckBox\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/NormalVBox/NormalMapHBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \" Orthoganolise Normals\"\n\n[node name=\"HSeparator2\" type=\"HSeparator\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nlayout_mode = 2\ntheme_override_constants/separation = 20\n\n[node name=\"RoughnessVBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"RoughnessLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox\"]\nlayout_mode = 2\ntext = \"Roughness Texture\"\nlabel_settings = SubResource(\"LabelSettings_mbo40\")\n\n[node name=\"RoughnessFileHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox\"]\nlayout_mode = 2\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessFileHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"PickButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessFileHBox\"]\nlayout_mode = 2\n\n[node name=\"ClearButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessFileHBox\"]\nlayout_mode = 2\n\n[node name=\"RoughnessMapHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_constants/separation = 30\n\n[node name=\"Panel\" type=\"Panel\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox\"]\ncustom_minimum_size = Vector2(110, 110)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_cb0xf\")\n\n[node name=\"TextureRect\" type=\"TextureRect\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -50.0\noffset_top = -50.0\noffset_right = 50.0\noffset_bottom = 50.0\ngrow_horizontal = 2\ngrow_vertical = 2\nexpand_mode = 1\n\n[node name=\"TextureButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_styles/normal = SubResource(\"StyleBoxEmpty_7qdas\")\n\n[node name=\"RoughnessSize\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/Panel\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 7\nanchor_left = 0.5\nanchor_top = 1.0\nanchor_right = 0.5\nanchor_bottom = 1.0\noffset_left = -40.0\noffset_top = -23.0\noffset_right = 40.0\ngrow_horizontal = 2\ngrow_vertical = 0\ntheme_override_colors/font_shadow_color = Color(0, 0, 0, 1)\ntext = \"(0, 0)\"\nlabel_settings = SubResource(\"LabelSettings_2eo1k\")\nhorizontal_alignment = 1\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_constants/separation = 5\n\n[node name=\"ChannelHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nalignment = 1\n\n[node name=\"RoughnessChannelLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox/ChannelHBox\"]\nlayout_mode = 2\ntext = \" Source Channel: \"\n\n[node name=\"RoughnessChannelR\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_pressed = true\nbutton_group = SubResource(\"ButtonGroup_47nos\")\ntext = \"R\"\n\n[node name=\"RoughnessChannelG\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_47nos\")\ntext = \"G\"\n\n[node name=\"RoughnessChannelB\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_47nos\")\ntext = \"B\"\n\n[node name=\"RoughnessChannelA\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_47nos\")\ntext = \"A\"\n\n[node name=\"InvertSmoothCheckBox\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/RoughnessVBox/RoughnessMapHBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"Some engines like Unity use Smoothness maps instead of Roughness maps like Godot Engine. This option inverts the map.\"\ntext = \" Convert Smooth to Rough\"\n\n[node name=\"HSeparator3\" type=\"HSeparator\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nlayout_mode = 2\ntheme_override_constants/separation = 20\n\n[node name=\"AOVBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"AOLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox\"]\nlayout_mode = 2\ntext = \"AO Texture (Opt)\"\nlabel_settings = SubResource(\"LabelSettings_mbo40\")\n\n[node name=\"AOFileHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox\"]\nlayout_mode = 2\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOFileHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"PickButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOFileHBox\"]\nlayout_mode = 2\n\n[node name=\"ClearButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOFileHBox\"]\nlayout_mode = 2\n\n[node name=\"AOMapHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_constants/separation = 30\n\n[node name=\"Panel\" type=\"Panel\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox\"]\ncustom_minimum_size = Vector2(110, 110)\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_cb0xf\")\n\n[node name=\"TextureRect\" type=\"TextureRect\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -50.0\noffset_top = -50.0\noffset_right = 50.0\noffset_bottom = 50.0\ngrow_horizontal = 2\ngrow_vertical = 2\nexpand_mode = 1\n\n[node name=\"TextureButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_styles/normal = SubResource(\"StyleBoxEmpty_7qdas\")\n\n[node name=\"AOSize\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/Panel\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 7\nanchor_left = 0.5\nanchor_top = 1.0\nanchor_right = 0.5\nanchor_bottom = 1.0\noffset_left = -18.5\noffset_top = -23.0\noffset_right = 18.5\ngrow_horizontal = 2\ngrow_vertical = 0\ntheme_override_colors/font_shadow_color = Color(0, 0, 0, 1)\ntext = \"(0, 0)\"\nlabel_settings = SubResource(\"LabelSettings_2eo1k\")\nhorizontal_alignment = 1\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_constants/separation = 5\n\n[node name=\"ChannelHBox\" type=\"HBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/VBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nalignment = 1\n\n[node name=\"AOChannelLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/VBox/ChannelHBox\"]\nlayout_mode = 2\ntext = \" Source Channel: \"\nhorizontal_alignment = 2\n\n[node name=\"AOChannelR\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_pressed = true\nbutton_group = SubResource(\"ButtonGroup_mbo40\")\ntext = \"R\"\n\n[node name=\"AOChannelG\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_mbo40\")\ntext = \"G\"\n\n[node name=\"AOChannelB\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_mbo40\")\ntext = \"B\"\n\n[node name=\"AOChannelA\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox/AOMapHBox/VBox/ChannelHBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntoggle_mode = true\nbutton_group = SubResource(\"ButtonGroup_mbo40\")\ntext = \"A\"\n\n[node name=\"Spacer\" type=\"Control\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Normal_Roughness/VBox/AOVBox\"]\nlayout_mode = 2\n\n[node name=\"Packing\" type=\"PanelContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_mbo40\")\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing\"]\ncustom_minimum_size = Vector2(300, 0)\nlayout_mode = 2\ntheme_override_constants/separation = 10\n\n[node name=\"PackingLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\nlayout_mode = 2\ntext = \"3. Pack Textures\"\nlabel_settings = SubResource(\"LabelSettings_3c3a7\")\n\n[node name=\"HSeparator\" type=\"HSeparator\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\nlayout_mode = 2\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 4\n\n[node name=\"ResizeToggle\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \" Resize Packed Image\"\n\n[node name=\"ResizeOptionBox\" type=\"SpinBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox/VBox\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntooltip_text = \"A value of 0 disables resizing.\"\nmin_value = 128.0\nmax_value = 4096.0\nstep = 128.0\nvalue = 1024.0\n\n[node name=\"GenerateMipmapsCheckBox\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\nbutton_pressed = true\ntext = \"Generate Mipmaps\"\n\n[node name=\"HighQualityCheckBox\" type=\"CheckBox\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Import High Quality\"\n\n[node name=\"Instructions\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\ncustom_minimum_size = Vector2(150, 0)\nlayout_mode = 2\ntext = \"You can pack the albedo, the normal, or both.\"\nhorizontal_alignment = 1\nautowrap_mode = 3\n\n[node name=\"PackButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Pack textures as...\"\n\n[node name=\"StatusLabel\" type=\"Label\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(0, 60)\nlayout_mode = 2\nhorizontal_alignment = 1\nautowrap_mode = 3\n\n[node name=\"CloseButton\" type=\"Button\" parent=\"PanelContainer/ScrollContainer/MarginContainer/FlowContainer/Packing/VBox\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntext = \"Close\"\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/channel_packer_dragdrop.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Channel Packer Dragdropper for Terrain3D\n@tool\nextends Button\n\nsignal dropped\n\nfunc _can_drop_data(p_position, p_data) -> bool:\n\tif typeof(p_data) == TYPE_DICTIONARY:\n\t\tif p_data.files.size() == 1:\n\t\t\tmatch p_data.files[0].get_extension():\n\t\t\t\t\"png\", \"bmp\", \"exr\", \"hdr\", \"jpg\", \"jpeg\", \"tga\", \"svg\", \"webp\", \"ktx\", \"dds\":\n\t\t\t\t\treturn true\n\treturn false\n\nfunc _drop_data(p_position, p_data) -> void:\n\tdropped.emit(p_data.files[0])\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/channel_packer_dragdrop.gd.uid",
    "content": "uid://br45krrqbw8bg\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/channel_packer_import_template.txt",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nmetadata={\n\"imported_formats\": [\"s3tc_bptc\"],\n\"vram_texture\": true\n}\n\n[deps]\n\nsource_file=\"$SOURCE_FILE\"\n\n[params]\n\ncompress/mode=2\ncompress/high_quality=$HIGH_QUALITY\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=2\ncompress/channel_pack=0\nmipmaps/generate=$GENERATE_MIPMAPS\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/directory_setup.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Directory Setup for Terrain3D\nextends Node\n\nconst DIRECTORY_SETUP: String = \"res://addons/terrain_3d/menu/directory_setup.tscn\"\n\nvar plugin: EditorPlugin\nvar dialog: ConfirmationDialog\nvar select_dir_btn: Button\nvar selected_dir_le: LineEdit\nvar editor_file_dialog: EditorFileDialog\n\n\nfunc _init() -> void:\n\teditor_file_dialog = EditorFileDialog.new()\n\teditor_file_dialog.set_filters(PackedStringArray([\"*.res\"]))\n\teditor_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_SAVE_FILE)\n\teditor_file_dialog.access = EditorFileDialog.ACCESS_RESOURCES\n\teditor_file_dialog.ok_button_text = \"Open\"\n\teditor_file_dialog.title = \"Open a folder or file\"\n\teditor_file_dialog.dir_selected.connect(_on_dir_selected)\n\teditor_file_dialog.size = Vector2i(850, 550)\n\teditor_file_dialog.transient = false\n\teditor_file_dialog.exclusive = false\n\teditor_file_dialog.popup_window = true\n\tadd_child(editor_file_dialog)\n\n\nfunc directory_setup_popup() -> void:\n\tdialog = load(DIRECTORY_SETUP).instantiate()\n\tdialog.hide()\n\t\n\t# Nodes\n\tselect_dir_btn = dialog.get_node(\"Margin/VBox/DirHBox/SelectDir\")\n\tselected_dir_le = dialog.get_node(\"Margin/VBox/DirHBox/LineEdit\")\n\n\tif plugin.terrain.data_directory:\n\t\tselected_dir_le.text = plugin.terrain.data_directory\n\t\t\n\t# Icons\n\tplugin.ui.set_button_editor_icon(select_dir_btn, \"Folder\")\n\t\n\t#Signals\n\tselect_dir_btn.pressed.connect(_on_select_file_pressed.bind(EditorFileDialog.FILE_MODE_OPEN_DIR))\n\tdialog.confirmed.connect(_on_close_requested)\n\tdialog.canceled.connect(_on_close_requested)\n\tdialog.get_ok_button().pressed.connect(_on_ok_pressed)\n\n\t# Popup\n\tEditorInterface.popup_dialog_centered(dialog)\n\n\nfunc _on_close_requested() -> void:\n\tdialog.queue_free()\n\tdialog = null\n\n\nfunc _on_select_file_pressed(file_mode: EditorFileDialog.FileMode) -> void:\n\teditor_file_dialog.file_mode = file_mode\n\teditor_file_dialog.popup_centered()\n\n\nfunc _on_dir_selected(path: String) -> void:\n\tselected_dir_le.text = path\n\n\nfunc _on_ok_pressed() -> void:\n\tif not plugin.terrain:\n\t\tpush_error(\"Not connected terrain. Click the Terrain3D node first\")\n\t\treturn\n\tif selected_dir_le.text.is_empty():\n\t\tpush_error(\"No data directory specified\")\n\t\treturn\n\tif not DirAccess.dir_exists_absolute(selected_dir_le.text):\n\t\tpush_error(\"Directory doesn't exist: \", selected_dir_le.text)\n\t\treturn\n\t# Check if directory empty of terrain files\t\t\n\tvar data_found: bool = false\n\tvar files: Array = DirAccess.get_files_at(selected_dir_le.text)\n\tfor file in files:\n\t\tif file.begins_with(\"terrain3d\") || file.ends_with(\".res\"):\n\t\t\tdata_found = true\n\t\t\tbreak\n\n\tprint(\"Setting terrain directory: \", selected_dir_le.text)\n\tplugin.terrain.data_directory = selected_dir_le.text\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/directory_setup.gd.uid",
    "content": "uid://0034ukv2mngn\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/directory_setup.tscn",
    "content": "[gd_scene format=3 uid=\"uid://by3kr2nqbqr67\"]\n\n[node name=\"DirectorySetup\" type=\"ConfirmationDialog\"]\ntitle = \"Terrain3D Data Directory Setup\"\nposition = Vector2i(0, 36)\nsize = Vector2i(750, 330)\nvisible = true\n\n[node name=\"Margin\" type=\"MarginContainer\" parent=\".\"]\noffset_left = 8.0\noffset_top = 8.0\noffset_right = 742.0\noffset_bottom = 281.0\ntheme_override_constants/margin_left = 20\ntheme_override_constants/margin_top = 20\ntheme_override_constants/margin_right = 20\ntheme_override_constants/margin_bottom = 20\n\n[node name=\"VBox\" type=\"VBoxContainer\" parent=\"Margin\"]\nlayout_mode = 2\n\n[node name=\"Instructions\" type=\"Label\" parent=\"Margin/VBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\ntext = \"Terrain3D now stores data in a directory instead of a single file. Each region is stored in a separate file named `terrain3d[-_]##[-_]##.res`. For instance, the region at location (-1, 1) would be named `terrain3d-01_01.res`. Enable Terrain3D / Debug / Show Region Labels for a visual display.\"\nautowrap_mode = 3\n\n[node name=\"DirectoryLabel\" type=\"Label\" parent=\"Margin/VBox\"]\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\ntext = \"\nSpecify the directory to store your data. Any existing region files will be loaded.\"\nautowrap_mode = 3\n\n[node name=\"DirHBox\" type=\"HBoxContainer\" parent=\"Margin/VBox\"]\nlayout_mode = 2\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\"Margin/VBox/DirHBox\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nplaceholder_text = \"Data directory\"\n\n[node name=\"SelectDir\" type=\"Button\" parent=\"Margin/VBox/DirHBox\"]\nlayout_mode = 2\n\n[node name=\"Spacer\" type=\"Control\" parent=\"Margin/VBox\"]\ncustom_minimum_size = Vector2(0, 40)\nlayout_mode = 2\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/terrain_menu.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Menu for Terrain3D\nextends HBoxContainer\n\n\nconst DirectoryWizard: Script = preload(\"res://addons/terrain_3d/menu/directory_setup.gd\")\nconst ChannelPacker: Script = preload(\"res://addons/terrain_3d/menu/channel_packer.gd\")\nconst LodBaker: Script = preload(\"res://addons/terrain_3d/menu/baker.gd\")\n\nvar plugin: EditorPlugin\nvar menu_button: MenuButton = MenuButton.new()\nvar directory_setup: DirectoryWizard = DirectoryWizard.new()\nvar packer: ChannelPacker = ChannelPacker.new()\nvar baker: LodBaker = LodBaker.new()\n\n# These are IDs and order must be consistent with add_item and set_disabled IDs\nenum {\n\tMENU_DIRECTORY_SETUP,\n\tMENU_PACK_TEXTURES,\n\tMENU_SEPARATOR,\n\tMENU_BAKE_ARRAY_MESH,\n\tMENU_BAKE_OCCLUDER,\n\tMENU_SEPARATOR2,\n\tMENU_SET_UP_NAVIGATION,\n\tMENU_BAKE_NAV_MESH,\n}\n\n\nfunc _enter_tree() -> void:\n\tdirectory_setup.plugin = plugin\n\tpacker.plugin = plugin\n\tbaker.plugin = plugin\n\tadd_child(directory_setup)\n\tadd_child(baker)\n\t\n\tmenu_button.text = \"Terrain3D\"\n\tmenu_button.get_popup().add_item(\"Directory Setup...\", \tMENU_DIRECTORY_SETUP)\n\tmenu_button.get_popup().add_item(\"Pack Textures...\", MENU_PACK_TEXTURES)\t\n\tmenu_button.get_popup().add_separator(\"\", MENU_SEPARATOR)\n\tmenu_button.get_popup().add_item(\"Bake ArrayMesh...\", MENU_BAKE_ARRAY_MESH)\n\tmenu_button.get_popup().add_item(\"Bake Occluder3D...\", MENU_BAKE_OCCLUDER)\n\tmenu_button.get_popup().add_separator(\"\", MENU_SEPARATOR2)\n\tmenu_button.get_popup().add_item(\"Set up Navigation...\", MENU_SET_UP_NAVIGATION)\n\tmenu_button.get_popup().add_item(\"Bake NavMesh...\", MENU_BAKE_NAV_MESH)\n\t\n\tmenu_button.get_popup().id_pressed.connect(_on_menu_pressed)\n\tmenu_button.about_to_popup.connect(_on_menu_about_to_popup)\n\tadd_child(menu_button)\n\n\nfunc _on_menu_pressed(p_id: int) -> void:\n\tmatch p_id:\n\t\tMENU_DIRECTORY_SETUP:\n\t\t\tdirectory_setup.directory_setup_popup()\n\t\tMENU_PACK_TEXTURES:\n\t\t\tpacker.pack_textures_popup()\n\t\tMENU_BAKE_ARRAY_MESH:\n\t\t\tbaker.bake_mesh_popup()\n\t\tMENU_BAKE_OCCLUDER:\n\t\t\tbaker.bake_occluder_popup()\n\t\tMENU_SET_UP_NAVIGATION:\n\t\t\tbaker.set_up_navigation_popup()\n\t\tMENU_BAKE_NAV_MESH:\n\t\t\tbaker.bake_nav_mesh()\n\n\nfunc _on_menu_about_to_popup() -> void:\n\tmenu_button.get_popup().set_item_disabled(MENU_DIRECTORY_SETUP, not plugin.terrain)\n\tmenu_button.get_popup().set_item_disabled(MENU_PACK_TEXTURES, not plugin.terrain)\n\tmenu_button.get_popup().set_item_disabled(MENU_BAKE_ARRAY_MESH, not plugin.terrain)\n\tmenu_button.get_popup().set_item_disabled(MENU_BAKE_OCCLUDER, not plugin.terrain)\n\n\tif plugin.terrain:\n\t\tvar nav_regions: Array[NavigationRegion3D] = baker.find_terrain_nav_regions(plugin.terrain)\n\t\tmenu_button.get_popup().set_item_disabled(MENU_BAKE_NAV_MESH, nav_regions.size() == 0)\n\t\tmenu_button.get_popup().set_item_disabled(MENU_SET_UP_NAVIGATION, nav_regions.size() != 0)\n\telif plugin.nav_region:\n\t\tvar terrains: Array[Terrain3D] = baker.find_nav_region_terrains(plugin.nav_region)\n\t\tmenu_button.get_popup().set_item_disabled(MENU_BAKE_NAV_MESH, terrains.size() == 0)\n\t\tmenu_button.get_popup().set_item_disabled(MENU_SET_UP_NAVIGATION, true)\n\telse:\n\t\tmenu_button.get_popup().set_item_disabled(MENU_BAKE_NAV_MESH, true)\n\t\tmenu_button.get_popup().set_item_disabled(MENU_SET_UP_NAVIGATION, true)\n"
  },
  {
    "path": "project/addons/terrain_3d/menu/terrain_menu.gd.uid",
    "content": "uid://3gxvahogxa10\n"
  },
  {
    "path": "project/addons/terrain_3d/plugin.cfg",
    "content": "[plugin]\n\nname=\"Terrain3D\"\ndescription=\"A high performance, editable terrain system for Godot 4.\"\nauthor=\"Cory Petkovsek & Roope Palmroos\"\nversion=\"1.1.0-dev\"\nscript=\"src/editor_plugin.gd\"\n"
  },
  {
    "path": "project/addons/terrain_3d/src/asset_dock.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Asset Dock for Terrain3D\n@tool\nextends PanelContainer\n\nsignal confirmation_closed\nsignal confirmation_confirmed\nsignal confirmation_canceled\n\nconst ES_DOCK_SLOT: String = \"terrain3d/dock/slot\"\nconst ES_DOCK_TILE_SIZE: String = \"terrain3d/dock/tile_size\"\nconst ES_DOCK_FLOATING: String = \"terrain3d/dock/floating\"\nconst ES_DOCK_PINNED: String = \"terrain3d/dock/always_on_top\"\nconst ES_DOCK_WINDOW_POSITION: String = \"terrain3d/dock/window_position\"\nconst ES_DOCK_WINDOW_SIZE: String = \"terrain3d/dock/window_size\"\nconst ES_DOCK_TAB: String = \"terrain3d/dock/tab\"\n\nvar texture_list: ListContainer\nvar mesh_list: ListContainer\nvar current_list: ListContainer\nvar _updating_list: bool\n\nvar placement_opt: OptionButton\nvar floating_btn: Button\nvar pinned_btn: Button\nvar size_slider: HSlider\nvar box: BoxContainer\nvar buttons: BoxContainer\nvar textures_btn: Button\nvar meshes_btn: Button\nvar asset_container: ScrollContainer\nvar confirm_dialog: ConfirmationDialog\nvar _confirmed: bool = false\nvar search_box: TextEdit\nvar search_button: Button\n\n# Used only for editor, so change to single visible/hiddden\nenum {\n\tHIDDEN = -1,\n\tSIDEBAR = 0,\n\tBOTTOM = 1,\n\tWINDOWED = 2,\n}\nvar state: int = HIDDEN\n\nenum {\n\tPOS_LEFT_UL = 0,\n\tPOS_LEFT_BL = 1,\n\tPOS_LEFT_UR = 2,\n\tPOS_LEFT_BR = 3,\n\tPOS_RIGHT_UL = 4,\n\tPOS_RIGHT_BL = 5,\n\tPOS_RIGHT_UR = 6,\n\tPOS_RIGHT_BR = 7,\n\tPOS_BOTTOM = 8,\n\tPOS_MAX = 9,\n}\nvar slot: int = POS_RIGHT_BR\nvar _initialized: bool = false\nvar plugin: EditorPlugin\nvar window: Window\nvar _godot_last_state: Window.Mode = Window.MODE_FULLSCREEN\n\n\nfunc initialize(p_plugin: EditorPlugin) -> void:\n\tif p_plugin:\n\t\tplugin = p_plugin\n\t\n\t_godot_last_state = plugin.godot_editor_window.mode\n\tplacement_opt = $Box/Buttons/PlacementOpt\n\tpinned_btn = $Box/Buttons/Pinned\n\tfloating_btn = $Box/Buttons/Floating\n\tfloating_btn.owner = null # Godot complains about buttons that are reparented\n\tsize_slider = $Box/Buttons/SizeSlider\n\tsize_slider.owner = null\n\tbox = $Box\n\tbuttons = $Box/Buttons\n\ttextures_btn = $Box/Buttons/TexturesBtn\n\tmeshes_btn = $Box/Buttons/MeshesBtn\n\tasset_container = $Box/ScrollContainer\n\tsearch_box = $Box/Buttons/SearchBox\n\tsearch_box.owner = null\n\tsearch_button = $Box/Buttons/SearchBox/SearchButton\n\t\n\ttexture_list = ListContainer.new()\n\ttexture_list.name = \"TextureList\"\n\ttexture_list.plugin = plugin\n\ttexture_list.type = Terrain3DAssets.TYPE_TEXTURE\n\tasset_container.add_child(texture_list, true)\n\tmesh_list = ListContainer.new()\n\tmesh_list.name = \"MeshList\"\n\tmesh_list.plugin = plugin\n\tmesh_list.type = Terrain3DAssets.TYPE_MESH\n\tmesh_list.visible = false\n\tasset_container.add_child(mesh_list, true)\n\tcurrent_list = texture_list\n\n\tload_editor_settings()\n\n\t# Connect signals\n\tresized.connect(update_layout)\n\ttextures_btn.pressed.connect(_on_textures_pressed)\n\tmeshes_btn.pressed.connect(_on_meshes_pressed)\n\tplacement_opt.item_selected.connect(set_slot)\n\tfloating_btn.pressed.connect(make_dock_float)\n\tpinned_btn.toggled.connect(_on_pin_changed)\n\tpinned_btn.visible = ( window != null )\n\tpinned_btn.owner = null\n\tsize_slider.value_changed.connect(_on_slider_changed)\n\tplugin.ui.toolbar.tool_changed.connect(_on_tool_changed)\n\n\tmeshes_btn.add_theme_font_size_override(\"font_size\", 16 * EditorInterface.get_editor_scale())\n\ttextures_btn.add_theme_font_size_override(\"font_size\", 16 * EditorInterface.get_editor_scale())\n\n\t_initialized = true\n\tupdate_dock()\n\tupdate_layout()\n\n\nfunc _ready() -> void:\n\tif not _initialized:\n\t\treturn\n\t\t\n\t# Setup styles\n\tset(\"theme_override_styles/panel\", get_theme_stylebox(\"panel\", \"Panel\"))\n\t# Avoid saving icon resources in tscn when editing w/ a tool script\n\tif EditorInterface.get_edited_scene_root() != self:\n\t\tpinned_btn.icon = get_theme_icon(\"Pin\", \"EditorIcons\")\n\t\tpinned_btn.text = \"\"\n\t\tfloating_btn.icon = get_theme_icon(\"MakeFloating\", \"EditorIcons\")\n\t\tfloating_btn.text = \"\"\n\t\tsearch_button.icon = get_theme_icon(\"Search\", \"EditorIcons\")\n\t\n\tsearch_box.text_changed.connect(_on_search_text_changed)\n\tsearch_button.pressed.connect(_on_search_button_pressed)\n\t\n\tconfirm_dialog = ConfirmationDialog.new()\n\tadd_child(confirm_dialog, true)\n\tconfirm_dialog.hide()\n\tconfirm_dialog.confirmed.connect(func(): _confirmed = true; \\\n\t\temit_signal(\"confirmation_closed\"); \\\n\t\temit_signal(\"confirmation_confirmed\") )\n\tconfirm_dialog.canceled.connect(func(): _confirmed = false; \\\n\t\temit_signal(\"confirmation_closed\"); \\\n\t\temit_signal(\"confirmation_canceled\") )\n\n\nfunc _gui_input(p_event: InputEvent) -> void:\n\tif p_event is InputEventMouseButton:\n\t\tif search_box.has_focus():\n\t\t\tif plugin.debug:\n\t\t\t\tprint(\"Terrain3DAssetDock: _on_box_gui_input: search_box releasing focus\")\n\t\t\tsearch_box.release_focus()\n\n\n## Dock placement\n\n\nfunc set_slot(p_slot: int) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: set_slot: \", p_slot)\n\tp_slot = clamp(p_slot, 0, POS_MAX-1)\n\t\n\tif slot != p_slot:\n\t\tslot = p_slot\n\t\tplacement_opt.selected = slot\n\t\tsave_editor_settings()\n\t\tplugin.select_terrain()\n\t\tupdate_dock()\n\n\nfunc remove_dock(p_force: bool = false) -> void:\n\tif state == SIDEBAR:\n\t\tplugin.remove_control_from_docks(self)\n\t\tstate = HIDDEN\n\n\telif state == BOTTOM:\n\t\tplugin.remove_control_from_bottom_panel(self)\n\t\tstate = HIDDEN\n\n\t# If windowed and destination is not window or final exit, otherwise leave\n\telif state == WINDOWED and p_force and window:\n\t\tvar parent: Node = get_parent()\n\t\tif parent:\n\t\t\tparent.remove_child(self)\n\t\tplugin.godot_editor_window.mouse_entered.disconnect(_on_godot_window_entered)\n\t\tplugin.godot_editor_window.focus_entered.disconnect(_on_godot_focus_entered)\n\t\tplugin.godot_editor_window.focus_exited.disconnect(_on_godot_focus_exited)\n\t\twindow.hide()\n\t\twindow.queue_free()\n\t\twindow = null\n\t\tfloating_btn.button_pressed = false\n\t\tfloating_btn.visible = true\n\t\tpinned_btn.visible = false\n\t\tplacement_opt.visible = true\n\t\tstate = HIDDEN\n\t\tupdate_dock() # return window to side/bottom\n\n\nfunc update_dock() -> void:\n\tif not _initialized or window:\n\t\treturn\n\n\tupdate_assets()\n\n\t# Move dock to new destination\n\tremove_dock()\n\t# Sidebar\n\tif slot < POS_BOTTOM:\n\t\tstate = SIDEBAR\n\t\tplugin.add_control_to_dock(slot, self)\n\t# Bottom\n\telif slot == POS_BOTTOM:\n\t\tstate = BOTTOM\n\t\tplugin.add_control_to_bottom_panel(self, \"Terrain3D\")\n\t\tplugin.make_bottom_panel_item_visible(self)\n\n\nfunc update_layout() -> void:\n\tif plugin.debug > 1:\n\t\tprint(\"Terrain3DAssetDock: update_layout\")\t\n\tif not _initialized:\n\t\treturn\n\n\t# Detect if we have a new window from Make floating, grab it so we can free it properly\n\tif not window and get_parent() and get_parent().get_parent() is Window:\n\t\twindow = get_parent().get_parent()\n\t\tmake_dock_float()\n\t\treturn # Will call this function again upon display\n\n\t# Vertical layout: buttons on top\n\tif size.x < 500 or ( not window and slot < POS_BOTTOM ):\n\t\tbox.vertical = true\n\t\tbuttons.vertical = false\n\t\tsearch_box.reparent(box)\n\t\tbox.move_child(search_box, 1)\n\t\tsize_slider.reparent(box)\n\t\tbox.move_child(size_slider, 2)\n\t\tfloating_btn.reparent(buttons)\n\t\tpinned_btn.reparent(buttons)\n\telse:\n\t# Wide layout: buttons on left\n\t\tbox.vertical = false\n\t\tbuttons.vertical = true\n\t\tsearch_box.reparent(buttons)\n\t\tbuttons.move_child(search_box, 0)\n\t\tsize_slider.reparent(buttons)\n\t\tbuttons.move_child(size_slider, 4)\n\t\tfloating_btn.reparent(box)\n\t\tpinned_btn.reparent(box)\n\n\tsave_editor_settings()\n\n\nfunc set_selected_by_asset_id(p_id: int) -> void:\n\tsearch_box.text = \"\"\n\t_on_search_text_changed()\n\tcurrent_list.set_selected_id(p_id)\n\t\n\t\nfunc _on_search_text_changed() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: _on_search_text_changed: \", search_box.text)\n\tsearch_box.text = search_box.text.strip_escapes()\n\tvar len: int = search_box.text.length()\n\tif len > 0:\n\t\tsearch_box.set_caret_column(len)\n\t\tsearch_button.icon = get_theme_icon(\"Close\", \"EditorIcons\")\n\telse:\n\t\tsearch_button.icon = get_theme_icon(\"Search\", \"EditorIcons\")\n\t\t\n\tmesh_list.search_text = search_box.text\n\ttexture_list.search_text = search_box.text\n\tcurrent_list.update_asset_list()\n\tcurrent_list.set_selected_id(0)\n\n\nfunc _on_search_button_pressed() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: _on_search_button_pressed\")\n\tif search_box.text.length() > 0:\n\t\tsearch_box.text = \"\"\n\t\t_on_search_text_changed()\n\telse:\n\t\tif plugin.debug:\n\t\t\tprint(\"Terrain3DAssetDock: _on_search_button_pressed: Search box grabbing focus\")\n\t\tsearch_box.grab_focus()\n\n\n## Dock Button handlers\n\n\nfunc _on_pin_changed(toggled: bool) -> void:\n\tif window:\n\t\twindow.always_on_top = pinned_btn.button_pressed\n\tsave_editor_settings()\n\n\nfunc _on_slider_changed(value: float) -> void:\n\t# Set both lists so they match\n\tif texture_list:\n\t\ttexture_list.set_entry_width(value)\n\tif mesh_list:\n\t\tmesh_list.set_entry_width(value)\n\tsave_editor_settings()\n\t# Hack to trigger ScrollContainer::_reposition_children() to update size of scroll bar handle\n\tasset_container.layout_direction = Control.LAYOUT_DIRECTION_LTR\n\tasset_container.layout_direction = Control.LAYOUT_DIRECTION_INHERITED\n\n\nfunc _on_textures_pressed() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: _on_textures_pressed\")\n\tif _updating_list or current_list == texture_list:\n\t\treturn\n\t_updating_list = true\n\tcurrent_list = texture_list\n\ttexture_list.visible = true\n\tmesh_list.visible = false\n\ttextures_btn.set_pressed_no_signal(true)\n\tmeshes_btn.set_pressed_no_signal(false)\n\ttexture_list.update_asset_list()\n\tif plugin.is_terrain_valid():\n\t\tEditorInterface.edit_node(plugin.terrain)\n\tsave_editor_settings()\n\t_updating_list = false\n\n\nfunc _on_meshes_pressed() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: _on_meshes_pressed\")\n\tif _updating_list or current_list == mesh_list:\n\t\treturn\n\t_updating_list = true\n\tcurrent_list = mesh_list\n\tmesh_list.visible = true\n\ttexture_list.visible = false\n\tmeshes_btn.set_pressed_no_signal(true)\n\ttextures_btn.set_pressed_no_signal(false)\n\tmesh_list.update_asset_list()\n\tif plugin.is_terrain_valid():\n\t\tEditorInterface.edit_node(plugin.terrain)\n\tsave_editor_settings()\n\t_updating_list = false\n\n\nfunc _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: _on_tool_changed: \", p_tool, \", \", p_operation)\n\tremove_all_highlights()\n\tif p_tool == Terrain3DEditor.INSTANCER:\n\t\t_on_meshes_pressed()\n\telif p_tool in [ Terrain3DEditor.TEXTURE, Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS ]:\n\t\t_on_textures_pressed()\n\n\n## Update Dock Contents\n\n\nfunc update_assets() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DAssetDock: update_assets: \", plugin.terrain.assets if plugin.terrain else \"\")\n\tif not _initialized:\n\t\treturn\n\t\n\t# Verify signals to individual lists\n\tif plugin.is_terrain_valid() and plugin.terrain.assets:\n\t\tif not plugin.terrain.assets.textures_changed.is_connected(texture_list.update_asset_list):\n\t\t\tplugin.terrain.assets.textures_changed.connect(texture_list.update_asset_list)\n\t\tif not plugin.terrain.assets.meshes_changed.is_connected(mesh_list.update_asset_list):\n\t\t\tplugin.terrain.assets.meshes_changed.connect(mesh_list.update_asset_list)\n\n\tcurrent_list.update_asset_list()\n\n\nfunc remove_all_highlights():\n\tif not plugin.terrain:\n\t\treturn\n\tfor i: int in texture_list.entries.size():\n\t\tvar resource: Terrain3DTextureAsset = texture_list.entries[i].resource\n\t\tif resource and resource.is_highlighted():\n\t\t\tresource.set_highlighted(false)\n\tfor i: int in mesh_list.entries.size():\n\t\tvar resource: Terrain3DMeshAsset = mesh_list.entries[i].resource\n\t\tif resource and resource.is_highlighted():\n\t\t\tresource.set_highlighted(false)\n\n\n## Window Management\n\n\nfunc make_dock_float() -> void:\n\t# If not already created (eg from editor panel 'Make Floating' button)\t\n\tif not window:\n\t\tremove_dock()\n\t\tcreate_window()\n\n\tstate = WINDOWED\n\tvisible = true # Asset dock contents are hidden when popping out of the bottom!\n\tpinned_btn.visible = true\n\tfloating_btn.visible = false\n\tplacement_opt.visible = false\n\twindow.title = \"Terrain3D Asset Dock\"\n\twindow.always_on_top = pinned_btn.button_pressed\n\twindow.close_requested.connect(remove_dock.bind(true))\n\twindow.window_input.connect(_on_window_input)\n\twindow.focus_exited.connect(save_editor_settings)\n\twindow.mouse_exited.connect(save_editor_settings)\n\twindow.size_changed.connect(save_editor_settings)\n\tplugin.godot_editor_window.mouse_entered.connect(_on_godot_window_entered)\n\tplugin.godot_editor_window.focus_entered.connect(_on_godot_focus_entered)\n\tplugin.godot_editor_window.focus_exited.connect(_on_godot_focus_exited)\n\tplugin.godot_editor_window.grab_focus()\n\tupdate_assets()\n\tsave_editor_settings()\n\n\nfunc create_window() -> void:\n\twindow = Window.new()\n\twindow.wrap_controls = true\n\tvar mc := MarginContainer.new()\n\tmc.set_anchors_preset(PRESET_FULL_RECT, false)\n\tmc.add_child(self, true)\n\twindow.add_child(mc, true)\n\twindow.set_transient(false)\n\twindow.set_size(plugin.get_setting(ES_DOCK_WINDOW_SIZE, Vector2i(512, 512)))\n\twindow.set_position(plugin.get_setting(ES_DOCK_WINDOW_POSITION, Vector2i(704, 284)))\n\tplugin.add_child(window, true)\n\twindow.show()\n\n\nfunc clamp_window_position() -> void:\n\tif window and window.visible:\n\t\tvar bounds: Vector2i\n\t\tif EditorInterface.get_editor_settings().get_setting(\"interface/editor/single_window_mode\"):\n\t\t\tbounds = EditorInterface.get_base_control().size\n\t\telse:\n\t\t\tbounds = DisplayServer.screen_get_position(window.current_screen)\n\t\t\tbounds += DisplayServer.screen_get_size(window.current_screen)\n\t\tvar margin: int = 40\n\t\twindow.position.x = clamp(window.position.x, -window.size.x + 2*margin, bounds.x - margin)\n\t\twindow.position.y = clamp(window.position.y, 25, bounds.y - margin)\n\n\nfunc _on_window_input(event: InputEvent) -> void:\n\t# Capture CTRL+S when doc focused to save scene\n\tif event is InputEventKey and event.keycode == KEY_S and event.pressed and event.is_command_or_control_pressed():\n\t\tsave_editor_settings()\n\t\tEditorInterface.save_scene()\n\n\nfunc _on_godot_window_entered() -> void:\n\tif plugin.debug > 1:\n\t\tprint(\"Terrain3DAssetDock: _on_godot_window_entered\")\n\tif is_instance_valid(window) and window.has_focus():\n\t\tplugin.godot_editor_window.grab_focus()\n\n\nfunc _on_godot_focus_entered() -> void:\n\tif plugin.debug > 1:\n\t\tprint(\"Terrain3DAssetDock: _on_godot_focus_entered\")\n\t# If asset dock is windowed, and Godot was minimized, and now is not, restore asset dock window\n\tif is_instance_valid(window):\n\t\tif _godot_last_state == Window.MODE_MINIMIZED and plugin.godot_editor_window.mode != Window.MODE_MINIMIZED:\n\t\t\twindow.show()\n\t\t\t_godot_last_state = plugin.godot_editor_window.mode\n\t\t\tplugin.godot_editor_window.grab_focus()\n\n\nfunc _on_godot_focus_exited() -> void:\n\tif plugin.debug > 1:\n\t\tprint(\"Terrain3DAssetDock: _on_godot_focus_exited\")\n\tif is_instance_valid(window) and plugin.godot_editor_window.mode == Window.MODE_MINIMIZED:\n\t\twindow.hide()\n\t\t_godot_last_state = plugin.godot_editor_window.mode\n\n\n## Manage Editor Settings\n\n\nfunc load_editor_settings() -> void:\n\tfloating_btn.button_pressed = plugin.get_setting(ES_DOCK_FLOATING, false)\n\tpinned_btn.button_pressed = plugin.get_setting(ES_DOCK_PINNED, true)\n\tsize_slider.value = plugin.get_setting(ES_DOCK_TILE_SIZE, 90)\n\t_on_slider_changed(size_slider.value)\n\tset_slot(plugin.get_setting(ES_DOCK_SLOT, POS_BOTTOM))\n\tif floating_btn.button_pressed:\n\t\tmake_dock_float()\n\t# TODO Don't save tab until thumbnail generation more reliable\n\t#if plugin.get_setting(ES_DOCK_TAB, 0) == 1:\n\t#\t_on_meshes_pressed()\n\n\nfunc save_editor_settings() -> void:\n\tif not _initialized:\n\t\treturn\n\tclamp_window_position()\n\tplugin.set_setting(ES_DOCK_SLOT, slot)\n\tplugin.set_setting(ES_DOCK_TILE_SIZE, size_slider.value)\n\tplugin.set_setting(ES_DOCK_FLOATING, floating_btn.button_pressed)\n\tplugin.set_setting(ES_DOCK_PINNED, pinned_btn.button_pressed)\n\t# TODO Don't save tab until thumbnail generation more reliable\n\t# plugin.set_setting(ES_DOCK_TAB, 0 if current_list == texture_list else 1)\n\tif window:\n\t\tplugin.set_setting(ES_DOCK_WINDOW_SIZE, window.size)\n\t\tplugin.set_setting(ES_DOCK_WINDOW_POSITION, window.position)\n\n\n##############################################################\n## class ListContainer\n##############################################################\n\n\t\nclass ListContainer extends Container:\n\tvar plugin: EditorPlugin\n\tvar type := Terrain3DAssets.TYPE_TEXTURE\n\tvar entries: Array[ListEntry]\n\tvar selected_id: int = 0\n\tvar height: float = 0.\n\tvar width: float = 90.\n\tvar focus_style: StyleBox\n\tvar _clearing_resource: bool = false\n\tvar search_text: String = \"\"\n\n\t\n\tfunc _ready() -> void:\n\t\tset_v_size_flags(SIZE_EXPAND_FILL)\n\t\tset_h_size_flags(SIZE_EXPAND_FILL)\n\t\tadd_theme_color_override(\"font_color\", Color.WHITE)\n\t\tadd_theme_color_override(\"font_shadow_color\", Color.BLACK)\n\t\tadd_theme_constant_override(\"shadow_offset_x\", 1)\n\t\tadd_theme_constant_override(\"shadow_offset_y\", 1)\n\n\n\tfunc clear() -> void:\n\t\tfor e in entries:\n\t\t\te.get_parent().remove_child(e)\n\t\t\te.queue_free()\n\t\tentries.clear()\n\n\n\tfunc update_asset_list() -> void:\n\t\tif plugin.debug:\n\t\t\tprint(\"Terrain3DListContainer \", name, \": update_asset_list\")\n\t\tclear()\n\t\t\n\t\t# Grab terrain\n\t\tvar t: Terrain3D = plugin.get_terrain()\n\t\tif not (t and t.assets):\n\t\t\treturn\n\t\t\n\t\tif type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\tvar texture_count: int = t.assets.get_texture_count()\n\t\t\tfor i in texture_count:\n\t\t\t\tvar texture: Terrain3DTextureAsset = t.assets.get_texture_asset(i)\n\t\t\t\tadd_item(texture)\n\t\t\tif texture_count < Terrain3DAssets.MAX_TEXTURES:\n\t\t\t\tadd_item()\n\t\telse:\n\t\t\tif plugin.terrain:\n\t\t\t\tplugin.terrain.assets.create_mesh_thumbnails()\n\t\t\tvar mesh_count: int = t.assets.get_mesh_count()\n\t\t\tfor i in mesh_count:\n\t\t\t\tvar mesh: Terrain3DMeshAsset = t.assets.get_mesh_asset(i)\n\t\t\t\tadd_item(mesh)\n\t\t\tif mesh_count < Terrain3DAssets.MAX_MESHES:\n\t\t\t\tadd_item()\n\t\tset_selected_id(selected_id)\n\n\n\tfunc add_item(p_resource: Resource = null) -> void:\n\t\tvar entry: ListEntry = ListEntry.new()\n\t\tentry.focus_style = focus_style\n\t\tentry.set_edited_resource(p_resource)\n\t\tif not entry.get_resource_name().containsn(search_text) and not search_text == \"\":\n\t\t\treturn\n\n\t\tvar res_id: int = p_resource.id if p_resource else entries.size()\n\t\tentry.hovered.connect(_on_resource_hovered.bind(res_id))\n\t\tentry.clicked.connect(clicked_id.bind(entries.size()))\n\t\tentry.inspected.connect(_on_resource_inspected)\n\t\tentry.changed.connect(_on_resource_changed.bind(res_id))\n\t\tentry.type = type\n\t\tadd_child(entry, true)\n\t\tentries.push_back(entry)\n\t\t\n\t\tif p_resource:\n\t\t\tif not p_resource.id_changed.is_connected(set_selected_after_swap):\n\t\t\t\tp_resource.id_changed.connect(set_selected_after_swap)\n\n\n\tfunc _on_resource_hovered(p_id: int):\n\t\tif type == Terrain3DAssets.TYPE_MESH:\n\t\t\tif plugin.terrain:\n\t\t\t\tplugin.terrain.assets.create_mesh_thumbnails(p_id, Vector2i(512, 512), true)\n\n\t\n\tfunc set_selected_after_swap(p_type: Terrain3DAssets.AssetType, p_old_id: int, p_new_id: int) -> void:\n\t\tEditorInterface.mark_scene_as_unsaved()\n\t\tset_selected_id(clamp(p_new_id, 0, entries.size() - 2))\n\n\n\tfunc clicked_id(p_id: int) -> void:\n\t\t# Select Tool if clicking an asset\n\t\tplugin.select_terrain()\n\t\tif type == Terrain3DAssets.TYPE_TEXTURE and \\\n\t\t\t\tnot plugin.editor.get_tool() in [ Terrain3DEditor.TEXTURE, Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS ]:\n\t\t\tplugin.ui.toolbar.change_tool(\"PaintTexture\")\n\t\telif type == Terrain3DAssets.TYPE_MESH and plugin.editor.get_tool() != Terrain3DEditor.INSTANCER:\n\t\t\tplugin.ui.toolbar.change_tool(\"InstanceMeshes\")\n\t\tset_selected_id(p_id)\n\n\n\tfunc set_selected_id(p_id: int) -> void:\n\t\t# \"Add new\" is the final entry only when search box is blank\n\t\tvar max_id: int = max(0, entries.size() - (1 if search_text else 2))\n\t\tif plugin.debug:\n\t\t\tprint(\"Terrain3DListContainer \", name, \": set_selected_id: \", selected_id, \" to \", clamp(p_id, 0, max_id))\n\t\tselected_id = clamp(p_id, 0, max_id)\n\t\tfor i in entries.size():\n\t\t\tvar entry: ListEntry = entries[i]\n\t\t\tentry.set_selected(i == selected_id)\n\t\tplugin.ui._on_setting_changed()\n\n\n\tfunc get_selected_asset_id() -> int:\n\t\t# \"Add new\" is the final entry only when search box is blank\n\t\tvar max_id: int = max(0, entries.size() - (1 if search_text else 2))\n\t\tvar id: int = clamp(selected_id, 0, max_id)\n\t\tif plugin.debug:\n\t\t\tprint(\"Terrain3DListContainer \", name, \": get_selected_asset_id: selected_id: \", selected_id, \", clamped: \", id, \", entries: \", entries.size())\n\t\tif id >= entries.size():\n\t\t\treturn 0\n\t\tvar res: Resource = entries[id].resource\n\t\tif not res:\n\t\t\treturn 0\n\t\tif type == Terrain3DAssets.AssetType.TYPE_MESH:\n\t\t\treturn (res as Terrain3DMeshAsset).id\n\t\telse:\n\t\t\treturn (res as Terrain3DTextureAsset).id\n\n\n\tfunc _on_resource_inspected(p_resource: Resource) -> void:\n\t\tawait get_tree().process_frame\n\t\tEditorInterface.edit_resource(p_resource)\n\t\n\t\n\tfunc _on_resource_changed(p_resource: Resource, p_id: int) -> void:\n\t\tif not p_resource and _clearing_resource:\n\t\t\treturn\n\t\tif not p_resource:\n\t\t\tif plugin.debug:\n\t\t\t\tprint(\"Terrain3DListContainer \", name, \": _on_resource_changed: removing asset ID: \", p_id)\n\t\t\t_clearing_resource = true\n\t\t\tvar asset_dock: Control = get_parent().get_parent().get_parent()\n\t\t\tif type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\t\tasset_dock.confirm_dialog.dialog_text = \"Are you sure you want to clear this texture?\"\n\t\t\telse:\n\t\t\t\tasset_dock.confirm_dialog.dialog_text = \"Are you sure you want to clear this mesh and delete all instances?\"\n\t\t\tasset_dock.confirm_dialog.popup_centered()\n\t\t\tawait asset_dock.confirmation_closed\n\t\t\tif not asset_dock._confirmed:\n\t\t\t\tupdate_asset_list()\n\t\t\t\t_clearing_resource = false\n\t\t\t\treturn\n\t\t\t\n\t\tif not plugin.is_terrain_valid():\n\t\t\tplugin.select_terrain()\n\t\t\tawait get_tree().process_frame\n\n\t\tif plugin.is_terrain_valid():\n\t\t\tif type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\t\tplugin.terrain.assets.set_texture_asset(p_id, p_resource)\n\t\t\telse:\n\t\t\t\tplugin.terrain.assets.set_mesh_asset(p_id, p_resource)\n\n\t\t\t# If removing an entry, clear inspector\n\t\t\tif not p_resource:\n\t\t\t\tEditorInterface.inspect_object(null)\t\t\t\n\t\t_clearing_resource = false\n\n\n\tfunc set_entry_width(value: float) -> void:\n\t\twidth = clamp(value, 90., 512.)\n\t\tredraw()\n\n\n\tfunc get_entry_width() -> float:\n\t\treturn width\n\t\n\n\tfunc redraw() -> void:\n\t\theight = 0\n\t\tvar id: int = 0\n\t\tvar separation: float = 2.\n\t\tvar columns: int = 3\n\t\tcolumns = clamp(size.x / width, 1, 100)\n\t\tvar tile_size: Vector2 = Vector2(width, width) - Vector2(separation, separation)\n\t\tvar count_font_size = clamp(tile_size.x/11, 11, 16)\n\t\tvar name_font_size = clamp(tile_size.x/12, 12, 18)\n\t\tfor c in get_children():\n\t\t\tif is_instance_valid(c):\n\t\t\t\tc.size = tile_size\n\t\t\t\tc.position = Vector2(id % columns, id / columns) * width + \\\n\t\t\t\t\tVector2(separation / columns, separation / columns)\n\t\t\t\theight = max(height, c.position.y + width)\n\t\t\t\tid += 1\n\t\t\t\tif type == Terrain3DAssets.TYPE_MESH:\n\t\t\t\t\tc.count_label.add_theme_font_size_override(\"font_size\", count_font_size)\n\t\t\t\tc.name_label.add_theme_font_size_override(\"font_size\", name_font_size)\n\n\n\t# Needed to enable ScrollContainer scroll bar\n\tfunc _get_minimum_size() -> Vector2:\n\t\treturn Vector2(0, height)\n\n\t\t\n\tfunc _notification(p_what) -> void:\n\t\tif p_what == NOTIFICATION_SORT_CHILDREN:\n\t\t\tredraw()\n\n\n##############################################################\n## class ListEntry\n##############################################################\n\n\nclass ListEntry extends MarginContainer:\n\tsignal hovered()\n\tsignal clicked()\n\tsignal changed(resource: Resource)\n\tsignal inspected(resource: Resource)\n\t\n\tvar resource: Resource\n\tvar type := Terrain3DAssets.TYPE_TEXTURE\n\tvar _thumbnail: Texture2D\n\tvar drop_data: bool = false\n\tvar is_hovered: bool = false\n\tvar is_selected: bool = false\n\tvar is_highlighted: bool = false\n\t\n\tvar name_label: Label\n\tvar count_label: Label\n\tvar button_row: FlowContainer\n\tvar button_enabled: TextureButton\n\tvar button_highlight: TextureButton\n\tvar button_edit: TextureButton\n\tvar spacer: Control \n\tvar button_clear: TextureButton\n\n\t@onready var focus_style: StyleBox = get_theme_stylebox(\"focus\", \"Button\").duplicate()\n\t@onready var background: StyleBox = get_theme_stylebox(\"pressed\", \"Button\")\n\t@onready var clear_icon: Texture2D = get_theme_icon(\"Close\", \"EditorIcons\")\n\t@onready var edit_icon: Texture2D = get_theme_icon(\"Edit\", \"EditorIcons\")\n\t@onready var enabled_icon: Texture2D = get_theme_icon(\"GuiVisibilityVisible\", \"EditorIcons\")\n\t@onready var disabled_icon: Texture2D = get_theme_icon(\"GuiVisibilityHidden\", \"EditorIcons\")\n\t@onready var highlight_icon: Texture2D = get_theme_icon(\"PreviewSun\", \"EditorIcons\")\n\t@onready var add_icon: Texture2D = get_theme_icon(\"Add\", \"EditorIcons\")\n\n\n\tfunc _ready() -> void:\n\t\tname = \"ListEntry\"\n\t\tcustom_minimum_size = Vector2i(86., 86.)\n\t\tmouse_filter = Control.MOUSE_FILTER_PASS\n\t\tadd_theme_constant_override(\"margin_top\", 5)\n\t\tadd_theme_constant_override(\"margin_left\", 5)\n\t\tadd_theme_constant_override(\"margin_right\", 5)\n\n\t\tif resource:\n\t\t\tis_highlighted = resource.is_highlighted()\n\n\t\tsetup_buttons()\n\t\tsetup_label()\n\t\tsetup_count_label()\n\t\tfocus_style.set_border_width_all(2)\n\t\tfocus_style.set_border_color(Color(1, 1, 1, .67))\n\n\n\tfunc setup_buttons() -> void:\n\t\tdestroy_buttons()\n\t\t\n\t\tbutton_row = FlowContainer.new()\n\t\tbutton_enabled = TextureButton.new() \n\t\tbutton_highlight = TextureButton.new() \n\t\tbutton_edit = TextureButton.new() \n\t\tspacer = Control.new()\n\t\tbutton_clear = TextureButton.new()\n\t\t\n\t\tvar icon_size: Vector2 = Vector2(12, 12)\n\t\t\n\t\tbutton_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL\n\t\tbutton_row.alignment = FlowContainer.ALIGNMENT_CENTER\n\t\tbutton_row.mouse_filter = Control.MOUSE_FILTER_PASS\n\t\tadd_child(button_row, true)\n\n\t\tif type == Terrain3DAssets.TYPE_MESH:\n\t\t\tbutton_enabled.set_texture_normal(enabled_icon)\n\t\t\tbutton_enabled.set_texture_pressed(disabled_icon)\n\t\t\tbutton_enabled.set_custom_minimum_size(icon_size)\n\t\t\tbutton_enabled.set_h_size_flags(Control.SIZE_SHRINK_END)\n\t\t\tbutton_enabled.set_visible(resource != null)\n\t\t\tbutton_enabled.tooltip_text = \"Enable Instances\"\n\t\t\tbutton_enabled.toggle_mode = true\n\t\t\tbutton_enabled.mouse_filter = Control.MOUSE_FILTER_PASS\n\t\t\tbutton_enabled.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND\n\t\t\tbutton_enabled.pressed.connect(_on_enable)\n\t\t\tbutton_row.add_child(button_enabled, true)\n\t\t\t\n\t\tbutton_highlight.set_texture_normal(highlight_icon)\n\t\tbutton_highlight.set_custom_minimum_size(icon_size)\n\t\tbutton_highlight.set_h_size_flags(Control.SIZE_SHRINK_END)\n\t\tbutton_highlight.set_visible(resource != null)\n\t\tbutton_highlight.tooltip_text = \"Highlight \" + ( \"Instances\" if type == Terrain3DAssets.TYPE_MESH else \"Texture\" )\n\t\tbutton_highlight.toggle_mode = true\n\t\tbutton_highlight.mouse_filter = Control.MOUSE_FILTER_PASS\n\t\tbutton_highlight.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND\n\t\tbutton_highlight.set_pressed_no_signal(is_highlighted)\n\t\tbutton_highlight.pressed.connect(_on_highlight)\n\t\tbutton_row.add_child(button_highlight, true)\n\t\t\n\t\tbutton_edit.set_texture_normal(edit_icon)\n\t\tbutton_edit.set_custom_minimum_size(icon_size)\n\t\tbutton_edit.set_h_size_flags(Control.SIZE_SHRINK_END)\n\t\tbutton_edit.set_visible(resource != null)\n\t\tbutton_edit.tooltip_text = \"Edit Asset\"\n\t\tbutton_edit.mouse_filter = Control.MOUSE_FILTER_PASS\n\t\tbutton_edit.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND\n\t\tbutton_edit.pressed.connect(_on_edit)\n\t\tbutton_row.add_child(button_edit, true)\n\n\t\tspacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL\n\t\tspacer.mouse_filter = Control.MOUSE_FILTER_PASS\n\t\tbutton_row.add_child(spacer, true)\n\t\t\n\t\tbutton_clear.set_texture_normal(clear_icon)\n\t\tbutton_clear.set_custom_minimum_size(icon_size)\n\t\tbutton_clear.set_h_size_flags(Control.SIZE_SHRINK_END)\n\t\tbutton_clear.set_visible(resource != null)\n\t\tbutton_clear.tooltip_text = \"Clear Asset\"\n\t\tbutton_clear.mouse_filter = Control.MOUSE_FILTER_PASS\n\t\tbutton_clear.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND\n\t\tbutton_clear.pressed.connect(_on_clear)\n\t\tbutton_row.add_child(button_clear, true)\n\t\t\n\n\tfunc destroy_buttons() -> void:\n\t\tif button_row:\n\t\t\tbutton_row.free()\n\t\t\tbutton_row = null\n\t\tif button_enabled:\n\t\t\tbutton_enabled.free()\n\t\t\tbutton_enabled = null\n\t\tif button_highlight:\n\t\t\tbutton_highlight.free()\n\t\t\tbutton_highlight = null\n\t\tif button_edit:\n\t\t\tbutton_edit.free()\n\t\t\tbutton_edit = null\n\t\tif spacer:\n\t\t\tspacer.free()\n\t\t\tspacer = null\n\t\tif button_clear:\n\t\t\tbutton_clear.free()\n\t\t\tbutton_clear = null\n\n\n\tfunc get_resource_name() -> StringName:\n\t\tif resource:\n\t\t\tif resource is Terrain3DMeshAsset:\n\t\t\t\treturn (resource as Terrain3DMeshAsset).get_name()\n\t\t\telif resource is Terrain3DTextureAsset:\n\t\t\t\treturn (resource as Terrain3DTextureAsset).get_name()\n\t\treturn \"\"\n\n\n\tfunc setup_label() -> void:\n\t\tname_label = Label.new()\n\t\tname_label.name = \"MeshLabel\"\n\t\tname_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER\n\t\tname_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER\n\t\tname_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL\n\t\tname_label.size_flags_vertical = Control.SIZE_EXPAND_FILL\n\t\tname_label.add_theme_font_size_override(\"font_size\", 14)\n\t\tname_label.add_theme_color_override(\"font_color\", Color.WHITE)\n\t\tname_label.add_theme_color_override(\"font_shadow_color\", Color.BLACK)\n\t\tname_label.add_theme_constant_override(\"shadow_offset_x\", 1)\n\t\tname_label.add_theme_constant_override(\"shadow_offset_y\", 1)\n\t\tname_label.visible = false\n\t\tname_label.autowrap_mode = TextServer.AUTOWRAP_OFF\n\t\tname_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS\t\n\t\tadd_child(name_label, true)\n\n\n\tfunc setup_count_label() -> void:\n\t\tcount_label = Label.new()\n\t\tcount_label.name = \"CountLabel\"\n\t\tcount_label.text = \"\"\n\t\tcount_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT\n\t\tcount_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM\n\t\tcount_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL\n\t\tcount_label.size_flags_vertical = Control.SIZE_EXPAND_FILL\n\t\tcount_label.add_theme_font_size_override(\"font_size\", 14)\n\t\tcount_label.add_theme_color_override(\"font_color\", Color.WHITE)\n\t\tcount_label.add_theme_color_override(\"font_shadow_color\", Color.BLACK)\n\t\tcount_label.add_theme_constant_override(\"shadow_offset_x\", 1)\n\t\tcount_label.add_theme_constant_override(\"shadow_offset_y\", 1)\n\t\tadd_child(count_label, true)\n\t\tvar mesh_resource: Terrain3DMeshAsset = resource as Terrain3DMeshAsset\n\t\tif not mesh_resource: \n\t\t\treturn\n\t\tmesh_resource.instance_count_changed.connect(update_count_label)\n\t\tupdate_count_label()\n\n\n\tfunc update_count_label() -> void:\n\t\tif not type == Terrain3DAssets.AssetType.TYPE_MESH or \\\n\t\t\t\t( resource and not resource.is_enabled() ):\n\t\t\tcount_label.text = \"\"\n\t\t\treturn\n\t\tvar mesh_resource: Terrain3DMeshAsset = resource as Terrain3DMeshAsset\n\t\tif not mesh_resource:\n\t\t\tcount_label.text = str(0)\n\t\telse:\n\t\t\tcount_label.text = _format_number(mesh_resource.get_instance_count())\n\n\n\tfunc _notification(p_what) -> void:\n\t\tmatch p_what:\n\t\t\tNOTIFICATION_PREDELETE:\n\t\t\t\tdestroy_buttons()\n\t\t\tNOTIFICATION_DRAW:\n\t\t\t\t# Hide spacer if icons are crowding small textures\n\t\t\t\tspacer.visible = size.x > 94. or type == Terrain3DAssets.TYPE_TEXTURE\n\t\t\t\tvar rect: Rect2 = Rect2(Vector2.ZERO, get_size())\n\t\t\t\tif !resource:\n\t\t\t\t\tdraw_style_box(background, rect)\n\t\t\t\t\tdraw_texture(add_icon, (get_size() / 2) - (add_icon.get_size() / 2))\n\t\t\t\telse:\n\t\t\t\t\t_thumbnail = resource.get_thumbnail()\n\t\t\t\t\tif _thumbnail:\n\t\t\t\t\t\tdraw_texture_rect(_thumbnail, rect, false)\n\t\t\t\t\t\ttexture_filter = CanvasItem.TEXTURE_FILTER_NEAREST_WITH_MIPMAPS\n\t\t\t\t\telse:\n\t\t\t\t\t\tdraw_rect(rect, Color(.15, .15, .15, 1.))\n\t\t\t\t\tif type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\t\t\t\tself_modulate = resource.get_highlight_color() if is_highlighted else resource.get_albedo_color()\n\t\t\t\t\telse:\n\t\t\t\t\t\tbutton_enabled.set_pressed_no_signal(!resource.is_enabled())\n\t\t\t\t\t\tself_modulate = resource.get_highlight_color()\n\t\t\t\t\tbutton_highlight.self_modulate = Color(\"FC7F7F\") if is_highlighted else Color.WHITE\n\t\t\t\tif drop_data:\n\t\t\t\t\tdraw_style_box(focus_style, rect)\n\t\t\t\tif is_hovered:\n\t\t\t\t\tdraw_rect(rect, Color(1, 1, 1, 0.2))\n\t\t\t\tif is_selected:\n\t\t\t\t\tdraw_style_box(focus_style, rect)\n\t\t\tNOTIFICATION_MOUSE_ENTER:\n\t\t\t\tif not resource:\n\t\t\t\t\tname_label.visible = false\n\t\t\t\telse:\n\t\t\t\t\tname_label.visible = true\n\t\t\t\tis_hovered = true\n\t\t\t\tname_label.text = get_resource_name()\n\t\t\t\ttooltip_text = get_resource_name()\n\t\t\t\temit_signal(\"hovered\")\n\t\t\t\tqueue_redraw()\n\t\t\tNOTIFICATION_MOUSE_EXIT:\n\t\t\t\tname_label.visible = false\n\t\t\t\tis_hovered = false\n\t\t\t\tdrop_data = false\n\t\t\t\tqueue_redraw()\n\n\n\tfunc _gui_input(p_event: InputEvent) -> void:\n\t\tif p_event is InputEventMouseButton:\n\t\t\tif p_event.is_pressed():\n\t\t\t\tmatch p_event.get_button_index():\n\t\t\t\t\tMOUSE_BUTTON_LEFT:\n\t\t\t\t\t\t# If `Add new` is clicked\n\t\t\t\t\t\tif !resource:\n\t\t\t\t\t\t\tif type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\t\t\t\t\t\tset_edited_resource(Terrain3DTextureAsset.new(), false)\n\t\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t\tset_edited_resource(Terrain3DMeshAsset.new(), false)\n\t\t\t\t\t\t\t_on_edit()\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\temit_signal(\"clicked\")\n\t\t\t\t\tMOUSE_BUTTON_RIGHT:\n\t\t\t\t\t\tif resource:\n\t\t\t\t\t\t\t_on_edit()\n\t\t\t\t\tMOUSE_BUTTON_MIDDLE:\n\t\t\t\t\t\tif resource:\n\t\t\t\t\t\t\t_on_clear()\n\n\n\tfunc _can_drop_data(p_at_position: Vector2, p_data: Variant) -> bool:\n\t\tdrop_data = false\n\t\tif typeof(p_data) == TYPE_DICTIONARY:\n\t\t\tif p_data.files.size() == 1:\n\t\t\t\tqueue_redraw()\n\t\t\t\tdrop_data = true\n\t\treturn drop_data\n\n\t\t\n\tfunc _drop_data(p_at_position: Vector2, p_data: Variant) -> void:\n\t\tif typeof(p_data) == TYPE_DICTIONARY:\n\t\t\tvar res: Resource = load(p_data.files[0])\n\t\t\tif res is Texture2D and type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\t\tvar ta := Terrain3DTextureAsset.new()\n\t\t\t\tif resource is Terrain3DTextureAsset:\n\t\t\t\t\tta.id = resource.id\n\t\t\t\tta.set_albedo_texture(res)\n\t\t\t\tset_edited_resource(ta, false)\n\t\t\t\tresource = ta\n\t\t\telif res is Terrain3DTextureAsset and type == Terrain3DAssets.TYPE_TEXTURE:\n\t\t\t\tif resource is Terrain3DTextureAsset:\n\t\t\t\t\tres.id = resource.id\n\t\t\t\tset_edited_resource(res, false)\n\t\t\telif res is PackedScene and type == Terrain3DAssets.TYPE_MESH:\n\t\t\t\tif not resource:\n\t\t\t\t\tresource = Terrain3DMeshAsset.new()\t\t\n\t\t\t\tset_edited_resource(resource, false)\n\t\t\t\tresource.set_scene_file(res)\n\t\t\telif res is Terrain3DMeshAsset and type == Terrain3DAssets.TYPE_MESH:\n\t\t\t\tif resource is Terrain3DMeshAsset:\n\t\t\t\t\tres.id = resource.id\n\t\t\t\tset_edited_resource(res, false)\n\t\t\temit_signal(\"clicked\")\n\t\t\temit_signal(\"inspected\", resource)\n\n\n\tfunc set_edited_resource(p_res: Resource, p_no_signal: bool = true) -> void:\n\t\tresource = p_res\n\t\tif resource:\n\t\t\tif not resource.setting_changed.is_connected(_on_resource_changed):\n\t\t\t\tresource.setting_changed.connect(_on_resource_changed)\n\t\t\tif resource is Terrain3DTextureAsset:\n\t\t\t\tif not resource.file_changed.is_connected(_on_resource_changed):\n\t\t\t\t\tresource.file_changed.connect(_on_resource_changed)\n\t\t\telif resource is Terrain3DMeshAsset:\n\t\t\t\tif not resource.instancer_setting_changed.is_connected(_on_resource_changed):\n\t\t\t\t\tresource.instancer_setting_changed.connect(_on_resource_changed)\n\t\t\n\t\tif button_clear:\n\t\t\tbutton_clear.set_visible(resource != null)\n\t\t\t\n\t\tqueue_redraw()\n\t\tif not p_no_signal:\n\t\t\temit_signal(\"changed\", resource)\n\n\n\tfunc _on_resource_changed(_value: int = 0) -> void:\n\t\tqueue_redraw()\n\t\temit_signal(\"changed\", resource)\n\n\n\tfunc set_selected(value: bool) -> void:\n\t\tis_selected = value\n\t\tif is_selected:\n\t\t\t# Handle scrolling to show the selected item\n\t\t\tawait get_tree().process_frame\n\t\t\tif is_inside_tree():\n\t\t\t\tget_parent().get_parent().get_v_scroll_bar().ratio = position.y / get_parent().size.y\n\t\tqueue_redraw()\n\n\n\tfunc _on_clear() -> void:\n\t\tif resource:\n\t\t\tname_label.hide()\n\t\t\tset_edited_resource(null, false)\n\t\t\tupdate_count_label()\n\n\t\n\tfunc _on_edit() -> void:\n\t\temit_signal(\"clicked\")\n\t\temit_signal(\"inspected\", resource)\n\n\n\tfunc _on_enable() -> void:\n\t\tif resource is Terrain3DMeshAsset:\n\t\t\tresource.set_enabled(!resource.is_enabled())\n\n\n\tfunc _on_highlight() -> void:\n\t\tis_highlighted = !is_highlighted\n\t\tresource.set_highlighted(is_highlighted)\n\n\n\tfunc _format_number(num: int) -> String:\n\t\tvar is_negative: bool = num < 0\n\t\tvar str_num: String = str(abs(num))\n\t\tvar result: String = \"\"\n\t\tvar length: int = str_num.length()\n\t\tfor i in length:\n\t\t\tresult = str_num[length - 1 - i] + result\n\t\t\tif i < length - 1 and (i + 1) % 3 == 0:\n\t\t\t\tresult = \",\" + result\n\t\treturn \"-\" + result if is_negative else result\n"
  },
  {
    "path": "project/addons/terrain_3d/src/asset_dock.gd.uid",
    "content": "uid://bgoifepft1hjw\n"
  },
  {
    "path": "project/addons/terrain_3d/src/asset_dock.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://dkb6hii5e48m2\"]\n\n[ext_resource type=\"Script\" uid=\"uid://bgoifepft1hjw\" path=\"res://addons/terrain_3d/src/asset_dock.gd\" id=\"1_e23pg\"]\n\n[node name=\"Terrain3D\" type=\"PanelContainer\"]\ncustom_minimum_size = Vector2(256, 95)\noffset_right = 766.0\noffset_bottom = 100.0\nscript = ExtResource(\"1_e23pg\")\n\n[node name=\"Box\" type=\"BoxContainer\" parent=\".\"]\nlayout_mode = 2\nsize_flags_vertical = 3\nvertical = true\n\n[node name=\"Buttons\" type=\"BoxContainer\" parent=\"Box\"]\nlayout_mode = 2\n\n[node name=\"SearchBox\" type=\"TextEdit\" parent=\"Box/Buttons\"]\ncustom_minimum_size = Vector2(100, 30)\nlayout_mode = 2\nplaceholder_text = \"Search\"\nemoji_menu_enabled = false\nscroll_fit_content_height = true\ncaret_blink = true\ncaret_multiple = false\n\n[node name=\"SearchButton\" type=\"Button\" parent=\"Box/Buttons/SearchBox\"]\nlayout_mode = 1\nanchors_preset = 6\nanchor_left = 1.0\nanchor_top = 0.5\nanchor_right = 1.0\nanchor_bottom = 0.5\noffset_left = -13.0\noffset_top = -4.0\noffset_right = -5.0\noffset_bottom = 4.0\ngrow_horizontal = 0\ngrow_vertical = 2\naction_mode = 0\n\n[node name=\"TexturesBtn\" type=\"Button\" parent=\"Box/Buttons\"]\ncustom_minimum_size = Vector2(80, 30)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_font_sizes/font_size = 16\ntoggle_mode = true\nbutton_pressed = true\ntext = \"Textures\"\n\n[node name=\"MeshesBtn\" type=\"Button\" parent=\"Box/Buttons\"]\ncustom_minimum_size = Vector2(80, 30)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntheme_override_font_sizes/font_size = 16\ntoggle_mode = true\ntext = \"Meshes\"\n\n[node name=\"PlacementOpt\" type=\"OptionButton\" parent=\"Box/Buttons\"]\ncustom_minimum_size = Vector2(80, 30)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\nselected = 7\nitem_count = 9\npopup/item_0/text = \"Left_UL\"\npopup/item_0/id = 0\npopup/item_1/text = \"Left_BL\"\npopup/item_1/id = 1\npopup/item_2/text = \"Left_UR\"\npopup/item_2/id = 2\npopup/item_3/text = \"Left_BR\"\npopup/item_3/id = 3\npopup/item_4/text = \"Right_UL\"\npopup/item_4/id = 4\npopup/item_5/text = \"Right_BL \"\npopup/item_5/id = 5\npopup/item_6/text = \"Right_UR\"\npopup/item_6/id = 6\npopup/item_7/text = \"Right_BR\"\npopup/item_7/id = 7\npopup/item_8/text = \"Bottom\"\npopup/item_8/id = 8\n\n[node name=\"SizeSlider\" type=\"HSlider\" parent=\"Box/Buttons\"]\ncustom_minimum_size = Vector2(80, 10)\nlayout_mode = 2\nsize_flags_horizontal = 3\nmin_value = 90.0\nmax_value = 512.0\nvalue = 90.0\n\n[node name=\"Floating\" type=\"Button\" parent=\"Box/Buttons\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 0\ntooltip_text = \"Pop this dock out to a floating window.\"\ntoggle_mode = true\ntext = \"F\"\nflat = true\n\n[node name=\"Pinned\" type=\"Button\" parent=\"Box/Buttons\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 0\ntooltip_text = \"Make this window \\\"Always on top\\\".\"\ntoggle_mode = true\ntext = \"P\"\nflat = true\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"Box\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n"
  },
  {
    "path": "project/addons/terrain_3d/src/double_slider.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# DoubleSlider for Terrain3D\n# Should work for other UIs\n@tool\nclass_name DoubleSlider\nextends Control\n\nsignal value_changed(Vector2)\nvar label: Label\nvar suffix: String\nvar grabbed_handle: int = 0 # -1 left, 0 none, 1 right\nvar min_value: float = 0.0\nvar max_value: float = 100.0\nvar step: float = 1.0\nvar range := Vector2(0, 100) \nvar display_scale: float = 1.\nvar position_x: float = 0.\nvar minimum_x: float = 60.\n\n\nfunc _ready() -> void:\n\t# Setup Display Scale\n\t# 0 auto, 1 75%, 2 100%, 3 125%, 4 150%, 5 175%, 6 200%, 7 custom\n\tvar es: EditorSettings = EditorInterface.get_editor_settings()\n\tvar ds: int = es.get_setting(\"interface/editor/display_scale\")\n\tif ds == 0:\n\t\tds = 2\n\telif ds == 7:\n\t\tdisplay_scale = es.get_setting(\"interface/editor/custom_display_scale\")\n\telse:\n\t\tdisplay_scale = float(ds + 2) * .25\n\n\tupdate_label()\n\n\nfunc set_min(p_value: float) -> void:\n\tmin_value = p_value\n\tif range.x <= min_value:\n\t\trange.x = min_value\n\t\tset_value(range)\n\tupdate_label()\n\n\nfunc get_min() -> float:\n\treturn min_value\n\t\n\t\nfunc set_max(p_value: float) -> void:\n\tmax_value = p_value\n\tif range.y == 0 or range.y >= max_value:\n\t\trange.y = max_value\n\t\tset_value(range)\n\tupdate_label()\n\n\nfunc get_max() -> float:\n\treturn max_value\n\t\n\t\nfunc set_step(p_step: float) -> void:\n\tstep = p_step\n\t\n\nfunc get_step() -> float:\n\treturn step\n\t\n\nfunc set_value(p_range: Vector2) -> void:\n\trange.x = clamp(p_range.x, min_value, max_value)\n\trange.y = clamp(p_range.y, min_value, max_value)\n\tif range.y < range.x:\n\t\tvar tmp: float = range.x\n\t\trange.x = range.y\n\t\trange.y = tmp\n\n\tupdate_label()\n\temit_signal(\"value_changed\", Vector2(range.x, range.y))\n\tqueue_redraw()\n\n\nfunc get_value() -> Vector2:\n\treturn range\n\n\nfunc update_label() -> void:\n\tif label:\n\t\tlabel.set_text(str(range.x) + suffix + \"/\" + str(range.y) + suffix)\n\t\tif position_x == 0:\n\t\t\tposition_x = label.position.x\n\t\telse:\n\t\t\tlabel.position.x = position_x + 5 * display_scale\n\t\tlabel.custom_minimum_size.x = minimum_x + 5 * display_scale\n\n\nfunc _get_handle() -> int:\n\treturn 1\n\n\nfunc _gui_input(p_event: InputEvent) -> void:\n\tif p_event is InputEventMouseButton:\n\t\tvar button: int = p_event.get_button_index()\n\t\tif button in [ MOUSE_BUTTON_LEFT, MOUSE_BUTTON_WHEEL_UP, MOUSE_BUTTON_WHEEL_DOWN ]:\n\t\t\tif p_event.is_pressed():\n\t\t\t\tvar mid_point = (range.x + range.y) / 2.0\n\t\t\t\tvar xpos: float = p_event.get_position().x * 2.0\n\t\t\t\tif xpos >= mid_point:\n\t\t\t\t\tgrabbed_handle = 1\n\t\t\t\telse:\n\t\t\t\t\tgrabbed_handle = -1\n\t\t\t\tmatch button:\n\t\t\t\t\tMOUSE_BUTTON_LEFT:\n\t\t\t\t\t\tset_slider(p_event.get_position().x)\n\t\t\t\t\tMOUSE_BUTTON_WHEEL_DOWN:\n\t\t\t\t\t\tset_slider(-1., true)\n\t\t\t\t\tMOUSE_BUTTON_WHEEL_UP:\n\t\t\t\t\t\tset_slider(1., true)\n\t\t\telse:\n\t\t\t\tgrabbed_handle = 0\n\t\t\t\n\tif p_event is InputEventMouseMotion:\n\t\tif grabbed_handle != 0:\n\t\t\tset_slider(p_event.get_position().x)\n\t\n\t\nfunc set_slider(p_xpos: float, p_relative: bool = false) -> void:\n\tif grabbed_handle == 0:\n\t\treturn\n\tvar xpos_step: float = clamp(snappedf((p_xpos / size.x) * max_value, step), min_value, max_value)\n\tif(grabbed_handle < 0):\n\t\tif p_relative:\n\t\t\trange.x += p_xpos\n\t\telse:\n\t\t\trange.x = xpos_step\n\telse:\n\t\tif p_relative:\n\t\t\trange.y += p_xpos\n\t\telse:\n\t\t\trange.y = xpos_step\t\n\tset_value(range)\n\n\nfunc _notification(p_what: int) -> void:\n\tif p_what == NOTIFICATION_DRAW:\n\t\t# Draw background bar\n\t\tvar bg: StyleBox = get_theme_stylebox(\"slider\", \"HSlider\")\n\t\tvar bg_height: float = bg.get_minimum_size().y\n\t\tvar mid_y: float = (size.y - bg_height) / 2.0\n\t\tdraw_style_box(bg, Rect2(Vector2(0, mid_y), Vector2(size.x, bg_height)))\n\t\t\n\t\t# Draw foreground bar\n\t\tvar handle: Texture2D = get_theme_icon(\"grabber\", \"HSlider\")\n\t\tvar area: StyleBox = get_theme_stylebox(\"grabber_area\", \"HSlider\")\n\t\tvar startx: float = (range.x / max_value) * size.x\n\t\tvar endx: float = (range.y / max_value) * size.x\n\t\tdraw_style_box(area, Rect2(Vector2(startx, mid_y), Vector2(endx - startx, bg_height)))\n\t\t\n\t\t# Draw handles, slightly in so they don't get on the outside edges\n\t\tvar handle_pos: Vector2\n\t\thandle_pos.x = clamp(startx - handle.get_size().x/2, -10, size.x)\n\t\thandle_pos.y = clamp(endx - handle.get_size().x/2, 0, size.x - 10)\n\t\tdraw_texture(handle, Vector2(handle_pos.x, -mid_y - 10 * (display_scale - 1.)))\n\t\tdraw_texture(handle, Vector2(handle_pos.y, -mid_y - 10 * (display_scale - 1.)))\n\t\t\n\t\tupdate_label()\n"
  },
  {
    "path": "project/addons/terrain_3d/src/double_slider.gd.uid",
    "content": "uid://stro0p1oawfb\n"
  },
  {
    "path": "project/addons/terrain_3d/src/editor_plugin.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Editor Plugin for Terrain3D\n@tool\nextends EditorPlugin\n\n\n# Includes\nconst Terrain3DUI: Script = preload(\"res://addons/terrain_3d/src/ui.gd\")\nconst ASSET_DOCK: String = \"res://addons/terrain_3d/src/asset_dock.tscn\"\n\n# Editor Plugin\nvar debug: int = 0 # Set in _edit()\nvar editor: Terrain3DEditor\nvar editor_settings: EditorSettings\nvar ui: Node # Terrain3DUI see Godot #75388\nvar asset_dock: PanelContainer\nvar current_region_position: Vector2\nvar mouse_global_position: Vector3 = Vector3.ZERO\nvar godot_editor_window: Window # The Godot Editor window\nvar viewport: SubViewport # Viewport the mouse was last in\nvar mouse_in_main: bool = false # Helper to track when mouse is in the editor vp\n\n# Terrain\nvar terrain: Terrain3D\nvar _last_terrain: Terrain3D\nvar nav_region: NavigationRegion3D\n\n# Input\nvar modifier_ctrl: bool\nvar modifier_alt: bool\nvar modifier_shift: bool\nvar _last_modifiers: int = 0\nvar _input_mode: int = 0 # -1: camera move, 0: none, 1: operating\nvar rmb_release_time: int = 0\nvar _use_meta: bool = false\n\n\nfunc _init() -> void:\n\tif debug:\n\t\tprint(\"Terrain3DEditorPlugin: _init\")\n\tif OS.get_name() == \"macOS\":\n\t\t_use_meta = true\n\t\n\t# Get the Godot Editor window. Structure is root:Window/EditorNode/Base Control\n\tgodot_editor_window = EditorInterface.get_base_control().get_parent().get_parent()\n\tgodot_editor_window.focus_entered.connect(_on_godot_focus_entered)\n\tEditorInterface.get_inspector().mouse_entered.connect(func(): mouse_in_main = false)\n\n\nfunc _enter_tree() -> void:\n\tif debug:\n\t\tprint(\"Terrain3DEditorPlugin: _enter_tree\")\n\teditor = Terrain3DEditor.new()\n\tsetup_editor_settings()\n\tui = Terrain3DUI.new()\n\tui.plugin = self\n\tadd_child(ui)\n\n\tscene_changed.connect(_on_scene_changed)\n\n\tasset_dock = load(ASSET_DOCK).instantiate()\n\tasset_dock.initialize(self)\n\n\nfunc _exit_tree() -> void:\n\tif debug:\n\t\tprint(\"Terrain3DEditorPlugin: _exit_tree\")\n\tasset_dock.remove_dock(true)\n\tasset_dock.queue_free()\n\tui.queue_free()\n\teditor.free()\n\n\tscene_changed.disconnect(_on_scene_changed)\n\tgodot_editor_window.focus_entered.disconnect(_on_godot_focus_entered)\n\n\nfunc _on_godot_focus_entered() -> void:\n\tif debug > 1:\n\t\tprint(\"Terrain3DEditorPlugin: _on_godot_focus_entered\")\n\t_read_input()\n\n\n## EditorPlugin selection function call chain isn't consistent. Here's the map of calls:\n## Assume we handle Terrain3D and NavigationRegion3D  \n# Click Terrain3D: \t\t\t\t\t_handles(Terrain3D), _edit(Terrain3D), _make_visible(true)\n# Deselect:\t\t\t\t\t\t\t_edit(null), _make_visible(false)\n# Click other node:\t\t\t\t\t_handles(OtherNode)\n# Click NavRegion3D:\t\t\t\t_handles(NavReg3D), _edit(NavReg3D), _make_visible(true)\n# Click NavRegion3D, Terrain3D:\t\t_handles(Terrain3D), _make_visible(true), _edit(Terrain3D)\n# Click Terrain3D, NavRegion3D:\t\t_handles(NavReg3D), _make_visible(true), _edit(NavReg3D)\nfunc _handles(p_object: Object) -> bool:\n\tif p_object is Terrain3D:\n\t\treturn true\n\telif p_object is NavigationRegion3D and is_instance_valid(_last_terrain):\n\t\treturn true\n\t\n\t# Terrain3DObjects requires access to EditorUndoRedoManager. The only way to make sure it\n\t# always has it, is to pass it in here. _edit is NOT called if the node is cut and pasted.\n\telif p_object is Terrain3DObjects:\n\t\tp_object.editor_setup(self)\n\telif p_object is Node3D and p_object.get_parent() is Terrain3DObjects:\n\t\tp_object.get_parent().editor_setup(self)\n\t\n\treturn false\n\n\nfunc _edit(p_object: Object) -> void:\n\tif !p_object:\n\t\t_clear()\n\n\tif p_object is Terrain3D:\n\t\tif p_object == terrain:\n\t\t\treturn\n\t\tterrain = p_object\n\t\t_last_terrain = terrain\n\t\tterrain.set_plugin(self)\n\t\tterrain.set_editor(editor)\n\t\tdebug = terrain.debug_level\n\t\teditor.set_terrain(terrain)\n\t\tterrain.set_meta(\"_edit_lock_\", true)\n\t\tui.set_visible(true)\n\n\t\t# Get alerted when a new asset list is loaded\n\t\tif not terrain.assets_changed.is_connected(asset_dock.update_assets):\n\t\t\tterrain.assets_changed.connect(asset_dock.update_assets)\n\t\tasset_dock.update_assets()\n\telse:\n\t\t_clear()\n\n\tif is_terrain_valid(_last_terrain):\n\t\tif p_object is NavigationRegion3D:\n\t\t\tui.set_visible(true, true)\n\t\t\tnav_region = p_object\n\t\telse:\n\t\t\tnav_region = null\n\n\t\nfunc _make_visible(p_visible: bool, p_redraw: bool = false) -> void:\n\tif debug:\n\t\tprint(\"Terrain3DEditorPlugin: _make_visible(%s, %s)\" % [ p_visible, p_redraw ])\n\tif p_visible and is_selected():\n\t\tui.set_visible(true)\n\t\tasset_dock.update_dock()\n\telse:\n\t\tui.set_visible(false)\n\n\nfunc _clear() -> void:\n\tif is_terrain_valid():\n\t\teditor.set_tool(Terrain3DEditor.TOOL_MAX)\n\t\teditor.set_operation(Terrain3DEditor.OP_MAX)\n\t\tterrain = null\n\t\teditor.set_terrain(null)\n\t\tui.clear_picking()\n\n\nfunc _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) -> AfterGUIInput:\n\tmouse_in_main = true\n\tif not is_terrain_valid():\n\t\treturn AFTER_GUI_INPUT_PASS\n\n\tvar continue_input: AfterGUIInput = _read_input(p_event)\n\tif continue_input != AFTER_GUI_INPUT_CUSTOM:\n\t\treturn continue_input\n\t\n\t## Setup active camera & viewport\n\t# Always update this for all inputs, as the mouse position can move without\n\t# necessarily being a InputEventMouseMotion object. get_intersection() also\n\t# returns the last frame position, and should be updated more frequently.\n\t\n\t# Snap terrain to current camera \n\tterrain.set_camera(p_viewport_camera)\n\n\t# Detect if viewport is set to half_resolution\n\t# Structure is: Node3DEditorViewportContainer/Node3DEditorViewport(4)/SubViewportContainer/SubViewport/Camera3D\n\tviewport = p_viewport_camera.get_parent()\n\tvar full_resolution: bool = false if viewport.get_parent().stretch_shrink == 2 else true\n\n\t## Get mouse location on terrain\n\t# Project 2D mouse position to 3D position and direction\n\tvar vp_mouse_pos: Vector2 = viewport.get_mouse_position()\n\tvar mouse_pos: Vector2 = vp_mouse_pos if full_resolution else vp_mouse_pos / 2\n\tvar camera_pos: Vector3 = p_viewport_camera.project_ray_origin(mouse_pos)\n\tvar camera_dir: Vector3 = p_viewport_camera.project_ray_normal(mouse_pos)\n\n\tui.update_decal()\n\n\t# If region tool, grab mouse position without considering height\n\tif editor.get_tool() == Terrain3DEditor.REGION:\n\t\tvar t = -Vector3(0, 1, 0).dot(camera_pos) / Vector3(0, 1, 0).dot(camera_dir)\n\t\tmouse_global_position = (camera_pos + t * camera_dir)\n\telse:\n\t#Else look for intersection with terrain\n\t\tvar intersection_point: Vector3 = terrain.get_intersection(camera_pos, camera_dir, true)\n\t\tif intersection_point.z > 3.4e38 or is_nan(intersection_point.y): # max double or nan\n\t\t\treturn AFTER_GUI_INPUT_PASS\n\t\tmouse_global_position = intersection_point\n\t\n\t## Handle mouse movement\n\tif p_event is InputEventMouseMotion:\n\n\t\tif _input_mode != -1: # Not cam rotation\n\t\t\t## Update region highlight\n\t\t\tvar region_position: Vector2 = ( Vector2(mouse_global_position.x, mouse_global_position.z) \\\n\t\t\t\t/ (terrain.get_region_size() * terrain.get_vertex_spacing()) ).floor()\n\n\t\t\tif _input_mode > 0 and editor.is_operating():\n\t\t\t\t# Inject pressure - Relies on C++ set_brush_data() using same dictionary instance\n\t\t\t\tui.brush_data[\"mouse_pressure\"] = p_event.pressure\n\n\t\t\t\teditor.operate(mouse_global_position, p_viewport_camera.rotation.y)\n\t\t\t\treturn AFTER_GUI_INPUT_STOP\n\t\t\t\n\t\treturn AFTER_GUI_INPUT_PASS\n\n\tif p_event is InputEventMouseButton and _input_mode > 0:\n\t\tif p_event.is_pressed():\n\t\t\t# If picking\n\t\t\tif ui.is_picking():\n\t\t\t\tui.pick(mouse_global_position)\n\t\t\t\tif not ui.operation_builder or not ui.operation_builder.is_ready():\n\t\t\t\t\treturn AFTER_GUI_INPUT_STOP\n\t\t\t\n\t\t\tif modifier_ctrl and editor.get_tool() == Terrain3DEditor.HEIGHT:\n\t\t\t\tvar height: float = terrain.data.get_height(mouse_global_position)\n\t\t\t\tui.brush_data[\"height\"] = height\n\t\t\t\tui.tool_settings.set_setting(\"height\", height)\n\t\t\t\t\n\t\t\t# If adjusting regions\n\t\t\tif editor.get_tool() == Terrain3DEditor.REGION:\n\t\t\t\t# Skip regions that already exist or don't\n\t\t\t\tvar has_region: bool = terrain.data.has_regionp(mouse_global_position)\n\t\t\t\tvar op: int = editor.get_operation()\n\t\t\t\tif\t( has_region and op == Terrain3DEditor.ADD) or \\\n\t\t\t\t\t( not has_region and op == Terrain3DEditor.SUBTRACT ):\n\t\t\t\t\treturn AFTER_GUI_INPUT_STOP\n\t\t\t\n\t\t\t# If an automatic operation is ready to go (e.g. gradient)\n\t\t\tif ui.operation_builder and ui.operation_builder.is_ready():\n\t\t\t\tui.operation_builder.apply_operation(editor, mouse_global_position, p_viewport_camera.rotation.y)\n\t\t\t\treturn AFTER_GUI_INPUT_STOP\n\t\t\t\n\t\t\t# Mouse clicked, start editing\n\t\t\teditor.start_operation(mouse_global_position)\n\t\t\teditor.operate(mouse_global_position, p_viewport_camera.rotation.y)\n\t\t\treturn AFTER_GUI_INPUT_STOP\n\t\t\n\t\t# _input_apply released, save undo data\n\t\telif editor.is_operating():\n\t\t\teditor.stop_operation()\n\t\t\treturn AFTER_GUI_INPUT_STOP\n\n\treturn AFTER_GUI_INPUT_PASS\n\n\nfunc _read_input(p_event: InputEvent = null) -> AfterGUIInput:\n\t## Determine if user is moving camera or applying\n\tif Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) or \\\n\t\tp_event is InputEventMouseButton and p_event.is_released() and \\\n\t\tp_event.get_button_index() == MOUSE_BUTTON_LEFT:\n\t\t\t_input_mode = 1 \n\telse:\n\t\t\t_input_mode = 0\n\t\n\tmatch get_setting(\"editors/3d/navigation/navigation_scheme\", 0):\n\t\t2, 1: # Modo, Maya\n\t\t\tif Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT) or \\\n\t \t\t\t( Input.is_key_pressed(KEY_ALT) and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) ):\n\t\t\t\t\t_input_mode = -1 \n\t\t\tif p_event is InputEventMouseButton and p_event.is_released() and \\\n\t\t\t\t( p_event.get_button_index() == MOUSE_BUTTON_RIGHT or \\\n\t\t\t\t( Input.is_key_pressed(KEY_ALT) and p_event.get_button_index() == MOUSE_BUTTON_LEFT )):\n\t\t\t\t\trmb_release_time = Time.get_ticks_msec()\n\t\t0, _: # Godot\n\t\t\tif Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT) or \\\n\t\t\t\tInput.is_mouse_button_pressed(MOUSE_BUTTON_MIDDLE):\n\t\t\t\t\t_input_mode = -1 \n\t\t\tif p_event is InputEventMouseButton and p_event.is_released() and \\\n\t\t\t\t( p_event.get_button_index() == MOUSE_BUTTON_RIGHT or \\\n\t\t\t\tp_event.get_button_index() == MOUSE_BUTTON_MIDDLE ):\n\t\t\t\t\trmb_release_time = Time.get_ticks_msec()\n\tif _input_mode < 0:\n\t\t# Camera is moving, skip input\n\t\treturn AFTER_GUI_INPUT_PASS\n\n\t## Determine modifiers pressed\n\tmodifier_shift = Input.is_key_pressed(KEY_SHIFT)\n\t\n\t# Editor responds to modifier_ctrl so we must register touchscreen Invert \n\tif _use_meta:\n\t\tmodifier_ctrl = Input.is_key_pressed(KEY_META) || ui.inverted_input\n\telse:\n\t\tmodifier_ctrl = Input.is_key_pressed(KEY_CTRL) || ui.inverted_input\n\t\n\t# Keybind enum: Alt,Space,Meta,Capslock\n\tvar alt_key: int\n\tmatch get_setting(\"terrain3d/config/alt_key_bind\", 0):\n\t\t3: alt_key = KEY_CAPSLOCK\n\t\t2: alt_key = KEY_META\n\t\t1: alt_key = KEY_SPACE\n\t\t0, _: alt_key = KEY_ALT\n\tmodifier_alt = Input.is_key_pressed(alt_key)\n\tvar current_mods: int = int(modifier_shift) | int(modifier_ctrl) << 1 | int(modifier_alt) << 2\n\n\t## Process Hotkeys\n\tif p_event is InputEventKey and \\\n\t\t\tcurrent_mods == 0 and \\\n\t\t\tp_event.is_pressed() and \\\n\t\t\tnot p_event.is_echo() and \\\n\t\t\tconsume_hotkey(p_event.keycode):\n\t\t# Hotkey found, consume event, and stop input processing\n\t\tEditorInterface.get_editor_viewport_3d().set_input_as_handled()\n\t\treturn AFTER_GUI_INPUT_STOP\n\n\t# Brush data is cleared on set_tool, or clicking textures in the asset dock\n\t# Update modifiers if changed or missing\n\tif  _last_modifiers != current_mods or not ui.brush_data.has(\"modifier_shift\"):\n\t\t_last_modifiers = current_mods\n\t\tui.brush_data[\"modifier_shift\"] = modifier_shift\n\t\tui.brush_data[\"modifier_ctrl\"] = modifier_ctrl\n\t\tui.brush_data[\"modifier_alt\"] = modifier_alt\n\t\tui.set_active_operation()\n\n\t## Continue processing input\n\treturn AFTER_GUI_INPUT_CUSTOM\n\n\n# Returns true if hotkey matches and operation triggered\nfunc consume_hotkey(keycode: int) -> bool:\n\tmatch keycode:\n\t\tKEY_1, KEY_KP_1:\n\t\t\tterrain.material.set_show_region_grid(!terrain.material.get_show_region_grid())\n\t\tKEY_2, KEY_KP_2:\n\t\t\tterrain.label_distance = 4096.0 if is_zero_approx(terrain.label_distance) else 0.0 \n\t\tKEY_3, KEY_KP_3:\n\t\t\tterrain.material.set_show_contours(!terrain.material.get_show_contours())\n\t\tKEY_4, KEY_KP_4:\n\t\t\tterrain.material.set_show_instancer_grid(!terrain.material.get_show_instancer_grid())\n\t\tKEY_5, KEY_KP_5:\n\t\t\tterrain.material.set_show_vertex_grid(!terrain.material.get_show_vertex_grid())\n\t\tKEY_E:\n\t\t\tui.toolbar.get_button(\"AddRegion\").set_pressed(true)\n\t\tKEY_R:\n\t\t\tui.toolbar.get_button(\"Raise\").set_pressed(true)\n\t\tKEY_H:\n\t\t\tui.toolbar.get_button(\"Height\").set_pressed(true)\n\t\tKEY_S:\n\t\t\tui.toolbar.get_button(\"Slope\").set_pressed(true)\n\t\tKEY_C:\n\t\t\tui.toolbar.get_button(\"PaintColor\").set_pressed(true)\n\t\tKEY_N:\n\t\t\tui.toolbar.get_button(\"PaintNavigableArea\").set_pressed(true)\n\t\tKEY_I:\n\t\t\tui.toolbar.get_button(\"InstanceMeshes\").set_pressed(true)\n\t\tKEY_X:\n\t\t\tui.toolbar.get_button(\"AddHoles\").set_pressed(true)\n\t\tKEY_W:\n\t\t\tui.toolbar.get_button(\"PaintWetness\").set_pressed(true)\n\t\tKEY_B:\n\t\t\tui.toolbar.get_button(\"PaintTexture\").set_pressed(true)\n\t\tKEY_V:\n\t\t\tui.toolbar.get_button(\"SprayTexture\").set_pressed(true)\n\t\tKEY_A:\n\t\t\tui.toolbar.get_button(\"PaintAutoshader\").set_pressed(true)\n\t\tKEY_T:\n\t\t\tui.tool_settings.inverse_slope_range()\n\t\t_:\n\t\t\treturn false\n\treturn true\n\n\nfunc _on_scene_changed(scene_root: Node) -> void:\n\tif debug:\n\t\tprint(\"Terrain3DEditorPlugin: _on_scene_changed: \", scene_root)\n\tif not scene_root:\n\t\treturn\n\t\t\n\tfor node in scene_root.find_children(\"\", \"Terrain3DObjects\"):\n\t\tnode.editor_setup(self)\n\n\tasset_dock.update_assets()\n\n\nfunc get_terrain() -> Terrain3D:\n\tif is_terrain_valid():\n\t\treturn terrain\n\telif is_instance_valid(_last_terrain) and is_terrain_valid(_last_terrain):\n\t\treturn _last_terrain\n\telse:\n\t\treturn null\n\n\nfunc is_terrain_valid(p_terrain: Terrain3D = null) -> bool:\n\tvar t: Terrain3D\n\tif p_terrain:\n\t\tt = p_terrain\n\telse:\n\t\tt = terrain\n\tif is_instance_valid(t) and t.is_inside_tree() and t.data:\n\t\treturn true\n\treturn false\n\n\nfunc is_selected() -> bool:\n\tvar selected: Array[Node] = EditorInterface.get_selection().get_selected_nodes()\n\tfor node in selected:\n\t\tif ( is_instance_valid(_last_terrain) and node.get_instance_id() == _last_terrain.get_instance_id() ) or \\\n\t\t\tnode is Terrain3D:\n\t\t\t\treturn true\n\treturn false\t\n\n\nfunc select_terrain() -> void:\n\tif debug and is_selected():\n\t\tprint(\"Terrain3DEditorPlugin: Terrain is selected, skipping\")\n\tif is_instance_valid(_last_terrain) and is_terrain_valid(_last_terrain) and not is_selected():\n\t\tvar es: EditorSelection = EditorInterface.get_selection()\n\t\tif debug:\n\t\t\tprint(\"Terrain3DEditorPlugin: Clearing and reselecting terrain\")\n\t\tes.clear()\n\t\tes.add_node(_last_terrain)\n\n\n## Editor Settings\n\n\nfunc setup_editor_settings() -> void:\n\teditor_settings = EditorInterface.get_editor_settings()\n\tif not editor_settings.has_setting(\"terrain3d/config/alt_key_bind\"):\n\t\teditor_settings.set(\"terrain3d/config/alt_key_bind\", 0)\n\tvar property_info = {\n\t\t\"name\": \"terrain3d/config/alt_key_bind\",\n\t\t\"type\": TYPE_INT,\n\t\t\"hint\": PROPERTY_HINT_ENUM,\n\t\t\"hint_string\": \"Alt,Space,Meta,Capslock\"\n\t}\n\teditor_settings.add_property_info(property_info)\n\t\n\nfunc set_setting(p_str: String, p_value: Variant) -> void:\n\teditor_settings.set_setting(p_str, p_value)\n\n\nfunc get_setting(p_str: String, p_default: Variant) -> Variant:\n\tif editor_settings.has_setting(p_str):\n\t\treturn editor_settings.get_setting(p_str)\n\telse:\n\t\treturn p_default\n\n\nfunc has_setting(p_str: String) -> bool:\n\treturn editor_settings.has_setting(p_str)\n\n\nfunc erase_setting(p_str: String) -> void:\n\teditor_settings.erase(p_str)\n\n\n## Undo / Redo Functions\n\n\nfunc create_undo_action(p_action_name: String) -> void:\n\tget_undo_redo().create_action(p_action_name, UndoRedo.MERGE_DISABLE, terrain)\n\n\nfunc add_undo_method(p_method: Callable) -> void:\n\tvar args := [ p_method.get_object(), p_method.get_method() ]\n\targs.append_array(p_method.get_bound_arguments())\n\tget_undo_redo().add_undo_method.callv(args)\n\n\nfunc add_do_method(p_method: Callable) -> void:\n\tvar args := [ p_method.get_object(), p_method.get_method() ]\n\targs.append_array(p_method.get_bound_arguments())\n\tget_undo_redo().add_do_method.callv(args)\n\n\nfunc commit_action(p_execute: bool) -> void:\n\tget_undo_redo().commit_action(p_execute)\n"
  },
  {
    "path": "project/addons/terrain_3d/src/editor_plugin.gd.uid",
    "content": "uid://bsgxo1qywjdf3\n"
  },
  {
    "path": "project/addons/terrain_3d/src/gradient_operation_builder.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Gradient Operation Builder for Terrain3D\nextends \"res://addons/terrain_3d/src/operation_builder.gd\"\n\n\nconst MultiPicker: Script = preload(\"res://addons/terrain_3d/src/multi_picker.gd\")\n\n\nfunc _get_point_picker() -> MultiPicker:\n\treturn tool_settings.settings[\"gradient_points\"]\n\n\nfunc _get_brush_size() -> float:\n\treturn tool_settings.get_setting(\"size\")\n\n\nfunc _is_drawable() -> bool:\n\treturn tool_settings.get_setting(\"drawable\")\n\n\nfunc is_picking() -> bool:\n\treturn not _get_point_picker().all_points_selected()\n\n\nfunc pick(p_global_position: Vector3, p_terrain: Terrain3D) -> void:\n\tif not _get_point_picker().all_points_selected():\n\t\t_get_point_picker().add_point(p_global_position)\n\n\nfunc is_ready() -> bool:\n\treturn _get_point_picker().all_points_selected() and not _is_drawable()\n\n\n# This function runs a \"brush\" operation from point1 to point2, when drawable is not checked\nfunc apply_operation(p_editor: Terrain3DEditor, p_global_position: Vector3, p_camera_direction: float) -> void:\n\tvar points: PackedVector3Array = _get_point_picker().get_points()\n\tassert(points.size() == 2)\n\tassert(not _is_drawable())\n\t\n\tvar brush_size: float = _get_brush_size()\n\tassert(brush_size > 0.0)\n\t\n\tvar start: Vector3 = points[0]\n\tvar end: Vector3 = points[1]\n\t\n\tp_editor.start_operation(start)\n\t\n\tvar dir: Vector3 = (end - start).normalized()\n\t\n\tvar pos: Vector3 = start\n\twhile dir.dot(end - pos) > 0.0:\n\t\tp_editor.operate(pos, p_camera_direction)\n\t\tpos += dir * brush_size * 0.2\n\t\n\tp_editor.stop_operation()\n\t\n\t_get_point_picker().clear()\n"
  },
  {
    "path": "project/addons/terrain_3d/src/gradient_operation_builder.gd.uid",
    "content": "uid://def7sych6dp8b\n"
  },
  {
    "path": "project/addons/terrain_3d/src/multi_picker.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Multipicker for Terrain3D\nextends HBoxContainer\n\n\nsignal pressed\nsignal value_changed\n\n\nconst ICON_PICKER_CHECKED: String = \"res://addons/terrain_3d/icons/picker_checked.svg\"\nconst MAX_POINTS: int = 2\n\n\nvar icon_picker: Texture2D\nvar icon_picker_checked: Texture2D\nvar points: PackedVector3Array\nvar picking_index: int = -1\n\n\nfunc _enter_tree() -> void:\n\ticon_picker = get_theme_icon(\"ColorPick\", \"EditorIcons\")\n\ticon_picker_checked = load(ICON_PICKER_CHECKED)\n\t\n\tpoints.resize(MAX_POINTS)\n\t\n\tfor i in range(MAX_POINTS):\n\t\tvar button := Button.new()\n\t\tbutton.icon = icon_picker\n\t\tbutton.tooltip_text = \"Pick point on the Terrain\"\n\t\tbutton.set_meta(&\"point_index\", i)\n\t\tbutton.pressed.connect(_on_button_pressed.bind(i))\n\t\tadd_child(button)\n\t\n\t_update_buttons()\n\n\nfunc _on_button_pressed(button_index: int) -> void:\n\tpoints[button_index] = Vector3.ZERO\n\tpicking_index = button_index\n\t_update_buttons()\n\tpressed.emit()\n\n\nfunc _update_buttons() -> void:\n\tfor child in get_children():\n\t\tif child is Button:\n\t\t\t_update_button(child)\n\n\nfunc _update_button(button: Button) -> void:\n\tvar index: int = button.get_meta(&\"point_index\")\n\t\n\tif points[index] != Vector3.ZERO:\n\t\tbutton.icon = icon_picker_checked\n\telse:\n\t\tbutton.icon = icon_picker\n\n\nfunc clear() -> void:\n\tpoints.fill(Vector3.ZERO)\n\t_update_buttons()\n\tvalue_changed.emit()\n\n\nfunc all_points_selected() -> bool:\n\treturn points.count(Vector3.ZERO) == 0\n\n\nfunc add_point(p_value: Vector3) -> void:\n\tif points.has(p_value):\n\t\treturn\n\t\n\t# If manually selecting a point individually\n\tif picking_index != -1:\n\t\tpoints[picking_index] = p_value\n\t\tpicking_index = -1\n\telse:\n\t\t# Else picking a sequence of points (non-drawable)\n\t\tfor i in range(MAX_POINTS):\n\t\t\tif points[i] == Vector3.ZERO:\n\t\t\t\tpoints[i] = p_value\n\t\t\t\tbreak\n\t_update_buttons()\n\tvalue_changed.emit()\n\n\nfunc get_points() -> PackedVector3Array:\n\treturn points\n"
  },
  {
    "path": "project/addons/terrain_3d/src/multi_picker.gd.uid",
    "content": "uid://dvdtoa32h6xdn\n"
  },
  {
    "path": "project/addons/terrain_3d/src/operation_builder.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Operation Builder for Terrain3D\nextends RefCounted\n\n\nconst ToolSettings: Script = preload(\"res://addons/terrain_3d/src/tool_settings.gd\")\n\n\nvar tool_settings: ToolSettings\n\n\nfunc is_picking() -> bool:\n\treturn false\n\n\nfunc pick(p_global_position: Vector3, p_terrain: Terrain3D) -> void:\n\tpass\n\n\nfunc is_ready() -> bool:\n\treturn false\n\n\nfunc apply_operation(editor: Terrain3DEditor, p_global_position: Vector3, p_camera_direction: float) -> void:\n\tpass\n"
  },
  {
    "path": "project/addons/terrain_3d/src/operation_builder.gd.uid",
    "content": "uid://bu5cm0eh052rm\n"
  },
  {
    "path": "project/addons/terrain_3d/src/tool_settings.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Tool settings bar for Terrain3D\nextends PanelContainer\n\nsignal picking(type: Terrain3DEditor.Tool, callback: Callable)\nsignal setting_changed(setting: Variant)\n\nenum Layout {\n\tHORIZONTAL,\n\tVERTICAL,\n\tGRID,\n}\n\nenum SettingType {\n\tCHECKBOX,\n\tCOLOR_SELECT,\n\tDOUBLE_SLIDER,\n\tOPTION,\n\tPICKER,\n\tMULTI_PICKER,\n\tSLIDER,\n\tLABEL,\n\tTYPE_MAX,\n}\n\nconst MultiPicker: Script = preload(\"res://addons/terrain_3d/src/multi_picker.gd\")\nconst DEFAULT_BRUSH: String = \"circle0.exr\"\nconst BRUSH_PATH: String = \"res://addons/terrain_3d/brushes\"\nconst ES_TOOL_SETTINGS: String = \"terrain3d/tool_settings/\"\n\n# Add settings flags\nconst NONE: int = 0x0\nconst ALLOW_LARGER: int = 0x1\nconst ALLOW_SMALLER: int = 0x2\nconst ALLOW_OUT_OF_BOUNDS: int = 0x3 # LARGER|SMALLER\nconst NO_LABEL: int = 0x4\nconst ADD_SEPARATOR: int = 0x8 # Add a vertical line before this entry\nconst ADD_SPACER: int = 0x10 # Add a space before this entry\nconst NO_SAVE: int = 0x20 # Don't save this in EditorSettings\n\nvar plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors\nvar brush_preview_material: ShaderMaterial\nvar select_brush_button: Button\nvar selected_brush_imgs: Array\nvar main_list: HFlowContainer\nvar advanced_list: VBoxContainer\nvar height_list: VBoxContainer\nvar scale_list: VBoxContainer\nvar rotation_list: VBoxContainer\nvar color_list: VBoxContainer\nvar collision_list: VBoxContainer\nvar settings: Dictionary = {}\n\n\nfunc _ready() -> void:\n\t# Remove old editor settings, newer first so oldest can be removed\n\tfor setting in [\"jitter\", \"lift_floor\", \"flatten_peaks\", \"lift_flatten\", \"automatic_regions\",\n\t\t\t\"show_cursor_while_painting\", \"crosshair_threshold\"]:\n\t\tplugin.erase_setting(ES_TOOL_SETTINGS + setting)\n\n\t# Setup buttons\t\n\tmain_list = HFlowContainer.new()\n\tadd_child(main_list, true)\n\t\n\tadd_brushes(main_list)\n\n\tadd_setting({ \"name\":\"instructions\", \"label\":\"Click the terrain to add a region. CTRL+Click to remove. Or select another tool on the left.\",\n\t\t\"type\":SettingType.LABEL, \"list\":main_list, \"flags\":NO_LABEL|NO_SAVE })\n\n\tadd_setting({ \"name\":\"size\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":20, \"unit\":\"m\",\n\t\t\t\t\t\t\t\"range\":Vector3(0.1, 200, 1), \"flags\":ALLOW_LARGER|ADD_SPACER })\n\t\t\n\tadd_setting({ \"name\":\"strength\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":33, \n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(1, 100, 1), \"flags\":ALLOW_LARGER })\n\n\tadd_setting({ \"name\":\"height\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":20, \n\t\t\t\t\t\t\t\"unit\":\"m\", \"range\":Vector3(-500, 500, 0.1), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\tadd_setting({ \"name\":\"height_picker\", \"type\":SettingType.PICKER, \"list\":main_list, \"default\":Terrain3DEditor.HEIGHT,\n\t\t\t\t\t\t\t\"flags\":NO_LABEL, \"tooltip\":\"Pick Height from the terrain.\" })\n\t\n\tadd_setting({ \"name\":\"color\", \"type\":SettingType.COLOR_SELECT, \"list\":main_list, \n\t\t\t\t\t\t\t\"default\":Color.WHITE, \"flags\":ADD_SEPARATOR })\n\tadd_setting({ \"name\":\"color_picker\", \"type\":SettingType.PICKER, \"list\":main_list, \"default\":Terrain3DEditor.COLOR,\n\t\t\t\t\t\t\t\"flags\":NO_LABEL, \"tooltip\":\"Pick Color from the terrain.\" })\n\n\tadd_setting({ \"name\":\"roughness\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":-65,\n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(-100, 100, 1), \"flags\":ADD_SEPARATOR })\n\tadd_setting({ \"name\":\"roughness_picker\", \"type\":SettingType.PICKER, \"list\":main_list, \"default\":Terrain3DEditor.ROUGHNESS,\n\t\t\t\t\t\t\t\"flags\":NO_LABEL, \"tooltip\":\"Pick Wetness from the terrain.\" })\n\n\tadd_setting({ \"name\":\"enable_texture\", \"label\":\"Texture\", \"type\":SettingType.CHECKBOX, \n\t\t\t\t\t\t\t\"list\":main_list, \"default\":true, \"flags\":ADD_SEPARATOR })\n\n\tadd_setting({ \"name\":\"texture_picker\", \"type\":SettingType.PICKER, \"list\":main_list, \"default\":Terrain3DEditor.TEXTURE,\n\t\t\t\t\t\t\t\"flags\":NO_LABEL, \"tooltip\":\"Pick Texture from the terrain.\" })\n\n\tadd_setting({ \"name\":\"texture_filter\", \"label\":\"Texture Filter\", \"type\":SettingType.CHECKBOX, \n\t\t\t\t\t\t\t\"list\":main_list, \"default\":false, \"flags\":ADD_SEPARATOR })\n\n\tadd_setting({ \"name\":\"margin\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":0, \n\t\t\t\t\t\t\t\"unit\":\"\", \"range\":Vector3(-50, 50, 1), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\n\t# Slope painting filter\n\tadd_setting({ \"name\":\"slope\", \"type\":SettingType.DOUBLE_SLIDER, \"list\":main_list, \"default\":Vector2(0, 90),\n\t\t\t\t\t\t\t\"unit\":\"°\", \"range\":Vector3(0, 90, 1), \"flags\":ADD_SEPARATOR })\n\t\n\tadd_setting({ \"name\":\"enable_angle\", \"label\":\"Angle\", \"type\":SettingType.CHECKBOX, \n\t\t\t\t\t\t\t\"list\":main_list, \"default\":true, \"flags\":ADD_SEPARATOR })\n\tadd_setting({ \"name\":\"angle\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":0,\n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(0, 337.5, 22.5), \"flags\":NO_LABEL })\n\tadd_setting({ \"name\":\"angle_picker\", \"type\":SettingType.PICKER, \"list\":main_list, \"default\":Terrain3DEditor.ANGLE,\n\t\t\t\t\t\t\t\"flags\":NO_LABEL, \"tooltip\":\"Pick Angle from the terrain.\" })\n\tadd_setting({ \"name\":\"dynamic_angle\", \"label\":\"Dynamic\", \"type\":SettingType.CHECKBOX, \n\t\t\t\t\t\t\t\"list\":main_list, \"default\":false, \"flags\":ADD_SPACER })\n\t\n\tadd_setting({ \"name\":\"enable_scale\", \"label\":\"Scale\", \"type\":SettingType.CHECKBOX, \n\t\t\t\t\t\t\t\"list\":main_list, \"default\":true, \"flags\":ADD_SEPARATOR })\n\tadd_setting({ \"name\":\"scale\", \"label\":\"±\", \"type\":SettingType.SLIDER, \"list\":main_list, \"default\":0,\n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(-60, 80, 20), \"flags\":NO_LABEL })\n\tadd_setting({ \"name\":\"scale_picker\", \"type\":SettingType.PICKER, \"list\":main_list, \"default\":Terrain3DEditor.SCALE,\n\t\t\t\t\t\t\t\"flags\":NO_LABEL, \"tooltip\":\"Pick Scale from the terrain.\" })\n\n\t## Slope sculpting brush\n\tadd_setting({ \"name\":\"gradient_points\", \"type\":SettingType.MULTI_PICKER, \"label\":\"Points\", \n\t\t\t\t\t\t\t\"list\":main_list, \"default\":Terrain3DEditor.SCULPT, \"flags\":ADD_SEPARATOR })\n\tadd_setting({ \"name\":\"drawable\", \"type\":SettingType.CHECKBOX, \"list\":main_list, \"default\":false, \n\t\t\t\t\t\t\t\"flags\":ADD_SEPARATOR })\n\tsettings[\"drawable\"].toggled.connect(_on_drawable_toggled)\n\t\n\t## Instancer\n\tadd_setting({ \"name\":\"mesh_picker\", \"type\":SettingType.PICKER, \"list\":main_list,\n\t\t\t\t\t\t\t\"default\":Terrain3DEditor.INSTANCER, \"flags\":NO_LABEL|ADD_SEPARATOR,\n\t\t\t\t\t\t\t\"tooltip\":\"Pick a Mesh asset from the terrain, within an instancer cell. (See overlays.)\" })\n\n\theight_list = create_submenu(main_list, \"Height\", Layout.VERTICAL)\n\tadd_setting({ \"name\":\"height_offset\", \"type\":SettingType.SLIDER, \"list\":height_list, \"default\":0, \n\t\t\t\t\t\t\t\"unit\":\"m\", \"range\":Vector3(-10, 10, 0.05), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\tadd_setting({ \"name\":\"random_height\", \"label\":\"Random Height ±\", \"type\":SettingType.SLIDER, \"list\":height_list,\n\t\t\t\t\t\t\t\"default\":0, \"unit\":\"m\", \"range\":Vector3(0, 10, 0.05), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\n\tscale_list = create_submenu(main_list, \"Scale\", Layout.VERTICAL)\n\tadd_setting({ \"name\":\"fixed_scale\", \"type\":SettingType.SLIDER, \"list\":scale_list, \"default\":100, \n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(1, 1000, 1), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\tadd_setting({ \"name\":\"random_scale\", \"label\":\"Random Scale ±\", \"type\":SettingType.SLIDER, \"list\":scale_list, \n\t\t\t\t\t\t\t\"default\":20, \"unit\":\"%\", \"range\":Vector3(0, 99, 1), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\n\trotation_list = create_submenu(main_list, \"Rotation\", Layout.VERTICAL)\n\tadd_setting({ \"name\":\"fixed_spin\", \"label\":\"Fixed Spin (Around Y)\", \"type\":SettingType.SLIDER, \"list\":rotation_list, \n\t\t\t\t\t\t\t\"default\":0, \"unit\":\"°\", \"range\":Vector3(0, 360, 1) })\n\tadd_setting({ \"name\":\"random_spin\", \"type\":SettingType.SLIDER, \"list\":rotation_list, \"default\":360, \n\t\t\t\t\t\t\t\"unit\":\"°\", \"range\":Vector3(0, 360, 1) })\n\tadd_setting({ \"name\":\"fixed_tilt\", \"label\":\"Fixed Tilt\", \"type\":SettingType.SLIDER, \"list\":rotation_list, \n\t\t\t\t\t\t\t\"default\":0, \"unit\":\"°\", \"range\":Vector3(-85, 85, 1), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\tadd_setting({ \"name\":\"random_tilt\", \"label\":\"Random Tilt ±\", \"type\":SettingType.SLIDER, \"list\":rotation_list, \n\t\t\t\t\t\t\t\"default\":10, \"unit\":\"°\", \"range\":Vector3(0, 85, 1), \"flags\":ALLOW_OUT_OF_BOUNDS })\n\tadd_setting({ \"name\":\"align_to_normal\", \"type\":SettingType.CHECKBOX, \"list\":rotation_list, \"default\":false })\n\t\n\tcolor_list = create_submenu(main_list, \"Color\", Layout.VERTICAL)\n\tadd_setting({ \"name\":\"vertex_color\", \"type\":SettingType.COLOR_SELECT, \"list\":color_list, \n\t\t\t\t\t\t\t\"default\":Color.WHITE })\n\tadd_setting({ \"name\":\"random_hue\", \"label\":\"Random Hue Shift ±\", \"type\":SettingType.SLIDER, \n\t\t\t\t\t\t\t\"list\":color_list, \"default\":0, \"unit\":\"°\", \"range\":Vector3(0, 360, 1) })\n\tadd_setting({ \"name\":\"random_darken\", \"type\":SettingType.SLIDER, \"list\":color_list, \"default\":50, \n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(0, 100, 1) })\n\tcollision_list = create_submenu(main_list, \"Collision\", Layout.VERTICAL)\n\tadd_setting({ \"name\":\"on_collision\", \"label\":\"On Collision\", \"type\":SettingType.CHECKBOX, \"list\":collision_list,\n\t\t\t\t\t\t\t\"default\":true })\n\tadd_setting({ \"name\":\"raycast_height\", \"label\":\"Raycast Height\", \"type\":SettingType.SLIDER, \n\t\t\t\t\t\t\t\"list\":collision_list, \"default\":10, \"unit\":\"m\", \"range\":Vector3(0, 200, .25) })\n\n\tif DisplayServer.is_touchscreen_available():\n\t\tadd_setting({ \"name\":\"invert\", \"label\":\"Invert\", \"type\":SettingType.CHECKBOX, \"list\":main_list, \"default\":false, \"flags\":ADD_SEPARATOR })\n\n\tvar spacer: Control = Control.new()\n\tspacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL\n\tmain_list.add_child(spacer, true)\n\n\t## Advanced Settings Menu\n\tadvanced_list = create_submenu(main_list, \"\", Layout.VERTICAL, false)\n\tadd_setting({ \"name\":\"auto_regions\", \"label\":\"Add regions while sculpting\", \"type\":SettingType.CHECKBOX, \n\t\t\t\t\t\t\t\"list\":advanced_list, \"default\":true })\n\tadvanced_list.add_child(HSeparator.new(), true)\n\tadd_setting({ \"name\":\"show_brush_texture\", \"type\":SettingType.CHECKBOX, \"list\":advanced_list, \"default\":true })\n\tadd_setting({ \"name\":\"align_to_view\", \"type\":SettingType.CHECKBOX, \"list\":advanced_list, \"default\":true })\n\tadd_setting({ \"name\":\"brush_spin_speed\", \"type\":SettingType.SLIDER, \"list\":advanced_list, \"default\":50, \n\t\t\t\t\t\t\t\"unit\":\"%\", \"range\":Vector3(0, 100, 1) })\n\tadd_setting({ \"name\":\"gamma\", \"type\":SettingType.SLIDER, \"list\":advanced_list, \"default\":1.0, \n\t\t\t\t\t\t\t\"unit\":\"γ\", \"range\":Vector3(0.1, 2.0, 0.01) })\n\n\nfunc create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout, p_hover_pop: bool = true) -> Container:\n\tvar menu_button: Button = Button.new()\n\tif p_button_name.is_empty():\n\t\tmenu_button.icon = get_theme_icon(\"GuiTabMenuHl\", \"EditorIcons\")\n\telse:\n\t\tmenu_button.set_text(p_button_name)\n\tmenu_button.set_toggle_mode(true)\n\tmenu_button.set_v_size_flags(SIZE_SHRINK_CENTER)\n\tmenu_button.toggled.connect(_on_show_submenu.bind(menu_button))\n\t\n\tvar submenu: PopupPanel = PopupPanel.new()\n\tsubmenu.popup_hide.connect(menu_button.set_pressed.bind(false))\n\tvar panel_style: StyleBox = get_theme_stylebox(\"panel\", \"PopupMenu\").duplicate()\n\tpanel_style.set_content_margin_all(10)\n\tsubmenu.set(\"theme_override_styles/panel\", panel_style)\n\tsubmenu.add_to_group(\"terrain3d_submenus\")\n\n\t# Pop up menu on hover, hide on exit\n\tif p_hover_pop:\n\t\tmenu_button.mouse_entered.connect(_on_show_submenu.bind(true, menu_button))\n\t\t\n\tsubmenu.mouse_entered.connect(func(): submenu.set_meta(\"mouse_entered\", true))\n\t\n\tsubmenu.mouse_exited.connect(func():\n\t\t# On mouse_exit, hide popup unless LineEdit focused\n\t\tvar focused_element: Control = submenu.gui_get_focus_owner()\n\t\tif not focused_element is LineEdit:\n\t\t\t_on_show_submenu(false, menu_button)\n\t\t\tsubmenu.set_meta(\"mouse_entered\", false)\n\t\t\treturn\n\t\t\t\n\t\tfocused_element.focus_exited.connect(func():\n\t\t\t# Close submenu once lineedit loses focus\n\t\t\tif not submenu.get_meta(\"mouse_entered\"):\n\t\t\t\t_on_show_submenu(false, menu_button)\n\t\t\t\tsubmenu.set_meta(\"mouse_entered\", false)\n\t\t)\n\t)\n\t\n\tvar sublist: Container\n\tmatch(p_layout):\n\t\tLayout.GRID:\n\t\t\tsublist = GridContainer.new()\n\t\tLayout.VERTICAL:\n\t\t\tsublist = VBoxContainer.new()\n\t\tLayout.HORIZONTAL, _:\n\t\t\tsublist = HBoxContainer.new()\n\t\n\tp_parent.add_child(menu_button, true)\n\tmenu_button.add_child(submenu, true)\n\tsubmenu.add_child(sublist, true)\n\t\n\treturn sublist\n\n\nfunc _on_show_submenu(p_toggled: bool, p_button: Button) -> void:\n\t# Don't show if mouse already down (from painting)\n\tif p_toggled and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):\n\t\treturn\n\t\n\t# Hide menu if mouse is not in button or panel \n\tvar button_rect: Rect2 = Rect2(p_button.get_screen_transform().origin, p_button.get_global_rect().size)\n\tvar in_button: bool = button_rect.has_point(DisplayServer.mouse_get_position())\n\tvar popup: PopupPanel = p_button.get_child(0)\n\tvar popup_rect: Rect2 = Rect2(popup.position, popup.size)\n\tvar in_popup: bool = popup_rect.has_point(DisplayServer.mouse_get_position())\n\tif not p_toggled and ( in_button or in_popup ):\n\t\treturn\n\t\n\t# Hide all submenus before possibly enabling the current one\n\tget_tree().call_group(\"terrain3d_submenus\", \"set_visible\", false)\n\tpopup.set_visible(p_toggled)\n\tvar popup_pos: Vector2 = p_button.get_screen_transform().origin\n\tpopup_pos.y -= popup.size.y\n\tif popup.get_child_count()>0 and popup.get_child(0) == advanced_list:\n\t\tpopup_pos.x -= popup.size.x - p_button.size.x\n\tpopup.set_position(popup_pos)\n\t\n\nfunc add_brushes(p_parent: Control) -> void:\n\tvar brush_list: GridContainer = create_submenu(p_parent, \"Brush\", Layout.GRID)\n\tbrush_list.name = \"BrushList\"\n\n\tvar brush_button_group: ButtonGroup = ButtonGroup.new()\n\tbrush_button_group.pressed.connect(_on_setting_changed)\n\tvar default_brush_btn: Button\n\t\n\tvar dir: DirAccess = DirAccess.open(BRUSH_PATH)\n\tif dir:\n\t\tdir.list_dir_begin()\n\t\tvar file_name = dir.get_next()\n\t\twhile file_name != \"\":\n\t\t\tif !dir.current_is_dir() and file_name.ends_with(\".exr\"):\n\t\t\t\tvar img: Image = Image.load_from_file(BRUSH_PATH + \"/\" + file_name)\n\t\t\t\tif img:\n\t\t\t\t\tvar value_range: Vector2 = Terrain3DUtil.get_min_max(img)\n\t\t\t\t\tif value_range.y - value_range.x < 0.333:\n\t\t\t\t\t\tpush_warning(\"'%s' has a low value range and may not be visible in the brush gallery or cursor\" % file_name)\n\t\t\t\t\tvar thumbimg: Image = img.duplicate()\n\t\t\t\t\timg.convert(Image.FORMAT_RF)\n\n\t\t\t\t\tif thumbimg.get_width() != 100 and thumbimg.get_height() != 100:\n\t\t\t\t\t\tthumbimg.resize(100, 100, Image.INTERPOLATE_CUBIC)\n\t\t\t\t\tthumbimg = Terrain3DUtil.black_to_alpha(thumbimg)\n\t\t\t\t\tthumbimg.convert(Image.FORMAT_LA8)\n\t\t\t\t\tvar thumbtex: ImageTexture = ImageTexture.create_from_image(thumbimg)\n\t\t\t\t\t\n\t\t\t\t\tvar brush_btn: Button = Button.new()\n\t\t\t\t\tbrush_btn.set_custom_minimum_size(Vector2.ONE * 100)\n\t\t\t\t\tbrush_btn.set_button_icon(thumbtex)\n\t\t\t\t\tbrush_btn.set_meta(\"image\", img)\n\t\t\t\t\tbrush_btn.set_expand_icon(true)\n\t\t\t\t\tbrush_btn.set_material(_get_brush_preview_material())\n\t\t\t\t\tbrush_btn.set_toggle_mode(true)\n\t\t\t\t\tbrush_btn.set_button_group(brush_button_group)\n\t\t\t\t\tbrush_btn.mouse_entered.connect(_on_brush_hover.bind(true, brush_btn))\n\t\t\t\t\tbrush_btn.mouse_exited.connect(_on_brush_hover.bind(false, brush_btn))\n\t\t\t\t\tbrush_list.add_child(brush_btn, true)\n\t\t\t\t\tif file_name == DEFAULT_BRUSH:\n\t\t\t\t\t\tdefault_brush_btn = brush_btn \n\t\t\t\t\t\n\t\t\t\t\tvar lbl: Label = Label.new()\n\t\t\t\t\tbrush_btn.name = file_name.get_basename().to_pascal_case()\n\t\t\t\t\tbrush_btn.add_child(lbl, true)\n\t\t\t\t\tlbl.text = brush_btn.name\n\t\t\t\t\tlbl.visible = false\n\t\t\t\t\tlbl.position.y = 70\n\t\t\t\t\tlbl.add_theme_color_override(\"font_shadow_color\", Color.BLACK)\n\t\t\t\t\tlbl.add_theme_constant_override(\"shadow_offset_x\", 1)\n\t\t\t\t\tlbl.add_theme_constant_override(\"shadow_offset_y\", 1)\n\t\t\t\t\tlbl.add_theme_font_size_override(\"font_size\", 16)\n\t\t\t\t\n\t\t\tfile_name = dir.get_next()\n\t\n\tbrush_list.columns = sqrt(brush_list.get_child_count()) + 2\n\t\n\tif not default_brush_btn:\n\t\tdefault_brush_btn = brush_button_group.get_buttons()[0]\n\tdefault_brush_btn.set_pressed(true)\n\t_generate_brush_texture(default_brush_btn)\n\t\n\tsettings[\"brush\"] = brush_button_group\n\n\tselect_brush_button = brush_list.get_parent().get_parent()\n\t# Optionally erase the main brush button text and replace it with the texture\n\tselect_brush_button.set_text(\"\")\n\tselect_brush_button.set_button_icon(default_brush_btn.get_button_icon())\n\tselect_brush_button.set_custom_minimum_size(Vector2.ONE * 36)\n\tselect_brush_button.set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER)\n\tselect_brush_button.set_expand_icon(true)\n\n\nfunc _on_brush_hover(p_hovering: bool, p_button: Button) -> void:\n\tif p_button.get_child_count() > 0:\n\t\tvar child = p_button.get_child(0)\n\t\tif child is Label:\n\t\t\tif p_hovering:\n\t\t\t\tchild.visible = true\n\t\t\telse:\n\t\t\t\tchild.visible = false\n\n\nfunc _on_pick(p_type: Terrain3DEditor.Tool) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DToolSettings: _on_pick: emitting picking: \", p_type, \", \", _on_picked)\n\temit_signal(\"picking\", p_type, _on_picked)\n\n\nfunc _on_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position: Vector3) -> void:\n\tmatch p_type:\n\t\tTerrain3DEditor.HEIGHT:\n\t\t\tsettings[\"height\"].value = p_color.r if not is_nan(p_color.r) else 0.\n\t\tTerrain3DEditor.COLOR:\n\t\t\tsettings[\"color\"].color = p_color if not is_nan(p_color.r) else Color.WHITE\n\t\tTerrain3DEditor.ROUGHNESS:\n\t\t\t# This converts 0,1 to -100,100\n\t\t\t# It also quantizes explicitly so picked values matches painted values\n\t\t\tsettings[\"roughness\"].value = round(200. * float(int(p_color.a * 255.) / 255. - .5)) if not is_nan(p_color.r) else 0.\n\t\tTerrain3DEditor.ANGLE:\n\t\t\tsettings[\"angle\"].value = p_color.r\n\t\tTerrain3DEditor.SCALE:\n\t\t\tsettings[\"scale\"].value = p_color.r\n\t\tTerrain3DEditor.INSTANCER:\n\t\t\tif p_color.r < 0:\n\t\t\t\treturn\n\t\t\tplugin.asset_dock.set_selected_by_asset_id(p_color.r)\n\t\tTerrain3DEditor.TEXTURE:\n\t\t\tif p_color.r < 0:\n\t\t\t\treturn\n\t\t\tplugin.asset_dock.set_selected_by_asset_id(p_color.r)\n\t_on_setting_changed()\n\n\nfunc _on_point_pick(p_type: Terrain3DEditor.Tool, p_name: String) -> void:\n\tassert(p_type == Terrain3DEditor.SCULPT)\n\tif plugin.debug:\n\t\tprint(\"Terrain3DToolSettings: _on_pick: emitting picking: \", p_type, \", \", _on_point_picked)\n\temit_signal(\"picking\", p_type, _on_point_picked.bind(p_name))\n\n\nfunc _on_point_picked(p_type: Terrain3DEditor.Tool, p_color: Color, p_global_position: Vector3, p_name: String) -> void:\n\tassert(p_type == Terrain3DEditor.SCULPT)\n\tvar point: Vector3 = p_global_position\n\tpoint.y = p_color.r\n\tsettings[p_name].add_point(point)\n\t_on_setting_changed()\n\n\nfunc add_setting(p_args: Dictionary) -> void:\n\tvar p_name: StringName = p_args.get(\"name\", \"\")\n\tvar p_label: String = p_args.get(\"label\", \"\") # Optional replacement for name\n\tvar p_type: SettingType = p_args.get(\"type\", SettingType.TYPE_MAX)\n\tvar p_list: Control = p_args.get(\"list\")\n\tvar p_default: Variant = p_args.get(\"default\")\n\tvar p_suffix: String = p_args.get(\"unit\", \"\")\n\tvar p_range: Vector3 = p_args.get(\"range\", Vector3(0, 0, 1))\n\tvar p_minimum: float = p_range.x\n\tvar p_maximum: float = p_range.y\n\tvar p_step: float = p_range.z\n\tvar p_flags: int = p_args.get(\"flags\", NONE)\n\tvar p_tooltip: String = p_args.get(\"tooltip\", \"\")\n\t\n\tif p_name.is_empty() or p_type == SettingType.TYPE_MAX:\n\t\treturn\n\n\tvar container: HBoxContainer = HBoxContainer.new()\n\tcontainer.custom_minimum_size.y = 36\n\tcontainer.set_v_size_flags(SIZE_EXPAND_FILL)\n\tvar control: Control\t# Houses the setting to be saved\n\tvar pending_children: Array[Control]\n\t\n\tmatch p_type:\n\t\tSettingType.LABEL:\n\t\t\tvar label := Label.new()\n\t\t\tlabel.set_text(p_label)\n\t\t\tpending_children.push_back(label)\n\t\t\tcontrol = label\n\n\t\tSettingType.CHECKBOX:\n\t\t\tvar checkbox := CheckBox.new()\n\t\t\tif !(p_flags & NO_SAVE):\n\t\t\t\tcheckbox.set_pressed_no_signal(plugin.get_setting(ES_TOOL_SETTINGS + p_name, p_default))\n\t\t\t\tcheckbox.toggled.connect( (\n\t\t\t\t\tfunc(value, path):\n\t\t\t\t\t\tplugin.set_setting(path, value)\n\t\t\t\t).bind(ES_TOOL_SETTINGS + p_name) )\n\t\t\telse:\n\t\t\t\tcheckbox.set_pressed_no_signal(p_default)\t\t\t\t\n\t\t\tcheckbox.pressed.connect(_on_setting_changed)\n\t\t\tpending_children.push_back(checkbox)\n\t\t\tcontrol = checkbox\n\t\t\t\n\t\tSettingType.COLOR_SELECT:\n\t\t\tvar picker := ColorPickerButton.new()\n\t\t\tpicker.set_custom_minimum_size(Vector2(100, 25))\n\t\t\tpicker.edit_alpha = false\n\t\t\tpicker.get_picker().set_color_mode(ColorPicker.MODE_HSV)\n\t\t\tif !(p_flags & NO_SAVE):\n\t\t\t\tpicker.set_pick_color(plugin.get_setting(ES_TOOL_SETTINGS + p_name, p_default))\n\t\t\t\tpicker.color_changed.connect( (\n\t\t\t\t\tfunc(value, path):\n\t\t\t\t\t\tplugin.set_setting(path, value)\n\t\t\t\t).bind(ES_TOOL_SETTINGS + p_name) )\n\t\t\telse:\n\t\t\t\tpicker.set_pick_color(p_default)\n\t\t\tpicker.color_changed.connect(_on_setting_changed)\n\t\t\tpending_children.push_back(picker)\n\t\t\tcontrol = picker\n\n\t\tSettingType.PICKER:\n\t\t\tvar button := Button.new()\n\t\t\tbutton.set_v_size_flags(SIZE_SHRINK_CENTER)\n\t\t\tbutton.icon = get_theme_icon(\"ColorPick\", \"EditorIcons\")\n\t\t\tbutton.pressed.connect(_on_pick.bind(p_default))\n\t\t\tpending_children.push_back(button)\n\t\t\tcontrol = button\n\n\t\tSettingType.MULTI_PICKER:\n\t\t\tvar multi_picker: HBoxContainer = MultiPicker.new()\n\t\t\tmulti_picker.pressed.connect(_on_point_pick.bind(p_default, p_name))\n\t\t\tmulti_picker.value_changed.connect(_on_setting_changed)\n\t\t\tpending_children.push_back(multi_picker)\n\t\t\tcontrol = multi_picker\n\n\t\tSettingType.OPTION:\n\t\t\tvar option := OptionButton.new()\n\t\t\tfor i in int(p_maximum):\n\t\t\t\toption.add_item(\"a\", i)\n\t\t\toption.selected = p_minimum\n\t\t\toption.item_selected.connect(_on_setting_changed)\n\t\t\tpending_children.push_back(option)\n\t\t\tcontrol = option\n\n\t\tSettingType.SLIDER, SettingType.DOUBLE_SLIDER:\n\t\t\tvar slider: Control\n\t\t\tif p_type == SettingType.SLIDER:\n\t\t\t\t# Create an editable value box\n\t\t\t\tvar spin_slider := EditorSpinSlider.new()\n\t\t\t\tspin_slider.set_flat(false)\n\t\t\t\tspin_slider.set_hide_slider(true)\n\t\t\t\tspin_slider.value_changed.connect(_on_setting_changed)\n\t\t\t\tspin_slider.set_max(p_maximum)\n\t\t\t\tspin_slider.set_min(p_minimum)\n\t\t\t\tspin_slider.set_step(p_step)\n\t\t\t\tspin_slider.set_suffix(p_suffix)\n\t\t\t\tspin_slider.set_v_size_flags(SIZE_SHRINK_CENTER)\n\t\t\t\tspin_slider.set_custom_minimum_size(Vector2(65, 0))\n\n\t\t\t\t# Create horizontal slider linked to the above box\n\t\t\t\tslider = HSlider.new()\n\t\t\t\tslider.share(spin_slider)\n\t\t\t\tif p_flags & ALLOW_LARGER:\n\t\t\t\t\tslider.set_allow_greater(true)\n\t\t\t\tif p_flags & ALLOW_SMALLER:\n\t\t\t\t\tslider.set_allow_lesser(true)\n\t\t\t\t\n\t\t\t\tpending_children.push_back(slider)\n\t\t\t\tpending_children.push_back(spin_slider)\n\t\t\t\tcontrol = spin_slider\n\t\t\t\t\t\t\n\t\t\telse: # DOUBLE_SLIDER\n\t\t\t\tvar label := Label.new()\n\t\t\t\tlabel.set_custom_minimum_size(Vector2(60, 0))\n\t\t\t\tlabel.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)\n\t\t\t\tslider = DoubleSlider.new()\n\t\t\t\tslider.label = label\n\t\t\t\tslider.suffix = p_suffix\n\t\t\t\tslider.value_changed.connect(_on_setting_changed)\n\t\t\t\tpending_children.push_back(slider)\n\t\t\t\tpending_children.push_back(label)\n\t\t\t\tcontrol = slider\n\t\t\t\n\t\t\tslider.set_min(p_minimum)\n\t\t\tslider.set_max(p_maximum)\n\t\t\tslider.set_step(p_step)\n\t\t\tslider.set_value(p_default)\n\t\t\tslider.set_v_size_flags(SIZE_SHRINK_CENTER)\n\t\t\tslider.set_custom_minimum_size(Vector2(50, 10))\n\n\t\t\tif !(p_flags & NO_SAVE):\n\t\t\t\tslider.set_value(plugin.get_setting(ES_TOOL_SETTINGS + p_name, p_default))\n\t\t\t\tslider.value_changed.connect( (\n\t\t\t\t\tfunc(value, path):\n\t\t\t\t\t\tplugin.set_setting(path, value)\n\t\t\t\t).bind(ES_TOOL_SETTINGS + p_name) )\n\t\t\telse:\n\t\t\t\tslider.set_value(p_default)\n\n\tcontrol.name = p_name.to_pascal_case()\n\tif not p_tooltip.is_empty():\n\t\tcontrol.tooltip_text = p_tooltip\n\tsettings[p_name] = control\n\n\t# Setup button labels\n\tif not (p_flags & NO_LABEL):\n\t\t# Labels are actually buttons styled to look like labels\n\t\tvar label := Button.new()\n\t\tlabel.set(\"theme_override_styles/normal\", get_theme_stylebox(\"normal\", \"Label\"))\n\t\tlabel.set(\"theme_override_styles/hover\", get_theme_stylebox(\"normal\", \"Label\"))\n\t\tlabel.set(\"theme_override_styles/pressed\", get_theme_stylebox(\"normal\", \"Label\"))\n\t\tlabel.set(\"theme_override_styles/focus\", get_theme_stylebox(\"normal\", \"Label\"))\n\t\tlabel.pressed.connect(_on_label_pressed.bind(p_name, p_default))\n\t\tif p_label.is_empty():\n\t\t\tlabel.set_text(p_name.capitalize() + \": \")\n\t\telse:\n\t\t\tlabel.set_text(p_label.capitalize() + \": \")\n\t\tpending_children.push_front(label)\n\n\t# Add separators to front\n\tif p_flags & ADD_SEPARATOR:\n\t\tpending_children.push_front(VSeparator.new())\n\tif p_flags & ADD_SPACER:\n\t\tvar spacer := Control.new()\n\t\tspacer.set_custom_minimum_size(Vector2(5, 0))\n\t\tpending_children.push_front(spacer)\n\n\t# Add all children to container and list\n\tfor child in pending_children:\n\t\tcontainer.add_child(child, true)\n\tp_list.add_child(container, true)\n\n\n# If label button is pressed, reset value to default or toggle checkbox\nfunc _on_label_pressed(p_name: String, p_default: Variant) -> void:\n\tvar control: Control = settings.get(p_name)\n\tif not control:\n\t\treturn\n\tif control is CheckBox:\n\t\tset_setting(p_name, !control.button_pressed)\n\telif p_default != null:\n\t\tset_setting(p_name, p_default)\n\n\nfunc get_settings() -> Dictionary:\n\tvar dict: Dictionary\n\tfor key in settings.keys():\n\t\tdict[key] = get_setting(key)\n\treturn dict\n\n\nfunc get_setting(p_setting: String) -> Variant:\n\tvar object: Object = settings.get(p_setting)\n\tvar value: Variant\n\tif object is Range:\n\t\tvalue = object.get_value()\n\t\t# Adjust widths of all sliders on update of values\n\t\tvar width: float = clamp( (1 + _count_digits(value)) * 19., 50, 80) * clamp(EditorInterface.get_editor_scale(), .9, 2)\n\t\tobject.set_custom_minimum_size(Vector2(width, 0))\n\telif object is DoubleSlider:\n\t\tvalue = object.get_value()\n\telif object is ButtonGroup: # \"brush\"\n\t\tvalue = selected_brush_imgs\n\telif object is CheckBox:\n\t\tvalue = object.is_pressed()\n\telif object is ColorPickerButton:\n\t\tvalue = object.color\n\telif object is MultiPicker:\n\t\tvalue = object.get_points()\n\tif value == null:\n\t\tvalue = 0\n\treturn value\n\n\nfunc set_setting(p_setting: String, p_value: Variant) -> void:\n\tvar object: Object = settings.get(p_setting)\n\tif object is DoubleSlider: # Expects p_value is Vector2\n\t\tobject.set_value(p_value)\n\telif object is Range:\n\t\tobject.set_value(p_value)\n\telif object is ButtonGroup: # Expects p_value is Array [ \"button name\", boolean ]\n\t\tif p_value is Array and p_value.size() == 2:\n\t\t\tfor button in object.get_buttons():\n\t\t\t\tif button.name == p_value[0]:\n\t\t\t\t\tbutton.button_pressed = p_value[1]\n\telif object is CheckBox:\n\t\tobject.button_pressed = p_value\n\telif object is ColorPickerButton:\n\t\tobject.color = p_value\n\t\tplugin.set_setting(ES_TOOL_SETTINGS + p_setting, p_value) # Signal doesn't fire on CPB\n\telif object is MultiPicker: # Expects p_value is PackedVector3Array\n\t\tobject.points = p_value\n\t_on_setting_changed(object)\n\n\nfunc show_settings(p_settings: PackedStringArray) -> void:\n\tfor setting in settings.keys():\n\t\tvar object: Object = settings[setting]\n\t\tif object is Control:\n\t\t\tif setting in p_settings:\n\t\t\t\tobject.get_parent().show()\n\t\t\telse:\n\t\t\t\tobject.get_parent().hide()\n\tif select_brush_button:\n\t\tif not \"brush\" in p_settings:\n\t\t\tselect_brush_button.hide()\n\t\telse:\n\t\t\tselect_brush_button.show()\n\n\nfunc _on_setting_changed(p_setting: Variant = null) -> void:\n\t# If a brush was selected\n\tif p_setting is Button and p_setting.get_parent().name == \"BrushList\":\n\t\t_generate_brush_texture(p_setting)\n\t\t# Optionally Set selected brush texture in main brush button\n\t\tif select_brush_button:\n\t\t\tselect_brush_button.set_button_icon(p_setting.get_button_icon())\n\t\t# Hide popup\n\t\tp_setting.get_parent().get_parent().set_visible(false)\n\t\t# Hide label\n\t\tif p_setting.get_child_count() > 0:\n\t\t\tp_setting.get_child(0).visible = false\n\tif plugin.debug:\n\t\tprint(\"Terrain3DToolSettings: _on_setting_changed: emitting setting_changed: \", p_setting)\t\t\t\n\temit_signal(\"setting_changed\", p_setting)\n\n\nfunc _generate_brush_texture(p_btn: Button) -> void:\n\tif p_btn is Button:\n\t\tvar img: Image = p_btn.get_meta(\"image\")\n\t\tif img.get_width() < 1024 and img.get_height() < 1024:\n\t\t\timg = img.duplicate()\n\t\t\timg.resize(1024, 1024, Image.INTERPOLATE_CUBIC)\n\t\tvar tex: ImageTexture = ImageTexture.create_from_image(img)\n\t\tselected_brush_imgs = [ img, tex ]\n\n\nfunc _on_drawable_toggled(p_button_pressed: bool) -> void:\n\tif not p_button_pressed:\n\t\tsettings[\"gradient_points\"].clear()\n\n\nfunc _get_brush_preview_material() -> ShaderMaterial:\n\tif !brush_preview_material:\n\t\tbrush_preview_material = ShaderMaterial.new()\n\t\tvar shader: Shader = Shader.new()\n\t\tvar code: String = \"shader_type canvas_item;\\n\"\n\t\tcode += \"varying vec4 v_vertex_color;\\n\"\n\t\tcode += \"void vertex() {\\n\"\n\t\tcode += \"\tv_vertex_color = COLOR;\\n\"\n\t\tcode += \"}\\n\"\n\t\tcode += \"void fragment(){\\n\"\n\t\tcode += \"\tvec4 tex = texture(TEXTURE, UV);\\n\"\n\t\tcode += \"\tCOLOR.a *= pow(tex.r, 0.666);\\n\"\n\t\tcode += \"\tCOLOR.rgb = v_vertex_color.rgb;\\n\"\n\t\tcode += \"}\\n\"\n\t\tshader.set_code(code)\n\t\tbrush_preview_material.set_shader(shader)\n\treturn brush_preview_material\n\n\n# Counts digits of a number including negative sign, decimal points, and up to 3 decimals \nfunc _count_digits(p_value: float) -> int:\n\tvar count: int = 1\n\tfor i in range(5, 0, -1):\n\t\tif abs(p_value) >= pow(10, i):\n\t\t\tcount = i+1\n\t\t\tbreak\n\tif p_value - floor(p_value) >= .1:\n\t\tcount += 1 # For the decimal\n\t\tif p_value*10 - floor(p_value*10.) >= .1: \n\t\t\tcount += 1\n\t\t\tif p_value*100 - floor(p_value*100.) >= .1: \n\t\t\t\tcount += 1\n\t\t\t\tif p_value*1000 - floor(p_value*1000.) >= .1: \n\t\t\t\t\tcount += 1\n\t# Negative sign\n\tif p_value < 0:\n\t\tcount += 1\n\treturn count\n\n\nfunc inverse_slope_range() -> void:\n\tvar slope_range: Vector2 = get_setting(\"slope\")\n\tif slope_range.y - slope_range.x > 89.99:\n\t\treturn\n\tif slope_range.x == 0.0:\n\t\tslope_range = Vector2(slope_range.y, 90.0)\n\telif slope_range.y == 90.0:\n\t\tslope_range = Vector2(0.0, slope_range.x)\n\telse:\n\t\t# If midpoint <= 45, inverse to 90, else to 0\n\t\tvar midpoint: float = 0.5 * (slope_range.x + slope_range.y)\n\t\tif midpoint <= 45.0:\n\t\t\tslope_range = Vector2(slope_range.y, 90.0)\n\t\telse:\n\t\t\tslope_range = Vector2(0.0, slope_range.x)\n\tset_setting(\"slope\", slope_range)\n"
  },
  {
    "path": "project/addons/terrain_3d/src/tool_settings.gd.uid",
    "content": "uid://ciskaaennrffu\n"
  },
  {
    "path": "project/addons/terrain_3d/src/toolbar.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Toolbar for Terrain3D\nextends VFlowContainer\n\nsignal tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation)\n\nconst ICON_REGION_ADD: String = \"res://addons/terrain_3d/icons/region_add.svg\"\nconst ICON_REGION_REMOVE: String = \"res://addons/terrain_3d/icons/region_remove.svg\"\nconst ICON_HEIGHT_ADD: String = \"res://addons/terrain_3d/icons/height_add.svg\"\nconst ICON_HEIGHT_SUB: String = \"res://addons/terrain_3d/icons/height_sub.svg\"\nconst ICON_HEIGHT_FLAT: String = \"res://addons/terrain_3d/icons/height_flat.svg\"\nconst ICON_HEIGHT_SLOPE: String = \"res://addons/terrain_3d/icons/height_slope.svg\"\nconst ICON_HEIGHT_SMOOTH: String = \"res://addons/terrain_3d/icons/height_smooth.svg\"\nconst ICON_PAINT_TEXTURE: String = \"res://addons/terrain_3d/icons/texture_paint.svg\"\nconst ICON_SPRAY_TEXTURE: String = \"res://addons/terrain_3d/icons/texture_spray.svg\"\nconst ICON_COLOR: String = \"res://addons/terrain_3d/icons/color_paint.svg\"\nconst ICON_WETNESS: String = \"res://addons/terrain_3d/icons/wetness.svg\"\nconst ICON_AUTOSHADER: String = \"res://addons/terrain_3d/icons/autoshader.svg\"\nconst ICON_HOLES: String = \"res://addons/terrain_3d/icons/holes.svg\"\nconst ICON_NAVIGATION: String = \"res://addons/terrain_3d/icons/navigation.svg\"\nconst ICON_INSTANCER: String = \"res://addons/terrain_3d/icons/multimesh.svg\"\n\nvar add_tool_group: ButtonGroup = ButtonGroup.new()\nvar sub_tool_group: ButtonGroup = ButtonGroup.new()\nvar buttons: Dictionary\nvar plugin: EditorPlugin\n\n\nfunc _init() -> void:\n\tset_custom_minimum_size(Vector2(20, 0))\n\n\nfunc _ready() -> void:\n\tadd_tool_group.pressed.connect(_on_tool_selected)\n\tsub_tool_group.pressed.connect(_on_tool_selected)\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.REGION, \n\t\t\"add_text\":\"Add Region (E)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_REGION_ADD,\n\t\t\"sub_text\":\"Remove Region\", \"sub_op\":Terrain3DEditor.SUBTRACT, \"sub_icon\":ICON_REGION_REMOVE })\n\t\n\tadd_child(HSeparator.new())\n\t\n\tadd_tool_button({ \"tool\":Terrain3DEditor.SCULPT, \n\t\t\"add_text\":\"Raise (R)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_HEIGHT_ADD,\n\t\t\"sub_text\":\"Lower (R)\", \"sub_op\":Terrain3DEditor.SUBTRACT, \"sub_icon\":ICON_HEIGHT_SUB })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.SCULPT, \n\t\t\"add_text\":\"Smooth (Shift)\", \"add_op\":Terrain3DEditor.AVERAGE, \"add_icon\":ICON_HEIGHT_SMOOTH })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.HEIGHT, \n\t\t\"add_text\":\"Height (H)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_HEIGHT_FLAT,\n\t\t\"sub_text\":\"Height (H)\", \"sub_op\":Terrain3DEditor.SUBTRACT, \"sub_icon\":ICON_HEIGHT_FLAT })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.SCULPT, \n\t\t\"add_text\":\"Slope (S)\", \"add_op\":Terrain3DEditor.GRADIENT, \"add_icon\":ICON_HEIGHT_SLOPE })\n\n\tadd_child(HSeparator.new())\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.TEXTURE, \n\t\t\"add_text\":\"Paint Texture (B)\", \"add_op\":Terrain3DEditor.REPLACE, \"add_icon\":ICON_PAINT_TEXTURE })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.TEXTURE, \n\t\t\"add_text\":\"Spray Texture (V)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_SPRAY_TEXTURE })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.AUTOSHADER,\n\t\t\"add_text\":\"Paint Autoshader (A)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_AUTOSHADER,\n\t\t\"sub_text\":\"Disable Autoshader (A)\", \"sub_op\":Terrain3DEditor.SUBTRACT })\n\n\tadd_child(HSeparator.new())\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.COLOR,\n\t\t\"add_text\":\"Paint Color (C)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_COLOR,\n\t\t\"sub_text\":\"Remove Color (C)\", \"sub_op\":Terrain3DEditor.SUBTRACT })\n\t\n\tadd_tool_button({ \"tool\":Terrain3DEditor.ROUGHNESS,\n\t\t\"add_text\":\"Paint Wetness (W)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_WETNESS,\n\t\t\"sub_text\":\"Remove Wetness (W)\", \"sub_op\":Terrain3DEditor.SUBTRACT })\n\n\tadd_child(HSeparator.new())\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.HOLES,\n\t\t\"add_text\":\"Add Holes (X)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_HOLES,\n\t\t\"sub_text\":\"Remove Holes (X)\", \"sub_op\":Terrain3DEditor.SUBTRACT })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.NAVIGATION,\n\t\t\"add_text\":\"Paint Navigable Area (N)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_NAVIGATION,\n\t\t\"sub_text\":\"Remove Navigable Area (N)\", \"sub_op\":Terrain3DEditor.SUBTRACT })\n\n\tadd_tool_button({ \"tool\":Terrain3DEditor.INSTANCER,\n\t\t\"add_text\":\"Instance Meshes (I)\", \"add_op\":Terrain3DEditor.ADD, \"add_icon\":ICON_INSTANCER,\n\t\t\"sub_text\":\"Remove Meshes (I)\", \"sub_op\":Terrain3DEditor.SUBTRACT })\n\n\t# Select first button\n\tvar buttons: Array[BaseButton] = add_tool_group.get_buttons()\n\tbuttons[0].set_pressed(true)\n\tshow_add_buttons(true)\n\n\nfunc add_tool_button(p_params: Dictionary) -> void:\n\t# Additive button\n\tvar button := Button.new()\n\tvar name_str: String = p_params.get(\"add_text\", \"blank\").get_slice('(', 0).to_pascal_case()\n\tbutton.set_name(name_str)\n\tbutton.set_meta(\"Tool\", p_params.get(\"tool\", 0))\n\tbutton.set_meta(\"Operation\", p_params.get(\"add_op\", 0))\n\tbutton.set_meta(\"ID\", add_tool_group.get_buttons().size() + 1)\n\tbutton.set_tooltip_text(p_params.get(\"add_text\", \"blank\"))\n\tbutton.set_button_icon(load(p_params.get(\"add_icon\")))\n\tbutton.set_flat(true)\n\tbutton.set_toggle_mode(true)\n\tbutton.set_h_size_flags(SIZE_SHRINK_END)\n\tbutton.set_button_group(p_params.get(\"group\", add_tool_group))\n\tadd_child(button, true)\n\tbuttons[button.get_name()] = button\n\n\t# Subtractive button\n\tvar button2: Button\n\tif p_params.has(\"sub_text\"):\n\t\tbutton2 = Button.new()\n\t\tname_str = p_params.get(\"sub_text\", \"blank\").get_slice('(', 0).to_pascal_case()\n\t\tbutton2.set_name(name_str)\n\t\tbutton2.set_meta(\"Tool\", p_params.get(\"tool\", 0))\n\t\tbutton2.set_meta(\"Operation\", p_params.get(\"sub_op\", 0))\n\t\tbutton2.set_meta(\"ID\", button.get_meta(\"ID\"))\n\t\tbutton2.set_tooltip_text(p_params.get(\"sub_text\", \"blank\"))\n\t\tbutton2.set_button_icon(load(p_params.get(\"sub_icon\", p_params.get(\"add_icon\"))))\n\t\tbutton2.set_flat(true)\n\t\tbutton2.set_toggle_mode(true)\n\t\tbutton2.set_h_size_flags(SIZE_SHRINK_END)\n\telse:\n\t\tbutton2 = button.duplicate()\n\tbutton2.set_button_group(p_params.get(\"group\", sub_tool_group))\n\tadd_child(button2, true)\n\tbuttons[button2.get_name()] = button\n\n\nfunc get_button(p_name: String) -> Button:\n\treturn buttons.get(p_name, null)\n\n\nfunc show_add_buttons(p_enable: bool) -> void:\n\tfor button in add_tool_group.get_buttons():\n\t\tbutton.visible = p_enable\n\tfor button in sub_tool_group.get_buttons():\n\t\tbutton.visible = !p_enable\n\n\nfunc _on_tool_selected(p_button: BaseButton) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DToolbar: _on_tool_selected: \", p_button)\n\t# Select same tool on negative bar\n\tvar group: ButtonGroup = p_button.get_button_group()\n\tvar change_group: ButtonGroup = add_tool_group if group == sub_tool_group else sub_tool_group\n\tvar id: int = p_button.get_meta(\"ID\", -2)\n\tfor button in change_group.get_buttons():\n\t\tbutton.set_pressed_no_signal(button.get_meta(\"ID\", -1) == id)\n\tif plugin.debug:\n\t\tprint(\"Terrain3DToolbar: _on_tool_selected: emitting tool_changed, \", \n\t\t\tp_button.get_meta(\"Tool\", Terrain3DEditor.TOOL_MAX), \", \", \n\t\t\tp_button.get_meta(\"Operation\", Terrain3DEditor.OP_MAX))\n\temit_signal(\"tool_changed\", p_button.get_meta(\"Tool\", Terrain3DEditor.TOOL_MAX), p_button.get_meta(\"Operation\", Terrain3DEditor.OP_MAX))\n\n\nfunc change_tool(p_name: String) -> void:\n\tvar btn: Button = get_node_or_null(p_name)\n\tif plugin.debug:\n\t\tprint(\"Terrain3DToolbar: change_tool: \", p_name, \", pressed: \", btn and btn.button_pressed)\n\tif btn and not btn.button_pressed:\n\t\tawait get_tree().process_frame\n\t\tbtn.set_pressed(true)\n"
  },
  {
    "path": "project/addons/terrain_3d/src/toolbar.gd.uid",
    "content": "uid://b1j37u6utjbom\n"
  },
  {
    "path": "project/addons/terrain_3d/src/ui.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# UI for Terrain3D\nextends Node\n\n\n# Includes\nconst TerrainMenu: Script = preload(\"res://addons/terrain_3d/menu/terrain_menu.gd\")\nconst TerrainToolbar: Script = preload(\"res://addons/terrain_3d/src/toolbar.gd\")\nconst TerrainToolSettings: Script = preload(\"res://addons/terrain_3d/src/tool_settings.gd\")\nconst OperationBuilder: Script = preload(\"res://addons/terrain_3d/src/operation_builder.gd\")\nconst GradientOperationBuilder: Script = preload(\"res://addons/terrain_3d/src/gradient_operation_builder.gd\")\n\n# Decal colors\nconst COLOR_RAISE := Color(1., 1., 1.) # White\nconst COLOR_LOWER := Color(0.2, 0.2, 0.2) # Dark gray\nconst COLOR_SMOOTH := Color(0.5, 0.0, 0.2) # Dark Red\nconst COLOR_AVERAGE := Color(0.6, 0.1, 0.3) # Neutral purple\nconst COLOR_LIFT := Color(1.0, 0.6, 0.0) # Bright orange\nconst COLOR_FLATTEN := Color(0.0, 0.6, 1.0) # Cyan\nconst COLOR_HEIGHT := Color(0.0, 0.8, 0.8) # Brighter cyan\nconst COLOR_SLOPE := Color(1.0, 1.0, 0.0) # Bright yellow\nconst COLOR_PAINT := Color(0.0, 0.5, 0.0) # Dark green\nconst COLOR_SPRAY := Color(0.4, 0.8, 0.4) # Lighter green\nconst COLOR_UNSPRAY := Color(0.5, 0.2, 0.5) # Neutral purple\nconst COLOR_WET := Color(0.4, 0.6, 1.0) # Light blue\nconst COLOR_DRY := Color(0.6, 0.4, 0.0) # Warm brown\nconst COLOR_AUTOSHADER := Color(0.36, 0.2, 0.09) # Chocolate\nconst COLOR_HOLES := Color(0.1, 0.1, 0.1) # Near-black\nconst COLOR_NAVIGATION := Color(0.5, 0.2, 0.5) # Purple\nconst COLOR_INSTANCE := Color(0.863, 0.08, 0.235) # Crimson\nconst COLOR_UNINSTANCE := Color(0.2, 0.9, 0.6) # Cyan-green\nconst COLOR_PICK := Color.WHITE\n\nconst OP_NONE: int = 0x0\nconst OP_POSITIVE_ONLY: int = 0x01\nconst OP_NEGATIVE_ONLY: int = 0x02\n\n@onready var region_texture := ImageTexture.new() :\n\tset(value):\n\t\tvar image: Image = Image.create_empty(1, 1, false, Image.FORMAT_R8)\n\t\timage.fill(Color.WHITE)\n\t\tvalue.create_from_image(image)\n\t\tregion_texture = value\nvar plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors\nvar toolbar: TerrainToolbar\nvar tool_settings: TerrainToolSettings\nvar terrain_menu: TerrainMenu\nvar setting_has_changed: bool = false\nvar visible: bool = false\nvar picking: int = Terrain3DEditor.TOOL_MAX\nvar picking_callback: Callable\nvar brush_data: Dictionary\nvar operation_builder: OperationBuilder\nvar active_tool: Terrain3DEditor.Tool = Terrain3DEditor.TOOL_MAX\nvar _selected_tool: Terrain3DEditor.Tool = Terrain3DEditor.TOOL_MAX\nvar active_operation: Terrain3DEditor.Operation = Terrain3DEditor.OP_MAX\nvar _selected_operation: Terrain3DEditor.Operation = Terrain3DEditor.OP_MAX\nvar inverted_input: bool = false\n\n# 3 Editor decals: 0 = cursor, 1 = slope point1, 2 = slope point2\nvar mat_rid: RID\nvar editor_brush_texture_rid: RID = RID()\nvar editor_decal_position: Array[Vector2] = [Vector2(), Vector2(), Vector2()]\nvar editor_decal_rotation: Array[float] = [0., 0., 0.]\nvar editor_decal_size: Array[float] = [0., 0., 0.]\nvar editor_decal_color: Array[Color] = [Color(), Color(), Color()]\nvar editor_decal_visible: Array[bool] = [false, false, false]\nvar editor_decal_part: Array[bool] = [true, true] # Decal[0] cursor components: brush, reticle\nvar editor_decal_timer: Timer\nvar editor_decal_fade: float :\n\tset(value):\n\t\teditor_decal_fade = value\n\t\tif editor_decal_color.size() > 0:\n\t\t\teditor_decal_color[0].a = value\n\t\t\tif is_shader_valid():\n\t\t\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_color\", editor_decal_color)\n\t\t\t\tif value < 0.001:\n\t\t\t\t\tvar r_map: PackedInt32Array = plugin.terrain.data.get_region_map()\n\t\t\t\t\tRenderingServer.material_set_param(mat_rid, \"_region_map\", r_map)\n\n\nfunc _enter_tree() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: _enter_tree()\")\n\n\ttoolbar = TerrainToolbar.new()\n\ttoolbar.plugin = plugin\n\ttoolbar.hide()\n\ttoolbar.tool_changed.connect(_on_tool_changed)\n\t\n\ttool_settings = TerrainToolSettings.new()\n\ttool_settings.setting_changed.connect(_on_setting_changed)\n\ttool_settings.picking.connect(_on_picking)\n\ttool_settings.plugin = plugin\n\ttool_settings.hide()\n\n\tterrain_menu = TerrainMenu.new()\n\tterrain_menu.plugin = plugin\n\tterrain_menu.hide()\n\n\tplugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, toolbar)\n\tplugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, tool_settings)\n\tplugin.add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, terrain_menu)\n\n\t_on_tool_changed(Terrain3DEditor.REGION, Terrain3DEditor.ADD)\n\t\n\teditor_decal_timer = Timer.new()\n\teditor_decal_timer.wait_time = .5\n\teditor_decal_timer.one_shot = true\n\teditor_decal_timer.timeout.connect(func():\n\t\tget_tree().create_tween().tween_property(self, \"editor_decal_fade\", 0.0, 0.15))\n\tadd_child(editor_decal_timer)\n\n\nfunc _exit_tree() -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: _exit_tree()\")\n\tplugin.remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, toolbar)\n\tplugin.remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, tool_settings)\n\ttoolbar.queue_free()\n\ttool_settings.queue_free()\n\tterrain_menu.queue_free()\n\teditor_decal_timer.queue_free()\n\n\nfunc set_visible(p_visible: bool, p_menu_only: bool = false) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: set_visible(%s, %s)\" % [ p_visible, p_menu_only ])\n\n\tterrain_menu.set_visible(p_visible)\n\n\tif p_menu_only:\n\t\ttoolbar.set_visible(false)\n\t\ttool_settings.set_visible(false)\n\telse:\n\t\tvisible = p_visible\n\t\ttoolbar.set_visible(p_visible)\n\t\ttool_settings.set_visible(p_visible)\n\n\tif plugin.editor and plugin.terrain and p_visible:\n\t\t\tawait get_tree().process_frame # Won't work, otherwise\n\t\t\tif plugin.debug:\n\t\t\t\tprint(\"Terrain3DUI: set_visible: calling _on_tool_changed()\")\n\t\t\t_on_tool_changed(_selected_tool, _selected_operation)\n\t\t\tif _selected_tool in [ Terrain3DEditor.REGION, Terrain3DEditor.NAVIGATION ]:\n\t\t\t\tplugin.terrain.material.update(Terrain3DMaterial.FULL_REBUILD)\n\n\t\nfunc set_menu_visibility(p_list: Control, p_visible: bool) -> void:\n\tif p_list:\n\t\tp_list.get_parent().get_parent().visible = p_visible\n\t\n\nfunc _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: _on_tool_changed: \", p_tool, \", \", p_operation)\n\tif active_tool == p_tool and active_operation == p_operation:\n\t\treturn\n\t_selected_tool = p_tool\n\t_selected_operation = p_operation\n\tclear_picking()\n\tset_menu_visibility(tool_settings.advanced_list, true)\n\tset_menu_visibility(tool_settings.scale_list, false)\n\tset_menu_visibility(tool_settings.rotation_list, false)\n\tset_menu_visibility(tool_settings.height_list, false)\n\tset_menu_visibility(tool_settings.color_list, false)\n\tset_menu_visibility(tool_settings.collision_list, false)\n\n\t# Select which settings to show. Options in tool_settings.gd:_ready\n\tvar to_show: PackedStringArray = []\n\t\n\tmatch _selected_tool:\n\t\tTerrain3DEditor.REGION:\n\t\t\tto_show.push_back(\"instructions\")\n\t\t\tto_show.push_back(\"invert\")\n\t\t\tset_menu_visibility(tool_settings.advanced_list, false)\n\n\t\tTerrain3DEditor.SCULPT:\n\t\t\tto_show.push_back(\"brush\")\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"strength\")\n\t\t\tif _selected_operation in [Terrain3DEditor.ADD, Terrain3DEditor.SUBTRACT]:\n\t\t\t\t\tto_show.push_back(\"invert\")\n\t\t\telif _selected_operation == Terrain3DEditor.GRADIENT:\n\t\t\t\tto_show.push_back(\"gradient_points\")\n\t\t\t\tto_show.push_back(\"drawable\")\n\n\t\tTerrain3DEditor.HEIGHT:\n\t\t\tto_show.push_back(\"brush\")\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"strength\")\n\t\t\tto_show.push_back(\"height\")\n\t\t\tto_show.push_back(\"height_picker\")\n\t\t\tto_show.push_back(\"invert\")\n\n\t\tTerrain3DEditor.TEXTURE:\n\t\t\tto_show.push_back(\"brush\")\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"enable_texture\")\n\t\t\tto_show.push_back(\"texture_picker\")\n\t\t\tif _selected_operation == Terrain3DEditor.ADD:\n\t\t\t\tto_show.push_back(\"strength\")\n\t\t\t\tto_show.push_back(\"invert\")\n\t\t\tto_show.push_back(\"slope\")\n\t\t\tto_show.push_back(\"enable_angle\")\n\t\t\tto_show.push_back(\"angle\")\n\t\t\tto_show.push_back(\"angle_picker\")\n\t\t\tto_show.push_back(\"dynamic_angle\")\n\t\t\tto_show.push_back(\"enable_scale\")\n\t\t\tto_show.push_back(\"scale\")\n\t\t\tto_show.push_back(\"scale_picker\")\n\n\t\tTerrain3DEditor.COLOR:\n\t\t\tto_show.push_back(\"brush\")\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"strength\")\n\t\t\tto_show.push_back(\"color\")\n\t\t\tto_show.push_back(\"color_picker\")\n\t\t\tto_show.push_back(\"slope\")\n\t\t\tto_show.push_back(\"texture_filter\")\n\t\t\tto_show.push_back(\"margin\")\n\t\t\tto_show.push_back(\"invert\")\n\n\t\tTerrain3DEditor.ROUGHNESS:\n\t\t\tto_show.push_back(\"brush\")\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"strength\")\n\t\t\tto_show.push_back(\"roughness\")\n\t\t\tto_show.push_back(\"roughness_picker\")\n\t\t\tto_show.push_back(\"slope\")\n\t\t\tto_show.push_back(\"texture_filter\")\n\t\t\tto_show.push_back(\"margin\")\n\t\t\tto_show.push_back(\"invert\")\n\n\t\tTerrain3DEditor.AUTOSHADER, Terrain3DEditor.HOLES, Terrain3DEditor.NAVIGATION:\n\t\t\tto_show.push_back(\"brush\")\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"invert\")\n\n\t\tTerrain3DEditor.INSTANCER:\n\t\t\tto_show.push_back(\"size\")\n\t\t\tto_show.push_back(\"strength\")\n\t\t\tto_show.push_back(\"slope\")\n\t\t\tto_show.push_back(\"mesh_picker\")\n\t\t\tset_menu_visibility(tool_settings.height_list, true)\n\t\t\tto_show.push_back(\"height_offset\")\n\t\t\tto_show.push_back(\"random_height\")\n\t\t\tset_menu_visibility(tool_settings.scale_list, true)\n\t\t\tto_show.push_back(\"fixed_scale\")\n\t\t\tto_show.push_back(\"random_scale\")\n\t\t\tset_menu_visibility(tool_settings.rotation_list, true)\n\t\t\tto_show.push_back(\"fixed_spin\")\n\t\t\tto_show.push_back(\"random_spin\")\n\t\t\tto_show.push_back(\"fixed_tilt\")\n\t\t\tto_show.push_back(\"random_tilt\")\n\t\t\tto_show.push_back(\"align_to_normal\")\n\t\t\tset_menu_visibility(tool_settings.color_list, true)\n\t\t\tto_show.push_back(\"vertex_color\")\n\t\t\tto_show.push_back(\"random_darken\")\n\t\t\tto_show.push_back(\"random_hue\")\n\t\t\tset_menu_visibility(tool_settings.collision_list, true)\n\t\t\tto_show.push_back(\"on_collision\")\n\t\t\tto_show.push_back(\"raycast_height\")\n\t\t\tto_show.push_back(\"invert\")\n\n\t\t_:\n\t\t\tpass\n\n\t# Advanced menu settings\n\tto_show.push_back(\"auto_regions\")\n\tto_show.push_back(\"align_to_view\")\n\tto_show.push_back(\"show_brush_texture\")\n\tto_show.push_back(\"gamma\")\n\tto_show.push_back(\"brush_spin_speed\")\n\ttool_settings.show_settings(to_show)\n\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: _on_tool_changed: calling _on_setting_changed()\")\n\t_on_setting_changed()\n\n\nfunc _on_setting_changed(p_setting: Variant = null) -> void:\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: _on_setting_changed: \", p_setting if p_setting else \"update all\")\n\tif not plugin.asset_dock: # Skip function if not _ready()\n\t\treturn\n\tbrush_data = tool_settings.get_settings()\n\tbrush_data[\"asset_id\"] = plugin.asset_dock.current_list.get_selected_asset_id()\n\tif plugin.debug:\n\t\tprint(\"Terrain3DUI: _on_setting_changed: selected resource ID: \", brush_data[\"asset_id\"])\n\tif plugin.editor:\n\t\tplugin.editor.set_brush_data(brush_data)\n\tinverted_input = brush_data.get(\"invert\", false)\n\tif p_setting is CheckBox and p_setting.name == &\"Invert\":\n\t\tplugin._read_input() # Revalidate keyboard input for modifier_ctrl\n\tset_active_operation()\n\tupdate_decal()\n\n\n# Change tool/operation based on modifiers. Called from:\n# * editor_plugin.gd:_read_input() - when a modifier key is pressed\n# * _on_tool_changed() via:\n# * _on_setting_changed() eg. Touchscreen Invert\nfunc set_active_operation() -> void:\n\tvar inverted: bool = plugin.modifier_ctrl || inverted_input\n\n\t# Toggle toolbar buttons\n\ttoolbar.show_add_buttons(not inverted)\n\t\n\t# If Shift, Smoothness\n\tif plugin.modifier_shift and not inverted:\n\t\tmatch _selected_tool:\n\t\t\tTerrain3DEditor.SCULPT, Terrain3DEditor.HEIGHT, Terrain3DEditor.HOLES, \\\n\t\t\tTerrain3DEditor.INSTANCER:\n\t\t\t\tactive_tool = Terrain3DEditor.SCULPT\n\t\t\t\tactive_operation = Terrain3DEditor.AVERAGE\n\t\t\tTerrain3DEditor.TEXTURE:\n\t\t\t\tactive_tool = Terrain3DEditor.TEXTURE\n\t\t\t\tactive_operation = Terrain3DEditor.AVERAGE\n\t\t\tTerrain3DEditor.COLOR:\n\t\t\t\tactive_tool = Terrain3DEditor.COLOR\n\t\t\t\tactive_operation = Terrain3DEditor.AVERAGE\n\t\t\tTerrain3DEditor.ROUGHNESS:\n\t\t\t\tactive_tool = Terrain3DEditor.ROUGHNESS\n\t\t\t\tactive_operation = Terrain3DEditor.AVERAGE\n\t\n\t# Else if Ctrl/Invert checked, opposite\n\telif _selected_operation == Terrain3DEditor.ADD and inverted:\n\t\tactive_tool = _selected_tool\n\t\tactive_operation = Terrain3DEditor.SUBTRACT\n\telif _selected_operation == Terrain3DEditor.SUBTRACT and not inverted:\n\t\tactive_tool = _selected_tool\n\t\tactive_operation = Terrain3DEditor.ADD\n\n\t# Else use default and set\n\telse:\n\t\tactive_tool = _selected_tool\n\t\tactive_operation = _selected_operation\n\n\t# Initiate Multipoint operation\n\toperation_builder = null\n\tif active_operation == Terrain3DEditor.GRADIENT:\n\t\toperation_builder = GradientOperationBuilder.new()\n\t\toperation_builder.tool_settings = tool_settings\n\n\tif plugin.editor:\n\t\tplugin.editor.set_tool(active_tool)\n\t\tplugin.editor.set_operation(active_operation)\n\n\nfunc update_decal() -> void:\n\tif not plugin.terrain or not plugin.viewport or brush_data.size() <= 3:\n\t\treturn\n\t\n\t# If not a state that should show the decal, hide everything and return\n\tmat_rid = plugin.terrain.material.get_material_rid() # Used in hide_decal() and below\n\tif not visible or \\\n\t\tplugin._input_mode == -1 or \\\n\t\t# After moving camera, wait for mouse cursor to update before revealing\n\t\t# See https://github.com/godotengine/godot/issues/70098\n\t\tTime.get_ticks_msec() - plugin.rmb_release_time <= 100:\n\t\t\thide_decal()\n\t\t\treturn\n\t\n\t# Only show decal if in viewport or toolbars\n\tvar main: Control = EditorInterface.get_editor_main_screen()\n\tvar main_rect := Rect2(main.position, main.size)\n\tmain_rect.size.y += tool_settings.size.y\n\tif not ( main_rect.has_point(plugin.viewport.get_mouse_position()) && plugin.mouse_in_main ):\n\t\treturn\n\t\n\treset_decal_arrays()\n\teditor_decal_position[0] = Vector2(plugin.mouse_global_position.x, plugin.mouse_global_position.z)\n\teditor_decal_visible = [true, false, false] # Show cursor by default\n\teditor_decal_part = [true, true] # Show brush and reticle by default\n\teditor_decal_timer.start()\n\t\n\t## Region Operations\n\tvar r_map: PackedInt32Array = plugin.terrain.data.get_region_map()\n\tif plugin.editor.get_tool() == Terrain3DEditor.REGION:\n\t\tvar r_size: float = float(plugin.terrain.get_region_size()) * plugin.terrain.get_vertex_spacing()\n\t\tvar map_size: int = plugin.terrain.data.REGION_MAP_SIZE\n\t\tvar half_r_size: float = r_size * 0.5\n\t\tvar pos: Vector2 = (Vector2(plugin.mouse_global_position.x, plugin.mouse_global_position.z) +\n\t\t\tVector2(half_r_size, half_r_size)).snappedf(r_size) - Vector2(half_r_size, half_r_size)\n\t\teditor_brush_texture_rid = region_texture.get_rid()\n\t\teditor_decal_position[0] = pos\n\t\teditor_decal_size[0] = r_size\n\t\teditor_decal_rotation[0] = 0.0\n\t\teditor_decal_part[1] = false # Disable reticle\n\t\t\n\t\tvar loc: Vector2i = plugin.terrain.data.get_region_location(plugin.mouse_global_position)\n\t\tloc += Vector2i(map_size / 2, map_size / 2)\n\t\tif !(loc.x < 0 or loc.x > map_size - 1 or loc.y < 0 or loc.y > map_size - 1):\n\t\t\tvar index: int = clampi(loc.y * map_size + loc.x, 0, map_size * map_size - 1)\n\t\t\tif plugin.terrain.material.get_world_background() == Terrain3DMaterial.WorldBackground.NONE:\n\t\t\t\tif r_map[index] == 0 and active_operation == Terrain3DEditor.ADD:\n\t\t\t\t\tr_map[index] = -index - 1\n\t\t\t\telse:\n\t\t\t\t\tr_map[index] = r_map[index]\n\t\t\t\n\t\t\tmatch active_operation:\n\t\t\t\tTerrain3DEditor.ADD:\n\t\t\t\t\tif r_map[index] <= 0:\n\t\t\t\t\t\teditor_decal_color[0] = Color.WHITE\n\t\t\t\t\t\teditor_decal_color[0].a = 0.25\n\t\t\t\t\telse:\n\t\t\t\t\t\thide_decal()\n\t\t\t\t\n\t\t\t\tTerrain3DEditor.SUBTRACT:\n\t\t\t\t\tif r_map[index] > 0:\n\t\t\t\t\t\teditor_decal_color[0] = Color.WHITE * .15\n\t\t\t\t\t\teditor_decal_color[0].a = 0.75\n\t\t\t\t\telse:\n\t\t\t\t\t\thide_decal()\n\t\telse:\n\t\t\thide_decal()\n\n\t## Picking\n\telif picking != Terrain3DEditor.TOOL_MAX:\n\t\teditor_decal_part[0] = false # Hide brush\n\t\teditor_decal_size[0] = plugin.terrain.get_vertex_spacing()\n\t\teditor_decal_color[0] = COLOR_PICK\n\t\teditor_decal_color[0].a = 1.0\n\n\t## Brushing Operations\n\telse:\n\t\teditor_brush_texture_rid = brush_data[\"brush\"][1].get_rid()\n\t\teditor_decal_size[0] = maxf(brush_data[\"size\"], .5)\n\t\tif brush_data[\"align_to_view\"]:\n\t\t\tvar cam: Camera3D = plugin.terrain.get_camera();\n\t\t\tif (cam):\n\t\t\t\teditor_decal_rotation[0] = cam.rotation.y\n\t\t\telse:\n\t\t\t\teditor_decal_rotation[0] = 0.\n\t\tmatch plugin.editor.get_tool():\n\t\t\tTerrain3DEditor.SCULPT:\n\t\t\t\tmatch active_operation:\n\t\t\t\t\tTerrain3DEditor.ADD:\n\t\t\t\t\t\tif plugin.modifier_alt:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_LIFT\n\t\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5)\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_RAISE\n\t\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .25, .5)\n\t\t\t\t\tTerrain3DEditor.SUBTRACT:\n\t\t\t\t\t\tif plugin.modifier_alt:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_FLATTEN\n\t\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .25, .5) + .1\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_LOWER\n\t\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .25\n\t\t\t\t\tTerrain3DEditor.AVERAGE:\n\t\t\t\t\t\teditor_decal_color[0] = COLOR_SMOOTH\n\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .25\n\t\t\t\t\tTerrain3DEditor.GRADIENT:\n\t\t\t\t\t\teditor_decal_color[0] = COLOR_SLOPE\n\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .4)\n\t\t\tTerrain3DEditor.HEIGHT:\n\t\t\t\teditor_decal_color[0] = COLOR_HEIGHT\n\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .25\n\t\t\tTerrain3DEditor.TEXTURE:\n\t\t\t\tif plugin._input_mode == 1:\n\t\t\t\t\teditor_decal_part[0] = false # Hide brush\n\t\t\t\tif plugin.modifier_shift:\n\t\t\t\t\teditor_decal_color[0] = COLOR_AVERAGE\n\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .25\n\t\t\t\telse:\n\t\t\t\t\tmatch active_operation:\n\t\t\t\t\t\tTerrain3DEditor.REPLACE:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_PAINT\n\t\t\t\t\t\t\teditor_decal_color[0].a = .6\n\t\t\t\t\t\tTerrain3DEditor.SUBTRACT:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_UNSPRAY\n\t\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .1\n\t\t\t\t\t\tTerrain3DEditor.ADD:\n\t\t\t\t\t\t\teditor_decal_color[0] = COLOR_SPRAY\n\t\t\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .15, .4)\n\t\t\tTerrain3DEditor.COLOR:\n\t\t\t\tif plugin.modifier_shift:\n\t\t\t\t\teditor_decal_color[0] = COLOR_AVERAGE\n\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .25\n\t\t\t\telif plugin.modifier_ctrl:\n\t\t\t\t\teditor_decal_color[0] = Color.WHITE\n\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5)\n\t\t\t\telse:\n\t\t\t\t\teditor_decal_color[0] = brush_data[\"color\"].srgb_to_linear()\n\t\t\t\t\teditor_decal_color[0].a *= clamp(brush_data[\"strength\"], .3, .5)\n\t\t\tTerrain3DEditor.ROUGHNESS:\n\t\t\t\tif plugin._input_mode == 1:\n\t\t\t\t\teditor_decal_part[0] = false # Hide brush\n\t\t\t\tif plugin.modifier_shift:\n\t\t\t\t\teditor_decal_color[0] = COLOR_AVERAGE\n\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .25\n\t\t\t\telif plugin.modifier_ctrl:\n\t\t\t\t\teditor_decal_color[0] = COLOR_DRY\n\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .1\n\t\t\t\telse:\n\t\t\t\t\teditor_decal_color[0] = COLOR_WET\n\t\t\t\t\teditor_decal_color[0].a = clamp(brush_data[\"strength\"], .2, .5) + .1\n\t\t\tTerrain3DEditor.AUTOSHADER:\n\t\t\t\teditor_decal_color[0] = COLOR_AUTOSHADER\n\t\t\t\teditor_decal_color[0].a = .6\n\t\t\tTerrain3DEditor.HOLES:\n\t\t\t\teditor_decal_color[0] = COLOR_HOLES\n\t\t\t\teditor_decal_color[0].a = .75\n\t\t\tTerrain3DEditor.NAVIGATION:\n\t\t\t\teditor_decal_color[0] = COLOR_NAVIGATION\n\t\t\t\teditor_decal_color[0].a = .80\n\t\t\tTerrain3DEditor.INSTANCER:\n\t\t\t\teditor_decal_part[0] = false # Hide brush\n\t\t\t\tif plugin.modifier_ctrl:\n\t\t\t\t\teditor_decal_color[0] = COLOR_UNINSTANCE\n\t\t\t\t\teditor_decal_color[0].a = .75\n\t\t\t\telse:\n\t\t\t\t\teditor_decal_color[0] = COLOR_INSTANCE\n\t\t\t\t\teditor_decal_color[0].a = .75\n\t\n\tif plugin.editor.get_tool() != Terrain3DEditor.REGION and not brush_data[\"show_brush_texture\"]:\n\t\teditor_decal_part[0] = false # Hide brush\n\t\n\tif active_operation == Terrain3DEditor.GRADIENT:\n\t\tvar point1: Vector3 = brush_data[\"gradient_points\"][0]\n\t\tif point1 != Vector3.ZERO:\n\t\t\teditor_decal_color[1] = COLOR_SLOPE\n\t\t\teditor_decal_size[1] = 0.25\n\t\t\teditor_decal_visible[1] = true\n\t\t\teditor_decal_position[1] = Vector2(point1.x, point1.z)\n\t\tvar point2: Vector3 = brush_data[\"gradient_points\"][1]\n\t\tif point2 != Vector3.ZERO:\n\t\t\teditor_decal_color[2] = COLOR_SLOPE\n\t\t\teditor_decal_size[2] = 0.25\n\t\t\teditor_decal_visible[2] = true\n\t\t\teditor_decal_position[2] = Vector2(point2.x, point2.z)\n\t\n\tif RenderingServer.get_current_rendering_method().contains(\"gl_compatibility\"):\n\t\tfor i in editor_decal_color.size():\n\t\t\teditor_decal_color[i].a = maxf(0.1, editor_decal_color[i].a - .25)\n\t\n\teditor_decal_fade = editor_decal_color[0].a\n\t# Update Shader params\n\tif is_shader_valid():\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_brush_texture\", editor_brush_texture_rid)\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_position\", editor_decal_position)\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_rotation\", editor_decal_rotation)\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_size\", editor_decal_size)\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_color\", editor_decal_color)\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_visible\", editor_decal_visible)\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_part\", editor_decal_part)\n\t\tRenderingServer.material_set_param(mat_rid, \"_region_map\", r_map)\n\n\nfunc is_shader_valid() -> bool:\n\t# As long as the compiled shader contains at least 1 uniform, we can use it to check\n\t# if the shader compilation has failed, as this will then return an empty dictionary.\n\tif not plugin.terrain:\n\t\treturn false\n\tvar params = RenderingServer.get_shader_parameter_list(plugin.terrain.material.get_shader_rid())\n\tif params.is_empty():\n\t\treturn false\n\telse:\n\t\treturn true\n\n\nfunc hide_decal() -> void:\n\teditor_decal_visible = [false, false, false]\n\tif is_shader_valid():\n\t\tvar r_map: PackedInt32Array = plugin.terrain.data.get_region_map()\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_visible\", editor_decal_visible)\n\t\tRenderingServer.material_set_param(mat_rid, \"_region_map\", r_map)\n\n\n# These array sizes are reset to 0 when closing scenes for some unknown reason, so check and reset\nfunc reset_decal_arrays() -> void:\n\tif editor_decal_color.size() < 3:\n\t\teditor_brush_texture_rid = RID()\n\t\teditor_decal_position = [Vector2(), Vector2(), Vector2()]\n\t\teditor_decal_rotation = [0., 0., 0.]\n\t\teditor_decal_size = [0., 0., 0.]\n\t\teditor_decal_color = [Color(), Color(), Color()]\n\t\teditor_decal_visible = [false, false, false]\n\t\teditor_decal_part = [true, true]\n\n\nfunc set_decal_rotation(p_rot: float) -> void:\n\teditor_decal_rotation[0] = p_rot\n\tif is_shader_valid():\n\t\tRenderingServer.material_set_param(mat_rid, \"_editor_decal_rotation\", editor_decal_rotation)\n\n\nfunc _on_picking(p_type: Terrain3DEditor.Tool, p_callback: Callable) -> void:\n\tpicking = p_type\n\tpicking_callback = p_callback\n\n\nfunc clear_picking() -> void:\n\tpicking = Terrain3DEditor.TOOL_MAX\n\n\nfunc is_picking() -> bool:\n\tif picking != Terrain3DEditor.TOOL_MAX:\n\t\treturn true\n\t\n\tif operation_builder and operation_builder.is_picking():\n\t\treturn true\n\t\n\treturn false\n\n\nfunc pick(p_global_position: Vector3) -> void:\n\tif picking != Terrain3DEditor.TOOL_MAX:\n\t\tvar color: Color\n\t\tmatch picking:\n\t\t\tTerrain3DEditor.HEIGHT, Terrain3DEditor.SCULPT:\n\t\t\t\tcolor = Color(plugin.terrain.data.get_height(p_global_position), 0., 0., 1.)\n\t\t\tTerrain3DEditor.ROUGHNESS:\n\t\t\t\tcolor = plugin.terrain.data.get_pixel(Terrain3DRegion.TYPE_COLOR, p_global_position)\n\t\t\tTerrain3DEditor.COLOR:\n\t\t\t\tcolor = plugin.terrain.data.get_color(p_global_position)\n\t\t\tTerrain3DEditor.ANGLE:\n\t\t\t\tcolor = Color(plugin.terrain.data.get_control_angle(p_global_position), 0., 0., 1.)\n\t\t\tTerrain3DEditor.SCALE:\n\t\t\t\tcolor = Color(plugin.terrain.data.get_control_scale(p_global_position), 0., 0., 1.)\n\t\t\tTerrain3DEditor.INSTANCER:\n\t\t\t\tvar mesh_asset_id: int = plugin.terrain.instancer.get_closest_mesh_id(p_global_position)\n\t\t\t\tcolor = Color(mesh_asset_id, 0., 0., 1.)\n\t\t\tTerrain3DEditor.TEXTURE:\n\t\t\t\tvar texture_blend_data: Vector3 = plugin.terrain.data.get_texture_id(p_global_position)\n\t\t\t\tif not texture_blend_data.is_finite():\n\t\t\t\t\treturn\n\t\t\t\tif texture_blend_data.z < 0.65:\n\t\t\t\t\tcolor = Color(texture_blend_data.x, 0., 0., 1.)\n\t\t\t\telse:\n\t\t\t\t\tcolor = Color(texture_blend_data.y, 0., 0., 1.)\n\t\t\t_:\n\t\t\t\tpush_error(\"Unsupported picking type: \", picking)\n\t\t\t\treturn\n\t\tif picking_callback.is_valid():\n\t\t\tpicking_callback.call(picking, color, p_global_position)\n\t\t\tpicking_callback = Callable()\n\t\tpicking = Terrain3DEditor.TOOL_MAX\n\t\n\telif operation_builder and operation_builder.is_picking():\n\t\toperation_builder.pick(p_global_position, plugin.terrain)\n\n\nfunc set_button_editor_icon(p_button: Button, p_icon_name: String) -> void:\n\tp_button.icon = EditorInterface.get_base_control().get_theme_icon(p_icon_name, \"EditorIcons\")\n"
  },
  {
    "path": "project/addons/terrain_3d/src/ui.gd.uid",
    "content": "uid://bpad72s36mwkx\n"
  },
  {
    "path": "project/addons/terrain_3d/terrain.gdextension",
    "content": "[configuration]\n\nentry_symbol = \"terrain_3d_init\"\ncompatibility_minimum = 4.4\n\n[icons]\n\nTerrain3D = \"res://addons/terrain_3d/icons/terrain3d.svg\"\n\n[libraries]\n\nwindows.debug.x86_64 = \"res://addons/terrain_3d/bin/libterrain.windows.debug.x86_64.dll\"\nwindows.release.x86_64 = \"res://addons/terrain_3d/bin/libterrain.windows.release.x86_64.dll\"\n\nlinux.debug.x86_64 = \"res://addons/terrain_3d/bin/libterrain.linux.debug.x86_64.so\"\nlinux.release.x86_64 = \"res://addons/terrain_3d/bin/libterrain.linux.release.x86_64.so\"\nlinux.debug.arm64 = \"res://addons/terrain_3d/bin/libterrain.linux.debug.arm64.so\"\nlinux.release.arm64 = \"res://addons/terrain_3d/bin/libterrain.linux.release.arm64.so\"\nlinux.debug.rv64 = \"res://addons/terrain_3d/bin/libterrain.linux.debug.rv64.so\"\nlinux.release.rv64 = \"res://addons/terrain_3d/bin/libterrain.linux.release.rv64.so\"\n\nmacos.debug = \"res://addons/terrain_3d/bin/libterrain.macos.debug.framework\"\nmacos.release = \"res://addons/terrain_3d/bin/libterrain.macos.release.framework\"\n\nandroid.debug.arm64 = \"res://addons/terrain_3d/bin/libterrain.android.debug.arm64.so\"\nandroid.release.arm64 = \"res://addons/terrain_3d/bin/libterrain.android.release.arm64.so\"\n\nios.debug = \"res://addons/terrain_3d/bin/libterrain.ios.debug.universal.dylib\"\nios.release = \"res://addons/terrain_3d/bin/libterrain.ios.release.universal.dylib\"\n\nweb.debug = \"res://addons/terrain_3d/bin/libterrain.web.debug.wasm32.wasm\"\nweb.release = \"res://addons/terrain_3d/bin/libterrain.web.release.wasm32.wasm\"\n"
  },
  {
    "path": "project/addons/terrain_3d/terrain.gdextension.uid",
    "content": "uid://bdn2ygldx56a8\n"
  },
  {
    "path": "project/addons/terrain_3d/tools/importer.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Importer for Terrain3D\n@tool\nextends Terrain3D\n\n\n@export_tool_button(\"Clear All\") var clear_all = reset_settings\n@export_tool_button(\"Clear Terrain\") var clear_terrain = reset_terrain\n@export_tool_button(\"Update Height Range\") var update_height_range = update_heights\n\n\nfunc reset_settings() -> void:\n\theight_file_name = \"\"\n\tcontrol_file_name = \"\"\n\tcolor_file_name = \"\"\n\tdestination_directory = \"\"\n\timport_position = Vector2i.ZERO\n\theight_offset = 0.0\n\timport_scale = 1.0\n\tr16_range = Vector2(0, 1)\n\tr16_size = Vector2i(1024, 1024)\n\tmaterial = null\n\tassets = null\n\treset_terrain()\n\n\nfunc reset_terrain() -> void:\n\tdata_directory = \"\"\n\tfor region:Terrain3DRegion in data.get_regions_active():\n\t\tdata.remove_region(region, false)\n\tdata.update_maps(Terrain3DRegion.TYPE_MAX, true, false)\n\n\n## Recalculates min and max heights for all regions.\nfunc update_heights() -> void:\n\tif data:\n\t\tdata.calc_height_range(true)\n\n\n@export_group(\"Import File\")\n## EXR or R16 are recommended for heightmaps. 16-bit PNGs are down sampled to 8-bit and not recommended.\n@export_global_file var height_file_name: String = \"\"\n## Only use EXR files in our proprietary format.\n@export_global_file var control_file_name: String = \"\"\n## Any RGB or RGBA format is fine; PNG or Webp are recommended.\n@export_global_file var color_file_name: String = \"\"\n## The top left (-X, -Y) corner position of where to place the imported data. Positions are descaled and ignore the vertex_spacing setting.\n@export var import_position: Vector2i = Vector2i(0, 0) : set = set_import_position\n## This scales the height of imported values.\n@export var import_scale: float = 1.0\n## This vertically offsets the height of imported values.\n@export var height_offset: float = 0.0\n## The lowest and highest height values of the imported image. Only use for r16 files.\n@export var r16_range: Vector2 = Vector2(0, 1)\n## The dimensions of the imported image. Only use for r16 files.\n@export var r16_size: Vector2i = Vector2i(1024, 1024) : set = set_r16_size\n@export_tool_button(\"Run Import\") var run_import = start_import\n\n@export_dir var destination_directory: String = \"\"\n@export_tool_button(\"Save to Disk\") var save_to_disk = save_data\n\n\nfunc set_import_position(p_value: Vector2i) -> void:\n\timport_position.x = clamp(p_value.x, -8192, 8192)\n\timport_position.y = clamp(p_value.y, -8192, 8192)\n\n\nfunc set_r16_size(p_value: Vector2i) -> void:\n\tr16_size.x = clamp(p_value.x, 0, 16384)\n\tr16_size.y = clamp(p_value.y, 0, 16384)\n\n\nfunc start_import() -> void:\n\tprint(\"Terrain3DImporter: Importing files:\\n\\t%s\\n\\t%s\\n\\t%s\" % [ height_file_name, control_file_name, color_file_name])\n\n\tvar imported_images: Array[Image]\n\timported_images.resize(Terrain3DRegion.TYPE_MAX)\n\tvar min_max := Vector2(0, 1)\n\tvar img: Image\n\tif height_file_name:\n\t\timg = Terrain3DUtil.load_image(height_file_name, ResourceLoader.CACHE_MODE_IGNORE, r16_range, r16_size)\n\t\tmin_max = Terrain3DUtil.get_min_max(img)\n\t\timported_images[Terrain3DRegion.TYPE_HEIGHT] = img\n\tif control_file_name:\n\t\timg = Terrain3DUtil.load_image(control_file_name, ResourceLoader.CACHE_MODE_IGNORE)\n\t\timported_images[Terrain3DRegion.TYPE_CONTROL] = img\n\tif color_file_name:\n\t\timg = Terrain3DUtil.load_image(color_file_name, ResourceLoader.CACHE_MODE_IGNORE)\n\t\timported_images[Terrain3DRegion.TYPE_COLOR] = img\n\t\tif assets.get_texture_count() == 0:\n\t\t\tmaterial.show_checkered = false\n\t\t\tmaterial.show_colormap = true\n\tvar pos := Vector3(import_position.x * vertex_spacing, 0, import_position.y * vertex_spacing)\n\tdata.import_images(imported_images, pos, height_offset, import_scale)\n\tprint(\"Terrain3DImporter: Import finished\")\n\n\nfunc save_data() -> void:\n\tif destination_directory.is_empty():\n\t\tpush_error(\"Set destination directory first\")\n\t\treturn\n\tdata.save_directory(destination_directory)\n\n\n@export_group(\"Export File\")\nenum { TYPE_HEIGHT, TYPE_CONTROL, TYPE_COLOR }\n@export_enum(\"Height:0\", \"Control:1\", \"Color:2\") var map_type: int = TYPE_HEIGHT\n@export var file_name_out: String = \"\"\n@export_tool_button(\"Run Export\") var run_export = start_export\n\nfunc start_export() -> void:\n\tvar err: int = data.export_image(file_name_out, map_type)\n\tprint(\"Terrain3DImporter: Export error status: \", err, \" \", error_string(err))\n\t\n"
  },
  {
    "path": "project/addons/terrain_3d/tools/importer.gd.uid",
    "content": "uid://cib2rig7vup10\n"
  },
  {
    "path": "project/addons/terrain_3d/tools/importer.tscn",
    "content": "[gd_scene load_steps=9 format=3 uid=\"uid://blaieaqp413k7\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/terrain_3d/tools/importer.gd\" id=\"1_60b8f\"]\n\n[sub_resource type=\"Gradient\" id=\"Gradient_88f3t\"]\noffsets = PackedFloat32Array(0.2, 1)\ncolors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)\n\n[sub_resource type=\"FastNoiseLite\" id=\"FastNoiseLite_muvel\"]\nnoise_type = 2\nfrequency = 0.03\ncellular_jitter = 3.0\ncellular_return_type = 0\ndomain_warp_enabled = true\ndomain_warp_type = 1\ndomain_warp_amplitude = 50.0\ndomain_warp_fractal_type = 2\ndomain_warp_fractal_lacunarity = 1.5\ndomain_warp_fractal_gain = 1.0\n\n[sub_resource type=\"NoiseTexture2D\" id=\"NoiseTexture2D_ve0yk\"]\nseamless = true\ncolor_ramp = SubResource(\"Gradient_88f3t\")\nnoise = SubResource(\"FastNoiseLite_muvel\")\n\n[sub_resource type=\"Terrain3DMaterial\" id=\"Terrain3DMaterial_p55u0\"]\n_shader_parameters = {\n\"blend_sharpness\": 0.87,\n\"height_blending\": true,\n\"macro_variation1\": Color(1, 1, 1, 1),\n\"macro_variation2\": Color(1, 1, 1, 1),\n\"noise1_angle\": 0.0,\n\"noise1_offset\": Vector2(0.5, 0.5),\n\"noise1_scale\": 0.04,\n\"noise2_scale\": 0.076,\n\"noise3_scale\": 0.225,\n\"noise_texture\": SubResource(\"NoiseTexture2D_ve0yk\"),\n\"vertex_normals_distance\": 128.0\n}\nshow_checkered = true\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_8rvqy\"]\ncull_mode = 2\nvertex_color_use_as_albedo = true\nbacklight_enabled = true\nbacklight = Color(0.5, 0.5, 0.5, 1)\n\n[sub_resource type=\"Terrain3DMeshAsset\" id=\"Terrain3DMeshAsset_7je72\"]\nheight_offset = 0.5\ndensity = 10.0\nmaterial_override = SubResource(\"StandardMaterial3D_8rvqy\")\ngenerated_type = 1\n\n[sub_resource type=\"Terrain3DAssets\" id=\"Terrain3DAssets_op32e\"]\nmesh_list = Array[Terrain3DMeshAsset]([SubResource(\"Terrain3DMeshAsset_7je72\")])\n\n[node name=\"Importer\" type=\"Terrain3D\"]\nmaterial = SubResource(\"Terrain3DMaterial_p55u0\")\nassets = SubResource(\"Terrain3DAssets_op32e\")\nmesh_lods = 8\ntop_level = true\nscript = ExtResource(\"1_60b8f\")\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "project/addons/terrain_3d/tools/region_mover.gd",
    "content": "# This script can be used to move your regions by an offset. \n# Eventually this tool will find its way into a built in UI\n# \n# Attach it to your Terrain3D node\n# Save and reload your scene\n# Select your Terrain3D node\n# Enter a valid `offset` where all regions will be within -16, +15\n# Run it\n# It should unload the regions, rename files, and reload them\n# Clear the script and resave your scene\n\n\n@tool\nextends Terrain3D\n\n\n@export var offset: Vector2i\n@export_tool_button(\"Run\") var run = start_rename\n\n\nfunc start_rename() -> void:\n\tif offset == Vector2i.ZERO:\n\t\treturn\n\t\t\n\tvar dir_name: String = data_directory\n\tdata_directory = \"\"\n\tvar dir := DirAccess.open(dir_name)\n\tif not dir:\n\t\tprint(\"An error occurred when trying to access the path: \", data_directory)\n\t\treturn\n\n\tvar affected_files: PackedStringArray\n\tvar files: PackedStringArray = dir.get_files()\n\tfor file_name in files:\n\t\tif file_name.match(\"terrain3d*.res\") and not dir.current_is_dir():\n\t\t\tvar region_loc: Vector2i = Terrain3DUtil.filename_to_location(file_name)\n\t\t\tvar new_loc: Vector2i = region_loc + offset\n\t\t\tif new_loc.x < -16 or new_loc.x > 15 or new_loc.y < -16 or new_loc.y > 15:\n\t\t\t\tpush_error(\"New location %.0v out of bounds for region %.0v. Aborting\" % [ new_loc, region_loc ])\n\t\t\t\treturn\n\t\t\tvar new_name: String = \"tmp_\" + Terrain3DUtil.location_to_filename(new_loc)\n\t\t\tdir.rename(file_name, new_name)\n\t\t\taffected_files.push_back(new_name)\n\t\t\tprint(\"File: %s renamed to: %s\" % [ file_name, new_name ])\n\t\t\t\t\n\tfor file_name in affected_files:\n\t\tvar new_name: String = file_name.trim_prefix(\"tmp_\")\n\t\tdir.rename(file_name, new_name)\n\t\tprint(\"File: %s renamed to: %s\" % [ file_name, new_name ])\n\t\t\n\tdata_directory = dir_name\n\tEditorInterface.get_resource_filesystem().scan()\t\n"
  },
  {
    "path": "project/addons/terrain_3d/tools/region_mover.gd.uid",
    "content": "uid://bngnvtbm6ifkk\n"
  },
  {
    "path": "project/addons/terrain_3d/utils/terrain_3d_objects.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Objects parent for Terrain3D\n# Children nodes get transform updates on sculpting\n@tool\nextends Node3D\nclass_name Terrain3DObjects\n\nconst TransformChangedNotifier: Script = preload(\"res://addons/terrain_3d/utils/transform_changed_notifier.gd\")\n\nconst CHILD_HELPER_NAME: StringName = &\"TransformChangedSignaller\"\nconst CHILD_HELPER_PATH: NodePath = ^\"TransformChangedSignaller\"\n\nvar _undo_redo = null\nvar _terrain_id: int\nvar _offsets: Dictionary # Object ID -> Vector3(X, Y offset relative to terrain height, Z)\nvar _ignore_transform_change: bool = false\n\n\nfunc _enter_tree() -> void:\n\tif not Engine.is_editor_hint():\n\t\treturn\n\t\n\tfor child in get_children():\n\t\t_on_child_entered_tree(child)\n\t\n\tchild_entered_tree.connect(_on_child_entered_tree)\n\tchild_exiting_tree.connect(_on_child_exiting_tree)\n\n\nfunc _exit_tree() -> void:\n\tif not Engine.is_editor_hint():\n\t\treturn\n\t\n\tchild_entered_tree.disconnect(_on_child_entered_tree)\n\tchild_exiting_tree.disconnect(_on_child_exiting_tree)\n\t\n\tfor child in get_children():\n\t\t_on_child_exiting_tree(child)\n\n\nfunc editor_setup(p_plugin) -> void:\n\t_undo_redo = p_plugin.get_undo_redo()\n\n\nfunc get_terrain() -> Terrain3D:\n\tvar terrain := instance_from_id(_terrain_id) as Terrain3D\n\tif not terrain or terrain.is_queued_for_deletion() or not terrain.is_inside_tree():\n\t\tvar terrains: Array[Node] = Engine.get_singleton(&\"EditorInterface\").get_edited_scene_root().find_children(\"\", \"Terrain3D\")\n\t\tif terrains.size() > 0:\n\t\t\tterrain = terrains[0]\n\t\t_terrain_id = terrain.get_instance_id() if terrain else 0\n\t\n\tif terrain and terrain.data and not terrain.data.maps_edited.is_connected(_on_maps_edited):\n\t\tterrain.data.maps_edited.connect(_on_maps_edited)\n\t\n\treturn terrain\n\n\nfunc _get_terrain_height(p_global_position: Vector3) -> float:\n\tvar terrain: Terrain3D = get_terrain()\n\tif not terrain or not terrain.data:\n\t\treturn 0.0\n\tvar height: float = terrain.data.get_height(p_global_position)\n\tif is_nan(height):\n\t\treturn 0.0\n\treturn height\n\n\nfunc _on_child_entered_tree(p_node: Node) -> void:\n\tif not (p_node is Node3D):\n\t\treturn\n\t\n\tassert(p_node.get_parent() == self)\n\t\n\tvar helper: TransformChangedNotifier = p_node.get_node_or_null(CHILD_HELPER_PATH)\n\tif not helper:\n\t\thelper = TransformChangedNotifier.new()\n\t\thelper.name = CHILD_HELPER_NAME\n\t\tp_node.add_child(helper, true, INTERNAL_MODE_BACK)\n\tassert(p_node.has_node(CHILD_HELPER_PATH))\n\t\n\t# When reparenting a Node3D, Godot changes its transform _after_ reparenting it. So here,\n\t# we must use call_deferred, to avoid receiving transform_changed as a result of reparenting.\n\t_setup_child_signal.call_deferred(p_node, helper)\n\n\nfunc _setup_child_signal(p_node: Node, helper: TransformChangedNotifier) -> void:\n\tif not p_node.is_inside_tree():\n\t\treturn\n\tif helper.transform_changed.is_connected(_on_child_transform_changed):\n\t\treturn\n\t\n\thelper.transform_changed.connect(_on_child_transform_changed.bind(p_node))\n\t_update_child_offset(p_node)\n\n\nfunc _on_child_exiting_tree(p_node: Node) -> void:\n\tif not (p_node is Node3D) or not p_node.has_node(CHILD_HELPER_PATH):\n\t\treturn\n\t\n\tvar helper: TransformChangedNotifier = p_node.get_node_or_null(CHILD_HELPER_PATH)\n\tif helper:\n\t\tif helper.transform_changed.is_connected(_on_child_transform_changed):\n\t\t\thelper.transform_changed.disconnect(_on_child_transform_changed)\n\t\tp_node.remove_child(helper)\n\t\thelper.queue_free()\n\t\n\t_offsets.erase(p_node.get_instance_id())\n\n\nfunc _is_node_selected(p_node: Node) -> bool:\n\tvar editor_sel = Engine.get_singleton(&\"EditorInterface\").get_selection()\n\treturn editor_sel.get_transformable_selected_nodes().has(p_node)\n\n\nfunc _on_child_transform_changed(p_node: Node3D) -> void:\n\tif _ignore_transform_change:\n\t\treturn\n\t\n\tvar lmb_down := Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)\n\tif lmb_down and (_is_node_selected(p_node) or _is_node_selected(self)):\n\t\t# The user may be moving the node using gizmos.\n\t\t# We should wait until they're done before updating otherwise gizmos + this node conflict.\n\t\treturn\n\t\n\tif not _offsets.has(p_node.get_instance_id()):\n\t\treturn\n\t\n\tvar old_offset: Vector3 = _offsets[p_node.get_instance_id()]\n\tvar old_h: float = _get_terrain_height(old_offset)\n\tvar old_position: Vector3 = old_offset + Vector3(0, old_h, 0)\n\tvar new_position: Vector3 = p_node.global_position\n\tif old_position.is_equal_approx(new_position):\n\t\treturn\n\tvar new_h: float = _get_terrain_height(new_position)\n\tvar new_offset: Vector3 = new_position - Vector3(0, new_h, 0)\n\t\n\tvar translate_without_reposition: bool = Input.is_key_pressed(KEY_SHIFT)\n\tvar y_changed: bool = not is_equal_approx(old_position.y, p_node.global_position.y)\n\tif not y_changed and not translate_without_reposition:\n\t\tnew_offset.y = old_offset.y\n\t\tnew_position = new_offset + Vector3(0, new_h, 0)\n\t\n\t# Make sure that when the user undo's the translation, the offset change gets undone too!\n\t_undo_redo.create_action(\"Translate\", UndoRedo.MERGE_ALL)\n\t_undo_redo.add_do_method(self, &\"_set_offset_and_position\", p_node.get_instance_id(), new_offset, new_position)\n\t_undo_redo.add_undo_method(self, &\"_set_offset_and_position\", p_node.get_instance_id(), old_offset, old_position)\n\t_undo_redo.commit_action()\n\n\nfunc _set_offset_and_position(p_id: int, p_offset: Vector3, p_position: Vector3) -> void:\n\tvar node := instance_from_id(p_id) as Node\n\tif not is_instance_valid(node):\n\t\treturn\n\t\n\t_ignore_transform_change = true\n\tnode.global_position = p_position\n\t_offsets[p_id] = p_offset\n\t_ignore_transform_change = false\n\n\n# Overwrite current offset stored for node with its current Y position relative to the terrain\nfunc _update_child_offset(p_node: Node3D) -> void:\n\tvar position: Vector3 = global_transform * p_node.position\n\tvar h: float = _get_terrain_height(position)\n\tvar offset: Vector3 = position - Vector3(0, h, 0)\n\t_offsets[p_node.get_instance_id()] = offset\n\n\n# Overwrite node's current position with terrain height + stored offset for this node\nfunc _update_child_position(p_node: Node3D) -> void:\n\tif not _offsets.has(p_node.get_instance_id()):\n\t\treturn\n\t\n\tvar position: Vector3 = global_transform * p_node.position\n\tvar h: float = _get_terrain_height(position)\n\tvar offset: Vector3 = _offsets[p_node.get_instance_id()]\n\tvar new_position: Vector3 = global_transform.inverse() * (offset + Vector3(0, h, 0))\n\tif not p_node.position.is_equal_approx(new_position):\n\t\tp_node.position = new_position\n\n\nfunc _on_maps_edited(p_edited_aabb: AABB) -> void:\n\tvar edited_area: AABB = p_edited_aabb.grow(1)\n\tedited_area.position.y = -INF\n\tedited_area.end.y = INF\n\t\n\tfor child in get_children():\n\t\tvar node := child as Node3D\n\t\tif node && edited_area.has_point(node.global_position):\n\t\t\t_update_child_position(node)\n"
  },
  {
    "path": "project/addons/terrain_3d/utils/terrain_3d_objects.gd.uid",
    "content": "uid://dbndw8p05yam7\n"
  },
  {
    "path": "project/addons/terrain_3d/utils/transform_changed_notifier.gd",
    "content": "# Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n# Transform Changed Notifier for Terrain3D\n@tool\nextends Node3D\n\nsignal transform_changed\n\n\nfunc _ready() -> void:\n\tassert(Engine.is_editor_hint())\n\tset_notify_transform(true)\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_TRANSFORM_CHANGED:\n\t\ttransform_changed.emit()\n"
  },
  {
    "path": "project/addons/terrain_3d/utils/transform_changed_notifier.gd.uid",
    "content": "uid://claxtgppe8keq\n"
  },
  {
    "path": "project/demo/CodeGeneratedDemo.tscn",
    "content": "[gd_scene load_steps=8 format=3 uid=\"uid://cofnhdcclon1w\"]\n\n[ext_resource type=\"Script\" uid=\"uid://dakis6gu8b7nm\" path=\"res://demo/src/CodeGenerated.gd\" id=\"1_h7vyv\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://domhm87hbhbg1\" path=\"res://demo/components/Player.tscn\" id=\"2_3v2uf\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bb2lp50sjndus\" path=\"res://demo/components/Environment.tscn\" id=\"3_71ikj\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://di5fovhcyd7re\" path=\"res://demo/components/Enemy.tscn\" id=\"4_p8qry\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://d2jihfohphuue\" path=\"res://demo/components/UI.tscn\" id=\"4_x5ge4\"]\n[ext_resource type=\"Script\" uid=\"uid://brh8x1wnycrl5\" path=\"res://demo/src/RuntimeNavigationBaker.gd\" id=\"5_445ur\"]\n\n[sub_resource type=\"NavigationMesh\" id=\"NavigationMesh_vs6am\"]\ngeometry_parsed_geometry_type = 1\nagent_height = 2.0\nagent_max_slope = 30.0\n\n[node name=\"CodeGenerated\" type=\"Node\"]\nscript = ExtResource(\"1_h7vyv\")\n\n[node name=\"Environment\" parent=\".\" instance=ExtResource(\"3_71ikj\")]\n\n[node name=\"Player\" parent=\".\" instance=ExtResource(\"2_3v2uf\")]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 50, 0)\n\n[node name=\"Enemy\" parent=\".\" node_paths=PackedStringArray(\"target\") instance=ExtResource(\"4_p8qry\")]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 50, -10)\ntarget = NodePath(\"../Player\")\n\n[node name=\"UI\" parent=\".\" instance=ExtResource(\"4_x5ge4\")]\n\n[node name=\"RuntimeNavigationBaker\" type=\"Node\" parent=\".\" node_paths=PackedStringArray(\"player\")]\nscript = ExtResource(\"5_445ur\")\nenabled = false\ntemplate = SubResource(\"NavigationMesh_vs6am\")\nplayer = NodePath(\"../Player\")\n\n[node name=\"RunThisSceneLabel3D\" type=\"Label3D\" parent=\".\"]\npixel_size = 0.001\nbillboard = 1\nno_depth_test = true\nfixed_size = true\ntext = \"Run This Scene\"\nfont_size = 64\noutline_size = 10\n"
  },
  {
    "path": "project/demo/Demo.tscn",
    "content": "[gd_scene load_steps=10 format=3 uid=\"uid://chol2xlfbq7cu\"]\n\n[ext_resource type=\"Script\" uid=\"uid://chstoagn42gbr\" path=\"res://demo/src/DemoScene.gd\" id=\"1_k7qca\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://d2jihfohphuue\" path=\"res://demo/components/UI.tscn\" id=\"2_nqak5\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bb2lp50sjndus\" path=\"res://demo/components/Environment.tscn\" id=\"3_egbwy\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://domhm87hbhbg1\" path=\"res://demo/components/Player.tscn\" id=\"3_ht63y\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://djhl3foqkj4e2\" path=\"res://demo/components/Tunnel.tscn\" id=\"3_kdh0b\"]\n[ext_resource type=\"Terrain3DMaterial\" uid=\"uid://ccvn15t8km0ft\" path=\"res://demo/data/M_terrain.tres\" id=\"5_fwrtk\"]\n[ext_resource type=\"Material\" uid=\"uid://cgk4glwmc4vk2\" path=\"res://addons/terrain_3d/extras/shaders/M_ocean.tres\" id=\"7_fwrtk\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://d3sr0a7dxfkr8\" path=\"res://addons/terrain_3d/extras/particle_example/Terrain3DParticles.tscn\" id=\"8_fwrtk\"]\n[ext_resource type=\"Terrain3DAssets\" uid=\"uid://dal3jhw6241qg\" path=\"res://demo/data/assets.tres\" id=\"8_g2of2\"]\n\n[node name=\"Demo\" type=\"Node\"]\nscript = ExtResource(\"1_k7qca\")\n\n[node name=\"UI\" parent=\".\" instance=ExtResource(\"2_nqak5\")]\n\n[node name=\"Environment\" parent=\".\" instance=ExtResource(\"3_egbwy\")]\n\n[node name=\"Tunnel\" parent=\".\" instance=ExtResource(\"3_kdh0b\")]\n\n[node name=\"Player\" parent=\".\" instance=ExtResource(\"3_ht63y\")]\ntransform = Transform3D(0.176947, 0, -0.98422, 0, 1, 0, 0.98422, 0, 0.176947, 223.143, 105.348, -1833.08)\n\n[node name=\"Terrain3D\" type=\"Terrain3D\" parent=\".\" node_paths=PackedStringArray(\"collision_target\", \"clipmap_target\", \"ocean_light_target\")]\ndata_directory = \"res://demo/data\"\nmaterial = ExtResource(\"5_fwrtk\")\nassets = ExtResource(\"8_g2of2\")\ncollision_target = NodePath(\"../Player\")\ncollision_mask = 3\nclipmap_target = NodePath(\"../Player\")\ntessellation_level = 3\nmesh_size = 96\ncull_margin = 1662.5\nocean_enabled = true\nocean_material = ExtResource(\"7_fwrtk\")\nocean_light_target = NodePath(\"../Environment/DirectionalLight3D\")\ntop_level = true\nmetadata/_edit_lock_ = true\n\n[node name=\"Terrain3DParticles\" parent=\"Terrain3D\" node_paths=PackedStringArray(\"terrain\") instance=ExtResource(\"8_fwrtk\")]\nterrain = NodePath(\"..\")\n\n[editable path=\"Environment\"]\n"
  },
  {
    "path": "project/demo/NavigationDemo.tscn",
    "content": "[gd_scene load_steps=10 format=3 uid=\"uid://x34e4v60vdmy\"]\n\n[ext_resource type=\"Script\" uid=\"uid://chstoagn42gbr\" path=\"res://demo/src/DemoScene.gd\" id=\"1_po4vw\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bb2lp50sjndus\" path=\"res://demo/components/Environment.tscn\" id=\"2_i74wg\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://domhm87hbhbg1\" path=\"res://demo/components/Player.tscn\" id=\"4_fk3ul\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://djhl3foqkj4e2\" path=\"res://demo/components/Tunnel.tscn\" id=\"5_2lvlr\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://d2jihfohphuue\" path=\"res://demo/components/UI.tscn\" id=\"5_rc7e2\"]\n[ext_resource type=\"NavigationMesh\" uid=\"uid://yf3fu23666k8\" path=\"res://demo/data/nav_mesh.res\" id=\"8_6jfdr\"]\n[ext_resource type=\"Terrain3DAssets\" uid=\"uid://dal3jhw6241qg\" path=\"res://demo/data/assets.tres\" id=\"10_qmhh8\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://di5fovhcyd7re\" path=\"res://demo/components/Enemy.tscn\" id=\"12_b2xl8\"]\n\n[sub_resource type=\"Terrain3DMaterial\" id=\"Terrain3DMaterial_s5mq5\"]\n_shader_parameters = {\n\"_mouse_layer\": 2147483648,\n\"auto_base_texture\": 0,\n\"auto_height_reduction\": 0.1,\n\"auto_overlay_texture\": 1,\n\"auto_slope\": 0.5,\n\"bias_distance\": 512.0,\n\"blend_sharpness\": 0.87,\n\"depth_blur\": 0.0,\n\"dual_scale_far\": 170.0,\n\"dual_scale_near\": 100.0,\n\"dual_scale_reduction\": 0.3,\n\"dual_scale_texture\": 0,\n&\"flat_terrain_normals\": false,\n\"mipmap_bias\": 1.0,\n&\"shader_uniforms\": null,\n&\"shader_uniforms::auto_shader\": null,\n&\"shader_uniforms::dual_scaling\": null,\n&\"shader_uniforms::general\": null,\n&\"shader_uniforms::mipmaps\": null,\n\"tri_scale_reduction\": 0.3\n}\nworld_background = 0\nauto_shader_enabled = true\ndual_scaling_enabled = true\n\n[node name=\"Demo\" type=\"Node\"]\nscript = ExtResource(\"1_po4vw\")\n\n[node name=\"UI\" parent=\".\" instance=ExtResource(\"5_rc7e2\")]\n\n[node name=\"Environment\" parent=\".\" instance=ExtResource(\"2_i74wg\")]\n\n[node name=\"Tunnel\" parent=\".\" instance=ExtResource(\"5_2lvlr\")]\n\n[node name=\"Player\" parent=\".\" instance=ExtResource(\"4_fk3ul\")]\ntransform = Transform3D(0.0774673, 0, -0.996995, 0, 1, 0, 0.996995, 0, 0.0774673, 229.418, 104.956, -1838.16)\n\n[node name=\"Enemy\" parent=\".\" node_paths=PackedStringArray(\"target\") instance=ExtResource(\"12_b2xl8\")]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 192.727, 105.171, -1787.81)\ntarget = NodePath(\"../Player\")\n\n[node name=\"NavigationRegion3D\" type=\"NavigationRegion3D\" parent=\".\"]\nnavigation_mesh = ExtResource(\"8_6jfdr\")\n\n[node name=\"Terrain3D\" type=\"Terrain3D\" parent=\"NavigationRegion3D\"]\ndata_directory = \"res://demo/data\"\nmaterial = SubResource(\"Terrain3DMaterial_s5mq5\")\nassets = ExtResource(\"10_qmhh8\")\ncollision_mask = 3\nmesh_size = 64\ndisplacement_sharpness = 0.5\ntop_level = true\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "project/demo/assets/materials/M_crystal_blue.tres",
    "content": "[gd_resource type=\"StandardMaterial3D\" load_steps=2 format=3 uid=\"uid://fkhkir6t1hml\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://dabyathlpy04p\" path=\"res://demo/assets/textures/rock023_nrm_rgh.png\" id=\"2_3is54\"]\n\n[resource]\ntransparency = 1\nvertex_color_use_as_albedo = true\nalbedo_color = Color(0.4, 1, 1, 0.647059)\nmetallic_specular = 1.0\nroughness = 0.2\nroughness_texture = ExtResource(\"2_3is54\")\nroughness_texture_channel = 3\nemission_enabled = true\nemission = Color(0, 0.145098, 0.776471, 1)\nemission_energy_multiplier = 1.4\nnormal_enabled = true\nnormal_scale = 0.4\nnormal_texture = ExtResource(\"2_3is54\")\nrim_enabled = true\nrim = 0.25\nclearcoat_roughness = 0.0\nao_light_affect = 1.0\nao_texture_channel = 3\nrefraction_enabled = true\nrefraction_scale = -0.1\n"
  },
  {
    "path": "project/demo/assets/materials/M_crystal_purple.tres",
    "content": "[gd_resource type=\"StandardMaterial3D\" load_steps=2 format=3 uid=\"uid://cso4f2iyuxpmc\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://dabyathlpy04p\" path=\"res://demo/assets/textures/rock023_nrm_rgh.png\" id=\"2_1t610\"]\n\n[resource]\ntransparency = 1\nvertex_color_use_as_albedo = true\nalbedo_color = Color(0.4, 0.4, 1, 0.556863)\nmetallic_specular = 1.0\nroughness = 0.2\nroughness_texture = ExtResource(\"2_1t610\")\nroughness_texture_channel = 3\nemission_enabled = true\nemission = Color(0.235294, 0.145098, 0.776471, 1)\nnormal_enabled = true\nnormal_scale = 0.4\nnormal_texture = ExtResource(\"2_1t610\")\nrim_enabled = true\nrim = 0.25\nclearcoat_roughness = 0.0\nao_light_affect = 1.0\nao_texture_channel = 3\nrefraction_enabled = true\nrefraction_scale = -0.1\n"
  },
  {
    "path": "project/demo/assets/materials/M_crystal_red.tres",
    "content": "[gd_resource type=\"StandardMaterial3D\" load_steps=2 format=3 uid=\"uid://ickkffutwcvo\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://dabyathlpy04p\" path=\"res://demo/assets/textures/rock023_nrm_rgh.png\" id=\"2_k2yhv\"]\n\n[resource]\ntransparency = 1\nalbedo_color = Color(0.670588, 0.0588235, 0.384314, 0.509804)\nmetallic_specular = 1.0\nroughness = 0.2\nroughness_texture = ExtResource(\"2_k2yhv\")\nroughness_texture_channel = 3\nemission_enabled = true\nemission = Color(0.258824, 0.0823529, 0.25098, 1)\nnormal_enabled = true\nnormal_scale = 0.4\nnormal_texture = ExtResource(\"2_k2yhv\")\nrim_enabled = true\nrim = 0.25\nclearcoat_roughness = 0.0\nao_enabled = true\nao_light_affect = 1.0\nao_texture_channel = 3\nrefraction_enabled = true\nrefraction_scale = -0.1\n"
  },
  {
    "path": "project/demo/assets/materials/M_rock23_black_tp.tres",
    "content": "[gd_resource type=\"StandardMaterial3D\" load_steps=3 format=3 uid=\"uid://d0hyi5n6ng25w\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://c88j3oj0lf6om\" path=\"res://demo/assets/textures/rock023_alb_ht.png\" id=\"1_1js8i\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dabyathlpy04p\" path=\"res://demo/assets/textures/rock023_nrm_rgh.png\" id=\"2_snl3x\"]\n\n[resource]\nalbedo_color = Color(0.501406, 0.501407, 0.501406, 1)\nalbedo_texture = ExtResource(\"1_1js8i\")\nroughness = 0.95\nroughness_texture = ExtResource(\"2_snl3x\")\nroughness_texture_channel = 3\nnormal_enabled = true\nnormal_texture = ExtResource(\"2_snl3x\")\nuv1_scale = Vector3(0.4, 0.4, 0.4)\nuv1_triplanar = true\nuv1_world_triplanar = true\ntexture_filter = 5\n"
  },
  {
    "path": "project/demo/assets/materials/M_rock23_tp.tres",
    "content": "[gd_resource type=\"StandardMaterial3D\" load_steps=3 format=3 uid=\"uid://nbbdrx8vma80\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://c88j3oj0lf6om\" path=\"res://demo/assets/textures/rock023_alb_ht.png\" id=\"1_mek0c\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dabyathlpy04p\" path=\"res://demo/assets/textures/rock023_nrm_rgh.png\" id=\"2_pp13c\"]\n\n[resource]\nvertex_color_use_as_albedo = true\nalbedo_color = Color(0.83, 0.83, 0.83, 1)\nalbedo_texture = ExtResource(\"1_mek0c\")\nnormal_enabled = true\nnormal_texture = ExtResource(\"2_pp13c\")\nuv1_scale = Vector3(0.02, 0.02, 0.02)\nuv1_triplanar = true\nuv1_triplanar_sharpness = 6.06286\nuv1_world_triplanar = true\ntexture_filter = 5\n"
  },
  {
    "path": "project/demo/assets/models/CrystalC.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://cribhhvg03u8g\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://c8cx4xjwluvxw\" path=\"res://demo/assets/models/RockC.glb\" id=\"1_1vww1\"]\n[ext_resource type=\"Material\" uid=\"uid://fkhkir6t1hml\" path=\"res://demo/assets/materials/M_crystal_blue.tres\" id=\"2_fclne\"]\n\n[sub_resource type=\"ConcavePolygonShape3D\" id=\"ConcavePolygonShape3D_ycsjC\"]\ndata = PackedVector3Array(1.3907, -4.3898, 0.264, 0.3707, -6.5026, 0.2358, 0.9197, -5.2139, 0.5803, 1.3907, -4.3898, 0.264, 1.5973, -4.4989, -0.523, 0.3707, -6.5026, 0.2358, 2.7182, -1.9153, 1.6333, 2.6862, -0.2733, 1.344, 2.791, -2.0828, 0.6971, -0.6892, -7.3327, -0.0456, -1.2211, -6.6432, -1.3733, -1.8677, -6.7222, 0.0053, 0.1584, -6.6788, -0.712, 0.8528, -5.1659, -1.2387, -0.129, -6.0155, -1.6627, 0.3707, -6.5026, 0.2358, 0.8249, -4.8431, 1.2373, 0.9197, -5.2139, 0.5803, 0.3707, -6.5026, 0.2358, -0.1504, -6.0468, 1.6347, 0.8249, -4.8431, 1.2373, 2.7182, -1.9153, 1.6333, 2.5729, -0.0463, 2.323, 2.6862, -0.2733, 1.344, -1.5434, -2.0626, 2.9798, -0.0319, -0.907, 3.3884, -0.6296, -2.1168, 3.0274, -1.5434, -2.0626, 2.9798, -2.2391, -1.0013, 3.2378, -0.0319, -0.907, 3.3884, -3.2503, -1.9438, -0.0444, -3.1045, -0.0443, -0.8667, -2.9895, -0.0523, 0.8233, -0.5191, -2.1018, -2.9939, -2.0489, -0.9812, -3.014, -1.4897, -2.0239, -2.8933, -0.5191, -2.1018, -2.9939, 0.0444, -0.8451, -3.3989, -2.0489, -0.9812, -3.014, 3.4189, -1.5098, -2.1519, 3.3947, 0.8197, -1.419, 2.9911, 0.177, -2.5055, -0.9484, -5.3598, 2.2535, -0.8975, -3.7031, 2.6018, -0.1302, -3.9225, 2.4564, -2.8602, -5.2902, -0.0034, -3.2063, -3.9919, -0.0118, -3.0246, -4.4027, 0.8468, -0.9423, -5.2303, -2.2174, -1.6189, -3.1121, -2.5666, -2.3373, -3.6333, -2.2624, -0.9423, -5.2303, -2.2174, -0.9004, -3.6647, -2.5803, -1.6189, -3.1121, -2.5666, 2.4159, -3.3908, 0.0683, 2.6374, -3.7219, -1.0723, 1.5973, -4.4989, -0.523, 0.0817, 0.7564, 3.232, 1.3747, 1.7762, 2.618, 1.9677, 0.877, 2.8315, 0.0817, 0.7564, 3.232, 0.5064, 1.8556, 2.7003, 1.3747, 1.7762, 2.618, -0.2637, 3.5029, 1.8153, 0.4487, 4.8708, 1.6514, 0.6919, 3.2703, 2.2188, -2.3926, 3.3478, 0.3255, -1.6818, 3.3981, 1.0198, -2.0691, 2.5964, 1.5185, -1.2285, 2.4662, -2.4325, -1.544, 3.8438, -1.471, -2.3777, 2.1645, -2.0413, 2.3543, 3.9299, -1.7328, 1.4379, 5.2295, -1.8152, 1.1494, 2.3687, -2.734, 1.4341, 6.0081, 1.2447, 0.9325, 6.924, 0.4365, 2.3528, 6.0999, 0.4838, -1.8677, -6.7222, 0.0053, -2.3434, -5.4566, 1.3758, -1.2352, -6.7568, 1.4079, 3.0694, 0.3768, 0.2917, 3.6836, 2.3906, 0.1427, 3.39, -0.1345, -0.5433, 1.5686, 6.857, -0.4389, 0.7824, 7.1818, -0.4789, 0.9515, 6.8097, -1.0683, 2.4165, 5.7335, -0.6508, 1.4379, 5.2295, -1.8152, 2.3543, 3.9299, -1.7328, 2.0681, 1.0413, -2.9849, 1.1494, 2.3687, -2.734, 0.0399, 0.8971, -3.3453, -0.1803, 5.4522, -1.6664, -0.2609, 6.3493, -0.8464, -1.544, 3.8438, -1.471, -3.1045, -0.0443, -0.8667, -2.9635, 2.0631, -0.0469, -2.9895, -0.0523, 0.8233, -2.8218, -0.0908, -2.3316, -2.8949, 1.7465, -1.4628, -3.1045, -0.0443, -0.8667, -1.137, 5.0901, -0.2048, -0.2548, 6.3653, 0.3115, -0.7059, 4.7856, 0.8682, -1.8973, 4.0438, -0.0231, -1.6818, 3.3981, 1.0198, -2.3926, 3.3478, 0.3255, -2.9895, -0.0523, 0.8233, -2.7222, 1.5834, 1.3251, -2.9289, -0.1232, 2.5316, -0.7059, 4.7856, 0.8682, -0.2548, 6.3653, 0.3115, 0.4487, 4.8708, 1.6514, -0.7059, 4.7856, 0.8682, 0.4487, 4.8708, 1.6514, -0.2637, 3.5029, 1.8153, 3.3947, 0.8197, -1.419, 3.6143, 2.6541, -0.7787, 3.0814, 2.2747, -1.5878, 3.3017, 4.3689, -0.5397, 2.4165, 5.7335, -0.6508, 2.3543, 3.9299, -1.7328, -2.1314, 0.7603, 3.0046, -1.0522, 1.986, 2.2721, 0.0817, 0.7564, 3.232, 0.0399, 0.8971, -3.3453, 1.1494, 2.3687, -2.734, -1.2285, 2.4662, -2.4325, 0.549, 3.3793, -2.5003, 0.4374, 4.5707, -2.224, -0.1459, 3.4745, -2.3857, 1.4341, 6.0081, 1.2447, 2.3528, 6.0999, 0.4838, 2.8818, 4.7444, 1.3483, -3.1045, -0.0443, -0.8667, -2.8949, 1.7465, -1.4628, -2.9635, 2.0631, -0.0469, -2.7186, 2.9035, -1.3821, -2.3162, 3.6228, -0.9721, -2.7435, 3.293, -0.5137, -0.1803, 5.4522, -1.6664, -1.544, 3.8438, -1.471, -1.2285, 2.4662, -2.4325, -2.1314, 0.7603, 3.0046, -2.0691, 2.5964, 1.5185, -1.0522, 1.986, 2.2721, -2.0691, 2.5964, 1.5185, -1.6818, 3.3981, 1.0198, -1.0522, 1.986, 2.2721, 0.0399, 0.8971, -3.3453, -1.2285, 2.4662, -2.4325, -2.0395, 0.8474, -2.9039, 1.9677, 0.877, 2.8315, 1.5121, 2.8416, 2.3106, 2.2766, 3.3307, 2.1367, 1.9677, 0.877, 2.8315, 1.3747, 1.7762, 2.618, 1.5121, 2.8416, 2.3106, 0.6919, 3.2703, 2.2188, 2.2766, 3.3307, 2.1367, 1.5121, 2.8416, 2.3106, 0.6919, 3.2703, 2.2188, 0.4487, 4.8708, 1.6514, 2.2766, 3.3307, 2.1367, 3.3947, 0.8197, -1.419, 3.0814, 2.2747, -1.5878, 2.9911, 0.177, -2.5055, 2.8818, 4.7444, 1.3483, 2.3528, 6.0999, 0.4838, 3.481, 4.5391, 0.4718, 2.0681, 1.0413, -2.9849, 2.3543, 3.9299, -1.7328, 1.1494, 2.3687, -2.734, 2.5729, -0.0463, 2.323, 2.8449, 1.8063, 1.8788, 2.6862, -0.2733, 1.344, -1.4897, -2.0239, -2.8933, -2.3373, -3.6333, -2.2624, -1.6189, -3.1121, -2.5666, -1.4897, -2.0239, -2.8933, -2.0489, -0.9812, -3.014, -2.3373, -3.6333, -2.2624, -2.0395, 0.8474, -2.9039, -1.2285, 2.4662, -2.4325, -2.3777, 2.1645, -2.0413, 2.1308, -0.8122, -3.0291, 0.0444, -0.8451, -3.3989, 1.2123, -2.2074, -2.6786, -3.2503, -1.9438, -0.0444, -2.9895, -0.0523, 0.8233, -3.048, -2.4163, 1.5053, -2.9895, -0.0523, 0.8233, -2.9635, 2.0631, -0.0469, -2.7222, 1.5834, 1.3251, -2.8218, -0.0908, -2.3316, -3.1045, -0.0443, -0.8667, -3.1353, -2.4738, -1.533, -0.6296, -2.1168, 3.0274, -0.0319, -0.907, 3.3884, 0.6607, -2.1929, 2.7904, 0.0817, 0.7564, 3.232, -1.0522, 1.986, 2.2721, 0.5064, 1.8556, 2.7003, 3.0694, 0.3768, 0.2917, 3.39, -0.1345, -0.5433, 3.0799, -1.877, -0.2485, 2.332, -3.2513, 1.4491, 1.8096, -3.8731, 0.517, 1.7532, -3.8773, 1.1862, 2.332, -3.2513, 1.4491, 2.4159, -3.3908, 0.0683, 1.8096, -3.8731, 0.517, 3.0694, 0.3768, 0.2917, 3.2927, 2.696, 1.0915, 3.6836, 2.3906, 0.1427, 0.8528, -5.1659, -1.2387, 0.5989, -4.5033, -1.836, -0.129, -6.0155, -1.6627, 1.6723, -4.1113, -1.5062, 2.2303, -3.7001, -1.8804, 1.5835, -3.611, -1.9401, 2.6485, -2.4319, -2.56, 2.1308, -0.8122, -3.0291, 1.2123, -2.2074, -2.6786, -1.2211, -6.6432, -1.3733, -2.3738, -5.4707, -1.3892, -1.8677, -6.7222, 0.0053, 1.2123, -2.2074, -2.6786, 0.0444, -0.8451, -3.3989, -0.5191, -2.1018, -2.9939, -0.9423, -5.2303, -2.2174, -0.0313, -3.8492, -2.3572, -0.9004, -3.6647, -2.5803, -2.9895, -0.0523, 0.8233, -2.9289, -0.1232, 2.5316, -3.048, -2.4163, 1.5053, -3.1353, -2.4738, -1.533, -3.1045, -0.0443, -0.8667, -3.2503, -1.9438, -0.0444, -2.8602, -5.2902, -0.0034, -3.0733, -4.4498, -0.8737, -3.2063, -3.9919, -0.0118, -0.1504, -6.0468, 1.6347, 0.6052, -4.4095, 1.8566, 0.8249, -4.8431, 1.2373, -2.3213, -3.5933, 2.2613, -1.5434, -2.0626, 2.9798, -1.6095, -3.1051, 2.5592, -2.3213, -3.5933, 2.2613, -2.2391, -1.0013, 3.2378, -1.5434, -2.0626, 2.9798, 3.0799, -1.877, -0.2485, 3.39, -0.1345, -0.5433, 3.4106, -2.2767, -1.2285, -0.0319, -0.907, 3.3884, 1.8364, -0.871, 2.91, 0.6607, -2.1929, 2.7904, 1.7532, -3.8773, 1.1862, 1.5121, -3.4321, 1.801, 2.332, -3.2513, 1.4491, -0.9484, -5.3598, 2.2535, -1.6095, -3.1051, 2.5592, -0.8975, -3.7031, 2.6018, -0.9484, -5.3598, 2.2535, -2.3213, -3.5933, 2.2613, -1.6095, -3.1051, 2.5592, 2.4159, -3.3908, 0.0683, 1.3907, -4.3898, 0.264, 1.8096, -3.8731, 0.517, 2.4159, -3.3908, 0.0683, 1.5973, -4.4989, -0.523, 1.3907, -4.3898, 0.264, 0.6607, -2.1929, 2.7904, 1.8364, -0.871, 2.91, 2.1556, -2.2057, 2.3132, -0.6892, -7.3327, -0.0456, 0.3707, -6.5026, 0.2358, 0.1584, -6.6788, -0.712, 2.7182, -1.9153, 1.6333, 2.332, -3.2513, 1.4491, 2.1556, -2.2057, 2.3132, -0.8975, -3.7031, 2.6018, -1.5434, -2.0626, 2.9798, -0.6296, -2.1168, 3.0274, -0.8975, -3.7031, 2.6018, -1.6095, -3.1051, 2.5592, -1.5434, -2.0626, 2.9798, -0.5191, -2.1018, -2.9939, -1.6189, -3.1121, -2.5666, -0.9004, -3.6647, -2.5803, -0.5191, -2.1018, -2.9939, -1.4897, -2.0239, -2.8933, -1.6189, -3.1121, -2.5666, 3.4106, -2.2767, -1.2285, 2.2303, -3.7001, -1.8804, 2.6374, -3.7219, -1.0723, 2.2303, -3.7001, -1.8804, 3.4189, -1.5098, -2.1519, 2.6485, -2.4319, -2.56, 2.2303, -3.7001, -1.8804, 3.4106, -2.2767, -1.2285, 3.4189, -1.5098, -2.1519, 0.6919, 3.2703, 2.2188, 1.3747, 1.7762, 2.618, 0.5064, 1.8556, 2.7003, 0.6919, 3.2703, 2.2188, 1.5121, 2.8416, 2.3106, 1.3747, 1.7762, 2.618, -2.7186, 2.9035, -1.3821, -2.8949, 1.7465, -1.4628, -2.3777, 2.1645, -2.0413, 3.6143, 2.6541, -0.7787, 3.481, 4.5391, 0.4718, 3.3017, 4.3689, -0.5397, 3.6143, 2.6541, -0.7787, 3.6836, 2.3906, 0.1427, 3.481, 4.5391, 0.4718, 0.7824, 7.1818, -0.4789, -0.2548, 6.3653, 0.3115, -0.2609, 6.3493, -0.8464, 0.7824, 7.1818, -0.4789, 0.9325, 6.924, 0.4365, -0.2548, 6.3653, 0.3115, -0.1504, -6.0468, 1.6347, -1.2352, -6.7568, 1.4079, -0.9484, -5.3598, 2.2535, 1.8096, -3.8731, 0.517, 0.8249, -4.8431, 1.2373, 1.7532, -3.8773, 1.1862, 0.8249, -4.8431, 1.2373, 1.3907, -4.3898, 0.264, 0.9197, -5.2139, 0.5803, 0.8249, -4.8431, 1.2373, 1.8096, -3.8731, 0.517, 1.3907, -4.3898, 0.264, -0.1302, -3.9225, 2.4564, 1.5121, -3.4321, 1.801, 0.6052, -4.4095, 1.8566, -0.1302, -3.9225, 2.4564, 0.6607, -2.1929, 2.7904, 1.5121, -3.4321, 1.801, 2.4159, -3.3908, 0.0683, 2.791, -2.0828, 0.6971, 3.0799, -1.877, -0.2485, 1.5973, -4.4989, -0.523, 1.6723, -4.1113, -1.5062, 0.8528, -5.1659, -1.2387, -3.0246, -4.4027, 0.8468, -2.3213, -3.5933, 2.2613, -2.3434, -5.4566, 1.3758, -3.0246, -4.4027, 0.8468, -3.048, -2.4163, 1.5053, -2.3213, -3.5933, 2.2613, -1.2211, -6.6432, -1.3733, -0.129, -6.0155, -1.6627, -0.9423, -5.2303, -2.2174, -3.0733, -4.4498, -0.8737, -2.3373, -3.6333, -2.2624, -3.1353, -2.4738, -1.533, -3.0733, -4.4498, -0.8737, -2.3738, -5.4707, -1.3892, -2.3373, -3.6333, -2.2624, 1.5835, -3.611, -1.9401, -0.0313, -3.8492, -2.3572, 0.5989, -4.5033, -1.836, 1.5835, -3.611, -1.9401, 1.2123, -2.2074, -2.6786, -0.0313, -3.8492, -2.3572, 2.5729, -0.0463, 2.323, 1.8364, -0.871, 2.91, 1.9677, 0.877, 2.8315, -2.9289, -0.1232, 2.5316, -2.1314, 0.7603, 3.0046, -2.2391, -1.0013, 3.2378, -2.0395, 0.8474, -2.9039, -2.8218, -0.0908, -2.3316, -2.0489, -0.9812, -3.014, 2.1308, -0.8122, -3.0291, 2.9911, 0.177, -2.5055, 2.0681, 1.0413, -2.9849, 3.2927, 2.696, 1.0915, 2.2766, 3.3307, 2.1367, 2.8818, 4.7444, 1.3483, 3.2927, 2.696, 1.0915, 2.8449, 1.8063, 1.8788, 2.2766, 3.3307, 2.1367, -2.9635, 2.0631, -0.0469, -2.7435, 3.293, -0.5137, -2.3926, 3.3478, 0.3255, 2.3528, 6.0999, 0.4838, 1.5686, 6.857, -0.4389, 2.4165, 5.7335, -0.6508, -2.3162, 3.6228, -0.9721, -1.137, 5.0901, -0.2048, -1.8973, 4.0438, -0.0231, -2.3162, 3.6228, -0.9721, -1.544, 3.8438, -1.471, -1.137, 5.0901, -0.2048, 0.4374, 4.5707, -2.224, 0.9515, 6.8097, -1.0683, -0.1803, 5.4522, -1.6664, 0.4374, 4.5707, -2.224, 1.4379, 5.2295, -1.8152, 0.9515, 6.8097, -1.0683, -1.2352, -6.7568, 1.4079, 0.3707, -6.5026, 0.2358, -0.6892, -7.3327, -0.0456, -1.2352, -6.7568, 1.4079, -0.1504, -6.0468, 1.6347, 0.3707, -6.5026, 0.2358, 0.6607, -2.1929, 2.7904, -0.8975, -3.7031, 2.6018, -0.6296, -2.1168, 3.0274, 0.6607, -2.1929, 2.7904, -0.1302, -3.9225, 2.4564, -0.8975, -3.7031, 2.6018, 2.791, -2.0828, 0.6971, 2.332, -3.2513, 1.4491, 2.7182, -1.9153, 1.6333, 2.791, -2.0828, 0.6971, 2.4159, -3.3908, 0.0683, 2.332, -3.2513, 1.4491, 1.6723, -4.1113, -1.5062, 2.6374, -3.7219, -1.0723, 2.2303, -3.7001, -1.8804, 1.6723, -4.1113, -1.5062, 1.5973, -4.4989, -0.523, 2.6374, -3.7219, -1.0723, -3.048, -2.4163, 1.5053, -3.2063, -3.9919, -0.0118, -3.2503, -1.9438, -0.0444, -3.048, -2.4163, 1.5053, -3.0246, -4.4027, 0.8468, -3.2063, -3.9919, -0.0118, -0.129, -6.0155, -1.6627, -0.6892, -7.3327, -0.0456, 0.1584, -6.6788, -0.712, -0.129, -6.0155, -1.6627, -1.2211, -6.6432, -1.3733, -0.6892, -7.3327, -0.0456, 1.2123, -2.2074, -2.6786, 2.2303, -3.7001, -1.8804, 2.6485, -2.4319, -2.56, 1.2123, -2.2074, -2.6786, 1.5835, -3.611, -1.9401, 2.2303, -3.7001, -1.8804, 3.3947, 0.8197, -1.419, 3.6836, 2.3906, 0.1427, 3.6143, 2.6541, -0.7787, 3.3947, 0.8197, -1.419, 3.39, -0.1345, -0.5433, 3.6836, 2.3906, 0.1427, -2.1314, 0.7603, 3.0046, -2.7222, 1.5834, 1.3251, -2.0691, 2.5964, 1.5185, -2.1314, 0.7603, 3.0046, -2.9289, -0.1232, 2.5316, -2.7222, 1.5834, 1.3251, 2.9911, 0.177, -2.5055, 2.6485, -2.4319, -2.56, 3.4189, -1.5098, -2.1519, 2.9911, 0.177, -2.5055, 2.1308, -0.8122, -3.0291, 2.6485, -2.4319, -2.56, -2.7435, 3.293, -0.5137, -2.8949, 1.7465, -1.4628, -2.7186, 2.9035, -1.3821, -2.7435, 3.293, -0.5137, -2.9635, 2.0631, -0.0469, -2.8949, 1.7465, -1.4628, -0.1459, 3.4745, -2.3857, 1.1494, 2.3687, -2.734, 0.549, 3.3793, -2.5003, -0.1459, 3.4745, -2.3857, -1.2285, 2.4662, -2.4325, 1.1494, 2.3687, -2.734, 2.3543, 3.9299, -1.7328, 3.6143, 2.6541, -0.7787, 3.3017, 4.3689, -0.5397, 2.3543, 3.9299, -1.7328, 3.0814, 2.2747, -1.5878, 3.6143, 2.6541, -0.7787, 1.5686, 6.857, -0.4389, 0.9325, 6.924, 0.4365, 0.7824, 7.1818, -0.4789, 1.5686, 6.857, -0.4389, 2.3528, 6.0999, 0.4838, 0.9325, 6.924, 0.4365, -1.544, 3.8438, -1.471, -2.7186, 2.9035, -1.3821, -2.3777, 2.1645, -2.0413, -1.544, 3.8438, -1.471, -2.3162, 3.6228, -0.9721, -2.7186, 2.9035, -1.3821, 1.4379, 5.2295, -1.8152, 0.549, 3.3793, -2.5003, 1.1494, 2.3687, -2.734, 1.4379, 5.2295, -1.8152, 0.4374, 4.5707, -2.224, 0.549, 3.3793, -2.5003, 2.1556, -2.2057, 2.3132, 1.5121, -3.4321, 1.801, 0.6607, -2.1929, 2.7904, 2.1556, -2.2057, 2.3132, 2.332, -3.2513, 1.4491, 1.5121, -3.4321, 1.801, 3.4106, -2.2767, -1.2285, 2.4159, -3.3908, 0.0683, 3.0799, -1.877, -0.2485, 3.4106, -2.2767, -1.2285, 2.6374, -3.7219, -1.0723, 2.4159, -3.3908, 0.0683, 0.1584, -6.6788, -0.712, 1.5973, -4.4989, -0.523, 0.8528, -5.1659, -1.2387, 0.1584, -6.6788, -0.712, 0.3707, -6.5026, 0.2358, 1.5973, -4.4989, -0.523, -3.2503, -1.9438, -0.0444, -3.0733, -4.4498, -0.8737, -3.1353, -2.4738, -1.533, -3.2503, -1.9438, -0.0444, -3.2063, -3.9919, -0.0118, -3.0733, -4.4498, -0.8737, -0.5191, -2.1018, -2.9939, -0.0313, -3.8492, -2.3572, 1.2123, -2.2074, -2.6786, -0.5191, -2.1018, -2.9939, -0.9004, -3.6647, -2.5803, -0.0313, -3.8492, -2.3572, 3.4189, -1.5098, -2.1519, 3.39, -0.1345, -0.5433, 3.3947, 0.8197, -1.419, 3.4189, -1.5098, -2.1519, 3.4106, -2.2767, -1.2285, 3.39, -0.1345, -0.5433, 2.7182, -1.9153, 1.6333, 1.8364, -0.871, 2.91, 2.5729, -0.0463, 2.323, 2.7182, -1.9153, 1.6333, 2.1556, -2.2057, 2.3132, 1.8364, -0.871, 2.91, -2.3777, 2.1645, -2.0413, -2.8218, -0.0908, -2.3316, -2.0395, 0.8474, -2.9039, -2.3777, 2.1645, -2.0413, -2.8949, 1.7465, -1.4628, -2.8218, -0.0908, -2.3316, 3.481, 4.5391, 0.4718, 3.2927, 2.696, 1.0915, 2.8818, 4.7444, 1.3483, 3.481, 4.5391, 0.4718, 3.6836, 2.3906, 0.1427, 3.2927, 2.696, 1.0915, 0.6919, 3.2703, 2.2188, -1.0522, 1.986, 2.2721, -0.2637, 3.5029, 1.8153, 0.6919, 3.2703, 2.2188, 0.5064, 1.8556, 2.7003, -1.0522, 1.986, 2.2721, -2.0691, 2.5964, 1.5185, -2.9635, 2.0631, -0.0469, -2.3926, 3.3478, 0.3255, -2.0691, 2.5964, 1.5185, -2.7222, 1.5834, 1.3251, -2.9635, 2.0631, -0.0469, -0.2548, 6.3653, 0.3115, 1.4341, 6.0081, 1.2447, 0.4487, 4.8708, 1.6514, -0.2548, 6.3653, 0.3115, 0.9325, 6.924, 0.4365, 1.4341, 6.0081, 1.2447, 3.3017, 4.3689, -0.5397, 2.3528, 6.0999, 0.4838, 2.4165, 5.7335, -0.6508, 3.3017, 4.3689, -0.5397, 3.481, 4.5391, 0.4718, 2.3528, 6.0999, 0.4838, -0.2609, 6.3493, -0.8464, -1.137, 5.0901, -0.2048, -1.544, 3.8438, -1.471, -0.2609, 6.3493, -0.8464, -0.2548, 6.3653, 0.3115, -1.137, 5.0901, -0.2048, 0.7824, 7.1818, -0.4789, -0.1803, 5.4522, -1.6664, 0.9515, 6.8097, -1.0683, 0.7824, 7.1818, -0.4789, -0.2609, 6.3493, -0.8464, -0.1803, 5.4522, -1.6664, 2.4165, 5.7335, -0.6508, 0.9515, 6.8097, -1.0683, 1.4379, 5.2295, -1.8152, 2.4165, 5.7335, -0.6508, 1.5686, 6.857, -0.4389, 0.9515, 6.8097, -1.0683, 0.4374, 4.5707, -2.224, -1.2285, 2.4662, -2.4325, -0.1459, 3.4745, -2.3857, 0.4374, 4.5707, -2.224, -0.1803, 5.4522, -1.6664, -1.2285, 2.4662, -2.4325, -1.8973, 4.0438, -0.0231, -0.7059, 4.7856, 0.8682, -1.6818, 3.3981, 1.0198, -1.8973, 4.0438, -0.0231, -1.137, 5.0901, -0.2048, -0.7059, 4.7856, 0.8682, -2.3162, 3.6228, -0.9721, -2.3926, 3.3478, 0.3255, -2.7435, 3.293, -0.5137, -2.3162, 3.6228, -0.9721, -1.8973, 4.0438, -0.0231, -2.3926, 3.3478, 0.3255, -1.6818, 3.3981, 1.0198, -0.2637, 3.5029, 1.8153, -1.0522, 1.986, 2.2721, -1.6818, 3.3981, 1.0198, -0.7059, 4.7856, 0.8682, -0.2637, 3.5029, 1.8153, 0.4487, 4.8708, 1.6514, 2.8818, 4.7444, 1.3483, 2.2766, 3.3307, 2.1367, 0.4487, 4.8708, 1.6514, 1.4341, 6.0081, 1.2447, 2.8818, 4.7444, 1.3483, 3.0814, 2.2747, -1.5878, 2.0681, 1.0413, -2.9849, 2.9911, 0.177, -2.5055, 3.0814, 2.2747, -1.5878, 2.3543, 3.9299, -1.7328, 2.0681, 1.0413, -2.9849, 0.0444, -0.8451, -3.3989, -2.0395, 0.8474, -2.9039, -2.0489, -0.9812, -3.014, 0.0444, -0.8451, -3.3989, 0.0399, 0.8971, -3.3453, -2.0395, 0.8474, -2.9039, -2.2391, -1.0013, 3.2378, 0.0817, 0.7564, 3.232, -0.0319, -0.907, 3.3884, -2.2391, -1.0013, 3.2378, -2.1314, 0.7603, 3.0046, 0.0817, 0.7564, 3.232, 2.5729, -0.0463, 2.323, 2.2766, 3.3307, 2.1367, 2.8449, 1.8063, 1.8788, 2.5729, -0.0463, 2.323, 1.9677, 0.877, 2.8315, 2.2766, 3.3307, 2.1367, 2.8449, 1.8063, 1.8788, 3.0694, 0.3768, 0.2917, 2.6862, -0.2733, 1.344, 2.8449, 1.8063, 1.8788, 3.2927, 2.696, 1.0915, 3.0694, 0.3768, 0.2917, 2.1308, -0.8122, -3.0291, 0.0399, 0.8971, -3.3453, 0.0444, -0.8451, -3.3989, 2.1308, -0.8122, -3.0291, 2.0681, 1.0413, -2.9849, 0.0399, 0.8971, -3.3453, -2.0489, -0.9812, -3.014, -3.1353, -2.4738, -1.533, -2.3373, -3.6333, -2.2624, -2.0489, -0.9812, -3.014, -2.8218, -0.0908, -2.3316, -3.1353, -2.4738, -1.533, -2.9289, -0.1232, 2.5316, -2.3213, -3.5933, 2.2613, -3.048, -2.4163, 1.5053, -2.9289, -0.1232, 2.5316, -2.2391, -1.0013, 3.2378, -2.3213, -3.5933, 2.2613, -0.0319, -0.907, 3.3884, 1.9677, 0.877, 2.8315, 1.8364, -0.871, 2.91, -0.0319, -0.907, 3.3884, 0.0817, 0.7564, 3.232, 1.9677, 0.877, 2.8315, 2.6862, -0.2733, 1.344, 3.0799, -1.877, -0.2485, 2.791, -2.0828, 0.6971, 2.6862, -0.2733, 1.344, 3.0694, 0.3768, 0.2917, 3.0799, -1.877, -0.2485, 0.8528, -5.1659, -1.2387, 1.5835, -3.611, -1.9401, 0.5989, -4.5033, -1.836, 0.8528, -5.1659, -1.2387, 1.6723, -4.1113, -1.5062, 1.5835, -3.611, -1.9401, 0.5989, -4.5033, -1.836, -0.9423, -5.2303, -2.2174, -0.129, -6.0155, -1.6627, 0.5989, -4.5033, -1.836, -0.0313, -3.8492, -2.3572, -0.9423, -5.2303, -2.2174, -1.2211, -6.6432, -1.3733, -2.3373, -3.6333, -2.2624, -2.3738, -5.4707, -1.3892, -1.2211, -6.6432, -1.3733, -0.9423, -5.2303, -2.2174, -2.3373, -3.6333, -2.2624, -2.3738, -5.4707, -1.3892, -2.8602, -5.2902, -0.0034, -1.8677, -6.7222, 0.0053, -2.3738, -5.4707, -1.3892, -3.0733, -4.4498, -0.8737, -2.8602, -5.2902, -0.0034, -1.8677, -6.7222, 0.0053, -3.0246, -4.4027, 0.8468, -2.3434, -5.4566, 1.3758, -1.8677, -6.7222, 0.0053, -2.8602, -5.2902, -0.0034, -3.0246, -4.4027, 0.8468, -2.3434, -5.4566, 1.3758, -0.9484, -5.3598, 2.2535, -1.2352, -6.7568, 1.4079, -2.3434, -5.4566, 1.3758, -2.3213, -3.5933, 2.2613, -0.9484, -5.3598, 2.2535, -0.1504, -6.0468, 1.6347, -0.1302, -3.9225, 2.4564, 0.6052, -4.4095, 1.8566, -0.1504, -6.0468, 1.6347, -0.9484, -5.3598, 2.2535, -0.1302, -3.9225, 2.4564, 0.6052, -4.4095, 1.8566, 1.7532, -3.8773, 1.1862, 0.8249, -4.8431, 1.2373, 0.6052, -4.4095, 1.8566, 1.5121, -3.4321, 1.801, 1.7532, -3.8773, 1.1862, -0.6892, -7.3327, -0.0456, -1.8677, -6.7222, 0.0053, -1.2352, -6.7568, 1.4079)\n\n[node name=\"CrystalC\" instance=ExtResource(\"1_1vww1\")]\ncollision_mask = 3\n\n[node name=\"Rock3\" parent=\".\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"2_fclne\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\".\" index=\"1\"]\nshape = SubResource(\"ConcavePolygonShape3D_ycsjC\")\n"
  },
  {
    "path": "project/demo/assets/models/LOD10Example.tscn",
    "content": "[gd_scene load_steps=21 format=3 uid=\"uid://dqwgv1ahsvqio\"]\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_788j8\"]\nalbedo_color = Color(1, 0, 0, 1)\n\n[sub_resource type=\"SphereMesh\" id=\"SphereMesh_u4ac2\"]\nmaterial = SubResource(\"StandardMaterial3D_788j8\")\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_0qa1y\"]\nalbedo_color = Color(1, 0.907, 0.38, 1)\n\n[sub_resource type=\"TorusMesh\" id=\"TorusMesh_tjrnq\"]\nmaterial = SubResource(\"StandardMaterial3D_0qa1y\")\ninner_radius = 0.141\nouter_radius = 0.601\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_3i52q\"]\nalbedo_color = Color(1, 0.540167, 0.11, 1)\n\n[sub_resource type=\"BoxMesh\" id=\"BoxMesh_xyuxq\"]\nmaterial = SubResource(\"StandardMaterial3D_3i52q\")\nsize = Vector3(0.85, 0.85, 0.85)\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_p0ha4\"]\nalbedo_color = Color(0.48, 1, 0.497333, 1)\n\n[sub_resource type=\"PrismMesh\" id=\"PrismMesh_xnm4h\"]\nmaterial = SubResource(\"StandardMaterial3D_p0ha4\")\nsize = Vector3(0.89, 0.82, 0.835)\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_jv6fb\"]\nalbedo_color = Color(0.3384, 0.538933, 0.94, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_i1yhp\"]\nmaterial = SubResource(\"StandardMaterial3D_jv6fb\")\ntop_radius = 0.0\nbottom_radius = 0.59\nheight = 1.015\nradial_segments = 4\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_0qk4s\"]\nalbedo_color = Color(0.62325, 0.495, 0.9, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_bvmel\"]\nmaterial = SubResource(\"StandardMaterial3D_0qk4s\")\ntop_radius = 0.58\nbottom_radius = 0.25\nheight = 0.895\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_438mn\"]\nalbedo_color = Color(0.933333, 0.2, 1, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_x8oip\"]\nmaterial = SubResource(\"StandardMaterial3D_438mn\")\ntop_radius = 0.4\nbottom_radius = 0.4\nheight = 0.96\nradial_segments = 7\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_hqr78\"]\nalbedo_color = Color(0.51, 0.456705, 0.3417, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_dp7xj\"]\nmaterial = SubResource(\"StandardMaterial3D_hqr78\")\ntop_radius = 0.29\nbottom_radius = 0.59\nheight = 0.65\nradial_segments = 4\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_aba40\"]\nalbedo_color = Color(0.1512, 0.36, 0.17208, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_8ctts\"]\nmaterial = SubResource(\"StandardMaterial3D_aba40\")\ntop_radius = 0.45\nbottom_radius = 0.495\nheight = 0.89\nradial_segments = 4\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_tl8dq\"]\nalbedo_color = Color(0.2279, 0.36888, 0.53, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_w2aj8\"]\nmaterial = SubResource(\"StandardMaterial3D_tl8dq\")\ntop_radius = 0.0\nbottom_radius = 0.575\nheight = 1.185\nradial_segments = 4\n\n[node name=\"Lod10Example\" type=\"Node3D\"]\n\n[node name=\"Node3D\" type=\"Node3D\" parent=\".\"]\n\n[node name=\"MeshInstance3D_LOD0\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"SphereMesh_u4ac2\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD1\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"TorusMesh_tjrnq\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD2\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"BoxMesh_xyuxq\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD3\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.118133, 0)\nmesh = SubResource(\"PrismMesh_xnm4h\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD4\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.246009, 0)\nmesh = SubResource(\"CylinderMesh_i1yhp\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD5\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"CylinderMesh_bvmel\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD6\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"CylinderMesh_x8oip\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD7\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.246009, 0)\nmesh = SubResource(\"CylinderMesh_dp7xj\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD8\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"CylinderMesh_8ctts\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD9\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.126, 0)\nmesh = SubResource(\"CylinderMesh_w2aj8\")\nskeleton = NodePath(\"../..\")\n"
  },
  {
    "path": "project/demo/assets/models/LOD5Example.tscn",
    "content": "[gd_scene load_steps=11 format=3 uid=\"uid://bn5nf4esciwex\"]\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_788j8\"]\nalbedo_color = Color(1, 0, 0, 1)\n\n[sub_resource type=\"SphereMesh\" id=\"SphereMesh_u4ac2\"]\nmaterial = SubResource(\"StandardMaterial3D_788j8\")\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_3i52q\"]\nalbedo_color = Color(1, 0.540167, 0.11, 1)\n\n[sub_resource type=\"BoxMesh\" id=\"BoxMesh_xyuxq\"]\nmaterial = SubResource(\"StandardMaterial3D_3i52q\")\nsize = Vector3(0.85, 0.85, 0.85)\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_p0ha4\"]\nalbedo_color = Color(0.48, 1, 0.497333, 1)\n\n[sub_resource type=\"PrismMesh\" id=\"PrismMesh_xnm4h\"]\nmaterial = SubResource(\"StandardMaterial3D_p0ha4\")\nsize = Vector3(1, 1, 0.855)\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_4koei\"]\nalbedo_color = Color(0.45, 0.596667, 1, 1)\n\n[sub_resource type=\"CylinderMesh\" id=\"CylinderMesh_dp7xj\"]\nmaterial = SubResource(\"StandardMaterial3D_4koei\")\ntop_radius = 0.0\nbottom_radius = 0.34\nheight = 1.54\nradial_segments = 4\n\n[sub_resource type=\"QuadMesh\" id=\"QuadMesh_264m5\"]\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_xl2nj\"]\ncull_mode = 2\nalbedo_color = Color(0.6205, 0.31, 1, 1)\n\n[node name=\"Lod5Example\" type=\"Node3D\"]\n\n[node name=\"Node3D\" type=\"Node3D\" parent=\".\"]\n\n[node name=\"MeshInstance3D_LOD0\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nmesh = SubResource(\"SphereMesh_u4ac2\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD1\" type=\"MeshInstance3D\" parent=\"Node3D\"]\nvisible = false\nmesh = SubResource(\"BoxMesh_xyuxq\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD2\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.118133, 0)\nvisible = false\nmesh = SubResource(\"PrismMesh_xnm4h\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD3\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.246009, 0)\nvisible = false\nmesh = SubResource(\"CylinderMesh_dp7xj\")\nskeleton = NodePath(\"../..\")\n\n[node name=\"MeshInstance3D_LOD4\" type=\"MeshInstance3D\" parent=\"Node3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.246009, 0)\nvisible = false\nmesh = SubResource(\"QuadMesh_264m5\")\nskeleton = NodePath(\"../..\")\nsurface_material_override/0 = SubResource(\"StandardMaterial3D_xl2nj\")\n"
  },
  {
    "path": "project/demo/assets/models/RockA.glb.import",
    "content": "[remap]\n\nimporter=\"scene\"\nimporter_version=1\ntype=\"PackedScene\"\nuid=\"uid://bxwiqxgwoh630\"\npath=\"res://.godot/imported/RockA.glb-2e416adf16cd89c5c5afc8e204027147.scn\"\n\n[deps]\n\nsource_file=\"res://demo/assets/models/RockA.glb\"\ndest_files=[\"res://.godot/imported/RockA.glb-2e416adf16cd89c5c5afc8e204027147.scn\"]\n\n[params]\n\nnodes/root_type=\"StaticBody3D\"\nnodes/root_name=\"Scene Root\"\nnodes/apply_root_scale=true\nnodes/root_scale=1.0\nnodes/import_as_skeleton_bones=false\nnodes/use_node_type_suffixes=true\nmeshes/ensure_tangents=true\nmeshes/generate_lods=true\nmeshes/create_shadow_meshes=true\nmeshes/light_baking=1\nmeshes/lightmap_texel_size=0.2\nmeshes/force_disable_compression=false\nskins/use_named_skins=true\nanimation/import=true\nanimation/fps=30\nanimation/trimming=false\nanimation/remove_immutable_tracks=true\nanimation/import_rest_as_RESET=false\nimport_script/path=\"\"\n_subresources={\n\"materials\": {\n\"@MATERIAL:0\": {\n\"use_external/enabled\": true,\n\"use_external/path\": \"res://demo/assets/materials/M_rock30.tres\"\n}\n}\n}\ngltf/naming_version=0\ngltf/embedded_image_handling=1\n"
  },
  {
    "path": "project/demo/assets/models/RockA.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://be6nrf0b8j4l0\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://bxwiqxgwoh630\" path=\"res://demo/assets/models/RockA.glb\" id=\"1_nu743\"]\n[ext_resource type=\"Material\" uid=\"uid://nbbdrx8vma80\" path=\"res://demo/assets/materials/M_rock23_tp.tres\" id=\"2_pmd5x\"]\n\n[sub_resource type=\"ConcavePolygonShape3D\" id=\"ConcavePolygonShape3D_2s34t\"]\ndata = PackedVector3Array(0.8507, -2.8222, -2.0706, 0.5747, -2.1329, -3.01, 0.0002, -3.2065, -2.7359, 2.2873, -1.7649, -0.5811, 2.4378, -0.9615, -0.323, 2.038, -0.9707, -1.2311, -0.6382, -2.68, -2.9376, -1.1067, -1.2731, -2.7048, -1.5545, -2.3633, -2.567, -0.6382, -2.68, -2.9376, -0.2162, -1.825, -3.2439, -1.1067, -1.2731, -2.7048, 0.0002, -3.2065, -2.7359, 0.0411, -3.76, -1.9518, 0.8507, -2.8222, -2.0706, 2.425, -2.0284, 0.49, 2.4024, -1.5988, 1.5279, 2.5851, -1.2056, 0.7305, 0.0831, -2.9699, 1.939, -0.0859, -2.2445, 2.6136, 0.748, -2.001, 2.5127, -2.448, -1.0454, -1.0528, -2.5272, 0.3979, -0.8764, -2.399, -0.7585, 0.2313, -1.5104, 0.6775, -2.512, -1.3227, 1.7291, -2.1514, -2.0387, 1.5996, -2.2267, 0.1427, 2.3057, -2.4478, -0.4979, 2.1368, -2.1727, -0.4143, 1.2902, -3.2951, 0.1351, -3.9012, -0.5924, 0.2299, -3.4688, 0.9248, 0.5827, -3.159, 0.0985, -1.6255, -3.1162, -1.4188, -2.0226, -2.1644, -0.379, -1.2838, -3.0035, 0.2928, -1.1067, -1.2731, -2.7048, -1.5104, 0.6775, -2.512, -1.9542, -0.4605, -2.5215, 0.748, -2.001, 2.5127, 1.4775, -1.3103, 2.8259, 1.9116, -1.7142, 2.341, 1.8789, 1.1592, -0.8303, 1.3001, 2.6786, -0.7585, 0.8831, 2.5385, -1.8483, 0.5951, -0.2972, 3.54, 1.1272, 0.2617, 2.9365, 1.3352, -0.2905, 3.0366, 0.5951, -0.2972, 3.54, 0.7324, 0.4498, 3.1493, 1.1272, 0.2617, 2.9365, -1.3976, 0.6213, 1.8313, -0.8149, 0.6856, 2.232, -1.0617, -0.1771, 2.0331, -0.7449, 2.9351, -0.0395, -0.5981, 2.6867, 0.9116, -1.33, 2.7367, 0.359, 0.9156, 3.0784, 0.1405, 0.1518, 3.0337, 0.6723, 0.0726, 3.4231, -0.5764, 0.7064, 1.456, 2.7883, 1.3708, 1.9744, 2.321, 1.1174, 1.3846, 2.616, 0.7064, 1.456, 2.7883, 0.5198, 2.1281, 2.596, 1.3708, 1.9744, 2.321, -1.6255, -3.1162, -1.4188, -1.2838, -3.0035, 0.2928, -0.6766, -3.9798, -0.6577, 2.4717, 0.9465, 0.6202, 2.4515, -0.0586, -0.1403, 2.6117, 0.0377, 0.454, 2.4515, -0.0586, -0.1403, 2.1036, 1.6103, 0.0759, 1.8789, 1.1592, -0.8303, 2.4515, -0.0586, -0.1403, 2.4717, 0.9465, 0.6202, 2.1036, 1.6103, 0.0759, 1.0451, 2.7721, 1.514, 0.2361, 2.7435, 1.8554, 0.1518, 3.0337, 0.6723, 1.0451, 2.7721, 1.514, 0.1518, 3.0337, 0.6723, 0.9156, 3.0784, 0.1405, 0.1427, 2.3057, -2.4478, -0.1394, 3.1268, -1.5759, -0.4979, 2.1368, -2.1727, 0.1518, 3.0337, 0.6723, 0.2361, 2.7435, 1.8554, -0.5981, 2.6867, 0.9116, 0.1518, 3.0337, 0.6723, -0.5981, 2.6867, 0.9116, -0.7449, 2.9351, -0.0395, -2.4827, 1.6372, -1.6179, -2.4412, 1.9847, -0.6202, -2.5272, 0.3979, -0.8764, -0.7996, 2.0136, 1.7125, -0.1737, 2.1372, 2.2437, -0.5194, 1.6162, 2.2076, -0.7996, 2.0136, 1.7125, -0.5194, 1.6162, 2.2076, -1.313, 1.5855, 1.6731, -1.6866, -0.7727, 1.7598, -1.0617, -0.1771, 2.0331, -1.0177, -1.42, 2.2188, 0.2988, 1.5533, 2.7449, 0.5198, 2.1281, 2.596, 0.7064, 1.456, 2.7883, 0.3266, 0.5073, 3.1476, 0.7324, 0.4498, 3.1493, 0.5951, -0.2972, 3.54, 1.8789, 1.1592, -0.8303, 2.1036, 1.6103, 0.0759, 1.3001, 2.6786, -0.7585, 2.1338, 1.9216, 1.0603, 1.8537, 2.1593, 1.6508, 1.6716, 2.5645, 0.6123, -0.0859, -2.2445, 2.6136, 0.5411, -1.3085, 3.348, 0.748, -2.001, 2.5127, -0.4979, 2.1368, -2.1727, -0.1394, 3.1268, -1.5759, -0.6639, 2.8841, -1.1739, 0.0726, 3.4231, -0.5764, 0.1518, 3.0337, 0.6723, -0.7449, 2.9351, -0.0395, 1.1174, 1.3846, 2.616, 1.9058, 0.4561, 2.6704, 1.3631, 0.7103, 2.7557, 1.1174, 1.3846, 2.616, 1.3708, 1.9744, 2.321, 1.9058, 0.4561, 2.6704, -2.2917, 0.7985, 0.4167, -2.1878, 1.682, 0.5464, -1.9453, 1.0221, 1.1785, -1.533, 2.1485, 1.1174, -0.7996, 2.0136, 1.7125, -1.313, 1.5855, 1.6731, -1.8821, -1.8796, 1.075, -0.8935, -2.6081, 1.7932, -1.2838, -3.0035, 0.2928, -0.3641, -1.322, 2.8122, -0.3719, -0.4418, 2.6845, 0.0592, -0.6578, 3.3401, -0.3719, -0.4418, 2.6845, -0.2183, 0.1183, 2.8781, 0.0592, -0.6578, 3.3401, -2.2917, 0.7985, 0.4167, -1.9453, 1.0221, 1.1785, -2.1903, -0.0178, 1.0505, 1.9116, -1.7142, 2.341, 1.4775, -1.3103, 2.8259, 2.1165, -0.7174, 2.6008, 1.3352, -0.2905, 3.0366, 1.3631, 0.7103, 2.7557, 1.9058, 0.4561, 2.6704, 1.3352, -0.2905, 3.0366, 1.1272, 0.2617, 2.9365, 1.3631, 0.7103, 2.7557, 0.8304, 0.2995, -2.6326, 1.3709, 0.7135, -1.8767, 0.6753, 1.5794, -2.5902, 2.4418, 0.2925, 1.9718, 2.1338, 1.9216, 1.0603, 2.4414, 1.1989, 1.2948, 2.4418, 0.2925, 1.9718, 1.8537, 2.1593, 1.6508, 2.1338, 1.9216, 1.0603, 0.8831, 2.5385, -1.8483, 1.3001, 2.6786, -0.7585, 0.5065, 3.2659, -1.3942, 2.4024, -1.5988, 1.5279, 2.6677, -0.3337, 1.0018, 2.5851, -1.2056, 0.7305, 2.4024, -1.5988, 1.5279, 2.4418, 0.2925, 1.9718, 2.6677, -0.3337, 1.0018, -1.5104, 0.6775, -2.512, -2.0387, 1.5996, -2.2267, -1.9542, -0.4605, -2.5215, -2.0037, 2.5218, -1.5743, -1.3876, 2.8143, -0.9703, -1.9473, 2.7594, -0.6554, 0.8304, 0.2995, -2.6326, 0.6753, 1.5794, -2.5902, 0.0953, 0.6217, -3.4674, -2.0226, -2.1644, -0.379, -1.8821, -1.8796, 1.075, -1.2838, -3.0035, 0.2928, -1.6866, -0.7727, 1.7598, -1.3976, 0.6213, 1.8313, -1.0617, -0.1771, 2.0331, -1.2743, 2.358, -1.6626, -1.3876, 2.8143, -0.9703, -2.0037, 2.5218, -1.5743, 0.2299, -3.4688, 0.9248, 0.6312, -2.7374, 1.3685, 0.5827, -3.159, 0.0985, 0.748, -2.001, 2.5127, 0.5411, -1.3085, 3.348, 1.4775, -1.3103, 2.8259, 1.3964, -0.6655, -2.1748, 0.8304, 0.2995, -2.6326, 0.5493, -0.7511, -3.2244, 1.7659, -2.4495, -1.2002, 1.4806, -1.8538, -1.9812, 0.8507, -2.8222, -2.0706, 2.4418, 0.2925, 1.9718, 2.6117, 0.0377, 0.454, 2.6677, -0.3337, 1.0018, 2.6117, 0.0377, 0.454, 2.4414, 1.1989, 1.2948, 2.4717, 0.9465, 0.6202, 2.6117, 0.0377, 0.454, 2.4418, 0.2925, 1.9718, 2.4414, 1.1989, 1.2948, 0.5493, -0.7511, -3.2244, 0.8304, 0.2995, -2.6326, 0.0953, 0.6217, -3.4674, 2.4515, -0.0586, -0.1403, 2.038, -0.9707, -1.2311, 2.4378, -0.9615, -0.323, 2.4515, -0.0586, -0.1403, 1.8789, 1.1592, -0.8303, 2.038, -0.9707, -1.2311, -0.1975, -0.7247, -3.4884, -0.5122, 0.3139, -3.5925, -0.7053, -0.3406, -3.1144, -1.1067, -1.2731, -2.7048, -1.9542, -0.4605, -2.5215, -1.5545, -2.3633, -2.567, -0.4143, 1.2902, -3.2951, -0.4979, 2.1368, -2.1727, -0.8466, 1.1377, -2.7759, -0.7053, -0.3406, -3.1144, -0.5122, 0.3139, -3.5925, -0.9168, 0.4069, -2.985, -2.4827, 1.6372, -1.6179, -2.5272, 0.3979, -0.8764, -2.4716, -0.4239, -1.8891, -2.4716, -0.4239, -1.8891, -2.5272, 0.3979, -0.8764, -2.448, -1.0454, -1.0528, -2.2072, -1.9264, -1.9655, -2.4716, -0.4239, -1.8891, -2.448, -1.0454, -1.0528, 0.1351, -3.9012, -0.5924, 0.5827, -3.159, 0.0985, 0.6881, -3.2916, -0.9673, -1.2838, -3.0035, 0.2928, -0.8935, -2.6081, 1.7932, -0.3984, -3.542, 1.0253, 1.3964, -0.6655, -2.1748, 1.3709, 0.7135, -1.8767, 0.8304, 0.2995, -2.6326, 1.1446, -2.3077, 1.6179, 1.8479, -2.2708, 1.4206, 1.3113, -2.5503, 0.6426, 1.2555, -2.8919, -0.6692, 1.3113, -2.5503, 0.6426, 1.8655, -2.6668, 0.0778, -0.6766, -3.9798, -0.6577, -1.2838, -3.0035, 0.2928, -0.3984, -3.542, 1.0253, 1.4806, -1.8538, -1.9812, 0.5747, -2.1329, -3.01, 0.8507, -2.8222, -2.0706, 1.3113, -2.5503, 0.6426, 1.8479, -2.2708, 1.4206, 1.8655, -2.6668, 0.0778, -0.6836, -3.6027, -2.5845, 0.0002, -3.2065, -2.7359, -0.6382, -2.68, -2.9376, 1.7659, -2.4495, -1.2002, 2.425, -2.0284, 0.49, 2.2873, -1.7649, -0.5811, 1.7659, -2.4495, -1.2002, 1.8655, -2.6668, 0.0778, 2.425, -2.0284, 0.49, 0.0831, -2.9699, 1.939, 0.2299, -3.4688, 0.9248, -0.3984, -3.542, 1.0253, -0.9168, 0.4069, -2.985, -0.8466, 1.1377, -2.7759, -1.5104, 0.6775, -2.512, -1.9473, 2.7594, -0.6554, -2.1878, 1.682, 0.5464, -2.4412, 1.9847, -0.6202, -2.1878, 1.682, 0.5464, -1.33, 2.7367, 0.359, -1.533, 2.1485, 1.1174, -2.1878, 1.682, 0.5464, -1.9473, 2.7594, -0.6554, -1.33, 2.7367, 0.359, 0.0726, 3.4231, -0.5764, -0.1394, 3.1268, -1.5759, 0.5065, 3.2659, -1.3942, 2.1036, 1.6103, 0.0759, 2.4414, 1.1989, 1.2948, 2.1338, 1.9216, 1.0603, 2.1036, 1.6103, 0.0759, 2.4717, 0.9465, 0.6202, 2.4414, 1.1989, 1.2948, 0.2361, 2.7435, 1.8554, 0.5198, 2.1281, 2.596, -0.1737, 2.1372, 2.2437, 0.0411, -3.76, -1.9518, -0.6766, -3.9798, -0.6577, 0.1351, -3.9012, -0.5924, 0.0411, -3.76, -1.9518, -0.6585, -3.9829, -1.9964, -0.6766, -3.9798, -0.6577, 1.2555, -2.8919, -0.6692, 0.8507, -2.8222, -2.0706, 0.6881, -3.2916, -0.9673, 1.4806, -1.8538, -1.9812, 2.038, -0.9707, -1.2311, 1.3964, -0.6655, -2.1748, -0.2162, -1.825, -3.2439, 0.5493, -0.7511, -3.2244, -0.1975, -0.7247, -3.4884, -0.2162, -1.825, -3.2439, 0.5747, -2.1329, -3.01, 0.5493, -0.7511, -3.2244, -1.1679, -3.5644, -2.3336, -2.2072, -1.9264, -1.9655, -1.6255, -3.1162, -1.4188, -1.1679, -3.5644, -2.3336, -1.5545, -2.3633, -2.567, -2.2072, -1.9264, -1.9655, -0.4143, 1.2902, -3.2951, -0.5122, 0.3139, -3.5925, 0.0953, 0.6217, -3.4674, 2.6677, -0.3337, 1.0018, 2.4378, -0.9615, -0.323, 2.5851, -1.2056, 0.7305, 2.4378, -0.9615, -0.323, 2.6117, 0.0377, 0.454, 2.4515, -0.0586, -0.1403, 2.4378, -0.9615, -0.323, 2.6677, -0.3337, 1.0018, 2.6117, 0.0377, 0.454, 0.6312, -2.7374, 1.3685, 0.748, -2.001, 2.5127, 1.1446, -2.3077, 1.6179, 2.4024, -1.5988, 1.5279, 1.8479, -2.2708, 1.4206, 1.9116, -1.7142, 2.341, -1.8821, -1.8796, 1.075, -2.1903, -0.0178, 1.0505, -1.6866, -0.7727, 1.7598, -1.8821, -1.8796, 1.075, -2.399, -0.7585, 0.2313, -2.1903, -0.0178, 1.0505, -1.0177, -1.42, 2.2188, -0.0859, -2.2445, 2.6136, -0.8935, -2.6081, 1.7932, -1.0177, -1.42, 2.2188, -0.3641, -1.322, 2.8122, -0.0859, -2.2445, 2.6136, -2.0037, 2.5218, -1.5743, -2.4827, 1.6372, -1.6179, -2.0387, 1.5996, -2.2267, 0.8831, 2.5385, -1.8483, 0.1427, 2.3057, -2.4478, 0.6753, 1.5794, -2.5902, -1.3227, 1.7291, -2.1514, -0.4979, 2.1368, -2.1727, -1.2743, 2.358, -1.6626, 2.1165, -0.7174, 2.6008, 1.9058, 0.4561, 2.6704, 2.4418, 0.2925, 1.9718, 0.5951, -0.2972, 3.54, 0.5411, -1.3085, 3.348, 0.0592, -0.6578, 3.3401, -1.9453, 1.0221, 1.1785, -1.313, 1.5855, 1.6731, -1.3976, 0.6213, 1.8313, -0.6639, 2.8841, -1.1739, -0.7449, 2.9351, -0.0395, -1.3876, 2.8143, -0.9703, 1.3001, 2.6786, -0.7585, 1.6716, 2.5645, 0.6123, 0.9156, 3.0784, 0.1405, 1.1174, 1.3846, 2.616, 0.7324, 0.4498, 3.1493, 0.7064, 1.456, 2.7883, 0.7324, 0.4498, 3.1493, 1.3631, 0.7103, 2.7557, 1.1272, 0.2617, 2.9365, 0.7324, 0.4498, 3.1493, 1.1174, 1.3846, 2.616, 1.3631, 0.7103, 2.7557, 1.3708, 1.9744, 2.321, 1.0451, 2.7721, 1.514, 1.8537, 2.1593, 1.6508, -0.8149, 0.6856, 2.232, 0.3266, 0.5073, 3.1476, -0.2183, 0.1183, 2.8781, 0.3266, 0.5073, 3.1476, -0.5194, 1.6162, 2.2076, 0.2988, 1.5533, 2.7449, 0.3266, 0.5073, 3.1476, -0.8149, 0.6856, 2.232, -0.5194, 1.6162, 2.2076, -0.6585, -3.9829, -1.9964, 0.0002, -3.2065, -2.7359, -0.6836, -3.6027, -2.5845, -0.6585, -3.9829, -1.9964, 0.0411, -3.76, -1.9518, 0.0002, -3.2065, -2.7359, 0.8507, -2.8222, -2.0706, 1.8655, -2.6668, 0.0778, 1.7659, -2.4495, -1.2002, 0.8507, -2.8222, -2.0706, 1.2555, -2.8919, -0.6692, 1.8655, -2.6668, 0.0778, 2.038, -0.9707, -1.2311, 1.7659, -2.4495, -1.2002, 2.2873, -1.7649, -0.5811, 2.038, -0.9707, -1.2311, 1.4806, -1.8538, -1.9812, 1.7659, -2.4495, -1.2002, -1.5545, -2.3633, -2.567, -0.6836, -3.6027, -2.5845, -0.6382, -2.68, -2.9376, -1.5545, -2.3633, -2.567, -1.1679, -3.5644, -2.3336, -0.6836, -3.6027, -2.5845, 2.5851, -1.2056, 0.7305, 2.2873, -1.7649, -0.5811, 2.425, -2.0284, 0.49, 2.5851, -1.2056, 0.7305, 2.4378, -0.9615, -0.323, 2.2873, -1.7649, -0.5811, 0.748, -2.001, 2.5127, 0.2299, -3.4688, 0.9248, 0.0831, -2.9699, 1.939, 0.748, -2.001, 2.5127, 0.6312, -2.7374, 1.3685, 0.2299, -3.4688, 0.9248, -2.399, -0.7585, 0.2313, -2.0226, -2.1644, -0.379, -2.448, -1.0454, -1.0528, -2.399, -0.7585, 0.2313, -1.8821, -1.8796, 1.075, -2.0226, -2.1644, -0.379, -0.3641, -1.322, 2.8122, -1.0617, -0.1771, 2.0331, -0.3719, -0.4418, 2.6845, -0.3641, -1.322, 2.8122, -1.0177, -1.42, 2.2188, -1.0617, -0.1771, 2.0331, -2.2917, 0.7985, 0.4167, -2.4412, 1.9847, -0.6202, -2.1878, 1.682, 0.5464, -2.2917, 0.7985, 0.4167, -2.5272, 0.3979, -0.8764, -2.4412, 1.9847, -0.6202, 1.9058, 0.4561, 2.6704, 1.4775, -1.3103, 2.8259, 1.3352, -0.2905, 3.0366, 1.9058, 0.4561, 2.6704, 2.1165, -0.7174, 2.6008, 1.4775, -1.3103, 2.8259, -1.313, 1.5855, 1.6731, -2.1878, 1.682, 0.5464, -1.533, 2.1485, 1.1174, -1.313, 1.5855, 1.6731, -1.9453, 1.0221, 1.1785, -2.1878, 1.682, 0.5464, -0.7449, 2.9351, -0.0395, -0.1394, 3.1268, -1.5759, 0.0726, 3.4231, -0.5764, -0.7449, 2.9351, -0.0395, -0.6639, 2.8841, -1.1739, -0.1394, 3.1268, -1.5759, 1.6716, 2.5645, 0.6123, 2.1036, 1.6103, 0.0759, 2.1338, 1.9216, 1.0603, 1.6716, 2.5645, 0.6123, 1.3001, 2.6786, -0.7585, 2.1036, 1.6103, 0.0759, 1.0451, 2.7721, 1.514, 0.5198, 2.1281, 2.596, 0.2361, 2.7435, 1.8554, 1.0451, 2.7721, 1.514, 1.3708, 1.9744, 2.321, 0.5198, 2.1281, 2.596, -0.8149, 0.6856, 2.232, -0.3719, -0.4418, 2.6845, -1.0617, -0.1771, 2.0331, -0.8149, 0.6856, 2.232, -0.2183, 0.1183, 2.8781, -0.3719, -0.4418, 2.6845, -0.5981, 2.6867, 0.9116, -1.533, 2.1485, 1.1174, -1.33, 2.7367, 0.359, -0.5981, 2.6867, 0.9116, -0.7996, 2.0136, 1.7125, -1.533, 2.1485, 1.1174, -0.3984, -3.542, 1.0253, 0.1351, -3.9012, -0.5924, -0.6766, -3.9798, -0.6577, -0.3984, -3.542, 1.0253, 0.2299, -3.4688, 0.9248, 0.1351, -3.9012, -0.5924, -0.6382, -2.68, -2.9376, 0.5747, -2.1329, -3.01, -0.2162, -1.825, -3.2439, -0.6382, -2.68, -2.9376, 0.0002, -3.2065, -2.7359, 0.5747, -2.1329, -3.01, -2.448, -1.0454, -1.0528, -1.6255, -3.1162, -1.4188, -2.2072, -1.9264, -1.9655, -2.448, -1.0454, -1.0528, -2.0226, -2.1644, -0.379, -1.6255, -3.1162, -1.4188, -0.9168, 0.4069, -2.985, -1.1067, -1.2731, -2.7048, -0.7053, -0.3406, -3.1144, -0.9168, 0.4069, -2.985, -1.5104, 0.6775, -2.512, -1.1067, -1.2731, -2.7048, -0.8466, 1.1377, -2.7759, -0.5122, 0.3139, -3.5925, -0.4143, 1.2902, -3.2951, -0.8466, 1.1377, -2.7759, -0.9168, 0.4069, -2.985, -0.5122, 0.3139, -3.5925, 2.425, -2.0284, 0.49, 1.8479, -2.2708, 1.4206, 2.4024, -1.5988, 1.5279, 2.425, -2.0284, 0.49, 1.8655, -2.6668, 0.0778, 1.8479, -2.2708, 1.4206, 0.0831, -2.9699, 1.939, -0.8935, -2.6081, 1.7932, -0.0859, -2.2445, 2.6136, 0.0831, -2.9699, 1.939, -0.3984, -3.542, 1.0253, -0.8935, -2.6081, 1.7932, -1.9473, 2.7594, -0.6554, -2.4827, 1.6372, -1.6179, -2.0037, 2.5218, -1.5743, -1.9473, 2.7594, -0.6554, -2.4412, 1.9847, -0.6202, -2.4827, 1.6372, -1.6179, 0.5065, 3.2659, -1.3942, 0.1427, 2.3057, -2.4478, 0.8831, 2.5385, -1.8483, 0.5065, 3.2659, -1.3942, -0.1394, 3.1268, -1.5759, 0.1427, 2.3057, -2.4478, -1.5104, 0.6775, -2.512, -0.4979, 2.1368, -2.1727, -1.3227, 1.7291, -2.1514, -1.5104, 0.6775, -2.512, -0.8466, 1.1377, -2.7759, -0.4979, 2.1368, -2.1727, 1.3352, -0.2905, 3.0366, 0.5411, -1.3085, 3.348, 0.5951, -0.2972, 3.54, 1.3352, -0.2905, 3.0366, 1.4775, -1.3103, 2.8259, 0.5411, -1.3085, 3.348, -1.33, 2.7367, 0.359, -1.3876, 2.8143, -0.9703, -0.7449, 2.9351, -0.0395, -1.33, 2.7367, 0.359, -1.9473, 2.7594, -0.6554, -1.3876, 2.8143, -0.9703, 0.0726, 3.4231, -0.5764, 1.3001, 2.6786, -0.7585, 0.9156, 3.0784, 0.1405, 0.0726, 3.4231, -0.5764, 0.5065, 3.2659, -1.3942, 1.3001, 2.6786, -0.7585, -0.1737, 2.1372, 2.2437, 0.2988, 1.5533, 2.7449, -0.5194, 1.6162, 2.2076, -0.1737, 2.1372, 2.2437, 0.5198, 2.1281, 2.596, 0.2988, 1.5533, 2.7449, 0.2361, 2.7435, 1.8554, -0.7996, 2.0136, 1.7125, -0.5981, 2.6867, 0.9116, 0.2361, 2.7435, 1.8554, -0.1737, 2.1372, 2.2437, -0.7996, 2.0136, 1.7125, 1.8537, 2.1593, 1.6508, 0.9156, 3.0784, 0.1405, 1.6716, 2.5645, 0.6123, 1.8537, 2.1593, 1.6508, 1.0451, 2.7721, 1.514, 0.9156, 3.0784, 0.1405, -0.5194, 1.6162, 2.2076, -1.3976, 0.6213, 1.8313, -1.313, 1.5855, 1.6731, -0.5194, 1.6162, 2.2076, -0.8149, 0.6856, 2.232, -1.3976, 0.6213, 1.8313, 0.3266, 0.5073, 3.1476, 0.7064, 1.456, 2.7883, 0.7324, 0.4498, 3.1493, 0.3266, 0.5073, 3.1476, 0.2988, 1.5533, 2.7449, 0.7064, 1.456, 2.7883, -0.2183, 0.1183, 2.8781, 0.5951, -0.2972, 3.54, 0.0592, -0.6578, 3.3401, -0.2183, 0.1183, 2.8781, 0.3266, 0.5073, 3.1476, 0.5951, -0.2972, 3.54, 1.3708, 1.9744, 2.321, 2.4418, 0.2925, 1.9718, 1.9058, 0.4561, 2.6704, 1.3708, 1.9744, 2.321, 1.8537, 2.1593, 1.6508, 2.4418, 0.2925, 1.9718, 1.3709, 0.7135, -1.8767, 0.8831, 2.5385, -1.8483, 0.6753, 1.5794, -2.5902, 1.3709, 0.7135, -1.8767, 1.8789, 1.1592, -0.8303, 0.8831, 2.5385, -1.8483, -1.2743, 2.358, -1.6626, -0.6639, 2.8841, -1.1739, -1.3876, 2.8143, -0.9703, -1.2743, 2.358, -1.6626, -0.4979, 2.1368, -2.1727, -0.6639, 2.8841, -1.1739, -1.3227, 1.7291, -2.1514, -2.0037, 2.5218, -1.5743, -2.0387, 1.5996, -2.2267, -1.3227, 1.7291, -2.1514, -1.2743, 2.358, -1.6626, -2.0037, 2.5218, -1.5743, -2.5272, 0.3979, -0.8764, -2.1903, -0.0178, 1.0505, -2.399, -0.7585, 0.2313, -2.5272, 0.3979, -0.8764, -2.2917, 0.7985, 0.4167, -2.1903, -0.0178, 1.0505, -1.9453, 1.0221, 1.1785, -1.6866, -0.7727, 1.7598, -2.1903, -0.0178, 1.0505, -1.9453, 1.0221, 1.1785, -1.3976, 0.6213, 1.8313, -1.6866, -0.7727, 1.7598, -0.0859, -2.2445, 2.6136, 0.0592, -0.6578, 3.3401, 0.5411, -1.3085, 3.348, -0.0859, -2.2445, 2.6136, -0.3641, -1.322, 2.8122, 0.0592, -0.6578, 3.3401, 2.4024, -1.5988, 1.5279, 2.1165, -0.7174, 2.6008, 2.4418, 0.2925, 1.9718, 2.4024, -1.5988, 1.5279, 1.9116, -1.7142, 2.341, 2.1165, -0.7174, 2.6008, 0.6753, 1.5794, -2.5902, -0.4143, 1.2902, -3.2951, 0.0953, 0.6217, -3.4674, 0.6753, 1.5794, -2.5902, 0.1427, 2.3057, -2.4478, -0.4143, 1.2902, -3.2951, -2.0387, 1.5996, -2.2267, -2.4716, -0.4239, -1.8891, -1.9542, -0.4605, -2.5215, -2.0387, 1.5996, -2.2267, -2.4827, 1.6372, -1.6179, -2.4716, -0.4239, -1.8891, -1.8821, -1.8796, 1.075, -1.0177, -1.42, 2.2188, -0.8935, -2.6081, 1.7932, -1.8821, -1.8796, 1.075, -1.6866, -0.7727, 1.7598, -1.0177, -1.42, 2.2188, 1.1446, -2.3077, 1.6179, 1.9116, -1.7142, 2.341, 1.8479, -2.2708, 1.4206, 1.1446, -2.3077, 1.6179, 0.748, -2.001, 2.5127, 1.9116, -1.7142, 2.341, 0.6312, -2.7374, 1.3685, 1.3113, -2.5503, 0.6426, 0.5827, -3.159, 0.0985, 0.6312, -2.7374, 1.3685, 1.1446, -2.3077, 1.6179, 1.3113, -2.5503, 0.6426, 1.8789, 1.1592, -0.8303, 1.3964, -0.6655, -2.1748, 2.038, -0.9707, -1.2311, 1.8789, 1.1592, -0.8303, 1.3709, 0.7135, -1.8767, 1.3964, -0.6655, -2.1748, -0.1975, -0.7247, -3.4884, 0.0953, 0.6217, -3.4674, -0.5122, 0.3139, -3.5925, -0.1975, -0.7247, -3.4884, 0.5493, -0.7511, -3.2244, 0.0953, 0.6217, -3.4674, -0.2162, -1.825, -3.2439, -0.7053, -0.3406, -3.1144, -1.1067, -1.2731, -2.7048, -0.2162, -1.825, -3.2439, -0.1975, -0.7247, -3.4884, -0.7053, -0.3406, -3.1144, -1.9542, -0.4605, -2.5215, -2.2072, -1.9264, -1.9655, -1.5545, -2.3633, -2.567, -1.9542, -0.4605, -2.5215, -2.4716, -0.4239, -1.8891, -2.2072, -1.9264, -1.9655, -1.1679, -3.5644, -2.3336, -0.6766, -3.9798, -0.6577, -0.6585, -3.9829, -1.9964, -1.1679, -3.5644, -2.3336, -1.6255, -3.1162, -1.4188, -0.6766, -3.9798, -0.6577, 1.4806, -1.8538, -1.9812, 0.5493, -0.7511, -3.2244, 0.5747, -2.1329, -3.01, 1.4806, -1.8538, -1.9812, 1.3964, -0.6655, -2.1748, 0.5493, -0.7511, -3.2244, 0.0411, -3.76, -1.9518, 0.6881, -3.2916, -0.9673, 0.8507, -2.8222, -2.0706, 0.0411, -3.76, -1.9518, 0.1351, -3.9012, -0.5924, 0.6881, -3.2916, -0.9673, 0.5827, -3.159, 0.0985, 1.2555, -2.8919, -0.6692, 0.6881, -3.2916, -0.9673, 0.5827, -3.159, 0.0985, 1.3113, -2.5503, 0.6426, 1.2555, -2.8919, -0.6692, -0.6836, -3.6027, -2.5845, -1.1679, -3.5644, -2.3336, -0.6585, -3.9829, -1.9964)\n\n[node name=\"RockA\" instance=ExtResource(\"1_nu743\")]\ncollision_mask = 3\n\n[node name=\"Rock1\" parent=\".\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"2_pmd5x\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\".\" index=\"1\"]\nshape = SubResource(\"ConcavePolygonShape3D_2s34t\")\n"
  },
  {
    "path": "project/demo/assets/models/RockB.glb.import",
    "content": "[remap]\n\nimporter=\"scene\"\nimporter_version=1\ntype=\"PackedScene\"\nuid=\"uid://nta3sef6c2el\"\npath=\"res://.godot/imported/RockB.glb-d0df90244ab14da61106a961f4faa07f.scn\"\n\n[deps]\n\nsource_file=\"res://demo/assets/models/RockB.glb\"\ndest_files=[\"res://.godot/imported/RockB.glb-d0df90244ab14da61106a961f4faa07f.scn\"]\n\n[params]\n\nnodes/root_type=\"StaticBody3D\"\nnodes/root_name=\"Scene Root\"\nnodes/apply_root_scale=true\nnodes/root_scale=1.0\nnodes/import_as_skeleton_bones=false\nnodes/use_node_type_suffixes=true\nmeshes/ensure_tangents=true\nmeshes/generate_lods=true\nmeshes/create_shadow_meshes=true\nmeshes/light_baking=1\nmeshes/lightmap_texel_size=0.2\nmeshes/force_disable_compression=false\nskins/use_named_skins=true\nanimation/import=true\nanimation/fps=30\nanimation/trimming=false\nanimation/remove_immutable_tracks=true\nanimation/import_rest_as_RESET=false\nimport_script/path=\"\"\n_subresources={\n\"materials\": {\n\"@MATERIAL:0\": {\n\"use_external/enabled\": true,\n\"use_external/path\": \"res://demo/assets/materials/M_rock30.tres\"\n}\n}\n}\ngltf/naming_version=0\ngltf/embedded_image_handling=1\n"
  },
  {
    "path": "project/demo/assets/models/RockB.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://bwvtgwartxt0g\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://nta3sef6c2el\" path=\"res://demo/assets/models/RockB.glb\" id=\"1_2nhli\"]\n[ext_resource type=\"Material\" uid=\"uid://nbbdrx8vma80\" path=\"res://demo/assets/materials/M_rock23_tp.tres\" id=\"2_dbm2k\"]\n\n[sub_resource type=\"ConcavePolygonShape3D\" id=\"ConcavePolygonShape3D_wfrmp\"]\ndata = PackedVector3Array(0.4584, -3.889, 0.8266, 0.6021, -4.1961, -0.0392, 0.169, -4.9369, 0.2441, 2.1498, -1.9824, 1.4097, 2.1074, -0.9551, 0.9434, 2.3387, -2.1373, 0.6494, -0.4293, -5.0774, 0.0003, -0.5108, -3.8408, -1.1476, -1.1208, -4.5065, 0.1953, 0.0671, -5.0216, -0.4515, 0.4756, -4.5162, -0.6549, 0.1398, -4.5397, -0.9338, 0.169, -4.9369, 0.2441, -0.0922, -4.5871, 1.3087, 0.4584, -3.889, 0.8266, 2.1498, -1.9824, 1.4097, 1.9393, -0.8033, 1.6608, 2.1074, -0.9551, 0.9434, -1.2288, -2.368, 2.4204, -1.4963, -0.9921, 2.5267, -0.7814, -0.8418, 2.7775, -2.6648, -1.8501, 0.307, -2.907, 0.041, -0.3088, -2.5202, -0.0318, 0.8043, -1.0077, -1.3716, -2.1657, -0.7692, -0.6389, -2.6654, -1.6842, -0.4729, -2.3712, 2.1195, -1.771, -1.5705, 2.5987, -0.8743, -1.1872, 1.9584, -0.8816, -2.0923, -0.2829, -3.5923, 2.1108, -0.2939, -2.4569, 2.4482, 0.5442, -2.0915, 2.2664, -1.1208, -4.5065, 0.1953, -2.179, -3.4152, 0.4259, -1.8849, -3.8097, 1.1593, -0.5108, -3.8408, -1.1476, -0.2695, -2.508, -2.0432, -1.1054, -2.8992, -1.0824, 2.1726, -2.8941, -0.5813, 1.87, -2.8735, -1.1419, 1.5863, -3.2514, -0.7541, 0.7315, 0.8274, 2.7196, 1.1136, 1.849, 2.4998, 1.409, 0.9631, 2.386, -0.4271, 2.5556, 2.4897, 0.5261, 4.2194, 1.9538, 0.5038, 3.0339, 2.4087, -1.9764, 3.1849, 0.2685, -1.0116, 3.5059, 0.8103, -1.5979, 2.7577, 1.2075, -0.9393, 2.872, -1.8539, -1.2485, 3.6723, -1.0264, -1.7216, 2.2099, -1.5001, 1.1827, 2.8613, -1.4341, 0.9077, 3.2903, -1.4616, 0.9177, 2.8679, -1.5387, 0.8594, 5.2909, 1.0912, 0.6378, 5.6752, 0.4017, 1.3249, 5.3073, 0.5102, -1.1208, -4.5065, 0.1953, -1.8849, -3.8097, 1.1593, -0.8729, -4.7982, 1.121, 2.4617, 0.7259, 0.0288, 2.5473, 1.387, -0.0726, 2.6196, 0.7185, -0.2944, 0.9445, 5.5277, -0.3666, 0.5746, 5.6649, -0.3989, 0.6884, 5.4332, -0.7011, 1.7744, 4.3299, -0.4393, 1.3226, 4.3217, -0.9655, 2.0312, 2.7158, -1.0135, 0.9877, 0.4313, -2.4389, 0.7444, 0.8429, -2.2856, 0.635, 0.4247, -2.5749, -0.1392, 4.5027, -1.2571, -0.2125, 5.045, -0.6732, -1.2485, 3.6723, -1.0264, -0.1392, 4.5027, -1.2571, -1.2485, 3.6723, -1.0264, -0.9393, 2.872, -1.8539, -1.7965, 0.899, -2.2295, -1.7216, 2.2099, -1.5001, -2.3834, 0.7782, -1.5631, -0.791, 4.3118, -0.0929, -0.2232, 5.1794, 0.1938, -0.5404, 4.4098, 0.4328, -1.3695, 3.5538, -0.0189, -1.0116, 3.5059, 0.8103, -1.9764, 3.1849, 0.2685, -2.5202, -0.0318, 0.8043, -2.1461, 1.5782, 1.2673, -2.0758, -0.0137, 1.8949, -0.5404, 4.4098, 0.4328, -0.2232, 5.1794, 0.1938, -0.0735, 4.938, 0.8308, -1.0116, 3.5059, 0.8103, -0.2982, 4.0929, 1.4648, -0.878, 3.135, 1.7811, 2.6003, 1.0473, -1.0133, 2.4031, 2.6779, -0.4122, 2.0312, 2.7158, -1.0135, 2.4031, 2.6779, -0.4122, 1.7744, 4.3299, -0.4393, 2.0312, 2.7158, -1.0135, -1.0159, 0.9756, 2.6776, -0.4271, 2.5556, 2.4897, -0.2195, 1.1266, 2.8293, -0.0823, 1.3076, -2.4751, 0.0456, 1.662, -2.2329, -0.2095, 1.8203, -2.3278, 0.1709, 2.9305, -1.8154, 0.1823, 3.4347, -1.7089, -0.0988, 3.0024, -1.9036, -2.907, 0.041, -0.3088, -2.575, 1.9843, 0.2578, -2.5202, -0.0318, 0.8043, -2.5502, 1.5321, -0.8246, -1.9974, 2.3952, -0.8364, -2.286, 2.7057, -0.3882, -1.9974, 2.3952, -0.8364, -1.7496, 3.0465, -0.6307, -2.286, 2.7057, -0.3882, 0.8594, 5.2909, 1.0912, 1.3249, 5.3073, 0.5102, 1.6148, 4.6612, 1.1746, -1.6176, 1.4128, 2.2491, -1.7058, 2.0492, 1.8707, -1.3001, 2.2705, 2.1959, -1.5979, 2.7577, 1.2075, -1.0116, 3.5059, 0.8103, -0.878, 3.135, 1.7811, -0.7261, 0.8081, -2.8158, -0.8202, 1.5078, -2.6285, -1.246, 0.719, -2.7492, 1.409, 0.9631, 2.386, 1.1136, 1.849, 2.4998, 1.6219, 2.267, 2.1861, 0.5038, 3.0339, 2.4087, 0.5261, 4.2194, 1.9538, 1.2634, 3.7734, 1.9919, 2.6003, 1.0473, -1.0133, 2.0312, 2.7158, -1.0135, 2.1515, 0.9506, -1.6595, 2.2544, 3.8319, 0.7934, 2.1174, 4.2399, 0.396, 2.3685, 3.5019, 0.414, 1.4453, 1.3219, -1.8647, 1.4442, 1.9, -1.6223, 1.2471, 1.631, -1.7857, 1.9293, 0.908, 1.6849, 2.1901, 2.3652, 1.3543, 2.1038, 1.0245, 0.9114, -1.0077, -1.3716, -2.1657, -1.6842, -0.4729, -2.3712, -1.6361, -1.5711, -1.5378, -1.7965, 0.899, -2.2295, -0.9393, 2.872, -1.8539, -1.7216, 2.2099, -1.5001, 1.1377, -0.8428, -2.7439, 0.7304, -0.9533, -2.8604, 0.9457, -1.6804, -2.7624, -2.6648, -1.8501, 0.307, -2.5202, -0.0318, 0.8043, -2.3406, -2.1709, 1.3672, -2.5202, -0.0318, 0.8043, -2.575, 1.9843, 0.2578, -2.1461, 1.5782, 1.2673, -1.6842, -0.4729, -2.3712, -2.3999, -0.6295, -1.5513, -1.6361, -1.5711, -1.5378, -0.2939, -2.4569, 2.4482, 0.256, -0.9397, 2.7009, 0.5442, -2.0915, 2.2664, -0.2195, 1.1266, 2.8293, -0.4271, 2.5556, 2.4897, 0.5038, 3.0339, 2.4087, 2.4385, -0.7713, 0.0524, 2.634, -0.7691, -0.3318, 2.5652, -1.538, -0.1083, 1.8065, -2.8661, 1.2715, 1.9869, -3.1191, 0.2426, 1.2817, -3.3103, 0.5621, 2.1038, 1.0245, 0.9114, 2.1901, 2.3652, 1.3543, 2.3678, 2.1513, 0.6399, 0.9342, -3.6502, -1.2505, 0.9178, -3.3811, -1.9588, 0.3423, -3.927, -1.4233, 0.9342, -3.6502, -1.2505, 1.4243, -3.0391, -1.5379, 0.9178, -3.3811, -1.9588, 2.1195, -1.771, -1.5705, 1.9584, -0.8816, -2.0923, 1.5426, -2.0265, -2.2438, -0.5108, -3.8408, -1.1476, -1.1054, -2.8992, -1.0824, -1.1208, -4.5065, 0.1953, 0.3194, -2.8417, -2.3407, 0.0974, -1.7758, -2.6186, -0.2695, -2.508, -2.0432, -0.5108, -3.8408, -1.1476, 0.3194, -2.8417, -2.3407, -0.2695, -2.508, -2.0432, -2.5202, -0.0318, 0.8043, -2.0758, -0.0137, 1.8949, -2.3406, -2.1709, 1.3672, -1.9059, -2.3487, -0.7483, -2.5883, -1.4508, -0.7517, -2.3688, -2.5627, -0.2622, -1.809, -3.263, -0.2744, -1.9059, -2.3487, -0.7483, -2.3688, -2.5627, -0.2622, -0.0922, -4.5871, 1.3087, 0.4197, -3.4404, 1.5487, 0.4584, -3.889, 0.8266, -1.7887, -3.0231, 1.9828, -1.4963, -0.9921, 2.5267, -1.2288, -2.368, 2.4204, 2.5358, -1.9021, -0.8254, 2.5987, -0.8743, -1.1872, 2.1195, -1.771, -1.5705, 0.256, -0.9397, 2.7009, 1.2883, -0.8696, 2.2885, 0.5442, -2.0915, 2.2664, 1.2817, -3.3103, 0.5621, 0.9704, -3.0065, 1.4497, 1.8065, -2.8661, 1.2715, -1.1122, -3.8993, 1.9946, -1.7887, -3.0231, 1.9828, -1.2288, -2.368, 2.4204, 1.9869, -3.1191, 0.2426, 1.267, -3.4909, -0.189, 1.2817, -3.3103, 0.5621, 0.5442, -2.0915, 2.2664, 1.2883, -0.8696, 2.2885, 1.6056, -1.9424, 1.9813, -0.4293, -5.0774, 0.0003, 0.169, -4.9369, 0.2441, 0.0671, -5.0216, -0.4515, 2.1498, -1.9824, 1.4097, 1.8065, -2.8661, 1.2715, 1.6056, -1.9424, 1.9813, -2.179, -3.4152, 0.4259, -2.3688, -2.5627, -0.2622, -2.6648, -1.8501, 0.307, 1.87, -2.8735, -1.1419, 2.1195, -1.771, -1.5705, 1.4243, -3.0391, -1.5379, -1.7058, 2.0492, 1.8707, -2.1461, 1.5782, 1.2673, -1.5979, 2.7577, 1.2075, 1.2471, 1.631, -1.7857, 0.0456, 1.662, -2.2329, 0.7444, 0.8429, -2.2856, 0.0456, 1.662, -2.2329, 0.9177, 2.8679, -1.5387, 0.1709, 2.9305, -1.8154, 0.0456, 1.662, -2.2329, 1.2471, 1.631, -1.7857, 0.9177, 2.8679, -1.5387, 2.5473, 1.387, -0.0726, 2.3685, 3.5019, 0.414, 2.4031, 2.6779, -0.4122, 2.5473, 1.387, -0.0726, 2.3678, 2.1513, 0.6399, 2.3685, 3.5019, 0.414, 0.5746, 5.6649, -0.3989, -0.2232, 5.1794, 0.1938, -0.2125, 5.045, -0.6732, 0.5746, 5.6649, -0.3989, 0.6378, 5.6752, 0.4017, -0.2232, 5.1794, 0.1938, -0.0922, -4.5871, 1.3087, -1.1122, -3.8993, 1.9946, -0.2829, -3.5923, 2.1108, -0.0922, -4.5871, 1.3087, -0.8729, -4.7982, 1.121, -1.1122, -3.8993, 1.9946, 0.9704, -3.0065, 1.4497, 0.4197, -3.4404, 1.5487, 0.5442, -2.0915, 2.2664, 2.3387, -2.1373, 0.6494, 2.1726, -2.8941, -0.5813, 1.9869, -3.1191, 0.2426, 2.1726, -2.8941, -0.5813, 2.5652, -1.538, -0.1083, 2.5358, -1.9021, -0.8254, 2.1726, -2.8941, -0.5813, 2.3387, -2.1373, 0.6494, 2.5652, -1.538, -0.1083, 0.9342, -3.6502, -1.2505, 1.267, -3.4909, -0.189, 1.5863, -3.2514, -0.7541, 1.267, -3.4909, -0.189, 0.4756, -4.5162, -0.6549, 0.6021, -4.1961, -0.0392, 1.267, -3.4909, -0.189, 0.9342, -3.6502, -1.2505, 0.4756, -4.5162, -0.6549, -1.8849, -3.8097, 1.1593, -2.3406, -2.1709, 1.3672, -1.7887, -3.0231, 1.9828, -0.5108, -3.8408, -1.1476, 0.1398, -4.5397, -0.9338, 0.3423, -3.927, -1.4233, -1.1054, -2.8992, -1.0824, -1.6361, -1.5711, -1.5378, -1.9059, -2.3487, -0.7483, 0.9178, -3.3811, -1.9588, 0.9457, -1.6804, -2.7624, 0.3194, -2.8417, -2.3407, 0.9178, -3.3811, -1.9588, 1.5426, -2.0265, -2.2438, 0.9457, -1.6804, -2.7624, 2.4617, 0.7259, 0.0288, 2.1074, -0.9551, 0.9434, 2.1038, 1.0245, 0.9114, 2.4617, 0.7259, 0.0288, 2.4385, -0.7713, 0.0524, 2.1074, -0.9551, 0.9434, 2.6196, 0.7185, -0.2944, 2.5987, -0.8743, -1.1872, 2.634, -0.7691, -0.3318, 2.6196, 0.7185, -0.2944, 2.6003, 1.0473, -1.0133, 2.5987, -0.8743, -1.1872, 0.256, -0.9397, 2.7009, -0.2195, 1.1266, 2.8293, 0.7315, 0.8274, 2.7196, 0.256, -0.9397, 2.7009, -0.7814, -0.8418, 2.7775, -0.2195, 1.1266, 2.8293, 1.9393, -0.8033, 1.6608, 1.409, 0.9631, 2.386, 1.9293, 0.908, 1.6849, 1.9393, -0.8033, 1.6608, 1.2883, -0.8696, 2.2885, 1.409, 0.9631, 2.386, -2.0758, -0.0137, 1.8949, -1.0159, 0.9756, 2.6776, -1.4963, -0.9921, 2.5267, -2.0758, -0.0137, 1.8949, -1.6176, 1.4128, 2.2491, -1.0159, 0.9756, 2.6776, -1.246, 0.719, -2.7492, -1.7965, 0.899, -2.2295, -1.6842, -0.4729, -2.3712, -2.5502, 1.5321, -0.8246, -2.3999, -0.6295, -1.5513, -2.3834, 0.7782, -1.5631, -2.3999, -0.6295, -1.5513, -2.907, 0.041, -0.3088, -2.5883, -1.4508, -0.7517, -2.3999, -0.6295, -1.5513, -2.5502, 1.5321, -0.8246, -2.907, 0.041, -0.3088, 1.1377, -0.8428, -2.7439, 1.4453, 1.3219, -1.8647, 0.9877, 0.4313, -2.4389, 1.4453, 1.3219, -1.8647, 1.9584, -0.8816, -2.0923, 2.1515, 0.9506, -1.6595, 1.4453, 1.3219, -1.8647, 1.1377, -0.8428, -2.7439, 1.9584, -0.8816, -2.0923, -0.0823, 1.3076, -2.4751, 0.7304, -0.9533, -2.8604, 0.635, 0.4247, -2.5749, 0.7304, -0.9533, -2.8604, -0.7692, -0.6389, -2.6654, 0.0974, -1.7758, -2.6186, -0.7692, -0.6389, -2.6654, -0.0823, 1.3076, -2.4751, -0.7261, 0.8081, -2.8158, 0.7304, -0.9533, -2.8604, -0.0823, 1.3076, -2.4751, -0.7692, -0.6389, -2.6654, 1.2634, 3.7734, 1.9919, 2.1901, 2.3652, 1.3543, 1.6219, 2.267, 2.1861, 2.1901, 2.3652, 1.3543, 1.6148, 4.6612, 1.1746, 2.2544, 3.8319, 0.7934, 2.1901, 2.3652, 1.3543, 1.2634, 3.7734, 1.9919, 1.6148, 4.6612, 1.1746, -1.3001, 2.2705, 2.1959, -0.878, 3.135, 1.7811, -0.4271, 2.5556, 2.4897, -1.9764, 3.1849, 0.2685, -2.575, 1.9843, 0.2578, -2.286, 2.7057, -0.3882, -0.2095, 1.8203, -2.3278, -0.9393, 2.872, -1.8539, -0.8202, 1.5078, -2.6285, -0.2095, 1.8203, -2.3278, -0.0988, 3.0024, -1.9036, -0.9393, 2.872, -1.8539, 1.4442, 1.9, -1.6223, 2.0312, 2.7158, -1.0135, 1.1827, 2.8613, -1.4341, -0.0735, 4.938, 0.8308, 0.5261, 4.2194, 1.9538, -0.2982, 4.0929, 1.4648, -0.0735, 4.938, 0.8308, 0.8594, 5.2909, 1.0912, 0.5261, 4.2194, 1.9538, 1.3249, 5.3073, 0.5102, 1.7744, 4.3299, -0.4393, 2.1174, 4.2399, 0.396, 1.3249, 5.3073, 0.5102, 0.9445, 5.5277, -0.3666, 1.7744, 4.3299, -0.4393, -1.7496, 3.0465, -0.6307, -0.791, 4.3118, -0.0929, -1.3695, 3.5538, -0.0189, -1.7496, 3.0465, -0.6307, -1.2485, 3.6723, -1.0264, -0.791, 4.3118, -0.0929, 0.9077, 3.2903, -1.4616, -0.1392, 4.5027, -1.2571, 0.1823, 3.4347, -1.7089, -0.1392, 4.5027, -1.2571, 1.3226, 4.3217, -0.9655, 0.6884, 5.4332, -0.7011, -0.1392, 4.5027, -1.2571, 0.9077, 3.2903, -1.4616, 1.3226, 4.3217, -0.9655, -0.8729, -4.7982, 1.121, 0.169, -4.9369, 0.2441, -0.4293, -5.0774, 0.0003, -0.8729, -4.7982, 1.121, -0.0922, -4.5871, 1.3087, 0.169, -4.9369, 0.2441, 2.3387, -2.1373, 0.6494, 1.8065, -2.8661, 1.2715, 2.1498, -1.9824, 1.4097, 2.3387, -2.1373, 0.6494, 1.9869, -3.1191, 0.2426, 1.8065, -2.8661, 1.2715, 0.9342, -3.6502, -1.2505, 1.87, -2.8735, -1.1419, 1.4243, -3.0391, -1.5379, 0.9342, -3.6502, -1.2505, 1.5863, -3.2514, -0.7541, 1.87, -2.8735, -1.1419, -2.3406, -2.1709, 1.3672, -2.179, -3.4152, 0.4259, -2.6648, -1.8501, 0.307, -2.3406, -2.1709, 1.3672, -1.8849, -3.8097, 1.1593, -2.179, -3.4152, 0.4259, 0.1398, -4.5397, -0.9338, -0.4293, -5.0774, 0.0003, 0.0671, -5.0216, -0.4515, 0.1398, -4.5397, -0.9338, -0.5108, -3.8408, -1.1476, -0.4293, -5.0774, 0.0003, -1.6361, -1.5711, -1.5378, -0.2695, -2.508, -2.0432, -1.0077, -1.3716, -2.1657, -1.6361, -1.5711, -1.5378, -1.1054, -2.8992, -1.0824, -0.2695, -2.508, -2.0432, 1.5426, -2.0265, -2.2438, 1.4243, -3.0391, -1.5379, 2.1195, -1.771, -1.5705, 1.5426, -2.0265, -2.2438, 0.9178, -3.3811, -1.9588, 1.4243, -3.0391, -1.5379, 2.6003, 1.0473, -1.0133, 2.5473, 1.387, -0.0726, 2.4031, 2.6779, -0.4122, 2.6003, 1.0473, -1.0133, 2.6196, 0.7185, -0.2944, 2.5473, 1.387, -0.0726, -0.7814, -0.8418, 2.7775, -0.2939, -2.4569, 2.4482, -1.2288, -2.368, 2.4204, -0.7814, -0.8418, 2.7775, 0.256, -0.9397, 2.7009, -0.2939, -2.4569, 2.4482, -1.6176, 1.4128, 2.2491, -2.1461, 1.5782, 1.2673, -1.7058, 2.0492, 1.8707, -1.6176, 1.4128, 2.2491, -2.0758, -0.0137, 1.8949, -2.1461, 1.5782, 1.2673, -2.5502, 1.5321, -0.8246, -1.7216, 2.2099, -1.5001, -1.9974, 2.3952, -0.8364, -2.5502, 1.5321, -0.8246, -2.3834, 0.7782, -1.5631, -1.7216, 2.2099, -1.5001, -0.0823, 1.3076, -2.4751, 0.7444, 0.8429, -2.2856, 0.0456, 1.662, -2.2329, -0.0823, 1.3076, -2.4751, 0.635, 0.4247, -2.5749, 0.7444, 0.8429, -2.2856, 1.2634, 3.7734, 1.9919, 1.1136, 1.849, 2.4998, 0.5038, 3.0339, 2.4087, 1.2634, 3.7734, 1.9919, 1.6219, 2.267, 2.1861, 1.1136, 1.849, 2.4998, -0.878, 3.135, 1.7811, -1.7058, 2.0492, 1.8707, -1.5979, 2.7577, 1.2075, -0.878, 3.135, 1.7811, -1.3001, 2.2705, 2.1959, -1.7058, 2.0492, 1.8707, -0.0988, 3.0024, -1.9036, 0.0456, 1.662, -2.2329, 0.1709, 2.9305, -1.8154, -0.0988, 3.0024, -1.9036, -0.2095, 1.8203, -2.3278, 0.0456, 1.662, -2.2329, 0.9445, 5.5277, -0.3666, 0.6378, 5.6752, 0.4017, 0.5746, 5.6649, -0.3989, 0.9445, 5.5277, -0.3666, 1.3249, 5.3073, 0.5102, 0.6378, 5.6752, 0.4017, -1.2485, 3.6723, -1.0264, -1.9974, 2.3952, -0.8364, -1.7216, 2.2099, -1.5001, -1.2485, 3.6723, -1.0264, -1.7496, 3.0465, -0.6307, -1.9974, 2.3952, -0.8364, 0.9077, 3.2903, -1.4616, 0.1709, 2.9305, -1.8154, 0.9177, 2.8679, -1.5387, 0.9077, 3.2903, -1.4616, 0.1823, 3.4347, -1.7089, 0.1709, 2.9305, -1.8154, -1.2288, -2.368, 2.4204, -0.2829, -3.5923, 2.1108, -1.1122, -3.8993, 1.9946, -1.2288, -2.368, 2.4204, -0.2939, -2.4569, 2.4482, -0.2829, -3.5923, 2.1108, 1.6056, -1.9424, 1.9813, 0.9704, -3.0065, 1.4497, 0.5442, -2.0915, 2.2664, 1.6056, -1.9424, 1.9813, 1.8065, -2.8661, 1.2715, 0.9704, -3.0065, 1.4497, 2.1195, -1.771, -1.5705, 2.1726, -2.8941, -0.5813, 2.5358, -1.9021, -0.8254, 2.1195, -1.771, -1.5705, 1.87, -2.8735, -1.1419, 2.1726, -2.8941, -0.5813, 0.0671, -5.0216, -0.4515, 0.6021, -4.1961, -0.0392, 0.4756, -4.5162, -0.6549, 0.0671, -5.0216, -0.4515, 0.169, -4.9369, 0.2441, 0.6021, -4.1961, -0.0392, -2.3688, -2.5627, -0.2622, -1.1208, -4.5065, 0.1953, -1.809, -3.263, -0.2744, -2.3688, -2.5627, -0.2622, -2.179, -3.4152, 0.4259, -1.1208, -4.5065, 0.1953, 2.3678, 2.1513, 0.6399, 2.4617, 0.7259, 0.0288, 2.1038, 1.0245, 0.9114, 2.3678, 2.1513, 0.6399, 2.5473, 1.387, -0.0726, 2.4617, 0.7259, 0.0288, 0.5038, 3.0339, 2.4087, 0.7315, 0.8274, 2.7196, -0.2195, 1.1266, 2.8293, 0.5038, 3.0339, 2.4087, 1.1136, 1.849, 2.4998, 0.7315, 0.8274, 2.7196, 2.1498, -1.9824, 1.4097, 1.2883, -0.8696, 2.2885, 1.9393, -0.8033, 1.6608, 2.1498, -1.9824, 1.4097, 1.6056, -1.9424, 1.9813, 1.2883, -0.8696, 2.2885, -2.6648, -1.8501, 0.307, -2.5883, -1.4508, -0.7517, -2.907, 0.041, -0.3088, -2.6648, -1.8501, 0.307, -2.3688, -2.5627, -0.2622, -2.5883, -1.4508, -0.7517, 1.2471, 1.631, -1.7857, 0.9877, 0.4313, -2.4389, 1.4453, 1.3219, -1.8647, 1.2471, 1.631, -1.7857, 0.7444, 0.8429, -2.2856, 0.9877, 0.4313, -2.4389, -1.0077, -1.3716, -2.1657, 0.0974, -1.7758, -2.6186, -0.7692, -0.6389, -2.6654, -1.0077, -1.3716, -2.1657, -0.2695, -2.508, -2.0432, 0.0974, -1.7758, -2.6186, 2.3685, 3.5019, 0.414, 2.1901, 2.3652, 1.3543, 2.2544, 3.8319, 0.7934, 2.3685, 3.5019, 0.414, 2.3678, 2.1513, 0.6399, 2.1901, 2.3652, 1.3543, -1.5979, 2.7577, 1.2075, -2.575, 1.9843, 0.2578, -1.9764, 3.1849, 0.2685, -1.5979, 2.7577, 1.2075, -2.1461, 1.5782, 1.2673, -2.575, 1.9843, 0.2578, 0.9177, 2.8679, -1.5387, 1.4442, 1.9, -1.6223, 1.1827, 2.8613, -1.4341, 0.9177, 2.8679, -1.5387, 1.2471, 1.631, -1.7857, 1.4442, 1.9, -1.6223, -0.2232, 5.1794, 0.1938, 0.8594, 5.2909, 1.0912, -0.0735, 4.938, 0.8308, -0.2232, 5.1794, 0.1938, 0.6378, 5.6752, 0.4017, 0.8594, 5.2909, 1.0912, 2.4031, 2.6779, -0.4122, 2.1174, 4.2399, 0.396, 1.7744, 4.3299, -0.4393, 2.4031, 2.6779, -0.4122, 2.3685, 3.5019, 0.414, 2.1174, 4.2399, 0.396, -0.2125, 5.045, -0.6732, -0.791, 4.3118, -0.0929, -1.2485, 3.6723, -1.0264, -0.2125, 5.045, -0.6732, -0.2232, 5.1794, 0.1938, -0.791, 4.3118, -0.0929, 0.5746, 5.6649, -0.3989, -0.1392, 4.5027, -1.2571, 0.6884, 5.4332, -0.7011, 0.5746, 5.6649, -0.3989, -0.2125, 5.045, -0.6732, -0.1392, 4.5027, -1.2571, 1.7744, 4.3299, -0.4393, 0.6884, 5.4332, -0.7011, 1.3226, 4.3217, -0.9655, 1.7744, 4.3299, -0.4393, 0.9445, 5.5277, -0.3666, 0.6884, 5.4332, -0.7011, 1.3226, 4.3217, -0.9655, 1.1827, 2.8613, -1.4341, 2.0312, 2.7158, -1.0135, 1.3226, 4.3217, -0.9655, 0.9077, 3.2903, -1.4616, 1.1827, 2.8613, -1.4341, 0.1823, 3.4347, -1.7089, -0.9393, 2.872, -1.8539, -0.0988, 3.0024, -1.9036, 0.1823, 3.4347, -1.7089, -0.1392, 4.5027, -1.2571, -0.9393, 2.872, -1.8539, -1.3695, 3.5538, -0.0189, -0.5404, 4.4098, 0.4328, -1.0116, 3.5059, 0.8103, -1.3695, 3.5538, -0.0189, -0.791, 4.3118, -0.0929, -0.5404, 4.4098, 0.4328, -1.7496, 3.0465, -0.6307, -1.9764, 3.1849, 0.2685, -2.286, 2.7057, -0.3882, -1.7496, 3.0465, -0.6307, -1.3695, 3.5538, -0.0189, -1.9764, 3.1849, 0.2685, -1.0116, 3.5059, 0.8103, -0.0735, 4.938, 0.8308, -0.2982, 4.0929, 1.4648, -1.0116, 3.5059, 0.8103, -0.5404, 4.4098, 0.4328, -0.0735, 4.938, 0.8308, -0.2982, 4.0929, 1.4648, -0.4271, 2.5556, 2.4897, -0.878, 3.135, 1.7811, -0.2982, 4.0929, 1.4648, 0.5261, 4.2194, 1.9538, -0.4271, 2.5556, 2.4897, 0.5261, 4.2194, 1.9538, 1.6148, 4.6612, 1.1746, 1.2634, 3.7734, 1.9919, 0.5261, 4.2194, 1.9538, 0.8594, 5.2909, 1.0912, 1.6148, 4.6612, 1.1746, 1.3249, 5.3073, 0.5102, 2.2544, 3.8319, 0.7934, 1.6148, 4.6612, 1.1746, 1.3249, 5.3073, 0.5102, 2.1174, 4.2399, 0.396, 2.2544, 3.8319, 0.7934, 2.5987, -0.8743, -1.1872, 2.1515, 0.9506, -1.6595, 1.9584, -0.8816, -2.0923, 2.5987, -0.8743, -1.1872, 2.6003, 1.0473, -1.0133, 2.1515, 0.9506, -1.6595, 2.0312, 2.7158, -1.0135, 1.4453, 1.3219, -1.8647, 2.1515, 0.9506, -1.6595, 2.0312, 2.7158, -1.0135, 1.4442, 1.9, -1.6223, 1.4453, 1.3219, -1.8647, -0.7261, 0.8081, -2.8158, -0.2095, 1.8203, -2.3278, -0.8202, 1.5078, -2.6285, -0.7261, 0.8081, -2.8158, -0.0823, 1.3076, -2.4751, -0.2095, 1.8203, -2.3278, -0.7692, -0.6389, -2.6654, -1.246, 0.719, -2.7492, -1.6842, -0.4729, -2.3712, -0.7692, -0.6389, -2.6654, -0.7261, 0.8081, -2.8158, -1.246, 0.719, -2.7492, -0.8202, 1.5078, -2.6285, -1.7965, 0.899, -2.2295, -1.246, 0.719, -2.7492, -0.8202, 1.5078, -2.6285, -0.9393, 2.872, -1.8539, -1.7965, 0.899, -2.2295, -2.907, 0.041, -0.3088, -2.286, 2.7057, -0.3882, -2.575, 1.9843, 0.2578, -2.907, 0.041, -0.3088, -2.5502, 1.5321, -0.8246, -2.286, 2.7057, -0.3882, -1.0159, 0.9756, 2.6776, -1.3001, 2.2705, 2.1959, -0.4271, 2.5556, 2.4897, -1.0159, 0.9756, 2.6776, -1.6176, 1.4128, 2.2491, -1.3001, 2.2705, 2.1959, -1.4963, -0.9921, 2.5267, -0.2195, 1.1266, 2.8293, -0.7814, -0.8418, 2.7775, -1.4963, -0.9921, 2.5267, -1.0159, 0.9756, 2.6776, -0.2195, 1.1266, 2.8293, 1.9293, 0.908, 1.6849, 1.6219, 2.267, 2.1861, 2.1901, 2.3652, 1.3543, 1.9293, 0.908, 1.6849, 1.409, 0.9631, 2.386, 1.6219, 2.267, 2.1861, 1.9393, -0.8033, 1.6608, 2.1038, 1.0245, 0.9114, 2.1074, -0.9551, 0.9434, 1.9393, -0.8033, 1.6608, 1.9293, 0.908, 1.6849, 2.1038, 1.0245, 0.9114, 1.1377, -0.8428, -2.7439, 0.635, 0.4247, -2.5749, 0.7304, -0.9533, -2.8604, 1.1377, -0.8428, -2.7439, 0.9877, 0.4313, -2.4389, 0.635, 0.4247, -2.5749, 1.9584, -0.8816, -2.0923, 0.9457, -1.6804, -2.7624, 1.5426, -2.0265, -2.2438, 1.9584, -0.8816, -2.0923, 1.1377, -0.8428, -2.7439, 0.9457, -1.6804, -2.7624, 0.7304, -0.9533, -2.8604, 0.3194, -2.8417, -2.3407, 0.9457, -1.6804, -2.7624, 0.7304, -0.9533, -2.8604, 0.0974, -1.7758, -2.6186, 0.3194, -2.8417, -2.3407, -1.6842, -0.4729, -2.3712, -2.3834, 0.7782, -1.5631, -2.3999, -0.6295, -1.5513, -1.6842, -0.4729, -2.3712, -1.7965, 0.899, -2.2295, -2.3834, 0.7782, -1.5631, -2.3999, -0.6295, -1.5513, -1.9059, -2.3487, -0.7483, -1.6361, -1.5711, -1.5378, -2.3999, -0.6295, -1.5513, -2.5883, -1.4508, -0.7517, -1.9059, -2.3487, -0.7483, -2.0758, -0.0137, 1.8949, -1.7887, -3.0231, 1.9828, -2.3406, -2.1709, 1.3672, -2.0758, -0.0137, 1.8949, -1.4963, -0.9921, 2.5267, -1.7887, -3.0231, 1.9828, 0.256, -0.9397, 2.7009, 1.409, 0.9631, 2.386, 1.2883, -0.8696, 2.2885, 0.256, -0.9397, 2.7009, 0.7315, 0.8274, 2.7196, 1.409, 0.9631, 2.386, 2.4385, -0.7713, 0.0524, 2.6196, 0.7185, -0.2944, 2.634, -0.7691, -0.3318, 2.4385, -0.7713, 0.0524, 2.4617, 0.7259, 0.0288, 2.6196, 0.7185, -0.2944, 2.1074, -0.9551, 0.9434, 2.5652, -1.538, -0.1083, 2.3387, -2.1373, 0.6494, 2.1074, -0.9551, 0.9434, 2.4385, -0.7713, 0.0524, 2.5652, -1.538, -0.1083, 2.634, -0.7691, -0.3318, 2.5358, -1.9021, -0.8254, 2.5652, -1.538, -0.1083, 2.634, -0.7691, -0.3318, 2.5987, -0.8743, -1.1872, 2.5358, -1.9021, -0.8254, 0.4756, -4.5162, -0.6549, 0.3423, -3.927, -1.4233, 0.1398, -4.5397, -0.9338, 0.4756, -4.5162, -0.6549, 0.9342, -3.6502, -1.2505, 0.3423, -3.927, -1.4233, 0.9178, -3.3811, -1.9588, -0.5108, -3.8408, -1.1476, 0.3423, -3.927, -1.4233, 0.9178, -3.3811, -1.9588, 0.3194, -2.8417, -2.3407, -0.5108, -3.8408, -1.1476, -1.1054, -2.8992, -1.0824, -1.809, -3.263, -0.2744, -1.1208, -4.5065, 0.1953, -1.1054, -2.8992, -1.0824, -1.9059, -2.3487, -0.7483, -1.809, -3.263, -0.2744, -1.8849, -3.8097, 1.1593, -1.1122, -3.8993, 1.9946, -0.8729, -4.7982, 1.121, -1.8849, -3.8097, 1.1593, -1.7887, -3.0231, 1.9828, -1.1122, -3.8993, 1.9946, 1.9869, -3.1191, 0.2426, 1.5863, -3.2514, -0.7541, 1.267, -3.4909, -0.189, 1.9869, -3.1191, 0.2426, 2.1726, -2.8941, -0.5813, 1.5863, -3.2514, -0.7541, 1.267, -3.4909, -0.189, 0.4584, -3.889, 0.8266, 1.2817, -3.3103, 0.5621, 1.267, -3.4909, -0.189, 0.6021, -4.1961, -0.0392, 0.4584, -3.889, 0.8266, -0.0922, -4.5871, 1.3087, 0.5442, -2.0915, 2.2664, 0.4197, -3.4404, 1.5487, -0.0922, -4.5871, 1.3087, -0.2829, -3.5923, 2.1108, 0.5442, -2.0915, 2.2664, 0.4197, -3.4404, 1.5487, 1.2817, -3.3103, 0.5621, 0.4584, -3.889, 0.8266, 0.4197, -3.4404, 1.5487, 0.9704, -3.0065, 1.4497, 1.2817, -3.3103, 0.5621, -0.4293, -5.0774, 0.0003, -1.1208, -4.5065, 0.1953, -0.8729, -4.7982, 1.121)\n\n[node name=\"RockB\" instance=ExtResource(\"1_2nhli\")]\ncollision_mask = 3\n\n[node name=\"Rock2\" parent=\".\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"2_dbm2k\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\".\" index=\"1\"]\nshape = SubResource(\"ConcavePolygonShape3D_wfrmp\")\n"
  },
  {
    "path": "project/demo/assets/models/RockC.glb.import",
    "content": "[remap]\n\nimporter=\"scene\"\nimporter_version=1\ntype=\"PackedScene\"\nuid=\"uid://c8cx4xjwluvxw\"\npath=\"res://.godot/imported/RockC.glb-5881ee1ff2072066a3de122ce4239bee.scn\"\n\n[deps]\n\nsource_file=\"res://demo/assets/models/RockC.glb\"\ndest_files=[\"res://.godot/imported/RockC.glb-5881ee1ff2072066a3de122ce4239bee.scn\"]\n\n[params]\n\nnodes/root_type=\"StaticBody3D\"\nnodes/root_name=\"Scene Root\"\nnodes/apply_root_scale=true\nnodes/root_scale=1.0\nnodes/import_as_skeleton_bones=false\nnodes/use_node_type_suffixes=true\nmeshes/ensure_tangents=true\nmeshes/generate_lods=true\nmeshes/create_shadow_meshes=true\nmeshes/light_baking=1\nmeshes/lightmap_texel_size=0.2\nmeshes/force_disable_compression=false\nskins/use_named_skins=true\nanimation/import=true\nanimation/fps=30\nanimation/trimming=false\nanimation/remove_immutable_tracks=true\nanimation/import_rest_as_RESET=false\nimport_script/path=\"\"\n_subresources={\n\"materials\": {\n\"@MATERIAL:0\": {\n\"use_external/enabled\": true,\n\"use_external/path\": \"res://demo/assets/materials/M_rock30.tres\"\n}\n}\n}\ngltf/naming_version=0\ngltf/embedded_image_handling=1\n"
  },
  {
    "path": "project/demo/assets/models/RockC.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://lsvs8a7urkca\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://c8cx4xjwluvxw\" path=\"res://demo/assets/models/RockC.glb\" id=\"1_pb27A\"]\n[ext_resource type=\"Material\" uid=\"uid://nbbdrx8vma80\" path=\"res://demo/assets/materials/M_rock23_tp.tres\" id=\"2_pcex8\"]\n\n[sub_resource type=\"ConcavePolygonShape3D\" id=\"ConcavePolygonShape3D_ycsjC\"]\ndata = PackedVector3Array(1.3907, -4.3898, 0.264, 0.3707, -6.5026, 0.2358, 0.9197, -5.2139, 0.5803, 1.3907, -4.3898, 0.264, 1.5973, -4.4989, -0.523, 0.3707, -6.5026, 0.2358, 2.7182, -1.9153, 1.6333, 2.6862, -0.2733, 1.344, 2.791, -2.0828, 0.6971, -0.6892, -7.3327, -0.0456, -1.2211, -6.6432, -1.3733, -1.8677, -6.7222, 0.0053, 0.1584, -6.6788, -0.712, 0.8528, -5.1659, -1.2387, -0.129, -6.0155, -1.6627, 0.3707, -6.5026, 0.2358, 0.8249, -4.8431, 1.2373, 0.9197, -5.2139, 0.5803, 0.3707, -6.5026, 0.2358, -0.1504, -6.0468, 1.6347, 0.8249, -4.8431, 1.2373, 2.7182, -1.9153, 1.6333, 2.5729, -0.0463, 2.323, 2.6862, -0.2733, 1.344, -1.5434, -2.0626, 2.9798, -0.0319, -0.907, 3.3884, -0.6296, -2.1168, 3.0274, -1.5434, -2.0626, 2.9798, -2.2391, -1.0013, 3.2378, -0.0319, -0.907, 3.3884, -3.2503, -1.9438, -0.0444, -3.1045, -0.0443, -0.8667, -2.9895, -0.0523, 0.8233, -0.5191, -2.1018, -2.9939, -2.0489, -0.9812, -3.014, -1.4897, -2.0239, -2.8933, -0.5191, -2.1018, -2.9939, 0.0444, -0.8451, -3.3989, -2.0489, -0.9812, -3.014, 3.4189, -1.5098, -2.1519, 3.3947, 0.8197, -1.419, 2.9911, 0.177, -2.5055, -0.9484, -5.3598, 2.2535, -0.8975, -3.7031, 2.6018, -0.1302, -3.9225, 2.4564, -2.8602, -5.2902, -0.0034, -3.2063, -3.9919, -0.0118, -3.0246, -4.4027, 0.8468, -0.9423, -5.2303, -2.2174, -1.6189, -3.1121, -2.5666, -2.3373, -3.6333, -2.2624, -0.9423, -5.2303, -2.2174, -0.9004, -3.6647, -2.5803, -1.6189, -3.1121, -2.5666, 2.4159, -3.3908, 0.0683, 2.6374, -3.7219, -1.0723, 1.5973, -4.4989, -0.523, 0.0817, 0.7564, 3.232, 1.3747, 1.7762, 2.618, 1.9677, 0.877, 2.8315, 0.0817, 0.7564, 3.232, 0.5064, 1.8556, 2.7003, 1.3747, 1.7762, 2.618, -0.2637, 3.5029, 1.8153, 0.4487, 4.8708, 1.6514, 0.6919, 3.2703, 2.2188, -2.3926, 3.3478, 0.3255, -1.6818, 3.3981, 1.0198, -2.0691, 2.5964, 1.5185, -1.2285, 2.4662, -2.4325, -1.544, 3.8438, -1.471, -2.3777, 2.1645, -2.0413, 2.3543, 3.9299, -1.7328, 1.4379, 5.2295, -1.8152, 1.1494, 2.3687, -2.734, 1.4341, 6.0081, 1.2447, 0.9325, 6.924, 0.4365, 2.3528, 6.0999, 0.4838, -1.8677, -6.7222, 0.0053, -2.3434, -5.4566, 1.3758, -1.2352, -6.7568, 1.4079, 3.0694, 0.3768, 0.2917, 3.6836, 2.3906, 0.1427, 3.39, -0.1345, -0.5433, 1.5686, 6.857, -0.4389, 0.7824, 7.1818, -0.4789, 0.9515, 6.8097, -1.0683, 2.4165, 5.7335, -0.6508, 1.4379, 5.2295, -1.8152, 2.3543, 3.9299, -1.7328, 2.0681, 1.0413, -2.9849, 1.1494, 2.3687, -2.734, 0.0399, 0.8971, -3.3453, -0.1803, 5.4522, -1.6664, -0.2609, 6.3493, -0.8464, -1.544, 3.8438, -1.471, -3.1045, -0.0443, -0.8667, -2.9635, 2.0631, -0.0469, -2.9895, -0.0523, 0.8233, -2.8218, -0.0908, -2.3316, -2.8949, 1.7465, -1.4628, -3.1045, -0.0443, -0.8667, -1.137, 5.0901, -0.2048, -0.2548, 6.3653, 0.3115, -0.7059, 4.7856, 0.8682, -1.8973, 4.0438, -0.0231, -1.6818, 3.3981, 1.0198, -2.3926, 3.3478, 0.3255, -2.9895, -0.0523, 0.8233, -2.7222, 1.5834, 1.3251, -2.9289, -0.1232, 2.5316, -0.7059, 4.7856, 0.8682, -0.2548, 6.3653, 0.3115, 0.4487, 4.8708, 1.6514, -0.7059, 4.7856, 0.8682, 0.4487, 4.8708, 1.6514, -0.2637, 3.5029, 1.8153, 3.3947, 0.8197, -1.419, 3.6143, 2.6541, -0.7787, 3.0814, 2.2747, -1.5878, 3.3017, 4.3689, -0.5397, 2.4165, 5.7335, -0.6508, 2.3543, 3.9299, -1.7328, -2.1314, 0.7603, 3.0046, -1.0522, 1.986, 2.2721, 0.0817, 0.7564, 3.232, 0.0399, 0.8971, -3.3453, 1.1494, 2.3687, -2.734, -1.2285, 2.4662, -2.4325, 0.549, 3.3793, -2.5003, 0.4374, 4.5707, -2.224, -0.1459, 3.4745, -2.3857, 1.4341, 6.0081, 1.2447, 2.3528, 6.0999, 0.4838, 2.8818, 4.7444, 1.3483, -3.1045, -0.0443, -0.8667, -2.8949, 1.7465, -1.4628, -2.9635, 2.0631, -0.0469, -2.7186, 2.9035, -1.3821, -2.3162, 3.6228, -0.9721, -2.7435, 3.293, -0.5137, -0.1803, 5.4522, -1.6664, -1.544, 3.8438, -1.471, -1.2285, 2.4662, -2.4325, -2.1314, 0.7603, 3.0046, -2.0691, 2.5964, 1.5185, -1.0522, 1.986, 2.2721, -2.0691, 2.5964, 1.5185, -1.6818, 3.3981, 1.0198, -1.0522, 1.986, 2.2721, 0.0399, 0.8971, -3.3453, -1.2285, 2.4662, -2.4325, -2.0395, 0.8474, -2.9039, 1.9677, 0.877, 2.8315, 1.5121, 2.8416, 2.3106, 2.2766, 3.3307, 2.1367, 1.9677, 0.877, 2.8315, 1.3747, 1.7762, 2.618, 1.5121, 2.8416, 2.3106, 0.6919, 3.2703, 2.2188, 2.2766, 3.3307, 2.1367, 1.5121, 2.8416, 2.3106, 0.6919, 3.2703, 2.2188, 0.4487, 4.8708, 1.6514, 2.2766, 3.3307, 2.1367, 3.3947, 0.8197, -1.419, 3.0814, 2.2747, -1.5878, 2.9911, 0.177, -2.5055, 2.8818, 4.7444, 1.3483, 2.3528, 6.0999, 0.4838, 3.481, 4.5391, 0.4718, 2.0681, 1.0413, -2.9849, 2.3543, 3.9299, -1.7328, 1.1494, 2.3687, -2.734, 2.5729, -0.0463, 2.323, 2.8449, 1.8063, 1.8788, 2.6862, -0.2733, 1.344, -1.4897, -2.0239, -2.8933, -2.3373, -3.6333, -2.2624, -1.6189, -3.1121, -2.5666, -1.4897, -2.0239, -2.8933, -2.0489, -0.9812, -3.014, -2.3373, -3.6333, -2.2624, -2.0395, 0.8474, -2.9039, -1.2285, 2.4662, -2.4325, -2.3777, 2.1645, -2.0413, 2.1308, -0.8122, -3.0291, 0.0444, -0.8451, -3.3989, 1.2123, -2.2074, -2.6786, -3.2503, -1.9438, -0.0444, -2.9895, -0.0523, 0.8233, -3.048, -2.4163, 1.5053, -2.9895, -0.0523, 0.8233, -2.9635, 2.0631, -0.0469, -2.7222, 1.5834, 1.3251, -2.8218, -0.0908, -2.3316, -3.1045, -0.0443, -0.8667, -3.1353, -2.4738, -1.533, -0.6296, -2.1168, 3.0274, -0.0319, -0.907, 3.3884, 0.6607, -2.1929, 2.7904, 0.0817, 0.7564, 3.232, -1.0522, 1.986, 2.2721, 0.5064, 1.8556, 2.7003, 3.0694, 0.3768, 0.2917, 3.39, -0.1345, -0.5433, 3.0799, -1.877, -0.2485, 2.332, -3.2513, 1.4491, 1.8096, -3.8731, 0.517, 1.7532, -3.8773, 1.1862, 2.332, -3.2513, 1.4491, 2.4159, -3.3908, 0.0683, 1.8096, -3.8731, 0.517, 3.0694, 0.3768, 0.2917, 3.2927, 2.696, 1.0915, 3.6836, 2.3906, 0.1427, 0.8528, -5.1659, -1.2387, 0.5989, -4.5033, -1.836, -0.129, -6.0155, -1.6627, 1.6723, -4.1113, -1.5062, 2.2303, -3.7001, -1.8804, 1.5835, -3.611, -1.9401, 2.6485, -2.4319, -2.56, 2.1308, -0.8122, -3.0291, 1.2123, -2.2074, -2.6786, -1.2211, -6.6432, -1.3733, -2.3738, -5.4707, -1.3892, -1.8677, -6.7222, 0.0053, 1.2123, -2.2074, -2.6786, 0.0444, -0.8451, -3.3989, -0.5191, -2.1018, -2.9939, -0.9423, -5.2303, -2.2174, -0.0313, -3.8492, -2.3572, -0.9004, -3.6647, -2.5803, -2.9895, -0.0523, 0.8233, -2.9289, -0.1232, 2.5316, -3.048, -2.4163, 1.5053, -3.1353, -2.4738, -1.533, -3.1045, -0.0443, -0.8667, -3.2503, -1.9438, -0.0444, -2.8602, -5.2902, -0.0034, -3.0733, -4.4498, -0.8737, -3.2063, -3.9919, -0.0118, -0.1504, -6.0468, 1.6347, 0.6052, -4.4095, 1.8566, 0.8249, -4.8431, 1.2373, -2.3213, -3.5933, 2.2613, -1.5434, -2.0626, 2.9798, -1.6095, -3.1051, 2.5592, -2.3213, -3.5933, 2.2613, -2.2391, -1.0013, 3.2378, -1.5434, -2.0626, 2.9798, 3.0799, -1.877, -0.2485, 3.39, -0.1345, -0.5433, 3.4106, -2.2767, -1.2285, -0.0319, -0.907, 3.3884, 1.8364, -0.871, 2.91, 0.6607, -2.1929, 2.7904, 1.7532, -3.8773, 1.1862, 1.5121, -3.4321, 1.801, 2.332, -3.2513, 1.4491, -0.9484, -5.3598, 2.2535, -1.6095, -3.1051, 2.5592, -0.8975, -3.7031, 2.6018, -0.9484, -5.3598, 2.2535, -2.3213, -3.5933, 2.2613, -1.6095, -3.1051, 2.5592, 2.4159, -3.3908, 0.0683, 1.3907, -4.3898, 0.264, 1.8096, -3.8731, 0.517, 2.4159, -3.3908, 0.0683, 1.5973, -4.4989, -0.523, 1.3907, -4.3898, 0.264, 0.6607, -2.1929, 2.7904, 1.8364, -0.871, 2.91, 2.1556, -2.2057, 2.3132, -0.6892, -7.3327, -0.0456, 0.3707, -6.5026, 0.2358, 0.1584, -6.6788, -0.712, 2.7182, -1.9153, 1.6333, 2.332, -3.2513, 1.4491, 2.1556, -2.2057, 2.3132, -0.8975, -3.7031, 2.6018, -1.5434, -2.0626, 2.9798, -0.6296, -2.1168, 3.0274, -0.8975, -3.7031, 2.6018, -1.6095, -3.1051, 2.5592, -1.5434, -2.0626, 2.9798, -0.5191, -2.1018, -2.9939, -1.6189, -3.1121, -2.5666, -0.9004, -3.6647, -2.5803, -0.5191, -2.1018, -2.9939, -1.4897, -2.0239, -2.8933, -1.6189, -3.1121, -2.5666, 3.4106, -2.2767, -1.2285, 2.2303, -3.7001, -1.8804, 2.6374, -3.7219, -1.0723, 2.2303, -3.7001, -1.8804, 3.4189, -1.5098, -2.1519, 2.6485, -2.4319, -2.56, 2.2303, -3.7001, -1.8804, 3.4106, -2.2767, -1.2285, 3.4189, -1.5098, -2.1519, 0.6919, 3.2703, 2.2188, 1.3747, 1.7762, 2.618, 0.5064, 1.8556, 2.7003, 0.6919, 3.2703, 2.2188, 1.5121, 2.8416, 2.3106, 1.3747, 1.7762, 2.618, -2.7186, 2.9035, -1.3821, -2.8949, 1.7465, -1.4628, -2.3777, 2.1645, -2.0413, 3.6143, 2.6541, -0.7787, 3.481, 4.5391, 0.4718, 3.3017, 4.3689, -0.5397, 3.6143, 2.6541, -0.7787, 3.6836, 2.3906, 0.1427, 3.481, 4.5391, 0.4718, 0.7824, 7.1818, -0.4789, -0.2548, 6.3653, 0.3115, -0.2609, 6.3493, -0.8464, 0.7824, 7.1818, -0.4789, 0.9325, 6.924, 0.4365, -0.2548, 6.3653, 0.3115, -0.1504, -6.0468, 1.6347, -1.2352, -6.7568, 1.4079, -0.9484, -5.3598, 2.2535, 1.8096, -3.8731, 0.517, 0.8249, -4.8431, 1.2373, 1.7532, -3.8773, 1.1862, 0.8249, -4.8431, 1.2373, 1.3907, -4.3898, 0.264, 0.9197, -5.2139, 0.5803, 0.8249, -4.8431, 1.2373, 1.8096, -3.8731, 0.517, 1.3907, -4.3898, 0.264, -0.1302, -3.9225, 2.4564, 1.5121, -3.4321, 1.801, 0.6052, -4.4095, 1.8566, -0.1302, -3.9225, 2.4564, 0.6607, -2.1929, 2.7904, 1.5121, -3.4321, 1.801, 2.4159, -3.3908, 0.0683, 2.791, -2.0828, 0.6971, 3.0799, -1.877, -0.2485, 1.5973, -4.4989, -0.523, 1.6723, -4.1113, -1.5062, 0.8528, -5.1659, -1.2387, -3.0246, -4.4027, 0.8468, -2.3213, -3.5933, 2.2613, -2.3434, -5.4566, 1.3758, -3.0246, -4.4027, 0.8468, -3.048, -2.4163, 1.5053, -2.3213, -3.5933, 2.2613, -1.2211, -6.6432, -1.3733, -0.129, -6.0155, -1.6627, -0.9423, -5.2303, -2.2174, -3.0733, -4.4498, -0.8737, -2.3373, -3.6333, -2.2624, -3.1353, -2.4738, -1.533, -3.0733, -4.4498, -0.8737, -2.3738, -5.4707, -1.3892, -2.3373, -3.6333, -2.2624, 1.5835, -3.611, -1.9401, -0.0313, -3.8492, -2.3572, 0.5989, -4.5033, -1.836, 1.5835, -3.611, -1.9401, 1.2123, -2.2074, -2.6786, -0.0313, -3.8492, -2.3572, 2.5729, -0.0463, 2.323, 1.8364, -0.871, 2.91, 1.9677, 0.877, 2.8315, -2.9289, -0.1232, 2.5316, -2.1314, 0.7603, 3.0046, -2.2391, -1.0013, 3.2378, -2.0395, 0.8474, -2.9039, -2.8218, -0.0908, -2.3316, -2.0489, -0.9812, -3.014, 2.1308, -0.8122, -3.0291, 2.9911, 0.177, -2.5055, 2.0681, 1.0413, -2.9849, 3.2927, 2.696, 1.0915, 2.2766, 3.3307, 2.1367, 2.8818, 4.7444, 1.3483, 3.2927, 2.696, 1.0915, 2.8449, 1.8063, 1.8788, 2.2766, 3.3307, 2.1367, -2.9635, 2.0631, -0.0469, -2.7435, 3.293, -0.5137, -2.3926, 3.3478, 0.3255, 2.3528, 6.0999, 0.4838, 1.5686, 6.857, -0.4389, 2.4165, 5.7335, -0.6508, -2.3162, 3.6228, -0.9721, -1.137, 5.0901, -0.2048, -1.8973, 4.0438, -0.0231, -2.3162, 3.6228, -0.9721, -1.544, 3.8438, -1.471, -1.137, 5.0901, -0.2048, 0.4374, 4.5707, -2.224, 0.9515, 6.8097, -1.0683, -0.1803, 5.4522, -1.6664, 0.4374, 4.5707, -2.224, 1.4379, 5.2295, -1.8152, 0.9515, 6.8097, -1.0683, -1.2352, -6.7568, 1.4079, 0.3707, -6.5026, 0.2358, -0.6892, -7.3327, -0.0456, -1.2352, -6.7568, 1.4079, -0.1504, -6.0468, 1.6347, 0.3707, -6.5026, 0.2358, 0.6607, -2.1929, 2.7904, -0.8975, -3.7031, 2.6018, -0.6296, -2.1168, 3.0274, 0.6607, -2.1929, 2.7904, -0.1302, -3.9225, 2.4564, -0.8975, -3.7031, 2.6018, 2.791, -2.0828, 0.6971, 2.332, -3.2513, 1.4491, 2.7182, -1.9153, 1.6333, 2.791, -2.0828, 0.6971, 2.4159, -3.3908, 0.0683, 2.332, -3.2513, 1.4491, 1.6723, -4.1113, -1.5062, 2.6374, -3.7219, -1.0723, 2.2303, -3.7001, -1.8804, 1.6723, -4.1113, -1.5062, 1.5973, -4.4989, -0.523, 2.6374, -3.7219, -1.0723, -3.048, -2.4163, 1.5053, -3.2063, -3.9919, -0.0118, -3.2503, -1.9438, -0.0444, -3.048, -2.4163, 1.5053, -3.0246, -4.4027, 0.8468, -3.2063, -3.9919, -0.0118, -0.129, -6.0155, -1.6627, -0.6892, -7.3327, -0.0456, 0.1584, -6.6788, -0.712, -0.129, -6.0155, -1.6627, -1.2211, -6.6432, -1.3733, -0.6892, -7.3327, -0.0456, 1.2123, -2.2074, -2.6786, 2.2303, -3.7001, -1.8804, 2.6485, -2.4319, -2.56, 1.2123, -2.2074, -2.6786, 1.5835, -3.611, -1.9401, 2.2303, -3.7001, -1.8804, 3.3947, 0.8197, -1.419, 3.6836, 2.3906, 0.1427, 3.6143, 2.6541, -0.7787, 3.3947, 0.8197, -1.419, 3.39, -0.1345, -0.5433, 3.6836, 2.3906, 0.1427, -2.1314, 0.7603, 3.0046, -2.7222, 1.5834, 1.3251, -2.0691, 2.5964, 1.5185, -2.1314, 0.7603, 3.0046, -2.9289, -0.1232, 2.5316, -2.7222, 1.5834, 1.3251, 2.9911, 0.177, -2.5055, 2.6485, -2.4319, -2.56, 3.4189, -1.5098, -2.1519, 2.9911, 0.177, -2.5055, 2.1308, -0.8122, -3.0291, 2.6485, -2.4319, -2.56, -2.7435, 3.293, -0.5137, -2.8949, 1.7465, -1.4628, -2.7186, 2.9035, -1.3821, -2.7435, 3.293, -0.5137, -2.9635, 2.0631, -0.0469, -2.8949, 1.7465, -1.4628, -0.1459, 3.4745, -2.3857, 1.1494, 2.3687, -2.734, 0.549, 3.3793, -2.5003, -0.1459, 3.4745, -2.3857, -1.2285, 2.4662, -2.4325, 1.1494, 2.3687, -2.734, 2.3543, 3.9299, -1.7328, 3.6143, 2.6541, -0.7787, 3.3017, 4.3689, -0.5397, 2.3543, 3.9299, -1.7328, 3.0814, 2.2747, -1.5878, 3.6143, 2.6541, -0.7787, 1.5686, 6.857, -0.4389, 0.9325, 6.924, 0.4365, 0.7824, 7.1818, -0.4789, 1.5686, 6.857, -0.4389, 2.3528, 6.0999, 0.4838, 0.9325, 6.924, 0.4365, -1.544, 3.8438, -1.471, -2.7186, 2.9035, -1.3821, -2.3777, 2.1645, -2.0413, -1.544, 3.8438, -1.471, -2.3162, 3.6228, -0.9721, -2.7186, 2.9035, -1.3821, 1.4379, 5.2295, -1.8152, 0.549, 3.3793, -2.5003, 1.1494, 2.3687, -2.734, 1.4379, 5.2295, -1.8152, 0.4374, 4.5707, -2.224, 0.549, 3.3793, -2.5003, 2.1556, -2.2057, 2.3132, 1.5121, -3.4321, 1.801, 0.6607, -2.1929, 2.7904, 2.1556, -2.2057, 2.3132, 2.332, -3.2513, 1.4491, 1.5121, -3.4321, 1.801, 3.4106, -2.2767, -1.2285, 2.4159, -3.3908, 0.0683, 3.0799, -1.877, -0.2485, 3.4106, -2.2767, -1.2285, 2.6374, -3.7219, -1.0723, 2.4159, -3.3908, 0.0683, 0.1584, -6.6788, -0.712, 1.5973, -4.4989, -0.523, 0.8528, -5.1659, -1.2387, 0.1584, -6.6788, -0.712, 0.3707, -6.5026, 0.2358, 1.5973, -4.4989, -0.523, -3.2503, -1.9438, -0.0444, -3.0733, -4.4498, -0.8737, -3.1353, -2.4738, -1.533, -3.2503, -1.9438, -0.0444, -3.2063, -3.9919, -0.0118, -3.0733, -4.4498, -0.8737, -0.5191, -2.1018, -2.9939, -0.0313, -3.8492, -2.3572, 1.2123, -2.2074, -2.6786, -0.5191, -2.1018, -2.9939, -0.9004, -3.6647, -2.5803, -0.0313, -3.8492, -2.3572, 3.4189, -1.5098, -2.1519, 3.39, -0.1345, -0.5433, 3.3947, 0.8197, -1.419, 3.4189, -1.5098, -2.1519, 3.4106, -2.2767, -1.2285, 3.39, -0.1345, -0.5433, 2.7182, -1.9153, 1.6333, 1.8364, -0.871, 2.91, 2.5729, -0.0463, 2.323, 2.7182, -1.9153, 1.6333, 2.1556, -2.2057, 2.3132, 1.8364, -0.871, 2.91, -2.3777, 2.1645, -2.0413, -2.8218, -0.0908, -2.3316, -2.0395, 0.8474, -2.9039, -2.3777, 2.1645, -2.0413, -2.8949, 1.7465, -1.4628, -2.8218, -0.0908, -2.3316, 3.481, 4.5391, 0.4718, 3.2927, 2.696, 1.0915, 2.8818, 4.7444, 1.3483, 3.481, 4.5391, 0.4718, 3.6836, 2.3906, 0.1427, 3.2927, 2.696, 1.0915, 0.6919, 3.2703, 2.2188, -1.0522, 1.986, 2.2721, -0.2637, 3.5029, 1.8153, 0.6919, 3.2703, 2.2188, 0.5064, 1.8556, 2.7003, -1.0522, 1.986, 2.2721, -2.0691, 2.5964, 1.5185, -2.9635, 2.0631, -0.0469, -2.3926, 3.3478, 0.3255, -2.0691, 2.5964, 1.5185, -2.7222, 1.5834, 1.3251, -2.9635, 2.0631, -0.0469, -0.2548, 6.3653, 0.3115, 1.4341, 6.0081, 1.2447, 0.4487, 4.8708, 1.6514, -0.2548, 6.3653, 0.3115, 0.9325, 6.924, 0.4365, 1.4341, 6.0081, 1.2447, 3.3017, 4.3689, -0.5397, 2.3528, 6.0999, 0.4838, 2.4165, 5.7335, -0.6508, 3.3017, 4.3689, -0.5397, 3.481, 4.5391, 0.4718, 2.3528, 6.0999, 0.4838, -0.2609, 6.3493, -0.8464, -1.137, 5.0901, -0.2048, -1.544, 3.8438, -1.471, -0.2609, 6.3493, -0.8464, -0.2548, 6.3653, 0.3115, -1.137, 5.0901, -0.2048, 0.7824, 7.1818, -0.4789, -0.1803, 5.4522, -1.6664, 0.9515, 6.8097, -1.0683, 0.7824, 7.1818, -0.4789, -0.2609, 6.3493, -0.8464, -0.1803, 5.4522, -1.6664, 2.4165, 5.7335, -0.6508, 0.9515, 6.8097, -1.0683, 1.4379, 5.2295, -1.8152, 2.4165, 5.7335, -0.6508, 1.5686, 6.857, -0.4389, 0.9515, 6.8097, -1.0683, 0.4374, 4.5707, -2.224, -1.2285, 2.4662, -2.4325, -0.1459, 3.4745, -2.3857, 0.4374, 4.5707, -2.224, -0.1803, 5.4522, -1.6664, -1.2285, 2.4662, -2.4325, -1.8973, 4.0438, -0.0231, -0.7059, 4.7856, 0.8682, -1.6818, 3.3981, 1.0198, -1.8973, 4.0438, -0.0231, -1.137, 5.0901, -0.2048, -0.7059, 4.7856, 0.8682, -2.3162, 3.6228, -0.9721, -2.3926, 3.3478, 0.3255, -2.7435, 3.293, -0.5137, -2.3162, 3.6228, -0.9721, -1.8973, 4.0438, -0.0231, -2.3926, 3.3478, 0.3255, -1.6818, 3.3981, 1.0198, -0.2637, 3.5029, 1.8153, -1.0522, 1.986, 2.2721, -1.6818, 3.3981, 1.0198, -0.7059, 4.7856, 0.8682, -0.2637, 3.5029, 1.8153, 0.4487, 4.8708, 1.6514, 2.8818, 4.7444, 1.3483, 2.2766, 3.3307, 2.1367, 0.4487, 4.8708, 1.6514, 1.4341, 6.0081, 1.2447, 2.8818, 4.7444, 1.3483, 3.0814, 2.2747, -1.5878, 2.0681, 1.0413, -2.9849, 2.9911, 0.177, -2.5055, 3.0814, 2.2747, -1.5878, 2.3543, 3.9299, -1.7328, 2.0681, 1.0413, -2.9849, 0.0444, -0.8451, -3.3989, -2.0395, 0.8474, -2.9039, -2.0489, -0.9812, -3.014, 0.0444, -0.8451, -3.3989, 0.0399, 0.8971, -3.3453, -2.0395, 0.8474, -2.9039, -2.2391, -1.0013, 3.2378, 0.0817, 0.7564, 3.232, -0.0319, -0.907, 3.3884, -2.2391, -1.0013, 3.2378, -2.1314, 0.7603, 3.0046, 0.0817, 0.7564, 3.232, 2.5729, -0.0463, 2.323, 2.2766, 3.3307, 2.1367, 2.8449, 1.8063, 1.8788, 2.5729, -0.0463, 2.323, 1.9677, 0.877, 2.8315, 2.2766, 3.3307, 2.1367, 2.8449, 1.8063, 1.8788, 3.0694, 0.3768, 0.2917, 2.6862, -0.2733, 1.344, 2.8449, 1.8063, 1.8788, 3.2927, 2.696, 1.0915, 3.0694, 0.3768, 0.2917, 2.1308, -0.8122, -3.0291, 0.0399, 0.8971, -3.3453, 0.0444, -0.8451, -3.3989, 2.1308, -0.8122, -3.0291, 2.0681, 1.0413, -2.9849, 0.0399, 0.8971, -3.3453, -2.0489, -0.9812, -3.014, -3.1353, -2.4738, -1.533, -2.3373, -3.6333, -2.2624, -2.0489, -0.9812, -3.014, -2.8218, -0.0908, -2.3316, -3.1353, -2.4738, -1.533, -2.9289, -0.1232, 2.5316, -2.3213, -3.5933, 2.2613, -3.048, -2.4163, 1.5053, -2.9289, -0.1232, 2.5316, -2.2391, -1.0013, 3.2378, -2.3213, -3.5933, 2.2613, -0.0319, -0.907, 3.3884, 1.9677, 0.877, 2.8315, 1.8364, -0.871, 2.91, -0.0319, -0.907, 3.3884, 0.0817, 0.7564, 3.232, 1.9677, 0.877, 2.8315, 2.6862, -0.2733, 1.344, 3.0799, -1.877, -0.2485, 2.791, -2.0828, 0.6971, 2.6862, -0.2733, 1.344, 3.0694, 0.3768, 0.2917, 3.0799, -1.877, -0.2485, 0.8528, -5.1659, -1.2387, 1.5835, -3.611, -1.9401, 0.5989, -4.5033, -1.836, 0.8528, -5.1659, -1.2387, 1.6723, -4.1113, -1.5062, 1.5835, -3.611, -1.9401, 0.5989, -4.5033, -1.836, -0.9423, -5.2303, -2.2174, -0.129, -6.0155, -1.6627, 0.5989, -4.5033, -1.836, -0.0313, -3.8492, -2.3572, -0.9423, -5.2303, -2.2174, -1.2211, -6.6432, -1.3733, -2.3373, -3.6333, -2.2624, -2.3738, -5.4707, -1.3892, -1.2211, -6.6432, -1.3733, -0.9423, -5.2303, -2.2174, -2.3373, -3.6333, -2.2624, -2.3738, -5.4707, -1.3892, -2.8602, -5.2902, -0.0034, -1.8677, -6.7222, 0.0053, -2.3738, -5.4707, -1.3892, -3.0733, -4.4498, -0.8737, -2.8602, -5.2902, -0.0034, -1.8677, -6.7222, 0.0053, -3.0246, -4.4027, 0.8468, -2.3434, -5.4566, 1.3758, -1.8677, -6.7222, 0.0053, -2.8602, -5.2902, -0.0034, -3.0246, -4.4027, 0.8468, -2.3434, -5.4566, 1.3758, -0.9484, -5.3598, 2.2535, -1.2352, -6.7568, 1.4079, -2.3434, -5.4566, 1.3758, -2.3213, -3.5933, 2.2613, -0.9484, -5.3598, 2.2535, -0.1504, -6.0468, 1.6347, -0.1302, -3.9225, 2.4564, 0.6052, -4.4095, 1.8566, -0.1504, -6.0468, 1.6347, -0.9484, -5.3598, 2.2535, -0.1302, -3.9225, 2.4564, 0.6052, -4.4095, 1.8566, 1.7532, -3.8773, 1.1862, 0.8249, -4.8431, 1.2373, 0.6052, -4.4095, 1.8566, 1.5121, -3.4321, 1.801, 1.7532, -3.8773, 1.1862, -0.6892, -7.3327, -0.0456, -1.8677, -6.7222, 0.0053, -1.2352, -6.7568, 1.4079)\n\n[node name=\"RockC\" instance=ExtResource(\"1_pb27A\")]\ncollision_mask = 3\n\n[node name=\"Rock3\" parent=\".\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"2_pcex8\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\".\" index=\"1\"]\nshape = SubResource(\"ConcavePolygonShape3D_ycsjC\")\n"
  },
  {
    "path": "project/demo/assets/models/Tunnel.glb.import",
    "content": "[remap]\n\nimporter=\"scene\"\nimporter_version=1\ntype=\"PackedScene\"\nuid=\"uid://jib546bbort5\"\npath=\"res://.godot/imported/Tunnel.glb-09729773791c3078e933c866961e1b60.scn\"\n\n[deps]\n\nsource_file=\"res://demo/assets/models/Tunnel.glb\"\ndest_files=[\"res://.godot/imported/Tunnel.glb-09729773791c3078e933c866961e1b60.scn\"]\n\n[params]\n\nnodes/root_type=\"StaticBody3D\"\nnodes/root_name=\"Scene Root\"\nnodes/apply_root_scale=true\nnodes/root_scale=1.0\nnodes/import_as_skeleton_bones=false\nnodes/use_node_type_suffixes=true\nmeshes/ensure_tangents=true\nmeshes/generate_lods=true\nmeshes/create_shadow_meshes=true\nmeshes/light_baking=1\nmeshes/lightmap_texel_size=0.2\nmeshes/force_disable_compression=true\nskins/use_named_skins=true\nanimation/import=true\nanimation/fps=30\nanimation/trimming=false\nanimation/remove_immutable_tracks=true\nanimation/import_rest_as_RESET=false\nimport_script/path=\"\"\n_subresources={\n\"materials\": {\n\"material_0\": {\n\"use_external/enabled\": false,\n\"use_external/path\": \"uid://nbbdrx8vma80\"\n}\n}\n}\ngltf/naming_version=0\ngltf/embedded_image_handling=1\n"
  },
  {
    "path": "project/demo/assets/models/Tunnel.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://vvayjv3rbx1d\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://jib546bbort5\" path=\"res://demo/assets/models/Tunnel.glb\" id=\"1_1sr0i\"]\n[ext_resource type=\"Material\" uid=\"uid://nbbdrx8vma80\" path=\"res://demo/assets/materials/M_rock23_tp.tres\" id=\"2_lkb1r\"]\n\n[sub_resource type=\"ConcavePolygonShape3D\" id=\"ConcavePolygonShape3D_bh6xx\"]\ndata = PackedVector3Array(266.67, 5.3424, -18.2649, 266.427, 5.495, -16.0799, 266.642, 5.495, -18.2666, 267.292, 1.9054, -18.2263, 267.068, 1.9054, -15.9524, 266.67, 5.3424, -18.2649, 267.068, 1.9054, -15.9524, 266.427, 5.495, -16.0799, 266.67, 5.3424, -18.2649, 264.802, 8.7484, -18.3807, 264.54, 8.8761, -16.4552, 264.73, 8.8761, -18.3851, 266.642, 5.495, -18.2666, 266.427, 5.495, -16.0799, 264.802, 8.7484, -18.3807, 266.427, 5.495, -16.0799, 264.54, 8.8761, -16.4552, 264.802, 8.7484, -18.3807, 261.76, 11.7626, -18.5693, 261.518, 11.852, -17.0563, 261.668, 11.852, -18.575, 264.73, 8.8761, -18.3851, 264.54, 8.8761, -16.4552, 261.76, 11.7626, -18.5693, 264.54, 8.8761, -16.4552, 261.518, 11.852, -17.0563, 261.76, 11.7626, -18.5693, 257.712, 14.2028, -18.8203, 257.536, 14.2498, -17.8483, 257.633, 14.2498, -18.8252, 261.668, 11.852, -18.575, 261.518, 11.852, -17.0563, 257.712, 14.2028, -18.8203, 261.518, 11.852, -17.0563, 257.536, 14.2498, -17.8483, 257.712, 14.2028, -18.8203, 252.892, 15.9187, -19.1192, 252.826, 15.9302, -18.7853, 252.859, 15.9302, -19.1212, 257.633, 14.2498, -18.8252, 257.536, 14.2498, -17.8483, 252.892, 15.9187, -19.1192, 257.536, 14.2498, -17.8483, 252.826, 15.9302, -18.7853, 252.892, 15.9187, -19.1192, 252.859, 15.9302, -19.1212, 252.826, 15.9302, -18.7853, 250.357, 16.3438, -19.2763, 252.823, -12.1253, -19.1234, 252.826, -12.1193, -18.7853, 252.859, -12.1193, -19.1212, 250.357, -12.5329, -19.2763, 252.826, -12.1193, -18.7853, 252.823, -12.1253, -19.1234, 257.538, -10.4722, -18.8311, 257.536, -10.4389, -17.8483, 257.633, -10.4389, -18.8252, 252.859, -12.1193, -19.1212, 252.826, -12.1193, -18.7853, 257.538, -10.4722, -18.8311, 252.826, -12.1193, -18.7853, 257.536, -10.4389, -17.8483, 257.538, -10.4722, -18.8311, 261.546, -8.1137, -18.5826, 261.518, -8.0411, -17.0563, 261.668, -8.0411, -18.575, 257.633, -10.4389, -18.8252, 257.536, -10.4389, -17.8483, 261.546, -8.1137, -18.5826, 257.536, -10.4389, -17.8483, 261.518, -8.0411, -17.0563, 261.546, -8.1137, -18.5826, 264.614, -5.1782, -18.3923, 264.54, -5.0652, -16.4552, 264.73, -5.0652, -18.3851, 261.668, -8.0411, -18.575, 261.518, -8.0411, -17.0563, 264.614, -5.1782, -18.3923, 261.518, -8.0411, -17.0563, 264.54, -5.0652, -16.4552, 264.614, -5.1782, -18.3923, 266.56, -1.8284, -18.2717, 266.427, -1.6842, -16.0799, 266.642, -1.6842, -18.2666, 264.73, -5.0652, -18.3851, 264.54, -5.0652, -16.4552, 266.56, -1.8284, -18.2717, 264.54, -5.0652, -16.4552, 266.427, -1.6842, -16.0799, 266.56, -1.8284, -18.2717, 267.263, 1.7469, -18.2281, 267.068, 1.9054, -15.9524, 267.292, 1.9054, -18.2263, 266.642, -1.6842, -18.2666, 266.427, -1.6842, -16.0799, 267.263, 1.7469, -18.2281, 266.427, -1.6842, -16.0799, 267.068, 1.9054, -15.9524, 267.263, 1.7469, -18.2281, 267.068, 1.9054, -15.9524, 251.436, 5.495, 33.3375, 266.427, 5.495, -16.0799, 252.04, 1.9054, 33.5877, 251.436, 5.495, 33.3375, 267.068, 1.9054, -15.9524, 266.427, 5.495, -16.0799, 249.659, 8.876, 32.6014, 264.54, 8.8761, -16.4552, 251.436, 5.495, 33.3375, 249.659, 8.876, 32.6014, 266.427, 5.495, -16.0799, 264.54, 8.8761, -16.4552, 246.812, 11.8519, 31.4223, 261.518, 11.852, -17.0563, 249.659, 8.876, 32.6014, 246.812, 11.8519, 31.4223, 264.54, 8.8761, -16.4552, 261.518, 11.852, -17.0563, 243.061, 14.2498, 29.8687, 257.536, 14.2498, -17.8483, 246.812, 11.8519, 31.4223, 243.061, 14.2498, 29.8687, 261.518, 11.852, -17.0563, 257.536, 14.2498, -17.8483, 238.624, 15.9302, 28.0308, 252.826, 15.9302, -18.7853, 243.061, 14.2498, 29.8687, 238.624, 15.9302, 28.0308, 257.536, 14.2498, -17.8483, 250.357, 16.3438, -19.2763, 252.826, 15.9302, -18.7853, 247.551, 16.7956, -19.4503, 252.826, 15.9302, -18.7853, 233.759, 16.7956, 26.0154, 247.551, 16.7956, -19.4503, 238.624, 15.9302, 28.0308, 233.759, 16.7956, 26.0154, 252.826, 15.9302, -18.7853, 247.505, 16.7956, -19.4531, 228.748, 16.7956, 23.9398, 242.014, 16.7956, -19.7936, 247.551, 16.7956, -19.4503, 233.759, 16.7956, 26.0154, 247.505, 16.7956, -19.4531, 233.759, 16.7956, 26.0154, 228.748, 16.7956, 23.9398, 247.505, 16.7956, -19.4531, 241.88, 16.774, -19.8019, 223.882, 15.9302, 21.9244, 236.638, 15.9302, -20.1269, 242.014, 16.7956, -19.7936, 228.748, 16.7956, 23.9398, 241.88, 16.774, -19.8019, 228.748, 16.7956, 23.9398, 223.882, 15.9302, 21.9244, 241.88, 16.774, -19.8019, 236.433, 15.8598, -20.1396, 219.445, 14.2498, 20.0865, 231.736, 14.2498, -20.4309, 236.638, 15.9302, -20.1269, 223.882, 15.9302, 21.9244, 236.433, 15.8598, -20.1396, 223.882, 15.9302, 21.9244, 219.445, 14.2498, 20.0865, 236.433, 15.8598, -20.1396, 231.497, 14.1113, -20.4457, 215.694, 11.8519, 18.5328, 227.592, 11.8519, -20.6878, 231.736, 14.2498, -20.4309, 219.445, 14.2498, 20.0865, 231.497, 14.1113, -20.4457, 219.445, 14.2498, 20.0865, 215.694, 11.8519, 18.5328, 231.497, 14.1113, -20.4457, 227.367, 11.6395, -20.7018, 212.848, 8.876, 17.3537, 224.447, 8.876, -20.8828, 227.592, 11.8519, -20.6878, 215.694, 11.8519, 18.5328, 227.367, 11.6395, -20.7018, 215.694, 11.8519, 18.5328, 212.848, 8.876, 17.3537, 227.367, 11.6395, -20.7018, 224.286, 8.6, -20.8928, 211.071, 5.4951, 16.6177, 222.483, 5.4951, -21.0046, 224.447, 8.876, -20.8828, 212.848, 8.876, 17.3537, 224.286, 8.6, -20.8928, 212.848, 8.876, 17.3537, 211.071, 5.495, 16.6177, 224.286, 8.6, -20.8928, 222.425, 5.18, -21.0082, 210.467, 1.9054, 16.3675, 221.816, 1.9054, -21.046, 222.483, 5.4951, -21.0046, 211.071, 5.495, 16.6177, 222.425, 5.18, -21.0082, 211.071, 5.495, 16.6177, 210.467, 1.9054, 16.3675, 222.425, 5.18, -21.0082, 221.876, 1.5852, -21.0423, 211.071, -1.6842, 16.6177, 222.483, -1.6842, -21.0046, 221.816, 1.9054, -21.046, 210.467, 1.9054, 16.3675, 221.876, 1.5852, -21.0423, 210.467, 1.9054, 16.3675, 211.071, -1.6842, 16.6177, 221.876, 1.5852, -21.0423, 222.652, -1.975, -20.9941, 212.848, -5.0652, 17.3537, 224.447, -5.0652, -20.8828, 222.483, -1.6842, -21.0046, 211.071, -1.6842, 16.6177, 222.652, -1.975, -20.9941, 211.071, -1.6842, 16.6177, 212.848, -5.0652, 17.3537, 222.652, -1.975, -20.9941, 224.694, -5.2991, -20.8675, 215.694, -8.0411, 18.5329, 227.592, -8.0411, -20.6878, 224.447, -5.0652, -20.8828, 212.848, -5.0652, 17.3537, 224.694, -5.2991, -20.8675, 212.848, -5.0652, 17.3537, 215.694, -8.0411, 18.5329, 224.694, -5.2991, -20.8675, 227.872, -8.2033, -20.6705, 219.445, -10.4389, 20.0865, 231.736, -10.4389, -20.4309, 227.592, -8.0411, -20.6878, 215.694, -8.0411, 18.5329, 227.872, -8.2033, -20.6705, 215.694, -8.0411, 18.5329, 219.445, -10.4389, 20.0865, 227.872, -8.2033, -20.6705, 232.001, -10.5298, -20.4144, 223.882, -12.1193, 21.9244, 236.638, -12.1193, -20.1269, 231.736, -10.4389, -20.4309, 219.445, -10.4389, 20.0865, 232.001, -10.5298, -20.4144, 219.445, -10.4389, 20.0865, 223.882, -12.1193, 21.9244, 232.001, -10.5298, -20.4144, 236.848, -12.153, -20.1139, 228.748, -12.9847, 23.9398, 242.014, -12.9847, -19.7936, 236.638, -12.1193, -20.1269, 223.882, -12.1193, 21.9244, 236.848, -12.153, -20.1139, 223.882, -12.1193, 21.9244, 228.748, -12.9847, 23.9398, 236.848, -12.153, -20.1139, 242.142, -12.9847, -19.7857, 233.759, -12.9847, 26.0154, 247.551, -12.9847, -19.4503, 242.014, -12.9847, -19.7936, 228.748, -12.9847, 23.9398, 242.142, -12.9847, -19.7857, 228.748, -12.9847, 23.9398, 233.759, -12.9847, 26.0154, 242.142, -12.9847, -19.7857, 247.592, -12.9781, -19.4478, 238.624, -12.1193, 28.0308, 252.826, -12.1193, -18.7853, 247.592, -12.9781, -19.4478, 252.826, -12.1193, -18.7853, 250.357, -12.5329, -19.2763, 247.551, -12.9847, -19.4503, 233.759, -12.9847, 26.0154, 247.592, -12.9781, -19.4478, 233.759, -12.9847, 26.0154, 238.624, -12.1193, 28.0308, 247.592, -12.9781, -19.4478, 252.826, -12.1193, -18.7853, 243.061, -10.4389, 29.8687, 257.536, -10.4389, -17.8483, 238.624, -12.1193, 28.0308, 243.061, -10.4389, 29.8687, 252.826, -12.1193, -18.7853, 257.536, -10.4389, -17.8483, 246.812, -8.0411, 31.4223, 261.518, -8.0411, -17.0563, 243.061, -10.4389, 29.8687, 246.812, -8.0411, 31.4223, 257.536, -10.4389, -17.8483, 261.518, -8.0411, -17.0563, 249.659, -5.0652, 32.6014, 264.54, -5.0652, -16.4552, 246.812, -8.0411, 31.4223, 249.659, -5.0652, 32.6014, 261.518, -8.0411, -17.0563, 264.54, -5.0652, -16.4552, 251.436, -1.6842, 33.3375, 266.427, -1.6842, -16.0799, 249.659, -5.0652, 32.6014, 251.436, -1.6842, 33.3375, 264.54, -5.0652, -16.4552, 266.427, -1.6842, -16.0799, 252.04, 1.9054, 33.5877, 267.068, 1.9054, -15.9524, 251.436, -1.6842, 33.3375, 252.04, 1.9054, 33.5877, 266.427, -1.6842, -16.0799, 252.04, 1.9054, 33.5877, 227.092, 5.495, 78.8808, 251.436, 5.495, 33.3375, 227.636, 1.9054, 79.2441, 227.092, 5.495, 78.8808, 252.04, 1.9054, 33.5877, 251.436, 5.495, 33.3375, 225.493, 8.876, 77.8123, 249.659, 8.876, 32.6014, 227.092, 5.495, 78.8808, 225.493, 8.876, 77.8123, 251.436, 5.495, 33.3375, 249.659, 8.876, 32.6014, 222.931, 11.8519, 76.1005, 246.812, 11.8519, 31.4223, 225.493, 8.876, 77.8123, 222.931, 11.8519, 76.1005, 249.659, 8.876, 32.6014, 246.812, 11.8519, 31.4223, 219.556, 14.2498, 73.8449, 243.061, 14.2498, 29.8687, 222.931, 11.8519, 76.1005, 219.556, 14.2498, 73.8449, 246.812, 11.8519, 31.4223, 243.061, 14.2498, 29.8687, 215.562, 15.9302, 71.1767, 238.624, 15.9302, 28.0308, 219.556, 14.2498, 73.8449, 215.562, 15.9302, 71.1767, 243.061, 14.2498, 29.8687, 238.624, 15.9302, 28.0308, 211.184, 16.7956, 68.2508, 233.759, 16.7956, 26.0154, 215.562, 15.9302, 71.1767, 211.184, 16.7956, 68.2508, 238.624, 15.9302, 28.0308, 233.759, 16.7956, 26.0154, 206.674, 16.7956, 65.2375, 228.748, 16.7956, 23.9398, 211.184, 16.7956, 68.2508, 206.674, 16.7956, 65.2375, 233.759, 16.7956, 26.0154, 228.748, 16.7956, 23.9398, 202.295, 15.9302, 62.3116, 223.882, 15.9302, 21.9244, 206.674, 16.7956, 65.2375, 202.295, 15.9302, 62.3116, 228.748, 16.7956, 23.9398, 223.882, 15.9302, 21.9244, 198.302, 14.2498, 59.6434, 219.445, 14.2498, 20.0865, 202.295, 15.9302, 62.3116, 198.302, 14.2498, 59.6434, 223.882, 15.9302, 21.9244, 219.445, 14.2498, 20.0865, 194.926, 11.8519, 57.3878, 215.694, 11.8519, 18.5328, 198.302, 14.2498, 59.6434, 194.926, 11.8519, 57.3878, 219.445, 14.2498, 20.0865, 215.694, 11.8519, 18.5328, 192.364, 8.876, 55.676, 212.848, 8.876, 17.3537, 194.926, 11.8519, 57.3878, 192.364, 8.876, 55.676, 215.694, 11.8519, 18.5328, 212.848, 8.876, 17.3537, 190.765, 5.495, 54.6075, 211.071, 5.495, 16.6177, 192.364, 8.876, 55.676, 190.765, 5.495, 54.6075, 212.848, 8.876, 17.3537, 211.071, 5.495, 16.6177, 190.221, 1.9054, 54.2442, 210.467, 1.9054, 16.3675, 190.765, 5.495, 54.6075, 190.221, 1.9054, 54.2442, 211.071, 5.495, 16.6177, 210.467, 1.9054, 16.3675, 190.765, -1.6842, 54.6075, 211.071, -1.6842, 16.6177, 190.221, 1.9054, 54.2442, 190.765, -1.6842, 54.6075, 210.467, 1.9054, 16.3675, 211.071, -1.6842, 16.6177, 192.364, -5.0652, 55.676, 212.848, -5.0652, 17.3537, 190.765, -1.6842, 54.6075, 192.364, -5.0652, 55.676, 211.071, -1.6842, 16.6177, 212.848, -5.0652, 17.3537, 194.926, -8.0411, 57.3878, 215.694, -8.0411, 18.5329, 192.364, -5.0652, 55.676, 194.926, -8.0411, 57.3878, 212.848, -5.0652, 17.3537, 215.694, -8.0411, 18.5329, 198.302, -10.4389, 59.6434, 219.445, -10.4389, 20.0865, 194.926, -8.0411, 57.3878, 198.302, -10.4389, 59.6434, 215.694, -8.0411, 18.5329, 219.445, -10.4389, 20.0865, 202.295, -12.1193, 62.3116, 223.882, -12.1193, 21.9244, 198.302, -10.4389, 59.6434, 202.295, -12.1193, 62.3116, 219.445, -10.4389, 20.0865, 223.882, -12.1193, 21.9244, 206.674, -12.9847, 65.2375, 228.748, -12.9847, 23.9398, 202.295, -12.1193, 62.3116, 206.674, -12.9847, 65.2375, 223.882, -12.1193, 21.9244, 228.748, -12.9847, 23.9398, 211.184, -12.9847, 68.2508, 233.759, -12.9847, 26.0154, 206.674, -12.9847, 65.2375, 211.184, -12.9847, 68.2508, 228.748, -12.9847, 23.9398, 233.759, -12.9847, 26.0154, 215.562, -12.1193, 71.1767, 238.624, -12.1193, 28.0308, 211.184, -12.9847, 68.2508, 215.562, -12.1193, 71.1767, 233.759, -12.9847, 26.0154, 238.624, -12.1193, 28.0308, 219.556, -10.4389, 73.8449, 243.061, -10.4389, 29.8687, 215.562, -12.1193, 71.1767, 219.556, -10.4389, 73.8449, 238.624, -12.1193, 28.0308, 243.061, -10.4389, 29.8687, 222.931, -8.0411, 76.1005, 246.812, -8.0411, 31.4223, 219.556, -10.4389, 73.8449, 222.931, -8.0411, 76.1005, 243.061, -10.4389, 29.8687, 246.812, -8.0411, 31.4223, 225.493, -5.0652, 77.8123, 249.659, -5.0652, 32.6014, 222.931, -8.0411, 76.1005, 225.493, -5.0652, 77.8123, 246.812, -8.0411, 31.4223, 249.659, -5.0652, 32.6014, 227.092, -1.6842, 78.8808, 251.436, -1.6842, 33.3375, 225.493, -5.0652, 77.8123, 227.092, -1.6842, 78.8808, 249.659, -5.0652, 32.6014, 251.436, -1.6842, 33.3375, 227.636, 1.9054, 79.2441, 252.04, 1.9054, 33.5877, 227.092, -1.6842, 78.8808, 227.636, 1.9054, 79.2441, 251.436, -1.6842, 33.3375, 227.636, 1.9054, 79.2441, 194.332, 5.495, 118.8, 227.092, 5.495, 78.8808, 194.794, 1.9054, 119.262, 194.332, 5.495, 118.8, 227.636, 1.9054, 79.2441, 227.092, 5.495, 78.8808, 192.972, 8.876, 117.44, 225.493, 8.876, 77.8123, 194.332, 5.495, 118.8, 192.972, 8.876, 117.44, 227.092, 5.495, 78.8808, 225.493, 8.876, 77.8123, 190.793, 11.8519, 115.261, 222.931, 11.8519, 76.1005, 192.972, 8.876, 117.44, 190.793, 11.8519, 115.261, 225.493, 8.876, 77.8123, 222.931, 11.8519, 76.1005, 187.922, 14.2498, 112.39, 219.556, 14.2498, 73.8449, 190.793, 11.8519, 115.261, 187.922, 14.2498, 112.39, 222.931, 11.8519, 76.1005, 219.556, 14.2498, 73.8449, 184.526, 15.9302, 108.994, 215.562, 15.9302, 71.1767, 187.922, 14.2498, 112.39, 184.526, 15.9302, 108.994, 219.556, 14.2498, 73.8449, 215.562, 15.9302, 71.1767, 180.802, 16.7956, 105.271, 211.184, 16.7956, 68.2508, 184.526, 15.9302, 108.994, 180.802, 16.7956, 105.271, 215.562, 15.9302, 71.1767, 211.184, 16.7956, 68.2508, 176.967, 16.7956, 101.435, 206.674, 16.7956, 65.2375, 180.802, 16.7956, 105.271, 176.967, 16.7956, 101.435, 211.184, 16.7956, 68.2508, 206.674, 16.7956, 65.2375, 173.243, 15.9302, 97.7113, 202.295, 15.9302, 62.3116, 176.967, 16.7956, 101.435, 173.243, 15.9302, 97.7113, 206.674, 16.7956, 65.2375, 202.295, 15.9302, 62.3116, 169.847, 14.2498, 94.3153, 198.302, 14.2498, 59.6434, 173.243, 15.9302, 97.7113, 169.847, 14.2498, 94.3153, 202.295, 15.9302, 62.3116, 198.302, 14.2498, 59.6434, 166.976, 11.8519, 91.4445, 194.926, 11.8519, 57.3878, 169.847, 14.2498, 94.3153, 166.976, 11.8519, 91.4445, 198.302, 14.2498, 59.6434, 194.926, 11.8519, 57.3878, 164.798, 8.876, 89.2658, 192.364, 8.876, 55.676, 166.976, 11.8519, 91.4445, 164.798, 8.876, 89.2658, 194.926, 11.8519, 57.3878, 192.364, 8.876, 55.676, 163.438, 5.495, 87.9057, 190.765, 5.495, 54.6075, 164.798, 8.876, 89.2658, 163.438, 5.495, 87.9057, 192.364, 8.876, 55.676, 190.765, 5.495, 54.6075, 162.975, 1.9054, 87.4435, 190.221, 1.9054, 54.2442, 163.438, 5.495, 87.9057, 162.975, 1.9054, 87.4435, 190.765, 5.495, 54.6075, 190.221, 1.9054, 54.2442, 163.438, -1.6842, 87.9057, 190.765, -1.6842, 54.6075, 162.975, 1.9054, 87.4435, 163.438, -1.6842, 87.9057, 190.221, 1.9054, 54.2442, 190.765, -1.6842, 54.6075, 164.798, -5.0652, 89.2658, 192.364, -5.0652, 55.676, 163.438, -1.6842, 87.9057, 164.798, -5.0652, 89.2658, 190.765, -1.6842, 54.6075, 192.364, -5.0652, 55.676, 166.976, -8.0411, 91.4445, 194.926, -8.0411, 57.3878, 164.798, -5.0652, 89.2658, 166.976, -8.0411, 91.4445, 192.364, -5.0652, 55.676, 194.926, -8.0411, 57.3878, 169.847, -10.4389, 94.3153, 198.302, -10.4389, 59.6434, 166.976, -8.0411, 91.4445, 169.847, -10.4389, 94.3153, 194.926, -8.0411, 57.3878, 198.302, -10.4389, 59.6434, 173.243, -12.1193, 97.7113, 202.295, -12.1193, 62.3116, 169.847, -10.4389, 94.3153, 173.243, -12.1193, 97.7113, 198.302, -10.4389, 59.6434, 202.295, -12.1193, 62.3116, 176.967, -12.9847, 101.435, 206.674, -12.9847, 65.2375, 173.243, -12.1193, 97.7113, 176.967, -12.9847, 101.435, 202.295, -12.1193, 62.3116, 206.674, -12.9847, 65.2375, 180.802, -12.9847, 105.271, 211.184, -12.9847, 68.2508, 176.967, -12.9847, 101.435, 180.802, -12.9847, 105.271, 206.674, -12.9847, 65.2375, 211.184, -12.9847, 68.2508, 184.526, -12.1193, 108.994, 215.562, -12.1193, 71.1767, 180.802, -12.9847, 105.271, 184.526, -12.1193, 108.994, 211.184, -12.9847, 68.2508, 215.562, -12.1193, 71.1767, 187.922, -10.4389, 112.39, 219.556, -10.4389, 73.8449, 184.526, -12.1193, 108.994, 187.922, -10.4389, 112.39, 215.562, -12.1193, 71.1767, 219.556, -10.4389, 73.8449, 190.793, -8.0411, 115.261, 222.931, -8.0411, 76.1005, 187.922, -10.4389, 112.39, 190.793, -8.0411, 115.261, 219.556, -10.4389, 73.8449, 222.931, -8.0411, 76.1005, 192.972, -5.0652, 117.44, 225.493, -5.0652, 77.8123, 190.793, -8.0411, 115.261, 192.972, -5.0652, 117.44, 222.931, -8.0411, 76.1005, 225.493, -5.0652, 77.8123, 194.332, -1.6842, 118.8, 227.092, -1.6842, 78.8808, 192.972, -5.0652, 117.44, 194.332, -1.6842, 118.8, 225.493, -5.0652, 77.8123, 227.092, -1.6842, 78.8808, 194.794, 1.9054, 119.262, 227.636, 1.9054, 79.2441, 194.332, -1.6842, 118.8, 194.794, 1.9054, 119.262, 227.092, -1.6842, 78.8808, 194.794, 1.9054, 119.262, 154.413, 5.495, 151.561, 194.332, 5.495, 118.8, 154.776, 1.9054, 152.104, 154.413, 5.495, 151.561, 194.794, 1.9054, 119.262, 194.332, 5.495, 118.8, 153.344, 8.876, 149.961, 192.972, 8.876, 117.44, 154.413, 5.495, 151.561, 153.344, 8.876, 149.961, 194.332, 5.495, 118.8, 192.972, 8.876, 117.44, 151.632, 11.8519, 147.399, 190.793, 11.8519, 115.261, 153.344, 8.876, 149.961, 151.632, 11.8519, 147.399, 192.972, 8.876, 117.44, 190.793, 11.8519, 115.261, 149.377, 14.2498, 144.024, 187.922, 14.2498, 112.39, 151.632, 11.8519, 147.399, 149.377, 14.2498, 144.024, 190.793, 11.8519, 115.261, 187.922, 14.2498, 112.39, 146.709, 15.9302, 140.031, 184.526, 15.9302, 108.994, 149.377, 14.2498, 144.024, 146.709, 15.9302, 140.031, 187.922, 14.2498, 112.39, 184.526, 15.9302, 108.994, 143.783, 16.7956, 135.652, 180.802, 16.7956, 105.271, 146.709, 15.9302, 140.031, 143.783, 16.7956, 135.652, 184.526, 15.9302, 108.994, 180.802, 16.7956, 105.271, 140.769, 16.7956, 131.142, 176.967, 16.7956, 101.435, 143.783, 16.7956, 135.652, 140.769, 16.7956, 131.142, 180.802, 16.7956, 105.271, 176.967, 16.7956, 101.435, 137.843, 15.9302, 126.763, 173.243, 15.9302, 97.7113, 140.769, 16.7956, 131.142, 137.843, 15.9302, 126.763, 176.967, 16.7956, 101.435, 173.243, 15.9302, 97.7113, 135.175, 14.2498, 122.77, 169.847, 14.2498, 94.3153, 137.843, 15.9302, 126.763, 135.175, 14.2498, 122.77, 173.243, 15.9302, 97.7113, 169.847, 14.2498, 94.3153, 132.92, 11.8519, 119.394, 166.976, 11.8519, 91.4445, 135.175, 14.2498, 122.77, 132.92, 11.8519, 119.394, 169.847, 14.2498, 94.3153, 166.976, 11.8519, 91.4445, 131.208, 8.876, 116.832, 164.798, 8.876, 89.2658, 132.92, 11.8519, 119.394, 131.208, 8.876, 116.832, 166.976, 11.8519, 91.4445, 164.798, 8.876, 89.2658, 130.139, 5.495, 115.233, 163.438, 5.495, 87.9057, 131.208, 8.876, 116.832, 130.139, 5.495, 115.233, 164.798, 8.876, 89.2658, 163.438, 5.495, 87.9057, 129.776, 1.9054, 114.689, 162.975, 1.9054, 87.4435, 130.139, 5.495, 115.233, 129.776, 1.9054, 114.689, 163.438, 5.495, 87.9057, 162.975, 1.9054, 87.4435, 130.139, -1.6842, 115.233, 163.438, -1.6842, 87.9057, 129.776, 1.9054, 114.689, 130.139, -1.6842, 115.233, 162.975, 1.9054, 87.4435, 163.438, -1.6842, 87.9057, 131.208, -5.0652, 116.832, 164.798, -5.0652, 89.2658, 130.139, -1.6842, 115.233, 131.208, -5.0652, 116.832, 163.438, -1.6842, 87.9057, 164.798, -5.0652, 89.2658, 132.92, -8.0411, 119.394, 166.976, -8.0411, 91.4445, 131.208, -5.0652, 116.832, 132.92, -8.0411, 119.394, 164.798, -5.0652, 89.2658, 166.976, -8.0411, 91.4445, 135.175, -10.4389, 122.77, 169.847, -10.4389, 94.3153, 132.92, -8.0411, 119.394, 135.175, -10.4389, 122.77, 166.976, -8.0411, 91.4445, 169.847, -10.4389, 94.3153, 137.843, -12.1193, 126.763, 173.243, -12.1193, 97.7113, 135.175, -10.4389, 122.77, 137.843, -12.1193, 126.763, 169.847, -10.4389, 94.3153, 173.243, -12.1193, 97.7113, 140.769, -12.9847, 131.142, 176.967, -12.9847, 101.435, 137.843, -12.1193, 126.763, 140.769, -12.9847, 131.142, 173.243, -12.1193, 97.7113, 176.967, -12.9847, 101.435, 143.783, -12.9847, 135.652, 180.802, -12.9847, 105.271, 140.769, -12.9847, 131.142, 143.783, -12.9847, 135.652, 176.967, -12.9847, 101.435, 180.802, -12.9847, 105.271, 146.709, -12.1193, 140.031, 184.526, -12.1193, 108.994, 143.783, -12.9847, 135.652, 146.709, -12.1193, 140.031, 180.802, -12.9847, 105.271, 184.526, -12.1193, 108.994, 149.377, -10.4389, 144.024, 187.922, -10.4389, 112.39, 146.709, -12.1193, 140.031, 149.377, -10.4389, 144.024, 184.526, -12.1193, 108.994, 187.922, -10.4389, 112.39, 151.632, -8.0411, 147.399, 190.793, -8.0411, 115.261, 149.377, -10.4389, 144.024, 151.632, -8.0411, 147.399, 187.922, -10.4389, 112.39, 190.793, -8.0411, 115.261, 153.344, -5.0652, 149.961, 192.972, -5.0652, 117.44, 151.632, -8.0411, 147.399, 153.344, -5.0652, 149.961, 190.793, -8.0411, 115.261, 192.972, -5.0652, 117.44, 154.413, -1.6842, 151.561, 194.332, -1.6842, 118.8, 153.344, -5.0652, 149.961, 154.413, -1.6842, 151.561, 192.972, -5.0652, 117.44, 194.332, -1.6842, 118.8, 154.776, 1.9054, 152.104, 194.794, 1.9054, 119.262, 154.413, -1.6842, 151.561, 154.776, 1.9054, 152.104, 194.332, -1.6842, 118.8, 154.776, 1.9054, 152.104, 108.869, 5.495, 175.904, 154.413, 5.495, 151.561, 109.12, 1.9054, 176.508, 108.869, 5.495, 175.904, 154.776, 1.9054, 152.104, 154.413, 5.495, 151.561, 108.133, 8.876, 174.127, 153.344, 8.876, 149.961, 108.869, 5.495, 175.904, 108.133, 8.876, 174.127, 154.413, 5.495, 151.561, 153.344, 8.876, 149.961, 106.954, 11.8519, 171.281, 151.632, 11.8519, 147.399, 108.133, 8.876, 174.127, 106.954, 11.8519, 171.281, 153.344, 8.876, 149.961, 151.632, 11.8519, 147.399, 105.401, 14.2498, 167.53, 149.377, 14.2498, 144.024, 106.954, 11.8519, 171.281, 105.401, 14.2498, 167.53, 151.632, 11.8519, 147.399, 149.377, 14.2498, 144.024, 103.563, 15.9302, 163.092, 146.709, 15.9302, 140.031, 105.401, 14.2498, 167.53, 103.563, 15.9302, 163.092, 149.377, 14.2498, 144.024, 146.709, 15.9302, 140.031, 101.547, 16.7956, 158.227, 143.783, 16.7956, 135.652, 103.563, 15.9302, 163.092, 101.547, 16.7956, 158.227, 146.709, 15.9302, 140.031, 143.783, 16.7956, 135.652, 99.4717, 16.7956, 153.216, 140.769, 16.7956, 131.142, 101.547, 16.7956, 158.227, 99.4717, 16.7956, 153.216, 143.783, 16.7956, 135.652, 140.769, 16.7956, 131.142, 97.4563, 15.9302, 148.35, 137.843, 15.9302, 126.763, 99.4717, 16.7956, 153.216, 97.4563, 15.9302, 148.35, 140.769, 16.7956, 131.142, 137.843, 15.9302, 126.763, 95.6184, 14.2498, 143.913, 135.175, 14.2498, 122.77, 97.4563, 15.9302, 148.35, 95.6184, 14.2498, 143.913, 137.843, 15.9302, 126.763, 135.175, 14.2498, 122.77, 94.0647, 11.8519, 140.162, 132.92, 11.8519, 119.394, 95.6184, 14.2498, 143.913, 94.0647, 11.8519, 140.162, 135.175, 14.2498, 122.77, 132.92, 11.8519, 119.394, 92.8856, 8.876, 137.316, 131.208, 8.876, 116.832, 94.0647, 11.8519, 140.162, 92.8856, 8.876, 137.316, 132.92, 11.8519, 119.394, 131.208, 8.876, 116.832, 92.1496, 5.495, 135.539, 130.139, 5.495, 115.233, 92.8856, 8.876, 137.316, 92.1496, 5.495, 135.539, 131.208, 8.876, 116.832, 130.139, 5.495, 115.233, 91.8994, 1.9054, 134.935, 129.776, 1.9054, 114.689, 92.1496, 5.495, 135.539, 91.8994, 1.9054, 134.935, 130.139, 5.495, 115.233, 129.776, 1.9054, 114.689, 92.1496, -1.6842, 135.539, 130.139, -1.6842, 115.233, 91.8994, 1.9054, 134.935, 92.1496, -1.6842, 135.539, 129.776, 1.9054, 114.689, 130.139, -1.6842, 115.233, 92.8856, -5.0652, 137.316, 131.208, -5.0652, 116.832, 92.1496, -1.6842, 135.539, 92.8856, -5.0652, 137.316, 130.139, -1.6842, 115.233, 131.208, -5.0652, 116.832, 94.0647, -8.0411, 140.162, 132.92, -8.0411, 119.394, 92.8856, -5.0652, 137.316, 94.0647, -8.0411, 140.162, 131.208, -5.0652, 116.832, 132.92, -8.0411, 119.394, 95.6184, -10.4389, 143.913, 135.175, -10.4389, 122.77, 94.0647, -8.0411, 140.162, 95.6184, -10.4389, 143.913, 132.92, -8.0411, 119.394, 135.175, -10.4389, 122.77, 97.4563, -12.1193, 148.35, 137.843, -12.1193, 126.763, 95.6184, -10.4389, 143.913, 97.4563, -12.1193, 148.35, 135.175, -10.4389, 122.77, 137.843, -12.1193, 126.763, 99.4717, -12.9847, 153.216, 140.769, -12.9847, 131.142, 97.4563, -12.1193, 148.35, 99.4717, -12.9847, 153.216, 137.843, -12.1193, 126.763, 140.769, -12.9847, 131.142, 101.547, -12.9847, 158.227, 143.783, -12.9847, 135.652, 99.4717, -12.9847, 153.216, 101.547, -12.9847, 158.227, 140.769, -12.9847, 131.142, 143.783, -12.9847, 135.652, 103.563, -12.1193, 163.092, 146.709, -12.1193, 140.031, 101.547, -12.9847, 158.227, 103.563, -12.1193, 163.092, 143.783, -12.9847, 135.652, 146.709, -12.1193, 140.031, 105.401, -10.4389, 167.53, 149.377, -10.4389, 144.024, 103.563, -12.1193, 163.092, 105.401, -10.4389, 167.53, 146.709, -12.1193, 140.031, 149.377, -10.4389, 144.024, 106.954, -8.0411, 171.281, 151.632, -8.0411, 147.399, 105.401, -10.4389, 167.53, 106.954, -8.0411, 171.281, 149.377, -10.4389, 144.024, 151.632, -8.0411, 147.399, 108.133, -5.0652, 174.127, 153.344, -5.0652, 149.961, 106.954, -8.0411, 171.281, 108.133, -5.0652, 174.127, 151.632, -8.0411, 147.399, 153.344, -5.0652, 149.961, 108.869, -1.6842, 175.904, 154.413, -1.6842, 151.561, 108.133, -5.0652, 174.127, 108.869, -1.6842, 175.904, 153.344, -5.0652, 149.961, 154.413, -1.6842, 151.561, 109.12, 1.9054, 176.508, 154.776, 1.9054, 152.104, 108.869, -1.6842, 175.904, 109.12, 1.9054, 176.508, 154.413, -1.6842, 151.561, 109.12, 1.9054, 176.508, 59.452, 5.495, 190.895, 108.869, 5.495, 175.904, 59.5795, 1.9054, 191.536, 59.452, 5.495, 190.895, 109.12, 1.9054, 176.508, 108.869, 5.495, 175.904, 59.0767, 8.876, 189.008, 108.133, 8.876, 174.127, 59.452, 5.495, 190.895, 59.0767, 8.876, 189.008, 108.869, 5.495, 175.904, 108.133, 8.876, 174.127, 58.4756, 11.8519, 185.986, 106.954, 11.8519, 171.281, 59.0767, 8.876, 189.008, 58.4756, 11.8519, 185.986, 108.133, 8.876, 174.127, 106.954, 11.8519, 171.281, 57.6836, 14.2498, 182.004, 105.401, 14.2498, 167.53, 58.4756, 11.8519, 185.986, 57.6836, 14.2498, 182.004, 106.954, 11.8519, 171.281, 105.401, 14.2498, 167.53, 56.7466, 15.9302, 177.294, 103.563, 15.9302, 163.092, 57.6836, 14.2498, 182.004, 56.7466, 15.9302, 177.294, 105.401, 14.2498, 167.53, 103.563, 15.9302, 163.092, 55.7192, 16.7956, 172.129, 101.547, 16.7956, 158.227, 56.7466, 15.9302, 177.294, 55.7192, 16.7956, 172.129, 103.563, 15.9302, 163.092, 101.547, 16.7956, 158.227, 54.6611, 16.7956, 166.809, 99.4717, 16.7956, 153.216, 55.7192, 16.7956, 172.129, 54.6611, 16.7956, 166.809, 101.547, 16.7956, 158.227, 99.4717, 16.7956, 153.216, 53.6336, 15.9302, 161.644, 97.4563, 15.9302, 148.35, 54.6611, 16.7956, 166.809, 53.6336, 15.9302, 161.644, 99.4717, 16.7956, 153.216, 97.4563, 15.9302, 148.35, 52.6967, 14.2498, 156.934, 95.6184, 14.2498, 143.913, 53.6336, 15.9302, 161.644, 52.6967, 14.2498, 156.934, 97.4563, 15.9302, 148.35, 95.6184, 14.2498, 143.913, 51.9046, 11.8519, 152.952, 94.0647, 11.8519, 140.162, 52.6967, 14.2498, 156.934, 51.9046, 11.8519, 152.952, 95.6184, 14.2498, 143.913, 94.0647, 11.8519, 140.162, 51.3035, 8.876, 149.93, 92.8856, 8.876, 137.316, 51.9046, 11.8519, 152.952, 51.3035, 8.876, 149.93, 94.0647, 11.8519, 140.162, 92.8856, 8.876, 137.316, 50.9283, 5.495, 148.043, 92.1496, 5.495, 135.539, 51.3035, 8.876, 149.93, 50.9283, 5.495, 148.043, 92.8856, 8.876, 137.316, 92.1496, 5.495, 135.539, 50.8007, 1.9054, 147.402, 91.8994, 1.9054, 134.935, 50.9283, 5.495, 148.043, 50.8007, 1.9054, 147.402, 92.1496, 5.495, 135.539, 91.8994, 1.9054, 134.935, 50.9283, -1.6842, 148.043, 92.1496, -1.6842, 135.539, 50.8007, 1.9054, 147.402, 50.9283, -1.6842, 148.043, 91.8994, 1.9054, 134.935, 92.1496, -1.6842, 135.539, 51.3035, -5.0652, 149.93, 92.8856, -5.0652, 137.316, 50.9283, -1.6842, 148.043, 51.3035, -5.0652, 149.93, 92.1496, -1.6842, 135.539, 92.8856, -5.0652, 137.316, 51.9046, -8.0411, 152.952, 94.0647, -8.0411, 140.162, 51.3035, -5.0652, 149.93, 51.9046, -8.0411, 152.952, 92.8856, -5.0652, 137.316, 94.0647, -8.0411, 140.162, 52.6967, -10.4389, 156.934, 95.6184, -10.4389, 143.913, 51.9046, -8.0411, 152.952, 52.6967, -10.4389, 156.934, 94.0647, -8.0411, 140.162, 95.6184, -10.4389, 143.913, 53.6336, -12.1193, 161.644, 97.4563, -12.1193, 148.35, 52.6967, -10.4389, 156.934, 53.6336, -12.1193, 161.644, 95.6184, -10.4389, 143.913, 97.4563, -12.1193, 148.35, 54.6611, -12.9847, 166.809, 99.4717, -12.9847, 153.216, 53.6336, -12.1193, 161.644, 54.6611, -12.9847, 166.809, 97.4563, -12.1193, 148.35, 99.4717, -12.9847, 153.216, 55.7192, -12.9847, 172.129, 101.547, -12.9847, 158.227, 54.6611, -12.9847, 166.809, 55.7192, -12.9847, 172.129, 99.4717, -12.9847, 153.216, 101.547, -12.9847, 158.227, 56.7466, -12.1193, 177.294, 103.563, -12.1193, 163.092, 55.7192, -12.9847, 172.129, 56.7466, -12.1193, 177.294, 101.547, -12.9847, 158.227, 103.563, -12.1193, 163.092, 57.6836, -10.4389, 182.004, 105.401, -10.4389, 167.53, 56.7466, -12.1193, 177.294, 57.6836, -10.4389, 182.004, 103.563, -12.1193, 163.092, 105.401, -10.4389, 167.53, 58.4756, -8.0411, 185.986, 106.954, -8.0411, 171.281, 57.6836, -10.4389, 182.004, 58.4756, -8.0411, 185.986, 105.401, -10.4389, 167.53, 106.954, -8.0411, 171.281, 59.0767, -5.0652, 189.008, 108.133, -5.0652, 174.127, 58.4756, -8.0411, 185.986, 59.0767, -5.0652, 189.008, 106.954, -8.0411, 171.281, 108.133, -5.0652, 174.127, 59.452, -1.6842, 190.895, 108.869, -1.6842, 175.904, 59.0767, -5.0652, 189.008, 59.452, -1.6842, 190.895, 108.133, -5.0652, 174.127, 108.869, -1.6842, 175.904, 59.5795, 1.9054, 191.536, 109.12, 1.9054, 176.508, 59.452, -1.6842, 190.895, 59.5795, 1.9054, 191.536, 108.869, -1.6842, 175.904, 59.5795, 1.9054, 191.536, 8.0596, 5.495, 195.956, 59.452, 5.495, 190.895, 8.0596, 1.9054, 196.61, 8.0596, 5.495, 195.956, 59.5795, 1.9054, 191.536, 59.452, 5.495, 190.895, 8.0596, 8.876, 194.033, 59.0767, 8.876, 189.008, 8.0596, 5.495, 195.956, 8.0596, 8.876, 194.033, 59.452, 5.495, 190.895, 59.0767, 8.876, 189.008, 8.0596, 11.8519, 190.952, 58.4756, 11.8519, 185.986, 8.0596, 8.876, 194.033, 8.0596, 11.8519, 190.952, 59.0767, 8.876, 189.008, 58.4756, 11.8519, 185.986, 8.0596, 14.2498, 186.892, 57.6836, 14.2498, 182.004, 8.0596, 11.8519, 190.952, 8.0596, 14.2498, 186.892, 58.4756, 11.8519, 185.986, 57.6836, 14.2498, 182.004, 8.0596, 15.9302, 182.089, 56.7466, 15.9302, 177.294, 8.0596, 14.2498, 186.892, 8.0596, 15.9302, 182.089, 57.6836, 14.2498, 182.004, 56.7466, 15.9302, 177.294, 8.0596, 16.7956, 176.823, 55.7192, 16.7956, 172.129, 8.0596, 15.9302, 182.089, 8.0596, 16.7956, 176.823, 56.7466, 15.9302, 177.294, 55.7192, 16.7956, 172.129, 8.0596, 16.7956, 171.399, 54.6611, 16.7956, 166.809, 8.0596, 16.7956, 176.823, 8.0596, 16.7956, 171.399, 55.7192, 16.7956, 172.129, 54.6611, 16.7956, 166.809, 8.0596, 15.9302, 166.133, 53.6336, 15.9302, 161.644, 8.0596, 16.7956, 171.399, 8.0596, 15.9302, 166.133, 54.6611, 16.7956, 166.809, 53.6336, 15.9302, 161.644, 8.0596, 14.2498, 161.33, 52.6967, 14.2498, 156.934, 8.0596, 15.9302, 166.133, 8.0596, 14.2498, 161.33, 53.6336, 15.9302, 161.644, 52.6967, 14.2498, 156.934, 8.0596, 11.8519, 157.27, 51.9046, 11.8519, 152.952, 8.0596, 14.2498, 161.33, 8.0596, 11.8519, 157.27, 52.6967, 14.2498, 156.934, 51.9046, 11.8519, 152.952, 8.0596, 8.876, 154.189, 51.3035, 8.876, 149.93, 8.0596, 11.8519, 157.27, 8.0596, 8.876, 154.189, 51.9046, 11.8519, 152.952, 51.3035, 8.876, 149.93, 8.0596, 5.495, 152.266, 50.9283, 5.495, 148.043, 8.0596, 8.876, 154.189, 8.0596, 5.495, 152.266, 51.3035, 8.876, 149.93, 50.9283, 5.495, 148.043, 8.0596, 1.9054, 151.612, 50.8007, 1.9054, 147.402, 8.0596, 5.495, 152.266, 8.0596, 1.9054, 151.612, 50.9283, 5.495, 148.043, 50.8007, 1.9054, 147.402, 8.0596, -1.6842, 152.266, 50.9283, -1.6842, 148.043, 8.0596, 1.9054, 151.612, 8.0596, -1.6842, 152.266, 50.8007, 1.9054, 147.402, 50.9283, -1.6842, 148.043, 8.0596, -5.0652, 154.189, 51.3035, -5.0652, 149.93, 8.0596, -1.6842, 152.266, 8.0596, -5.0652, 154.189, 50.9283, -1.6842, 148.043, 51.3035, -5.0652, 149.93, 8.0596, -8.0411, 157.27, 51.9046, -8.0411, 152.952, 8.0596, -5.0652, 154.189, 8.0596, -8.0411, 157.27, 51.3035, -5.0652, 149.93, 51.9046, -8.0411, 152.952, 8.0596, -10.4389, 161.33, 52.6967, -10.4389, 156.934, 8.0596, -8.0411, 157.27, 8.0596, -10.4389, 161.33, 51.9046, -8.0411, 152.952, 52.6967, -10.4389, 156.934, 8.0596, -12.1193, 166.133, 53.6336, -12.1193, 161.644, 8.0596, -10.4389, 161.33, 8.0596, -12.1193, 166.133, 52.6967, -10.4389, 156.934, 53.6336, -12.1193, 161.644, 8.0596, -12.9847, 171.399, 54.6611, -12.9847, 166.809, 8.0596, -12.1193, 166.133, 8.0596, -12.9847, 171.399, 53.6336, -12.1193, 161.644, 54.6611, -12.9847, 166.809, 8.0596, -12.9847, 176.823, 55.7192, -12.9847, 172.129, 8.0596, -12.9847, 171.399, 8.0596, -12.9847, 176.823, 54.6611, -12.9847, 166.809, 55.7192, -12.9847, 172.129, 8.0596, -12.1193, 182.089, 56.7466, -12.1193, 177.294, 8.0596, -12.9847, 176.823, 8.0596, -12.1193, 182.089, 55.7192, -12.9847, 172.129, 56.7466, -12.1193, 177.294, 8.0596, -10.4389, 186.892, 57.6836, -10.4389, 182.004, 8.0596, -12.1193, 182.089, 8.0596, -10.4389, 186.892, 56.7466, -12.1193, 177.294, 57.6836, -10.4389, 182.004, 8.0596, -8.0411, 190.952, 58.4756, -8.0411, 185.986, 8.0596, -10.4389, 186.892, 8.0596, -8.0411, 190.952, 57.6836, -10.4389, 182.004, 58.4756, -8.0411, 185.986, 8.0596, -5.0652, 194.033, 59.0767, -5.0652, 189.008, 8.0596, -8.0411, 190.952, 8.0596, -5.0652, 194.033, 58.4756, -8.0411, 185.986, 59.0767, -5.0652, 189.008, 8.0596, -1.6842, 195.956, 59.452, -1.6842, 190.895, 8.0596, -5.0652, 194.033, 8.0596, -1.6842, 195.956, 59.0767, -5.0652, 189.008, 59.452, -1.6842, 190.895, 8.0596, 1.9054, 196.61, 59.5795, 1.9054, 191.536, 8.0596, -1.6842, 195.956, 8.0596, 1.9054, 196.61, 59.452, -1.6842, 190.895, 8.0596, 1.9054, 196.61, -43.3328, 5.495, 190.895, 8.0596, 5.495, 195.956, -43.4604, 1.9054, 191.536, -43.3328, 5.495, 190.895, 8.0596, 1.9054, 196.61, 8.0596, 5.495, 195.956, -42.9576, 8.876, 189.008, 8.0596, 8.876, 194.033, -43.3328, 5.495, 190.895, -42.9576, 8.876, 189.008, 8.0596, 5.495, 195.956, 8.0596, 8.876, 194.033, -42.3565, 11.8519, 185.986, 8.0596, 11.8519, 190.952, -42.9576, 8.876, 189.008, -42.3565, 11.8519, 185.986, 8.0596, 8.876, 194.033, 8.0596, 11.8519, 190.952, -41.5644, 14.2498, 182.004, 8.0596, 14.2498, 186.892, -42.3565, 11.8519, 185.986, -41.5644, 14.2498, 182.004, 8.0596, 11.8519, 190.952, 8.0596, 14.2498, 186.892, -40.6275, 15.9302, 177.294, 8.0596, 15.9302, 182.089, -41.5644, 14.2498, 182.004, -40.6275, 15.9302, 177.294, 8.0596, 14.2498, 186.892, 8.0596, 15.9302, 182.089, -39.6001, 16.7956, 172.129, 8.0596, 16.7956, 176.823, -40.6275, 15.9302, 177.294, -39.6001, 16.7956, 172.129, 8.0596, 15.9302, 182.089, 8.0596, 16.7956, 176.823, -38.5419, 16.7956, 166.809, 8.0596, 16.7956, 171.399, -39.6001, 16.7956, 172.129, -38.5419, 16.7956, 166.809, 8.0596, 16.7956, 176.823, 8.0596, 16.7956, 171.399, -37.5145, 15.9302, 161.644, 8.0596, 15.9302, 166.133, -38.5419, 16.7956, 166.809, -37.5145, 15.9302, 161.644, 8.0596, 16.7956, 171.399, 8.0596, 15.9302, 166.133, -36.5775, 14.2498, 156.934, 8.0596, 14.2498, 161.33, -37.5145, 15.9302, 161.644, -36.5775, 14.2498, 156.934, 8.0596, 15.9302, 166.133, 8.0596, 14.2498, 161.33, -35.7855, 11.8519, 152.952, 8.0596, 11.8519, 157.27, -36.5775, 14.2498, 156.934, -35.7855, 11.8519, 152.952, 8.0596, 14.2498, 161.33, 8.0596, 11.8519, 157.27, -35.1844, 8.876, 149.93, 8.0596, 8.876, 154.189, -35.7855, 11.8519, 152.952, -35.1844, 8.876, 149.93, 8.0596, 11.8519, 157.27, 8.0596, 8.876, 154.189, -34.8091, 5.495, 148.043, 8.0596, 5.495, 152.266, -35.1844, 8.876, 149.93, -34.8091, 5.495, 148.043, 8.0596, 8.876, 154.189, 8.0596, 5.495, 152.266, -34.6816, 1.9054, 147.402, 8.0596, 1.9054, 151.612, -34.8091, 5.495, 148.043, -34.6816, 1.9054, 147.402, 8.0596, 5.495, 152.266, 8.0596, 1.9054, 151.612, -34.8091, -1.6842, 148.043, 8.0596, -1.6842, 152.266, -34.6816, 1.9054, 147.402, -34.8091, -1.6842, 148.043, 8.0596, 1.9054, 151.612, 8.0596, -1.6842, 152.266, -35.1844, -5.0652, 149.93, 8.0596, -5.0652, 154.189, -34.8091, -1.6842, 148.043, -35.1844, -5.0652, 149.93, 8.0596, -1.6842, 152.266, 8.0596, -5.0652, 154.189, -35.7855, -8.0411, 152.952, 8.0596, -8.0411, 157.27, -35.1844, -5.0652, 149.93, -35.7855, -8.0411, 152.952, 8.0596, -5.0652, 154.189, 8.0596, -8.0411, 157.27, -36.5775, -10.4389, 156.934, 8.0596, -10.4389, 161.33, -35.7855, -8.0411, 152.952, -36.5775, -10.4389, 156.934, 8.0596, -8.0411, 157.27, 8.0596, -10.4389, 161.33, -37.5145, -12.1193, 161.644, 8.0596, -12.1193, 166.133, -36.5775, -10.4389, 156.934, -37.5145, -12.1193, 161.644, 8.0596, -10.4389, 161.33, 8.0596, -12.1193, 166.133, -38.5419, -12.9847, 166.809, 8.0596, -12.9847, 171.399, -37.5145, -12.1193, 161.644, -38.5419, -12.9847, 166.809, 8.0596, -12.1193, 166.133, 8.0596, -12.9847, 171.399, -39.6001, -12.9847, 172.129, 8.0596, -12.9847, 176.823, -38.5419, -12.9847, 166.809, -39.6001, -12.9847, 172.129, 8.0596, -12.9847, 171.399, 8.0596, -12.9847, 176.823, -40.6275, -12.1193, 177.294, 8.0596, -12.1193, 182.089, -39.6001, -12.9847, 172.129, -40.6275, -12.1193, 177.294, 8.0596, -12.9847, 176.823, 8.0596, -12.1193, 182.089, -41.5644, -10.4389, 182.004, 8.0596, -10.4389, 186.892, -40.6275, -12.1193, 177.294, -41.5644, -10.4389, 182.004, 8.0596, -12.1193, 182.089, 8.0596, -10.4389, 186.892, -42.3565, -8.0411, 185.986, 8.0596, -8.0411, 190.952, -41.5644, -10.4389, 182.004, -42.3565, -8.0411, 185.986, 8.0596, -10.4389, 186.892, 8.0596, -8.0411, 190.952, -42.9576, -5.0652, 189.008, 8.0596, -5.0652, 194.033, -42.3565, -8.0411, 185.986, -42.9576, -5.0652, 189.008, 8.0596, -8.0411, 190.952, 8.0596, -5.0652, 194.033, -43.3328, -1.6842, 190.895, 8.0596, -1.6842, 195.956, -42.9576, -5.0652, 189.008, -43.3328, -1.6842, 190.895, 8.0596, -5.0652, 194.033, 8.0596, -1.6842, 195.956, -43.4604, 1.9054, 191.536, 8.0596, 1.9054, 196.61, -43.3328, -1.6842, 190.895, -43.4604, 1.9054, 191.536, 8.0596, -1.6842, 195.956, -43.4604, 1.9054, 191.536, -92.7502, 5.495, 175.904, -43.3328, 5.495, 190.895, -93.0004, 1.9054, 176.508, -92.7502, 5.495, 175.904, -43.4604, 1.9054, 191.536, -43.3328, 5.495, 190.895, -92.0142, 8.876, 174.127, -42.9576, 8.876, 189.008, -92.7502, 5.495, 175.904, -92.0142, 8.876, 174.127, -43.3328, 5.495, 190.895, -42.9576, 8.876, 189.008, -90.8351, 11.8519, 171.281, -42.3565, 11.8519, 185.986, -92.0142, 8.876, 174.127, -90.8351, 11.8519, 171.281, -42.9576, 8.876, 189.008, -42.3565, 11.8519, 185.986, -89.2814, 14.2498, 167.53, -41.5644, 14.2498, 182.004, -90.8351, 11.8519, 171.281, -89.2814, 14.2498, 167.53, -42.3565, 11.8519, 185.986, -41.5644, 14.2498, 182.004, -87.4435, 15.9302, 163.092, -40.6275, 15.9302, 177.294, -89.2814, 14.2498, 167.53, -87.4435, 15.9302, 163.092, -41.5644, 14.2498, 182.004, -40.6275, 15.9302, 177.294, -85.4282, 16.7956, 158.227, -39.6001, 16.7956, 172.129, -87.4435, 15.9302, 163.092, -85.4282, 16.7956, 158.227, -40.6275, 15.9302, 177.294, -39.6001, 16.7956, 172.129, -83.3525, 16.7956, 153.216, -38.5419, 16.7956, 166.809, -85.4282, 16.7956, 158.227, -83.3525, 16.7956, 153.216, -39.6001, 16.7956, 172.129, -38.5419, 16.7956, 166.809, -81.3372, 15.9302, 148.35, -37.5145, 15.9302, 161.644, -83.3525, 16.7956, 153.216, -81.3372, 15.9302, 148.35, -38.5419, 16.7956, 166.809, -37.5145, 15.9302, 161.644, -79.4992, 14.2498, 143.913, -36.5775, 14.2498, 156.934, -81.3372, 15.9302, 148.35, -79.4992, 14.2498, 143.913, -37.5145, 15.9302, 161.644, -36.5775, 14.2498, 156.934, -77.9456, 11.8519, 140.162, -35.7855, 11.8519, 152.952, -79.4992, 14.2498, 143.913, -77.9456, 11.8519, 140.162, -36.5775, 14.2498, 156.934, -35.7855, 11.8519, 152.952, -76.7665, 8.876, 137.316, -35.1844, 8.876, 149.93, -77.9456, 11.8519, 140.162, -76.7665, 8.876, 137.316, -35.7855, 11.8519, 152.952, -35.1844, 8.876, 149.93, -76.0304, 5.495, 135.539, -34.8091, 5.495, 148.043, -76.7665, 8.876, 137.316, -76.0304, 5.495, 135.539, -35.1844, 8.876, 149.93, -34.8091, 5.495, 148.043, -75.7802, 1.9054, 134.935, -34.6816, 1.9054, 147.402, -76.0304, 5.495, 135.539, -75.7802, 1.9054, 134.935, -34.8091, 5.495, 148.043, -34.6816, 1.9054, 147.402, -76.0304, -1.6842, 135.539, -34.8091, -1.6842, 148.043, -75.7802, 1.9054, 134.935, -76.0304, -1.6842, 135.539, -34.6816, 1.9054, 147.402, -34.8091, -1.6842, 148.043, -76.7665, -5.0652, 137.316, -35.1844, -5.0652, 149.93, -76.0304, -1.6842, 135.539, -76.7665, -5.0652, 137.316, -34.8091, -1.6842, 148.043, -35.1844, -5.0652, 149.93, -77.9456, -8.0411, 140.162, -35.7855, -8.0411, 152.952, -76.7665, -5.0652, 137.316, -77.9456, -8.0411, 140.162, -35.1844, -5.0652, 149.93, -35.7855, -8.0411, 152.952, -79.4992, -10.4389, 143.913, -36.5775, -10.4389, 156.934, -77.9456, -8.0411, 140.162, -79.4992, -10.4389, 143.913, -35.7855, -8.0411, 152.952, -36.5775, -10.4389, 156.934, -81.3372, -12.1193, 148.35, -37.5145, -12.1193, 161.644, -79.4992, -10.4389, 143.913, -81.3372, -12.1193, 148.35, -36.5775, -10.4389, 156.934, -37.5145, -12.1193, 161.644, -83.3525, -12.9847, 153.216, -38.5419, -12.9847, 166.809, -81.3372, -12.1193, 148.35, -83.3525, -12.9847, 153.216, -37.5145, -12.1193, 161.644, -38.5419, -12.9847, 166.809, -85.4282, -12.9847, 158.227, -39.6001, -12.9847, 172.129, -83.3525, -12.9847, 153.216, -85.4282, -12.9847, 158.227, -38.5419, -12.9847, 166.809, -39.6001, -12.9847, 172.129, -87.4435, -12.1193, 163.092, -40.6275, -12.1193, 177.294, -85.4282, -12.9847, 158.227, -87.4435, -12.1193, 163.092, -39.6001, -12.9847, 172.129, -40.6275, -12.1193, 177.294, -89.2814, -10.4389, 167.53, -41.5644, -10.4389, 182.004, -87.4435, -12.1193, 163.092, -89.2814, -10.4389, 167.53, -40.6275, -12.1193, 177.294, -41.5644, -10.4389, 182.004, -90.8351, -8.0411, 171.281, -42.3565, -8.0411, 185.986, -89.2814, -10.4389, 167.53, -90.8351, -8.0411, 171.281, -41.5644, -10.4389, 182.004, -42.3565, -8.0411, 185.986, -92.0142, -5.0652, 174.127, -42.9576, -5.0652, 189.008, -90.8351, -8.0411, 171.281, -92.0142, -5.0652, 174.127, -42.3565, -8.0411, 185.986, -42.9576, -5.0652, 189.008, -92.7502, -1.6842, 175.904, -43.3328, -1.6842, 190.895, -92.0142, -5.0652, 174.127, -92.7502, -1.6842, 175.904, -42.9576, -5.0652, 189.008, -43.3328, -1.6842, 190.895, -93.0004, 1.9054, 176.508, -43.4604, 1.9054, 191.536, -92.7502, -1.6842, 175.904, -93.0004, 1.9054, 176.508, -43.3328, -1.6842, 190.895, -93.0004, 1.9054, 176.508, -138.294, 5.495, 151.561, -92.7502, 5.495, 175.904, -138.657, 1.9054, 152.104, -138.294, 5.495, 151.561, -93.0004, 1.9054, 176.508, -92.7502, 5.495, 175.904, -137.225, 8.876, 149.961, -92.0142, 8.876, 174.127, -138.294, 5.495, 151.561, -137.225, 8.876, 149.961, -92.7502, 5.495, 175.904, -92.0142, 8.876, 174.127, -135.513, 11.8519, 147.399, -90.8351, 11.8519, 171.281, -137.225, 8.876, 149.961, -135.513, 11.8519, 147.399, -92.0142, 8.876, 174.127, -90.8351, 11.8519, 171.281, -133.258, 14.2498, 144.024, -89.2814, 14.2498, 167.53, -135.513, 11.8519, 147.399, -133.258, 14.2498, 144.024, -90.8351, 11.8519, 171.281, -89.2814, 14.2498, 167.53, -130.589, 15.9302, 140.031, -87.4435, 15.9302, 163.092, -133.258, 14.2498, 144.024, -130.589, 15.9302, 140.031, -89.2814, 14.2498, 167.53, -87.4435, 15.9302, 163.092, -127.664, 16.7956, 135.652, -85.4282, 16.7956, 158.227, -130.589, 15.9302, 140.031, -127.664, 16.7956, 135.652, -87.4435, 15.9302, 163.092, -85.4282, 16.7956, 158.227, -124.65, 16.7956, 131.142, -83.3525, 16.7956, 153.216, -127.664, 16.7956, 135.652, -124.65, 16.7956, 131.142, -85.4282, 16.7956, 158.227, -83.3525, 16.7956, 153.216, -121.724, 15.9302, 126.763, -81.3372, 15.9302, 148.35, -124.65, 16.7956, 131.142, -121.724, 15.9302, 126.763, -83.3525, 16.7956, 153.216, -81.3372, 15.9302, 148.35, -119.056, 14.2498, 122.77, -79.4992, 14.2498, 143.913, -121.724, 15.9302, 126.763, -119.056, 14.2498, 122.77, -81.3372, 15.9302, 148.35, -79.4992, 14.2498, 143.913, -116.801, 11.8519, 119.394, -77.9456, 11.8519, 140.162, -119.056, 14.2498, 122.77, -116.801, 11.8519, 119.394, -79.4992, 14.2498, 143.913, -77.9456, 11.8519, 140.162, -115.089, 8.876, 116.832, -76.7665, 8.876, 137.316, -116.801, 11.8519, 119.394, -115.089, 8.876, 116.832, -77.9456, 11.8519, 140.162, -76.7665, 8.876, 137.316, -114.02, 5.495, 115.233, -76.0304, 5.495, 135.539, -115.089, 8.876, 116.832, -114.02, 5.495, 115.233, -76.7665, 8.876, 137.316, -76.0304, 5.495, 135.539, -113.657, 1.9054, 114.689, -75.7802, 1.9054, 134.935, -114.02, 5.495, 115.233, -113.657, 1.9054, 114.689, -76.0304, 5.495, 135.539, -75.7802, 1.9054, 134.935, -114.02, -1.6842, 115.233, -76.0304, -1.6842, 135.539, -113.657, 1.9054, 114.689, -114.02, -1.6842, 115.233, -75.7802, 1.9054, 134.935, -76.0304, -1.6842, 135.539, -115.089, -5.0652, 116.832, -76.7665, -5.0652, 137.316, -114.02, -1.6842, 115.233, -115.089, -5.0652, 116.832, -76.0304, -1.6842, 135.539, -76.7665, -5.0652, 137.316, -116.801, -8.0411, 119.394, -77.9456, -8.0411, 140.162, -115.089, -5.0652, 116.832, -116.801, -8.0411, 119.394, -76.7665, -5.0652, 137.316, -77.9456, -8.0411, 140.162, -119.056, -10.4389, 122.77, -79.4992, -10.4389, 143.913, -116.801, -8.0411, 119.394, -119.056, -10.4389, 122.77, -77.9456, -8.0411, 140.162, -79.4992, -10.4389, 143.913, -121.724, -12.1193, 126.763, -81.3372, -12.1193, 148.35, -119.056, -10.4389, 122.77, -121.724, -12.1193, 126.763, -79.4992, -10.4389, 143.913, -81.3372, -12.1193, 148.35, -124.65, -12.9847, 131.142, -83.3525, -12.9847, 153.216, -121.724, -12.1193, 126.763, -124.65, -12.9847, 131.142, -81.3372, -12.1193, 148.35, -83.3525, -12.9847, 153.216, -127.664, -12.9847, 135.652, -85.4282, -12.9847, 158.227, -124.65, -12.9847, 131.142, -127.664, -12.9847, 135.652, -83.3525, -12.9847, 153.216, -85.4282, -12.9847, 158.227, -130.589, -12.1193, 140.031, -87.4435, -12.1193, 163.092, -127.664, -12.9847, 135.652, -130.589, -12.1193, 140.031, -85.4282, -12.9847, 158.227, -87.4435, -12.1193, 163.092, -133.258, -10.4389, 144.024, -89.2814, -10.4389, 167.53, -130.589, -12.1193, 140.031, -133.258, -10.4389, 144.024, -87.4435, -12.1193, 163.092, -89.2814, -10.4389, 167.53, -135.513, -8.0411, 147.399, -90.8351, -8.0411, 171.281, -133.258, -10.4389, 144.024, -135.513, -8.0411, 147.399, -89.2814, -10.4389, 167.53, -90.8351, -8.0411, 171.281, -137.225, -5.0652, 149.961, -92.0142, -5.0652, 174.127, -135.513, -8.0411, 147.399, -137.225, -5.0652, 149.961, -90.8351, -8.0411, 171.281, -92.0142, -5.0652, 174.127, -138.294, -1.6842, 151.561, -92.7502, -1.6842, 175.904, -137.225, -5.0652, 149.961, -138.294, -1.6842, 151.561, -92.0142, -5.0652, 174.127, -92.7502, -1.6842, 175.904, -138.657, 1.9054, 152.104, -93.0004, 1.9054, 176.508, -138.294, -1.6842, 151.561, -138.657, 1.9054, 152.104, -92.7502, -1.6842, 175.904, -138.657, 1.9054, 152.104, -178.213, 5.495, 118.8, -138.294, 5.495, 151.561, -178.675, 1.9054, 119.262, -178.213, 5.495, 118.8, -138.657, 1.9054, 152.104, -138.294, 5.495, 151.561, -176.853, 8.876, 117.44, -137.225, 8.876, 149.961, -178.213, 5.495, 118.8, -176.853, 8.876, 117.44, -138.294, 5.495, 151.561, -137.225, 8.876, 149.961, -174.674, 11.8519, 115.261, -135.513, 11.8519, 147.399, -176.853, 8.876, 117.44, -174.674, 11.8519, 115.261, -137.225, 8.876, 149.961, -135.513, 11.8519, 147.399, -171.803, 14.2498, 112.39, -133.258, 14.2498, 144.024, -174.674, 11.8519, 115.261, -171.803, 14.2498, 112.39, -135.513, 11.8519, 147.399, -133.258, 14.2498, 144.024, -168.407, 15.9302, 108.994, -130.589, 15.9302, 140.031, -171.803, 14.2498, 112.39, -168.407, 15.9302, 108.994, -133.258, 14.2498, 144.024, -130.589, 15.9302, 140.031, -164.683, 16.7956, 105.271, -127.664, 16.7956, 135.652, -168.407, 15.9302, 108.994, -164.683, 16.7956, 105.271, -130.589, 15.9302, 140.031, -127.664, 16.7956, 135.652, -160.848, 16.7956, 101.435, -124.65, 16.7956, 131.142, -164.683, 16.7956, 105.271, -160.848, 16.7956, 101.435, -127.664, 16.7956, 135.652, -124.65, 16.7956, 131.142, -157.124, 15.9302, 97.7113, -121.724, 15.9302, 126.763, -160.848, 16.7956, 101.435, -157.124, 15.9302, 97.7113, -124.65, 16.7956, 131.142, -121.724, 15.9302, 126.763, -153.728, 14.2498, 94.3153, -119.056, 14.2498, 122.77, -157.124, 15.9302, 97.7113, -153.728, 14.2498, 94.3153, -121.724, 15.9302, 126.763, -119.056, 14.2498, 122.77, -150.857, 11.8519, 91.4445, -116.801, 11.8519, 119.394, -153.728, 14.2498, 94.3153, -150.857, 11.8519, 91.4445, -119.056, 14.2498, 122.77, -116.801, 11.8519, 119.394, -148.678, 8.876, 89.2658, -115.089, 8.876, 116.832, -150.857, 11.8519, 91.4445, -148.678, 8.876, 89.2658, -116.801, 11.8519, 119.394, -115.089, 8.876, 116.832, -147.318, 5.495, 87.9057, -114.02, 5.495, 115.233, -148.678, 8.876, 89.2658, -147.318, 5.495, 87.9057, -115.089, 8.876, 116.832, -114.02, 5.495, 115.233, -146.856, 1.9054, 87.4435, -113.657, 1.9054, 114.689, -147.318, 5.495, 87.9057, -146.856, 1.9054, 87.4435, -114.02, 5.495, 115.233, -113.657, 1.9054, 114.689, -147.318, -1.6842, 87.9057, -114.02, -1.6842, 115.233, -146.856, 1.9054, 87.4435, -147.318, -1.6842, 87.9057, -113.657, 1.9054, 114.689, -114.02, -1.6842, 115.233, -148.678, -5.0652, 89.2658, -115.089, -5.0652, 116.832, -147.318, -1.6842, 87.9057, -148.678, -5.0652, 89.2658, -114.02, -1.6842, 115.233, -115.089, -5.0652, 116.832, -150.857, -8.0411, 91.4445, -116.801, -8.0411, 119.394, -148.678, -5.0652, 89.2658, -150.857, -8.0411, 91.4445, -115.089, -5.0652, 116.832, -116.801, -8.0411, 119.394, -153.728, -10.4389, 94.3153, -119.056, -10.4389, 122.77, -150.857, -8.0411, 91.4445, -153.728, -10.4389, 94.3153, -116.801, -8.0411, 119.394, -119.056, -10.4389, 122.77, -157.124, -12.1193, 97.7113, -121.724, -12.1193, 126.763, -153.728, -10.4389, 94.3153, -157.124, -12.1193, 97.7113, -119.056, -10.4389, 122.77, -121.724, -12.1193, 126.763, -160.848, -12.9847, 101.435, -124.65, -12.9847, 131.142, -157.124, -12.1193, 97.7113, -160.848, -12.9847, 101.435, -121.724, -12.1193, 126.763, -124.65, -12.9847, 131.142, -164.683, -12.9847, 105.271, -127.664, -12.9847, 135.652, -160.848, -12.9847, 101.435, -164.683, -12.9847, 105.271, -124.65, -12.9847, 131.142, -127.664, -12.9847, 135.652, -168.407, -12.1193, 108.994, -130.589, -12.1193, 140.031, -164.683, -12.9847, 105.271, -168.407, -12.1193, 108.994, -127.664, -12.9847, 135.652, -130.589, -12.1193, 140.031, -171.803, -10.4389, 112.39, -133.258, -10.4389, 144.024, -168.407, -12.1193, 108.994, -171.803, -10.4389, 112.39, -130.589, -12.1193, 140.031, -133.258, -10.4389, 144.024, -174.674, -8.0411, 115.261, -135.513, -8.0411, 147.399, -171.803, -10.4389, 112.39, -174.674, -8.0411, 115.261, -133.258, -10.4389, 144.024, -135.513, -8.0411, 147.399, -176.853, -5.0652, 117.44, -137.225, -5.0652, 149.961, -174.674, -8.0411, 115.261, -176.853, -5.0652, 117.44, -135.513, -8.0411, 147.399, -137.225, -5.0652, 149.961, -178.213, -1.6842, 118.8, -138.294, -1.6842, 151.561, -176.853, -5.0652, 117.44, -178.213, -1.6842, 118.8, -137.225, -5.0652, 149.961, -138.294, -1.6842, 151.561, -178.675, 1.9054, 119.262, -138.657, 1.9054, 152.104, -178.213, -1.6842, 118.8, -178.675, 1.9054, 119.262, -138.294, -1.6842, 151.561, -178.675, 1.9054, 119.262, -210.973, 5.495, 78.8808, -178.213, 5.495, 118.8, -211.517, 1.9054, 79.244, -210.973, 5.495, 78.8808, -178.675, 1.9054, 119.262, -178.213, 5.495, 118.8, -209.374, 8.876, 77.8122, -176.853, 8.876, 117.44, -210.973, 5.495, 78.8808, -209.374, 8.876, 77.8122, -178.213, 5.495, 118.8, -176.853, 8.876, 117.44, -206.812, 11.8519, 76.1004, -174.674, 11.8519, 115.261, -209.374, 8.876, 77.8122, -206.812, 11.8519, 76.1004, -176.853, 8.876, 117.44, -174.674, 11.8519, 115.261, -203.437, 14.2498, 73.8449, -171.803, 14.2498, 112.39, -206.812, 11.8519, 76.1004, -203.437, 14.2498, 73.8449, -174.674, 11.8519, 115.261, -171.803, 14.2498, 112.39, -199.443, 15.9302, 71.1767, -168.407, 15.9302, 108.994, -203.437, 14.2498, 73.8449, -199.443, 15.9302, 71.1767, -171.803, 14.2498, 112.39, -168.407, 15.9302, 108.994, -195.064, 16.7956, 68.2508, -164.683, 16.7956, 105.271, -199.443, 15.9302, 71.1767, -195.064, 16.7956, 68.2508, -168.407, 15.9302, 108.994, -164.683, 16.7956, 105.271, -190.555, 16.7956, 65.2374, -160.848, 16.7956, 101.435, -195.064, 16.7956, 68.2508, -190.555, 16.7956, 65.2374, -164.683, 16.7956, 105.271, -160.848, 16.7956, 101.435, -186.176, 15.9302, 62.3116, -157.124, 15.9302, 97.7113, -190.555, 16.7956, 65.2374, -186.176, 15.9302, 62.3116, -160.848, 16.7956, 101.435, -157.124, 15.9302, 97.7113, -182.182, 14.2498, 59.6434, -153.728, 14.2498, 94.3153, -186.176, 15.9302, 62.3116, -182.182, 14.2498, 59.6434, -157.124, 15.9302, 97.7113, -153.728, 14.2498, 94.3153, -178.807, 11.8519, 57.3878, -150.857, 11.8519, 91.4445, -182.182, 14.2498, 59.6434, -178.807, 11.8519, 57.3878, -153.728, 14.2498, 94.3153, -150.857, 11.8519, 91.4445, -176.245, 8.876, 55.676, -148.678, 8.876, 89.2658, -178.807, 11.8519, 57.3878, -176.245, 8.876, 55.676, -150.857, 11.8519, 91.4445, -148.678, 8.876, 89.2658, -174.646, 5.495, 54.6074, -147.318, 5.495, 87.9057, -176.245, 8.876, 55.676, -174.646, 5.495, 54.6074, -148.678, 8.876, 89.2658, -147.318, 5.495, 87.9057, -174.102, 1.9054, 54.2442, -146.856, 1.9054, 87.4435, -174.646, 5.495, 54.6074, -174.102, 1.9054, 54.2442, -147.318, 5.495, 87.9057, -146.856, 1.9054, 87.4435, -174.646, -1.6842, 54.6074, -147.318, -1.6842, 87.9057, -174.102, 1.9054, 54.2442, -174.646, -1.6842, 54.6074, -146.856, 1.9054, 87.4435, -147.318, -1.6842, 87.9057, -176.245, -5.0652, 55.676, -148.678, -5.0652, 89.2658, -174.646, -1.6842, 54.6074, -176.245, -5.0652, 55.676, -147.318, -1.6842, 87.9057, -148.678, -5.0652, 89.2658, -178.807, -8.0411, 57.3878, -150.857, -8.0411, 91.4445, -176.245, -5.0652, 55.676, -178.807, -8.0411, 57.3878, -148.678, -5.0652, 89.2658, -150.857, -8.0411, 91.4445, -182.182, -10.4389, 59.6434, -153.728, -10.4389, 94.3153, -178.807, -8.0411, 57.3878, -182.182, -10.4389, 59.6434, -150.857, -8.0411, 91.4445, -153.728, -10.4389, 94.3153, -186.176, -12.1193, 62.3116, -157.124, -12.1193, 97.7113, -182.182, -10.4389, 59.6434, -186.176, -12.1193, 62.3116, -153.728, -10.4389, 94.3153, -157.124, -12.1193, 97.7113, -190.555, -12.9847, 65.2374, -160.848, -12.9847, 101.435, -186.176, -12.1193, 62.3116, -190.555, -12.9847, 65.2374, -157.124, -12.1193, 97.7113, -160.848, -12.9847, 101.435, -195.064, -12.9847, 68.2508, -164.683, -12.9847, 105.271, -190.555, -12.9847, 65.2374, -195.064, -12.9847, 68.2508, -160.848, -12.9847, 101.435, -164.683, -12.9847, 105.271, -199.443, -12.1193, 71.1767, -168.407, -12.1193, 108.994, -195.064, -12.9847, 68.2508, -199.443, -12.1193, 71.1767, -164.683, -12.9847, 105.271, -168.407, -12.1193, 108.994, -203.437, -10.4389, 73.8449, -171.803, -10.4389, 112.39, -199.443, -12.1193, 71.1767, -203.437, -10.4389, 73.8449, -168.407, -12.1193, 108.994, -171.803, -10.4389, 112.39, -206.812, -8.0411, 76.1004, -174.674, -8.0411, 115.261, -203.437, -10.4389, 73.8449, -206.812, -8.0411, 76.1004, -171.803, -10.4389, 112.39, -174.674, -8.0411, 115.261, -209.374, -5.0652, 77.8122, -176.853, -5.0652, 117.44, -206.812, -8.0411, 76.1004, -209.374, -5.0652, 77.8122, -174.674, -8.0411, 115.261, -176.853, -5.0652, 117.44, -210.973, -1.6842, 78.8808, -178.213, -1.6842, 118.8, -209.374, -5.0652, 77.8122, -210.973, -1.6842, 78.8808, -176.853, -5.0652, 117.44, -178.213, -1.6842, 118.8, -211.517, 1.9054, 79.244, -178.675, 1.9054, 119.262, -210.973, -1.6842, 78.8808, -211.517, 1.9054, 79.244, -178.213, -1.6842, 118.8, -211.517, 1.9054, 79.244, -235.317, 5.495, 33.3375, -210.973, 5.495, 78.8808, -235.921, 1.9054, 33.5877, -235.317, 5.495, 33.3375, -211.517, 1.9054, 79.244, -210.973, 5.495, 78.8808, -233.54, 8.876, 32.6014, -209.374, 8.876, 77.8122, -235.317, 5.495, 33.3375, -233.54, 8.876, 32.6014, -210.973, 5.495, 78.8808, -209.374, 8.876, 77.8122, -230.693, 11.8519, 31.4223, -206.812, 11.8519, 76.1004, -233.54, 8.876, 32.6014, -230.693, 11.8519, 31.4223, -209.374, 8.876, 77.8122, -206.812, 11.8519, 76.1004, -226.942, 14.2498, 29.8687, -203.437, 14.2498, 73.8449, -230.693, 11.8519, 31.4223, -226.942, 14.2498, 29.8687, -206.812, 11.8519, 76.1004, -203.437, 14.2498, 73.8449, -222.505, 15.9302, 28.0308, -199.443, 15.9302, 71.1767, -226.942, 14.2498, 29.8687, -222.505, 15.9302, 28.0308, -203.437, 14.2498, 73.8449, -199.443, 15.9302, 71.1767, -217.64, 16.7956, 26.0154, -195.064, 16.7956, 68.2508, -222.505, 15.9302, 28.0308, -217.64, 16.7956, 26.0154, -199.443, 15.9302, 71.1767, -195.064, 16.7956, 68.2508, -212.629, 16.7956, 23.9398, -190.555, 16.7956, 65.2374, -217.64, 16.7956, 26.0154, -212.629, 16.7956, 23.9398, -195.064, 16.7956, 68.2508, -190.555, 16.7956, 65.2374, -207.763, 15.9302, 21.9244, -186.176, 15.9302, 62.3116, -212.629, 16.7956, 23.9398, -207.763, 15.9302, 21.9244, -190.555, 16.7956, 65.2374, -186.176, 15.9302, 62.3116, -203.326, 14.2498, 20.0865, -182.182, 14.2498, 59.6434, -207.763, 15.9302, 21.9244, -203.326, 14.2498, 20.0865, -186.176, 15.9302, 62.3116, -182.182, 14.2498, 59.6434, -199.575, 11.8519, 18.5329, -178.807, 11.8519, 57.3878, -203.326, 14.2498, 20.0865, -199.575, 11.8519, 18.5329, -182.182, 14.2498, 59.6434, -178.807, 11.8519, 57.3878, -196.729, 8.876, 17.3537, -176.245, 8.876, 55.676, -199.575, 11.8519, 18.5329, -196.729, 8.876, 17.3537, -178.807, 11.8519, 57.3878, -176.245, 8.876, 55.676, -194.952, 5.495, 16.6177, -174.646, 5.495, 54.6074, -196.729, 8.876, 17.3537, -194.952, 5.495, 16.6177, -176.245, 8.876, 55.676, -174.646, 5.495, 54.6074, -194.348, 1.9054, 16.3675, -174.102, 1.9054, 54.2442, -194.952, 5.495, 16.6177, -194.348, 1.9054, 16.3675, -174.646, 5.495, 54.6074, -174.102, 1.9054, 54.2442, -194.952, -1.6842, 16.6177, -174.646, -1.6842, 54.6074, -194.348, 1.9054, 16.3675, -194.952, -1.6842, 16.6177, -174.102, 1.9054, 54.2442, -174.646, -1.6842, 54.6074, -196.729, -5.0652, 17.3537, -176.245, -5.0652, 55.676, -194.952, -1.6842, 16.6177, -196.729, -5.0652, 17.3537, -174.646, -1.6842, 54.6074, -176.245, -5.0652, 55.676, -199.575, -8.0411, 18.5329, -178.807, -8.0411, 57.3878, -196.729, -5.0652, 17.3537, -199.575, -8.0411, 18.5329, -176.245, -5.0652, 55.676, -178.807, -8.0411, 57.3878, -203.326, -10.4389, 20.0865, -182.182, -10.4389, 59.6434, -199.575, -8.0411, 18.5329, -203.326, -10.4389, 20.0865, -178.807, -8.0411, 57.3878, -182.182, -10.4389, 59.6434, -207.763, -12.1193, 21.9244, -186.176, -12.1193, 62.3116, -203.326, -10.4389, 20.0865, -207.763, -12.1193, 21.9244, -182.182, -10.4389, 59.6434, -186.176, -12.1193, 62.3116, -212.629, -12.9847, 23.9398, -190.555, -12.9847, 65.2374, -207.763, -12.1193, 21.9244, -212.629, -12.9847, 23.9398, -186.176, -12.1193, 62.3116, -190.555, -12.9847, 65.2374, -217.64, -12.9847, 26.0154, -195.064, -12.9847, 68.2508, -212.629, -12.9847, 23.9398, -217.64, -12.9847, 26.0154, -190.555, -12.9847, 65.2374, -195.064, -12.9847, 68.2508, -222.505, -12.1193, 28.0308, -199.443, -12.1193, 71.1767, -217.64, -12.9847, 26.0154, -222.505, -12.1193, 28.0308, -195.064, -12.9847, 68.2508, -199.443, -12.1193, 71.1767, -226.942, -10.4389, 29.8687, -203.437, -10.4389, 73.8449, -222.505, -12.1193, 28.0308, -226.942, -10.4389, 29.8687, -199.443, -12.1193, 71.1767, -203.437, -10.4389, 73.8449, -230.693, -8.0411, 31.4223, -206.812, -8.0411, 76.1004, -226.942, -10.4389, 29.8687, -230.693, -8.0411, 31.4223, -203.437, -10.4389, 73.8449, -206.812, -8.0411, 76.1004, -233.54, -5.0652, 32.6014, -209.374, -5.0652, 77.8122, -230.693, -8.0411, 31.4223, -233.54, -5.0652, 32.6014, -206.812, -8.0411, 76.1004, -209.374, -5.0652, 77.8122, -235.317, -1.6842, 33.3375, -210.973, -1.6842, 78.8808, -233.54, -5.0652, 32.6014, -235.317, -1.6842, 33.3375, -209.374, -5.0652, 77.8122, -210.973, -1.6842, 78.8808, -235.921, 1.9054, 33.5877, -211.517, 1.9054, 79.244, -235.317, -1.6842, 33.3375, -235.921, 1.9054, 33.5877, -210.973, -1.6842, 78.8808, -235.921, 1.9054, 33.5877, -250.307, 5.495, -16.0799, -235.317, 5.495, 33.3375, -250.949, 1.9054, -15.9524, -250.307, 5.495, -16.0799, -235.921, 1.9054, 33.5877, -235.317, 5.495, 33.3375, -248.421, 8.876, -16.4552, -233.54, 8.876, 32.6014, -250.307, 5.495, -16.0799, -248.421, 8.876, -16.4552, -235.317, 5.495, 33.3375, -233.54, 8.876, 32.6014, -245.399, 11.8519, -17.0563, -230.693, 11.8519, 31.4223, -248.421, 8.876, -16.4552, -245.399, 11.8519, -17.0563, -233.54, 8.876, 32.6014, -230.693, 11.8519, 31.4223, -241.417, 14.2498, -17.8483, -226.942, 14.2498, 29.8687, -245.399, 11.8519, -17.0563, -241.417, 14.2498, -17.8483, -230.693, 11.8519, 31.4223, -226.942, 14.2498, 29.8687, -236.707, 15.9302, -18.7853, -222.505, 15.9302, 28.0308, -241.417, 14.2498, -17.8483, -236.707, 15.9302, -18.7853, -226.942, 14.2498, 29.8687, -222.505, 15.9302, 28.0308, -231.542, 16.7956, -19.8127, -217.64, 16.7956, 26.0154, -236.707, 15.9302, -18.7853, -231.542, 16.7956, -19.8127, -222.505, 15.9302, 28.0308, -217.64, 16.7956, 26.0154, -226.222, 16.7956, -20.8709, -212.629, 16.7956, 23.9398, -231.542, 16.7956, -19.8127, -226.222, 16.7956, -20.8709, -217.64, 16.7956, 26.0154, -212.629, 16.7956, 23.9398, -221.057, 15.9302, -21.8983, -207.763, 15.9302, 21.9244, -226.222, 16.7956, -20.8709, -221.057, 15.9302, -21.8983, -212.629, 16.7956, 23.9398, -207.763, 15.9302, 21.9244, -216.346, 14.2498, -22.8352, -203.326, 14.2498, 20.0865, -221.057, 15.9302, -21.8983, -216.346, 14.2498, -22.8352, -207.763, 15.9302, 21.9244, -203.326, 14.2498, 20.0865, -212.364, 11.8519, -23.6273, -199.575, 11.8519, 18.5329, -216.346, 14.2498, -22.8352, -212.364, 11.8519, -23.6273, -203.326, 14.2498, 20.0865, -199.575, 11.8519, 18.5329, -209.342, 8.876, -24.2284, -196.729, 8.876, 17.3537, -212.364, 11.8519, -23.6273, -209.342, 8.876, -24.2284, -199.575, 11.8519, 18.5329, -196.729, 8.876, 17.3537, -207.456, 5.495, -24.6036, -194.952, 5.495, 16.6177, -209.342, 8.876, -24.2284, -207.456, 5.495, -24.6036, -196.729, 8.876, 17.3537, -194.952, 5.495, 16.6177, -206.815, 1.9054, -24.7312, -194.348, 1.9054, 16.3675, -207.456, 5.495, -24.6036, -206.815, 1.9054, -24.7312, -194.952, 5.495, 16.6177, -194.348, 1.9054, 16.3675, -207.456, -1.6842, -24.6036, -194.952, -1.6842, 16.6177, -206.815, 1.9054, -24.7312, -207.456, -1.6842, -24.6036, -194.348, 1.9054, 16.3675, -194.952, -1.6842, 16.6177, -209.342, -5.0652, -24.2284, -196.729, -5.0652, 17.3537, -207.456, -1.6842, -24.6036, -209.342, -5.0652, -24.2284, -194.952, -1.6842, 16.6177, -196.729, -5.0652, 17.3537, -212.364, -8.0411, -23.6273, -199.575, -8.0411, 18.5329, -209.342, -5.0652, -24.2284, -212.364, -8.0411, -23.6273, -196.729, -5.0652, 17.3537, -199.575, -8.0411, 18.5329, -216.346, -10.4389, -22.8352, -203.326, -10.4389, 20.0865, -212.364, -8.0411, -23.6273, -216.346, -10.4389, -22.8352, -199.575, -8.0411, 18.5329, -203.326, -10.4389, 20.0865, -221.057, -12.1193, -21.8983, -207.763, -12.1193, 21.9244, -216.346, -10.4389, -22.8352, -221.057, -12.1193, -21.8983, -203.326, -10.4389, 20.0865, -207.763, -12.1193, 21.9244, -226.222, -12.9847, -20.8709, -212.629, -12.9847, 23.9398, -221.057, -12.1193, -21.8983, -226.222, -12.9847, -20.8709, -207.763, -12.1193, 21.9244, -212.629, -12.9847, 23.9398, -231.542, -12.9847, -19.8127, -217.64, -12.9847, 26.0154, -226.222, -12.9847, -20.8709, -231.542, -12.9847, -19.8127, -212.629, -12.9847, 23.9398, -217.64, -12.9847, 26.0154, -236.707, -12.1193, -18.7853, -222.505, -12.1193, 28.0308, -231.542, -12.9847, -19.8127, -236.707, -12.1193, -18.7853, -217.64, -12.9847, 26.0154, -222.505, -12.1193, 28.0308, -241.417, -10.4389, -17.8483, -226.942, -10.4389, 29.8687, -236.707, -12.1193, -18.7853, -241.417, -10.4389, -17.8483, -222.505, -12.1193, 28.0308, -226.942, -10.4389, 29.8687, -245.399, -8.0411, -17.0563, -230.693, -8.0411, 31.4223, -241.417, -10.4389, -17.8483, -245.399, -8.0411, -17.0563, -226.942, -10.4389, 29.8687, -230.693, -8.0411, 31.4223, -248.421, -5.0652, -16.4552, -233.54, -5.0652, 32.6014, -245.399, -8.0411, -17.0563, -248.421, -5.0652, -16.4552, -230.693, -8.0411, 31.4223, -233.54, -5.0652, 32.6014, -250.307, -1.6842, -16.0799, -235.317, -1.6842, 33.3375, -248.421, -5.0652, -16.4552, -250.307, -1.6842, -16.0799, -233.54, -5.0652, 32.6014, -235.317, -1.6842, 33.3375, -250.949, 1.9054, -15.9524, -235.921, 1.9054, 33.5877, -250.307, -1.6842, -16.0799, -250.949, 1.9054, -15.9524, -235.317, -1.6842, 33.3375, -253.7, 5.495, -50.5297, -250.307, 5.495, -16.0799, -253.917, 4.3155, -50.5431, -250.307, 5.495, -16.0799, -250.949, 1.9054, -15.9524, -253.917, 4.3155, -50.5431, -253.917, 4.3155, -50.5431, -250.949, 1.9054, -15.9524, -254.358, 1.9054, -50.5705, -251.765, 8.8761, -50.4097, -248.421, 8.876, -16.4552, -252.406, 7.7562, -50.4494, -248.421, 8.876, -16.4552, -250.307, 5.495, -16.0799, -252.406, 7.7562, -50.4494, -252.406, 7.7562, -50.4494, -250.307, 5.495, -16.0799, -253.7, 5.495, -50.5297, -248.665, 11.8519, -50.2175, -245.399, 11.8519, -17.0563, -249.71, 10.8492, -50.2822, -245.399, 11.8519, -17.0563, -248.421, 8.876, -16.4552, -249.71, 10.8492, -50.2822, -249.71, 10.8492, -50.2822, -248.421, 8.876, -16.4552, -251.765, 8.8761, -50.4097, -244.58, 14.2498, -49.9642, -241.417, 14.2498, -17.8483, -245.992, 13.4212, -50.0517, -241.417, 14.2498, -17.8483, -245.399, 11.8519, -17.0563, -245.992, 13.4212, -50.0517, -245.992, 13.4212, -50.0517, -245.399, 11.8519, -17.0563, -248.665, 11.8519, -50.2175, -239.748, 15.9302, -49.6646, -236.707, 15.9302, -18.7853, -241.472, 15.3308, -49.7714, -236.707, 15.9302, -18.7853, -241.417, 14.2498, -17.8483, -241.472, 15.3308, -49.7714, -241.472, 15.3308, -49.7714, -241.417, 14.2498, -17.8483, -244.58, 14.2498, -49.9642, -234.449, 16.7956, -49.336, -231.542, 16.7956, -19.8127, -236.41, 16.4754, -49.4576, -231.542, 16.7956, -19.8127, -236.707, 15.9302, -18.7853, -236.41, 16.4754, -49.4576, -236.41, 16.4754, -49.4576, -236.707, 15.9302, -18.7853, -239.748, 15.9302, -49.6646, -228.992, 16.7956, -48.9977, -226.222, 16.7956, -20.8709, -231.093, 16.7956, -49.1279, -226.222, 16.7956, -20.8709, -231.542, 16.7956, -19.8127, -231.093, 16.7956, -49.1279, -231.093, 16.7956, -49.1279, -231.542, 16.7956, -19.8127, -234.449, 16.7956, -49.336, -223.693, 15.9302, -48.6691, -221.057, 15.9302, -21.8983, -225.816, 16.2769, -48.8008, -221.057, 15.9302, -21.8983, -226.222, 16.7956, -20.8709, -225.816, 16.2769, -48.8008, -225.816, 16.2769, -48.8008, -226.222, 16.7956, -20.8709, -228.992, 16.7956, -48.9977, -218.861, 14.2498, -48.3695, -216.346, 14.2498, -22.8352, -220.873, 14.9495, -48.4943, -216.346, 14.2498, -22.8352, -221.057, 15.9302, -21.8983, -220.873, 14.9495, -48.4943, -220.873, 14.9495, -48.4943, -221.057, 15.9302, -21.8983, -223.693, 15.9302, -48.6691, -214.776, 11.8519, -48.1162, -212.364, 11.8519, -23.6273, -216.538, 12.8859, -48.2254, -212.364, 11.8519, -23.6273, -216.346, 14.2498, -22.8352, -216.538, 12.8859, -48.2254, -216.538, 12.8859, -48.2254, -216.346, 14.2498, -22.8352, -218.861, 14.2498, -48.3695, -211.676, 8.8761, -47.924, -209.342, 8.876, -24.2284, -213.052, 10.1971, -48.0094, -209.342, 8.876, -24.2284, -212.364, 11.8519, -23.6273, -213.052, 10.1971, -48.0094, -213.052, 10.1971, -48.0094, -212.364, 11.8519, -23.6273, -214.776, 11.8519, -48.1162, -209.741, 5.495, -47.804, -207.456, 5.495, -24.6036, -210.619, 7.0285, -47.8585, -207.456, 5.495, -24.6036, -209.342, 8.876, -24.2284, -210.619, 7.0285, -47.8585, -210.619, 7.0285, -47.8585, -209.342, 8.876, -24.2284, -211.676, 8.8761, -47.924, -209.083, 1.9054, -47.7632, -206.815, 1.9054, -24.7312, -209.385, 3.5542, -47.782, -206.815, 1.9054, -24.7312, -207.456, 5.495, -24.6036, -209.385, 3.5542, -47.782, -209.385, 3.5542, -47.782, -207.456, 5.495, -24.6036, -209.741, 5.495, -47.804, -209.741, -1.6842, -47.804, -207.456, -1.6842, -24.6036, -209.438, -0.0308, -47.7852, -207.456, -1.6842, -24.6036, -206.815, 1.9054, -24.7312, -209.438, -0.0308, -47.7852, -209.438, -0.0308, -47.7852, -206.815, 1.9054, -24.7312, -209.083, 1.9054, -47.7632, -211.676, -5.0652, -47.924, -209.342, -5.0652, -24.2284, -210.791, -3.5191, -47.8691, -209.342, -5.0652, -24.2284, -207.456, -1.6842, -24.6036, -210.791, -3.5191, -47.8691, -210.791, -3.5191, -47.8691, -207.456, -1.6842, -24.6036, -209.741, -1.6842, -47.804, -214.776, -8.0411, -48.1162, -212.364, -8.0411, -23.6273, -213.383, -6.7031, -48.0298, -212.364, -8.0411, -23.6273, -209.342, -5.0652, -24.2284, -213.383, -6.7031, -48.0298, -213.383, -6.7031, -48.0298, -209.342, -5.0652, -24.2284, -211.676, -5.0652, -47.924, -218.861, -10.4389, -48.3695, -216.346, -10.4389, -22.8352, -217.071, -9.3881, -48.2585, -216.346, -10.4389, -22.8352, -212.364, -8.0411, -23.6273, -217.071, -9.3881, -48.2585, -217.071, -9.3881, -48.2585, -212.364, -8.0411, -23.6273, -214.776, -8.0411, -48.1162, -223.693, -12.1193, -48.6691, -221.057, -12.1193, -21.8983, -221.644, -11.4067, -48.5421, -221.057, -12.1193, -21.8983, -216.346, -10.4389, -22.8352, -221.644, -11.4067, -48.5421, -221.644, -11.4067, -48.5421, -216.346, -10.4389, -22.8352, -218.861, -10.4389, -48.3695, -228.992, -12.9847, -48.9977, -226.222, -12.9847, -20.8709, -226.828, -12.6314, -48.8635, -226.222, -12.9847, -20.8709, -221.057, -12.1193, -21.8983, -226.828, -12.6314, -48.8635, -226.828, -12.6314, -48.8635, -221.057, -12.1193, -21.8983, -223.693, -12.1193, -48.6691, -234.449, -12.9847, -49.336, -231.542, -12.9847, -19.8127, -232.31, -12.9847, -49.2034, -231.542, -12.9847, -19.8127, -226.222, -12.9847, -20.8709, -232.31, -12.9847, -49.2034, -232.31, -12.9847, -49.2034, -226.222, -12.9847, -20.8709, -228.992, -12.9847, -48.9977, -239.748, -12.1193, -49.6646, -236.707, -12.1193, -18.7853, -237.755, -12.4449, -49.541, -236.707, -12.1193, -18.7853, -231.542, -12.9847, -19.8127, -237.755, -12.4449, -49.541, -237.755, -12.4449, -49.541, -231.542, -12.9847, -19.8127, -234.449, -12.9847, -49.336, -244.58, -10.4389, -49.9642, -241.417, -10.4389, -17.8483, -242.832, -11.0469, -49.8558, -241.417, -10.4389, -17.8483, -236.707, -12.1193, -18.7853, -242.832, -11.0469, -49.8558, -242.832, -11.0469, -49.8558, -236.707, -12.1193, -18.7853, -239.748, -12.1193, -49.6646, -248.665, -8.0411, -50.2175, -245.399, -8.0411, -17.0563, -247.237, -8.8791, -50.129, -245.399, -8.0411, -17.0563, -241.417, -10.4389, -17.8483, -247.237, -8.8791, -50.129, -247.237, -8.8791, -50.129, -241.417, -10.4389, -17.8483, -244.58, -10.4389, -49.9642, -251.765, -5.0652, -50.4097, -248.421, -5.0652, -16.4552, -250.712, -6.0762, -50.3444, -248.421, -5.0652, -16.4552, -245.399, -8.0411, -17.0563, -250.712, -6.0762, -50.3444, -250.712, -6.0762, -50.3444, -245.399, -8.0411, -17.0563, -248.665, -8.0411, -50.2175, -253.7, -1.6842, -50.5297, -250.307, -1.6842, -16.0799, -253.056, -2.8097, -50.4897, -250.307, -1.6842, -16.0799, -248.421, -5.0652, -16.4552, -253.056, -2.8097, -50.4897, -253.056, -2.8097, -50.4897, -248.421, -5.0652, -16.4552, -251.765, -5.0652, -50.4097, -254.358, 1.9054, -50.5705, -250.949, 1.9054, -15.9524, -254.142, 0.7239, -50.557, -250.949, 1.9054, -15.9524, -250.307, -1.6842, -16.0799, -254.142, 0.7239, -50.557, -254.142, 0.7239, -50.557, -250.307, -1.6842, -16.0799, -253.7, -1.6842, -50.5297, 260.638, 4.4899, -18.6388, 260.502, 4.4899, -17.2583, 260.65, 4.417, -18.6381, 260.65, 4.417, -18.6381, 260.502, 4.4899, -17.2583, 261.075, 1.8384, -18.6118, 260.502, 4.4899, -17.2583, 260.933, 1.8384, -17.1727, 261.075, 1.8384, -18.6118, 259.355, 6.9872, -18.7184, 259.236, 6.9872, -17.5103, 259.386, 6.9269, -18.7165, 259.386, 6.9269, -18.7165, 259.236, 6.9872, -17.5103, 260.638, 4.4899, -18.6388, 259.236, 6.9872, -17.5103, 260.502, 4.4899, -17.2583, 260.638, 4.4899, -18.6388, 257.298, 9.1853, -18.8459, 257.206, 9.1853, -17.9139, 257.337, 9.1441, -18.8435, 257.337, 9.1441, -18.8435, 257.206, 9.1853, -17.9139, 259.355, 6.9872, -18.7184, 257.206, 9.1853, -17.9139, 259.236, 6.9872, -17.5103, 259.355, 6.9872, -18.7184, 254.589, 10.9564, -19.014, 254.533, 10.9564, -18.4458, 254.62, 10.936, -19.012, 254.62, 10.936, -19.012, 254.533, 10.9564, -18.4458, 257.298, 9.1853, -18.8459, 254.533, 10.9564, -18.4458, 257.206, 9.1853, -17.9139, 257.298, 9.1853, -18.8459, 251.383, 12.1977, -19.2127, 251.37, 12.1977, -19.0749, 251.392, 12.1941, -19.2121, 251.392, 12.1941, -19.2121, 251.37, 12.1977, -19.0749, 254.589, 10.9564, -19.014, 251.37, 12.1977, -19.0749, 254.533, 10.9564, -18.4458, 254.589, 10.9564, -19.014, 250.357, 12.3842, -19.2763, 251.37, 12.1977, -19.0749, 251.383, 12.1977, -19.2127, 251.383, -8.5208, -19.2127, 251.37, -8.5208, -19.0749, 251.373, -8.5226, -19.2133, 251.373, -8.5226, -19.2133, 251.37, -8.5208, -19.0749, 250.357, -8.7074, -19.2763, 254.589, -7.2796, -19.0139, 254.533, -7.2796, -18.4458, 254.551, -7.294, -19.0163, 254.551, -7.294, -19.0163, 254.533, -7.2796, -18.4458, 251.383, -8.5208, -19.2127, 254.533, -7.2796, -18.4458, 251.37, -8.5208, -19.0749, 251.383, -8.5208, -19.2127, 257.298, -5.5084, -18.8459, 257.206, -5.5084, -17.9139, 257.247, -5.5419, -18.8491, 257.247, -5.5419, -18.8491, 257.206, -5.5084, -17.9139, 254.589, -7.2796, -19.0139, 257.206, -5.5084, -17.9139, 254.533, -7.2796, -18.4458, 254.589, -7.2796, -19.0139, 259.355, -3.3103, -18.7184, 259.236, -3.3103, -17.5103, 259.305, -3.3636, -18.7215, 259.305, -3.3636, -18.7215, 259.236, -3.3103, -17.5103, 257.298, -5.5084, -18.8459, 259.236, -3.3103, -17.5103, 257.206, -5.5084, -17.9139, 257.298, -5.5084, -18.8459, 260.638, -0.813, -18.6388, 260.502, -0.813, -17.2583, 260.603, -0.8818, -18.641, 260.603, -0.8818, -18.641, 260.502, -0.813, -17.2583, 259.355, -3.3103, -18.7184, 260.502, -0.813, -17.2583, 259.236, -3.3103, -17.5103, 259.355, -3.3103, -18.7184, 261.075, 1.8384, -18.6118, 260.933, 1.8384, -17.1727, 261.062, 1.7625, -18.6126, 261.062, 1.7625, -18.6126, 260.933, 1.8384, -17.1727, 260.638, -0.813, -18.6388, 260.933, 1.8384, -17.1727, 260.502, -0.813, -17.2583, 260.638, -0.813, -18.6388, 260.933, 1.8384, -17.1727, 260.502, 4.4899, -17.2583, 245.855, 4.4899, 31.026, 246.261, 1.8384, 31.194, 260.933, 1.8384, -17.1727, 245.855, 4.4899, 31.026, 260.502, 4.4899, -17.2583, 259.236, 6.9872, -17.5103, 244.662, 6.9872, 30.5317, 245.855, 4.4899, 31.026, 260.502, 4.4899, -17.2583, 244.662, 6.9872, 30.5317, 259.236, 6.9872, -17.5103, 257.206, 9.1853, -17.9139, 242.751, 9.1853, 29.74, 244.662, 6.9872, 30.5317, 259.236, 6.9872, -17.5103, 242.751, 9.1853, 29.74, 257.206, 9.1853, -17.9139, 254.533, 10.9564, -18.4458, 240.232, 10.9564, 28.6967, 242.751, 9.1853, 29.74, 257.206, 9.1853, -17.9139, 240.232, 10.9564, 28.6967, 254.533, 10.9564, -18.4458, 251.37, 12.1977, -19.0749, 237.253, 12.1977, 27.4626, 240.232, 10.9564, 28.6967, 254.533, 10.9564, -18.4458, 237.253, 12.1977, 27.4626, 247.801, 12.8368, -19.4348, 233.986, 12.8369, 26.1093, 250.357, 12.3842, -19.2763, 233.986, 12.8369, 26.1093, 251.37, 12.1977, -19.0749, 250.357, 12.3842, -19.2763, 237.253, 12.1977, 27.4626, 251.37, 12.1977, -19.0749, 233.986, 12.8369, 26.1093, 244.083, 12.8369, -19.6653, 230.621, 12.8369, 24.7155, 247.774, 12.8369, -19.4365, 247.774, 12.8369, -19.4365, 230.621, 12.8369, 24.7155, 247.801, 12.8368, -19.4348, 230.621, 12.8369, 24.7155, 233.986, 12.8369, 26.1093, 247.801, 12.8368, -19.4348, 240.474, 12.1977, -19.8891, 227.354, 12.1977, 23.3622, 244.017, 12.8251, -19.6694, 244.017, 12.8251, -19.6694, 227.354, 12.1977, 23.3622, 244.083, 12.8369, -19.6653, 227.354, 12.1977, 23.3622, 230.621, 12.8369, 24.7155, 244.083, 12.8369, -19.6653, 237.182, 10.9564, -20.0932, 224.374, 10.9564, 22.1281, 240.377, 12.1612, -19.8951, 240.377, 12.1612, -19.8951, 224.374, 10.9564, 22.1281, 240.474, 12.1977, -19.8891, 224.374, 10.9564, 22.1281, 227.354, 12.1977, 23.3622, 240.474, 12.1977, -19.8891, 234.399, 9.1853, -20.2658, 221.855, 9.1853, 21.0848, 237.072, 10.8864, -20.1, 237.072, 10.8864, -20.1, 221.855, 9.1853, 21.0848, 237.182, 10.9564, -20.0932, 221.855, 9.1853, 21.0848, 224.374, 10.9564, 22.1281, 237.182, 10.9564, -20.0932, 232.287, 6.9872, -20.3967, 219.944, 6.9872, 20.2931, 234.297, 9.0794, -20.2721, 234.297, 9.0794, -20.2721, 219.944, 6.9872, 20.2931, 234.399, 9.1853, -20.2658, 219.944, 6.9872, 20.2931, 221.855, 9.1853, 21.0848, 234.399, 9.1853, -20.2658, 230.969, 4.4899, -20.4785, 218.751, 4.4899, 19.7988, 232.215, 6.8504, -20.4012, 232.215, 6.8504, -20.4012, 218.751, 4.4899, 19.7988, 232.287, 6.9872, -20.3967, 218.751, 4.4899, 19.7988, 219.944, 6.9872, 20.2931, 232.287, 6.9872, -20.3967, 230.521, 1.8384, -20.5062, 218.345, 1.8384, 19.6308, 230.942, 4.3342, -20.4801, 230.942, 4.3342, -20.4801, 218.345, 1.8384, 19.6308, 230.969, 4.4899, -20.4785, 218.345, 1.8384, 19.6308, 218.751, 4.4899, 19.7988, 230.969, 4.4899, -20.4785, 230.969, -0.813, -20.4785, 218.751, -0.813, 19.7988, 230.547, 1.6799, -20.5046, 230.547, 1.6799, -20.5046, 218.751, -0.813, 19.7988, 230.521, 1.8384, -20.5062, 218.751, -0.813, 19.7988, 218.345, 1.8384, 19.6308, 230.521, 1.8384, -20.5062, 232.287, -3.3103, -20.3967, 219.944, -3.3103, 20.2931, 231.045, -0.9577, -20.4737, 231.045, -0.9577, -20.4737, 219.944, -3.3103, 20.2931, 230.969, -0.813, -20.4785, 219.944, -3.3103, 20.2931, 218.751, -0.813, 19.7988, 230.969, -0.813, -20.4785, 234.399, -5.5084, -20.2658, 221.855, -5.5085, 21.0848, 232.4, -3.4277, -20.3897, 232.4, -3.4277, -20.3897, 221.855, -5.5085, 21.0848, 232.287, -3.3103, -20.3967, 221.855, -5.5085, 21.0848, 219.944, -3.3103, 20.2931, 232.287, -3.3103, -20.3967, 237.182, -7.2796, -20.0932, 224.374, -7.2796, 22.1281, 234.528, -5.5908, -20.2577, 234.528, -5.5908, -20.2577, 224.374, -7.2796, 22.1281, 234.399, -5.5084, -20.2658, 224.374, -7.2796, 22.1281, 221.855, -5.5085, 21.0848, 234.399, -5.5084, -20.2658, 240.474, -8.5208, -19.8891, 227.354, -8.5208, 23.3622, 237.306, -7.3265, -20.0855, 237.306, -7.3265, -20.0855, 227.354, -8.5208, 23.3622, 237.182, -7.2796, -20.0932, 227.354, -8.5208, 23.3622, 224.374, -7.2796, 22.1281, 237.182, -7.2796, -20.0932, 244.083, -9.16, -19.6653, 230.621, -9.16, 24.7155, 240.575, -8.5387, -19.8829, 240.575, -8.5387, -19.8829, 230.621, -9.16, 24.7155, 240.474, -8.5208, -19.8891, 230.621, -9.16, 24.7155, 227.354, -8.5208, 23.3622, 240.474, -8.5208, -19.8891, 247.801, -9.16, -19.4348, 233.986, -9.16, 26.1093, 244.148, -9.16, -19.6613, 244.148, -9.16, -19.6613, 233.986, -9.16, 26.1093, 244.083, -9.16, -19.6653, 233.986, -9.16, 26.1093, 230.621, -9.16, 24.7155, 244.083, -9.16, -19.6653, 250.357, -8.7074, -19.2763, 251.37, -8.5208, -19.0749, 237.253, -8.5208, 27.4626, 250.357, -8.7074, -19.2763, 237.253, -8.5208, 27.4626, 247.827, -9.1555, -19.4332, 247.827, -9.1555, -19.4332, 237.253, -8.5208, 27.4626, 247.801, -9.16, -19.4348, 237.253, -8.5208, 27.4626, 233.986, -9.16, 26.1093, 247.801, -9.16, -19.4348, 251.37, -8.5208, -19.0749, 254.533, -7.2796, -18.4458, 240.232, -7.2796, 28.6967, 237.253, -8.5208, 27.4626, 251.37, -8.5208, -19.0749, 240.232, -7.2796, 28.6967, 254.533, -7.2796, -18.4458, 257.206, -5.5084, -17.9139, 242.751, -5.5084, 29.74, 240.232, -7.2796, 28.6967, 254.533, -7.2796, -18.4458, 242.751, -5.5084, 29.74, 257.206, -5.5084, -17.9139, 259.236, -3.3103, -17.5103, 244.662, -3.3103, 30.5317, 242.751, -5.5084, 29.74, 257.206, -5.5084, -17.9139, 244.662, -3.3103, 30.5317, 259.236, -3.3103, -17.5103, 260.502, -0.813, -17.2583, 245.855, -0.813, 31.026, 244.662, -3.3103, 30.5317, 259.236, -3.3103, -17.5103, 245.855, -0.813, 31.026, 260.502, -0.813, -17.2583, 260.933, 1.8384, -17.1727, 246.261, 1.8384, 31.194, 245.855, -0.813, 31.026, 260.502, -0.813, -17.2583, 246.261, 1.8384, 31.194, 246.261, 1.8384, 31.194, 245.855, 4.4899, 31.026, 222.07, 4.4899, 75.5251, 222.435, 1.8384, 75.769, 246.261, 1.8384, 31.194, 222.07, 4.4899, 75.5251, 245.855, 4.4899, 31.026, 244.662, 6.9872, 30.5317, 220.996, 6.9872, 74.8075, 222.07, 4.4899, 75.5251, 245.855, 4.4899, 31.026, 220.996, 6.9872, 74.8075, 244.662, 6.9872, 30.5317, 242.751, 9.1853, 29.74, 219.276, 9.1853, 73.6581, 220.996, 6.9872, 74.8075, 244.662, 6.9872, 30.5317, 219.276, 9.1853, 73.6581, 242.751, 9.1853, 29.74, 240.232, 10.9564, 28.6967, 217.009, 10.9564, 72.1435, 219.276, 9.1853, 73.6581, 242.751, 9.1853, 29.74, 217.009, 10.9564, 72.1435, 240.232, 10.9564, 28.6967, 237.253, 12.1977, 27.4626, 214.328, 12.1977, 70.3518, 217.009, 10.9564, 72.1435, 240.232, 10.9564, 28.6967, 214.328, 12.1977, 70.3518, 237.253, 12.1977, 27.4626, 233.986, 12.8369, 26.1093, 211.388, 12.8369, 68.3871, 214.328, 12.1977, 70.3518, 237.253, 12.1977, 27.4626, 211.388, 12.8369, 68.3871, 233.986, 12.8369, 26.1093, 230.621, 12.8369, 24.7155, 208.359, 12.8369, 66.3637, 211.388, 12.8369, 68.3871, 233.986, 12.8369, 26.1093, 208.359, 12.8369, 66.3637, 230.621, 12.8369, 24.7155, 227.354, 12.1977, 23.3622, 205.419, 12.1977, 64.399, 208.359, 12.8369, 66.3637, 230.621, 12.8369, 24.7155, 205.419, 12.1977, 64.399, 227.354, 12.1977, 23.3622, 224.374, 10.9564, 22.1281, 202.737, 10.9564, 62.6073, 205.419, 12.1977, 64.399, 227.354, 12.1977, 23.3622, 202.737, 10.9564, 62.6073, 224.374, 10.9564, 22.1281, 221.855, 9.1853, 21.0848, 200.471, 9.1853, 61.0927, 202.737, 10.9564, 62.6073, 224.374, 10.9564, 22.1281, 200.471, 9.1853, 61.0927, 221.855, 9.1853, 21.0848, 219.944, 6.9872, 20.2931, 198.75, 6.9872, 59.9433, 200.471, 9.1853, 61.0927, 221.855, 9.1853, 21.0848, 198.75, 6.9872, 59.9433, 219.944, 6.9872, 20.2931, 218.751, 4.4899, 19.7988, 197.677, 4.4899, 59.2257, 198.75, 6.9872, 59.9433, 219.944, 6.9872, 20.2931, 197.677, 4.4899, 59.2257, 218.751, 4.4899, 19.7988, 218.345, 1.8384, 19.6308, 197.312, 1.8384, 58.9819, 197.677, 4.4899, 59.2257, 218.751, 4.4899, 19.7988, 197.312, 1.8384, 58.9819, 218.345, 1.8384, 19.6308, 218.751, -0.813, 19.7988, 197.677, -0.813, 59.2257, 197.312, 1.8384, 58.9819, 218.345, 1.8384, 19.6308, 197.677, -0.813, 59.2257, 218.751, -0.813, 19.7988, 219.944, -3.3103, 20.2931, 198.75, -3.3103, 59.9433, 197.677, -0.813, 59.2257, 218.751, -0.813, 19.7988, 198.75, -3.3103, 59.9433, 219.944, -3.3103, 20.2931, 221.855, -5.5085, 21.0848, 200.471, -5.5084, 61.0927, 198.75, -3.3103, 59.9433, 219.944, -3.3103, 20.2931, 200.471, -5.5084, 61.0927, 221.855, -5.5085, 21.0848, 224.374, -7.2796, 22.1281, 202.737, -7.2796, 62.6073, 200.471, -5.5084, 61.0927, 221.855, -5.5085, 21.0848, 202.737, -7.2796, 62.6073, 224.374, -7.2796, 22.1281, 227.354, -8.5208, 23.3622, 205.419, -8.5208, 64.399, 202.737, -7.2796, 62.6073, 224.374, -7.2796, 22.1281, 205.419, -8.5208, 64.399, 227.354, -8.5208, 23.3622, 230.621, -9.16, 24.7155, 208.359, -9.16, 66.3637, 205.419, -8.5208, 64.399, 227.354, -8.5208, 23.3622, 208.359, -9.16, 66.3637, 230.621, -9.16, 24.7155, 233.986, -9.16, 26.1093, 211.388, -9.16, 68.3871, 208.359, -9.16, 66.3637, 230.621, -9.16, 24.7155, 211.388, -9.16, 68.3871, 233.986, -9.16, 26.1093, 237.253, -8.5208, 27.4626, 214.328, -8.5208, 70.3518, 211.388, -9.16, 68.3871, 233.986, -9.16, 26.1093, 214.328, -8.5208, 70.3518, 237.253, -8.5208, 27.4626, 240.232, -7.2796, 28.6967, 217.009, -7.2796, 72.1435, 214.328, -8.5208, 70.3518, 237.253, -8.5208, 27.4626, 217.009, -7.2796, 72.1435, 240.232, -7.2796, 28.6967, 242.751, -5.5084, 29.74, 219.276, -5.5084, 73.6581, 217.009, -7.2796, 72.1435, 240.232, -7.2796, 28.6967, 219.276, -5.5084, 73.6581, 242.751, -5.5084, 29.74, 244.662, -3.3103, 30.5317, 220.996, -3.3103, 74.8075, 219.276, -5.5084, 73.6581, 242.751, -5.5084, 29.74, 220.996, -3.3103, 74.8075, 244.662, -3.3103, 30.5317, 245.855, -0.813, 31.026, 222.07, -0.813, 75.5251, 220.996, -3.3103, 74.8075, 244.662, -3.3103, 30.5317, 222.07, -0.813, 75.5251, 245.855, -0.813, 31.026, 246.261, 1.8384, 31.194, 222.435, 1.8384, 75.769, 222.07, -0.813, 75.5251, 245.855, -0.813, 31.026, 222.435, 1.8384, 75.769, 222.435, 1.8384, 75.769, 222.07, 4.4899, 75.5251, 190.061, 4.4899, 114.529, 190.371, 1.8384, 114.839, 222.435, 1.8384, 75.769, 190.061, 4.4899, 114.529, 222.07, 4.4899, 75.5251, 220.996, 6.9872, 74.8075, 189.147, 6.9872, 113.616, 190.061, 4.4899, 114.529, 222.07, 4.4899, 75.5251, 189.147, 6.9872, 113.616, 220.996, 6.9872, 74.8075, 219.276, 9.1853, 73.6581, 187.684, 9.1853, 112.153, 189.147, 6.9872, 113.616, 220.996, 6.9872, 74.8075, 187.684, 9.1853, 112.153, 219.276, 9.1853, 73.6581, 217.009, 10.9564, 72.1435, 185.757, 10.9564, 110.225, 187.684, 9.1853, 112.153, 219.276, 9.1853, 73.6581, 185.757, 10.9564, 110.225, 217.009, 10.9564, 72.1435, 214.328, 12.1977, 70.3518, 183.476, 12.1977, 107.944, 185.757, 10.9564, 110.225, 217.009, 10.9564, 72.1435, 183.476, 12.1977, 107.944, 214.328, 12.1977, 70.3518, 211.388, 12.8369, 68.3871, 180.976, 12.8369, 105.444, 183.476, 12.1977, 107.944, 214.328, 12.1977, 70.3518, 180.976, 12.8369, 105.444, 211.388, 12.8369, 68.3871, 208.359, 12.8369, 66.3637, 178.4, 12.8369, 102.869, 180.976, 12.8369, 105.444, 211.388, 12.8369, 68.3871, 178.4, 12.8369, 102.869, 208.359, 12.8369, 66.3637, 205.419, 12.1977, 64.399, 175.9, 12.1977, 100.368, 178.4, 12.8369, 102.869, 208.359, 12.8369, 66.3637, 175.9, 12.1977, 100.368, 205.419, 12.1977, 64.399, 202.737, 10.9564, 62.6073, 173.62, 10.9564, 98.0877, 175.9, 12.1977, 100.368, 205.419, 12.1977, 64.399, 173.62, 10.9564, 98.0877, 202.737, 10.9564, 62.6073, 200.471, 9.1853, 61.0927, 171.692, 9.1853, 96.1599, 173.62, 10.9564, 98.0877, 202.737, 10.9564, 62.6073, 171.692, 9.1853, 96.1599, 200.471, 9.1853, 61.0927, 198.75, 6.9872, 59.9433, 170.229, 6.9872, 94.697, 171.692, 9.1853, 96.1599, 200.471, 9.1853, 61.0927, 170.229, 6.9872, 94.697, 198.75, 6.9872, 59.9433, 197.677, 4.4899, 59.2257, 169.316, 4.4899, 93.7837, 170.229, 6.9872, 94.697, 198.75, 6.9872, 59.9433, 169.316, 4.4899, 93.7837, 197.677, 4.4899, 59.2257, 197.312, 1.8384, 58.9819, 169.005, 1.8384, 93.4733, 169.316, 4.4899, 93.7837, 197.677, 4.4899, 59.2257, 169.005, 1.8384, 93.4733, 197.312, 1.8384, 58.9819, 197.677, -0.813, 59.2257, 169.316, -0.813, 93.7837, 169.005, 1.8384, 93.4733, 197.312, 1.8384, 58.9819, 169.316, -0.813, 93.7837, 197.677, -0.813, 59.2257, 198.75, -3.3103, 59.9433, 170.229, -3.3103, 94.697, 169.316, -0.813, 93.7837, 197.677, -0.813, 59.2257, 170.229, -3.3103, 94.697, 198.75, -3.3103, 59.9433, 200.471, -5.5084, 61.0927, 171.692, -5.5084, 96.1599, 170.229, -3.3103, 94.697, 198.75, -3.3103, 59.9433, 171.692, -5.5084, 96.1599, 200.471, -5.5084, 61.0927, 202.737, -7.2796, 62.6073, 173.62, -7.2796, 98.0877, 171.692, -5.5084, 96.1599, 200.471, -5.5084, 61.0927, 173.62, -7.2796, 98.0877, 202.737, -7.2796, 62.6073, 205.419, -8.5208, 64.399, 175.9, -8.5208, 100.368, 173.62, -7.2796, 98.0877, 202.737, -7.2796, 62.6073, 175.9, -8.5208, 100.368, 205.419, -8.5208, 64.399, 208.359, -9.16, 66.3637, 178.4, -9.16, 102.869, 175.9, -8.5208, 100.368, 205.419, -8.5208, 64.399, 178.4, -9.16, 102.869, 208.359, -9.16, 66.3637, 211.388, -9.16, 68.3871, 180.976, -9.16, 105.444, 178.4, -9.16, 102.869, 208.359, -9.16, 66.3637, 180.976, -9.16, 105.444, 211.388, -9.16, 68.3871, 214.328, -8.5208, 70.3518, 183.476, -8.5208, 107.944, 180.976, -9.16, 105.444, 211.388, -9.16, 68.3871, 183.476, -8.5208, 107.944, 214.328, -8.5208, 70.3518, 217.009, -7.2796, 72.1435, 185.757, -7.2796, 110.225, 183.476, -8.5208, 107.944, 214.328, -8.5208, 70.3518, 185.757, -7.2796, 110.225, 217.009, -7.2796, 72.1435, 219.276, -5.5084, 73.6581, 187.684, -5.5084, 112.153, 185.757, -7.2796, 110.225, 217.009, -7.2796, 72.1435, 187.684, -5.5084, 112.153, 219.276, -5.5084, 73.6581, 220.996, -3.3103, 74.8075, 189.147, -3.3103, 113.616, 187.684, -5.5084, 112.153, 219.276, -5.5084, 73.6581, 189.147, -3.3103, 113.616, 220.996, -3.3103, 74.8075, 222.07, -0.813, 75.5251, 190.061, -0.813, 114.529, 189.147, -3.3103, 113.616, 220.996, -3.3103, 74.8075, 190.061, -0.813, 114.529, 222.07, -0.813, 75.5251, 222.435, 1.8384, 75.769, 190.371, 1.8384, 114.839, 190.061, -0.813, 114.529, 222.07, -0.813, 75.5251, 190.371, 1.8384, 114.839, 190.371, 1.8384, 114.839, 190.061, 4.4899, 114.529, 151.057, 4.4899, 146.538, 151.301, 1.8384, 146.903, 190.371, 1.8384, 114.839, 151.057, 4.4899, 146.538, 190.061, 4.4899, 114.529, 189.147, 6.9872, 113.616, 150.339, 6.9872, 145.464, 151.057, 4.4899, 146.538, 190.061, 4.4899, 114.529, 150.339, 6.9872, 145.464, 189.147, 6.9872, 113.616, 187.684, 9.1853, 112.153, 149.19, 9.1853, 143.744, 150.339, 6.9872, 145.464, 189.147, 6.9872, 113.616, 149.19, 9.1853, 143.744, 187.684, 9.1853, 112.153, 185.757, 10.9564, 110.225, 147.675, 10.9564, 141.477, 149.19, 9.1853, 143.744, 187.684, 9.1853, 112.153, 147.675, 10.9564, 141.477, 185.757, 10.9564, 110.225, 183.476, 12.1977, 107.944, 145.884, 12.1977, 138.796, 147.675, 10.9564, 141.477, 185.757, 10.9564, 110.225, 145.884, 12.1977, 138.796, 183.476, 12.1977, 107.944, 180.976, 12.8369, 105.444, 143.919, 12.8369, 135.856, 145.884, 12.1977, 138.796, 183.476, 12.1977, 107.944, 143.919, 12.8369, 135.856, 180.976, 12.8369, 105.444, 178.4, 12.8369, 102.869, 141.896, 12.8369, 132.827, 143.919, 12.8369, 135.856, 180.976, 12.8369, 105.444, 141.896, 12.8369, 132.827, 178.4, 12.8369, 102.869, 175.9, 12.1977, 100.368, 139.931, 12.1977, 129.887, 141.896, 12.8369, 132.827, 178.4, 12.8369, 102.869, 139.931, 12.1977, 129.887, 175.9, 12.1977, 100.368, 173.62, 10.9564, 98.0877, 138.139, 10.9564, 127.206, 139.931, 12.1977, 129.887, 175.9, 12.1977, 100.368, 138.139, 10.9564, 127.206, 173.62, 10.9564, 98.0877, 171.692, 9.1853, 96.1599, 136.625, 9.1853, 124.939, 138.139, 10.9564, 127.206, 173.62, 10.9564, 98.0877, 136.625, 9.1853, 124.939, 171.692, 9.1853, 96.1599, 170.229, 6.9872, 94.697, 135.475, 6.9872, 123.219, 136.625, 9.1853, 124.939, 171.692, 9.1853, 96.1599, 135.475, 6.9872, 123.219, 170.229, 6.9872, 94.697, 169.316, 4.4899, 93.7837, 134.758, 4.4899, 122.145, 135.475, 6.9872, 123.219, 170.229, 6.9872, 94.697, 134.758, 4.4899, 122.145, 169.316, 4.4899, 93.7837, 169.005, 1.8384, 93.4733, 134.514, 1.8384, 121.78, 134.758, 4.4899, 122.145, 169.316, 4.4899, 93.7837, 134.514, 1.8384, 121.78, 169.005, 1.8384, 93.4733, 169.316, -0.813, 93.7837, 134.758, -0.813, 122.145, 134.514, 1.8384, 121.78, 169.005, 1.8384, 93.4733, 134.758, -0.813, 122.145, 169.316, -0.813, 93.7837, 170.229, -3.3103, 94.697, 135.475, -3.3103, 123.219, 134.758, -0.813, 122.145, 169.316, -0.813, 93.7837, 135.475, -3.3103, 123.219, 170.229, -3.3103, 94.697, 171.692, -5.5084, 96.1599, 136.625, -5.5084, 124.939, 135.475, -3.3103, 123.219, 170.229, -3.3103, 94.697, 136.625, -5.5084, 124.939, 171.692, -5.5084, 96.1599, 173.62, -7.2796, 98.0877, 138.139, -7.2796, 127.206, 136.625, -5.5084, 124.939, 171.692, -5.5084, 96.1599, 138.139, -7.2796, 127.206, 173.62, -7.2796, 98.0877, 175.9, -8.5208, 100.368, 139.931, -8.5208, 129.887, 138.139, -7.2796, 127.206, 173.62, -7.2796, 98.0877, 139.931, -8.5208, 129.887, 175.9, -8.5208, 100.368, 178.4, -9.16, 102.869, 141.896, -9.16, 132.827, 139.931, -8.5208, 129.887, 175.9, -8.5208, 100.368, 141.896, -9.16, 132.827, 178.4, -9.16, 102.869, 180.976, -9.16, 105.444, 143.919, -9.16, 135.856, 141.896, -9.16, 132.827, 178.4, -9.16, 102.869, 143.919, -9.16, 135.856, 180.976, -9.16, 105.444, 183.476, -8.5208, 107.944, 145.884, -8.5208, 138.796, 143.919, -9.16, 135.856, 180.976, -9.16, 105.444, 145.884, -8.5208, 138.796, 183.476, -8.5208, 107.944, 185.757, -7.2796, 110.225, 147.675, -7.2796, 141.477, 145.884, -8.5208, 138.796, 183.476, -8.5208, 107.944, 147.675, -7.2796, 141.477, 185.757, -7.2796, 110.225, 187.684, -5.5084, 112.153, 149.19, -5.5084, 143.744, 147.675, -7.2796, 141.477, 185.757, -7.2796, 110.225, 149.19, -5.5084, 143.744, 187.684, -5.5084, 112.153, 189.147, -3.3103, 113.616, 150.339, -3.3103, 145.464, 149.19, -5.5084, 143.744, 187.684, -5.5084, 112.153, 150.339, -3.3103, 145.464, 189.147, -3.3103, 113.616, 190.061, -0.813, 114.529, 151.057, -0.813, 146.538, 150.339, -3.3103, 145.464, 189.147, -3.3103, 113.616, 151.057, -0.813, 146.538, 190.061, -0.813, 114.529, 190.371, 1.8384, 114.839, 151.301, 1.8384, 146.903, 151.057, -0.813, 146.538, 190.061, -0.813, 114.529, 151.301, 1.8384, 146.903, 151.301, 1.8384, 146.903, 151.057, 4.4899, 146.538, 106.558, 4.4899, 170.324, 106.726, 1.8384, 170.729, 151.301, 1.8384, 146.903, 106.558, 4.4899, 170.324, 151.057, 4.4899, 146.538, 150.339, 6.9872, 145.464, 106.064, 6.9872, 169.13, 106.558, 4.4899, 170.324, 151.057, 4.4899, 146.538, 106.064, 6.9872, 169.13, 150.339, 6.9872, 145.464, 149.19, 9.1853, 143.744, 105.272, 9.1853, 167.219, 106.064, 6.9872, 169.13, 150.339, 6.9872, 145.464, 105.272, 9.1853, 167.219, 149.19, 9.1853, 143.744, 147.675, 10.9564, 141.477, 104.229, 10.9564, 164.7, 105.272, 9.1853, 167.219, 149.19, 9.1853, 143.744, 104.229, 10.9564, 164.7, 147.675, 10.9564, 141.477, 145.884, 12.1977, 138.796, 102.994, 12.1977, 161.721, 104.229, 10.9564, 164.7, 147.675, 10.9564, 141.477, 102.994, 12.1977, 161.721, 145.884, 12.1977, 138.796, 143.919, 12.8369, 135.856, 101.641, 12.8369, 158.454, 102.994, 12.1977, 161.721, 145.884, 12.1977, 138.796, 101.641, 12.8369, 158.454, 143.919, 12.8369, 135.856, 141.896, 12.8369, 132.827, 100.247, 12.8369, 155.089, 101.641, 12.8369, 158.454, 143.919, 12.8369, 135.856, 100.247, 12.8369, 155.089, 141.896, 12.8369, 132.827, 139.931, 12.1977, 129.887, 98.8941, 12.1977, 151.822, 100.247, 12.8369, 155.089, 141.896, 12.8369, 132.827, 98.8941, 12.1977, 151.822, 139.931, 12.1977, 129.887, 138.139, 10.9564, 127.206, 97.66, 10.9564, 148.842, 98.8941, 12.1977, 151.822, 139.931, 12.1977, 129.887, 97.66, 10.9564, 148.842, 138.139, 10.9564, 127.206, 136.625, 9.1853, 124.939, 96.6167, 9.1853, 146.324, 97.66, 10.9564, 148.842, 138.139, 10.9564, 127.206, 96.6167, 9.1853, 146.324, 136.625, 9.1853, 124.939, 135.475, 6.9872, 123.219, 95.825, 6.9872, 144.412, 96.6167, 9.1853, 146.324, 136.625, 9.1853, 124.939, 95.825, 6.9872, 144.412, 135.475, 6.9872, 123.219, 134.758, 4.4899, 122.145, 95.3307, 4.4899, 143.219, 95.825, 6.9872, 144.412, 135.475, 6.9872, 123.219, 95.3307, 4.4899, 143.219, 134.758, 4.4899, 122.145, 134.514, 1.8384, 121.78, 95.1627, 1.8384, 142.813, 95.3307, 4.4899, 143.219, 134.758, 4.4899, 122.145, 95.1627, 1.8384, 142.813, 134.514, 1.8384, 121.78, 134.758, -0.813, 122.145, 95.3307, -0.813, 143.219, 95.1627, 1.8384, 142.813, 134.514, 1.8384, 121.78, 95.3307, -0.813, 143.219, 134.758, -0.813, 122.145, 135.475, -3.3103, 123.219, 95.825, -3.3103, 144.412, 95.3307, -0.813, 143.219, 134.758, -0.813, 122.145, 95.825, -3.3103, 144.412, 135.475, -3.3103, 123.219, 136.625, -5.5084, 124.939, 96.6167, -5.5084, 146.324, 95.825, -3.3103, 144.412, 135.475, -3.3103, 123.219, 96.6167, -5.5084, 146.324, 136.625, -5.5084, 124.939, 138.139, -7.2796, 127.206, 97.66, -7.2796, 148.842, 96.6167, -5.5084, 146.324, 136.625, -5.5084, 124.939, 97.66, -7.2796, 148.842, 138.139, -7.2796, 127.206, 139.931, -8.5208, 129.887, 98.8941, -8.5208, 151.822, 97.66, -7.2796, 148.842, 138.139, -7.2796, 127.206, 98.8941, -8.5208, 151.822, 139.931, -8.5208, 129.887, 141.896, -9.16, 132.827, 100.247, -9.16, 155.089, 98.8941, -8.5208, 151.822, 139.931, -8.5208, 129.887, 100.247, -9.16, 155.089, 141.896, -9.16, 132.827, 143.919, -9.16, 135.856, 101.641, -9.16, 158.454, 100.247, -9.16, 155.089, 141.896, -9.16, 132.827, 101.641, -9.16, 158.454, 143.919, -9.16, 135.856, 145.884, -8.5208, 138.796, 102.994, -8.5208, 161.721, 101.641, -9.16, 158.454, 143.919, -9.16, 135.856, 102.994, -8.5208, 161.721, 145.884, -8.5208, 138.796, 147.675, -7.2796, 141.477, 104.229, -7.2796, 164.7, 102.994, -8.5208, 161.721, 145.884, -8.5208, 138.796, 104.229, -7.2796, 164.7, 147.675, -7.2796, 141.477, 149.19, -5.5084, 143.744, 105.272, -5.5084, 167.219, 104.229, -7.2796, 164.7, 147.675, -7.2796, 141.477, 105.272, -5.5084, 167.219, 149.19, -5.5084, 143.744, 150.339, -3.3103, 145.464, 106.064, -3.3103, 169.13, 105.272, -5.5084, 167.219, 149.19, -5.5084, 143.744, 106.064, -3.3103, 169.13, 150.339, -3.3103, 145.464, 151.057, -0.813, 146.538, 106.558, -0.813, 170.324, 106.064, -3.3103, 169.13, 150.339, -3.3103, 145.464, 106.558, -0.813, 170.324, 151.057, -0.813, 146.538, 151.301, 1.8384, 146.903, 106.726, 1.8384, 170.729, 106.558, -0.813, 170.324, 151.057, -0.813, 146.538, 106.726, 1.8384, 170.729, 106.726, 1.8384, 170.729, 106.558, 4.4899, 170.324, 58.2736, 4.4899, 184.97, 58.3592, 1.8384, 185.401, 106.726, 1.8384, 170.729, 58.2736, 4.4899, 184.97, 106.558, 4.4899, 170.324, 106.064, 6.9872, 169.13, 58.0216, 6.9872, 183.704, 58.2736, 4.4899, 184.97, 106.558, 4.4899, 170.324, 58.0216, 6.9872, 183.704, 106.064, 6.9872, 169.13, 105.272, 9.1853, 167.219, 57.618, 9.1853, 181.675, 58.0216, 6.9872, 183.704, 106.064, 6.9872, 169.13, 57.618, 9.1853, 181.675, 105.272, 9.1853, 167.219, 104.229, 10.9564, 164.7, 57.0861, 10.9564, 179.001, 57.618, 9.1853, 181.675, 105.272, 9.1853, 167.219, 57.0861, 10.9564, 179.001, 104.229, 10.9564, 164.7, 102.994, 12.1977, 161.721, 56.457, 12.1977, 175.838, 57.0861, 10.9564, 179.001, 104.229, 10.9564, 164.7, 56.457, 12.1977, 175.838, 102.994, 12.1977, 161.721, 101.641, 12.8369, 158.454, 55.7671, 12.8369, 172.369, 56.457, 12.1977, 175.838, 102.994, 12.1977, 161.721, 55.7671, 12.8369, 172.369, 101.641, 12.8369, 158.454, 100.247, 12.8369, 155.089, 55.0565, 12.8369, 168.797, 55.7671, 12.8369, 172.369, 101.641, 12.8369, 158.454, 55.0565, 12.8369, 168.797, 100.247, 12.8369, 155.089, 98.8941, 12.1977, 151.822, 54.3666, 12.1977, 165.329, 55.0565, 12.8369, 168.797, 100.247, 12.8369, 155.089, 54.3666, 12.1977, 165.329, 98.8941, 12.1977, 151.822, 97.66, 10.9564, 148.842, 53.7375, 10.9564, 162.166, 54.3666, 12.1977, 165.329, 98.8941, 12.1977, 151.822, 53.7375, 10.9564, 162.166, 97.66, 10.9564, 148.842, 96.6167, 9.1853, 146.324, 53.2056, 9.1853, 159.492, 53.7375, 10.9564, 162.166, 97.66, 10.9564, 148.842, 53.2056, 9.1853, 159.492, 96.6167, 9.1853, 146.324, 95.825, 6.9872, 144.412, 52.802, 6.9872, 157.463, 53.2056, 9.1853, 159.492, 96.6167, 9.1853, 146.324, 52.802, 6.9872, 157.463, 95.825, 6.9872, 144.412, 95.3307, 4.4899, 143.219, 52.55, 4.4899, 156.196, 52.802, 6.9872, 157.463, 95.825, 6.9872, 144.412, 52.55, 4.4899, 156.196, 95.3307, 4.4899, 143.219, 95.1627, 1.8384, 142.813, 52.4644, 1.8384, 155.766, 52.55, 4.4899, 156.196, 95.3307, 4.4899, 143.219, 52.4644, 1.8384, 155.766, 95.1627, 1.8384, 142.813, 95.3307, -0.813, 143.219, 52.55, -0.813, 156.196, 52.4644, 1.8384, 155.766, 95.1627, 1.8384, 142.813, 52.55, -0.813, 156.196, 95.3307, -0.813, 143.219, 95.825, -3.3103, 144.412, 52.802, -3.3103, 157.463, 52.55, -0.813, 156.196, 95.3307, -0.813, 143.219, 52.802, -3.3103, 157.463, 95.825, -3.3103, 144.412, 96.6167, -5.5084, 146.324, 53.2056, -5.5084, 159.492, 52.802, -3.3103, 157.463, 95.825, -3.3103, 144.412, 53.2056, -5.5084, 159.492, 96.6167, -5.5084, 146.324, 97.66, -7.2796, 148.842, 53.7375, -7.2796, 162.166, 53.2056, -5.5084, 159.492, 96.6167, -5.5084, 146.324, 53.7375, -7.2796, 162.166, 97.66, -7.2796, 148.842, 98.8941, -8.5208, 151.822, 54.3666, -8.5208, 165.329, 53.7375, -7.2796, 162.166, 97.66, -7.2796, 148.842, 54.3666, -8.5208, 165.329, 98.8941, -8.5208, 151.822, 100.247, -9.16, 155.089, 55.0565, -9.16, 168.797, 54.3666, -8.5208, 165.329, 98.8941, -8.5208, 151.822, 55.0565, -9.16, 168.797, 100.247, -9.16, 155.089, 101.641, -9.16, 158.454, 55.7671, -9.16, 172.369, 55.0565, -9.16, 168.797, 100.247, -9.16, 155.089, 55.7671, -9.16, 172.369, 101.641, -9.16, 158.454, 102.994, -8.5208, 161.721, 56.457, -8.5208, 175.838, 55.7671, -9.16, 172.369, 101.641, -9.16, 158.454, 56.457, -8.5208, 175.838, 102.994, -8.5208, 161.721, 104.229, -7.2796, 164.7, 57.0861, -7.2796, 179.001, 56.457, -8.5208, 175.838, 102.994, -8.5208, 161.721, 57.0861, -7.2796, 179.001, 104.229, -7.2796, 164.7, 105.272, -5.5084, 167.219, 57.618, -5.5084, 181.675, 57.0861, -7.2796, 179.001, 104.229, -7.2796, 164.7, 57.618, -5.5084, 181.675, 105.272, -5.5084, 167.219, 106.064, -3.3103, 169.13, 58.0216, -3.3103, 183.704, 57.618, -5.5084, 181.675, 105.272, -5.5084, 167.219, 58.0216, -3.3103, 183.704, 106.064, -3.3103, 169.13, 106.558, -0.813, 170.324, 58.2736, -0.813, 184.97, 58.0216, -3.3103, 183.704, 106.064, -3.3103, 169.13, 58.2736, -0.813, 184.97, 106.558, -0.813, 170.324, 106.726, 1.8384, 170.729, 58.3592, 1.8384, 185.401, 58.2736, -0.813, 184.97, 106.558, -0.813, 170.324, 58.3592, 1.8384, 185.401, 58.3592, 1.8384, 185.401, 58.2736, 4.4899, 184.97, 8.0596, 4.4899, 189.916, 8.0596, 1.8384, 190.355, 58.3592, 1.8384, 185.401, 8.0596, 4.4899, 189.916, 58.2736, 4.4899, 184.97, 58.0216, 6.9872, 183.704, 8.0596, 6.9872, 188.625, 8.0596, 4.4899, 189.916, 58.2736, 4.4899, 184.97, 8.0596, 6.9872, 188.625, 58.0216, 6.9872, 183.704, 57.618, 9.1853, 181.675, 8.0596, 9.1853, 186.556, 8.0596, 6.9872, 188.625, 58.0216, 6.9872, 183.704, 8.0596, 9.1853, 186.556, 57.618, 9.1853, 181.675, 57.0861, 10.9564, 179.001, 8.0596, 10.9564, 183.829, 8.0596, 9.1853, 186.556, 57.618, 9.1853, 181.675, 8.0596, 10.9564, 183.829, 57.0861, 10.9564, 179.001, 56.457, 12.1977, 175.838, 8.0596, 12.1977, 180.604, 8.0596, 10.9564, 183.829, 57.0861, 10.9564, 179.001, 8.0596, 12.1977, 180.604, 56.457, 12.1977, 175.838, 55.7671, 12.8369, 172.369, 8.0596, 12.8369, 177.068, 8.0596, 12.1977, 180.604, 56.457, 12.1977, 175.838, 8.0596, 12.8369, 177.068, 55.7671, 12.8369, 172.369, 55.0565, 12.8369, 168.797, 8.0596, 12.8369, 173.426, 8.0596, 12.8369, 177.068, 55.7671, 12.8369, 172.369, 8.0596, 12.8369, 173.426, 55.0565, 12.8369, 168.797, 54.3666, 12.1977, 165.329, 8.0596, 12.1977, 169.89, 8.0596, 12.8369, 173.426, 55.0565, 12.8369, 168.797, 8.0596, 12.1977, 169.89, 54.3666, 12.1977, 165.329, 53.7375, 10.9564, 162.166, 8.0596, 10.9564, 166.665, 8.0596, 12.1977, 169.89, 54.3666, 12.1977, 165.329, 8.0596, 10.9564, 166.665, 53.7375, 10.9564, 162.166, 53.2056, 9.1853, 159.492, 8.0596, 9.1853, 163.939, 8.0596, 10.9564, 166.665, 53.7375, 10.9564, 162.166, 8.0596, 9.1853, 163.939, 53.2056, 9.1853, 159.492, 52.802, 6.9872, 157.463, 8.0596, 6.9872, 161.87, 8.0596, 9.1853, 163.939, 53.2056, 9.1853, 159.492, 8.0596, 6.9872, 161.87, 52.802, 6.9872, 157.463, 52.55, 4.4899, 156.196, 8.0596, 4.4899, 160.578, 8.0596, 6.9872, 161.87, 52.802, 6.9872, 157.463, 8.0596, 4.4899, 160.578, 52.55, 4.4899, 156.196, 52.4644, 1.8384, 155.766, 8.0596, 1.8384, 160.139, 8.0596, 4.4899, 160.578, 52.55, 4.4899, 156.196, 8.0596, 1.8384, 160.139, 52.4644, 1.8384, 155.766, 52.55, -0.813, 156.196, 8.0596, -0.813, 160.578, 8.0596, 1.8384, 160.139, 52.4644, 1.8384, 155.766, 8.0596, -0.813, 160.578, 52.55, -0.813, 156.196, 52.802, -3.3103, 157.463, 8.0596, -3.3103, 161.87, 8.0596, -0.813, 160.578, 52.55, -0.813, 156.196, 8.0596, -3.3103, 161.87, 52.802, -3.3103, 157.463, 53.2056, -5.5084, 159.492, 8.0596, -5.5084, 163.939, 8.0596, -3.3103, 161.87, 52.802, -3.3103, 157.463, 8.0596, -5.5084, 163.939, 53.2056, -5.5084, 159.492, 53.7375, -7.2796, 162.166, 8.0596, -7.2796, 166.665, 8.0596, -5.5084, 163.939, 53.2056, -5.5084, 159.492, 8.0596, -7.2796, 166.665, 53.7375, -7.2796, 162.166, 54.3666, -8.5208, 165.329, 8.0596, -8.5208, 169.89, 8.0596, -7.2796, 166.665, 53.7375, -7.2796, 162.166, 8.0596, -8.5208, 169.89, 54.3666, -8.5208, 165.329, 55.0565, -9.16, 168.797, 8.0596, -9.16, 173.426, 8.0596, -8.5208, 169.89, 54.3666, -8.5208, 165.329, 8.0596, -9.16, 173.426, 55.0565, -9.16, 168.797, 55.7671, -9.16, 172.369, 8.0596, -9.16, 177.068, 8.0596, -9.16, 173.426, 55.0565, -9.16, 168.797, 8.0596, -9.16, 177.068, 55.7671, -9.16, 172.369, 56.457, -8.5208, 175.838, 8.0596, -8.5208, 180.604, 8.0596, -9.16, 177.068, 55.7671, -9.16, 172.369, 8.0596, -8.5208, 180.604, 56.457, -8.5208, 175.838, 57.0861, -7.2796, 179.001, 8.0596, -7.2796, 183.829, 8.0596, -8.5208, 180.604, 56.457, -8.5208, 175.838, 8.0596, -7.2796, 183.829, 57.0861, -7.2796, 179.001, 57.618, -5.5084, 181.675, 8.0596, -5.5084, 186.556, 8.0596, -7.2796, 183.829, 57.0861, -7.2796, 179.001, 8.0596, -5.5084, 186.556, 57.618, -5.5084, 181.675, 58.0216, -3.3103, 183.704, 8.0596, -3.3103, 188.625, 8.0596, -5.5084, 186.556, 57.618, -5.5084, 181.675, 8.0596, -3.3103, 188.625, 58.0216, -3.3103, 183.704, 58.2736, -0.813, 184.97, 8.0596, -0.813, 189.916, 8.0596, -3.3103, 188.625, 58.0216, -3.3103, 183.704, 8.0596, -0.813, 189.916, 58.2736, -0.813, 184.97, 58.3592, 1.8384, 185.401, 8.0596, 1.8384, 190.355, 8.0596, -0.813, 189.916, 58.2736, -0.813, 184.97, 8.0596, 1.8384, 190.355, 8.0596, 1.8384, 190.355, 8.0596, 4.4899, 189.916, -42.1544, 4.4899, 184.97, -42.2401, 1.8384, 185.401, 8.0596, 1.8384, 190.355, -42.1544, 4.4899, 184.97, 8.0596, 4.4899, 189.916, 8.0596, 6.9872, 188.625, -41.9025, 6.9872, 183.704, -42.1544, 4.4899, 184.97, 8.0596, 4.4899, 189.916, -41.9025, 6.9872, 183.704, 8.0596, 6.9872, 188.625, 8.0596, 9.1853, 186.556, -41.4988, 9.1853, 181.675, -41.9025, 6.9872, 183.704, 8.0596, 6.9872, 188.625, -41.4988, 9.1853, 181.675, 8.0596, 9.1853, 186.556, 8.0596, 10.9564, 183.829, -40.967, 10.9564, 179.001, -41.4988, 9.1853, 181.675, 8.0596, 9.1853, 186.556, -40.967, 10.9564, 179.001, 8.0596, 10.9564, 183.829, 8.0596, 12.1977, 180.604, -40.3378, 12.1977, 175.838, -40.967, 10.9564, 179.001, 8.0596, 10.9564, 183.829, -40.3378, 12.1977, 175.838, 8.0596, 12.1977, 180.604, 8.0596, 12.8369, 177.068, -39.6479, 12.8369, 172.369, -40.3378, 12.1977, 175.838, 8.0596, 12.1977, 180.604, -39.6479, 12.8369, 172.369, 8.0596, 12.8369, 177.068, 8.0596, 12.8369, 173.426, -38.9374, 12.8369, 168.797, -39.6479, 12.8369, 172.369, 8.0596, 12.8369, 177.068, -38.9374, 12.8369, 168.797, 8.0596, 12.8369, 173.426, 8.0596, 12.1977, 169.89, -38.2475, 12.1977, 165.329, -38.9374, 12.8369, 168.797, 8.0596, 12.8369, 173.426, -38.2475, 12.1977, 165.329, 8.0596, 12.1977, 169.89, 8.0596, 10.9564, 166.665, -37.6183, 10.9564, 162.166, -38.2475, 12.1977, 165.329, 8.0596, 12.1977, 169.89, -37.6183, 10.9564, 162.166, 8.0596, 10.9564, 166.665, 8.0596, 9.1853, 163.939, -37.0865, 9.1853, 159.492, -37.6183, 10.9564, 162.166, 8.0596, 10.9564, 166.665, -37.0865, 9.1853, 159.492, 8.0596, 9.1853, 163.939, 8.0596, 6.9872, 161.87, -36.6828, 6.9872, 157.463, -37.0865, 9.1853, 159.492, 8.0596, 9.1853, 163.939, -36.6828, 6.9872, 157.463, 8.0596, 6.9872, 161.87, 8.0596, 4.4899, 160.578, -36.4309, 4.4899, 156.196, -36.6828, 6.9872, 157.463, 8.0596, 6.9872, 161.87, -36.4309, 4.4899, 156.196, 8.0596, 4.4899, 160.578, 8.0596, 1.8384, 160.139, -36.3452, 1.8384, 155.766, -36.4309, 4.4899, 156.196, 8.0596, 4.4899, 160.578, -36.3452, 1.8384, 155.766, 8.0596, 1.8384, 160.139, 8.0596, -0.813, 160.578, -36.4309, -0.813, 156.196, -36.3452, 1.8384, 155.766, 8.0596, 1.8384, 160.139, -36.4309, -0.813, 156.196, 8.0596, -0.813, 160.578, 8.0596, -3.3103, 161.87, -36.6828, -3.3103, 157.463, -36.4309, -0.813, 156.196, 8.0596, -0.813, 160.578, -36.6828, -3.3103, 157.463, 8.0596, -3.3103, 161.87, 8.0596, -5.5084, 163.939, -37.0865, -5.5084, 159.492, -36.6828, -3.3103, 157.463, 8.0596, -3.3103, 161.87, -37.0865, -5.5084, 159.492, 8.0596, -5.5084, 163.939, 8.0596, -7.2796, 166.665, -37.6183, -7.2796, 162.166, -37.0865, -5.5084, 159.492, 8.0596, -5.5084, 163.939, -37.6183, -7.2796, 162.166, 8.0596, -7.2796, 166.665, 8.0596, -8.5208, 169.89, -38.2475, -8.5208, 165.329, -37.6183, -7.2796, 162.166, 8.0596, -7.2796, 166.665, -38.2475, -8.5208, 165.329, 8.0596, -8.5208, 169.89, 8.0596, -9.16, 173.426, -38.9374, -9.16, 168.797, -38.2475, -8.5208, 165.329, 8.0596, -8.5208, 169.89, -38.9374, -9.16, 168.797, 8.0596, -9.16, 173.426, 8.0596, -9.16, 177.068, -39.6479, -9.16, 172.369, -38.9374, -9.16, 168.797, 8.0596, -9.16, 173.426, -39.6479, -9.16, 172.369, 8.0596, -9.16, 177.068, 8.0596, -8.5208, 180.604, -40.3378, -8.5208, 175.838, -39.6479, -9.16, 172.369, 8.0596, -9.16, 177.068, -40.3378, -8.5208, 175.838, 8.0596, -8.5208, 180.604, 8.0596, -7.2796, 183.829, -40.967, -7.2796, 179.001, -40.3378, -8.5208, 175.838, 8.0596, -8.5208, 180.604, -40.967, -7.2796, 179.001, 8.0596, -7.2796, 183.829, 8.0596, -5.5084, 186.556, -41.4988, -5.5084, 181.675, -40.967, -7.2796, 179.001, 8.0596, -7.2796, 183.829, -41.4988, -5.5084, 181.675, 8.0596, -5.5084, 186.556, 8.0596, -3.3103, 188.625, -41.9025, -3.3103, 183.704, -41.4988, -5.5084, 181.675, 8.0596, -5.5084, 186.556, -41.9025, -3.3103, 183.704, 8.0596, -3.3103, 188.625, 8.0596, -0.813, 189.916, -42.1544, -0.813, 184.97, -41.9025, -3.3103, 183.704, 8.0596, -3.3103, 188.625, -42.1544, -0.813, 184.97, 8.0596, -0.813, 189.916, 8.0596, 1.8384, 190.355, -42.2401, 1.8384, 185.401, -42.1544, -0.813, 184.97, 8.0596, -0.813, 189.916, -42.2401, 1.8384, 185.401, -42.2401, 1.8384, 185.401, -42.1544, 4.4899, 184.97, -90.4387, 4.4899, 170.324, -90.6067, 1.8384, 170.729, -42.2401, 1.8384, 185.401, -90.4387, 4.4899, 170.324, -42.1544, 4.4899, 184.97, -41.9025, 6.9872, 183.704, -89.9445, 6.9872, 169.13, -90.4387, 4.4899, 170.324, -42.1544, 4.4899, 184.97, -89.9445, 6.9872, 169.13, -41.9025, 6.9872, 183.704, -41.4988, 9.1853, 181.675, -89.1527, 9.1853, 167.219, -89.9445, 6.9872, 169.13, -41.9025, 6.9872, 183.704, -89.1527, 9.1853, 167.219, -41.4988, 9.1853, 181.675, -40.967, 10.9564, 179.001, -88.1095, 10.9564, 164.7, -89.1527, 9.1853, 167.219, -41.4988, 9.1853, 181.675, -88.1095, 10.9564, 164.7, -40.967, 10.9564, 179.001, -40.3378, 12.1977, 175.838, -86.8753, 12.1977, 161.721, -88.1095, 10.9564, 164.7, -40.967, 10.9564, 179.001, -86.8753, 12.1977, 161.721, -40.3378, 12.1977, 175.838, -39.6479, 12.8369, 172.369, -85.522, 12.8369, 158.454, -86.8753, 12.1977, 161.721, -40.3378, 12.1977, 175.838, -85.522, 12.8369, 158.454, -39.6479, 12.8369, 172.369, -38.9374, 12.8369, 168.797, -84.1283, 12.8369, 155.089, -85.522, 12.8369, 158.454, -39.6479, 12.8369, 172.369, -84.1283, 12.8369, 155.089, -38.9374, 12.8369, 168.797, -38.2475, 12.1977, 165.329, -82.775, 12.1977, 151.822, -84.1283, 12.8369, 155.089, -38.9374, 12.8369, 168.797, -82.775, 12.1977, 151.822, -38.2475, 12.1977, 165.329, -37.6183, 10.9564, 162.166, -81.5408, 10.9564, 148.842, -82.775, 12.1977, 151.822, -38.2475, 12.1977, 165.329, -81.5408, 10.9564, 148.842, -37.6183, 10.9564, 162.166, -37.0865, 9.1853, 159.492, -80.4976, 9.1853, 146.324, -81.5408, 10.9564, 148.842, -37.6183, 10.9564, 162.166, -80.4976, 9.1853, 146.324, -37.0865, 9.1853, 159.492, -36.6828, 6.9872, 157.463, -79.7058, 6.9872, 144.412, -80.4976, 9.1853, 146.324, -37.0865, 9.1853, 159.492, -79.7058, 6.9872, 144.412, -36.6828, 6.9872, 157.463, -36.4309, 4.4899, 156.196, -79.2116, 4.4899, 143.219, -79.7058, 6.9872, 144.412, -36.6828, 6.9872, 157.463, -79.2116, 4.4899, 143.219, -36.4309, 4.4899, 156.196, -36.3452, 1.8384, 155.766, -79.0436, 1.8384, 142.813, -79.2116, 4.4899, 143.219, -36.4309, 4.4899, 156.196, -79.0436, 1.8384, 142.813, -36.3452, 1.8384, 155.766, -36.4309, -0.813, 156.196, -79.2116, -0.813, 143.219, -79.0436, 1.8384, 142.813, -36.3452, 1.8384, 155.766, -79.2116, -0.813, 143.219, -36.4309, -0.813, 156.196, -36.6828, -3.3103, 157.463, -79.7058, -3.3103, 144.412, -79.2116, -0.813, 143.219, -36.4309, -0.813, 156.196, -79.7058, -3.3103, 144.412, -36.6828, -3.3103, 157.463, -37.0865, -5.5084, 159.492, -80.4976, -5.5084, 146.324, -79.7058, -3.3103, 144.412, -36.6828, -3.3103, 157.463, -80.4976, -5.5084, 146.324, -37.0865, -5.5084, 159.492, -37.6183, -7.2796, 162.166, -81.5408, -7.2796, 148.842, -80.4976, -5.5084, 146.324, -37.0865, -5.5084, 159.492, -81.5408, -7.2796, 148.842, -37.6183, -7.2796, 162.166, -38.2475, -8.5208, 165.329, -82.775, -8.5208, 151.822, -81.5408, -7.2796, 148.842, -37.6183, -7.2796, 162.166, -82.775, -8.5208, 151.822, -38.2475, -8.5208, 165.329, -38.9374, -9.16, 168.797, -84.1283, -9.16, 155.089, -82.775, -8.5208, 151.822, -38.2475, -8.5208, 165.329, -84.1283, -9.16, 155.089, -38.9374, -9.16, 168.797, -39.6479, -9.16, 172.369, -85.522, -9.16, 158.454, -84.1283, -9.16, 155.089, -38.9374, -9.16, 168.797, -85.522, -9.16, 158.454, -39.6479, -9.16, 172.369, -40.3378, -8.5208, 175.838, -86.8753, -8.5208, 161.721, -85.522, -9.16, 158.454, -39.6479, -9.16, 172.369, -86.8753, -8.5208, 161.721, -40.3378, -8.5208, 175.838, -40.967, -7.2796, 179.001, -88.1095, -7.2796, 164.7, -86.8753, -8.5208, 161.721, -40.3378, -8.5208, 175.838, -88.1095, -7.2796, 164.7, -40.967, -7.2796, 179.001, -41.4988, -5.5084, 181.675, -89.1527, -5.5084, 167.219, -88.1095, -7.2796, 164.7, -40.967, -7.2796, 179.001, -89.1527, -5.5084, 167.219, -41.4988, -5.5084, 181.675, -41.9025, -3.3103, 183.704, -89.9445, -3.3103, 169.13, -89.1527, -5.5084, 167.219, -41.4988, -5.5084, 181.675, -89.9445, -3.3103, 169.13, -41.9025, -3.3103, 183.704, -42.1544, -0.813, 184.97, -90.4387, -0.813, 170.324, -89.9445, -3.3103, 169.13, -41.9025, -3.3103, 183.704, -90.4387, -0.813, 170.324, -42.1544, -0.813, 184.97, -42.2401, 1.8384, 185.401, -90.6067, 1.8384, 170.729, -90.4387, -0.813, 170.324, -42.1544, -0.813, 184.97, -90.6067, 1.8384, 170.729, -90.6067, 1.8384, 170.729, -90.4387, 4.4899, 170.324, -134.938, 4.4899, 146.538, -135.182, 1.8384, 146.903, -90.6067, 1.8384, 170.729, -134.938, 4.4899, 146.538, -90.4387, 4.4899, 170.324, -89.9445, 6.9872, 169.13, -134.22, 6.9872, 145.464, -134.938, 4.4899, 146.538, -90.4387, 4.4899, 170.324, -134.22, 6.9872, 145.464, -89.9445, 6.9872, 169.13, -89.1527, 9.1853, 167.219, -133.071, 9.1853, 143.744, -134.22, 6.9872, 145.464, -89.9445, 6.9872, 169.13, -133.071, 9.1853, 143.744, -89.1527, 9.1853, 167.219, -88.1095, 10.9564, 164.7, -131.556, 10.9564, 141.477, -133.071, 9.1853, 143.744, -89.1527, 9.1853, 167.219, -131.556, 10.9564, 141.477, -88.1095, 10.9564, 164.7, -86.8753, 12.1977, 161.721, -129.764, 12.1977, 138.796, -131.556, 10.9564, 141.477, -88.1095, 10.9564, 164.7, -129.764, 12.1977, 138.796, -86.8753, 12.1977, 161.721, -85.522, 12.8369, 158.454, -127.8, 12.8369, 135.856, -129.764, 12.1977, 138.796, -86.8753, 12.1977, 161.721, -127.8, 12.8369, 135.856, -85.522, 12.8369, 158.454, -84.1283, 12.8369, 155.089, -125.776, 12.8369, 132.827, -127.8, 12.8369, 135.856, -85.522, 12.8369, 158.454, -125.776, 12.8369, 132.827, -84.1283, 12.8369, 155.089, -82.775, 12.1977, 151.822, -123.812, 12.1977, 129.887, -125.776, 12.8369, 132.827, -84.1283, 12.8369, 155.089, -123.812, 12.1977, 129.887, -82.775, 12.1977, 151.822, -81.5408, 10.9564, 148.842, -122.02, 10.9564, 127.206, -123.812, 12.1977, 129.887, -82.775, 12.1977, 151.822, -122.02, 10.9564, 127.206, -81.5408, 10.9564, 148.842, -80.4976, 9.1853, 146.324, -120.505, 9.1853, 124.939, -122.02, 10.9564, 127.206, -81.5408, 10.9564, 148.842, -120.505, 9.1853, 124.939, -80.4976, 9.1853, 146.324, -79.7058, 6.9872, 144.412, -119.356, 6.9872, 123.219, -120.505, 9.1853, 124.939, -80.4976, 9.1853, 146.324, -119.356, 6.9872, 123.219, -79.7058, 6.9872, 144.412, -79.2116, 4.4899, 143.219, -118.638, 4.4899, 122.145, -119.356, 6.9872, 123.219, -79.7058, 6.9872, 144.412, -118.638, 4.4899, 122.145, -79.2116, 4.4899, 143.219, -79.0436, 1.8384, 142.813, -118.395, 1.8384, 121.78, -118.638, 4.4899, 122.145, -79.2116, 4.4899, 143.219, -118.395, 1.8384, 121.78, -79.0436, 1.8384, 142.813, -79.2116, -0.813, 143.219, -118.638, -0.813, 122.145, -118.395, 1.8384, 121.78, -79.0436, 1.8384, 142.813, -118.638, -0.813, 122.145, -79.2116, -0.813, 143.219, -79.7058, -3.3103, 144.412, -119.356, -3.3103, 123.219, -118.638, -0.813, 122.145, -79.2116, -0.813, 143.219, -119.356, -3.3103, 123.219, -79.7058, -3.3103, 144.412, -80.4976, -5.5084, 146.324, -120.505, -5.5084, 124.939, -119.356, -3.3103, 123.219, -79.7058, -3.3103, 144.412, -120.505, -5.5084, 124.939, -80.4976, -5.5084, 146.324, -81.5408, -7.2796, 148.842, -122.02, -7.2796, 127.206, -120.505, -5.5084, 124.939, -80.4976, -5.5084, 146.324, -122.02, -7.2796, 127.206, -81.5408, -7.2796, 148.842, -82.775, -8.5208, 151.822, -123.812, -8.5208, 129.887, -122.02, -7.2796, 127.206, -81.5408, -7.2796, 148.842, -123.812, -8.5208, 129.887, -82.775, -8.5208, 151.822, -84.1283, -9.16, 155.089, -125.776, -9.16, 132.827, -123.812, -8.5208, 129.887, -82.775, -8.5208, 151.822, -125.776, -9.16, 132.827, -84.1283, -9.16, 155.089, -85.522, -9.16, 158.454, -127.8, -9.16, 135.856, -125.776, -9.16, 132.827, -84.1283, -9.16, 155.089, -127.8, -9.16, 135.856, -85.522, -9.16, 158.454, -86.8753, -8.5208, 161.721, -129.764, -8.5208, 138.796, -127.8, -9.16, 135.856, -85.522, -9.16, 158.454, -129.764, -8.5208, 138.796, -86.8753, -8.5208, 161.721, -88.1095, -7.2796, 164.7, -131.556, -7.2796, 141.477, -129.764, -8.5208, 138.796, -86.8753, -8.5208, 161.721, -131.556, -7.2796, 141.477, -88.1095, -7.2796, 164.7, -89.1527, -5.5084, 167.219, -133.071, -5.5084, 143.744, -131.556, -7.2796, 141.477, -88.1095, -7.2796, 164.7, -133.071, -5.5084, 143.744, -89.1527, -5.5084, 167.219, -89.9445, -3.3103, 169.13, -134.22, -3.3103, 145.464, -133.071, -5.5084, 143.744, -89.1527, -5.5084, 167.219, -134.22, -3.3103, 145.464, -89.9445, -3.3103, 169.13, -90.4387, -0.813, 170.324, -134.938, -0.813, 146.538, -134.22, -3.3103, 145.464, -89.9445, -3.3103, 169.13, -134.938, -0.813, 146.538, -90.4387, -0.813, 170.324, -90.6067, 1.8384, 170.729, -135.182, 1.8384, 146.903, -134.938, -0.813, 146.538, -90.4387, -0.813, 170.324, -135.182, 1.8384, 146.903, -135.182, 1.8384, 146.903, -134.938, 4.4899, 146.538, -173.942, 4.4899, 114.529, -174.252, 1.8384, 114.839, -135.182, 1.8384, 146.903, -173.942, 4.4899, 114.529, -134.938, 4.4899, 146.538, -134.22, 6.9872, 145.464, -173.028, 6.9872, 113.616, -173.942, 4.4899, 114.529, -134.938, 4.4899, 146.538, -173.028, 6.9872, 113.616, -134.22, 6.9872, 145.464, -133.071, 9.1853, 143.744, -171.565, 9.1853, 112.153, -173.028, 6.9872, 113.616, -134.22, 6.9872, 145.464, -171.565, 9.1853, 112.153, -133.071, 9.1853, 143.744, -131.556, 10.9564, 141.477, -169.638, 10.9564, 110.225, -171.565, 9.1853, 112.153, -133.071, 9.1853, 143.744, -169.638, 10.9564, 110.225, -131.556, 10.9564, 141.477, -129.764, 12.1977, 138.796, -167.357, 12.1977, 107.944, -169.638, 10.9564, 110.225, -131.556, 10.9564, 141.477, -167.357, 12.1977, 107.944, -129.764, 12.1977, 138.796, -127.8, 12.8369, 135.856, -164.857, 12.8369, 105.444, -167.357, 12.1977, 107.944, -129.764, 12.1977, 138.796, -164.857, 12.8369, 105.444, -127.8, 12.8369, 135.856, -125.776, 12.8369, 132.827, -162.281, 12.8369, 102.869, -164.857, 12.8369, 105.444, -127.8, 12.8369, 135.856, -162.281, 12.8369, 102.869, -125.776, 12.8369, 132.827, -123.812, 12.1977, 129.887, -159.781, 12.1977, 100.368, -162.281, 12.8369, 102.869, -125.776, 12.8369, 132.827, -159.781, 12.1977, 100.368, -123.812, 12.1977, 129.887, -122.02, 10.9564, 127.206, -157.5, 10.9564, 98.0877, -159.781, 12.1977, 100.368, -123.812, 12.1977, 129.887, -157.5, 10.9564, 98.0877, -122.02, 10.9564, 127.206, -120.505, 9.1853, 124.939, -155.573, 9.1853, 96.1599, -157.5, 10.9564, 98.0877, -122.02, 10.9564, 127.206, -155.573, 9.1853, 96.1599, -120.505, 9.1853, 124.939, -119.356, 6.9872, 123.219, -154.11, 6.9872, 94.697, -155.573, 9.1853, 96.1599, -120.505, 9.1853, 124.939, -154.11, 6.9872, 94.697, -119.356, 6.9872, 123.219, -118.638, 4.4899, 122.145, -153.197, 4.4899, 93.7837, -154.11, 6.9872, 94.697, -119.356, 6.9872, 123.219, -153.197, 4.4899, 93.7837, -118.638, 4.4899, 122.145, -118.395, 1.8384, 121.78, -152.886, 1.8384, 93.4733, -153.197, 4.4899, 93.7837, -118.638, 4.4899, 122.145, -152.886, 1.8384, 93.4733, -118.395, 1.8384, 121.78, -118.638, -0.813, 122.145, -153.197, -0.813, 93.7837, -152.886, 1.8384, 93.4733, -118.395, 1.8384, 121.78, -153.197, -0.813, 93.7837, -118.638, -0.813, 122.145, -119.356, -3.3103, 123.219, -154.11, -3.3103, 94.697, -153.197, -0.813, 93.7837, -118.638, -0.813, 122.145, -154.11, -3.3103, 94.697, -119.356, -3.3103, 123.219, -120.505, -5.5084, 124.939, -155.573, -5.5084, 96.1599, -154.11, -3.3103, 94.697, -119.356, -3.3103, 123.219, -155.573, -5.5084, 96.1599, -120.505, -5.5084, 124.939, -122.02, -7.2796, 127.206, -157.5, -7.2796, 98.0877, -155.573, -5.5084, 96.1599, -120.505, -5.5084, 124.939, -157.5, -7.2796, 98.0877, -122.02, -7.2796, 127.206, -123.812, -8.5208, 129.887, -159.781, -8.5208, 100.368, -157.5, -7.2796, 98.0877, -122.02, -7.2796, 127.206, -159.781, -8.5208, 100.368, -123.812, -8.5208, 129.887, -125.776, -9.16, 132.827, -162.281, -9.16, 102.869, -159.781, -8.5208, 100.368, -123.812, -8.5208, 129.887, -162.281, -9.16, 102.869, -125.776, -9.16, 132.827, -127.8, -9.16, 135.856, -164.857, -9.16, 105.444, -162.281, -9.16, 102.869, -125.776, -9.16, 132.827, -164.857, -9.16, 105.444, -127.8, -9.16, 135.856, -129.764, -8.5208, 138.796, -167.357, -8.5208, 107.944, -164.857, -9.16, 105.444, -127.8, -9.16, 135.856, -167.357, -8.5208, 107.944, -129.764, -8.5208, 138.796, -131.556, -7.2796, 141.477, -169.638, -7.2796, 110.225, -167.357, -8.5208, 107.944, -129.764, -8.5208, 138.796, -169.638, -7.2796, 110.225, -131.556, -7.2796, 141.477, -133.071, -5.5084, 143.744, -171.565, -5.5084, 112.153, -169.638, -7.2796, 110.225, -131.556, -7.2796, 141.477, -171.565, -5.5084, 112.153, -133.071, -5.5084, 143.744, -134.22, -3.3103, 145.464, -173.028, -3.3103, 113.616, -171.565, -5.5084, 112.153, -133.071, -5.5084, 143.744, -173.028, -3.3103, 113.616, -134.22, -3.3103, 145.464, -134.938, -0.813, 146.538, -173.942, -0.813, 114.529, -173.028, -3.3103, 113.616, -134.22, -3.3103, 145.464, -173.942, -0.813, 114.529, -134.938, -0.813, 146.538, -135.182, 1.8384, 146.903, -174.252, 1.8384, 114.839, -173.942, -0.813, 114.529, -134.938, -0.813, 146.538, -174.252, 1.8384, 114.839, -174.252, 1.8384, 114.839, -173.942, 4.4899, 114.529, -205.951, 4.4899, 75.525, -206.316, 1.8384, 75.7689, -174.252, 1.8384, 114.839, -205.951, 4.4899, 75.525, -173.942, 4.4899, 114.529, -173.028, 6.9872, 113.616, -204.877, 6.9872, 74.8075, -205.951, 4.4899, 75.525, -173.942, 4.4899, 114.529, -204.877, 6.9872, 74.8075, -173.028, 6.9872, 113.616, -171.565, 9.1853, 112.153, -203.157, 9.1853, 73.6581, -204.877, 6.9872, 74.8075, -173.028, 6.9872, 113.616, -203.157, 9.1853, 73.6581, -171.565, 9.1853, 112.153, -169.638, 10.9564, 110.225, -200.89, 10.9564, 72.1435, -203.157, 9.1853, 73.6581, -171.565, 9.1853, 112.153, -200.89, 10.9564, 72.1435, -169.638, 10.9564, 110.225, -167.357, 12.1977, 107.944, -198.209, 12.1977, 70.3518, -200.89, 10.9564, 72.1435, -169.638, 10.9564, 110.225, -198.209, 12.1977, 70.3518, -167.357, 12.1977, 107.944, -164.857, 12.8369, 105.444, -195.268, 12.8369, 68.3871, -198.209, 12.1977, 70.3518, -167.357, 12.1977, 107.944, -195.268, 12.8369, 68.3871, -164.857, 12.8369, 105.444, -162.281, 12.8369, 102.869, -192.24, 12.8369, 66.3636, -195.268, 12.8369, 68.3871, -164.857, 12.8369, 105.444, -192.24, 12.8369, 66.3636, -162.281, 12.8369, 102.869, -159.781, 12.1977, 100.368, -189.3, 12.1977, 64.399, -192.24, 12.8369, 66.3636, -162.281, 12.8369, 102.869, -189.3, 12.1977, 64.399, -159.781, 12.1977, 100.368, -157.5, 10.9564, 98.0877, -186.618, 10.9564, 62.6073, -189.3, 12.1977, 64.399, -159.781, 12.1977, 100.368, -186.618, 10.9564, 62.6073, -157.5, 10.9564, 98.0877, -155.573, 9.1853, 96.1599, -184.352, 9.1853, 61.0927, -186.618, 10.9564, 62.6073, -157.5, 10.9564, 98.0877, -184.352, 9.1853, 61.0927, -155.573, 9.1853, 96.1599, -154.11, 6.9872, 94.697, -182.631, 6.9872, 59.9433, -184.352, 9.1853, 61.0927, -155.573, 9.1853, 96.1599, -182.631, 6.9872, 59.9433, -154.11, 6.9872, 94.697, -153.197, 4.4899, 93.7837, -181.557, 4.4899, 59.2257, -182.631, 6.9872, 59.9433, -154.11, 6.9872, 94.697, -181.557, 4.4899, 59.2257, -153.197, 4.4899, 93.7837, -152.886, 1.8384, 93.4733, -181.192, 1.8384, 58.9818, -181.557, 4.4899, 59.2257, -153.197, 4.4899, 93.7837, -181.192, 1.8384, 58.9818, -152.886, 1.8384, 93.4733, -153.197, -0.813, 93.7837, -181.557, -0.813, 59.2257, -181.192, 1.8384, 58.9818, -152.886, 1.8384, 93.4733, -181.557, -0.813, 59.2257, -153.197, -0.813, 93.7837, -154.11, -3.3103, 94.697, -182.631, -3.3103, 59.9433, -181.557, -0.813, 59.2257, -153.197, -0.813, 93.7837, -182.631, -3.3103, 59.9433, -154.11, -3.3103, 94.697, -155.573, -5.5084, 96.1599, -184.352, -5.5084, 61.0927, -182.631, -3.3103, 59.9433, -154.11, -3.3103, 94.697, -184.352, -5.5084, 61.0927, -155.573, -5.5084, 96.1599, -157.5, -7.2796, 98.0877, -186.618, -7.2796, 62.6073, -184.352, -5.5084, 61.0927, -155.573, -5.5084, 96.1599, -186.618, -7.2796, 62.6073, -157.5, -7.2796, 98.0877, -159.781, -8.5208, 100.368, -189.3, -8.5208, 64.399, -186.618, -7.2796, 62.6073, -157.5, -7.2796, 98.0877, -189.3, -8.5208, 64.399, -159.781, -8.5208, 100.368, -162.281, -9.16, 102.869, -192.24, -9.16, 66.3636, -189.3, -8.5208, 64.399, -159.781, -8.5208, 100.368, -192.24, -9.16, 66.3636, -162.281, -9.16, 102.869, -164.857, -9.16, 105.444, -195.268, -9.16, 68.3871, -192.24, -9.16, 66.3636, -162.281, -9.16, 102.869, -195.268, -9.16, 68.3871, -164.857, -9.16, 105.444, -167.357, -8.5208, 107.944, -198.209, -8.5208, 70.3518, -195.268, -9.16, 68.3871, -164.857, -9.16, 105.444, -198.209, -8.5208, 70.3518, -167.357, -8.5208, 107.944, -169.638, -7.2796, 110.225, -200.89, -7.2796, 72.1435, -198.209, -8.5208, 70.3518, -167.357, -8.5208, 107.944, -200.89, -7.2796, 72.1435, -169.638, -7.2796, 110.225, -171.565, -5.5084, 112.153, -203.157, -5.5084, 73.6581, -200.89, -7.2796, 72.1435, -169.638, -7.2796, 110.225, -203.157, -5.5084, 73.6581, -171.565, -5.5084, 112.153, -173.028, -3.3103, 113.616, -204.877, -3.3103, 74.8075, -203.157, -5.5084, 73.6581, -171.565, -5.5084, 112.153, -204.877, -3.3103, 74.8075, -173.028, -3.3103, 113.616, -173.942, -0.813, 114.529, -205.951, -0.813, 75.525, -204.877, -3.3103, 74.8075, -173.028, -3.3103, 113.616, -205.951, -0.813, 75.525, -173.942, -0.813, 114.529, -174.252, 1.8384, 114.839, -206.316, 1.8384, 75.7689, -205.951, -0.813, 75.525, -173.942, -0.813, 114.529, -206.316, 1.8384, 75.7689, -206.316, 1.8384, 75.7689, -205.951, 4.4899, 75.525, -229.736, 4.4899, 31.026, -230.142, 1.8384, 31.194, -206.316, 1.8384, 75.7689, -229.736, 4.4899, 31.026, -205.951, 4.4899, 75.525, -204.877, 6.9872, 74.8075, -228.543, 6.9872, 30.5317, -229.736, 4.4899, 31.026, -205.951, 4.4899, 75.525, -228.543, 6.9872, 30.5317, -204.877, 6.9872, 74.8075, -203.157, 9.1853, 73.6581, -226.632, 9.1853, 29.74, -228.543, 6.9872, 30.5317, -204.877, 6.9872, 74.8075, -226.632, 9.1853, 29.74, -203.157, 9.1853, 73.6581, -200.89, 10.9564, 72.1435, -224.113, 10.9564, 28.6967, -226.632, 9.1853, 29.74, -203.157, 9.1853, 73.6581, -224.113, 10.9564, 28.6967, -200.89, 10.9564, 72.1435, -198.209, 12.1977, 70.3518, -221.134, 12.1977, 27.4626, -224.113, 10.9564, 28.6967, -200.89, 10.9564, 72.1435, -221.134, 12.1977, 27.4626, -198.209, 12.1977, 70.3518, -195.268, 12.8369, 68.3871, -217.866, 12.8369, 26.1093, -221.134, 12.1977, 27.4626, -198.209, 12.1977, 70.3518, -217.866, 12.8369, 26.1093, -195.268, 12.8369, 68.3871, -192.24, 12.8369, 66.3636, -214.501, 12.8369, 24.7155, -217.866, 12.8369, 26.1093, -195.268, 12.8369, 68.3871, -214.501, 12.8369, 24.7155, -192.24, 12.8369, 66.3636, -189.3, 12.1977, 64.399, -211.234, 12.1977, 23.3622, -214.501, 12.8369, 24.7155, -192.24, 12.8369, 66.3636, -211.234, 12.1977, 23.3622, -189.3, 12.1977, 64.399, -186.618, 10.9564, 62.6073, -208.255, 10.9564, 22.1281, -211.234, 12.1977, 23.3622, -189.3, 12.1977, 64.399, -208.255, 10.9564, 22.1281, -186.618, 10.9564, 62.6073, -184.352, 9.1853, 61.0927, -205.736, 9.1853, 21.0848, -208.255, 10.9564, 22.1281, -186.618, 10.9564, 62.6073, -205.736, 9.1853, 21.0848, -184.352, 9.1853, 61.0927, -182.631, 6.9872, 59.9433, -203.825, 6.9872, 20.2931, -205.736, 9.1853, 21.0848, -184.352, 9.1853, 61.0927, -203.825, 6.9872, 20.2931, -182.631, 6.9872, 59.9433, -181.557, 4.4899, 59.2257, -202.632, 4.4899, 19.7988, -203.825, 6.9872, 20.2931, -182.631, 6.9872, 59.9433, -202.632, 4.4899, 19.7988, -181.557, 4.4899, 59.2257, -181.192, 1.8384, 58.9818, -202.226, 1.8384, 19.6308, -202.632, 4.4899, 19.7988, -181.557, 4.4899, 59.2257, -202.226, 1.8384, 19.6308, -181.192, 1.8384, 58.9818, -181.557, -0.813, 59.2257, -202.632, -0.813, 19.7988, -202.226, 1.8384, 19.6308, -181.192, 1.8384, 58.9818, -202.632, -0.813, 19.7988, -181.557, -0.813, 59.2257, -182.631, -3.3103, 59.9433, -203.825, -3.3103, 20.2931, -202.632, -0.813, 19.7988, -181.557, -0.813, 59.2257, -203.825, -3.3103, 20.2931, -182.631, -3.3103, 59.9433, -184.352, -5.5084, 61.0927, -205.736, -5.5084, 21.0848, -203.825, -3.3103, 20.2931, -182.631, -3.3103, 59.9433, -205.736, -5.5084, 21.0848, -184.352, -5.5084, 61.0927, -186.618, -7.2796, 62.6073, -208.255, -7.2796, 22.1281, -205.736, -5.5084, 21.0848, -184.352, -5.5084, 61.0927, -208.255, -7.2796, 22.1281, -186.618, -7.2796, 62.6073, -189.3, -8.5208, 64.399, -211.234, -8.5208, 23.3622, -208.255, -7.2796, 22.1281, -186.618, -7.2796, 62.6073, -211.234, -8.5208, 23.3622, -189.3, -8.5208, 64.399, -192.24, -9.16, 66.3636, -214.501, -9.16, 24.7155, -211.234, -8.5208, 23.3622, -189.3, -8.5208, 64.399, -214.501, -9.16, 24.7155, -192.24, -9.16, 66.3636, -195.268, -9.16, 68.3871, -217.866, -9.16, 26.1093, -214.501, -9.16, 24.7155, -192.24, -9.16, 66.3636, -217.866, -9.16, 26.1093, -195.268, -9.16, 68.3871, -198.209, -8.5208, 70.3518, -221.134, -8.5208, 27.4626, -217.866, -9.16, 26.1093, -195.268, -9.16, 68.3871, -221.134, -8.5208, 27.4626, -198.209, -8.5208, 70.3518, -200.89, -7.2796, 72.1435, -224.113, -7.2796, 28.6967, -221.134, -8.5208, 27.4626, -198.209, -8.5208, 70.3518, -224.113, -7.2796, 28.6967, -200.89, -7.2796, 72.1435, -203.157, -5.5084, 73.6581, -226.632, -5.5084, 29.74, -224.113, -7.2796, 28.6967, -200.89, -7.2796, 72.1435, -226.632, -5.5084, 29.74, -203.157, -5.5084, 73.6581, -204.877, -3.3103, 74.8075, -228.543, -3.3103, 30.5317, -226.632, -5.5084, 29.74, -203.157, -5.5084, 73.6581, -228.543, -3.3103, 30.5317, -204.877, -3.3103, 74.8075, -205.951, -0.813, 75.525, -229.736, -0.813, 31.026, -228.543, -3.3103, 30.5317, -204.877, -3.3103, 74.8075, -229.736, -0.813, 31.026, -205.951, -0.813, 75.525, -206.316, 1.8384, 75.7689, -230.142, 1.8384, 31.194, -229.736, -0.813, 31.026, -205.951, -0.813, 75.525, -230.142, 1.8384, 31.194, -230.142, 1.8384, 31.194, -229.736, 4.4899, 31.026, -244.383, 4.4899, -17.2583, -244.814, 1.8384, -17.1727, -230.142, 1.8384, 31.194, -244.383, 4.4899, -17.2583, -229.736, 4.4899, 31.026, -228.543, 6.9872, 30.5317, -243.117, 6.9872, -17.5103, -244.383, 4.4899, -17.2583, -229.736, 4.4899, 31.026, -243.117, 6.9872, -17.5103, -228.543, 6.9872, 30.5317, -226.632, 9.1853, 29.74, -241.087, 9.1853, -17.9139, -243.117, 6.9872, -17.5103, -228.543, 6.9872, 30.5317, -241.087, 9.1853, -17.9139, -226.632, 9.1853, 29.74, -224.113, 10.9564, 28.6967, -238.413, 10.9564, -18.4458, -241.087, 9.1853, -17.9139, -226.632, 9.1853, 29.74, -238.413, 10.9564, -18.4458, -224.113, 10.9564, 28.6967, -221.134, 12.1977, 27.4626, -235.251, 12.1977, -19.0749, -238.413, 10.9564, -18.4458, -224.113, 10.9564, 28.6967, -235.251, 12.1977, -19.0749, -221.134, 12.1977, 27.4626, -217.866, 12.8369, 26.1093, -231.782, 12.8369, -19.7648, -235.251, 12.1977, -19.0749, -221.134, 12.1977, 27.4626, -231.782, 12.8369, -19.7648, -217.866, 12.8369, 26.1093, -214.501, 12.8369, 24.7155, -228.21, 12.8369, -20.4754, -231.782, 12.8369, -19.7648, -217.866, 12.8369, 26.1093, -228.21, 12.8369, -20.4754, -214.501, 12.8369, 24.7155, -211.234, 12.1977, 23.3622, -224.742, 12.1977, -21.1653, -228.21, 12.8369, -20.4754, -214.501, 12.8369, 24.7155, -224.742, 12.1977, -21.1653, -211.234, 12.1977, 23.3622, -208.255, 10.9564, 22.1281, -221.579, 10.9564, -21.7944, -224.742, 12.1977, -21.1653, -211.234, 12.1977, 23.3622, -221.579, 10.9564, -21.7944, -208.255, 10.9564, 22.1281, -205.736, 9.1853, 21.0848, -218.905, 9.1853, -22.3263, -221.579, 10.9564, -21.7944, -208.255, 10.9564, 22.1281, -218.905, 9.1853, -22.3263, -205.736, 9.1853, 21.0848, -203.825, 6.9872, 20.2931, -216.876, 6.9872, -22.7299, -218.905, 9.1853, -22.3263, -205.736, 9.1853, 21.0848, -216.876, 6.9872, -22.7299, -203.825, 6.9872, 20.2931, -202.632, 4.4899, 19.7988, -215.609, 4.4899, -22.9819, -216.876, 6.9872, -22.7299, -203.825, 6.9872, 20.2931, -215.609, 4.4899, -22.9819, -202.632, 4.4899, 19.7988, -202.226, 1.8384, 19.6308, -215.178, 1.8384, -23.0675, -215.609, 4.4899, -22.9819, -202.632, 4.4899, 19.7988, -215.178, 1.8384, -23.0675, -202.226, 1.8384, 19.6308, -202.632, -0.813, 19.7988, -215.609, -0.813, -22.9819, -215.178, 1.8384, -23.0675, -202.226, 1.8384, 19.6308, -215.609, -0.813, -22.9819, -202.632, -0.813, 19.7988, -203.825, -3.3103, 20.2931, -216.876, -3.3103, -22.7299, -215.609, -0.813, -22.9819, -202.632, -0.813, 19.7988, -216.876, -3.3103, -22.7299, -203.825, -3.3103, 20.2931, -205.736, -5.5084, 21.0848, -218.905, -5.5084, -22.3263, -216.876, -3.3103, -22.7299, -203.825, -3.3103, 20.2931, -218.905, -5.5084, -22.3263, -205.736, -5.5084, 21.0848, -208.255, -7.2796, 22.1281, -221.579, -7.2796, -21.7944, -218.905, -5.5084, -22.3263, -205.736, -5.5084, 21.0848, -221.579, -7.2796, -21.7944, -208.255, -7.2796, 22.1281, -211.234, -8.5208, 23.3622, -224.742, -8.5208, -21.1653, -221.579, -7.2796, -21.7944, -208.255, -7.2796, 22.1281, -224.742, -8.5208, -21.1653, -211.234, -8.5208, 23.3622, -214.501, -9.16, 24.7155, -228.21, -9.16, -20.4754, -224.742, -8.5208, -21.1653, -211.234, -8.5208, 23.3622, -228.21, -9.16, -20.4754, -214.501, -9.16, 24.7155, -217.866, -9.16, 26.1093, -231.782, -9.16, -19.7648, -228.21, -9.16, -20.4754, -214.501, -9.16, 24.7155, -231.782, -9.16, -19.7648, -217.866, -9.16, 26.1093, -221.134, -8.5208, 27.4626, -235.251, -8.5208, -19.0749, -231.782, -9.16, -19.7648, -217.866, -9.16, 26.1093, -235.251, -8.5208, -19.0749, -221.134, -8.5208, 27.4626, -224.113, -7.2796, 28.6967, -238.413, -7.2796, -18.4458, -235.251, -8.5208, -19.0749, -221.134, -8.5208, 27.4626, -238.413, -7.2796, -18.4458, -224.113, -7.2796, 28.6967, -226.632, -5.5084, 29.74, -241.087, -5.5084, -17.9139, -238.413, -7.2796, -18.4458, -224.113, -7.2796, 28.6967, -241.087, -5.5084, -17.9139, -226.632, -5.5084, 29.74, -228.543, -3.3103, 30.5317, -243.117, -3.3103, -17.5103, -241.087, -5.5084, -17.9139, -226.632, -5.5084, 29.74, -243.117, -3.3103, -17.5103, -228.543, -3.3103, 30.5317, -229.736, -0.813, 31.026, -244.383, -0.813, -17.2583, -243.117, -3.3103, -17.5103, -228.543, -3.3103, 30.5317, -244.383, -0.813, -17.2583, -229.736, -0.813, 31.026, -230.142, 1.8384, 31.194, -244.814, 1.8384, -17.1727, -244.383, -0.813, -17.2583, -229.736, -0.813, 31.026, -244.814, 1.8384, -17.1727, -247.775, 3.5774, -50.1623, -244.814, 1.8384, -17.1727, -247.623, 4.4899, -50.1529, -244.814, 1.8384, -17.1727, -244.383, 4.4899, -17.2583, -247.623, 4.4899, -50.1529, -248.065, 1.8384, -50.1802, -244.814, 1.8384, -17.1727, -247.775, 3.5774, -50.1623, -246.773, 6.1232, -50.1002, -244.383, 4.4899, -17.2583, -246.324, 6.9872, -50.0723, -244.383, 4.4899, -17.2583, -243.117, 6.9872, -17.5103, -246.324, 6.9872, -50.0723, -247.623, 4.4899, -50.1529, -244.383, 4.4899, -17.2583, -246.773, 6.1232, -50.1002, -244.97, 8.4161, -49.9884, -243.117, 6.9872, -17.5103, -244.242, 9.1853, -49.9432, -243.117, 6.9872, -17.5103, -241.087, 9.1853, -17.9139, -244.242, 9.1853, -49.9432, -246.324, 6.9872, -50.0723, -243.117, 6.9872, -17.5103, -244.97, 8.4161, -49.9884, -242.475, 10.3261, -49.8337, -241.087, 9.1853, -17.9139, -241.499, 10.9564, -49.7731, -241.087, 9.1853, -17.9139, -238.413, 10.9564, -18.4458, -241.499, 10.9564, -49.7731, -244.242, 9.1853, -49.9432, -241.087, 9.1853, -17.9139, -242.475, 10.3261, -49.8337, -239.434, 11.7463, -49.6451, -238.413, 10.9564, -18.4458, -238.254, 12.1977, -49.572, -238.413, 10.9564, -18.4458, -235.251, 12.1977, -19.0749, -238.254, 12.1977, -49.572, -241.499, 10.9564, -49.7731, -238.413, 10.9564, -18.4458, -239.434, 11.7463, -49.6451, -236.022, 12.5986, -49.4336, -235.251, 12.1977, -19.0749, -234.696, 12.8369, -49.3513, -235.251, 12.1977, -19.0749, -231.782, 12.8369, -19.7648, -234.696, 12.8369, -49.3513, -238.254, 12.1977, -49.572, -235.251, 12.1977, -19.0749, -236.022, 12.5986, -49.4336, -232.434, 12.8369, -49.2111, -231.782, 12.8369, -19.7648, -231.032, 12.8369, -49.1241, -231.782, 12.8369, -19.7648, -228.21, 12.8369, -20.4754, -231.032, 12.8369, -49.1241, -234.696, 12.8369, -49.3513, -231.782, 12.8369, -19.7648, -232.434, 12.8369, -49.2111, -228.873, 12.449, -48.9903, -228.21, 12.8369, -20.4754, -227.474, 12.1977, -48.9035, -228.21, 12.8369, -20.4754, -224.742, 12.1977, -21.1653, -227.474, 12.1977, -48.9035, -231.032, 12.8369, -49.1241, -228.21, 12.8369, -20.4754, -228.873, 12.449, -48.9903, -225.538, 11.4574, -48.7835, -224.742, 12.1977, -21.1653, -224.229, 10.9564, -48.7023, -224.742, 12.1977, -21.1653, -221.579, 10.9564, -21.7944, -224.229, 10.9564, -48.7023, -227.474, 12.1977, -48.9035, -224.742, 12.1977, -21.1653, -225.538, 11.4574, -48.7835, -222.619, 9.917, -48.6025, -221.579, 10.9564, -21.7944, -221.486, 9.1853, -48.5323, -221.579, 10.9564, -21.7944, -218.905, 9.1853, -22.3263, -221.486, 9.1853, -48.5323, -224.229, 10.9564, -48.7023, -221.579, 10.9564, -21.7944, -222.619, 9.917, -48.6025, -220.281, 7.913, -48.4576, -218.905, 9.1853, -22.3263, -219.404, 6.9872, -48.4032, -218.905, 9.1853, -22.3263, -216.876, 6.9872, -22.7299, -219.404, 6.9872, -48.4032, -221.486, 9.1853, -48.5323, -218.905, 9.1853, -22.3263, -220.281, 7.913, -48.4576, -218.66, 5.5568, -48.357, -216.876, 6.9872, -22.7299, -218.105, 4.4899, -48.3226, -216.876, 6.9872, -22.7299, -215.609, 4.4899, -22.9819, -218.105, 4.4899, -48.3226, -219.404, 6.9872, -48.4032, -216.876, 6.9872, -22.7299, -218.66, 5.5568, -48.357, -217.853, 2.9806, -48.307, -215.609, 4.4899, -22.9819, -217.663, 1.8384, -48.2952, -215.609, 4.4899, -22.9819, -215.178, 1.8384, -23.0675, -217.663, 1.8384, -48.2952, -218.105, 4.4899, -48.3226, -215.609, 4.4899, -22.9819, -217.853, 2.9806, -48.307, -217.914, 0.3311, -48.3108, -215.178, 1.8384, -23.0675, -218.105, -0.813, -48.3226, -215.178, 1.8384, -23.0675, -215.609, -0.813, -22.9819, -218.105, -0.813, -48.3226, -217.663, 1.8384, -48.2952, -215.178, 1.8384, -23.0675, -217.914, 0.3311, -48.3108, -218.846, -2.238, -48.3686, -215.609, -0.813, -22.9819, -219.404, -3.3103, -48.4032, -215.609, -0.813, -22.9819, -216.876, -3.3103, -22.7299, -219.404, -3.3103, -48.4032, -218.105, -0.813, -48.3226, -215.609, -0.813, -22.9819, -218.846, -2.238, -48.3686, -220.602, -4.5753, -48.4775, -216.876, -3.3103, -22.7299, -221.486, -5.5084, -48.5323, -216.876, -3.3103, -22.7299, -218.905, -5.5084, -22.3263, -221.486, -5.5084, -48.5323, -219.404, -3.3103, -48.4032, -216.876, -3.3103, -22.7299, -220.602, -4.5753, -48.4775, -223.084, -6.5404, -48.6314, -218.905, -5.5084, -22.3263, -224.229, -7.2796, -48.7023, -218.905, -5.5084, -22.3263, -221.579, -7.2796, -21.7944, -224.229, -7.2796, -48.7023, -221.486, -5.5084, -48.5323, -218.905, -5.5084, -22.3263, -223.084, -6.5404, -48.6314, -226.149, -8.014, -48.8214, -221.579, -7.2796, -21.7944, -227.474, -8.5208, -48.9035, -221.579, -7.2796, -21.7944, -224.742, -8.5208, -21.1653, -227.474, -8.5208, -48.9035, -224.229, -7.2796, -48.7023, -221.579, -7.2796, -21.7944, -226.149, -8.014, -48.8214, -229.615, -8.9055, -49.0363, -224.742, -8.5208, -21.1653, -231.032, -9.16, -49.1241, -224.742, -8.5208, -21.1653, -228.21, -9.16, -20.4754, -231.032, -9.16, -49.1241, -227.474, -8.5208, -48.9035, -224.742, -8.5208, -21.1653, -229.615, -8.9055, -49.0363, -233.276, -9.16, -49.2633, -228.21, -9.16, -20.4754, -234.696, -9.16, -49.3514, -228.21, -9.16, -20.4754, -231.782, -9.16, -19.7648, -234.696, -9.16, -49.3514, -231.032, -9.16, -49.1241, -228.21, -9.16, -20.4754, -233.276, -9.16, -49.2633, -236.913, -8.7617, -49.4888, -231.782, -9.16, -19.7648, -238.254, -8.5208, -49.572, -231.782, -9.16, -19.7648, -235.251, -8.5208, -19.0749, -238.254, -8.5208, -49.572, -234.696, -9.16, -49.3514, -231.782, -9.16, -19.7648, -236.913, -8.7617, -49.4888, -240.307, -7.7354, -49.6993, -235.251, -8.5208, -19.0749, -241.499, -7.2796, -49.7731, -235.251, -8.5208, -19.0749, -238.413, -7.2796, -18.4458, -241.499, -7.2796, -49.7731, -238.254, -8.5208, -49.572, -235.251, -8.5208, -19.0749, -240.307, -7.7354, -49.6993, -243.258, -6.1439, -49.8822, -238.413, -7.2796, -18.4458, -244.242, -5.5084, -49.9432, -238.413, -7.2796, -18.4458, -241.087, -5.5084, -17.9139, -244.242, -5.5084, -49.9432, -241.499, -7.2796, -49.7731, -238.413, -7.2796, -18.4458, -243.258, -6.1439, -49.8822, -245.591, -4.0841, -50.0268, -241.087, -5.5084, -17.9139, -246.324, -3.3103, -50.0723, -241.087, -5.5084, -17.9139, -243.117, -3.3103, -17.5103, -246.324, -3.3103, -50.0723, -244.242, -5.5084, -49.9432, -241.087, -5.5084, -17.9139, -245.591, -4.0841, -50.0268, -247.172, -1.6801, -50.1249, -243.117, -3.3103, -17.5103, -247.623, -0.813, -50.1528, -243.117, -3.3103, -17.5103, -244.383, -0.813, -17.2583, -247.623, -0.813, -50.1528, -246.324, -3.3103, -50.0723, -243.117, -3.3103, -17.5103, -247.172, -1.6801, -50.1249, -247.913, 0.9249, -50.1708, -244.383, -0.813, -17.2583, -248.065, 1.8384, -50.1802, -244.383, -0.813, -17.2583, -244.814, 1.8384, -17.1727, -248.065, 1.8384, -50.1802, -247.623, -0.813, -50.1528, -244.383, -0.813, -17.2583, -247.913, 0.9249, -50.1708, 252.823, -12.1253, -19.1234, 252.859, -12.1193, -19.1212, 267.292, 1.9054, -18.2263, 252.859, -12.1193, -19.1212, 257.538, -10.4722, -18.8311, 267.292, 1.9054, -18.2263, 257.538, -10.4722, -18.8311, 257.633, -10.4389, -18.8252, 267.292, 1.9054, -18.2263, 257.633, -10.4389, -18.8252, 261.546, -8.1137, -18.5826, 267.292, 1.9054, -18.2263, 261.546, -8.1137, -18.5826, 261.668, -8.0411, -18.575, 267.292, 1.9054, -18.2263, 261.668, -8.0411, -18.575, 264.614, -5.1782, -18.3923, 267.292, 1.9054, -18.2263, 264.614, -5.1782, -18.3923, 264.73, -5.0652, -18.3851, 267.292, 1.9054, -18.2263, 264.73, -5.0652, -18.3851, 266.56, -1.8284, -18.2717, 267.292, 1.9054, -18.2263, 267.263, 1.7469, -18.2281, 266.56, -1.8284, -18.2717, 266.642, -1.6842, -18.2666, 266.56, -1.8284, -18.2717, 267.263, 1.7469, -18.2281, 267.292, 1.9054, -18.2263, 236.638, 15.9302, -20.1269, 236.433, 15.8598, -20.1396, 241.88, 16.774, -19.8019, 242.014, 16.7956, -19.7936, 231.736, 14.2498, -20.4309, 247.505, 16.7956, -19.4531, 231.736, 14.2498, -20.4309, 242.014, 16.7956, -19.7936, 241.88, 16.774, -19.8019, 236.433, 15.8598, -20.1396, 231.736, 14.2498, -20.4309, 241.88, 16.774, -19.8019, 231.736, 14.2498, -20.4309, 231.497, 14.1113, -20.4457, 247.505, 16.7956, -19.4531, 247.551, 16.7956, -19.4503, 227.592, 11.8519, -20.6878, 250.357, 16.3438, -19.2763, 227.592, 11.8519, -20.6878, 247.551, 16.7956, -19.4503, 247.505, 16.7956, -19.4531, 231.497, 14.1113, -20.4457, 227.592, 11.8519, -20.6878, 247.505, 16.7956, -19.4531, 227.592, 11.8519, -20.6878, 227.367, 11.6395, -20.7018, 250.357, 16.3438, -19.2763, 224.447, 8.876, -20.8828, 252.892, 15.9187, -19.1192, 252.859, 15.9302, -19.1212, 224.447, 8.876, -20.8828, 252.859, 15.9302, -19.1212, 250.357, 16.3438, -19.2763, 227.367, 11.6395, -20.7018, 224.447, 8.876, -20.8828, 250.357, 16.3438, -19.2763, 257.633, 14.2498, -18.8252, 250.357, 12.3842, -19.2763, 257.712, 14.2028, -18.8203, 227.592, -8.0411, -20.6878, 227.872, -8.2033, -20.6705, 252.823, -12.1253, -19.1234, 227.872, -8.2033, -20.6705, 231.736, -10.4389, -20.4309, 252.823, -12.1253, -19.1234, 231.736, -10.4389, -20.4309, 232.001, -10.5298, -20.4144, 252.823, -12.1253, -19.1234, 232.001, -10.5298, -20.4144, 236.638, -12.1193, -20.1269, 252.823, -12.1253, -19.1234, 236.638, -12.1193, -20.1269, 236.848, -12.153, -20.1139, 252.823, -12.1253, -19.1234, 236.848, -12.153, -20.1139, 242.014, -12.9847, -19.7936, 252.823, -12.1253, -19.1234, 242.014, -12.9847, -19.7936, 242.142, -12.9847, -19.7857, 252.823, -12.1253, -19.1234, 242.142, -12.9847, -19.7857, 250.357, -12.5329, -19.2763, 252.823, -12.1253, -19.1234, 247.592, -12.9781, -19.4478, 242.142, -12.9847, -19.7857, 247.551, -12.9847, -19.4503, 242.142, -12.9847, -19.7857, 247.592, -12.9781, -19.4478, 250.357, -12.5329, -19.2763, 260.638, 4.4899, -18.6388, 260.65, 4.417, -18.6381, 266.642, 5.495, -18.2666, 266.67, 5.3424, -18.2649, 261.075, 1.8384, -18.6118, 267.292, 1.9054, -18.2263, 260.65, 4.417, -18.6381, 266.67, 5.3424, -18.2649, 266.642, 5.495, -18.2666, 261.075, 1.8384, -18.6118, 266.67, 5.3424, -18.2649, 260.65, 4.417, -18.6381, 264.802, 8.7484, -18.3807, 259.355, 6.9872, -18.7184, 266.642, 5.495, -18.2666, 259.355, 6.9872, -18.7184, 259.386, 6.9269, -18.7165, 266.642, 5.495, -18.2666, 259.386, 6.9269, -18.7165, 260.638, 4.4899, -18.6388, 266.642, 5.495, -18.2666, 261.76, 11.7626, -18.5693, 257.298, 9.1853, -18.8459, 264.73, 8.8761, -18.3851, 257.298, 9.1853, -18.8459, 257.337, 9.1441, -18.8435, 264.73, 8.8761, -18.3851, 259.355, 6.9872, -18.7184, 264.802, 8.7484, -18.3807, 264.73, 8.8761, -18.3851, 259.355, 6.9872, -18.7184, 264.73, 8.8761, -18.3851, 257.337, 9.1441, -18.8435, 257.712, 14.2028, -18.8203, 254.589, 10.9564, -19.014, 261.668, 11.852, -18.575, 254.589, 10.9564, -19.014, 254.62, 10.936, -19.012, 261.668, 11.852, -18.575, 257.298, 9.1853, -18.8459, 261.76, 11.7626, -18.5693, 261.668, 11.852, -18.575, 257.298, 9.1853, -18.8459, 261.668, 11.852, -18.575, 254.62, 10.936, -19.012, 250.357, 12.3842, -19.2763, 251.383, 12.1977, -19.2127, 257.712, 14.2028, -18.8203, 251.392, 12.1941, -19.2121, 257.712, 14.2028, -18.8203, 251.383, 12.1977, -19.2127, 257.712, 14.2028, -18.8203, 251.392, 12.1941, -19.2121, 254.589, 10.9564, -19.014, 251.383, -8.5208, -19.2127, 251.373, -8.5226, -19.2133, 252.823, -12.1253, -19.1234, 251.373, -8.5226, -19.2133, 250.357, -8.7074, -19.2763, 252.823, -12.1253, -19.1234, 254.551, -7.294, -19.0163, 252.823, -12.1253, -19.1234, 254.589, -7.2796, -19.0139, 252.823, -12.1253, -19.1234, 254.551, -7.294, -19.0163, 251.383, -8.5208, -19.2127, 267.292, 1.9054, -18.2263, 257.298, -5.5084, -18.8459, 252.823, -12.1253, -19.1234, 257.247, -5.5419, -18.8491, 252.823, -12.1253, -19.1234, 257.298, -5.5084, -18.8459, 252.823, -12.1253, -19.1234, 257.247, -5.5419, -18.8491, 254.589, -7.2796, -19.0139, 259.355, -3.3103, -18.7184, 259.305, -3.3636, -18.7215, 267.292, 1.9054, -18.2263, 257.298, -5.5084, -18.8459, 267.292, 1.9054, -18.2263, 259.305, -3.3636, -18.7215, 260.638, -0.813, -18.6388, 260.603, -0.8818, -18.641, 267.292, 1.9054, -18.2263, 260.603, -0.8818, -18.641, 259.355, -3.3103, -18.7184, 267.292, 1.9054, -18.2263, 261.075, 1.8384, -18.6118, 261.062, 1.7625, -18.6126, 267.292, 1.9054, -18.2263, 260.638, -0.813, -18.6388, 267.292, 1.9054, -18.2263, 261.062, 1.7625, -18.6126, 252.892, 15.9187, -19.1192, 247.801, 12.8368, -19.4348, 257.633, 14.2498, -18.8252, 250.357, 12.3842, -19.2763, 257.633, 14.2498, -18.8252, 247.801, 12.8368, -19.4348, 247.774, 12.8369, -19.4365, 252.892, 15.9187, -19.1192, 244.083, 12.8369, -19.6653, 252.892, 15.9187, -19.1192, 247.774, 12.8369, -19.4365, 247.801, 12.8368, -19.4348, 224.447, 8.876, -20.8828, 240.474, 12.1977, -19.8891, 252.892, 15.9187, -19.1192, 244.017, 12.8251, -19.6694, 252.892, 15.9187, -19.1192, 240.474, 12.1977, -19.8891, 252.892, 15.9187, -19.1192, 244.017, 12.8251, -19.6694, 244.083, 12.8369, -19.6653, 224.286, 8.6, -20.8928, 237.182, 10.9564, -20.0932, 224.447, 8.876, -20.8828, 224.447, 8.876, -20.8828, 240.377, 12.1612, -19.8951, 240.474, 12.1977, -19.8891, 240.377, 12.1612, -19.8951, 224.447, 8.876, -20.8828, 237.182, 10.9564, -20.0932, 222.483, 5.4951, -21.0046, 234.399, 9.1853, -20.2658, 224.286, 8.6, -20.8928, 234.399, 9.1853, -20.2658, 237.072, 10.8864, -20.1, 224.286, 8.6, -20.8928, 237.072, 10.8864, -20.1, 237.182, 10.9564, -20.0932, 224.286, 8.6, -20.8928, 222.425, 5.18, -21.0082, 232.287, 6.9872, -20.3967, 222.483, 5.4951, -21.0046, 222.483, 5.4951, -21.0046, 234.297, 9.0794, -20.2721, 234.399, 9.1853, -20.2658, 232.287, 6.9872, -20.3967, 234.297, 9.0794, -20.2721, 222.483, 5.4951, -21.0046, 221.816, 1.9054, -21.046, 230.969, 4.4899, -20.4785, 222.425, 5.18, -21.0082, 230.969, 4.4899, -20.4785, 232.215, 6.8504, -20.4012, 222.425, 5.18, -21.0082, 232.287, 6.9872, -20.3967, 222.425, 5.18, -21.0082, 232.215, 6.8504, -20.4012, 222.483, -1.6842, -21.0046, 230.521, 1.8384, -20.5062, 221.876, 1.5852, -21.0423, 230.521, 1.8384, -20.5062, 221.816, 1.9054, -21.046, 221.876, 1.5852, -21.0423, 230.521, 1.8384, -20.5062, 230.942, 4.3342, -20.4801, 221.816, 1.9054, -21.046, 230.969, 4.4899, -20.4785, 221.816, 1.9054, -21.046, 230.942, 4.3342, -20.4801, 224.447, -5.0652, -20.8828, 230.969, -0.813, -20.4785, 222.652, -1.975, -20.9941, 222.483, -1.6842, -21.0046, 230.547, 1.6799, -20.5046, 230.521, 1.8384, -20.5062, 230.969, -0.813, -20.4785, 222.483, -1.6842, -21.0046, 222.652, -1.975, -20.9941, 230.969, -0.813, -20.4785, 230.547, 1.6799, -20.5046, 222.483, -1.6842, -21.0046, 224.447, -5.0652, -20.8828, 231.045, -0.9577, -20.4737, 230.969, -0.813, -20.4785, 231.045, -0.9577, -20.4737, 224.447, -5.0652, -20.8828, 232.287, -3.3103, -20.3967, 227.592, -8.0411, -20.6878, 234.399, -5.5084, -20.2658, 224.694, -5.2991, -20.8675, 224.447, -5.0652, -20.8828, 232.4, -3.4277, -20.3897, 232.287, -3.3103, -20.3967, 234.399, -5.5084, -20.2658, 224.447, -5.0652, -20.8828, 224.694, -5.2991, -20.8675, 234.399, -5.5084, -20.2658, 232.4, -3.4277, -20.3897, 224.447, -5.0652, -20.8828, 227.592, -8.0411, -20.6878, 234.528, -5.5908, -20.2577, 234.399, -5.5084, -20.2658, 237.182, -7.2796, -20.0932, 234.528, -5.5908, -20.2577, 227.592, -8.0411, -20.6878, 252.823, -12.1253, -19.1234, 240.474, -8.5208, -19.8891, 227.592, -8.0411, -20.6878, 227.592, -8.0411, -20.6878, 237.306, -7.3265, -20.0855, 237.182, -7.2796, -20.0932, 240.474, -8.5208, -19.8891, 237.306, -7.3265, -20.0855, 227.592, -8.0411, -20.6878, 240.575, -8.5387, -19.8829, 252.823, -12.1253, -19.1234, 244.083, -9.16, -19.6653, 252.823, -12.1253, -19.1234, 240.575, -8.5387, -19.8829, 240.474, -8.5208, -19.8891, 247.827, -9.1555, -19.4332, 252.823, -12.1253, -19.1234, 250.357, -8.7074, -19.2763, 252.823, -12.1253, -19.1234, 247.827, -9.1555, -19.4332, 247.801, -9.16, -19.4348, 244.148, -9.16, -19.6613, 252.823, -12.1253, -19.1234, 247.801, -9.16, -19.4348, 252.823, -12.1253, -19.1234, 244.148, -9.16, -19.6613, 244.083, -9.16, -19.6653, -244.58, 14.2498, -49.9642, -231.093, 16.7956, -49.1279, -241.472, 15.3308, -49.7714, -244.58, 14.2498, -49.9642, -228.992, 16.7956, -48.9977, -231.093, 16.7956, -49.1279, -231.093, 16.7956, -49.1279, -239.748, 15.9302, -49.6646, -241.472, 15.3308, -49.7714, -231.093, 16.7956, -49.1279, -236.41, 16.4754, -49.4576, -239.748, 15.9302, -49.6646, -234.449, 16.7956, -49.336, -236.41, 16.4754, -49.4576, -231.093, 16.7956, -49.1279, -248.665, 11.8519, -50.2175, -225.816, 16.2769, -48.8008, -245.992, 13.4212, -50.0517, -248.665, 11.8519, -50.2175, -223.693, 15.9302, -48.6691, -225.816, 16.2769, -48.8008, -225.816, 16.2769, -48.8008, -244.58, 14.2498, -49.9642, -245.992, 13.4212, -50.0517, -228.992, 16.7956, -48.9977, -244.58, 14.2498, -49.9642, -225.816, 16.2769, -48.8008, -220.873, 14.9495, -48.4943, -248.665, 11.8519, -50.2175, -249.71, 10.8492, -50.2822, -223.693, 15.9302, -48.6691, -248.665, 11.8519, -50.2175, -220.873, 14.9495, -48.4943, -210.791, -3.5191, -47.8691, -213.383, -6.7031, -48.0298, -211.676, -5.0652, -47.924, -210.791, -3.5191, -47.8691, -214.776, -8.0411, -48.1162, -213.383, -6.7031, -48.0298, -209.741, -1.6842, -47.804, -217.071, -9.3881, -48.2585, -210.791, -3.5191, -47.8691, -209.438, -0.0308, -47.7852, -218.861, -10.4389, -48.3695, -209.741, -1.6842, -47.804, -217.071, -9.3881, -48.2585, -209.741, -1.6842, -47.804, -218.861, -10.4389, -48.3695, -214.776, -8.0411, -48.1162, -210.791, -3.5191, -47.8691, -217.071, -9.3881, -48.2585, -209.083, 1.9054, -47.7632, -221.644, -11.4067, -48.5421, -209.438, -0.0308, -47.7852, -209.385, 3.5542, -47.782, -223.693, -12.1193, -48.6691, -209.083, 1.9054, -47.7632, -221.644, -11.4067, -48.5421, -209.083, 1.9054, -47.7632, -223.693, -12.1193, -48.6691, -218.861, -10.4389, -48.3695, -209.438, -0.0308, -47.7852, -221.644, -11.4067, -48.5421, -210.619, 7.0285, -47.8585, -226.828, -12.6314, -48.8635, -209.741, 5.495, -47.804, -226.828, -12.6314, -48.8635, -209.385, 3.5542, -47.782, -209.741, 5.495, -47.804, -226.828, -12.6314, -48.8635, -223.693, -12.1193, -48.6691, -209.385, 3.5542, -47.782, -254.358, 1.9054, -50.5705, -237.755, -12.4449, -49.541, -234.449, -12.9847, -49.336, -254.358, 1.9054, -50.5705, -239.748, -12.1193, -49.6646, -237.755, -12.4449, -49.541, -254.358, 1.9054, -50.5705, -242.832, -11.0469, -49.8558, -239.748, -12.1193, -49.6646, -254.358, 1.9054, -50.5705, -244.58, -10.4389, -49.9642, -242.832, -11.0469, -49.8558, -254.358, 1.9054, -50.5705, -247.237, -8.8791, -50.129, -244.58, -10.4389, -49.9642, -254.358, 1.9054, -50.5705, -248.665, -8.0411, -50.2175, -247.237, -8.8791, -50.129, -254.358, 1.9054, -50.5705, -250.712, -6.0762, -50.3444, -248.665, -8.0411, -50.2175, -254.358, 1.9054, -50.5705, -251.765, -5.0652, -50.4097, -250.712, -6.0762, -50.3444, -254.358, 1.9054, -50.5705, -253.056, -2.8097, -50.4897, -251.765, -5.0652, -50.4097, -254.142, 0.7239, -50.557, -253.056, -2.8097, -50.4897, -254.358, 1.9054, -50.5705, -253.056, -2.8097, -50.4897, -254.142, 0.7239, -50.557, -253.7, -1.6842, -50.5297, -254.358, 1.9054, -50.5705, -247.623, 4.4899, -50.1529, -253.917, 4.3155, -50.5431, -254.358, 1.9054, -50.5705, -247.775, 3.5774, -50.1623, -247.623, 4.4899, -50.1529, -247.775, 3.5774, -50.1623, -254.358, 1.9054, -50.5705, -248.065, 1.8384, -50.1802, -253.7, 5.495, -50.5297, -246.324, 6.9872, -50.0723, -252.406, 7.7562, -50.4494, -253.917, 4.3155, -50.5431, -246.773, 6.1232, -50.1002, -253.7, 5.495, -50.5297, -246.324, 6.9872, -50.0723, -253.7, 5.495, -50.5297, -246.773, 6.1232, -50.1002, -247.623, 4.4899, -50.1529, -246.773, 6.1232, -50.1002, -253.917, 4.3155, -50.5431, -252.406, 7.7562, -50.4494, -244.242, 9.1853, -49.9432, -251.765, 8.8761, -50.4097, -246.324, 6.9872, -50.0723, -244.97, 8.4161, -49.9884, -252.406, 7.7562, -50.4494, -244.97, 8.4161, -49.9884, -244.242, 9.1853, -49.9432, -252.406, 7.7562, -50.4494, -251.765, 8.8761, -50.4097, -241.499, 10.9564, -49.7731, -249.71, 10.8492, -50.2822, -242.475, 10.3261, -49.8337, -241.499, 10.9564, -49.7731, -251.765, 8.8761, -50.4097, -242.475, 10.3261, -49.8337, -251.765, 8.8761, -50.4097, -244.242, 9.1853, -49.9432, -239.434, 11.7463, -49.6451, -249.71, 10.8492, -50.2822, -241.499, 10.9564, -49.7731, -249.71, 10.8492, -50.2822, -239.434, 11.7463, -49.6451, -238.254, 12.1977, -49.572, -249.71, 10.8492, -50.2822, -234.696, 12.8369, -49.3513, -220.873, 14.9495, -48.4943, -236.022, 12.5986, -49.4336, -249.71, 10.8492, -50.2822, -238.254, 12.1977, -49.572, -249.71, 10.8492, -50.2822, -236.022, 12.5986, -49.4336, -234.696, 12.8369, -49.3513, -220.873, 14.9495, -48.4943, -231.032, 12.8369, -49.1241, -218.861, 14.2498, -48.3695, -231.032, 12.8369, -49.1241, -220.873, 14.9495, -48.4943, -232.434, 12.8369, -49.2111, -234.696, 12.8369, -49.3513, -232.434, 12.8369, -49.2111, -220.873, 14.9495, -48.4943, -218.861, 14.2498, -48.3695, -227.474, 12.1977, -48.9035, -216.538, 12.8859, -48.2254, -218.861, 14.2498, -48.3695, -228.873, 12.449, -48.9903, -227.474, 12.1977, -48.9035, -231.032, 12.8369, -49.1241, -228.873, 12.449, -48.9903, -218.861, 14.2498, -48.3695, -216.538, 12.8859, -48.2254, -224.229, 10.9564, -48.7023, -214.776, 11.8519, -48.1162, -224.229, 10.9564, -48.7023, -216.538, 12.8859, -48.2254, -225.538, 11.4574, -48.7835, -227.474, 12.1977, -48.9035, -225.538, 11.4574, -48.7835, -216.538, 12.8859, -48.2254, -214.776, 11.8519, -48.1162, -221.486, 9.1853, -48.5323, -213.052, 10.1971, -48.0094, -214.776, 11.8519, -48.1162, -222.619, 9.917, -48.6025, -221.486, 9.1853, -48.5323, -224.229, 10.9564, -48.7023, -222.619, 9.917, -48.6025, -214.776, 11.8519, -48.1162, -211.676, 8.8761, -47.924, -219.404, 6.9872, -48.4032, -210.619, 7.0285, -47.8585, -213.052, 10.1971, -48.0094, -220.281, 7.913, -48.4576, -211.676, 8.8761, -47.924, -220.281, 7.913, -48.4576, -219.404, 6.9872, -48.4032, -211.676, 8.8761, -47.924, -221.486, 9.1853, -48.5323, -220.281, 7.913, -48.4576, -213.052, 10.1971, -48.0094, -210.619, 7.0285, -47.8585, -218.66, 5.5568, -48.357, -218.105, 4.4899, -48.3226, -218.66, 5.5568, -48.357, -210.619, 7.0285, -47.8585, -219.404, 6.9872, -48.4032, -217.853, 2.9806, -48.307, -210.619, 7.0285, -47.8585, -218.105, 4.4899, -48.3226, -210.619, 7.0285, -47.8585, -217.853, 2.9806, -48.307, -217.663, 1.8384, -48.2952, -217.914, 0.3311, -48.3108, -210.619, 7.0285, -47.8585, -217.663, 1.8384, -48.2952, -210.619, 7.0285, -47.8585, -217.914, 0.3311, -48.3108, -218.105, -0.813, -48.3226, -210.619, 7.0285, -47.8585, -219.404, -3.3103, -48.4032, -226.828, -12.6314, -48.8635, -210.619, 7.0285, -47.8585, -218.846, -2.238, -48.3686, -219.404, -3.3103, -48.4032, -218.846, -2.238, -48.3686, -210.619, 7.0285, -47.8585, -218.105, -0.813, -48.3226, -226.828, -12.6314, -48.8635, -221.486, -5.5084, -48.5323, -228.992, -12.9847, -48.9977, -220.602, -4.5753, -48.4775, -226.828, -12.6314, -48.8635, -219.404, -3.3103, -48.4032, -226.828, -12.6314, -48.8635, -220.602, -4.5753, -48.4775, -221.486, -5.5084, -48.5323, -223.084, -6.5404, -48.6314, -224.229, -7.2796, -48.7023, -228.992, -12.9847, -48.9977, -223.084, -6.5404, -48.6314, -228.992, -12.9847, -48.9977, -221.486, -5.5084, -48.5323, -228.992, -12.9847, -48.9977, -226.149, -8.014, -48.8214, -227.474, -8.5208, -48.9035, -226.149, -8.014, -48.8214, -228.992, -12.9847, -48.9977, -224.229, -7.2796, -48.7023, -229.615, -8.9055, -49.0363, -228.992, -12.9847, -48.9977, -227.474, -8.5208, -48.9035, -228.992, -12.9847, -48.9977, -229.615, -8.9055, -49.0363, -231.032, -9.16, -49.1241, -231.032, -9.16, -49.1241, -233.276, -9.16, -49.2633, -228.992, -12.9847, -48.9977, -233.276, -9.16, -49.2633, -234.696, -9.16, -49.3514, -228.992, -12.9847, -48.9977, -228.992, -12.9847, -48.9977, -238.254, -8.5208, -49.572, -232.31, -12.9847, -49.2034, -228.992, -12.9847, -48.9977, -236.913, -8.7617, -49.4888, -238.254, -8.5208, -49.572, -234.696, -9.16, -49.3514, -236.913, -8.7617, -49.4888, -228.992, -12.9847, -48.9977, -232.31, -12.9847, -49.2034, -241.499, -7.2796, -49.7731, -234.449, -12.9847, -49.336, -232.31, -12.9847, -49.2034, -240.307, -7.7354, -49.6993, -241.499, -7.2796, -49.7731, -238.254, -8.5208, -49.572, -240.307, -7.7354, -49.6993, -232.31, -12.9847, -49.2034, -234.449, -12.9847, -49.336, -244.242, -5.5084, -49.9432, -254.358, 1.9054, -50.5705, -243.258, -6.1439, -49.8822, -234.449, -12.9847, -49.336, -241.499, -7.2796, -49.7731, -234.449, -12.9847, -49.336, -243.258, -6.1439, -49.8822, -244.242, -5.5084, -49.9432, -254.358, 1.9054, -50.5705, -245.591, -4.0841, -50.0268, -246.324, -3.3103, -50.0723, -245.591, -4.0841, -50.0268, -254.358, 1.9054, -50.5705, -244.242, -5.5084, -49.9432, -247.913, 0.9249, -50.1708, -254.358, 1.9054, -50.5705, -247.623, -0.813, -50.1528, -254.358, 1.9054, -50.5705, -247.913, 0.9249, -50.1708, -248.065, 1.8384, -50.1802, -254.358, 1.9054, -50.5705, -247.172, -1.6801, -50.1249, -247.623, -0.813, -50.1528, -247.172, -1.6801, -50.1249, -254.358, 1.9054, -50.5705, -246.324, -3.3103, -50.0723)\n\n[node name=\"Tunnel\" instance=ExtResource(\"1_1sr0i\")]\ncollision_mask = 3\n\n[node name=\"Tunnel2\" parent=\".\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"2_lkb1r\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\".\" index=\"1\"]\nshape = SubResource(\"ConcavePolygonShape3D_bh6xx\")\n"
  },
  {
    "path": "project/demo/assets/textures/asset_licenses.txt",
    "content": "All ambientCG assets are provided under the\nCreative Commons CC0 1.0 Universal License.\nhttps://docs.ambientcg.com/license/\n\nGrass:\nhttps://ambientcg.com/view?id=Ground037\n\nRock:\nhttps://ambientcg.com/view?id=Rock030\n\nCliff:\nhttps://ambientcg.com/view?id=Rock023\n"
  },
  {
    "path": "project/demo/assets/textures/ground037_alb_ht.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ddprscrpsofah\"\npath.bptc=\"res://.godot/imported/ground037_alb_ht.png-d854c3c88beba9927351b95063edffa2.bptc.ctex\"\npath.astc=\"res://.godot/imported/ground037_alb_ht.png-d854c3c88beba9927351b95063edffa2.astc.ctex\"\nmetadata={\n\"imported_formats\": [\"s3tc_bptc\", \"etc2_astc\"],\n\"vram_texture\": true\n}\n\n[deps]\n\nsource_file=\"res://demo/assets/textures/ground037_alb_ht.png\"\ndest_files=[\"res://.godot/imported/ground037_alb_ht.png-d854c3c88beba9927351b95063edffa2.bptc.ctex\", \"res://.godot/imported/ground037_alb_ht.png-d854c3c88beba9927351b95063edffa2.astc.ctex\"]\n\n[params]\n\ncompress/mode=2\ncompress/high_quality=true\ncompress/lossy_quality=0.7\ncompress/uastc_level=0\ncompress/rdo_quality_loss=0.0\ncompress/hdr_compression=1\ncompress/normal_map=2\ncompress/channel_pack=0\nmipmaps/generate=true\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/channel_remap/red=0\nprocess/channel_remap/green=1\nprocess/channel_remap/blue=2\nprocess/channel_remap/alpha=3\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "project/demo/assets/textures/ground037_nrm_rgh.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://g80pbqtklcws\"\npath.bptc=\"res://.godot/imported/ground037_nrm_rgh.png-02372c72ee87f1844ee708d952e43e96.bptc.ctex\"\npath.astc=\"res://.godot/imported/ground037_nrm_rgh.png-02372c72ee87f1844ee708d952e43e96.astc.ctex\"\nmetadata={\n\"imported_formats\": [\"s3tc_bptc\", \"etc2_astc\"],\n\"vram_texture\": true\n}\n\n[deps]\n\nsource_file=\"res://demo/assets/textures/ground037_nrm_rgh.png\"\ndest_files=[\"res://.godot/imported/ground037_nrm_rgh.png-02372c72ee87f1844ee708d952e43e96.bptc.ctex\", \"res://.godot/imported/ground037_nrm_rgh.png-02372c72ee87f1844ee708d952e43e96.astc.ctex\"]\n\n[params]\n\ncompress/mode=2\ncompress/high_quality=true\ncompress/lossy_quality=0.7\ncompress/uastc_level=0\ncompress/rdo_quality_loss=0.0\ncompress/hdr_compression=1\ncompress/normal_map=2\ncompress/channel_pack=0\nmipmaps/generate=true\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/channel_remap/red=0\nprocess/channel_remap/green=1\nprocess/channel_remap/blue=2\nprocess/channel_remap/alpha=3\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "project/demo/assets/textures/rock023_alb_ht.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c88j3oj0lf6om\"\npath.bptc=\"res://.godot/imported/rock023_alb_ht.png-9c06b8a5f11d940d1c46d8928f6d6a5c.bptc.ctex\"\npath.astc=\"res://.godot/imported/rock023_alb_ht.png-9c06b8a5f11d940d1c46d8928f6d6a5c.astc.ctex\"\nmetadata={\n\"imported_formats\": [\"s3tc_bptc\", \"etc2_astc\"],\n\"vram_texture\": true\n}\n\n[deps]\n\nsource_file=\"res://demo/assets/textures/rock023_alb_ht.png\"\ndest_files=[\"res://.godot/imported/rock023_alb_ht.png-9c06b8a5f11d940d1c46d8928f6d6a5c.bptc.ctex\", \"res://.godot/imported/rock023_alb_ht.png-9c06b8a5f11d940d1c46d8928f6d6a5c.astc.ctex\"]\n\n[params]\n\ncompress/mode=2\ncompress/high_quality=true\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=2\ncompress/channel_pack=0\nmipmaps/generate=true\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=0\n"
  },
  {
    "path": "project/demo/assets/textures/rock023_nrm_rgh.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dabyathlpy04p\"\npath.bptc=\"res://.godot/imported/rock023_nrm_rgh.png-dd524905a15817dac8149681cddc8975.bptc.ctex\"\npath.astc=\"res://.godot/imported/rock023_nrm_rgh.png-dd524905a15817dac8149681cddc8975.astc.ctex\"\nmetadata={\n\"imported_formats\": [\"s3tc_bptc\", \"etc2_astc\"],\n\"vram_texture\": true\n}\n\n[deps]\n\nsource_file=\"res://demo/assets/textures/rock023_nrm_rgh.png\"\ndest_files=[\"res://.godot/imported/rock023_nrm_rgh.png-dd524905a15817dac8149681cddc8975.bptc.ctex\", \"res://.godot/imported/rock023_nrm_rgh.png-dd524905a15817dac8149681cddc8975.astc.ctex\"]\n\n[params]\n\ncompress/mode=2\ncompress/high_quality=true\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=2\ncompress/channel_pack=0\nmipmaps/generate=true\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=0\n"
  },
  {
    "path": "project/demo/components/DemoBenchmark.tscn",
    "content": "[gd_scene load_steps=8 format=3 uid=\"uid://dyt8c2xqmddo2\"]\n\n[ext_resource type=\"Script\" uid=\"uid://chstoagn42gbr\" path=\"res://demo/src/DemoScene.gd\" id=\"1_2gjn4\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://d2jihfohphuue\" path=\"res://demo/components/UI.tscn\" id=\"2_3obxr\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bb2lp50sjndus\" path=\"res://demo/components/Environment.tscn\" id=\"3_b7ioy\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://domhm87hbhbg1\" path=\"res://demo/components/Player.tscn\" id=\"5_bwggt\"]\n[ext_resource type=\"Terrain3DAssets\" uid=\"uid://dal3jhw6241qg\" path=\"res://demo/data/assets.tres\" id=\"6_3r2au\"]\n\n[sub_resource type=\"Terrain3DMaterial\" id=\"Terrain3DMaterial_klrp5\"]\n_shader_parameters = {\n\"bias_distance\": 512.0,\n\"blend_sharpness\": 0.87,\n\"depth_blur\": 0.0,\n&\"flat_terrain_normals\": false,\n&\"ground_level\": 0.0,\n\"mipmap_bias\": 1.0,\n&\"region_blend\": 0.25,\n&\"shader_uniforms\": null,\n&\"shader_uniforms::general\": null,\n&\"shader_uniforms::mipmaps\": null\n}\n\n[node name=\"Demo\" type=\"Node\"]\nscript = ExtResource(\"1_2gjn4\")\n\n[node name=\"UI\" parent=\".\" instance=ExtResource(\"2_3obxr\")]\n\n[node name=\"World\" parent=\".\" instance=ExtResource(\"3_b7ioy\")]\n\n[node name=\"Player\" parent=\".\" instance=ExtResource(\"5_bwggt\")]\ntransform = Transform3D(0.134125, 0, -0.990965, 0, 1, 0, 0.990965, 0, 0.134125, 216.455, 103.968, -1835.34)\n\n[node name=\"Terrain3D\" type=\"Terrain3D\" parent=\".\"]\ndata_directory = \"res://demo/data\"\nmaterial = SubResource(\"Terrain3DMaterial_klrp5\")\nassets = ExtResource(\"6_3r2au\")\ncollision_mask = 3\ndisplacement_sharpness = 0.5\ntop_level = true\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "project/demo/components/Enemy.tscn",
    "content": "[gd_scene load_steps=6 format=3 uid=\"uid://di5fovhcyd7re\"]\n\n[ext_resource type=\"Script\" uid=\"uid://6j2rrp5f1gjs\" path=\"res://demo/src/Enemy.gd\" id=\"1_yudyn\"]\n\n[sub_resource type=\"CapsuleShape3D\" id=\"CapsuleShape3D_lwhhq\"]\nheight = 1.5\n\n[sub_resource type=\"SeparationRayShape3D\" id=\"SeparationRayShape3D_i8f01\"]\n\n[sub_resource type=\"CapsuleMesh\" id=\"CapsuleMesh_lsqiy\"]\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_d4cor\"]\nalbedo_color = Color(1, 0, 0, 1)\n\n[node name=\"Enemy\" type=\"CharacterBody3D\"]\ncollision_layer = 2\nscript = ExtResource(\"1_yudyn\")\n\n[node name=\"CollisionShapeBody\" type=\"CollisionShape3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)\nshape = SubResource(\"CapsuleShape3D_lwhhq\")\n\n[node name=\"CollisionShapeRay\" type=\"CollisionShape3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1, 0)\nshape = SubResource(\"SeparationRayShape3D_i8f01\")\n\n[node name=\"Body\" type=\"MeshInstance3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)\nmesh = SubResource(\"CapsuleMesh_lsqiy\")\nsurface_material_override/0 = SubResource(\"StandardMaterial3D_d4cor\")\n\n[node name=\"NavigationAgent3D\" type=\"NavigationAgent3D\" parent=\".\"]\npath_desired_distance = 2.0\ndebug_enabled = true\n"
  },
  {
    "path": "project/demo/components/Environment.tscn",
    "content": "[gd_scene load_steps=5 format=3 uid=\"uid://bb2lp50sjndus\"]\n\n[sub_resource type=\"ProceduralSkyMaterial\" id=\"ProceduralSkyMaterial_fhs52\"]\nsky_top_color = Color(0.192157, 0.282353, 0.509804, 1)\nsky_horizon_color = Color(0.505882, 0.615686, 0.709804, 1)\nground_bottom_color = Color(0.211765, 0.313726, 0.552941, 1)\nground_horizon_color = Color(0.505882, 0.615686, 0.709804, 1)\nground_curve = 0.13\nsun_angle_max = 15.0\n\n[sub_resource type=\"Sky\" id=\"Sky_jkl3n\"]\nsky_material = SubResource(\"ProceduralSkyMaterial_fhs52\")\n\n[sub_resource type=\"Environment\" id=\"Environment_877j2\"]\nbackground_mode = 2\nsky = SubResource(\"Sky_jkl3n\")\nambient_light_source = 3\nambient_light_color = Color(0.55, 0.55, 0.55, 1)\nambient_light_sky_contribution = 0.7\ntonemap_mode = 3\n\n[sub_resource type=\"CameraAttributesPractical\" id=\"CameraAttributesPractical_0gcl0\"]\n\n[node name=\"Environment\" type=\"Node3D\"]\n\n[node name=\"WorldEnvironment\" type=\"WorldEnvironment\" parent=\".\"]\nenvironment = SubResource(\"Environment_877j2\")\ncamera_attributes = SubResource(\"CameraAttributesPractical_0gcl0\")\n\n[node name=\"DirectionalLight3D\" type=\"DirectionalLight3D\" parent=\".\"]\ntransform = Transform3D(1.0000001, 0, 0, 0, 0.6414489, 0.7671647, 0, -0.76716435, 0.6414493, 0, 0, 0)\nshadow_enabled = true\nshadow_blur = 2.0\ndirectional_shadow_blend_splits = true\ndirectional_shadow_max_distance = 1024.0\n"
  },
  {
    "path": "project/demo/components/Player.tscn",
    "content": "[gd_scene load_steps=8 format=3 uid=\"uid://domhm87hbhbg1\"]\n\n[ext_resource type=\"Script\" uid=\"uid://dajlr3n5wjwmb\" path=\"res://demo/src/Player.gd\" id=\"1_nm1yx\"]\n[ext_resource type=\"Script\" uid=\"uid://b62ppvc03a6b1\" path=\"res://demo/src/CameraManager.gd\" id=\"2_loos7\"]\n\n[sub_resource type=\"SphereShape3D\" id=\"SphereShape3D_smq6u\"]\n\n[sub_resource type=\"CapsuleShape3D\" id=\"CapsuleShape3D_lwhhq\"]\nheight = 1.5\n\n[sub_resource type=\"SeparationRayShape3D\" id=\"SeparationRayShape3D_twc2s\"]\n\n[sub_resource type=\"CapsuleMesh\" id=\"CapsuleMesh_lsqiy\"]\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_mox7b\"]\n\n[node name=\"Player\" type=\"CharacterBody3D\"]\ncollision_layer = 2\nscript = ExtResource(\"1_nm1yx\")\n\n[node name=\"CameraManager\" type=\"Node3D\" parent=\".\"]\nscript = ExtResource(\"2_loos7\")\n\n[node name=\"Arm\" type=\"SpringArm3D\" parent=\"CameraManager\"]\nunique_name_in_owner = true\ntransform = Transform3D(1, 0, 0, 0, 0.906308, 0.422618, 0, -0.422618, 0.906308, 0, 2.32515, -0.0321627)\nshape = SubResource(\"SphereShape3D_smq6u\")\nspring_length = 6.0\nmargin = 0.5\n\n[node name=\"Camera3D\" type=\"Camera3D\" parent=\"CameraManager/Arm\"]\nunique_name_in_owner = true\nnear = 0.25\nfar = 16384.0\n\n[node name=\"CollisionShapeBody\" type=\"CollisionShape3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)\nshape = SubResource(\"CapsuleShape3D_lwhhq\")\n\n[node name=\"CollisionShapeRay\" type=\"CollisionShape3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1, 0)\nshape = SubResource(\"SeparationRayShape3D_twc2s\")\n\n[node name=\"Body\" type=\"MeshInstance3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)\nmesh = SubResource(\"CapsuleMesh_lsqiy\")\nsurface_material_override/0 = SubResource(\"StandardMaterial3D_mox7b\")\n"
  },
  {
    "path": "project/demo/components/Tunnel.tscn",
    "content": "[gd_scene load_steps=10 format=3 uid=\"uid://djhl3foqkj4e2\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://be6nrf0b8j4l0\" path=\"res://demo/assets/models/RockA.tscn\" id=\"1_m1xck\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bwvtgwartxt0g\" path=\"res://demo/assets/models/RockB.tscn\" id=\"2_hybky\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://lsvs8a7urkca\" path=\"res://demo/assets/models/RockC.tscn\" id=\"3_nbn1a\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://vvayjv3rbx1d\" path=\"res://demo/assets/models/Tunnel.tscn\" id=\"4_klbpo\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cribhhvg03u8g\" path=\"res://demo/assets/models/CrystalC.tscn\" id=\"5_bb2w0\"]\n[ext_resource type=\"Material\" uid=\"uid://cso4f2iyuxpmc\" path=\"res://demo/assets/materials/M_crystal_purple.tres\" id=\"6_s6twx\"]\n[ext_resource type=\"Material\" uid=\"uid://ickkffutwcvo\" path=\"res://demo/assets/materials/M_crystal_red.tres\" id=\"7_7fkm2\"]\n[ext_resource type=\"Script\" uid=\"uid://c444j1ucmv5ti\" path=\"res://demo/src/CaveEntrance.gd\" id=\"9_fn2ke\"]\n\n[sub_resource type=\"BoxShape3D\" id=\"BoxShape3D_goiy4\"]\nsize = Vector3(530.482, 38.6343, 235.603)\n\n[node name=\"Tunnel\" type=\"Node3D\"]\n\n[node name=\"EntranceL\" type=\"Node3D\" parent=\".\"]\n\n[node name=\"RockA1\" parent=\"EntranceL\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(5.82988, -5.4374, -4.00004, 6.04396, 6.55839, -0.106264, 3.00604, -2.64109, 7.9713, 807.937, 137.298, 467.411)\n\n[node name=\"RockA2\" parent=\"EntranceL\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(4.67905, 4.22076, -5.5926, 6.98724, -3.31022, 3.34764, -0.52024, -6.49718, -5.3387, 766.019, 104.95, 452.605)\n\n[node name=\"RockA3\" parent=\"EntranceL\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(1.19782, 11.0889, 4.42738, -11.9291, 0.921057, 0.9205, 0.510788, -4.49312, 11.1153, 793.166, 76.9378, 454.834)\n\n[node name=\"RockB1\" parent=\"EntranceL\" instance=ExtResource(\"2_hybky\")]\ntransform = Transform3D(-2.10522, -4.25988, 3.15298, 3.05166, -3.74811, -3.02637, 4.33304, 0.570028, 3.66329, 824.574, 112.54, 446.946)\n\n[node name=\"RockB3\" parent=\"EntranceL\" instance=ExtResource(\"2_hybky\")]\ntransform = Transform3D(4.29222, 4.57142, 1.24961, -2.49948, 3.61605, -4.6432, -4.02641, 2.62846, 4.21447, 773.937, 140.911, 469.972)\n\n[node name=\"RockC1\" parent=\"EntranceL\" instance=ExtResource(\"3_nbn1a\")]\ntransform = Transform3D(-1.86839, -3.85072, -1.99813, -0.0474326, -2.1573, 4.20181, -4.33801, 1.6821, 0.814656, 786.081, 135.376, 453.531)\n\n[node name=\"RockC2\" parent=\"EntranceL\" instance=ExtResource(\"3_nbn1a\")]\ntransform = Transform3D(-3.90856, -4.56077, 3.35866, 3.81036, 0.902037, 5.65911, -4.19074, 5.07383, 2.01294, 791.859, 164.408, 508.107)\n\n[node name=\"EntranceR\" type=\"Node3D\" parent=\".\"]\n\n[node name=\"RockA1\" parent=\"EntranceR\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(-0.271255, 1.28607, -2.97471, -1.25761, 2.70942, 1.28605, 2.98685, 1.25759, 0.271335, 299.644, 127.203, 424.309)\n\n[node name=\"RockA2\" parent=\"EntranceR\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(2.29164, -2.44454, 3.0731, 3.84134, 0.657286, -2.34167, 0.814771, 3.77671, 2.39666, 318.179, 95.8599, 421.731)\n\n[node name=\"RockA5\" parent=\"EntranceR\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(1.09929, -4.37397, 0.575562, 3.84134, 0.657286, -2.34167, 2.16957, 1.05247, 3.85443, 321.814, 95.8599, 436.931)\n\n[node name=\"RockA3\" parent=\"EntranceR\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(3.70822, 1.86229, 1.85804, 2.19079, -0.408409, -3.96295, -1.45634, 4.12752, -1.23046, 338.177, 116.66, 430.367)\n\n[node name=\"RockA4\" parent=\"EntranceR\" instance=ExtResource(\"1_m1xck\")]\ntransform = Transform3D(1.9779, 0.270409, 4.08487, -4.00125, 1.0869, 1.86547, -0.865577, -4.40646, 0.710812, 320.977, 134.583, 441.012)\n\n[node name=\"RockB1\" parent=\"EntranceR\" instance=ExtResource(\"2_hybky\")]\ntransform = Transform3D(-1.64956, -1.63286, 1.75857, 0.196316, 2.03499, 2.07367, -2.39171, 1.29322, -1.04267, 295.848, 109.828, 419.45)\n\n[node name=\"RockB3\" parent=\"EntranceR\" instance=ExtResource(\"2_hybky\")]\ntransform = Transform3D(-2.03718, 1.29729, 2.88404, -2.82928, 0.784888, -2.35156, -1.41272, -3.44263, 0.550663, 306.288, 128.712, 434.042)\n\n[node name=\"RockB4\" parent=\"EntranceR\" instance=ExtResource(\"2_hybky\")]\ntransform = Transform3D(1.97436, 2.87133, -1.41708, -2.82928, 0.784888, -2.35156, -1.49926, 2.30004, 2.57153, 328.686, 129.709, 427.63)\n\n[node name=\"RockB2\" parent=\"EntranceR\" instance=ExtResource(\"2_hybky\")]\ntransform = Transform3D(-2.17746, 0.476253, -1.87396, -1.61495, 1.104, 2.15708, 1.06324, 2.65221, -0.561397, 303.461, 107.797, 428.826)\n\n[node name=\"RockC1\" parent=\"EntranceR\" instance=ExtResource(\"3_nbn1a\")]\ntransform = Transform3D(1.74949, 1.15588, 0.576742, -1.01213, 1.82987, -0.597152, -0.802676, 0.21197, 2.01002, 335.27, 110.419, 418.362)\n\n[node name=\"RockC2\" parent=\"EntranceR\" instance=ExtResource(\"3_nbn1a\")]\ntransform = Transform3D(4.31956, 5.88829, -1.29353, -3.67086, 1.30662, -6.31041, -4.78226, 4.31561, 3.67549, 345.465, 146.581, 480.747)\n\n[node name=\"TunnelMesh\" parent=\".\" instance=ExtResource(\"4_klbpo\")]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 546.733, 112.043, 468.686)\n\n[node name=\"CrystalGroup1\" type=\"Node3D\" parent=\".\"]\ntransform = Transform3D(0.567756, 0.0788585, -0.00345403, 0.0729824, -0.514888, 0.241124, 0.0300693, -0.239266, -0.520024, 415.722, 122.719, 611.385)\n\n[node name=\"CrystalC\" parent=\"CrystalGroup1\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(0.990472, -0.134216, 0.0307963, 0.132893, 0.873058, -0.469155, 0.0360812, 0.468779, 0.882577, 0.050354, 1.72128, 4.54956)\n\n[node name=\"CrystalC3\" parent=\"CrystalGroup1\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(0.999999, 2.98023e-08, 0, 1.40402e-08, 0.999999, 1.19209e-07, 6.89635e-09, -1.49012e-07, 0.999999, 4.00897, 3.69431, -0.617554)\n\n[node name=\"CrystalC2\" parent=\"CrystalGroup1\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(-0.513116, -0.374576, 0.95887, -0.130632, 1.08641, 0.354495, -1.02111, 0.0492404, -0.527189, -1.70374, 4.03528, -0.568237)\n\n[node name=\"OmniLight3D\" type=\"OmniLight3D\" parent=\"CrystalGroup1\"]\ntransform = Transform3D(1, 3.21278e-08, -1.96807e-08, -3.21278e-08, 1, 1.02499e-07, 1.96807e-08, -1.02499e-07, 1, 1.11737, 4.79865, 1.8949)\nlight_color = Color(0.4, 0.760784, 1, 1)\nlight_energy = 16.0\nshadow_enabled = true\nomni_range = 83.054\n\n[node name=\"CrystalGroup2\" type=\"Node3D\" parent=\".\"]\ntransform = Transform3D(-0.536793, -0.0213862, -0.199931, -0.116106, 0.498308, 0.25843, 0.164163, 0.282504, -0.470976, 553.318, 103.571, 647.425)\n\n[node name=\"CrystalC\" parent=\"CrystalGroup2\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(0.704558, -0.579782, 0.130258, 0.554941, 0.56972, -0.465804, 0.212495, 0.434496, 0.784585, 0.815979, 1.24854, 2.5437)\n\n[node name=\"Rock3\" parent=\"CrystalGroup2/CrystalC\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"6_s6twx\")\n\n[node name=\"CrystalC3\" parent=\"CrystalGroup2\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(0.127208, -0.0568556, -1.06836, -0.317718, 1.02534, -0.0923969, 1.02161, 0.32596, 0.104294, 4.00861, 3.69458, -0.617188)\n\n[node name=\"Rock3\" parent=\"CrystalGroup2/CrystalC3\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"6_s6twx\")\n\n[node name=\"CrystalC2\" parent=\"CrystalGroup2\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(-0.642172, -0.213226, 0.774009, 0.484154, 0.687794, 0.591163, -0.640428, 0.733767, -0.329203, 7.28876, 1.71277, 4.52783)\n\n[node name=\"Rock3\" parent=\"CrystalGroup2/CrystalC2\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"6_s6twx\")\n\n[node name=\"OmniLight3D\" type=\"OmniLight3D\" parent=\"CrystalGroup2\"]\ntransform = Transform3D(1.74454, 2.98023e-08, -2.98023e-08, -5.60482e-08, 1.74454, 1.49012e-07, 3.43338e-08, -1.78814e-07, 1.74454, 1.11737, 4.79865, 1.8949)\nlight_color = Color(0.396078, 0.333333, 0.878431, 1)\nlight_energy = 16.0\nshadow_enabled = true\nomni_range = 83.054\n\n[node name=\"CrystalGroup3\" type=\"Node3D\" parent=\".\"]\ntransform = Transform3D(-0.536793, -0.0213862, -0.199931, 0.0789312, -0.546626, -0.153451, -0.184932, -0.17123, 0.514837, 711.291, 124.136, 579.252)\n\n[node name=\"CrystalC\" parent=\"CrystalGroup3\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(0.898105, -0.188271, 0.0864646, 0.196692, 0.654036, -0.618921, 0.0650697, 0.621535, 0.677476, 0.589844, 2.22089, 3.04785)\n\n[node name=\"Rock3\" parent=\"CrystalGroup3/CrystalC\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"7_7fkm2\")\n\n[node name=\"CrystalC3\" parent=\"CrystalGroup3\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(0.15544, 0.0752453, -1.06348, -0.370935, 1.01139, 0.0173435, 0.999527, 0.363637, 0.171821, 4.00867, 3.69458, -0.617432)\n\n[node name=\"Rock3\" parent=\"CrystalGroup3/CrystalC3\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"7_7fkm2\")\n\n[node name=\"CrystalC2\" parent=\"CrystalGroup3\" instance=ExtResource(\"5_bb2w0\")]\ntransform = Transform3D(-0.573134, -0.100696, 0.847536, 0.285048, 0.939695, 0.304405, -0.804489, 0.404691, -0.495942, 7.2887, 1.71283, 4.52771)\n\n[node name=\"Rock3\" parent=\"CrystalGroup3/CrystalC2\" index=\"0\"]\nsurface_material_override/0 = ExtResource(\"7_7fkm2\")\n\n[node name=\"OmniLight3D\" type=\"OmniLight3D\" parent=\"CrystalGroup3\"]\ntransform = Transform3D(1.74454, 2.98023e-08, -2.98023e-08, -5.60482e-08, 1.74454, 1.49012e-07, 3.43338e-08, -1.78814e-07, 1.74454, 1.11737, 4.79865, 1.8949)\nlight_color = Color(0.388235, 0.101961, 0.2, 1)\nlight_energy = 16.0\nshadow_enabled = true\nomni_range = 83.054\n\n[node name=\"CaveArea3D\" type=\"Area3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 549.961, 110.763, 526.656)\ncollision_mask = 2\nscript = ExtResource(\"9_fn2ke\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\"CaveArea3D\"]\ntransform = Transform3D(0.998441, 0, -0.0558215, 0, 1, 0, 0.0558215, 0, 0.998441, -8.36676, 3.78637, 30.7709)\nshape = SubResource(\"BoxShape3D_goiy4\")\n\n[editable path=\"CrystalGroup2/CrystalC\"]\n[editable path=\"CrystalGroup2/CrystalC3\"]\n[editable path=\"CrystalGroup2/CrystalC2\"]\n[editable path=\"CrystalGroup3/CrystalC\"]\n[editable path=\"CrystalGroup3/CrystalC3\"]\n[editable path=\"CrystalGroup3/CrystalC2\"]\n"
  },
  {
    "path": "project/demo/components/UI.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://d2jihfohphuue\"]\n\n[ext_resource type=\"Script\" uid=\"uid://dne6na1m4xku8\" path=\"res://demo/src/UI.gd\" id=\"1_why5e\"]\n\n[node name=\"UI\" type=\"Control\"]\nlayout_mode = 3\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nscript = ExtResource(\"1_why5e\")\n\n[node name=\"Label\" type=\"Label\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 1\noffset_left = 5.0\noffset_top = 5.0\noffset_right = 275.0\noffset_bottom = 340.0\ntheme_override_colors/font_shadow_color = Color(0, 0, 0, 0.662745)\ntheme_override_constants/shadow_offset_x = 1\ntheme_override_constants/shadow_offset_y = 1\ntext = \"FPS: 100\nPosition: (100, 100, 100)\nMove Speed: 10\n\nPlayer\nMove: WASDEQ,Space,Mouse\nMove speed: Wheel,+/-,Shift\nCamera view: V\nGravity toggle: G\nCollision toggle: C\n\nWindow\nQuit: F8\nUI toggle: F9\nRender mode: F10\nFull screen: F11\nMouse toggle: Escape\n\"\n\n[node name=\"Panel\" type=\"Panel\" parent=\"Label\"]\nmodulate = Color(1, 1, 1, 0.392157)\nshow_behind_parent = true\nlayout_mode = 0\noffset_left = -5.0\noffset_top = -5.0\noffset_right = 248.0\noffset_bottom = 444.0\n\n[node name=\"HSeparator\" type=\"HSeparator\" parent=\"Label/Panel\"]\ntop_level = true\nlayout_mode = 0\noffset_left = 6.0\noffset_top = 129.0\noffset_right = 246.0\noffset_bottom = 138.0\n\n[node name=\"HSeparator2\" type=\"HSeparator\" parent=\"Label/Panel\"]\ntop_level = true\nlayout_mode = 0\noffset_left = 6.0\noffset_top = 310.0\noffset_right = 246.0\noffset_bottom = 319.0\n"
  },
  {
    "path": "project/demo/csharp/CodeGenerated.cs",
    "content": "using Godot;\nusing System;\nusing System.Threading.Tasks;\nusing TokisanGames;\nusing System.Linq;\n\npublic partial class CodeGenerated : Node\n{\n\tpublic Terrain3D terrain;\n\n\tpublic override void _Ready()\n\t{\n\t\tvar ui = GetNode<Control>(\"UI\");\n\t\tvar player = GetNode<CharacterBody3D>(\"Player\");\n\t\tui.Set(\"player\", player);\n\n\t\tif (HasNode(\"RunThisSceneLabel3D\"))\n\t\t{\n\t\t\tGetNode<Label3D>(\"RunThisSceneLabel3D\").QueueFree();\n\t\t}\n\t\t\n\t\t_ = CreateTerrainAsync();\n\t}\n\n\tprivate async Task CreateTerrainAsync()\n\t{\n\t\tawait CreateTerrain();\n\t\t\n\t\tvar baker = GetNode<Node>(\"RuntimeNavigationBaker\");\n\t\tbaker.Set(\"terrain\", terrain);\n\t\tbaker.Set(\"enabled\", true);\n\t}\n\n\tprivate async Task CreateTerrain()\n\t{\n\t\tvar greenGr = new Gradient();\n\t\tgreenGr.SetColor(0, Color.FromHsv(100f / 360f, 0.35f, 0.3f));\n\t\tgreenGr.SetColor(1, Color.FromHsv(120f / 360f, 0.4f, 0.37f));\n\t\tvar greenTa = await CreateTextureAsset(\"Grass\", greenGr, 1024);\n\t\tgreenTa.UvScale = 0.02f;\n\n\t\tvar brownGr = new Gradient();\n\t\tbrownGr.SetColor(0, Color.FromHsv(30f / 360f, 0.4f, 0.3f));\n\t\tbrownGr.SetColor(1, Color.FromHsv(30f / 360f, 0.4f, 0.4f));\n\t\tvar brownTa = await CreateTextureAsset(\"Dirt\", brownGr, 1024);\n\t\tbrownTa.UvScale = 0.03f;\n\n\t\tvar grassMa = CreateMeshAsset(\"Grass\", Color.FromHsv(120f / 360f, 0.4f, 0.37f));\n\t\t\n\t\tterrain = Terrain3D.Instantiate();\n\t\tterrain.Name = \"Terrain3D\";\n\t\t// Optionally log to the console. Use the console version of Godot. See Troubleshooting doc.\n\t\t//terrain.DebugLevel = Variant.From(Terrain3D.DebugLevelEnum.Debug);\n\t\tAddChild(terrain, true);\n\t\tterrain.Owner = GetTree().GetCurrentScene();\n\t\t\n\t\tvar material = terrain.Material;\n\t\tmaterial.WorldBackground = Terrain3DMaterial.WorldBackgroundEnum.None;\n\t\tmaterial.AutoShaderEnabled = true;\n\t\tmaterial.SetShaderParam(\"auto_slope\", 10f);\n\t\tmaterial.SetShaderParam(\"blend_sharpness\", 0.975f);\n\n\t\tvar assets = terrain.Assets;\n\t\tassets.SetTextureAsset(0, greenTa);\n\t\tassets.SetTextureAsset(1, brownTa);\n\t\tassets.SetMeshAsset(0, grassMa);\n\t\t\n\t\tvar noise = new FastNoiseLite { Frequency = 0.0005f };\n\t\tvar img = Image.CreateEmpty(2048, 2048, false, Image.Format.Rf);\n\t\tfor (int x = 0; x < img.GetWidth(); x++)\n\t\t{\n\t\t\tfor (int y = 0; y < img.GetHeight(); y++)\n\t\t\t{\n\t\t\t\timg.SetPixel(x, y, new Color(noise.GetNoise2D(x, y), 0f, 0f, 1f));\n\t\t\t}\n\t\t}\n\t\tterrain.RegionSize = 1024;\n\t\tvar data = terrain.Data;\n\t\tvar images = new Godot.Collections.Array { img, new Variant(), new Variant() };\n\t\tdata.ImportImages(images, new Vector3(-1024, 0, -1024), 0.0, 150.0);\n\t\t\t\n\t\t// Setup Instancer\n\t\tvar instancer = terrain.Instancer;\n\t\tvar xforms = new Godot.Collections.Array();\n\t\tint width = 100;\n\t\tint step = 2;\n\t\tfor (int x = 0; x < width; x += step)\n\t\t{\n\t\t\tfor (int z = 0; z < width; z += step)\n\t\t\t{\n\t\t\t\tvar pos = new Vector3(x, 0, z) - new Vector3(width, 0, width) * 0.5f;\n\t\t\t\tpos.Y = (float)data.GetHeight(pos);\n\t\t\t\txforms.Add(new Transform3D(Basis.Identity, pos));\n\t\t\t}\n\t\t}\n\t\tinstancer.AddTransforms(0, xforms);\n\t}\n\n\tprivate async Task<Terrain3DTextureAsset> CreateTextureAsset(string assetName, Gradient gradient, int textureSize = 512)\n\t{\n\t\tvar fnl = new FastNoiseLite { Frequency = 0.004f };\n\n\t\tvar albNoiseTex = new NoiseTexture2D\n\t\t{\n\t\t\tWidth = textureSize,\n\t\t\tHeight = textureSize,\n\t\t\tSeamless = true,\n\t\t\tNoise = fnl,\n\t\t\tColorRamp = gradient\n\t\t};\n\t\tawait ToSignal(albNoiseTex, NoiseTexture2D.SignalName.Changed);\n\t\tvar albNoiseImg = albNoiseTex.GetImage();\n\t\t\n\t\tfor (int x = 0; x < albNoiseImg.GetWidth(); x++)\n\t\t{\n\t\t\tfor (int y = 0; y < albNoiseImg.GetHeight(); y++)\n\t\t\t{\n\t\t\t\tvar clr = albNoiseImg.GetPixel(x, y);\n\t\t\t\tclr.A = clr.V;\n\t\t\t\talbNoiseImg.SetPixel(x, y, clr);\n\t\t\t}\n\t\t}\n\t\talbNoiseImg.GenerateMipmaps();\n\t\tvar albedo = ImageTexture.CreateFromImage(albNoiseImg);\n\t\t\n\t\tvar nrmNoiseTex = new NoiseTexture2D\n\t\t{\n\t\t\tWidth = textureSize,\n\t\t\tHeight = textureSize,\n\t\t\tAsNormalMap = true,\n\t\t\tSeamless = true,\n\t\t\tNoise = fnl\n\t\t};\n\t\tawait ToSignal(nrmNoiseTex, NoiseTexture2D.SignalName.Changed);\n\t\tvar nrmNoiseImg = nrmNoiseTex.GetImage();\n\t\tfor (int x = 0; x < nrmNoiseImg.GetWidth(); x++)\n\t\t{\n\t\t\tfor (int y = 0; y < nrmNoiseImg.GetHeight(); y++)\n\t\t\t{\n\t\t\t\tvar normalRgh = nrmNoiseImg.GetPixel(x, y);\n\t\t\t\tnormalRgh.A = 0.8f;\n\t\t\t\tnrmNoiseImg.SetPixel(x, y, normalRgh);\n\t\t\t}\n\t\t}\n\t\tnrmNoiseImg.GenerateMipmaps();\n\t\tvar normal = ImageTexture.CreateFromImage(nrmNoiseImg);\n\t\t\n\t\tvar ta = Terrain3DTextureAsset.Instantiate();\n\t\tta.Name = assetName;\n\t\tta.AlbedoTexture = albedo;\n\t\tta.NormalTexture = normal;\n\t\treturn ta;\n\t}\n\n\tprivate Terrain3DMeshAsset CreateMeshAsset(string assetName, Color color)\n\t{\n\t\tvar ma = Terrain3DMeshAsset.Instantiate();\n\t\tma.Name = assetName;\n\t\tma.GeneratedType = Variant.From(Terrain3DMeshAsset.GenType.TextureCard);\n\t\tma.HeightOffset = 0.5f;\n\t\tma.Lod0Range = 128.0f;\n\t\t(ma.MaterialOverride as StandardMaterial3D).AlbedoColor = color;\n\t\treturn ma;\n\t}\n}\n"
  },
  {
    "path": "project/demo/csharp/CodeGenerated.cs.uid",
    "content": "uid://3cpwhtusnhk4\n"
  },
  {
    "path": "project/demo/csharp/CodeGeneratedCSDemo.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://w4eisl2c6xal\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://cofnhdcclon1w\" path=\"res://demo/CodeGeneratedDemo.tscn\" id=\"1_qdypl\"]\n[ext_resource type=\"Script\" uid=\"uid://3cpwhtusnhk4\" path=\"res://demo/csharp/CodeGenerated.cs\" id=\"2_qdypl\"]\n\n[node name=\"CodeGenerated\" instance=ExtResource(\"1_qdypl\")]\nscript = ExtResource(\"2_qdypl\")\n"
  },
  {
    "path": "project/demo/data/M_terrain.tres",
    "content": "[gd_resource type=\"Terrain3DMaterial\" load_steps=4 format=3 uid=\"uid://ccvn15t8km0ft\"]\n\n[sub_resource type=\"Gradient\" id=\"Gradient_vr1m7\"]\noffsets = PackedFloat32Array(0.2, 1)\ncolors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)\n\n[sub_resource type=\"FastNoiseLite\" id=\"FastNoiseLite_d8lcj\"]\nnoise_type = 2\nfrequency = 0.03\ncellular_jitter = 3.0\ncellular_return_type = 0\ndomain_warp_enabled = true\ndomain_warp_type = 1\ndomain_warp_amplitude = 50.0\ndomain_warp_fractal_type = 2\ndomain_warp_fractal_lacunarity = 1.5\ndomain_warp_fractal_gain = 1.0\n\n[sub_resource type=\"NoiseTexture2D\" id=\"NoiseTexture2D_bov7h\"]\nnoise = SubResource(\"FastNoiseLite_d8lcj\")\ncolor_ramp = SubResource(\"Gradient_vr1m7\")\nseamless = true\n\n[resource]\n_shader_parameters = {\n\"_mouse_layer\": 2147483648,\n\"auto_base_texture\": 0,\n\"auto_height_reduction\": 0.05,\n\"auto_overlay_texture\": 1,\n\"auto_slope\": 0.45,\n\"bias_distance\": 512.0,\n\"blend_sharpness\": 0.5,\n\"depth_blur\": 0.0,\n\"dual_scale_far\": 170.0,\n\"dual_scale_near\": 100.0,\n\"dual_scale_reduction\": 0.3,\n\"dual_scale_texture\": 0,\n&\"flat_terrain_normals\": false,\n&\"ground_level\": -618.9519819002201,\n\"macro_variation1\": Color(0.855, 0.8625, 0.9, 1),\n\"macro_variation2\": Color(0.9, 0.885, 0.81, 1),\n\"macro_variation_slope\": 0.333,\n\"mipmap_bias\": 1.0,\n\"noise1_angle\": 0.1,\n\"noise1_offset\": Vector2(0.5, 0.5),\n\"noise1_scale\": 0.04,\n\"noise2_scale\": 0.076,\n\"noise_texture\": SubResource(\"NoiseTexture2D_bov7h\"),\n&\"region_blend\": 0.95600004541,\n&\"shader_uniforms\": null,\n&\"shader_uniforms::auto_shader\": null,\n&\"shader_uniforms::displacement\": null,\n&\"shader_uniforms::dual_scaling\": null,\n&\"shader_uniforms::general\": null,\n&\"shader_uniforms::macro_variation\": null,\n&\"shader_uniforms::mipmaps\": null,\n&\"shader_uniforms::world_background_noise\": null,\n\"tri_scale_reduction\": 0.3,\n&\"world_noise_fragment_normals\": false,\n&\"world_noise_height\": 75.00001601878989,\n&\"world_noise_lod_distance\": 7500.0,\n&\"world_noise_max_octaves\": 4,\n&\"world_noise_min_octaves\": 2,\n&\"world_noise_offset\": Vector3(0, 0, 0),\n&\"world_noise_scale\": 5.0\n}\nworld_background = 2\nauto_shader_enabled = true\ndual_scaling_enabled = true\nmacro_variation_enabled = true\nprojection_enabled = true\ndisplacement_sharpness = 0.25\n"
  },
  {
    "path": "project/demo/data/assets.tres",
    "content": "[gd_resource type=\"Terrain3DAssets\" load_steps=11 format=3 uid=\"uid://dal3jhw6241qg\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://bn5nf4esciwex\" path=\"res://demo/assets/models/LOD5Example.tscn\" id=\"1_4jrdu\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c88j3oj0lf6om\" path=\"res://demo/assets/textures/rock023_alb_ht.png\" id=\"2_pog6b\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ddprscrpsofah\" path=\"res://demo/assets/textures/ground037_alb_ht.png\" id=\"3_g8f2m\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dabyathlpy04p\" path=\"res://demo/assets/textures/rock023_nrm_rgh.png\" id=\"3_wncaf\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://g80pbqtklcws\" path=\"res://demo/assets/textures/ground037_nrm_rgh.png\" id=\"4_aw5y1\"]\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_b2vqk\"]\ntransparency = 4\ncull_mode = 2\nvertex_color_use_as_albedo = true\nbacklight_enabled = true\nbacklight = Color(0.5, 0.5, 0.5, 1)\ndistance_fade_mode = 1\ndistance_fade_min_distance = 128.0\ndistance_fade_max_distance = 96.0\n\n[sub_resource type=\"Terrain3DMeshAsset\" id=\"Terrain3DMeshAsset_2qf8x\"]\nname = \"TextureCard\"\ngenerated_type = 1\nheight_offset = 0.5\ndensity = 10.0\nmaterial_override = SubResource(\"StandardMaterial3D_b2vqk\")\nlast_lod = 0\nlast_shadow_lod = 0\nlod0_range = 128.0\n\n[sub_resource type=\"Terrain3DMeshAsset\" id=\"Terrain3DMeshAsset_y3ibi\"]\nname = \"LODExample\"\nid = 1\nscene_file = ExtResource(\"1_4jrdu\")\nheight_offset = 0.5\ndensity = 10.0\nlast_lod = 4\nlast_shadow_lod = 4\nlod4_range = 256.0\n\n[sub_resource type=\"Terrain3DTextureAsset\" id=\"Terrain3DTextureAsset_lha57\"]\nname = \"Cliff\"\nalbedo_texture = ExtResource(\"2_pog6b\")\nnormal_texture = ExtResource(\"3_wncaf\")\nnormal_depth = 1.5\nao_strength = 1.0\nao_light_affect = 0.8\nroughness = -0.05\ndisplacement_scale = 0.65\n\n[sub_resource type=\"Terrain3DTextureAsset\" id=\"Terrain3DTextureAsset_od0q7\"]\nname = \"Grass\"\nid = 1\nalbedo_color = Color(0.67451, 0.74902, 0.686275, 1)\nalbedo_texture = ExtResource(\"3_g8f2m\")\nnormal_texture = ExtResource(\"4_aw5y1\")\nnormal_depth = 1.4\nao_strength = 1.0\ndisplacement_scale = 0.2\ndetiling_rotation = 0.161\n\n[resource]\nmesh_list = Array[Terrain3DMeshAsset]([SubResource(\"Terrain3DMeshAsset_2qf8x\"), SubResource(\"Terrain3DMeshAsset_y3ibi\")])\ntexture_list = Array[Terrain3DTextureAsset]([SubResource(\"Terrain3DTextureAsset_lha57\"), SubResource(\"Terrain3DTextureAsset_od0q7\")])\n"
  },
  {
    "path": "project/demo/src/CameraManager.gd",
    "content": "extends Node3D\n\nconst CAMERA_MAX_PITCH: float = deg_to_rad(70)\nconst CAMERA_MIN_PITCH: float = deg_to_rad(-89.9)\nconst CAMERA_RATIO: float = .625\n\n@export var mouse_sensitivity: float = .002\n@export var mouse_y_inversion: float = -1.0\n\n@onready var _camera_yaw: Node3D = self\n@onready var _camera_pitch: Node3D = %Arm\n\n\nfunc _ready() -> void:\n\tInput.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)\n\n\nfunc _input(p_event: InputEvent) -> void:\n\tif p_event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:\n\t\trotate_camera(p_event.relative)\n\t\tget_viewport().set_input_as_handled()\n\t\treturn\n\n\nfunc rotate_camera(p_relative:Vector2) -> void:\n\t_camera_yaw.rotation.y -= p_relative.x * mouse_sensitivity\n\t_camera_yaw.orthonormalize()\n\t_camera_pitch.rotation.x += p_relative.y * mouse_sensitivity * CAMERA_RATIO * mouse_y_inversion \n\t_camera_pitch.rotation.x = clamp(_camera_pitch.rotation.x, CAMERA_MIN_PITCH, CAMERA_MAX_PITCH)\n"
  },
  {
    "path": "project/demo/src/CameraManager.gd.uid",
    "content": "uid://b62ppvc03a6b1\n"
  },
  {
    "path": "project/demo/src/CaveEntrance.gd",
    "content": "extends Area3D\n\n\nfunc _ready() -> void:\n\tbody_entered.connect(_on_body_entered)\n\tbody_exited.connect(_on_body_exited)\n\n\nfunc _on_body_entered(body: Node3D) -> void:\n\tif body.name == \"Player\":\n\t\tvar env: WorldEnvironment = get_node_or_null(\"../../Environment/WorldEnvironment\")\n\t\tif env:\n\t\t\tvar tween: Tween = get_tree().create_tween()\n\t\t\ttween.tween_property(env.environment, \"ambient_light_energy\", .1, .33)\n\t\n\nfunc _on_body_exited(body: Node3D) -> void:\n\tif body.name == \"Player\":\n\t\tvar env: WorldEnvironment = get_node_or_null(\"../../Environment/WorldEnvironment\")\n\t\tif env:\n\t\t\tvar tween: Tween = get_tree().create_tween()\n\t\t\ttween.tween_property(env.environment, \"ambient_light_energy\", 1., .33)\n"
  },
  {
    "path": "project/demo/src/CaveEntrance.gd.uid",
    "content": "uid://c444j1ucmv5ti\n"
  },
  {
    "path": "project/demo/src/CodeGenerated.gd",
    "content": "extends Node\n\nvar terrain: Terrain3D\n\n\nfunc _ready() -> void:\n\t$UI.player = $Player\n\t\t\n\tif has_node(\"RunThisSceneLabel3D\"):\n\t\t$RunThisSceneLabel3D.queue_free()\n\n\tterrain = await create_terrain()\n\n\t# Enable runtime navigation baking using the terrain\n\t# Enable `Debug/Visible Navigation` if you wish to see it\n\t$RuntimeNavigationBaker.terrain = terrain\n\t$RuntimeNavigationBaker.enabled = true\n\n\nfunc create_terrain() -> Terrain3D:\n\t# Create textures\n\tvar green_gr := Gradient.new()\n\tgreen_gr.set_color(0, Color.from_hsv(100./360., .35, .3))\n\tgreen_gr.set_color(1, Color.from_hsv(120./360., .4, .37))\n\tvar green_ta: Terrain3DTextureAsset = await create_texture_asset(\"Grass\", green_gr, 1024)\n\tgreen_ta.uv_scale = 0.02\n\n\tvar brown_gr := Gradient.new()\n\tbrown_gr.set_color(0, Color.from_hsv(30./360., .4, .3))\n\tbrown_gr.set_color(1, Color.from_hsv(30./360., .4, .4))\n\tvar brown_ta: Terrain3DTextureAsset = await create_texture_asset(\"Dirt\", brown_gr, 1024)\n\tbrown_ta.uv_scale = 0.03\n\t\n\tvar grass_ma: Terrain3DMeshAsset = create_mesh_asset(\"Grass\", Color.from_hsv(120./360., .4, .37)) \n\n\t# Create a terrain\n\tterrain = Terrain3D.new()\n\tterrain.name = \"Terrain3D\"\n\t# Optionally log to the console. Use the console version of Godot. See Troubleshooting doc.\n\t#terrain.debug_level = Terrain3D.DEBUG\n\tadd_child(terrain, true)\n\tterrain.owner = get_tree().get_current_scene()\n\n\t# Set material and assets\n\tterrain.material.world_background = Terrain3DMaterial.NONE\n\tterrain.material.auto_shader_enabled = true\n\tterrain.material.set_shader_param(\"auto_slope\", 10)\n\tterrain.material.set_shader_param(\"blend_sharpness\", .975)\n\tterrain.assets.set_texture_asset(0, green_ta)\n\tterrain.assets.set_texture_asset(1, brown_ta)\n\tterrain.assets.set_mesh_asset(0, grass_ma)\n\n\t# Generate height map w/ 32-bit noise and import it with scale\n\tvar noise := FastNoiseLite.new()\n\tnoise.frequency = 0.0005\n\tvar img: Image = Image.create_empty(2048, 2048, false, Image.FORMAT_RF)\n\tfor x in img.get_width():\n\t\tfor y in img.get_height():\n\t\t\timg.set_pixel(x, y, Color(noise.get_noise_2d(x, y), 0., 0., 1.))\n\tterrain.region_size = Terrain3D.SIZE_1024\n\tterrain.data.import_images([img, null, null], Vector3(-1024, 0, -1024), 0.0, 150.0)\n\n\t# Instance foliage\n\tvar xforms: Array[Transform3D]\n\tvar width: int = 100\n\tvar step: int = 2\n\tfor x in range(0, width, step):\n\t\tfor z in range(0, width, step):\n\t\t\tvar pos := Vector3(x, 0, z) - Vector3(width, 0, width) * .5\n\t\t\tpos.y = terrain.data.get_height(pos)\n\t\t\txforms.push_back(Transform3D(Basis(), pos))\n\tterrain.instancer.add_transforms(0, xforms)\n\n\t# Enable the next line and `Debug/Visible Collision Shapes` to see collision\n\t#terrain.collision.mode = Terrain3DCollision.DYNAMIC_EDITOR\n\n\treturn terrain\n\n\nfunc create_texture_asset(asset_name: String, gradient: Gradient, texture_size: int = 512) -> Terrain3DTextureAsset:\n\t# Create noise map\n\tvar fnl := FastNoiseLite.new()\n\tfnl.frequency = 0.004\n\t\n\t# Create albedo noise texture\n\tvar alb_noise_tex := NoiseTexture2D.new()\n\talb_noise_tex.width = texture_size\n\talb_noise_tex.height = texture_size\n\talb_noise_tex.seamless = true\n\talb_noise_tex.noise = fnl\n\talb_noise_tex.color_ramp = gradient\n\tawait alb_noise_tex.changed\n\tvar alb_noise_img: Image = alb_noise_tex.get_image()\n\n\t# Create albedo + height texture\n\tfor x in alb_noise_img.get_width():\n\t\tfor y in alb_noise_img.get_height():\n\t\t\tvar clr: Color = alb_noise_img.get_pixel(x, y)\n\t\t\tclr.a = clr.v # Noise as height\n\t\t\talb_noise_img.set_pixel(x, y, clr)\n\talb_noise_img.generate_mipmaps()\n\tvar albedo := ImageTexture.create_from_image(alb_noise_img)\n\n\t# Create normal + rough texture\n\tvar nrm_noise_tex := NoiseTexture2D.new()\n\tnrm_noise_tex.width = texture_size\n\tnrm_noise_tex.height = texture_size\n\tnrm_noise_tex.as_normal_map = true\n\tnrm_noise_tex.seamless = true\n\tnrm_noise_tex.noise = fnl\n\tawait nrm_noise_tex.changed\n\tvar nrm_noise_img = nrm_noise_tex.get_image()\n\tfor x in nrm_noise_img.get_width():\n\t\tfor y in nrm_noise_img.get_height():\n\t\t\tvar normal_rgh: Color = nrm_noise_img.get_pixel(x, y)\n\t\t\tnormal_rgh.a = 0.8 # Roughness\n\t\t\tnrm_noise_img.set_pixel(x, y, normal_rgh)\n\tnrm_noise_img.generate_mipmaps()\n\tvar normal := ImageTexture.create_from_image(nrm_noise_img)\n\n\tvar ta := Terrain3DTextureAsset.new()\n\tta.name = asset_name\n\tta.albedo_texture = albedo\n\tta.normal_texture = normal\n\treturn ta\n\n\nfunc create_mesh_asset(asset_name: String, color: Color) -> Terrain3DMeshAsset:\n\tvar ma := Terrain3DMeshAsset.new()\n\tma.name = asset_name\n\tma.set_generated_type(Terrain3DMeshAsset.TYPE_TEXTURE_CARD)\n\tma.height_offset = 0.5\n\tma.lod0_range = 128.0\n\tma.material_override.albedo_color = color\n\treturn ma\n"
  },
  {
    "path": "project/demo/src/CodeGenerated.gd.uid",
    "content": "uid://dakis6gu8b7nm\n"
  },
  {
    "path": "project/demo/src/DemoScene.gd",
    "content": "@tool\nextends Node\n\n@onready var terrain: Terrain3D = find_child(\"Terrain3D\")\n\n\nfunc _ready():\n\tif not Engine.is_editor_hint() and has_node(\"UI\"):\n\t\t$UI.player = $Player\n\n\t# Load Sky3D into the demo environment if enabled\n\tif Engine.is_editor_hint() and has_node(\"Environment\") and \\\n\t\tEngine.get_singleton(&\"EditorInterface\").is_plugin_enabled(\"sky_3d\"):\n\t\t\t$Environment.queue_free()\n\t\t\tvar sky3d = load(\"res://addons/sky_3d/src/Sky3D.gd\").new()\n\t\t\tsky3d.name = \"Sky3D\"\n\t\t\tadd_child(sky3d, true)\n\t\t\tmove_child(sky3d, 1)\n\t\t\tsky3d.owner = self\n\t\t\tsky3d.current_time = 10\n\t\t\tsky3d.enable_editor_time = false\n\t\t\t\n\t\t\n"
  },
  {
    "path": "project/demo/src/DemoScene.gd.uid",
    "content": "uid://chstoagn42gbr\n"
  },
  {
    "path": "project/demo/src/Enemy.gd",
    "content": "extends CharacterBody3D\n\nconst RETARGET_COOLDOWN: float = 1.0\n\n@export var MOVE_SPEED: float = 50.0\n@export var target: Node3D\n\n@onready var nav_agent: NavigationAgent3D = $NavigationAgent3D\n\nvar _retarget_timer: float = 1.0\n\n\nfunc _ready() -> void:\n\tnav_agent.velocity_computed.connect(_on_velocity_computed)\n\n\nfunc _process(p_delta: float) -> void:\n\t_retarget_timer += p_delta\n\tif _retarget_timer > RETARGET_COOLDOWN and target:\n\t\t# Don't reset the target position every frame. It triggers an A* search, which is expensive.\n\t\t_retarget_timer = 0.0\n\t\tnav_agent.set_target_position(target.global_position)\n\n\nfunc is_on_nav_mesh() -> bool:\n\tvar closest_point := NavigationServer3D.map_get_closest_point(nav_agent.get_navigation_map(), global_position)\n\treturn global_position.distance_squared_to(closest_point) < nav_agent.path_max_distance ** 2\n\n\nfunc _physics_process(p_delta: float) -> void:\n\tif nav_agent.is_navigation_finished():\n\t\tvelocity.x = 0.0\n\t\tvelocity.z = 0.0\n\telse:\n\t\tvar next_path_position: Vector3 = nav_agent.get_next_path_position()\n\t\tvar current_agent_position: Vector3 = global_position\n\t\tvar velocity_xz := (next_path_position - current_agent_position).normalized() * MOVE_SPEED\n\t\tvelocity.x = velocity_xz.x\n\t\tvelocity.z = velocity_xz.z\n\t\n\tvelocity.y -= 40 * p_delta\n\t\n\tif nav_agent.avoidance_enabled:\n\t\tnav_agent.set_velocity(velocity)\n\telse:\n\t\t_on_velocity_computed(velocity)\n\n\t# Ensure enemy doesn't fall through terrain when collision absent\n\tif get_parent().terrain:\n\t\tvar height: float = get_parent().terrain.data.get_height(global_position)\n\t\tif not is_nan(height):\n\t\t\tglobal_position.y = maxf(global_position.y, height)\n\n\nfunc _on_velocity_computed(p_safe_velocity: Vector3) -> void:\n\tvelocity.x = p_safe_velocity.x\n\tvelocity.z = p_safe_velocity.z\n\tmove_and_slide()\n"
  },
  {
    "path": "project/demo/src/Enemy.gd.uid",
    "content": "uid://6j2rrp5f1gjs\n"
  },
  {
    "path": "project/demo/src/Player.gd",
    "content": "extends CharacterBody3D\n\n@export var MOVE_SPEED: float = 50.0\n@export var JUMP_SPEED: float = 2.0\n@export var first_person: bool = false : \n\tset(p_value):\n\t\tfirst_person = p_value\n\t\tif first_person:\n\t\t\tvar tween: Tween = create_tween()\n\t\t\ttween.tween_property($CameraManager/Arm, \"spring_length\", 0.0, .33)\n\t\t\ttween.tween_callback($Body.set_visible.bind(false))\n\t\telse:\n\t\t\t$Body.visible = true\n\t\t\tcreate_tween().tween_property($CameraManager/Arm, \"spring_length\", 6.0, .33)\n\n@export var gravity_enabled: bool = true :\n\tset(p_value):\n\t\tgravity_enabled = p_value\n\t\tif not gravity_enabled:\n\t\t\tvelocity.y = 0\n\t\t\t\n@export var collision_enabled: bool = true :\n\tset(p_value):\n\t\tcollision_enabled = p_value\n\t\t$CollisionShapeBody.disabled = ! collision_enabled\n\t\t$CollisionShapeRay.disabled = ! collision_enabled\n\n\nfunc _physics_process(p_delta) -> void:\n\tvar direction: Vector3 = get_camera_relative_input()\n\tvar h_veloc: Vector2 = Vector2(direction.x, direction.z).normalized() * MOVE_SPEED\n\tif Input.is_key_pressed(KEY_SHIFT):\n\t\th_veloc *= 2\n\tvelocity.x = h_veloc.x\n\tvelocity.z = h_veloc.y\n\tif gravity_enabled:\n\t\tvelocity.y -= 40 * p_delta\n\tmove_and_slide()\n\t# Allow player to walk on waves in the ocean\n\tif get_parent().terrain.ocean_enabled:\n\t\tposition.y = max(3, position.y)\n\n\n# Returns the input vector relative to the camera. Forward is always the direction the camera is facing\nfunc get_camera_relative_input() -> Vector3:\n\tvar input_dir: Vector3 = Vector3.ZERO\n\tif Input.is_key_pressed(KEY_A): # Left\n\t\tinput_dir -= %Camera3D.global_transform.basis.x\n\tif Input.is_key_pressed(KEY_D): # Right\n\t\tinput_dir += %Camera3D.global_transform.basis.x\n\tif Input.is_key_pressed(KEY_W): # Forward\n\t\tinput_dir -= %Camera3D.global_transform.basis.z\n\tif Input.is_key_pressed(KEY_S): # Backward\n\t\tinput_dir += %Camera3D.global_transform.basis.z\n\tif Input.is_key_pressed(KEY_E) or Input.is_key_pressed(KEY_SPACE): # Up\n\t\tvelocity.y += JUMP_SPEED + MOVE_SPEED*.016\n\tif Input.is_key_pressed(KEY_Q): # Down\n\t\tvelocity.y -= JUMP_SPEED + MOVE_SPEED*.016\n\tif Input.is_key_pressed(KEY_KP_ADD) or Input.is_key_pressed(KEY_EQUAL):\n\t\tMOVE_SPEED = clamp(MOVE_SPEED + .5, 5, 9999)\n\tif Input.is_key_pressed(KEY_KP_SUBTRACT) or Input.is_key_pressed(KEY_MINUS):\n\t\tMOVE_SPEED = clamp(MOVE_SPEED - .5, 5, 9999)\n\treturn input_dir\n\n\nfunc _input(p_event: InputEvent) -> void:\n\tif p_event is InputEventMouseButton and p_event.pressed:\n\t\tif p_event.button_index == MOUSE_BUTTON_WHEEL_UP:\n\t\t\tMOVE_SPEED = clamp(MOVE_SPEED + 5, 5, 9999)\n\t\telif p_event.button_index == MOUSE_BUTTON_WHEEL_DOWN:\n\t\t\tMOVE_SPEED = clamp(MOVE_SPEED - 5, 5, 9999)\n\t\n\telif p_event is InputEventKey:\n\t\tif p_event.pressed:\n\t\t\tif p_event.keycode == KEY_V:\n\t\t\t\tfirst_person = ! first_person\n\t\t\telif p_event.keycode == KEY_G:\n\t\t\t\tgravity_enabled = ! gravity_enabled\n\t\t\telif p_event.keycode == KEY_C:\n\t\t\t\tcollision_enabled = ! collision_enabled\n\n\t\t# Else if up/down released\n\t\telif p_event.keycode in [ KEY_Q, KEY_E, KEY_SPACE ]:\n\t\t\tvelocity.y = 0\n"
  },
  {
    "path": "project/demo/src/Player.gd.uid",
    "content": "uid://dajlr3n5wjwmb\n"
  },
  {
    "path": "project/demo/src/RuntimeNavigationBaker.gd",
    "content": "extends Node\n\nsignal bake_finished\n\n@export var enabled: bool = true : set = set_enabled\n@export var enter_cost: float = 0.0 : set = set_enter_cost\n@export var travel_cost: float = 1.0 : set = set_travel_cost\n@export_flags_3d_navigation var navigation_layers: int = 1 : set = set_navigation_layers\n@export var template: NavigationMesh : set = set_template\n@export var terrain: Terrain3D\n@export var player: Node3D\n@export var mesh_size := Vector3(256, 512, 256)\n@export var min_rebake_distance: float = 64.0\n@export var bake_cooldown: float = 1.0\n@export_group(\"Debug\")\n@export var log_timing: bool = false\n\nvar _scene_geometry: NavigationMeshSourceGeometryData3D\nvar _current_center := Vector3(INF,INF,INF)\n\nvar _bake_task_id: int = -1\nvar _bake_task_timer: float = 0.0\nvar _bake_cooldown_timer: float = 0.0\nvar _nav_region: NavigationRegion3D\n\n\nfunc _ready():\n\t_nav_region = NavigationRegion3D.new()\n\t_nav_region.navigation_layers = navigation_layers\n\t_nav_region.enabled = enabled\n\t_nav_region.enter_cost = enter_cost\n\t_nav_region.travel_cost = travel_cost\n\t\n\t# Enabling edge connections comes with a performance penalty that causes hitches whenever\n\t# the nav mesh is updated. The navigation server has to compare each edge, and it does this on\n\t# the main thread.\n\t_nav_region.use_edge_connections = false\n\t\n\tadd_child(_nav_region)\n\t\n\t_update_map_cell_size()\n\t\n\t# If you're using ProtonScatter, you will want to delay this next call until after all\n\t# your scatter nodes have finished setting up. Here, we just defer one frame so that nodes\n\t# after this one in the tree get set up first\n\tparse_scene.call_deferred()\n\n\nfunc set_enabled(p_value: bool) -> void:\n\tenabled = p_value\n\tif _nav_region:\n\t\t_nav_region.enabled = enabled\n\tset_process(enabled and template)\n\n\nfunc set_enter_cost(p_value: bool) -> void:\n\tenter_cost = p_value\n\tif _nav_region:\n\t\t_nav_region.enter_cost = enter_cost\n\n\nfunc set_travel_cost(p_value: bool) -> void:\n\ttravel_cost = p_value\n\tif _nav_region:\n\t\t_nav_region.travel_cost = travel_cost\n\n\nfunc set_navigation_layers(p_value: int) -> void:\n\tnavigation_layers = p_value\n\tif _nav_region:\n\t\t_nav_region.navigation_layers = navigation_layers\n\n\nfunc set_template(p_value: NavigationMesh) -> void:\n\ttemplate = p_value\n\tset_process(enabled and template)\n\t_update_map_cell_size()\n\n\nfunc parse_scene() -> void:\n\t_scene_geometry = NavigationMeshSourceGeometryData3D.new()\n\tNavigationServer3D.parse_source_geometry_data(template, _scene_geometry, self)\n\n\nfunc _update_map_cell_size() -> void:\n\tif get_viewport() and template:\n\t\tvar map := get_viewport().find_world_3d().navigation_map\n\t\tNavigationServer3D.map_set_cell_size(map, template.cell_size)\n\t\tNavigationServer3D.map_set_cell_height(map, template.cell_height)\n\n\nfunc _process(p_delta: float) -> void:\n\tif _bake_task_id != -1:\n\t\t_bake_task_timer += p_delta\n\t\n\tif not player or _bake_task_id != -1:\n\t\treturn\n\t\n\tif _bake_cooldown_timer > 0.0:\n\t\t_bake_cooldown_timer -= p_delta\n\t\treturn\n\t\n\tvar track_pos := player.global_position\n\tif player is CharacterBody3D:\n\t\t# Center on where the player is likely _going to be_:\n\t\ttrack_pos += player.velocity * bake_cooldown\n\t\n\tif track_pos.distance_squared_to(_current_center) >= min_rebake_distance * min_rebake_distance:\n\t\t_current_center = track_pos\n\t\t_rebake(_current_center)\n\n\nfunc _rebake(p_center: Vector3) -> void:\n\tassert(template != null)\n\t_bake_task_id = WorkerThreadPool.add_task(_task_bake.bind(p_center), false, \"RuntimeNavigationBaker\")\n\t_bake_task_timer = 0.0\n\t_bake_cooldown_timer = bake_cooldown\n\n\nfunc _task_bake(p_center: Vector3) -> void:\n\tvar nav_mesh: NavigationMesh = template.duplicate()\n\tnav_mesh.filter_baking_aabb = AABB(-mesh_size * 0.5, mesh_size)\n\tnav_mesh.filter_baking_aabb_offset = p_center\n\tvar source_geometry: NavigationMeshSourceGeometryData3D\n\tsource_geometry = _scene_geometry.duplicate()\n\t\n\tif terrain:\n\t\tvar aabb: AABB = nav_mesh.filter_baking_aabb\n\t\taabb.position += nav_mesh.filter_baking_aabb_offset\n\t\tvar faces: PackedVector3Array = terrain.generate_nav_mesh_source_geometry(aabb, false)\n\t\tsource_geometry.add_faces(faces, Transform3D.IDENTITY)\n\t\n\tif source_geometry.has_data():\n\t\tNavigationServer3D.bake_from_source_geometry_data(nav_mesh, source_geometry)\n\t\t_bake_finished.call_deferred(nav_mesh)\n\telse:\n\t\t_bake_finished.call_deferred(null)\n\n\nfunc _bake_finished(p_nav_mesh: NavigationMesh) -> void:\n\tif log_timing:\n\t\tprint(\"Navigation bake took \", _bake_task_timer, \"s\")\n\t\n\t_bake_task_timer = 0.0\n\t_bake_task_id = -1\n\t\n\tif p_nav_mesh:\n\t\t_nav_region.navigation_mesh = p_nav_mesh\n\t\n\tbake_finished.emit()\n\tassert(!NavigationServer3D.region_get_use_edge_connections(_nav_region.get_region_rid()))\n"
  },
  {
    "path": "project/demo/src/RuntimeNavigationBaker.gd.uid",
    "content": "uid://brh8x1wnycrl5\n"
  },
  {
    "path": "project/demo/src/UI.gd",
    "content": "extends Control\n\n\nvar player: Node\nvar visible_mode: int = 1\n\n\nfunc _init() -> void:\n\tRenderingServer.set_debug_generate_wireframes(true)\n\n\nfunc _process(_p_delta: float) -> void:\n\t$Label.text = \"FPS: %d\\n\" % Engine.get_frames_per_second()\n\tif(visible_mode == 1):\n\t\t$Label.text += \"Move Speed: %.1f\\n\" % player.MOVE_SPEED if player else \"\"\n\t\t$Label.text += \"Position: %.1v\\n\" % player.global_position if player else \"\"\n\t\t$Label.text += \"\"\"\n\t\t\tPlayer\n\t\t\tMove: WASDEQ,Space,Mouse\n\t\t\tMove speed: Wheel,+/-,Shift\n\t\t\tCamera View: V\n\t\t\tGravity toggle: G\n\t\t\tCollision toggle: C\n\n\t\t\tWindow\n\t\t\tQuit: F8\n\t\t\tUI toggle: F9\n\t\t\tRender mode: F10\n\t\t\tFull screen: F11\n\t\t\tMouse toggle: Escape / F12\n\t\t\t\"\"\"\n\n\nfunc _unhandled_key_input(p_event: InputEvent) -> void:\n\tif p_event is InputEventKey and p_event.pressed:\n\t\tmatch p_event.keycode:\n\t\t\tKEY_F8:\n\t\t\t\tget_tree().quit()\n\t\t\tKEY_F9:\n\t\t\t\tvisible_mode = (visible_mode + 1 ) % 3\n\t\t\t\t$Label/Panel.visible = (visible_mode == 1)\n\t\t\t\tvisible = visible_mode > 0\n\t\t\tKEY_F10:\n\t\t\t\tvar vp = get_viewport()\n\t\t\t\tvp.debug_draw = (vp.debug_draw + 1 ) % 6\n\t\t\t\tget_viewport().set_input_as_handled()\n\t\t\tKEY_F11:\n\t\t\t\ttoggle_fullscreen()\n\t\t\t\tget_viewport().set_input_as_handled()\n\t\t\tKEY_ESCAPE, KEY_F12:\n\t\t\t\tif Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:\n\t\t\t\t\tInput.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)\n\t\t\t\telse:\n\t\t\t\t\tInput.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)\n\t\t\t\tget_viewport().set_input_as_handled()\n\t\t\n\t\t\nfunc toggle_fullscreen() -> void:\n\tif DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN or \\\n\t\tDisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:\n\t\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)\n\t\tDisplayServer.window_set_size(Vector2(1280, 720))\n\telse:\n\t\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)\n"
  },
  {
    "path": "project/demo/src/UI.gd.uid",
    "content": "uid://dne6na1m4xku8\n"
  },
  {
    "path": "project/icon.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://h68cek51vakm\"\npath.bptc=\"res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.bptc.ctex\"\npath.astc=\"res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.astc.ctex\"\nmetadata={\n\"imported_formats\": [\"s3tc_bptc\", \"etc2_astc\"],\n\"vram_texture\": true\n}\n\n[deps]\n\nsource_file=\"res://icon.png\"\ndest_files=[\"res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.bptc.ctex\", \"res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.astc.ctex\"]\n\n[params]\n\ncompress/mode=2\ncompress/high_quality=true\ncompress/lossy_quality=0.7\ncompress/uastc_level=0\ncompress/rdo_quality_loss=0.0\ncompress/hdr_compression=1\ncompress/normal_map=2\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/channel_remap/red=0\nprocess/channel_remap/green=1\nprocess/channel_remap/blue=2\nprocess/channel_remap/alpha=3\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "project/project.godot",
    "content": "; Engine configuration file.\n; It's best edited using the editor UI and not directly,\n; since the parameters that go here are not all obvious.\n;\n; Format:\n;   [section] ; section goes between []\n;   param=value ; assign values to parameters\n\nconfig_version=5\n\n[application]\n\nconfig/name=\"Terrain3D\"\nrun/main_scene=\"res://demo/Demo.tscn\"\nconfig/features=PackedStringArray(\"4.5\")\nconfig/icon=\"res://icon.png\"\n\n[display]\n\nwindow/vsync/vsync_mode=0\n\n[dotnet]\n\nproject/assembly_name=\"Terrain3D\"\n\n[editor_plugins]\n\nenabled=PackedStringArray(\"res://addons/terrain_3d/plugin.cfg\")\n\n[filesystem]\n\nimport/blender/enabled=false\n\n[layer_names]\n\n3d_physics/layer_1=\"Environment\"\n3d_physics/layer_2=\"Player\"\n\n[rendering]\n\ntextures/vram_compression/import_etc2_astc=true\nocclusion_culling/use_occlusion_culling=true\n"
  },
  {
    "path": "src/constants.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef CONSTANTS_CLASS_H\n#define CONSTANTS_CLASS_H\n\n#include <functional>\n\n// GDExtension uses the godot namespace, custom modules do not.\n#if defined(GDEXTENSION) && !defined(GODOT_MODULE)\nusing namespace godot;\n#endif\n\n// Engine Shortcuts\n#define RS RenderingServer::get_singleton()\n#define PS PhysicsServer3D::get_singleton()\n#define IS_EDITOR Engine::get_singleton()->is_editor_hint()\n\n// Constants\nstatic const Color COLOR_NAN{ NAN, NAN, NAN, NAN };\nstatic const Color COLOR_WHITE{ 1.0f, 1.0f, 1.0f, 1.0f };\n#define COLOR_BLACK Color(0.0f, 0.0f, 0.0f, 1.0f)\n#define COLOR_ROUGHNESS Color(1.0f, 1.0f, 1.0f, 0.5f)\n#define COLOR_CHECKED Color(1.f, 1.f, 1.0f, -1.0f)\n#define COLOR_NORMAL Color(0.5f, 0.5f, 1.0f, 1.0f)\n#define COLOR_CONTROL Color(as_float(enc_auto(true)), 0.f, 0.f, 1.0f)\n\n// For consistency between MSVC, gcc, clang\n#ifndef FLT_MAX\n#define FLT_MAX __FLT_MAX__\n#endif\n\n// Terrain3D::_warnings is uint8_t\n#define WARN_MISMATCHED_SIZE 0x01\n#define WARN_MISMATCHED_FORMAT 0x02\n#define WARN_MISMATCHED_MIPMAPS 0x04\n#define WARN_ALL 0xFF\n\n// Global Types\n\n#define V2(x) Vector2(x, x)\n#define V2I(x) Vector2i(x, x)\n#define V3(x) Vector3(x, x, x)\nstatic const Vector2 V2_ZERO{ 0.f, 0.f };\nstatic const Vector2 V2_MAX{ FLT_MAX, FLT_MAX };\nstatic const Vector2i V2I_ZERO{ 0, 0 };\nstatic const Vector2i V2I_MAX{ INT32_MAX, INT32_MAX };\nstatic const Vector3 V3_ZERO{ 0.f, 0.f, 0.f };\nstatic const Vector3 V3_MAX{ FLT_MAX, FLT_MAX, FLT_MAX };\nstatic const Vector3 V3_NAN{ NAN, NAN, NAN };\nstatic const Vector3 V3_UP{ 0.f, 1.f, 0.f };\n//static const Vector3 V3_DOWN{ 0.f, -1.f, 0.f };\n\nstruct Vector2iHash {\n\tstd::size_t operator()(const Vector2i &v) const {\n\t\tstd::size_t h1 = std::hash<int>()(v.x);\n\t\tstd::size_t h2 = std::hash<int>()(v.y);\n\t\treturn h1 ^ (h2 << 1);\n\t}\n};\n\nstruct PairVector2iIntHash {\n\tstd::size_t operator()(const std::pair<Vector2i, int> &p) const {\n\t\tstd::size_t h1 = Vector2iHash{}(p.first);\n\t\tstd::size_t h2 = std::hash<int>{}(p.second);\n\t\treturn h1 ^ (h2 << 1);\n\t}\n};\n\nstruct Vector3Hash {\n\tstd::size_t operator()(const Vector3 &v) const {\n\t\tstd::size_t h1 = std::hash<float>()(v.x);\n\t\tstd::size_t h2 = std::hash<float>()(v.y);\n\t\tstd::size_t h3 = std::hash<float>()(v.z);\n\t\treturn h1 ^ (h2 << 1) ^ (h3 << 2);\n\t}\n};\n\n// Set class name for logger.h\n\n#define CLASS_NAME() const String __class__ = get_class_static() + \\\n\t\tString(\"#\") + String::num_uint64(get_instance_id()).right(4);\n\n#define CLASS_NAME_STATIC(p_name) static inline const char *__class__ = p_name;\n\n// Validation macros\n\n#define ASSERT(cond, ret)                                                                            \\\n\tif (!(cond)) {                                                                                   \\\n\t\tUtilityFunctions::push_error(\"Assertion '\", #cond, \"' failed at \", __FILE__, \":\", __LINE__); \\\n\t\treturn ret;                                                                                  \\\n\t}\n\n#define VOID // a return value for void, to avoid compiler warnings\n\n#define IS_INIT(ret) \\\n\tif (!_terrain) { \\\n\t\treturn ret;  \\\n\t}\n\n#define IS_INIT_MESG(mesg, ret) \\\n\tif (!_terrain) {            \\\n\t\tLOG(ERROR, mesg);       \\\n\t\treturn ret;             \\\n\t}\n\n#define IS_INIT_COND(cond, ret) \\\n\tif (!_terrain || cond) {    \\\n\t\treturn ret;             \\\n\t}\n\n#define IS_INIT_COND_MESG(cond, mesg, ret) \\\n\tif (!_terrain || cond) {               \\\n\t\tLOG(ERROR, mesg);                  \\\n\t\treturn ret;                        \\\n\t}\n\n#define IS_INSTANCER_INIT(ret)                     \\\n\tif (!_terrain || !_terrain->get_instancer()) { \\\n\t\treturn ret;                                \\\n\t}\n\n#define IS_INSTANCER_INIT_MESG(mesg, ret)          \\\n\tif (!_terrain || !_terrain->get_instancer()) { \\\n\t\tLOG(ERROR, mesg);                          \\\n\t\treturn ret;                                \\\n\t}\n\n#define IS_DATA_INIT(ret)                     \\\n\tif (!_terrain || !_terrain->get_data()) { \\\n\t\treturn ret;                           \\\n\t}\n\n#define IS_DATA_INIT_MESG(mesg, ret)          \\\n\tif (!_terrain || !_terrain->get_data()) { \\\n\t\tLOG(ERROR, mesg);                     \\\n\t\treturn ret;                           \\\n\t}\n\n#endif // CONSTANTS_CLASS_H"
  },
  {
    "path": "src/generated_texture.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/rendering_server.hpp>\n\n#include \"generated_texture.h\"\n#include \"logger.h\"\n#include \"terrain_3d.h\"\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid GeneratedTexture::clear() {\n\tif (_rid.is_valid()) {\n\t\tLOG(EXTREME, \"GeneratedTexture freeing \", _rid);\n\t\tRS->free_rid(_rid);\n\t}\n\tif (_image.is_valid()) {\n\t\tLOG(EXTREME, \"GeneratedTexture unref image\", _image);\n\t\t_image.unref();\n\t}\n\t_rid = RID();\n\t_dirty = true;\n\t_size = 0;\n}\n\nRID GeneratedTexture::create(const TypedArray<Image> &p_layers) {\n\tif (!p_layers.is_empty()) {\n\t\tif (Terrain3D::debug_level >= DEBUG) {\n\t\t\tLOG(EXTREME, \"RenderingServer creating Texture2DArray, layers size: \", p_layers.size());\n\t\t\tfor (int i = 0; i < p_layers.size(); i++) {\n\t\t\t\tRef<Image> img = p_layers[i];\n\t\t\t\tLOG(EXTREME, i, \": \", img, \", empty: \", img->is_empty(), \", size: \", img->get_size(), \", format: \", img->get_format());\n\t\t\t}\n\t\t}\n\t\t_rid = RS->texture_2d_layered_create(p_layers, RenderingServer::TEXTURE_LAYERED_2D_ARRAY);\n\t\t_dirty = false;\n\t\t_size = p_layers.size();\n\t} else {\n\t\tclear();\n\t}\n\treturn _rid;\n}\n\nvoid GeneratedTexture::update(const Ref<Image> &p_image, const int p_layer) {\n\tLOG(EXTREME, \"RenderingServer updating Texture2DArray at index: \", p_layer);\n\tRS->texture_2d_update(_rid, p_image, p_layer);\n}\n\nRID GeneratedTexture::create(const Ref<Image> &p_image) {\n\tLOG(EXTREME, \"RenderingServer creating Texture2D\");\n\t_image = p_image;\n\t_rid = RS->texture_2d_create(_image);\n\t_dirty = false;\n\treturn _rid;\n}\n"
  },
  {
    "path": "src/generated_texture.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef GENERATEDTEXTURE_CLASS_H\n#define GENERATEDTEXTURE_CLASS_H\n\n#include <godot_cpp/classes/image.hpp>\n\n#include \"constants.h\"\n\nclass GeneratedTexture {\n\tCLASS_NAME_STATIC(\"Terrain3DGenTex\");\n\nprivate:\n\tRID _rid = RID();\n\tRef<Image> _image;\n\tbool _dirty = false;\n\tint _size = 0;\n\npublic:\n\tvoid clear();\n\tbool is_dirty() const { return _dirty; }\n\tRID create(const TypedArray<Image> &p_layers);\n\tvoid update(const Ref<Image> &p_image, const int p_layer);\n\tRID create(const Ref<Image> &p_image);\n\tRef<Image> get_image() const { return _image; }\n\tRID get_rid() const { return _rid; }\n\tint size() const { return _size; }\n};\n\n#endif // GENERATEDTEXTURE_CLASS_H"
  },
  {
    "path": "src/logger.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef LOGGER_CLASS_H\n#define LOGGER_CLASS_H\n\n#include <godot_cpp/variant/utility_functions.hpp>\n\n#include \"constants.h\"\n#include \"terrain_3d.h\"\n\n/**\n * Prints warnings, errors, and messages to the console.\n * Regular messages are filtered based on the user specified debug level.\n * Warnings and errors always print except in release builds.\n * EXTREME is for continuously called prints like inside snapping.\n * See Terrain3D::DebugLevel and Terrain3D::debug_level.\n *\n * Note that in DEBUG mode Godot will crash on quit due to an\n * access violation in editor_log.cpp EditorLog::_process_message().\n * This is most likely caused by us printing messages as Godot is\n * attempting to quit.\n */\n\n#ifdef DEBUG_ENABLED\n#define LOG(level, ...)                                                                                 \\\n\tdo {                                                                                                \\\n\t\tif (level == ERROR)                                                                             \\\n\t\t\tUtilityFunctions::push_error(__class__, \":\", __func__, \":\", __LINE__, \": \", __VA_ARGS__);   \\\n\t\telse if (level == WARN)                                                                         \\\n\t\t\tUtilityFunctions::push_warning(__class__, \":\", __func__, \":\", __LINE__, \": \", __VA_ARGS__); \\\n\t\telse if (level <= Terrain3D::debug_level)                                                       \\\n\t\t\tUtilityFunctions::print(__class__, \":\", __func__, \":\", __LINE__, \": \", __VA_ARGS__);        \\\n\t} while (false); // Macro safety\n#else\n#define LOG(...)\n#endif\n\n#endif // LOGGER_CLASS_H"
  },
  {
    "path": "src/register_types.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifdef GDEXTENSION\n#include <gdextension_interface.h>\n#endif\n\n#include <godot_cpp/core/class_db.hpp>\n\n#include \"register_types.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_editor.h\"\n\nvoid initialize_terrain_3d_module(ModuleInitializationLevel p_level) {\n\tif (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {\n\t\treturn;\n\t}\n\tClassDB::register_class<Terrain3D>();\n\tClassDB::register_class<Terrain3DAssets>();\n\tClassDB::register_class<Terrain3DData>();\n\tClassDB::register_class<Terrain3DEditor>();\n\tClassDB::register_class<Terrain3DCollision>();\n\tClassDB::register_class<Terrain3DInstancer>();\n\tClassDB::register_class<Terrain3DMaterial>();\n\tClassDB::register_class<Terrain3DMeshAsset>();\n\tClassDB::register_class<Terrain3DRegion>();\n\tClassDB::register_class<Terrain3DTextureAsset>();\n\tClassDB::register_class<Terrain3DUtil>();\n}\n\nvoid uninitialize_terrain_3d_module(ModuleInitializationLevel p_level) {\n\tif (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {\n\t\treturn;\n\t}\n}\n\n#ifdef GDEXTENSION\nextern \"C\" {\n// Initialization.\nGDExtensionBool GDE_EXPORT terrain_3d_init(\n\t\tGDExtensionInterfaceGetProcAddress p_get_proc_address,\n\t\tGDExtensionClassLibraryPtr p_library,\n\t\tGDExtensionInitialization *r_initialization) {\n\tGDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);\n\n\tinit_obj.register_initializer(initialize_terrain_3d_module);\n\tinit_obj.register_terminator(uninitialize_terrain_3d_module);\n\tinit_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SERVERS);\n\n\treturn init_obj.init();\n}\n}\n#endif /* GDEXTENSION */\n"
  },
  {
    "path": "src/register_types.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_REGISTER_TYPES_H\n#define TERRAIN3D_REGISTER_TYPES_H\n\n#ifdef GDEXTENSION\n#include <godot_cpp/godot.hpp>\nusing namespace godot;\n#else\n#include \"modules/register_module_types.h\"\n#endif\n\n// NOTE: These have module ending for custom module build compatibility.\nvoid initialize_terrain_3d_module(ModuleInitializationLevel p_level);\nvoid uninitialize_terrain_3d_module(ModuleInitializationLevel p_level);\n\n#endif // TERRAIN3D_REGISTER_TYPES_H\n"
  },
  {
    "path": "src/shaders/auto_shader.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n\n//INSERT: AUTO_SHADER_UNIFORMS\n#define AUTO_SHADER\ngroup_uniforms shader_uniforms.auto_shader;\nuniform float auto_slope : hint_range(0, 10) = 1.0;\nuniform float auto_height_reduction : hint_range(0, 1) = 0.1;\nuniform int auto_base_texture : hint_range(0, 31) = 0;\nuniform int auto_overlay_texture : hint_range(0, 31) = 1;\ngroup_uniforms;\n\n//INSERT: AUTO_SHADER\n\t{\n\t\t// Auto blend calculation\n\t\tfloat auto_blend = clamp(fma(auto_slope * 2.0, (w_normal.y - 1.0), 1.0)\n\t\t\t- auto_height_reduction * 0.01 * v_vertex.y, 0.0, 1.0);\n\t\t// Enable Autoshader if outside regions or painted in regions, otherwise manual painted\n\t\tuvec4 is_auto = (control & uvec4(0x1u)) | \n\t\t\tuvec4(lessThan(ivec4(index[0].z, index[1].z, index[2].z, index[3].z), ivec4(0)));\n\t\tuint u_auto = \n\t\t\t((uint(auto_base_texture) & 0x1Fu) << 27u) |\n\t\t\t((uint(auto_overlay_texture) & 0x1Fu) << 22u) |\n\t\t\t((uint(fma(auto_blend, 255.0 , 0.5)) & 0xFFu) << 14u);\n\t\tcontrol = control * (1u - is_auto) + u_auto * is_auto;\n\t}\n\n)\""
  },
  {
    "path": "src/shaders/backgrounds.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n//INSERT: NONE_FUNCTIONS\n// Takes in world space XZ (UV) and returns if the coordinate should be part of the NONE background.\nbool is_none_bg(const vec2 uv) {\n\tivec4 regions = ivec4(\n\t\tget_index_coord(uv - vec2(0.5, 0.0)).z,\n\t\tget_index_coord(uv - vec2(0.0, 0.5)).z,\n\t\tget_index_coord(uv + vec2(1.0, 0.0)).z,\n\t\tget_index_coord(uv + vec2(0.0, 1.0)).z);\n\treturn any(equal(regions, ivec4(-1)));\n}\n\n//INSERT: NONE_CHECK\n\t\t|| (_background_mode == 0u && is_none_bg(UV))\n//INSERT: FLAT_UNIFORMS\nuniform float ground_level : hint_range(-1000., 1000.) = -20.0;\nuniform float region_blend : hint_range(.001, 1., 0.001) = 0.25;\n\n//INSERT: FLAT_FUNCTIONS\n// Takes in UV2 region space coordinates, returns 1.0 or 0.0 if a region is present or not.\nfloat check_region(const vec2 uv2) {\n\tivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);\n\tint layer_index = 0;\n\tif (uint(pos.x | pos.y) < uint(_region_map_size)) {\n\t\tlayer_index = clamp(_region_map[ pos.y * _region_map_size + pos.x ] - 1, -1, 0) + 1;\n\t}\n\treturn float(layer_index);\n}\n\n// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions\nfloat get_region_blend(vec2 uv2) {\n\tuv2 -= 0.5011; // correct for floating point error\n\tconst vec2 offset = vec2(0.0, 1.0);\n\tfloat a = check_region(uv2 + offset.xy);\n\tfloat b = check_region(uv2 + offset.yy);\n\tfloat c = check_region(uv2 + offset.yx);\n\tfloat d = check_region(uv2 + offset.xx);\n\tvec2 blend_factor = vec2(2.0 + 126.0 * (1.0 - region_blend));\n\tvec2 f = fract(uv2);\n\tvec2 w = 1.0 / (1.0 + exp(blend_factor * log((1.0 - f) / f)));\n\tfloat blend = mix(mix(d, c, w.x), mix(a, b, w.x), w.y);\n\treturn (1.0 - blend) * 2.0;\n}\n\n//INSERT: FLAT_VERTEX\n\t\t// Apply background ground level and region blend, dont include texel offset\n\t\th = mix(h, ground_level, smoothstep(0.0, 1.0, get_region_blend(UV * _region_texel_size)));\n\n//INSERT: FLAT_FRAGMENT\n\tif (_background_mode != 0u) {\n\t\tfloat blend = get_region_blend(index_id * _region_texel_size + offset * _region_texel_size);\n\t\theight = mix(height, ground_level, smoothstep(0., 1., blend));\n\t}\n\n//INSERT: WORLD_NOISE_UNIFORMS\ngroup_uniforms shader_uniforms.world_background_noise;\nuniform bool world_noise_fragment_normals = false;\nuniform int world_noise_max_octaves : hint_range(0, 15) = 4;\nuniform int world_noise_min_octaves : hint_range(0, 15) = 2;\nuniform float world_noise_lod_distance : hint_range(0., 40000., 1.) = 7500.;\nuniform float world_noise_scale : hint_range(0.25, 20, 0.01) = 5.0;\nuniform float world_noise_height : hint_range(-1000., 1000., 0.1) = 32.0;\nuniform vec3 world_noise_offset = vec3(0.0);\ngroup_uniforms;\nvarying vec2 world_noise_ddxy;\n\n//INSERT: WORLD_NOISE_FUNCTIONS\n// World Noise Functions Start\n\n// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions\nfloat get_noise_region_blend(vec2 uv2) {\n\tuv2 -= 0.5011; // correct for floating point error\n\tconst vec2 offset = vec2(0.0, 1.0);\n\tfloat a = check_region(uv2 + offset.xy);\n\tfloat b = check_region(uv2 + offset.yy);\n\tfloat c = check_region(uv2 + offset.yx);\n\tfloat d = check_region(uv2 + offset.xx);\n\tvec2 w = smoothstep(vec2(0.0), vec2(1.0), fract(uv2));\n\tfloat blend = mix(mix(d, c, w.x), mix(a, b, w.x), w.y);\n    return 1.0 - blend * 2.0;\n}\n\nfloat hashf(float f) {\n\treturn fract(sin(f) * 1e4);\n}\n\nfloat hashv2(vec2 v) {\n\treturn fract(1e4 * sin(fma(17.0, v.x, v.y * 0.1)) * (0.1 + abs(sin(fma(v.y, 13.0, v.x)))));\n}\n\n// https://iquilezles.org/articles/morenoise/\nvec3 noise2D(vec2 x) {\n    vec2 f = fract(x);\n    // Quintic Hermine Curve.  Similar to SmoothStep()\n\tvec2 f2 = f * f, f3 = f2 * f, s = f - 1.0, s2 = s * s;\n\tvec2 u = f3 * fma(vec2(6.0), f2, fma(vec2(-15.0), f, vec2(10.0)));\n\tvec2 du = 30.0 * f2 * s2;\n\n    vec2 p = floor(x);\n\n\t// Four corners in 2D of a tile\n\tfloat a = hashv2( p+vec2(0,0) );\n    float b = hashv2( p+vec2(1,0) );\n    float c = hashv2( p+vec2(0,1) );\n    float d = hashv2( p+vec2(1,1) );\n\n    // Mix 4 corner percentages\n    float k0 =   a;\n    float k1 =   b - a;\n    float k2 =   c - a;\n    float k3 =   d - (b + k2);\n\treturn vec3(fma(k2, u.y, fma(u.x, fma(k3, u.y, k1), k0)),\n\t\t du * fma(vec2(k3), u.yx, vec2(k1, k2)));\n}\n\nfloat world_noise(vec2 p) {\n    float a = 0.0;\n    float b = 1.0;\n    vec2  d = vec2(0.0);\n\n    int octaves = int( clamp(\n\t\tfloat(world_noise_max_octaves) - floor(v_vertex_xz_dist/(world_noise_lod_distance)),\n\t\tfloat(world_noise_min_octaves), float(world_noise_max_octaves))\n\t);\n\t\n    for( int i=0; i < octaves; i++ ) {\n        vec3 n = noise2D(p);\n        d += n.yz;\n        a += b * n.x / (1.0 + dot(d,d));\n        b *= 0.5;\n        p = mat2( vec2(0.8, -0.6), vec2(0.6, 0.8) ) * p * 2.0;\n    }\n    return a;\n}\n\nfloat get_noise_height(const vec2 uv) {\n\tfloat weight = get_noise_region_blend(uv);\n\t// Only calculate world noise when it would be visible.\n    if (weight <= 0.5) {\n\t\treturn 0.0;\n\t}\n\t//TODO: Offset/scale UVs are semi-dependent upon region size 1024. Base on v_vertex.xz instead\n\tfloat noise = world_noise((uv + world_noise_offset.xz * 1024. / _region_size) * world_noise_scale * _region_size / 1024. * .1) *\n            world_noise_height * 10. + world_noise_offset.y * 100.;\n    weight = smoothstep(0.5, 1.0, weight);\n    return mix(0.0, noise, weight);\n}\n\n// World Noise Functions End\n\n//INSERT: WORLD_NOISE_VERTEX\n\t\t// World Noise\n\t\tif (_background_mode == 2u) {\n\t\t\tvec2 nuv_a = start_pos * _region_texel_size;\n\t\t\tvec2 nuv_b = end_pos * _region_texel_size;\n\t\t\tfloat nh = mix(get_noise_height(nuv_a), get_noise_height(nuv_b), vertex_lerp);\n\t\t\tfloat nu = mix(get_noise_height(nuv_a + vec2(_region_texel_size, 0.0)),\n\t\t\t\tget_noise_height(nuv_b + vec2(_region_texel_size, 0.0)), vertex_lerp);\n\t\t\tfloat nv = mix(get_noise_height(nuv_a + vec2(0.0, _region_texel_size)),\n\t\t\t\tget_noise_height(nuv_b + vec2(0.0, _region_texel_size)), vertex_lerp);\n\t\t\tworld_noise_ddxy = vec2(nh - nu, nh - nv);\n\t\t\th += nh;\n\t\t}\n\n//INSERT: WORLD_NOISE_FRAGMENT\n\t// World Noise\n\tif (_background_mode == 2u && world_noise_fragment_normals) {\n\t\tfloat noise_height = get_noise_height(uv2);\n\t\tu += noise_height - get_noise_height(uv2 + vec2(_region_texel_size, 0.0));\n\t\tv += noise_height - get_noise_height(uv2 + vec2(0.0, _region_texel_size));\n\t}\n\tif (_background_mode == 2u && !world_noise_fragment_normals) {\n\t\tu += world_noise_ddxy.x;\n\t\tv += world_noise_ddxy.y;\n\t}\n)\"\n"
  },
  {
    "path": "src/shaders/debug_views.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// These special inserts are injected into the shader code at the end of fragment().\n// Variables should be prefaced with __ to avoid name conflicts.\n\nR\"(\n//INSERT: DEBUG_CHECKERED\n\t// Show a checkered grid\n\t{\n\t\tvec2 __p = uv * 1.0; // scale\n\t\tvec2 __ddx = dFdx(__p);\n\t\tvec2 __ddy = dFdy(__p);\n\t\tvec2 __w = max(abs(__ddx), abs(__ddy)) + 0.01;\n\t\tvec2 __i = 2.0 * (abs(fract((__p - 0.5 * __w) / 2.0) - 0.5) - abs(fract((__p + 0.5 * __w) / 2.0) - 0.5)) / __w;\n\t\tALBEDO = vec3((0.5 - 0.5 * __i.x * __i.y) * 0.2 + 0.2);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_GREY\n\t// Show all grey\n\t{\n\t\tALBEDO = vec3(0.2);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_HEIGHTMAP_SETUP\ngroup_uniforms shader_uniforms.debug_heightmap;\nuniform float heightmap_black_height: hint_range(-2048.,2048.,.5) = -100.0;\nuniform float heightmap_white_height: hint_range(-2048.,2048.,.5) = 300.0;\ngroup_uniforms;\n\n//INSERT: DEBUG_HEIGHTMAP\n\t// Show heightmap\n\t{\n\t\tfloat __lo = min(heightmap_black_height, heightmap_white_height);\n\t\tfloat __hi = max(heightmap_black_height, heightmap_white_height);\n\t\tfloat __factor = clamp((v_vertex.y - __lo) / max(__hi - __lo, 1e-6), 0.0, 1.0);\n\t\t__factor = mix(__factor, 1.0 - __factor, float(heightmap_white_height < heightmap_black_height));\n\t\tALBEDO = vec3(smoothstep(0.0, 1.0, __factor));\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_JAGGEDNESS\n\t// Show turbulent areas of the terrain surface\n\t{\n\t\tconst vec3 __offsets = vec3(0, 1, 2);\n\t\tvec2 __index_id = floor((INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xz);\n\t\tfloat __h[6];\n\t\t__h[0] = texelFetch(_height_maps, get_index_coord(__index_id + __offsets.xy), 0).r;\n\t\t__h[1] = texelFetch(_height_maps, get_index_coord(__index_id + __offsets.yy), 0).r;\n\t\t__h[2] = texelFetch(_height_maps, get_index_coord(__index_id + __offsets.yx), 0).r;\n\t\t__h[3] = texelFetch(_height_maps, get_index_coord(__index_id + __offsets.xx), 0).r;\n\t\t__h[4] = texelFetch(_height_maps, get_index_coord(__index_id + __offsets.zx), 0).r;\n\t\t__h[5] = texelFetch(_height_maps, get_index_coord(__index_id + __offsets.xz), 0).r;\n\n\t\tvec3 __normal[3];\n\t\t__normal[0] = normalize(vec3(__h[0] - __h[1], _vertex_spacing, __h[0] - __h[5]));\n\t\t__normal[1] = normalize(vec3(__h[2] - __h[4], _vertex_spacing, __h[2] - __h[1]));\n\t\t__normal[2] = normalize(vec3(__h[3] - __h[2], _vertex_spacing, __h[3] - __h[0]));\n\n\t\tfloat __jaggedness = max(length(__normal[2] - __normal[1]),length(__normal[2] - __normal[0]));\n\t\tALBEDO = vec3(0.01 + pow(__jaggedness, 8.));\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_AUTOSHADER\n\t// Show where autoshader enabled\n\t{\n\t\tivec3 __ruv = get_index_coord(floor(uv));\n\t\tuint __control = floatBitsToUint(texelFetch(_control_maps, __ruv, 0).r);\n\t\tfloat __autoshader = float( bool(__control & 0x1u) || __ruv.z<0 );\n\t\tALBEDO = vec3(__autoshader);\n\t\tROUGHNESS = 1.;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_CONTROL_TEXTURE\n\t// Show control map texture selection\n\t{\n\t\tvec3 __t_colors[32];\n\t\t__t_colors[0] = vec3(1.0, 0.0, 0.0);\n\t\t__t_colors[1] = vec3(0.0, 1.0, 0.0);\n\t\t__t_colors[2] = vec3(0.0, 0.0, 1.0);\n\t\t__t_colors[3] = vec3(1.0, 0.0, 1.0);\n\t\t__t_colors[4] = vec3(0.0, 1.0, 1.0);\n\t\t__t_colors[5] = vec3(1.0, 1.0, 0.0);\n\t\t__t_colors[6] = vec3(0.2, 0.0, 0.0);\n\t\t__t_colors[7] = vec3(0.0, 0.2, 0.0);\n\t\t__t_colors[8] = vec3(0.0, 0.0, 0.35);\n\t\t__t_colors[9] = vec3(0.2, 0.0, 0.2);\n\t\t__t_colors[10] = vec3(0.0, 0.2, 0.2);\n\t\t__t_colors[11] = vec3(0.2, 0.2, 0.0);\n\t\t__t_colors[12] = vec3(0.1, 0.0, 0.0);\n\t\t__t_colors[13] = vec3(0.0, 0.1, 0.0);\n\t\t__t_colors[14] = vec3(0.0, 0.0, 0.15);\n\t\t__t_colors[15] = vec3(0.1, 0.0, 0.1);\n\t\t__t_colors[16] = vec3(0.0, 0.1, 0.1);\n\t\t__t_colors[17] = vec3(0.1, 0.1, 0.0);\n\t\t__t_colors[18] = vec3(0.2, 0.05, 0.05);\n\t\t__t_colors[19] = vec3(0.1, 0.3, 0.1);\n\t\t__t_colors[20] = vec3(0.05, 0.05, 0.2);\n\t\t__t_colors[21] = vec3(0.1, 0.05, 0.2);\n\t\t__t_colors[22] = vec3(0.05, 0.15, 0.2);\n\t\t__t_colors[23] = vec3(0.2, 0.2, 0.1);\n\t\t__t_colors[24] = vec3(1.0);\n\t\t__t_colors[25] = vec3(0.5);\n\t\t__t_colors[26] = vec3(0.35);\n\t\t__t_colors[27] = vec3(0.25);\n\t\t__t_colors[28] = vec3(0.15);\n\t\t__t_colors[29] = vec3(0.1);\n\t\t__t_colors[30] = vec3(0.05);\n\t\t__t_colors[31] = vec3(0.0125);\n\t\tivec3 __uv = get_index_coord(floor(uv));\n\t\tuint __control = floatBitsToUint(texelFetch(_control_maps, __uv, 0).r);\n\t\tvec3 __ctrl_base = __t_colors[int(__control >>27u & 0x1Fu)];\n\t\tvec3 __ctrl_over = __t_colors[int(__control >>22u & 0x1Fu)];\n\t\tfloat __blend = float(__control >>14u & 0xFFu) * 0.003921568627450; // 1.0/255.0\n\t\tfloat base_over = (length(fract(uv) - 0.5) < fma(__blend, 0.45, 0.1) ? 1.0 : 0.0);\n\t\tALBEDO = mix(__ctrl_base, __ctrl_over, base_over);\t\n\t\tROUGHNESS = 1.0;\n\t\tSPECULAR = 0.0;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_CONTROL_BLEND\n\t// Show control map blend values\n\t{\n\t\tivec3 __uv = get_index_coord(floor(uv));\n        uint __control = floatBitsToUint(texelFetch(_control_maps, __uv, 0).r);\n        float __ctrl_blend = float(__control >>14u & 0xFFu) * 0.003921568627450; // 1.0/255.0\n\t\tfloat __is_auto = 0.;\n\t\t#ifdef AUTO_SHADER\n\t\t\t__is_auto = float( bool(__control & 0x1u) || __uv.z < 0 );\n\t\t#endif\n\t\tALBEDO = vec3(__ctrl_blend) * (1.0 - __is_auto) + vec3(0.2, 0.0, 0.0) * __is_auto;\n\t\tROUGHNESS = 1.;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_CONTROL_ANGLE\n\t// Show control map texture angle\n\t{\n\t\tivec3 __auv = get_index_coord(floor(uv));\n\t\tuint __a_control = floatBitsToUint(texelFetch(_control_maps, __auv, 0)).r;\n\t\tuint __angle = (__a_control >>10u & 0xFu);\n\t\tvec3 __a_colors[16] = {\n\t\t\tvec3(1., .2, .0), vec3(.8, 0., .2), vec3(.6, .0, .4), vec3(.4, .0, .6),\n\t\t\tvec3(.2, 0., .8), vec3(.1, .1, .8), vec3(0., .2, .8), vec3(0., .4, .6),\n\t\t\tvec3(0., .6, .4), vec3(0., .8, .2), vec3(0., 1., 0.), vec3(.2, 1., 0.),\n\t\t\tvec3(.4, 1., 0.), vec3(.6, 1., 0.), vec3(.8, .6, 0.), vec3(1., .4, 0.)\n\t\t};\n\t\tALBEDO = __a_colors[__angle];\n\t\tROUGHNESS = 1.;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_CONTROL_SCALE\n\t// Show control map texture scale\n\t{\n\t\tivec3 __suv = get_index_coord(floor(uv));\n\t\tuint __s_control = floatBitsToUint(texelFetch(_control_maps, __suv, 0)).r;\n\t\tuint __scale = (__s_control >>7u & 0x7u);\n\t\tvec3 __s_colors[8] = {\n\t\t\tvec3(.5, .5, .5), vec3(.675, .25, .375), vec3(.75, .125, .25), vec3(.875, .0, .125), vec3(1., 0., 0.),\n\t\t\tvec3(0., 0., 1.), vec3(.0, .166, .833), vec3(.166, .333, .666)\n\t\t};\n\t\tALBEDO = __s_colors[__scale];\n\t\tROUGHNESS = 1.;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5 ,0.5 ,1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_COLORMAP\n\t// Show colormap\n\t{\n\t\tALBEDO = color_map.rgb;\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_ROUGHMAP\n\t// Show roughness map\n\t{\n\t\tALBEDO = vec3(color_map.a);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: DEBUG_DISPLACEMENT_BUFFER\n\t// Show displacement buffer\n\t#ifdef DISPLACEMENT\n\t{\n\t\tfloat scale = MODEL_MATRIX[0][0];\n\t\tfloat vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));\n\t\tfloat disp = dot(w_normal, mix(get_displacement(uv, scale), get_displacement(uv, scale * 2.0), vertex_lerp));\n\t\t// Red values lower than collision, Green values above. Black if no deviation.\n\t\tALBEDO = vec3(-disp, disp, 0.);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\t#endif\n\n)\"\n"
  },
  {
    "path": "src/shaders/displacement.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n\n//INSERT: DISPLACEMENT_UNIFORMS\n#define DISPLACEMENT\nuniform float _displacement_scale : hint_range(0.0, 2.0, 0.01) = 1.0;\nuniform highp sampler2D _displacement_buffer : repeat_disable, filter_linear;\n\n//INSERT: DISPLACEMENT_FUNCTIONS\nvec3 get_displacement(vec2 pos, float scale) {\n\tfloat s = floor(log2(1.0 / (scale * _vertex_density)));\n\tscale = pow(2.0, s) * 0.5;\n\tvec2 d_uv = (scale * pos - round(_target_pos.xz * _vertex_density * scale)) / (_mesh_size * 2.0) + 0.5;\n\td_uv.x += s - 1.;\n\td_uv.x /= log2(_subdiv);\n\thighp vec3 disp = vec3(0.);\n\tif (all(greaterThanEqual(d_uv, vec2(0.0))) && all(lessThanEqual(d_uv, vec2(1.0)))) {\n\t\tdisp = textureLod(_displacement_buffer, d_uv, 0.).rgb * 2.0 - 1.0;\n\t\tdisp *= _displacement_scale;\n\t}\n\treturn disp;\n}\n\n//INSERT: DISPLACEMENT_VERTEX\n\t\tif (!(CAMERA_VISIBLE_LAYERS == _mouse_layer)) {\n\t\t\tdisplacement = mix(get_displacement(start_pos, scale), get_displacement(end_pos, scale * 2.0), vertex_lerp);\n\t\t}\n\n)\"\n"
  },
  {
    "path": "src/shaders/displacement_buffer.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(shader_type canvas_item;\n\n// Displacement buffer shader, mimics the main shader in 2d and outputs\n// xyz offset stored in RGB channels. A is unusuable when passing\n// the buffer directly via RID (avoids GPU > CPU > GPU copies) as alpha\n// is premultiplied by the renderer.\n\n#define IS_DISPLACEMENT_BUFFER\n\n// Defined Constants\n#define SKIP_PASS 0\n#define VERTEX_PASS 1\n#define FRAGMENT_PASS 2\n#define COLOR_MAP_DEF vec4(1.0, 1.0, 1.0, 0.5)\n#define DIV_255 0.003921568627450 // 1. / 255.\n#define DIV_1024 0.0009765625 // 1. / 1024.\n#define TAU_16TH -0.392699081698724 // -TAU / 16.\n\n// Inline Functions\n#define DECODE_BLEND(control) float(control >>14u & 0xFFu) * DIV_255\n#define DECODE_AUTO(control) bool(control & 0x1u)\n#define DECODE_BASE(control) int(control >>27u & 0x1Fu)\n#define DECODE_OVER(control) int(control >>22u & 0x1Fu)\n#define DECODE_ANGLE(control) float(control >>10u & 0xFu) * TAU_16TH\n// This math recreates the scale value directly rather than using an 8 float const array.\n#define DECODE_SCALE(control) (0.9 - float(((control >>7u & 0x7u) + 3u) % 8u + 1u) * 0.1)\n#define DECODE_HOLE(control) bool(control >>2u & 0x1u)\n\n#define TEXTURE_ID_PROJECTED(id) bool((_texture_vertical_projections >> uint(id)) & 0x1u)\n\n#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n    #define fma(a, b, c) ((a) * (b) + (c))\n    #define dFdxCoarse(a) dFdx(a)\n    #define dFdyCoarse(a) dFdy(a)\n#endif\n\n// Private uniforms\nuniform float _tessellation_level = 0.;\nuniform vec3 _target_pos = vec3(0.f);\nuniform float _mesh_size = 48.f;\nuniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2\nuniform float _vertex_spacing = 1.0;\nuniform float _vertex_density = 1.0; // = 1./_vertex_spacing\nuniform float _region_size = 1024.0;\nuniform float _region_texel_size = 0.0009765625; // = 1./region_size\nuniform int _region_map_size = 32;\nuniform int _region_map[1024];\nuniform vec2 _region_locations[1024];\nuniform float _texture_uv_scale_array[32];\nuniform vec2 _texture_detile_array[32];\nuniform vec2 _texture_displacement_array[32];\nuniform highp sampler2DArray _height_maps : repeat_disable;\nuniform highp sampler2DArray _control_maps : repeat_disable;\n//INSERT: TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\n//INSERT: TEXTURE_SAMPLERS_LINEAR\n//INSERT: TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\n//INSERT: TEXTURE_SAMPLERS_NEAREST\nuniform highp sampler2DArray _texture_array_albedo : source_color, FILTER_METHOD, repeat_enable;\nuniform highp sampler2DArray _texture_array_normal : hint_normal, FILTER_METHOD, repeat_enable;\n\n// Public uniforms\ngroup_uniforms shader_uniforms.general;\nuniform float blend_sharpness : hint_range(0, 1) = 0.5;\ngroup_uniforms;\n//INSERT: AUTO_SHADER_UNIFORMS\n\n//INSERT: FLAT_UNIFORMS\n//INSERT: FLAT_FUNCTIONS\n\n// Uniquely named displacement uniforms should be in this group.\n// Uniforms that are shared with the main shader are automatically synchronised.\n// Subgroups should work as expected.\ngroup_uniforms shader_uniforms.displacement;\nuniform float _displacement_sharpness : hint_range(0.0, 1.0, 0.01) = 0.25;\ngroup_uniforms;\n\n// Varyings & Types\n\n// We only care about texture height value.\nstruct material {\n\tfloat height;\n\tfloat total_weight;\n};\n\n////////////////////////\n// Vertex\n////////////////////////\n\n// Takes in world space XZ (UV) coordinates\n// Returns ivec3 with:\n// XY: (0 to _region_size - 1) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nivec3 get_index_coord(const vec2 uv) {\n\tvec2 r_uv = round(uv);\n\tivec2 pos = ivec2(floor(r_uv * _region_texel_size)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[pos.y * _region_map_size + pos.x] * bounds - 1;\n\treturn ivec3(ivec2(mod(r_uv, _region_size)), layer_index);\n}\n\n// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:\n// XY: (0. to 1.) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nvec3 get_index_uv(const vec2 uv2) {\n\tivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;\n\treturn vec3(uv2 - _region_locations[layer_index], float(layer_index));\n}\n\n////////////////////////\n// Fragment\n////////////////////////\n\nfloat random(in vec2 xy) {\n\treturn fract(sin(dot(xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nvec2 rotate_vec2(const vec2 v, const vec2 cs) {\n\treturn vec2(fma(cs.x, v.x,  cs.y * v.y), fma(cs.x, v.y, -cs.y * v.x));\n}\n\n// 2-4 lookups ( 2-6 with dual scaling )\nvoid accumulate_material(const mat3 TNB, const float weight, const ivec3 index,\n\t\t\tconst uint control, const vec2 texture_weight, const ivec2 texture_id, const vec3 i_normal,\n\t\t\tfloat h, inout material mat, const vec3 v_vertex) {\n\n\t// Applying scaling before projection reduces the number of multiplys ops required.\n\tvec3 i_vertex = v_vertex;\n\n\t// Control map scale\n\tfloat control_scale = DECODE_SCALE(control);\n\ti_vertex *= control_scale;\n\th *= control_scale;\n\n\t// Index position for detiling.\n\tvec2 i_pos = fma(_region_locations[index.z], vec2(_region_size), vec2(index.xy));\n\ti_pos *= _vertex_spacing * control_scale;\n\n\t// Projection\n\tvec2 i_uv = i_vertex.xz;\n\tmat2 p_align = mat2(1.);\n//INSERT: PROJECTION\n\n\t// Control map rotation. Must be applied seperatley from detiling to maintain UV continuity.\n\tfloat c_angle = DECODE_ANGLE(control);\n\tvec2 c_cs_angle = vec2(cos(c_angle), sin(c_angle));\n\ti_uv = rotate_vec2(i_uv, c_cs_angle);\n\ti_pos = rotate_vec2(i_pos, c_cs_angle);\n\n\t// Blend adjustment of Higher ID from Lower ID normal map in world space.\n\tfloat world_normal = 1.;\n\t// mat3 multiply, reduced to 2x fma and 1x mult.\n\t#define FAST_WORLD_NORMAL(n) fma(TNB[0], vec3(n.x), fma(TNB[2], vec3(n.z), TNB[1] * vec3(n.y)))\n\n\tfloat blend = DECODE_BLEND(control); // only used for branching.\n\tfloat sharpness = fma(60., blend_sharpness * _displacement_sharpness, 4.);\n\n\t// 1st Texture Asset ID\n\tif (blend < 1.0) {\n\t\tint id = texture_id[0];\n\t\tfloat id_w = texture_weight[0];\n\t\tfloat id_scale = _texture_uv_scale_array[id];\n\n\t\t// Detiling and Control map rotation\n\t\tvec2 uv_center = floor(fma(i_pos, vec2(id_scale), vec2(0.5)));\n\t\tvec2 id_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[id] * TAU;\n\t\tvec2 id_cs_angle = vec2(cos(id_detile.x), sin(id_detile.x));\n\t\t// Apply UV rotation and shift around pivot.\n\t\tvec2 id_uv = rotate_vec2(fma(i_uv, vec2(id_scale), -uv_center), id_cs_angle) + uv_center + id_detile.y - 0.5;\n\t\t// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also\n\t\t// including control map rotation. avoids extra matrix op, and sin/cos calls.\n\t\tid_cs_angle = vec2(\n\t\t\tfma(id_cs_angle.x, c_cs_angle.x, -id_cs_angle.y * c_cs_angle.y),\n\t\t\tfma(id_cs_angle.y, c_cs_angle.x, id_cs_angle.x * c_cs_angle.y));\n\n\t\tfloat h = textureLod(_texture_array_albedo, vec3(id_uv, float(id)), 0.).a;\n\t\tvec4 nrm = textureLod(_texture_array_normal, vec3(id_uv, float(id)), 0.);\n\t\t// Unpack and rotate normal map.\n\t\tnrm.xyz = fma(nrm.xzy, vec3(2.0), vec3(-1.0));\n\t\tnrm.xz = rotate_vec2(nrm.xz, id_cs_angle) * p_align;\n\n\t\tworld_normal = FAST_WORLD_NORMAL(nrm).y;\n\n\t\tfloat id_weight = exp2(sharpness * log2(weight + id_w + h)) * weight;\n\n\t\t// height is modified after weight calculation so that asset offset and scale values do not interfear with material blending.\n\t\tfloat height_scale  = (_texture_displacement_array[id].y * 0.04) / (control_scale * id_scale);\n\t\tfloat height_offset = 0.5 + _texture_displacement_array[id].x * height_scale - 0.5 * height_scale;\n\t\th = clamp(fma(h, height_scale, height_offset), 0., 1.);\n\n\t\tmat.height = fma(h, id_weight, mat.height);\n\t\tmat.total_weight += id_weight;\n\t}\n\n\t// 2nd Texture Asset ID\n\tif (blend > 0.0 && texture_id[1] != texture_id[0]) {\n\t\tint id = texture_id[1];\n\t\tfloat id_w = texture_weight[1];\n\t\tfloat id_scale = _texture_uv_scale_array[id];\n\n\t\t// Detiling and Control map rotation\n\t\tvec2 uv_center = floor(fma(i_pos, vec2(id_scale), vec2(0.5)));\n\t\tvec2 id_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[id] * TAU;\n\t\tvec2 id_cs_angle = vec2(cos(id_detile.x), sin(id_detile.x));\n\t\t// Apply UV rotation and shift around pivot.\n\t\tvec2 id_uv = rotate_vec2(fma(i_uv, vec2(id_scale), -uv_center), id_cs_angle) + uv_center + id_detile.y - 0.5;\n\t\t// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also\n\t\t// including control map rotation. avoids extra matrix op, and sin/cos calls.\n\t\tid_cs_angle = vec2(\n\t\t\tfma(id_cs_angle.x, c_cs_angle.x, -id_cs_angle.y * c_cs_angle.y),\n\t\t\tfma(id_cs_angle.y, c_cs_angle.x, id_cs_angle.x * c_cs_angle.y));\n\n\t\tfloat h = textureLod(_texture_array_albedo, vec3(id_uv, float(id)), 0.).a;\n\t\t// Normals are not required for 2nd ID as they are not used to adjust the weights.\n\n\t\tfloat id_weight = exp2(sharpness * log2(weight + id_w + h * clamp(world_normal, 0., 1.))) * weight;\n\n\t\t// height is modified after weight calculation so that asset offset and scale values do not interfear with material blending.\n\t\tfloat height_scale  = (_texture_displacement_array[id].y * 0.04) / (control_scale * id_scale);\n\t\tfloat height_offset = 0.5 + _texture_displacement_array[id].x * height_scale - 0.5 * height_scale;\n\t\th = clamp(fma(h, height_scale, height_offset), 0., 1.);\n\n\t\tmat.height = fma(h, id_weight, mat.height);\n\t\tmat.total_weight += id_weight;\n\t}\n}\n\nfloat get_height(vec2 index_id, vec2 offset) {\n\tfloat height = texelFetch(_height_maps, get_index_coord(index_id + offset), 0).r;\n//INSERT: FLAT_FRAGMENT\n\treturn height;\n}\n\n)\"\n\n\t\tR\"(\nvoid fragment() {\n\t// Calculate Tiled UVs\n\tfloat scale = floor(UV.x * (_tessellation_level));\n\tfloat p_scale = pow(2.0, scale);\n\tvec2 uv = (vec2(fract(UV.x * _tessellation_level), UV.y) - 0.5) * (_mesh_size * 2.0) / p_scale;\n\tuv += round(_target_pos.xz * _vertex_density * p_scale) / p_scale;\n\tvec2 uv2 = uv * _region_texel_size;\n\n\t// Lookup offsets, ID and blend weight\n\tvec3 region_uv = get_index_uv(uv2);\n\tconst vec3 offsets = vec3(0, 1, 2);\n\tvec2 index_id = floor(uv);\n\tvec2 weight = fract(uv);\n\tvec2 invert = 1.0 - weight;\n\tvec4 weights = vec4(\n\t\tinvert.x * weight.y, // 0\n\t\tweight.x * weight.y, // 1\n\t\tweight.x * invert.y, // 2\n\t\tinvert.x * invert.y  // 3\n\t);\n\n\tivec3 index[4];\n\t// control map lookups, used for some normal lookups as well\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\n\t// Terrain normals\n\tvec3 index_normal[4];\n\tfloat h[4];\n\t// allows additional derivatives, eg world noise, brush previews etc\n\tfloat u = 0.0;\n\tfloat v = 0.0;\n\n\t// Re-use index[] for the first lookups, skipping some math. 3 lookups\n\th[3] = get_height(index_id, offsets.xx); // 0 (0, 0)\n\th[2] = get_height(index_id, offsets.yx); // 1 (1, 0)\n\th[0] = get_height(index_id, offsets.xy); // 2 (0, 1)\n\tindex_normal[3] = normalize(vec3(h[3] - h[2] + u, _vertex_spacing, h[3] - h[0] + v));\n\n\t// 5 lookups\n\t// Fetch the additional required height values for smooth normals\n\th[1] = get_height(index_id, offsets.yy); // 3 (1, 1)\n\tfloat h_4 = get_height(index_id, offsets.yz); // 4 (1, 2)\n\tfloat h_5 = get_height(index_id, offsets.zy); // 5 (2, 1)\n\tfloat h_6 = get_height(index_id, offsets.zx); // 6 (2, 0)\n\tfloat h_7 = get_height(index_id, offsets.xz); // 7 (0, 2)\n\n\t// Calculate the normal for the remaining index ids.\n\tindex_normal[0] = normalize(vec3(h[0] - h[1] + u, _vertex_spacing, h[0] - h_7 + v));\n\tindex_normal[1] = normalize(vec3(h[1] - h_5 + u, _vertex_spacing, h[1] - h_4 + v));\n\tindex_normal[2] = normalize(vec3(h[2] - h_6 + u, _vertex_spacing, h[2] - h[1] + v));\n\n\t// Set interpolated world normal\n\tvec3 w_normal =\n\t\tindex_normal[0] * weights[0] +\n\t\tindex_normal[1] * weights[1] +\n\t\tindex_normal[2] * weights[2] +\n\t\tindex_normal[3] * weights[3] ;\n\tvec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));\n\tvec3 w_binormal = normalize(cross(w_normal, w_tangent));\n\tmat3 TNB = mat3(w_tangent, w_normal, w_binormal);\n\n\t// We have to construct interpolted height value here, as we are operating solely in texture space.\n\tfloat terrain_height = h[0] * weights[0] + h[1] * weights[1] + h[2] * weights[2] + h[3] * weights[3];\n\tvec3 v_vertex = vec3(uv.x * _vertex_spacing, terrain_height, uv.y * _vertex_spacing);\n\n\t// Get index control data\n\t// 1 - 4 lookups\n\tuvec4 control = uvec4(\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[0], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[1], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[2], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[3], 0).r));\n\n//INSERT: AUTO_SHADER\n\n\t// Vectorised Deocode of all texture IDs, then swizzle to per index mapping.\n\t// Passed to accumulate_material to avoid repeated decoding.\n\tivec4 t_id[2] = {ivec4(control >> uvec4(27u) & uvec4(0x1Fu)),\n\t\tivec4(control >> uvec4(22u) & uvec4(0x1Fu))};\n\tivec2 texture_ids[4] = ivec2[4](\n\t\tivec2(t_id[0].x, t_id[1].x),\n\t\tivec2(t_id[0].y, t_id[1].y),\n\t\tivec2(t_id[0].z, t_id[1].z),\n\t\tivec2(t_id[0].w, t_id[1].w));\n\n\t// uninterpolated weights.\n\tvec4 weights_id_1 = vec4(control >> uvec4(14u) & uvec4(0xFFu)) * DIV_255;\n\tvec4 weights_id_0 = 1.0 - weights_id_1;\n\tvec2 t_weights[4] = vec2[4](\n\t\t\t\tvec2(weights_id_0[0], weights_id_1[0]),\n\t\t\t\tvec2(weights_id_0[1], weights_id_1[1]),\n\t\t\t\tvec2(weights_id_0[2], weights_id_1[2]),\n\t\t\t\tvec2(weights_id_0[3], weights_id_1[3]));\n\t// interpolated weights\n\n\tt_weights = {vec2(0), vec2(0), vec2(0), vec2(0)};\n\tweights_id_0 *= weights;\n\tweights_id_1 *= weights;\n\tfor (int i = 0; i < 4; i++) {\n\t\tvec2 w_0 = vec2(weights_id_0[i]);\n\t\tvec2 w_1 = vec2(weights_id_1[i]);\n\t\tivec2 id_0 = texture_ids[i].xx;\n\t\tivec2 id_1 = texture_ids[i].yy;\n\t\tt_weights[0] += fma(w_0, vec2(equal(texture_ids[0], id_0)), w_1 * vec2(equal(texture_ids[0], id_1)));\n\t\tt_weights[1] += fma(w_0, vec2(equal(texture_ids[1], id_0)), w_1 * vec2(equal(texture_ids[1], id_1)));\n\t\tt_weights[2] += fma(w_0, vec2(equal(texture_ids[2], id_0)), w_1 * vec2(equal(texture_ids[2], id_1)));\n\t\tt_weights[3] += fma(w_0, vec2(equal(texture_ids[3], id_0)), w_1 * vec2(equal(texture_ids[3], id_1)));\n\t}\n\n\n\t// Struct to accumulate all texture data.\n\tmaterial mat = material(0., 0.);\n\taccumulate_material(TNB, weights[3], index[3], control[3], t_weights[3],\n\t\ttexture_ids[3], index_normal[3], h[3], mat, v_vertex);\n\taccumulate_material(TNB, weights[2], index[2], control[2], t_weights[2],\n\t\ttexture_ids[2], index_normal[2], h[2], mat, v_vertex);\n\taccumulate_material(TNB, weights[1], index[1], control[1], t_weights[1],\n\t\ttexture_ids[1], index_normal[1], h[1], mat, v_vertex);\n\taccumulate_material(TNB, weights[0], index[0], control[0], t_weights[0],\n\t\ttexture_ids[0], index_normal[0], h[0], mat, v_vertex);\n\n\t// normalize accumulated values back to 0.0 - 1.0 range.\n\tfloat weight_inv = 1.0 / max(mat.total_weight, 1e-8);\n\tmat.height *= weight_inv;\n\n\t// Output\n\tCOLOR.rgb = clamp(fma(w_normal * (mat.height - 0.5) * 2.0, vec3(0.5), vec3(0.5)), 0.0, 1.0);\n\n}\n)\"\n"
  },
  {
    "path": "src/shaders/dual_scaling.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n\n//INSERT: DUAL_SCALING_UNIFORMS\ngroup_uniforms shader_uniforms.dual_scaling;\nuniform int dual_scale_texture : hint_range(0,31) = 0;\nuniform float dual_scale_reduction : hint_range(0.001,1) = 0.3;\nuniform float tri_scale_reduction : hint_range(0.001,1) = 0.3;\nuniform float dual_scale_far : hint_range(0,1000) = 170.0;\nuniform float dual_scale_near : hint_range(0,1000) = 100.0;\ngroup_uniforms;\n\n//INSERT: DUAL_SCALING\n\t// dual scaling\n\tfloat far_factor = clamp(smoothstep(dual_scale_near, dual_scale_far, length(v_vertex - v_camera_pos)), 0.0, 1.0);\n\tvec4 far_alb = vec4(0.);\n\tvec4 far_nrm = vec4(0.);\n\tfloat far_ao = 1.0;\n\tif (far_factor > 0. && any(equal(texture_id, ivec2(dual_scale_texture)))) {\n\t\tfloat far_scale = _texture_uv_scale_array[dual_scale_texture] * dual_scale_reduction;\n\t\tif (index.z < 0) {\n\t\t\tfar_scale *= tri_scale_reduction;\n\t\t}\n\t\tvec4 far_dd = i_dd * far_scale;\n\n\t\t// Detiling and Control map rotation\n\t\tvec2 uv_center = floor(fma(i_pos, vec2(far_scale), vec2(0.5)));\n\t\tvec2 far_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[dual_scale_texture] * TAU;\n\t\tvec2 far_cs_angle = vec2(cos(far_detile.x), sin(far_detile.x));\n\t\t// Apply UV rotation and shift around pivot.\n\t\tvec2 far_uv = rotate_vec2(fma(i_uv, vec2(far_scale), -uv_center), far_cs_angle) + uv_center + far_detile.y - 0.5;\n\t\t// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also\n\t\t// including control map rotation. avoids extra matrix op, and sin/cos calls.\n\t\tfar_cs_angle = vec2(\n\t\t\tfma(far_cs_angle.x, c_cs_angle.x, -far_cs_angle.y * c_cs_angle.y),\n\t\t\tfma(far_cs_angle.y, c_cs_angle.x, far_cs_angle.x * c_cs_angle.y));\n\t\t// Align derivatives for correct anisotropic filtering\n\t\tfar_dd.xy = rotate_vec2(far_dd.xy, far_cs_angle);\n\t\tfar_dd.zw = rotate_vec2(far_dd.zw, far_cs_angle);\n\n\t\tfar_alb = textureGrad(_texture_array_albedo, vec3(far_uv, float(dual_scale_texture)), far_dd.xy, far_dd.zw);\n\t\tfar_nrm = textureGrad(_texture_array_normal, vec3(far_uv, float(dual_scale_texture)), far_dd.xy, far_dd.zw);\n\t\tfar_alb.rgb *= _texture_color_array[dual_scale_texture].rgb;\n\t\tfar_nrm.a = clamp(far_nrm.a + _texture_roughness_mod_array[dual_scale_texture], 0., 1.);\n\t\t// Unpack and rotate normal map.\n\t\tfar_nrm.xyz = fma(far_nrm.xzy, vec3(2.0), vec3(-1.0));\n\t\tfar_ao = length(far_nrm.xyz) * 2.0 - 1.0;\n\t\tfar_ao = mix(far_ao * far_ao * _texture_ao_strength_array[dual_scale_texture] + 1.0 - _texture_ao_strength_array[dual_scale_texture], 1.0, far_alb.a * far_alb.a);\n\t\tfar_nrm.xyz = normalize(far_nrm.xyz);\n\t\tfar_nrm.xz = rotate_vec2(far_nrm.xz, far_cs_angle) * p_align;\n\t\t\n\t\t// apply weighting when far_factor == 1.0 as the later lookup will be skipped.\n\t\tif (far_factor == 1.0) {\n\t\t\tfloat id_w = texture_id[0] == dual_scale_texture ? texture_weight[0] : texture_weight[1];\n\t\t\tfloat id_weight = exp2(sharpness * log2(weight + id_w + far_alb.a)) * weight;\n\t\t\tworld_normal = FAST_WORLD_NORMAL(far_nrm).y;\n\t\t\tmat.albedo_height = fma(far_alb, vec4(id_weight), mat.albedo_height);\n\t\t\tmat.normal_rough = fma(far_nrm, vec4(id_weight), mat.normal_rough);\n\t\t\tmat.normal_map_depth = fma(_texture_normal_depth_array[dual_scale_texture], id_weight, mat.normal_map_depth);\n\t\t\tmat.ao = fma(far_ao, id_weight, mat.ao);\n\t\t\tmat.ao_affect = fma(_texture_ao_affect_array[dual_scale_texture], id_weight, mat.ao_affect);\n\t\t\tmat.total_weight += id_weight;\n\t\t}\n\t}\n\n//INSERT: DUAL_SCALING_CONDITION_0\n\t\t&& !(far_factor == 1.0 && texture_id[0] == dual_scale_texture)\n//INSERT: DUAL_SCALING_CONDITION_1\n\t\t&& !(far_factor == 1.0 && texture_id[1] == dual_scale_texture)\n//INSERT: DUAL_SCALING_MIX\n\t\t// If dual scaling, apply to overlay texture\n\t\tif (id == dual_scale_texture && far_factor > 0.) {\n\t\t\talb = mix(alb, far_alb, far_factor);\n\t\t\tnrm = mix(nrm, far_nrm, far_factor);\n\t\t\tao = mix(ao, far_ao, far_factor);\n\t\t\tworld_normal = mix(world_normal, 1.0, far_factor);\n\t\t}\n)\""
  },
  {
    "path": "src/shaders/editor_functions.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// These special inserts are injected into the shader code at the end of fragment().\n\nR\"(\n//INSERT: EDITOR_NAVIGATION\n\t// Show navigation\n\t{\n\t\tif(bool(floatBitsToUint(texelFetch(_control_maps, get_index_coord(floor(uv + 0.5)), 0)).r >>1u & 0x1u)) {\n\t\t\tALBEDO *= vec3(.5, .0, .85);\n\t\t}\n\t}\n\n//INSERT: EDITOR_REGION_GRID\n\t// Show region grid\n\t{\n\t\tvec3 __boundary_color = pow(vec3(0.095, 0.328, 0.56), vec3(2.2)); // Medium dark blue, hue 210, converted to linear\n\t\tvec3 __active_color = vec3(1.0);\n\t\tvec3 __inactive_color = vec3(0.1);\n\t\tfloat __line_thickness = 0.05 * sqrt(-VERTEX.z);\n\t\tvec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz * _vertex_density;\n\t\tvec2 __p = __pixel_pos.xz;\n\t\t// Region Grid\n\t\tvec2 __g = abs(fract((__p + _region_size * 0.5) / _region_size) - 0.5) * _region_size;\n\t\tfloat __grid_d = min(__g.x, __g.y);\n\t\tfloat __grid_mask = 1.0 - smoothstep(__line_thickness - fwidth(__grid_d), __line_thickness + fwidth(__grid_d), __grid_d);\n\t\t// Region Map Boundry\n\t\tfloat __hmap = _region_size * 16.0;\n\t\tvec2 __bp = abs(__p) - __hmap;\n\t\tfloat __box_d = abs(max(__bp.x, __bp.y));\n\t\tfloat __box_mask = 1.0 - smoothstep( __line_thickness - fwidth(__box_d), __line_thickness + fwidth(__box_d), __box_d);\n\t\t// Clip Grid at Boundary\n\t\tfloat __b_line = __hmap - __line_thickness - fwidth(__box_d);\n\t\t__grid_mask *= step(abs(__p.x), __b_line) * step(abs(__p.y), __b_line);\n\t\tvec3 __grid_color = mix(__inactive_color, __active_color, float(clamp(get_index_coord(__pixel_pos.xz - 0.5).z + 1, 0, 1)));\n\t\tALBEDO = mix(ALBEDO, __grid_mask * __grid_color + __box_mask * __boundary_color, max(__grid_mask, __box_mask));\n\t}\n\n//INSERT: EDITOR_DECAL_SETUP\nuniform highp sampler2D _editor_brush_texture : source_color, filter_linear, repeat_disable;\nuniform vec2 _editor_decal_position[3];\nuniform float _editor_decal_size[3];\nuniform float _editor_decal_rotation[3];\nuniform vec4 _editor_decal_color[3] : source_color;\nuniform bool _editor_decal_visible[3]; // show decal: brush, slope point1, point2\nuniform bool _editor_decal_part[2]; // show decal[0] component: texture, reticle\n\nfloat get_reticle(vec2 uv, int index) {\n\tfloat cam_dist = clamp(length(v_camera_pos - v_vertex), 0., 4000.);\n\tfloat sq_cam_dist = sqrt(cam_dist);\n\tfloat view_scale = 16.0 / sq_cam_dist;\n\tvec2 cross_uv = (uv * _vertex_spacing - _editor_decal_position[index]) * view_scale;\n\tfloat brush_radius = max((_editor_decal_size[index] * 0.5) * view_scale, 1.);\n\tfloat line_start = brush_radius + log2(cam_dist);\n\tfloat line_end = 2.5 * line_start;\n\tfloat line_thickness = .03 * length(cross_uv) + .03 * sq_cam_dist; // flanged lines + distant thickness\n\n\tfloat cursor = 0.;\n\tfloat h, v;\n\n\t// Crosshair\n\tvec2 d = abs(cross_uv);\n\th = smoothstep(line_thickness, 0.0, d.y) * step(d.x, line_end) * step(line_start, d.x);\n\tv = smoothstep(line_thickness, 0.0, d.x) * step(d.y, line_end) * step(line_start, d.y);\n\tcursor = h + v;\n\n\t// Circle\n\tfloat dist = length(cross_uv);\n\th = smoothstep(brush_radius + line_thickness, brush_radius, dist);\n\tv = 1.0 - smoothstep(brush_radius, brush_radius - line_thickness, dist);\n\tcursor += h * v;\n\n\treturn clamp(cursor, 0.0, 1.0);\n}\n\n// Expects uv (Texture/world space 0 to +/- inf 1m units).\nvec3 get_decal(vec3 albedo, vec2 uv) {\n\tfor (int i = 0; i < 3; ++i) {\n\t\tif (!_editor_decal_visible[i]) {\n\t\t\tcontinue;\n\t\t}\n\t\tfloat size = 1.0 / _editor_decal_size[i];\n\t\tfloat cosa = cos(_editor_decal_rotation[i]);\n\t\tfloat sina = sin(_editor_decal_rotation[i]);\n\t\tvec2 decal_uv = (vec2(cosa * uv.x - sina * uv.y, sina * uv.x + cosa * uv.y) - \n\t\t\tvec2(cosa * _editor_decal_position[i].x - sina * _editor_decal_position[i].y,\n\t\t\tsina * _editor_decal_position[i].x + cosa * _editor_decal_position[i].y) * _vertex_density) * size * _vertex_spacing;\n\n\t\tfloat decal = 0.0;\n        if (i == 0 && abs(decal_uv.x) <= 0.499 && abs(decal_uv.y) <= 0.499 && _editor_decal_part[0]) {\n            decal = texture(_editor_brush_texture, decal_uv + 0.5).r;\n        }\n\t\tif (_editor_decal_part[1]) {\n\t\t\tdecal += get_reticle(uv, i);\n\t\t}\n\t\t// Blend in decal; square for better visual blend\n        albedo = mix(albedo, _editor_decal_color[i].rgb, decal * decal * _editor_decal_color[i].a);\n\t}\n\treturn albedo;\n}\n\n//INSERT: EDITOR_DECAL_RENDER\n\t// Render decal\n\t{\n\t\tALBEDO = get_decal(ALBEDO, uv);\n\t}\n)\""
  },
  {
    "path": "src/shaders/gpu_depth.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// This shader reads the screen and returns absolute depth encoded in Albedo.rg\n// It is not used as an INSERT\n\nR\"(\nshader_type spatial;\nrender_mode unshaded;\n\nuniform highp sampler2D depth_texture : source_color, hint_depth_texture, filter_nearest, repeat_disable;\n\nvoid fragment() {\n\tfloat depth = textureLod(depth_texture, SCREEN_UV, 0.).x;\n\t#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n\tdepth = depth * 2.0 - 1.0;\n\t#endif\n\tvec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);\n\tvec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);\n\tview.xyz /= view.w;\n\tfloat depth_linear = -view.z;\n\n\t// Normalize depth to the range 0 - 1. Divided by camera.get_far()\n\thighp float scaledDepth = clamp(depth_linear / 100000.0, 0.0, 1.0);\n\n\t// Encode using 127 steps, which map to the 128 - 255 range.\n\t// Avoids precision loss for compatability and mobile renderer\n\t// 21bit depth value\n\thighp float r = (floor(scaledDepth * 127.0) + 128.0) / 255.0;\n\thighp float g = (floor(fract(scaledDepth * 127.0) * 127.0) + 128.0) / 255.0;\n\thighp float b = (floor(fract(scaledDepth * 127.0 * 127.0) * 127.0) + 128.0) / 255.0;\n\t\t\n\tALBEDO = vec3(r, g, b); // Return encoded value, required for Mobile & Compatibility renderers\n\n}\n\n)\""
  },
  {
    "path": "src/shaders/macro_variation.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n\n//INSERT: MACRO_VARIATION_UNIFORMS\ngroup_uniforms shader_uniforms.macro_variation;\nuniform vec3 macro_variation1 : source_color = vec3(1.);\nuniform vec3 macro_variation2 : source_color = vec3(1.);\nuniform float macro_variation_slope : hint_range(0., 1.)  = 0.333;\nuniform highp sampler2D noise_texture : source_color, FILTER_METHOD, repeat_enable;\nuniform float noise1_scale : hint_range(0.001, 1.) = 0.04; // Used for macro variation 1. Scaled up 10x\nuniform float noise1_angle : hint_range(0, 6.283) = 0.;\nuniform vec2 noise1_offset = vec2(0.5);\nuniform float noise2_scale : hint_range(0.001, 1.) = 0.076;\t// Used for macro variation 2. Scaled up 10x\ngroup_uniforms;\n\n//INSERT: MACRO_VARIATION\n\t// Macro variation. 2 lookups\n\t{\n\t\tfloat noise1 = texture(noise_texture, rotate_vec2(fma(uv, vec2(noise1_scale * .1), noise1_offset) , vec2(cos(noise1_angle), sin(noise1_angle)))).r;\n\t\tfloat noise2 = texture(noise_texture, uv * noise2_scale * .1).r;\n\t\tvec3 macrov = mix(macro_variation1, vec3(1.), noise1);\n\t\tmacrov *= mix(macro_variation2, vec3(1.), noise2);\n\t\tmat.albedo_height.rgb *= mix(vec3(1.0), macrov, clamp(w_normal.y + macro_variation_slope, 0., 1.));\n\t}\n\n)\"\n"
  },
  {
    "path": "src/shaders/main.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// Raw strings have a limit of 64k, but MSVC has a limit of 2k in a string literal. This file is split into\n// multiple raw strings that are concatenated by the compiler.\n\nR\"(shader_type spatial;\nrender_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx, skip_vertex_transform;\n\n/* The terrain depends on this shader to function. Don't change most things in vertex() or \n * terrain normal calculations in fragment(). You probably only want to customize the \n * material calculation and PBR application in fragment().\n *\n * Uniforms that begin with _ are private and will not display in the inspector. However, \n * you can set them via code. You are welcome to create more of your own hidden uniforms.\n *\n * This system only supports albedo, height, normal, roughness. Most textures don't need the other\n * PBR channels. Height can be used as an approximation for AO. For the rare textures do need\n * additional channels, you can add maps for that one texture. e.g. an emissive map for lava.\n *\n */\n\n// Defined Constants\n#define COLOR_MAP_DEF vec4(1.0, 1.0, 1.0, 0.5)\n#define DIV_255 0.003921568627450 // 1. / 255.\n#define DIV_1024 0.0009765625 // 1. / 1024.\n#define TAU_16TH -0.392699081698724 // -TAU / 16.\n\n// Inline Functions\n#define DECODE_BLEND(control) float(control >>14u & 0xFFu) * DIV_255\n#define DECODE_AUTO(control) bool(control & 0x1u)\n#define DECODE_BASE(control) int(control >>27u & 0x1Fu)\n#define DECODE_OVER(control) int(control >>22u & 0x1Fu)\n#define DECODE_ANGLE(control) float(control >>10u & 0xFu) * TAU_16TH\n// This math recreates the scale value directly rather than using an 8 float const array.\n#define DECODE_SCALE(control) (0.9 - float(((control >>7u & 0x7u) + 3u) % 8u + 1u) * 0.1)\n#define DECODE_HOLE(control) bool(control >>2u & 0x1u)\n\n#if CURRENT_RENDERER == RENDERER_COMPATIBILITY\n    #define fma(a, b, c) ((a) * (b) + (c))\n    #define dFdxCoarse(a) dFdx(a)\n    #define dFdyCoarse(a) dFdy(a)\n#endif\n\n// Private uniforms\ngroup_uniforms shader_uniforms;\nuniform vec3 _target_pos = vec3(0.f);\nuniform float _mesh_size = 48.f;\nuniform float _subdiv = 1.f;\nuniform float _tessellation_level = 0.f;\nuniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2\nuniform uint _mouse_layer = 0x80000000u; // Layer 32\nuniform float _vertex_spacing = 1.0;\nuniform float _vertex_density = 1.0; // = 1./_vertex_spacing\nuniform float _region_size = 1024.0;\nuniform float _region_texel_size = 0.0009765625; // = 1./region_size\nuniform int _region_map_size = 32;\nuniform int _region_map[1024];\nuniform vec2 _region_locations[1024];\nuniform float _texture_normal_depth_array[32];\nuniform float _texture_ao_strength_array[32];\nuniform float _texture_ao_affect_array[32];\nuniform float _texture_roughness_mod_array[32];\nuniform float _texture_uv_scale_array[32];\nuniform vec2 _texture_detile_array[32];\nuniform vec4 _texture_color_array[32];\nuniform highp sampler2DArray _height_maps : repeat_disable;\nuniform highp sampler2DArray _control_maps : repeat_disable;\n//INSERT: TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\n//INSERT: TEXTURE_SAMPLERS_LINEAR\n//INSERT: TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\n//INSERT: TEXTURE_SAMPLERS_NEAREST\nuniform highp sampler2DArray _color_maps : source_color, FILTER_METHOD, repeat_disable;\nuniform highp sampler2DArray _texture_array_albedo : source_color, FILTER_METHOD, repeat_enable;\nuniform highp sampler2DArray _texture_array_normal : hint_normal, FILTER_METHOD, repeat_enable;\ngroup_uniforms;\n\n// Public uniforms\ngroup_uniforms shader_uniforms.general;\n//INSERT: FLAT_UNIFORMS\nuniform bool flat_terrain_normals = false;\nuniform float blend_sharpness : hint_range(0, 1) = 0.5;\n//INSERT: PROJECTION_UNIFORMS\ngroup_uniforms;\n\n//INSERT: AUTO_SHADER_UNIFORMS\n//INSERT: DISPLACEMENT_UNIFORMS\n//INSERT: DUAL_SCALING_UNIFORMS\n//INSERT: MACRO_VARIATION_UNIFORMS\n\ngroup_uniforms shader_uniforms.mipmaps;\nuniform float bias_distance : hint_range(0.0, 16384.0, 0.1) = 512.0;\nuniform float mipmap_bias : hint_range(0.5, 1.5, 0.01) = 1.0;\nuniform float depth_blur : hint_range(0.0, 35.0, 0.1) = 0.0;\ngroup_uniforms;\n\n//INSERT: WORLD_NOISE_UNIFORMS\n\n// Varyings & Types\n\nstruct material {\n\tvec4 albedo_height;\n\tvec4 normal_rough;\n\tfloat normal_map_depth;\n\tfloat ao;\n\tfloat ao_affect;\n\tfloat total_weight;\n};\n\nvarying vec3 v_vertex;\nvarying float v_vertex_xz_dist;\nvarying vec3 v_camera_pos;\n)\"\n\n\t\tR\"(\n////////////////////////\n// Vertex\n////////////////////////\n\n// Takes in world space XZ (UV) coordinates\n// Returns ivec3 with:\n// XY: (0 to _region_size - 1) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nivec3 get_index_coord(const vec2 uv) {\n\tvec2 r_uv = round(uv);\n\tivec2 pos = ivec2(floor(r_uv * _region_texel_size)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[pos.y * _region_map_size + pos.x] * bounds - 1;\n\treturn ivec3(ivec2(mod(r_uv, _region_size)), layer_index);\n}\n\n// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:\n// XY: (0. to 1.) coordinates within a region\n// Z: layer index used for texturearrays, -1 if not in a region\nvec3 get_index_uv(const vec2 uv2) {\n\tivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);\n\tint bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));\n\tint layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;\n\treturn vec3(uv2 - _region_locations[layer_index], float(layer_index));\n}\n\nfloat interpolated_height(vec2 pos) {\n\tconst vec2 offsets = vec2(0, 1);\n\tvec2 index_id = floor(pos);\n\tivec3 index[4];\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\tfloat h0 = texelFetch(_height_maps, index[0], 0).r;\n\tfloat h1 = texelFetch(_height_maps, index[1], 0).r;\n\tfloat h2 = texelFetch(_height_maps, index[2], 0).r;\n\tfloat h3 = texelFetch(_height_maps, index[3], 0).r;\n\tvec2 f = fract(pos);\n\tvec2 i = 1.0 - f;\n\tvec4 w = vec4(i.x * f.y, f.x * f.y, f.x * i.y, i.x * i.y);\n\tfloat h = h0 * w[0] + h1 * w[1] + h2 * w[2] + h3 * w[3];\n\treturn h;\n}\n\n//INSERT: DISPLACEMENT_FUNCTIONS\n//INSERT: NONE_FUNCTIONS\n//INSERT: FLAT_FUNCTIONS\n//INSERT: WORLD_NOISE_FUNCTIONS\n\nvoid vertex() {\n\t// Save Camera Position to varying for access in later functions\n\tv_camera_pos = MAIN_CAM_INV_VIEW_MATRIX[3].xyz;\n\n\t// Get vertex of flat plane in world coordinates and set world UV\n\tv_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;\n\n\t// Distance from target node to vertex on a flat plane\n\tv_vertex_xz_dist = length(v_vertex.xz - _target_pos.xz);\n\n\t// Geomorph vertex across clipmap LODs, set end and start for linear height interpolate\n\tfloat scale = MODEL_MATRIX[0][0];\n\tfloat inv_scale = 1.0 / scale;\n\tfloat max_xz = max(abs(v_vertex.x - _target_pos.x), abs(v_vertex.z - _target_pos.z));\n\tfloat vertex_lerp = smoothstep(0.0, 1.0, (max_xz * inv_scale - _mesh_size - 4.0) / (_mesh_size - 4.0));\n\tvec2 vertex_fract = fract(VERTEX.xz * 0.5) * 2.0;\n\t// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+\n\tvec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not\n\t\t// Shift from regular to symmetric\n\t\tmix(vertex_fract, vec2(vertex_fract.x, -vertex_fract.y),\n\t\t\tround(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *\n\t\t\tround(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :\n\t\t// Symmetric shift\n\t\tvertex_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);\n\tvec2 start_pos = v_vertex.xz * _vertex_density;\n\tvec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;\n\tv_vertex.xz -= shift * scale * vertex_lerp;\n\n\t// UV coordinates in region space. 0-1 covers 1 region, 1-2 is the next region, etc.\n\tUV = v_vertex.xz * _vertex_density;\n\n\t// UV coordinates in region space + texel offset. Values are 0 to 1 within regions\n\tUV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));\n\n\t// Discard vertices for Holes. 1 lookup\n\tivec3 v_region = get_index_coord(start_pos);\n\tuint control = floatBitsToUint(texelFetch(_control_maps, v_region, 0)).r;\n\tbool hole = DECODE_HOLE(control);\n\n\tvec3 displacement = vec3(0.);\n\t// Show holes to all cameras except mouse camera (on exactly 1 layer)\n\tif ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) && (hole\n//INSERT: NONE_CHECK\n\t\t)){\n\t\tv_vertex.x = 0. / 0.;\n\t} else {\n\t\t// Set final vertex height.\n\t\tfloat h;\n\t\t// This branch is static for each of the clipmap segments\n\t\t// Interpolated reads only occur where sub-texel values are required.\n\t\tif (scale < _vertex_spacing) {\n\t\t\th = interpolated_height(UV);\n\t\t} else {\n\t\t\tivec3 coord_a = get_index_coord(start_pos);\n\t\t\tivec3 coord_b = get_index_coord(end_pos);\n\t\t\th = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);\n\t\t}\n\n//INSERT: FLAT_VERTEX\n//INSERT: WORLD_NOISE_VERTEX\n//INSERT: DISPLACEMENT_VERTEX\n\t\tv_vertex.y = h;\n\t}\n\n\t// Convert model space to view space w/ skip_vertex_transform render mode\n\t// Include displacement without modifying v_vertex.\n\tVERTEX = (VIEW_MATRIX * vec4(v_vertex + displacement, 1.0)).xyz;\n\tNORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);\n\tBINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);\n\tTANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);\n}\n)\"\n\n\t\tR\"(\n////////////////////////\n// Fragment\n////////////////////////\n\nfloat random(in vec2 xy) {\n\treturn fract(sin(dot(xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nvec2 rotate_vec2(const vec2 v, const vec2 cs) {\n\treturn vec2(fma(cs.x, v.x,  cs.y * v.y), fma(cs.x, v.y, -cs.y * v.x));\n}\n\n// 2-4 lookups ( 2-6 with dual scaling )\nvoid accumulate_material(vec3 base_ddx, vec3 base_ddy, const mat3 TNB, const float weight, const ivec3 index,\n\t\t\tconst uint control, const vec2 texture_weight, const ivec2 texture_id, const vec3 i_normal,\n\t\t\tfloat h, inout material mat) {\n\n\t// Applying scaling before projection reduces the number of multiplys ops required.\n\tvec3 i_vertex = v_vertex;\n\n\t// Control map scale\n\tfloat control_scale = DECODE_SCALE(control);\n\tbase_ddx *= control_scale;\n\tbase_ddy *= control_scale;\n\ti_vertex *= control_scale;\n\th *= control_scale;\n\n\t// Index position for detiling.\n\tvec2 i_pos = fma(_region_locations[index.z], vec2(_region_size), vec2(index.xy));\n\ti_pos *= _vertex_spacing * control_scale;\n\n\t// Projection\n\tvec2 i_uv = i_vertex.xz;\n\tvec4 i_dd = vec4(base_ddx.xz, base_ddy.xz);\n\tmat2 p_align = mat2(1.);\n//INSERT: PROJECTION\n\n\t// Control map rotation. Must be applied seperatley from detiling to maintain UV continuity.\n\tfloat c_angle = DECODE_ANGLE(control);\n\tvec2 c_cs_angle = vec2(cos(c_angle), sin(c_angle));\n\ti_uv = rotate_vec2(i_uv, c_cs_angle);\n\ti_pos = rotate_vec2(i_pos, c_cs_angle);\n\n\t// Blend adjustment of Higher ID from Lower ID normal map in world space.\n\tfloat world_normal = 1.;\n\t// mat3 multiply, reduced to 2x fma and 1x mult.\n\t#define FAST_WORLD_NORMAL(n) fma(TNB[0], vec3(n.x), fma(TNB[2], vec3(n.z), TNB[1] * vec3(n.y)))\n\t\n\tfloat blend = DECODE_BLEND(control); // only used for branching.\n\tfloat sharpness = fma(60., blend_sharpness, 4.);\n\n//INSERT: DUAL_SCALING\n\n\t// 1st Texture Asset ID\n\tif (blend < 1.0 \n\t//INSERT: DUAL_SCALING_CONDITION_0\n\t\t) {\n\t\tint id = texture_id[0];\n\t\tfloat id_w = texture_weight[0];\n\t\tfloat id_scale = _texture_uv_scale_array[id];\n\t\tvec4 id_dd = i_dd * id_scale;\n\n\t\t// Detiling and Control map rotation\n\t\tvec2 uv_center = floor(fma(i_pos, vec2(id_scale), vec2(0.5)));\n\t\tvec2 id_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[id] * TAU;\n\t\tvec2 id_cs_angle = vec2(cos(id_detile.x), sin(id_detile.x));\n\t\t// Apply UV rotation and shift around pivot.\n\t\tvec2 id_uv = rotate_vec2(fma(i_uv, vec2(id_scale), -uv_center), id_cs_angle) + uv_center + id_detile.y - 0.5;\n\t\t// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also\n\t\t// including control map rotation. avoids extra matrix op, and sin/cos calls.\n\t\tid_cs_angle = vec2(\n\t\t\tfma(id_cs_angle.x, c_cs_angle.x, -id_cs_angle.y * c_cs_angle.y),\n\t\t\tfma(id_cs_angle.y, c_cs_angle.x, id_cs_angle.x * c_cs_angle.y));\n\t\t// Align derivatives for correct anisotropic filtering\n\t\tid_dd.xy = rotate_vec2(id_dd.xy, id_cs_angle);\n\t\tid_dd.zw = rotate_vec2(id_dd.zw, id_cs_angle);\n\n\t\tvec4 alb = textureGrad(_texture_array_albedo, vec3(id_uv, float(id)), id_dd.xy, id_dd.zw);\n\t\tvec4 nrm = textureGrad(_texture_array_normal, vec3(id_uv, float(id)), id_dd.xy, id_dd.zw);\n\t\talb.rgb *= _texture_color_array[id].rgb;\n\t\tnrm.a = clamp(nrm.a + _texture_roughness_mod_array[id], 0., 1.);\n\t\t// Unpack and rotate normal map.\n\t\tnrm.xyz = fma(nrm.xzy, vec3(2.0), vec3(-1.0));\n\t\tfloat ao = length(nrm.xyz) * 2.0 - 1.0;\n\t\tao = mix(ao * ao * _texture_ao_strength_array[id] + 1.0 - _texture_ao_strength_array[id], 1.0, alb.a * alb.a);\n\t\tnrm.xyz = normalize(nrm.xyz);\n\t\tnrm.xz = rotate_vec2(nrm.xz, id_cs_angle) * p_align;\n\n//INSERT: DUAL_SCALING_MIX\n\t\tworld_normal = FAST_WORLD_NORMAL(nrm).y;\n\n\t\tfloat id_weight = exp2(sharpness * log2(weight + id_w + alb.a)) * weight;\n\t\tmat.albedo_height = fma(alb, vec4(id_weight), mat.albedo_height);\n\t\tmat.normal_rough = fma(nrm, vec4(id_weight), mat.normal_rough);\n\t\tmat.normal_map_depth = fma(_texture_normal_depth_array[id], id_weight, mat.normal_map_depth);\n\t\tmat.ao = fma(ao, id_weight, mat.ao);\n\t\tmat.ao_affect = fma(_texture_ao_affect_array[id], id_weight, mat.ao_affect);\n\t\tmat.total_weight += id_weight;\n\t}\n\n\t// 2nd Texture Asset ID\n\tif (blend > 0.0 && texture_id[1] != texture_id[0]\n//INSERT: DUAL_SCALING_CONDITION_1\n\t\t) {\n\t\tint id = texture_id[1];\n\t\tfloat id_w = texture_weight[1];\n\t\tfloat id_scale = _texture_uv_scale_array[id];\n\t\tvec4 id_dd = i_dd * id_scale;\n\n\t\t// Detiling and Control map rotation\n\t\tvec2 uv_center = floor(fma(i_pos, vec2(id_scale), vec2(0.5)));\n\t\tvec2 id_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[id] * TAU;\n\t\tvec2 id_cs_angle = vec2(cos(id_detile.x), sin(id_detile.x));\n\t\t// Apply UV rotation and shift around pivot.\n\t\tvec2 id_uv = rotate_vec2(fma(i_uv, vec2(id_scale), -uv_center), id_cs_angle) + uv_center + id_detile.y - 0.5;\n\t\t// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also\n\t\t// including control map rotation. avoids extra matrix op, and sin/cos calls.\n\t\tid_cs_angle = vec2(\n\t\t\tfma(id_cs_angle.x, c_cs_angle.x, -id_cs_angle.y * c_cs_angle.y),\n\t\t\tfma(id_cs_angle.y, c_cs_angle.x, id_cs_angle.x * c_cs_angle.y));\n\t\t// Align derivatives for correct anisotropic filtering\n\t\tid_dd.xy = rotate_vec2(id_dd.xy, id_cs_angle);\n\t\tid_dd.zw = rotate_vec2(id_dd.zw, id_cs_angle);\n\n\t\tvec4 alb = textureGrad(_texture_array_albedo, vec3(id_uv, float(id)), id_dd.xy, id_dd.zw);\n\t\tvec4 nrm = textureGrad(_texture_array_normal, vec3(id_uv, float(id)), id_dd.xy, id_dd.zw);\n\t\talb.rgb *= _texture_color_array[id].rgb;\n\t\tnrm.a = clamp(nrm.a + _texture_roughness_mod_array[id], 0., 1.);\n\t\t// Unpack and rotate normal map.\n\t\tnrm.xyz = fma(nrm.xzy, vec3(2.0), vec3(-1.0));\n\t\tfloat ao = length(nrm.xyz) * 2.0 - 1.0;\n\t\tao = mix(ao * ao * _texture_ao_strength_array[id] + 1.0 - _texture_ao_strength_array[id], 1.0, alb.a * alb.a);\n\t\tnrm.xyz = normalize(nrm.xyz);\n\t\tnrm.xz = rotate_vec2(nrm.xz, id_cs_angle) * p_align;\n\n//INSERT: DUAL_SCALING_MIX\n\t\tfloat id_weight = exp2(sharpness * log2(weight + id_w + alb.a * clamp(world_normal, 0., 1.))) * weight;\n\t\tmat.albedo_height = fma(alb, vec4(id_weight), mat.albedo_height);\n\t\tmat.normal_rough = fma(nrm, vec4(id_weight), mat.normal_rough);\n\t\tmat.normal_map_depth = fma(_texture_normal_depth_array[id], id_weight, mat.normal_map_depth);\n\t\tmat.ao = fma(ao, id_weight, mat.ao);\n\t\tmat.ao_affect = fma(_texture_ao_affect_array[id], id_weight, mat.ao_affect);\n\t\tmat.total_weight += id_weight;\n\t}\n}\n\nfloat get_height(vec2 index_id, vec2 offset) {\n\tfloat height = texelFetch(_height_maps, get_index_coord(index_id + offset), 0).r;\n//INSERT: FLAT_FRAGMENT\n\treturn height;\n}\n\n)\"\n\n\t\tR\"(\nvoid fragment() {\n\t// Recover UVs\n\tvec2 uv = UV;\n\tvec2 uv2 = UV2;\n\t\n\t// Lookup offsets, ID and blend weight\n\tvec3 region_uv = get_index_uv(uv2);\n\tconst vec3 offsets = vec3(0, 1, 2);\n\tvec2 index_id = floor(uv);\n\tvec2 weight = fract(uv);\n\tvec2 invert = 1.0 - weight;\n\tvec4 weights = vec4(\n\t\tinvert.x * weight.y, // 0\n\t\tweight.x * weight.y, // 1\n\t\tweight.x * invert.y, // 2\n\t\tinvert.x * invert.y  // 3\n\t);\n\n\tivec3 index[4];\n\t// control map lookups\n\tindex[0] = get_index_coord(index_id + offsets.xy);\n\tindex[1] = get_index_coord(index_id + offsets.yy);\n\tindex[2] = get_index_coord(index_id + offsets.yx);\n\tindex[3] = get_index_coord(index_id + offsets.xx);\n\t\n\tvec3 base_ddx = dFdxCoarse(v_vertex);\n\tvec3 base_ddy = dFdyCoarse(v_vertex);\n\t// Calculate the effective mipmap for regionspace, and when less than 0,\n\t// skip all extra lookups required for bilinear blend.\n\tfloat region_mip = log2(max(length(base_ddx.xz), length(base_ddy.xz)) * _vertex_density);\n\tbool bilerp = region_mip < 4.0 && any(greaterThan(ivec4(index[0].z, index[1].z, index[2].z, index[3].z), ivec4(-1)));\n\n\t// Terrain normals\n\tvec3 index_normal[4];\n\tfloat h[4];\n\t// Allows for additional derivatives, eg world background, brush previews etc\n\tfloat u = 0.0;\n\tfloat v = 0.0;\n\n//INSERT: WORLD_NOISE_FRAGMENT\n\n\th[3] = get_height(index_id, offsets.xx); // 0 (0, 0)\n\th[2] = get_height(index_id, offsets.yx); // 1 (1, 0)\n\th[0] = get_height(index_id, offsets.xy); // 2 (0, 1)\n\tindex_normal[3] = normalize(vec3(h[3] - h[2] + u, _vertex_spacing, h[3] - h[0] + v));\n\n\t// Set flat world normal - overwritten if bilerp is true\n\tvec3 w_normal = index_normal[3];\n\n\t// Adjust derivatives for mipmap bias and depth blur effect\n\tfloat bias = mix(mipmap_bias,\n\t\tdepth_blur + 1.,\n\t\tsmoothstep(0.0, 1.0, (v_vertex_xz_dist - bias_distance) * DIV_1024));\n\tbase_ddx *= bias;\n\tbase_ddy *= bias;\n\n\t// Color map\n\tvec4 color_map = region_uv.z > -1.0 ? textureLod(_color_maps, region_uv, region_mip) : COLOR_MAP_DEF;\n\n\t// Branching smooth normals and manually interpolated color map - fixes cross region artifacts\n\tif (bilerp) {\n\t\t// 4 lookups if linear filtering, else 1 lookup.\n\t\tvec4 col_map[4];\n\t\tcol_map[3] = index[3].z > -1 ? texelFetch(_color_maps, index[3], 0) : COLOR_MAP_DEF;\n\t\t#ifdef FILTER_LINEAR\n\t\tcol_map[0] = index[0].z > -1 ? texelFetch(_color_maps, index[0], 0) : COLOR_MAP_DEF;\n\t\tcol_map[1] = index[1].z > -1 ? texelFetch(_color_maps, index[1], 0) : COLOR_MAP_DEF;\n\t\tcol_map[2] = index[2].z > -1 ? texelFetch(_color_maps, index[2], 0) : COLOR_MAP_DEF;\n\n\t\tcolor_map =\n\t\t\tcol_map[0] * weights[0] +\n\t\t\tcol_map[1] * weights[1] +\n\t\t\tcol_map[2] * weights[2] +\n\t\t\tcol_map[3] * weights[3] ;\n\t\t#else\n\t\tcolor_map = col_map[3];\n\t\t#endif\n\n\t\t// 5 lookups\n\t\t// Fetch the additional required height values for smooth normals\n\t\th[1] = get_height(index_id, offsets.yy); // 3 (1, 1)\n\t\tfloat h_4 = get_height(index_id, offsets.yz); // 4 (1, 2)\n\t\tfloat h_5 = get_height(index_id, offsets.zy); // 5 (2, 1)\n\t\tfloat h_6 = get_height(index_id, offsets.zx); // 6 (2, 0)\n\t\tfloat h_7 = get_height(index_id, offsets.xz); // 7 (0, 2)\n\n\t\t// Calculate the normal for the remaining index ids.\n\t\tindex_normal[0] = normalize(vec3(h[0] - h[1] + u, _vertex_spacing, h[0] - h_7 + v));\n\t\tindex_normal[1] = normalize(vec3(h[1] - h_5 + u, _vertex_spacing, h[1] - h_4 + v));\n\t\tindex_normal[2] = normalize(vec3(h[2] - h_6 + u, _vertex_spacing, h[2] - h[1] + v));\n\n\t\t// Set interpolated world normal\n\t\tw_normal =\n\t\t\tindex_normal[0] * weights[0] +\n\t\t\tindex_normal[1] * weights[1] +\n\t\t\tindex_normal[2] * weights[2] +\n\t\t\tindex_normal[3] * weights[3] ;\n\t}\n\n\tvec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));\n\tvec3 w_binormal = normalize(cross(w_normal, w_tangent));\n\tmat3 TNB = mat3(w_tangent, w_normal, w_binormal);\n\n\t// Apply terrain normals\n\tif (flat_terrain_normals) {\n\t\tNORMAL = normalize(cross(dFdyCoarse(VERTEX), dFdxCoarse(VERTEX)));\n\t\tTANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));\n\t\tBINORMAL = normalize(cross(NORMAL, TANGENT));\n\t} else {\n\t\tNORMAL = mat3(VIEW_MATRIX) * w_normal;\n\t\tTANGENT = mat3(VIEW_MATRIX) * w_tangent;\n\t\tBINORMAL = mat3(VIEW_MATRIX) * w_binormal;\n\t}\n\n\t// Get index control data\n\t// 1 - 4 lookups\n\tuvec4 control = uvec4(floatBitsToUint(texelFetch(_control_maps, index[3], 0).r));\n\tif (bilerp) {\n\t\tcontrol = uvec4(\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[0], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[1], 0).r),\n\t\tfloatBitsToUint(texelFetch(_control_maps, index[2], 0).r),\n\t\tcontrol[3]);\n\t}\n\n//INSERT: AUTO_SHADER\n\n\t// Texture weights\n\t// Vectorised Deocode of all texture IDs, then swizzle to per index mapping.\n\t// Passed to accumulate_material to avoid repeated decoding.\n\tivec4 t_id[2] = {ivec4(control >> uvec4(27u) & uvec4(0x1Fu)),\n\t\tivec4(control >> uvec4(22u) & uvec4(0x1Fu))};\n\tivec2 texture_ids[4] = ivec2[4](\n\t\tivec2(t_id[0].x, t_id[1].x),\n\t\tivec2(t_id[0].y, t_id[1].y),\n\t\tivec2(t_id[0].z, t_id[1].z),\n\t\tivec2(t_id[0].w, t_id[1].w));\n\n\t// uninterpolated weights.\n\tvec4 weights_id_1 = vec4(control >> uvec4(14u) & uvec4(0xFFu)) * DIV_255;\n\tvec4 weights_id_0 = 1.0 - weights_id_1;\n\tvec2 t_weights[4] = vec2[4](\n\t\t\t\tvec2(weights_id_0[0], weights_id_1[0]),\n\t\t\t\tvec2(weights_id_0[1], weights_id_1[1]),\n\t\t\t\tvec2(weights_id_0[2], weights_id_1[2]),\n\t\t\t\tvec2(weights_id_0[3], weights_id_1[3]));\n\t// interpolated weights\n\tif (bilerp) {\n\t\tt_weights = {vec2(0), vec2(0), vec2(0), vec2(0)};\n\t\tweights_id_0 *= weights;\n\t\tweights_id_1 *= weights;\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tvec2 w_0 = vec2(weights_id_0[i]);\n\t\t\tvec2 w_1 = vec2(weights_id_1[i]);\n\t\t\tivec2 id_0 = texture_ids[i].xx;\n\t\t\tivec2 id_1 = texture_ids[i].yy;\n\t\t\tt_weights[0] += fma(w_0, vec2(equal(texture_ids[0], id_0)), w_1 * vec2(equal(texture_ids[0], id_1)));\n\t\t\tt_weights[1] += fma(w_0, vec2(equal(texture_ids[1], id_0)), w_1 * vec2(equal(texture_ids[1], id_1)));\n\t\t\tt_weights[2] += fma(w_0, vec2(equal(texture_ids[2], id_0)), w_1 * vec2(equal(texture_ids[2], id_1)));\n\t\t\tt_weights[3] += fma(w_0, vec2(equal(texture_ids[3], id_0)), w_1 * vec2(equal(texture_ids[3], id_1)));\n\t\t}\n\t}\n\n\t// Struct to accumulate all texture data.\n\tmaterial mat = material(vec4(0.0), vec4(0.0), 0., 0., 0., 0.);\n\n\t// 2 - 4 lookups, 2 - 6 if dual scale texture\n\taccumulate_material(base_ddx, base_ddy, TNB, weights[3], index[3], control[3], t_weights[3],\n\t\ttexture_ids[3], index_normal[3], h[3], mat);\n\n\t// 6 - 12 lookups, 6 - 18 if dual scale texture\n\tif (bilerp) {\n\t\taccumulate_material(base_ddx, base_ddy, TNB, weights[2], index[2], control[2], t_weights[2],\n\t\t\ttexture_ids[2], index_normal[2], h[2], mat);\n\t\taccumulate_material(base_ddx, base_ddy, TNB, weights[1], index[1], control[1], t_weights[1],\n\t\t\ttexture_ids[1], index_normal[1], h[1], mat);\n\t\taccumulate_material(base_ddx, base_ddy, TNB, weights[0], index[0], control[0], t_weights[0],\n\t\t\ttexture_ids[0], index_normal[0], h[0], mat);\n\t}\n\n\t// normalize accumulated values back to 0.0 - 1.0 range.\n\tfloat weight_inv = 1.0 / max(mat.total_weight, 1e-8);\n\tmat.albedo_height *= weight_inv;\n\tmat.normal_rough *= weight_inv;\n\tmat.normal_map_depth *= weight_inv;\n\tmat.ao *= weight_inv;\n\tmat.ao_affect *= weight_inv;\n\n\t//INSERT: MACRO_VARIATION\n\t\n\t// Wetness/roughness modifier, converting 0 - 1 range to -1 to 1 range, clamped to Godot roughness values \n\tfloat roughness = clamp(fma(color_map.a - 0.5, 2.0, mat.normal_rough.a), 0., 1.);\n\t\n\t// Apply PBR\n//INSERT: OUTPUT_ALBEDO\n//INSERT: OUTPUT_ALBEDO_GREY\n//INSERT: OUTPUT_ROUGHNESS\n//INSERT: OUTPUT_NORMAL_MAP\n//INSERT: OUTPUT_AMBIENT_OCCLUSION\n\n}\n\n)\"\n"
  },
  {
    "path": "src/shaders/overlays.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// These special inserts are injected into the shader code at the end of fragment().\n// Variables should be prefaced with __ to avoid name conflicts.\n\nR\"(\n//INSERT: OVERLAY_INSTANCER_GRID\n\t// Show instancer grid\n\t{\n\t\tvec3 __grid_color = vec3(.05);\n\t\tfloat __line_thickness = 0.01 * sqrt(-VERTEX.z);\n\t\tvec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz * _vertex_density;\n\t\tvec2 __p = __pixel_pos.xz;\n\t\t// Instancer Grid\n\t\t#define CELL_SIZE 32.0\n\t\tvec2 __g = abs(fract((__p + CELL_SIZE * 0.5) / CELL_SIZE) - 0.5) * CELL_SIZE;\n\t\tfloat __grid_d = min(__g.x, __g.y);\n\t\tfloat __grid_mask = 1.0 - smoothstep(__line_thickness - fwidth(__grid_d), __line_thickness + fwidth(__grid_d), __grid_d);\n\t\t// Clip Grid outside regions\n\t\t__grid_mask *= float(clamp(get_index_coord(__pixel_pos.xz - 0.5).z + 1, 0, 1));\n\t\tALBEDO = mix(ALBEDO, __grid_mask * __grid_color, __grid_mask);\n\t}\n\n//INSERT: OVERLAY_VERTEX_GRID\n\t// Show vertex grids\n\t{\n\t\tvec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;\n\t\tvec3 __camera_pos = INV_VIEW_MATRIX[3].xyz;\n\t\tfloat __grid_line = 0.05;\t\t// Vertex grid line thickness\n\t\tfloat __grid_step = 1.0;\t\t\t// Vertex grid size, 1.0 == integer units\n\t\tfloat __vertex_size = 4.;\t\t// Size of vertices\n\t\tfloat __view_distance = 300.0;\t// Visible distance of grid\n\t\tvec3 __vertex_mul = vec3(0.);\n\t\tvec3 __vertex_add = vec3(0.);\n\t\tfloat __distance_factor = clamp(1.-length(__camera_pos - __pixel_pos)/__view_distance, 0., 1.);\n\t\t// Draw vertex grid\n\t\tif ( mod(__pixel_pos.x * _vertex_density + __grid_line*.5, __grid_step) < __grid_line || \n\t  \t\t mod(__pixel_pos.z * _vertex_density + __grid_line*.5, __grid_step) < __grid_line ) { \n\t\t\t__vertex_mul = vec3(0.5) * __distance_factor;\n\t\t}\n\t\t// Draw Vertices\n\t\tif ( mod(UV.x + __grid_line*__vertex_size*.5, __grid_step) < __grid_line*__vertex_size &&\n\t  \t\t mod(UV.y + __grid_line*__vertex_size*.5, __grid_step) < __grid_line*__vertex_size ) { \n\t\t\t__vertex_add = vec3(0.15) * __distance_factor;\n\t\t}\n\t\tALBEDO = fma(ALBEDO, 1.-__vertex_mul, __vertex_add);\n\t}\n\n//INSERT: OVERLAY_CONTOURS_SETUP\ngroup_uniforms shader_uniforms.contour_lines;\nuniform float contour_interval: hint_range(0.25, 100.0, 0.001) = 1.0;\nuniform float contour_thickness : hint_range(0.0, 10.0, 0.001) = 1.0;\nuniform vec4 contour_color : source_color = vec4(.85, .85, .19, 1.);\ngroup_uniforms;\n\nfloat fractal_contour_lines(float thickness, float interval, vec3 spatial_coords, vec3 normal, vec3 base_ddx, vec3 base_ddy, vec3 __camera_pos) {\n    float depth = max(log(length(spatial_coords - __camera_pos) / interval) * (1.0 / log2(2.0)) - 1.0, 1.0);\n\n    float interval_a = interval * exp2(max(floor(depth) - 1.0, 1.0)) * 0.5;\n    float interval_b = interval * exp2(max(floor(depth), 1.0)) * 0.5;\n    float interval_c = interval * exp2(max(floor(depth + 0.5) - 1.0, 1.0)) * 0.5;\n\n    float y = spatial_coords.y;\n    float y_fwidth = abs(base_ddx.y) + abs(base_ddy.y);\n\n    thickness *= smoothstep(0., 0.0125, clamp(1.0 - normal.y, 0., 1.));\n    float mi = max(0.0, thickness - 1.0);\n    float ma = max(1.0, thickness);\n    float mx = max(0.0, 1.0 - thickness);\n\n    // Line A\n    float inv_interval_a = 1.0 / interval_a;\n    float f_a = abs(fract((y + interval_a * 0.5) * inv_interval_a) - 0.5);\n    float df_a = y_fwidth * inv_interval_a;\n    float line_a = clamp((f_a - df_a * mi) / (df_a * (ma - mi)), mx, 1.0);\n\n    // Line B\n    float inv_interval_b = 1.0 / interval_b;\n    float f_b = abs(fract((y + interval_b * 0.5) * inv_interval_b) - 0.5);\n    float df_b = y_fwidth * inv_interval_b;\n    float line_b = clamp((f_b - df_b * mi) / (df_b * (ma - mi)), mx, 1.0);\n\n    // Line C\n    float inv_interval_c = 1.0 / interval_c;\n    float f_c = abs(fract((y + interval_c * 0.5) * inv_interval_c) - 0.5);\n    float df_c = y_fwidth * inv_interval_c;\n    float line_c = clamp((f_c - df_c * mi) / (df_c * (ma - mi)), mx, 1.0);\n\n    // Blend out\n    float p = fract(depth - 0.5);\n    float line = mix(mix(line_a, line_b, fract(depth)), line_c, (4.0 * p * (1.0 - p)));\n    return line;\n}\n\n//INSERT: OVERLAY_CONTOURS_RENDER\n\t// Show contour lines\n\t{\n\t\tvec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;\n\t\tvec3 __camera_pos = INV_VIEW_MATRIX[3].xyz;\n\t\tvec3 __base_ddx = dFdxCoarse(__pixel_pos);\n\t\tvec3 __base_ddy = dFdyCoarse(__pixel_pos);\n\t\tvec3 __w_normal = normalize(cross(__base_ddy, __base_ddx));\n\t\tfloat __line = fractal_contour_lines(contour_thickness, contour_interval, __pixel_pos, __w_normal, __base_ddx, __base_ddy, __camera_pos);\n\t\tALBEDO = mix(ALBEDO, contour_color.rgb, (1.-__line) * contour_color.a);\n\t}\n)\""
  },
  {
    "path": "src/shaders/pbr_views.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n// These special inserts are injected into the shader code at the end of fragment().\n// Variables should be prefaced with __ to avoid name conflicts.\n\nR\"(\n//INSERT: PBR_TEXTURE_ALBEDO\n\t// Show albedo textures\n\t{\n\t\tALBEDO = mat.albedo_height.rgb;\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: PBR_TEXTURE_HEIGHT\n\t// Show height textures\n\t{\n\t\tALBEDO = vec3(mat.albedo_height.a);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: PBR_TEXTURE_NORMAL\n\t// Show normal map textures\n\t{\n\t\tALBEDO = fma(normalize(mat.normal_rough.xzy), vec3(0.5), vec3(0.5));\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: PBR_TEXTURE_ROUGHNESS\n\t// Show roughness textures\n\t{\n\t\tALBEDO = vec3(mat.normal_rough.a);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: PBR_TEXTURE_AO\n\t// Show normal map decoded AO value\n\t{\n\t\tALBEDO = vec3(mat.ao);\n\t\tROUGHNESS = 0.7;\n\t\tSPECULAR = 0.;\n\t\tNORMAL_MAP = vec3(0.5, 0.5, 1.0);\n\t\tAO = 1.0;\n\t}\n\n//INSERT: OUTPUT_ALBEDO\n\tALBEDO = mat.albedo_height.rgb * color_map.rgb;\n//INSERT: OUTPUT_ALBEDO_GREY\n\tALBEDO = vec3(0.2);\n//INSERT: OUTPUT_ROUGHNESS\n\tROUGHNESS = roughness;\n\tSPECULAR = 1. - mat.normal_rough.a;\n//INSERT: OUTPUT_NORMAL_MAP\n\t// Repack final normal map value.\n\tNORMAL_MAP = fma(normalize(mat.normal_rough.xzy), vec3(0.5), vec3(0.5));\n\tNORMAL_MAP_DEPTH = mat.normal_map_depth;\n//INSERT: OUTPUT_AMBIENT_OCCLUSION\n\tAO = clamp(mat.ao, 0., 1.);\n\tAO_LIGHT_AFFECT = mat.ao_affect;\n\n)\"\n"
  },
  {
    "path": "src/shaders/projection.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n\n//INSERT: PROJECTION\nif (i_normal.y <= 0.7071067811865475) { // sqrt(0.5)\n\t\t// Projected normal map alignment matrix\n\t\tp_align = mat2(vec2(i_normal.z, -i_normal.x), vec2(i_normal.x, i_normal.z));\n\t\t// Fast 45 degree snapping https://iquilezles.org/articles/noatan/\n\t\tvec2 xz = round(normalize(-i_normal.xz) * 1.3065629648763765); // sqrt(1.0 + sqrt(0.5))\n\t\txz *= abs(xz.x) + abs(xz.y) > 1.5 ? 0.7071067811865475 : 1.0; // sqrt(0.5)\n\t\txz = vec2(-xz.y, xz.x);\n\t\ti_pos = floor(vec2(dot(i_pos, xz), -h));\n\t\ti_uv = vec2(dot(i_vertex.xz, xz), -i_vertex.y);\n\t\t#ifndef IS_DISPLACEMENT_BUFFER\n\t\ti_dd.xy = vec2(dot(base_ddx.xz, xz), -base_ddx.y);\n\t\ti_dd.zw = vec2(dot(base_ddy.xz, xz), -base_ddy.y);\n\t\t#endif\n\t}\n\n)\"\n"
  },
  {
    "path": "src/shaders/samplers.glsl",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\nR\"(\n\n//INSERT: TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\n#define FILTER_LINEAR\n#define FILTER_METHOD filter_linear_mipmap_anisotropic\n\n//INSERT: TEXTURE_SAMPLERS_LINEAR\n#define FILTER_LINEAR\n#define FILTER_METHOD filter_linear_mipmap\n\n//INSERT: TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\n#define FILTER_NEAREST\n#define FILTER_METHOD filter_nearest_mipmap_anisotropic\n\n//INSERT: TEXTURE_SAMPLERS_NEAREST\n#define FILTER_NEAREST\n#define FILTER_METHOD filter_nearest_mipmap\n\n)\""
  },
  {
    "path": "src/target_node_3d.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TARGET_NODE3D_CLASS_H\n#define TARGET_NODE3D_CLASS_H\n\n#include <godot_cpp/classes/node3d.hpp>\n\n#include \"constants.h\"\n\nclass TargetNode3D {\n\tCLASS_NAME_STATIC(\"Terrain3DTargetNode3D\");\n\nprivate:\n\tuint64_t _instance_id = 0;\n\npublic:\n\tvoid clear() { _instance_id = 0; }\n\n\tvoid set_target(Node3D *p_node) {\n\t\tif (p_node && !p_node->is_queued_for_deletion()) {\n\t\t\t_instance_id = p_node->get_instance_id();\n\t\t} else {\n\t\t\tclear();\n\t\t}\n\t}\n\n\tNode3D *get_target() const {\n\t\tif (_instance_id == 0) {\n\t\t\treturn nullptr;\n\t\t}\n\t\tObject *obj = ObjectDB::get_instance(_instance_id);\n\t\treturn obj ? Object::cast_to<Node3D>(obj) : nullptr;\n\t}\n\n\tNode3D *ptr() const { return get_target(); }\n\n\tbool is_valid() const {\n\t\tNode3D *node = get_target();\n\t\treturn node && node->is_inside_tree() && !node->is_queued_for_deletion();\n\t}\n\n\tbool is_null() const {\n\t\treturn !is_valid();\n\t}\n};\n\n#endif // TARGET_NODE3D_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/compositor.hpp>\n#include <godot_cpp/classes/directional_light3d.hpp>\n#include <godot_cpp/classes/editor_interface.hpp>\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/environment.hpp>\n#include <godot_cpp/classes/label3d.hpp>\n#include <godot_cpp/classes/os.hpp>\n#include <godot_cpp/classes/physics_direct_space_state3d.hpp>\n#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>\n#include <godot_cpp/classes/quad_mesh.hpp>\n#include <godot_cpp/classes/shader_material.hpp>\n#include <godot_cpp/classes/surface_tool.hpp>\n#include <godot_cpp/classes/viewport_texture.hpp>\n#include <godot_cpp/classes/world3d.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_util.h\"\n\n// Initialize static member variable\nTerrain3D::DebugLevel Terrain3D::debug_level{ ERROR };\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\nvoid Terrain3D::_initialize() {\n\tLOG(INFO, \"Instantiating main subsystems\");\n\t// Make blank objects if needed\n\tif (!_data) {\n\t\tLOG(DEBUG, \"Creating blank data object\");\n\t\t_data = memnew(Terrain3DData);\n\t}\n\tif (_material.is_null()) {\n\t\tLOG(DEBUG, \"Creating blank material\");\n\t\t_material.instantiate();\n\t}\n\tif (_assets.is_null()) {\n\t\tLOG(DEBUG, \"Creating blank texture list\");\n\t\t_assets.instantiate();\n\t}\n\tif (!_collision) {\n\t\tLOG(DEBUG, \"Creating collision manager\");\n\t\t_collision = memnew(Terrain3DCollision);\n\t}\n\tif (!_instancer) {\n\t\tLOG(DEBUG, \"Creating instancer\");\n\t\t_instancer = memnew(Terrain3DInstancer);\n\t}\n\t// Connect signals\n\t// Any region was changed, update region labels\n\tif (!_data->is_connected(\"region_map_changed\", callable_mp(this, &Terrain3D::update_region_labels))) {\n\t\tLOG(DEBUG, \"Connecting _data::region_map_changed signal to set_show_region_locations()\");\n\t\t_data->connect(\"region_map_changed\", callable_mp(this, &Terrain3D::update_region_labels));\n\t}\n\t// Any region was changed, regenerate collision if enabled\n\tif (!_data->is_connected(\"region_map_changed\", callable_mp(_collision, &Terrain3DCollision::build))) {\n\t\tLOG(DEBUG, \"Connecting _data::region_map_changed signal to build()\");\n\t\t_data->connect(\"region_map_changed\", callable_mp(_collision, &Terrain3DCollision::build));\n\t}\n\t// Any map was regenerated or regions changed, update material uniforms without rebuilding shaders\n\tif (!_data->is_connected(\"maps_changed\", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(Terrain3DMaterial::REGION_ARRAYS))) {\n\t\tLOG(DEBUG, \"Connecting _data::maps_changed signal to _material->_update()\");\n\t\t_data->connect(\"maps_changed\", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(Terrain3DMaterial::REGION_ARRAYS));\n\t}\n\t// Height map was regenerated, update aabbs\n\tif (!_data->is_connected(\"height_maps_changed\", callable_mp(this, &Terrain3D::_update_mesher_aabbs))) {\n\t\tLOG(DEBUG, \"Connecting _data::height_maps_changed signal to update_aabbs()\");\n\t\t_data->connect(\"height_maps_changed\", callable_mp(this, &Terrain3D::_update_mesher_aabbs));\n\t}\n\t// Texture assets changed, update material uniforms without rebuilding shaders\n\tif (!_assets->is_connected(\"textures_changed\", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(Terrain3DMaterial::TEXTURE_ARRAYS))) {\n\t\tLOG(DEBUG, \"Connecting _assets.textures_changed to _material->update()\");\n\t\t_assets->connect(\"textures_changed\", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(Terrain3DMaterial::TEXTURE_ARRAYS));\n\t}\n\t// Initialize the system\n\tif (!_initialized && _is_inside_world && is_inside_tree()) {\n\t\tLOG(INFO, \"Initializing main subsystems\");\n\t\t_data->initialize(this);\n\t\t_material->initialize(this);\n\t\t_assets->initialize(this);\n\t\t_collision->initialize(this);\n\t\t_instancer->initialize(this);\n\t\t_setup_terrain_mesher();\n\t\t_setup_ocean_mesher();\n\t\t_update_displacement_buffer();\n\t\t_initialized = true;\n\t\tsnap();\n\t}\n\tupdate_configuration_warnings();\n}\n\n/**\n * This is a proxy for _process(delta) called by _notification() due to\n * https://github.com/godotengine/godot-cpp/issues/1022\n */\nvoid Terrain3D::__physics_process(const double p_delta) {\n\tif (!_initialized)\n\t\treturn;\n\tif (!_camera.is_valid()) {\n\t\tLOG(DEBUG, \"Camera is null, getting the current one\");\n\t\t_grab_camera();\n\t}\n\tif (_tessellation_level > 0) {\n\t\tif (_terrain_mesher && _d_buffer_vp && _material.is_valid()) {\n\t\t\t// If clipmap target has moved enough, re-center buffer on the target.\n\t\t\tVector2 target_pos_2d = v3v2(get_clipmap_target_position());\n\t\t\treal_t tessellation_density = 1.f / pow(2.f, _tessellation_level);\n\t\t\treal_t vertex_spacing = _vertex_spacing * tessellation_density;\n\t\t\tif (!(MAX(std::abs(_last_buffer_position.x - target_pos_2d.x), std::abs(_last_buffer_position.y - target_pos_2d.y)) < vertex_spacing)) {\n\t\t\t\t_last_buffer_position = target_pos_2d;\n\t\t\t\tRS->material_set_param(_material->get_buffer_material_rid(), \"_target_pos\", get_clipmap_target_position());\n\t\t\t\t_d_buffer_vp->set_update_mode(SubViewport::UPDATE_ONCE);\n\t\t\t\t// Only call snap on _mesher if the buffer has snapped, prevents stuttering.\n\t\t\t\t_terrain_mesher->snap();\n\t\t\t}\n\t\t}\n\t} else if (_terrain_mesher) {\n\t\t_terrain_mesher->snap();\n\t}\n\tif (_ocean_enabled && _ocean_mesher) {\n\t\t_ocean_mesher->snap();\n\t\tif (_ocean_material.is_valid() && _ocean_light_target.is_valid()) {\n\t\t\tDirectionalLight3D *light = cast_to<DirectionalLight3D>(_ocean_light_target.ptr());\n\t\t\tShaderMaterial *ocean_shader_mat = Object::cast_to<ShaderMaterial>(_ocean_material.ptr());\n\t\t\tif (light && ocean_shader_mat) {\n\t\t\t\tColor color = COLOR_WHITE;\n\t\t\t\tcolor = light->get_color() * light->get_param(DirectionalLight3D::PARAM_ENERGY);\n\t\t\t\tocean_shader_mat->set_shader_parameter(\"_light_color\", color);\n\t\t\t\tVector3 direction = light->get_global_basis().get_column(2);\n\t\t\t\tocean_shader_mat->set_shader_parameter(\"_light_direction\", direction);\n\t\t\t}\n\t\t}\n\t}\n\tif (_collision && _collision->is_dynamic_mode()) {\n\t\t_collision->update();\n\t}\n}\n\n/**\n * If running in the editor, grab the first editor viewport camera.\n * The edited_scene_root is excluded in case the user already has a Camera3D in their scene.\n */\nvoid Terrain3D::_grab_camera() {\n\tif (IS_EDITOR) {\n\t\t_camera.set_target(EditorInterface::get_singleton()->get_editor_viewport_3d(0)->get_camera_3d());\n\t\tLOG(DEBUG, \"Grabbing the first editor viewport camera: \", _camera.get_target());\n\t} else {\n\t\t_camera.set_target(get_viewport()->get_camera_3d());\n\t\tLOG(DEBUG, \"Grabbing the in-game viewport camera: \", _camera.get_target());\n\t}\n\tif (!_camera.is_valid() && !_clipmap_target.is_valid()) {\n\t\tset_physics_process(false); // No target to follow, disable snapping until one set\n\t\tLOG(ERROR, \"Cannot find clipmap target or active camera. LODs won't be updated. Set manually with set_clipmap_target() or set_camera()\");\n\t}\n}\n\nvoid Terrain3D::_destroy_collision(const bool p_final) {\n\tLOG(INFO, \"Destroying Collision\");\n\tif (_collision) {\n\t\t_collision->destroy();\n\t}\n\tif (p_final) {\n\t\tmemdelete_safely(_collision);\n\t}\n}\n\nvoid Terrain3D::_setup_terrain_mesher() {\n\tif (!_terrain_mesher) {\n\t\tLOG(DEBUG, \"Creating mesher\");\n\t\t_terrain_mesher = new Terrain3DMesher();\n\t}\n\t_terrain_mesher->initialize(this, _mesh_size, _mesh_lods, _tessellation_level, _vertex_spacing, _material->get_material_rid(), _render_layers);\n}\n\nvoid Terrain3D::_destroy_terrain_mesher(const bool p_final) {\n\tLOG(INFO, \"Destroying terrain mesher\");\n\tif (_terrain_mesher) {\n\t\t_terrain_mesher->destroy();\n\t\tif (p_final) {\n\t\t\tdelete _terrain_mesher;\n\t\t\t_terrain_mesher = nullptr;\n\t\t}\n\t}\n}\n\nvoid Terrain3D::_setup_ocean_mesher() {\n\tif (_ocean_enabled) {\n\t\tif (!_ocean_mesher) {\n\t\t\tLOG(DEBUG, \"Creating mesher\");\n\t\t\t_ocean_mesher = new Terrain3DMesher();\n\t\t}\n\t\t_ocean_mesher->initialize(this, _ocean_mesh_size, _ocean_mesh_lods, _ocean_tessellation_level, _ocean_vertex_spacing, _ocean_material.is_valid() ? _ocean_material->get_rid() : RID(), _ocean_render_layers);\n\t\t_ocean_mesher->update_aabbs(_ocean_cull_margin, V2_ZERO);\n\t\tif (_ocean_material.is_valid()) {\n\t\t\tShaderMaterial *ocean_shader_mat = Object::cast_to<ShaderMaterial>(_ocean_material.ptr());\n\t\t\tif (ocean_shader_mat) {\n\t\t\t\tocean_shader_mat->set_shader_parameter(\"_mesh_size\", _ocean_mesh_size);\n\t\t\t\tocean_shader_mat->set_shader_parameter(\"_vertex_spacing\", _ocean_vertex_spacing);\n\t\t\t\tocean_shader_mat->set_shader_parameter(\"_vertex_density\", 1.0f / _ocean_vertex_spacing);\n\t\t\t\tocean_shader_mat->set_shader_parameter(\"_subdiv\", pow(2.f, real_t(_ocean_tessellation_level)));\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terrain3D::_destroy_ocean_mesher(const bool p_final) {\n\tLOG(INFO, \"Destroying ocean mesher\");\n\tif (_ocean_mesher) {\n\t\t_ocean_mesher->destroy();\n\t\tif (p_final) {\n\t\t\tdelete _ocean_mesher;\n\t\t\t_ocean_mesher = nullptr;\n\t\t}\n\t}\n}\n\nvoid Terrain3D::_setup_displacement_buffer() {\n\tif (!is_inside_tree()) {\n\t\tLOG(ERROR, \"Not inside the tree, skipping displacement buffer setup\");\n\t\treturn;\n\t}\n\t_destroy_displacement_buffer();\n\tLOG(INFO, \"Setting up displacement buffer\");\n\t_d_buffer_vp = memnew(SubViewport);\n\t_d_buffer_vp->set_name(\"DBufferViewport\");\n\tadd_child(_d_buffer_vp, true);\n\t_d_buffer_vp->set_size(Vector2i(2, 2));\n\t_d_buffer_vp->set_disable_3d(true);\n\t_d_buffer_vp->set_update_mode(SubViewport::UPDATE_ONCE);\n\t_d_buffer_vp->set_disable_input(true);\n\t_d_buffer_vp->set_default_canvas_item_texture_filter(Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);\n\n\t_d_buffer_rect = memnew(ColorRect);\n\t_d_buffer_rect->set_name(\"DBufferRect\");\n\t_d_buffer_vp->add_child(_d_buffer_rect, true);\n\t_d_buffer_rect->set_anchors_preset(Control::PRESET_FULL_RECT);\n}\n\nvoid Terrain3D::_update_displacement_buffer() {\n\tif (!_d_buffer_vp) {\n\t\treturn;\n\t}\n\tif (_tessellation_level == 0) {\n\t\t_d_buffer_vp->set_size(V2I_ZERO);\n\t\t_d_buffer_rect->set_size(V2I_ZERO);\n\t} else {\n\t\t_d_buffer_vp->set_size(Vector2i(_mesh_size * 4 * _tessellation_level, _mesh_size * 4));\n\t\t_d_buffer_rect->set_size(Vector2i(_mesh_size * 4 * _tessellation_level, _mesh_size * 4));\n\t\tLOG(INFO, \"Updating displacement buffer to Size: \", _d_buffer_vp->get_size());\n\t\tif (_material.is_valid() && _material->get_material_rid().is_valid()) {\n\t\t\tRS->canvas_item_set_material(_d_buffer_rect->get_canvas_item(), _material->get_buffer_material_rid());\n\t\t\tRS->material_set_param(_material->get_material_rid(), \"_displacement_buffer\", _d_buffer_vp->get_texture()->get_rid());\n\t\t}\n\t}\n}\n\nvoid Terrain3D::_build_containers() {\n\t_label_parent = memnew(Node3D);\n\t_label_parent->set_name(\"Labels\");\n\tadd_child(_label_parent, true);\n}\n\nvoid Terrain3D::_destroy_containers() {\n\tmemdelete_safely(_label_parent);\n}\n\nvoid Terrain3D::_destroy_labels() {\n\tArray labels = _label_parent->get_children();\n\tLOG(DEBUG, \"Destroying \", labels.size(), \" region labels\");\n\tfor (const Variant &var : labels) {\n\t\tNode *label = cast_to<Node>(var);\n\t\tmemdelete_safely(label);\n\t}\n}\n\nvoid Terrain3D::_destroy_displacement_buffer() {\n\tLOG(DEBUG, \"Freeing d_buffer_rect\");\n\tmemdelete_safely(_d_buffer_rect);\n\tLOG(DEBUG, \"Freeing d_buffer_vp\");\n\tmemdelete_safely(_d_buffer_vp);\n}\n\nvoid Terrain3D::_setup_mouse_picking() {\n\tif (!is_inside_tree()) {\n\t\tLOG(ERROR, \"Not inside the tree, skipping mouse setup\");\n\t\treturn;\n\t}\n\tLOG(INFO, \"Setting up mouse picker and get_intersection viewport, camera & screen quad\");\n\t_mouse_vp = memnew(SubViewport);\n\t_mouse_vp->set_name(\"MouseViewport\");\n\tadd_child(_mouse_vp, true);\n\t_mouse_vp->set_size(V2I(2));\n\t_mouse_vp->set_scaling_3d_mode(Viewport::SCALING_3D_MODE_BILINEAR);\n\t_mouse_vp->set_update_mode(SubViewport::UPDATE_ONCE);\n\t_mouse_vp->set_disable_input(true);\n\t_mouse_vp->set_canvas_cull_mask(0);\n\t_mouse_vp->set_use_hdr_2d(true);\n\t_mouse_vp->set_anisotropic_filtering_level(Viewport::ANISOTROPY_DISABLED);\n\t_mouse_vp->set_default_canvas_item_texture_filter(Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);\n\t_mouse_vp->set_positional_shadow_atlas_size(0);\n\t_mouse_vp->set_positional_shadow_atlas_quadrant_subdiv(0, Viewport::SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);\n\t_mouse_vp->set_positional_shadow_atlas_quadrant_subdiv(1, Viewport::SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);\n\t_mouse_vp->set_positional_shadow_atlas_quadrant_subdiv(2, Viewport::SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);\n\t_mouse_vp->set_positional_shadow_atlas_quadrant_subdiv(3, Viewport::SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);\n\n\t_mouse_cam = memnew(Camera3D);\n\t_mouse_cam->set_name(\"MouseCamera\");\n\t_mouse_vp->add_child(_mouse_cam, true);\n\tRef<Environment> env;\n\tenv.instantiate();\n\tenv->set_tonemapper(Environment::TONE_MAPPER_LINEAR);\n\t_mouse_cam->set_environment(env);\n\tRef<Compositor> comp;\n\tcomp.instantiate();\n\t_mouse_cam->set_compositor(comp);\n\t_mouse_cam->set_projection(Camera3D::PROJECTION_ORTHOGONAL);\n\t_mouse_cam->set_size(0.1f);\n\t_mouse_cam->set_far(100000.f);\n\n\t_mouse_quad = memnew(MeshInstance3D);\n\t_mouse_quad->set_name(\"MouseQuad\");\n\t_mouse_cam->add_child(_mouse_quad, true);\n\tRef<QuadMesh> quad;\n\tquad.instantiate();\n\tquad->set_size(V2(0.1f));\n\t_mouse_quad->set_mesh(quad);\n\tString shader_code = String(\n#include \"shaders/gpu_depth.glsl\"\n\t);\n\tRef<Shader> shader;\n\tshader.instantiate();\n\tshader->set_code(shader_code);\n\tRef<ShaderMaterial> shader_material;\n\tshader_material.instantiate();\n\tshader_material->set_shader(shader);\n\t_mouse_quad->set_surface_override_material(0, shader_material);\n\t_mouse_quad->set_position(Vector3(0.f, 0.f, -0.5f));\n\n\t// Set terrain, terrain shader, mouse camera, and screen quad to mouse layer\n\tuint32_t force_update_layer = _mouse_layer;\n\t_mouse_layer = 0u;\n\tset_mouse_layer(force_update_layer);\n}\n\nvoid Terrain3D::_destroy_mouse_picking() {\n\tLOG(DEBUG, \"Freeing mouse_quad\");\n\tmemdelete_safely(_mouse_quad);\n\tLOG(DEBUG, \"Freeing mouse_cam\");\n\tmemdelete_safely(_mouse_cam);\n\tLOG(DEBUG, \"Freeing mouse_vp\");\n\tmemdelete_safely(_mouse_vp);\n}\n\nvoid Terrain3D::_destroy_instancer() {\n\tLOG(INFO, \"Destroying Instancer\");\n\tmemdelete_safely(_instancer);\n}\n\nvoid Terrain3D::_generate_triangles(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs, const int32_t p_lod,\n\t\tconst Terrain3DData::HeightFilter p_filter, const bool p_require_nav, const AABB &p_global_aabb) const {\n\tERR_FAIL_COND(_data == nullptr);\n\tint32_t step = 1 << CLAMP(p_lod, 0, 8);\n\n\t// Bake whole mesh, e.g. bake_mesh and painted navigation\n\tif (!p_global_aabb.has_volume()) {\n\t\tint32_t region_size = (int32_t)_region_size;\n\n\t\tTypedArray<Vector2i> region_locations = _data->get_region_locations();\n\t\tfor (const Vector2i &region_loc : region_locations) {\n\t\t\tVector2i region_pos = region_loc * region_size;\n\t\t\tfor (int32_t z = region_pos.y; z < region_pos.y + region_size; z += step) {\n\t\t\t\tfor (int32_t x = region_pos.x; x < region_pos.x + region_size; x += step) {\n\t\t\t\t\t_generate_triangle_pair(p_vertices, p_uvs, p_lod, p_filter, p_require_nav, x, z);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Bake within an AABB, e.g. runtime navigation baker\n\t\tint32_t z_start = (int32_t)Math::ceil(p_global_aabb.position.z / _vertex_spacing);\n\t\tint32_t z_end = (int32_t)Math::floor(p_global_aabb.get_end().z / _vertex_spacing) + 1;\n\t\tint32_t x_start = (int32_t)Math::ceil(p_global_aabb.position.x / _vertex_spacing);\n\t\tint32_t x_end = (int32_t)Math::floor(p_global_aabb.get_end().x / _vertex_spacing) + 1;\n\n\t\tfor (int32_t z = z_start; z < z_end; ++z) {\n\t\t\tfor (int32_t x = x_start; x < x_end; ++x) {\n\t\t\t\treal_t height = _data->get_height(Vector3(x, 0.f, z));\n\t\t\t\tif (height >= p_global_aabb.position.y && height <= p_global_aabb.get_end().y) {\n\t\t\t\t\t_generate_triangle_pair(p_vertices, p_uvs, p_lod, p_filter, p_require_nav, x, z);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Generates two triangles: Top 124, Bottom 143\n//\t\t1  __  2\n//\t\t  |\\ |\n//\t\t  | \\|\n//\t\t3  --  4\n// p_vertices is assumed to exist and the destination for data\n// p_uvs might not exist, so a pointer is fine\n// p_require_nav is false for the runtime baker, which ignores navigation\nvoid Terrain3D::_generate_triangle_pair(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs,\n\t\tconst int32_t p_lod, const Terrain3DData::HeightFilter p_filter, const bool p_require_nav,\n\t\tconst int32_t x, const int32_t z) const {\n\tint32_t step = 1 << CLAMP(p_lod, 0, 8);\n\tVector3 xz = Vector3(x, 0.0f, z) * _vertex_spacing;\n\tVector3 xsz = Vector3(x + step, 0.0f, z) * _vertex_spacing;\n\tVector3 xzs = Vector3(x, 0.0f, z + step) * _vertex_spacing;\n\tVector3 xszs = Vector3(x + step, 0.0f, z + step) * _vertex_spacing;\n\tVector3 v1 = _data->get_mesh_vertex(p_lod, p_filter, xz);\n\tbool nan1 = std::isnan(v1.y);\n\tif (nan1) {\n\t\treturn;\n\t}\n\tVector3 v2 = _data->get_mesh_vertex(p_lod, p_filter, xsz);\n\tVector3 v3 = _data->get_mesh_vertex(p_lod, p_filter, xzs);\n\tVector3 v4 = _data->get_mesh_vertex(p_lod, p_filter, xszs);\n\tbool nan2 = std::isnan(v2.y);\n\tbool nan3 = std::isnan(v3.y);\n\tbool nan4 = std::isnan(v4.y);\n\t// If on the region edge, duplicate the edge pixels\n\t// Check #2 upper right\n\tif (nan2) {\n\t\tv2.y = v1.y;\n\t}\n\t// Check #3 lower left\n\tif (nan3) {\n\t\tv3.y = v1.y;\n\t}\n\t// Check #4 lower right\n\tif (nan4) {\n\t\tif (!nan2) {\n\t\t\tv4.y = v2.y;\n\t\t} else if (!nan3) {\n\t\t\tv4.y = v3.y;\n\t\t} else {\n\t\t\tv4.y = v1.y;\n\t\t}\n\t}\n\tuint32_t ctrl1 = _data->get_control(xz);\n\tuint32_t ctrl2 = _data->get_control(xsz);\n\tuint32_t ctrl3 = _data->get_control(xzs);\n\tuint32_t ctrl4 = _data->get_control(xszs);\n\t// Holes are only where the control map is valid and the bit is set\n\tbool hole1 = ctrl1 != UINT32_MAX && is_hole(ctrl1);\n\tbool hole2 = ctrl2 != UINT32_MAX && is_hole(ctrl2);\n\tbool hole3 = ctrl3 != UINT32_MAX && is_hole(ctrl3);\n\tbool hole4 = ctrl4 != UINT32_MAX && is_hole(ctrl4);\n\t// Navigation is where the control map is valid and the bit is set, or it's the region edge and nav1 is set\n\tbool nav1 = ctrl1 != UINT32_MAX && is_nav(ctrl1);\n\tbool nav2 = ctrl2 != UINT32_MAX && is_nav(ctrl2) || nan2 && nav1;\n\tbool nav3 = ctrl3 != UINT32_MAX && is_nav(ctrl3) || nan3 && nav1;\n\tbool nav4 = ctrl4 != UINT32_MAX && is_nav(ctrl4) || nan4 && nav1;\n\t//Bottom 143 triangle\n\tif (!(hole1 || hole4 || hole3) && (!p_require_nav || (nav1 && nav4 && nav3))) {\n\t\tp_vertices.push_back(v1);\n\t\tp_vertices.push_back(v4);\n\t\tp_vertices.push_back(v3);\n\t\tif (p_uvs) {\n\t\t\tp_uvs->push_back(Vector2(v1.x, v1.z));\n\t\t\tp_uvs->push_back(Vector2(v4.x, v4.z));\n\t\t\tp_uvs->push_back(Vector2(v3.x, v3.z));\n\t\t}\n\t}\n\t// Top 124 triangle\n\tif (!(hole1 || hole2 || hole4) && (!p_require_nav || (nav1 && nav2 && nav4))) {\n\t\tp_vertices.push_back(v1);\n\t\tp_vertices.push_back(v2);\n\t\tp_vertices.push_back(v4);\n\t\tif (p_uvs) {\n\t\t\tp_uvs->push_back(Vector2(v1.x, v1.z));\n\t\t\tp_uvs->push_back(Vector2(v2.x, v2.z));\n\t\t\tp_uvs->push_back(Vector2(v4.x, v4.z));\n\t\t}\n\t}\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nTerrain3D::Terrain3D() {\n\t// Process the command line\n\tPackedStringArray args = OS::get_singleton()->get_cmdline_args();\n\tfor (int i = args.size() - 1; i >= 0; i--) {\n\t\tString arg = args[i];\n\t\tif (arg.begins_with(\"--terrain3d-debug=\")) {\n\t\t\tString value = arg.rsplit(\"=\")[1];\n\t\t\tif (value == \"ERROR\") {\n\t\t\t\tset_debug_level(ERROR);\n\t\t\t} else if (value == \"INFO\") {\n\t\t\t\tset_debug_level(INFO);\n\t\t\t} else if (value == \"DEBUG\") {\n\t\t\t\tset_debug_level(DEBUG);\n\t\t\t} else if (value == \"EXTREME\") {\n\t\t\t\tset_debug_level(EXTREME);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terrain3D::set_debug_level(const DebugLevel p_level) {\n\tSET_IF_DIFF(debug_level, CLAMP(p_level, ERROR, EXTREME));\n\tLOG(INFO, \"Setting debug level: \", debug_level);\n}\n\nvoid Terrain3D::set_data_directory(String p_dir) {\n\tString old_dir = _data_directory;\n\tSET_IF_DIFF(_data_directory, p_dir);\n\tLOG(INFO, \"Setting data directory to \", _data_directory);\n\t// If _data_directory was empty and now specified, and has no data\n\t// assume we want to retain the current data.\n\t// Otherwise, clear data and reload dir\n\tif (!old_dir.is_empty() || Util::get_files(p_dir, \"terrain3d*.res\").size() > 0) {\n\t\t_initialized = false;\n\t\t_destroy_labels();\n\t\t_destroy_collision();\n\t\t_destroy_instancer();\n\t\tmemdelete_safely(_data);\n\t\t_initialize();\n\t}\n\tupdate_configuration_warnings();\n}\n\nvoid Terrain3D::set_assets(const Ref<Terrain3DAssets> &p_assets) {\n\tSET_IF_DIFF(_assets, p_assets);\n\tLOG(INFO, \"Setting asset list\");\n\t_initialized = false;\n\t_initialize();\n\tLOG(DEBUG, \"Emitting assets_changed\");\n\temit_signal(\"assets_changed\");\n}\n\nvoid Terrain3D::set_editor(Terrain3DEditor *p_editor) {\n\tif (p_editor && p_editor->is_queued_for_deletion()) {\n\t\tLOG(ERROR, \"Attempted to set a node queued for deletion\");\n\t\treturn;\n\t}\n\tSET_IF_DIFF(_editor, p_editor);\n\tLOG(INFO, \"Setting Terrain3DEditor: \", _editor);\n\tif (_material.is_valid()) {\n\t\t_material->update(Terrain3DMaterial::FULL_REBUILD);\n\t}\n}\n\nvoid Terrain3D::set_plugin(Object *p_plugin) {\n\tif (p_plugin && p_plugin->is_queued_for_deletion()) {\n\t\tLOG(ERROR, \"Attempted to set a node queued for deletion\");\n\t\treturn;\n\t}\n\tSET_IF_DIFF(_editor_plugin, p_plugin);\n\tLOG(INFO, \"Setting Editor Plugin: \", _editor_plugin);\n}\n\nvoid Terrain3D::set_region_size(const RegionSize p_size) {\n\tif (!is_valid_region_size(p_size)) {\n\t\tLOG(ERROR, \"Invalid region size: \", p_size, \". Must be power of 2, 64-2048\");\n\t\treturn;\n\t}\n\tSET_IF_DIFF(_region_size, p_size);\n\tLOG(INFO, \"Setting region size: \", _region_size);\n\tif (_data) {\n\t\t_data->_region_size = _region_size;\n\t\t_data->_region_sizev = V2I(_region_size);\n\t}\n\tif (_material.is_valid()) {\n\t\t_material->update();\n\t}\n\t_update_displacement_buffer();\n}\n\nvoid Terrain3D::set_save_16_bit(const bool p_enabled) {\n\tSET_IF_DIFF(_save_16_bit, p_enabled);\n\tLOG(INFO, \"Save heightmaps as 16-bit: \", _save_16_bit);\n\tTypedArray<Terrain3DRegion> regions = _data->get_regions_active();\n\tfor (Ref<Terrain3DRegion> region : regions) {\n\t\tregion->set_modified(true);\n\t}\n}\n\nvoid Terrain3D::set_label_distance(const real_t p_distance) {\n\tSET_IF_DIFF(_label_distance, CLAMP(p_distance, 0.f, 100000.f));\n\tLOG(INFO, \"Setting region label distance: \", _label_distance);\n\tupdate_region_labels();\n}\n\nvoid Terrain3D::set_label_size(const int p_size) {\n\tSET_IF_DIFF(_label_size, CLAMP(p_size, 24, 128));\n\tLOG(INFO, \"Setting region label size: \", _label_size);\n\tupdate_region_labels();\n}\n\nvoid Terrain3D::update_region_labels() {\n\t_destroy_labels();\n\tif (_label_distance > 0.f && _data) {\n\t\tTypedArray<Vector2i> region_locations = _data->get_region_locations();\n\t\tLOG(DEBUG, \"Creating \", region_locations.size(), \" region labels\");\n\t\tfor (const Vector2i &region_loc : region_locations) {\n\t\t\tLabel3D *label = memnew(Label3D);\n\t\t\tString text = region_loc;\n\t\t\tlabel->set_name(\"Label3D\" + text.replace(\" \", \"\"));\n\t\t\tlabel->set_pixel_size(.001f);\n\t\t\tlabel->set_billboard_mode(BaseMaterial3D::BILLBOARD_ENABLED);\n\t\t\tlabel->set_draw_flag(Label3D::FLAG_DOUBLE_SIDED, true);\n\t\t\tlabel->set_draw_flag(Label3D::FLAG_DISABLE_DEPTH_TEST, true);\n\t\t\tlabel->set_draw_flag(Label3D::FLAG_FIXED_SIZE, true);\n\t\t\tlabel->set_render_priority(127);\n\t\t\tlabel->set_outline_render_priority(126);\n\t\t\tlabel->set_text(text);\n\t\t\tlabel->set_modulate(Color(1.f, 1.f, 1.f, .5f));\n\t\t\tlabel->set_outline_modulate(Color(0.f, 0.f, 0.f, .5f));\n\t\t\tlabel->set_font_size(_label_size);\n\t\t\tlabel->set_outline_size(_label_size / 6);\n\t\t\tlabel->set_visibility_range_end(_label_distance);\n\t\t\tlabel->set_visibility_range_end_margin(_label_distance / 10.f);\n\t\t\tlabel->set_visibility_range_fade_mode(GeometryInstance3D::VISIBILITY_RANGE_FADE_SELF);\n\t\t\t_label_parent->add_child(label, true);\n\t\t\tVector3 pos = Vector3(real_t(region_loc.x) + .5f, 0.f, real_t(region_loc.y) + .5f) * _region_size * _vertex_spacing;\n\t\t\treal_t height = _data->get_height(pos);\n\t\t\tpos.y = (std::isnan(height)) ? 0.f : height;\n\t\t\tlabel->set_position(pos);\n\t\t}\n\t}\n}\n\nvoid Terrain3D::set_camera(Camera3D *p_camera) {\n\tif (_camera.ptr() != p_camera) {\n\t\tLOG(EXTREME, \"Setting camera: \", p_camera);\n\t\t_camera.set_target(p_camera);\n\t\tif (_clipmap_target.is_valid()) {\n\t\t\tset_physics_process(true);\n\t\t}\n\t}\n}\n\nvoid Terrain3D::set_clipmap_target(Node3D *p_node) {\n\tif (_clipmap_target.ptr() != p_node) {\n\t\tLOG(INFO, \"Setting clipmap target: \", p_node);\n\t\t_clipmap_target.set_target(p_node);\n\t\tif (_clipmap_target.is_valid()) {\n\t\t\tset_physics_process(true);\n\t\t}\n\t}\n}\n\nVector3 Terrain3D::get_clipmap_target_position() const {\n\t// In Editor, or no clipmap target, use camera\n\tif (IS_EDITOR || !_clipmap_target.get_target()) {\n\t\tif (Node3D *cam = _camera.get_target()) {\n\t\t\treturn cam->get_global_position();\n\t\t}\n\t}\n\tif (Node3D *target = _clipmap_target.get_target()) {\n\t\treturn target->get_global_position();\n\t}\n\treturn V3_ZERO;\n}\n\nvoid Terrain3D::set_collision_target(Node3D *p_node) {\n\tif (_collision_target.ptr() != p_node) {\n\t\tLOG(INFO, \"Setting collision target: \", p_node);\n\t\t_collision_target.set_target(p_node);\n\t\tif (_collision_target.is_valid()) {\n\t\t\tset_physics_process(true);\n\t\t}\n\t}\n}\n\nVector3 Terrain3D::get_collision_target_position() const {\n\t// In Editor, always prefer camera\n\tif (IS_EDITOR) {\n\t\tif (Node3D *cam = _camera.get_target()) {\n\t\t\treturn cam->get_global_position();\n\t\t}\n\t}\n\tif (Node3D *target = _collision_target.get_target()) {\n\t\treturn target->get_global_position();\n\t}\n\tif (Node3D *target = _clipmap_target.get_target()) {\n\t\treturn target->get_global_position();\n\t}\n\tif (Node3D *cam = _camera.get_target()) {\n\t\treturn cam->get_global_position();\n\t}\n\treturn V3_ZERO;\n}\n\nvoid Terrain3D::set_ocean_light_target(Node3D *p_node) {\n\tif (_ocean_light_target.ptr() != p_node) {\n\t\tLOG(INFO, \"Setting directional light target: \", p_node);\n\t\t_ocean_light_target.set_target(p_node);\n\t\tif (_ocean_light_target.is_valid()) {\n\t\t\tset_physics_process(true);\n\t\t}\n\t}\n}\n\nvoid Terrain3D::snap() {\n\tif (_terrain_mesher) {\n\t\t_terrain_mesher->reset_target_position();\n\t}\n\tif (_ocean_enabled && _ocean_mesher) {\n\t\t_ocean_mesher->reset_target_position();\n\t}\n\tif (_collision) {\n\t\t_collision->reset_target_position();\n\t}\n\tif (_tessellation_level > 0) {\n\t\t_last_buffer_position = V2_MAX;\n\t}\n}\n\nvoid Terrain3D::set_material(const Ref<Terrain3DMaterial> &p_material) {\n\tSET_IF_DIFF(_material, p_material);\n\tLOG(INFO, \"Setting material\");\n\t_initialized = false;\n\t_initialize();\n\tLOG(DEBUG, \"Emitting material_changed\");\n\temit_signal(\"material_changed\");\n}\n\nvoid Terrain3D::set_mesh_lods(const int p_count) {\n\tSET_IF_DIFF(_mesh_lods, CLAMP(p_count, 1, 10));\n\tLOG(INFO, \"Setting mesh levels: \", _mesh_lods);\n\tif (_terrain_mesher && _material.is_valid()) {\n\t\t_material->update();\n\t\t_setup_terrain_mesher();\n\t}\n}\n\nvoid Terrain3D::set_tessellation_level(const int p_level) {\n\tSET_IF_DIFF(_tessellation_level, CLAMP(p_level, 0, 6));\n\tLOG(INFO, \"Setting tessellation level: \", p_level);\n\tif (_terrain_mesher && _material.is_valid()) {\n\t\t_material->update(Terrain3DMaterial::FULL_REBUILD);\n\t\t_setup_terrain_mesher();\n\t\t_update_displacement_buffer();\n\t}\n\tnotify_property_list_changed();\n}\n\nvoid Terrain3D::set_mesh_size(const int p_size) {\n\tSET_IF_DIFF(_mesh_size, CLAMP(p_size & ~1, 8, 256)); // Ensure even\n\tLOG(INFO, \"Setting mesh size: \", _mesh_size);\n\tif (_terrain_mesher && _material.is_valid()) {\n\t\t_material->update();\n\t\t_setup_terrain_mesher();\n\t\t_update_displacement_buffer();\n\t}\n}\n\nvoid Terrain3D::set_vertex_spacing(const real_t p_spacing) {\n\tSET_IF_DIFF(_vertex_spacing, CLAMP(p_spacing, 0.25f, 100.0f));\n\tLOG(INFO, \"Setting vertex spacing: \", _vertex_spacing);\n\tif (_collision && _data && _instancer && _material.is_valid()) {\n\t\t_instancer->_update_vertex_spacing(_vertex_spacing);\n\t\t_data->_vertex_spacing = _vertex_spacing;\n\t\tupdate_region_labels();\n\t\t_material->update();\n\t\t_setup_terrain_mesher();\n\t\t_collision->destroy();\n\t\t_collision->build();\n\t\t_update_displacement_buffer();\n\t}\n}\n\nvoid Terrain3D::set_cull_margin(const real_t p_margin) {\n\tSET_IF_DIFF(_cull_margin, CLAMP(p_margin, 0.f, 100000.f));\n\tLOG(INFO, \"Setting extra cull margin: \", _cull_margin);\n\tif (_terrain_mesher) {\n\t\t_terrain_mesher->update_aabbs();\n\t}\n}\n\nvoid Terrain3D::set_cast_shadows(const RenderingServer::ShadowCastingSetting p_cast_shadows) {\n\tSET_IF_DIFF(_cast_shadows, p_cast_shadows);\n\tif (_terrain_mesher) {\n\t\t_terrain_mesher->update();\n\t}\n}\n\nvoid Terrain3D::set_gi_mode(const GeometryInstance3D::GIMode p_gi_mode) {\n\tSET_IF_DIFF(_gi_mode, p_gi_mode);\n\tif (_terrain_mesher) {\n\t\t_terrain_mesher->update();\n\t}\n}\n\nvoid Terrain3D::set_render_layers(const uint32_t p_layers) {\n\tSET_IF_DIFF(_render_layers, p_layers);\n\tLOG(INFO, \"Setting terrain render layers to: \", p_layers);\n\tif (_terrain_mesher) {\n\t\t_terrain_mesher->update();\n\t}\n}\n\nvoid Terrain3D::set_ocean_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_ocean_enabled, p_enabled);\n\tLOG(INFO, \"Setting ocean enabled: \", _ocean_enabled);\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t} else {\n\t\t_destroy_ocean_mesher(false);\n\t}\n}\n\nvoid Terrain3D::set_ocean_mesh_lods(const int p_count) {\n\tSET_IF_DIFF(_ocean_mesh_lods, CLAMP(p_count, 1, 10));\n\tLOG(INFO, \"Setting ocean mesh levels: \", _ocean_mesh_lods);\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t}\n}\n\nvoid Terrain3D::set_ocean_tessellation_level(const int p_level) {\n\tSET_IF_DIFF(_ocean_tessellation_level, CLAMP(p_level, 0, 6));\n\tLOG(INFO, \"Setting ocean tessellation level: \", p_level);\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t}\n}\n\nvoid Terrain3D::set_ocean_mesh_size(const int p_size) {\n\tSET_IF_DIFF(_ocean_mesh_size, CLAMP(p_size & ~1, 8, 256)); // Ensure even\n\tLOG(INFO, \"Setting ocean mesh size: \", _ocean_mesh_size);\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t}\n}\n\nvoid Terrain3D::set_ocean_vertex_spacing(const real_t p_spacing) {\n\tSET_IF_DIFF(_ocean_vertex_spacing, CLAMP(p_spacing, 0.25f, 100.0f));\n\tLOG(INFO, \"Setting ocean vertex spacing: \", _ocean_vertex_spacing);\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t}\n}\n\nvoid Terrain3D::set_ocean_cull_margin(const real_t p_margin) {\n\tSET_IF_DIFF(_ocean_cull_margin, CLAMP(p_margin, 0.f, 100000.f));\n\tLOG(INFO, \"Setting extra cull margin: \", _ocean_cull_margin);\n\tif (_ocean_mesher) {\n\t\t_ocean_mesher->update_aabbs(_ocean_cull_margin, V2_ZERO);\n\t}\n}\n\nvoid Terrain3D::set_ocean_cast_shadows(const RenderingServer::ShadowCastingSetting p_cast_shadows) {\n\tSET_IF_DIFF(_ocean_cast_shadows, p_cast_shadows);\n\tif (_ocean_mesher) {\n\t\t_ocean_mesher->update();\n\t}\n}\n\nvoid Terrain3D::set_ocean_gi_mode(const GeometryInstance3D::GIMode p_gi_mode) {\n\tSET_IF_DIFF(_ocean_gi_mode, p_gi_mode);\n\tif (_ocean_mesher) {\n\t\t_ocean_mesher->update();\n\t}\n}\n\nvoid Terrain3D::set_ocean_render_layers(const uint32_t p_layers) {\n\tSET_IF_DIFF(_ocean_render_layers, p_layers);\n\tLOG(INFO, \"Setting ocean render layers to: \", p_layers);\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t}\n}\n\nvoid Terrain3D::set_ocean_material(const Ref<Material> &p_material) {\n\tSET_IF_DIFF(_ocean_material, p_material);\n\tLOG(INFO, \"Setting ocean material\");\n\tif (_ocean_enabled) {\n\t\t_setup_ocean_mesher();\n\t}\n}\n\nvoid Terrain3D::set_mouse_layer(const uint32_t p_layer) {\n\tSET_IF_DIFF(_mouse_layer, CLAMP(p_layer, 21, 32));\n\tuint32_t mouse_mask = 1 << (_mouse_layer - 1);\n\tLOG(INFO, \"Setting mouse layer: \", _mouse_layer, \" (\", mouse_mask,\n\t\t\t\") on terrain mesh, material, mouse camera, mouse quad\");\n\n\t// Set terrain meshes to mouse layer\n\t// Mask off editor render layers by ORing user layers 1-20 and current mouse layer\n\tset_render_layers((_render_layers & 0xFFFFF) | mouse_mask);\n\t// Set terrain shader to exclude mouse camera from showing holes\n\tif (_material.is_valid()) {\n\t\t_material->set_shader_param(\"_mouse_layer\", mouse_mask);\n\t}\n\t// Set mouse camera to see only mouse layer\n\tif (_mouse_cam) {\n\t\t_mouse_cam->set_cull_mask(mouse_mask);\n\t}\n\t// Set screenquad to mouse layer\n\tif (_mouse_quad) {\n\t\t_mouse_quad->set_layer_mask(mouse_mask);\n\t}\n}\n\n/* Returns the point a ray intersects the ground using either raymarching or the GPU depth texture\n *\tp_src_pos (camera position)\n *\tp_direction (camera direction looking at the terrain)\n *  p_gpu_mode - false: use raymarching, true: use GPU mode\n * Returns Vec3(NAN) on error or vec3(3.402823466e+38F) on no intersection. Test w/ if (var.x < 3.4e38)\n */\nVector3 Terrain3D::get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction, const bool p_gpu_mode) {\n\tif (p_direction.is_zero_approx() || !p_direction.is_finite()) {\n\t\tLOG(ERROR, \"Invalid direction vector: \", p_direction);\n\t\treturn V3_NAN;\n\t}\n\tif (!p_src_pos.is_finite()) {\n\t\tLOG(ERROR, \"Invalid source vector: \", p_src_pos);\n\t\treturn V3_NAN;\n\t}\n\n\tVector3 direction = p_direction.normalized();\n\t// If looking straight down in a region, use get_height\n\tif (direction.y < -.99999f) {\n\t\treal_t height = _data->get_height(p_src_pos);\n\t\tif (std ::isfinite(height)) {\n\t\t\treturn Vector3(p_src_pos.x, height, p_src_pos.z);\n\t\t}\n\t}\n\n\t// Raymarching mode\n\tif (!p_gpu_mode) {\n\t\t// Must start above terrain if in a region\n\t\treal_t height = _data->get_height(p_src_pos);\n\t\tif (height > p_src_pos.y) { // False if Nan\n\t\t\treturn V3_MAX;\n\t\t}\n\t\t// Raymarch down the ray in small increments until we find the terrain height\n\t\tVector3 point = p_src_pos;\n\t\tfor (int i = 0; i < 4000; i++) {\n\t\t\theight = _data->get_height(point);\n\t\t\tif (point.y - height <= 0.f) { // Nan comparison is false, which continues loop\n\t\t\t\treturn point;\n\t\t\t}\n\t\t\tpoint += direction;\n\t\t}\n\t\treturn V3_MAX;\n\n\t} else {\n\t\t// Get depth from perspective camera snapshot\n\t\tif (!_mouse_cam) {\n\t\t\tLOG(ERROR, \"Invalid mouse camera\");\n\t\t\treturn V3_NAN;\n\t\t}\n\t\t// Position mouse cam one unit behind the requested position\n\t\t_mouse_cam->set_global_position(p_src_pos - direction);\n\n\t\t// If looking straight down, then we're not in a region, set rotation directly as look_at() doesn't work\n\t\tif (direction.y < -.99999f) {\n\t\t\t_mouse_cam->set_rotation_degrees(Vector3(-90.f, 0.f, 0.f));\n\t\t} else {\n\t\t\t_mouse_cam->look_at(_mouse_cam->get_global_position() + direction, V3_UP);\n\t\t}\n\n\t\t_mouse_vp->set_update_mode(SubViewport::UPDATE_ONCE);\n\t\tRef<ViewportTexture> vp_tex = _mouse_vp->get_texture();\n\t\tRef<Image> vp_img = vp_tex->get_image();\n\n\t\t// Read the depth pixel from the camera viewport\n\t\tColor screen_depth = vp_img->get_pixel(0, 0);\n\n\t\t// Get position from depth packed in RGB - unpack back to float.\n\t\t// Forward+ is 16bit, mobile and compatibility is 10bit.\n\t\t// Compatibility also has precision loss for values below 0.5, so\n\t\t// we use only the top half of the range, for 21bit depth encoded.\n\t\treal_t r = floor((screen_depth.r * 256.f) - 128.f);\n\t\treal_t g = floor((screen_depth.g * 256.f) - 128.f);\n\t\treal_t b = floor((screen_depth.b * 256.f) - 128.f);\n\n\t\t// Decode the full depth value\n\t\treal_t decoded_depth = (r + g / 127.f + b / (127.f * 127.f)) / 127.f;\n\n\t\t// Near-plane noise filter, or no hit (sky, underside, far clip)\n\t\tif (decoded_depth < 0.00001f || decoded_depth > 1.f) {\n\t\t\t// Catch editor ortho camera with src_pos.y at some random value around 500k\n\t\t\tif (direction.y < -.99999f && p_src_pos.y >= 100000.f) {\n\t\t\t\treturn Vector3(p_src_pos.x, 0.f, p_src_pos.z);\n\t\t\t}\n\t\t\treturn V3_MAX;\n\t\t}\n\n\t\t// Necessary for a near-far precision on hits\n\t\tif (decoded_depth > 0.99999f) {\n\t\t\tdecoded_depth = 1.f;\n\t\t}\n\n\t\t// Denormalize distance to get real depth and terrain position.\n\t\tdecoded_depth *= _mouse_cam->get_far();\n\n\t\t// Project the camera position by the depth value to get the intersection point.\n\t\treturn _mouse_cam->get_global_position() + direction * decoded_depth;\n\t}\n}\n\n/* Returns the results of a physics raycast, optionally excluding the terrain\n *\tp_src_pos (ray start position)\n *\tp_direction (ray direction * magnitude), relative to src_pos\n */\nDictionary Terrain3D::get_raycast_result(const Vector3 &p_src_pos, const Vector3 &p_direction, const uint32_t p_col_mask, const bool p_exclude_self) const {\n\tif (!_is_inside_world) {\n\t\treturn Dictionary();\n\t}\n\tPhysicsDirectSpaceState3D *space_state = get_world_3d()->get_direct_space_state();\n\tif (!space_state) {\n\t\tLOG(ERROR, \"Invalid PhysicsDirectSpaceState3D\");\n\t\treturn Dictionary();\n\t}\n\tRef<PhysicsRayQueryParameters3D> query = PhysicsRayQueryParameters3D::create(p_src_pos, p_src_pos + p_direction, p_col_mask);\n\tif (_collision && p_exclude_self) {\n\t\tquery->set_exclude(TypedArray<RID>(_collision->get_rid()));\n\t}\n\treturn space_state->intersect_ray(query);\n}\n\n/**\n * Generates a static ArrayMesh for the terrain.\n * p_lod (0-8): Determines the granularity of the generated mesh.\n * p_filter: Controls how vertices' Y coordinates are generated from the height map.\n *  HEIGHT_FILTER_NEAREST: Samples the height map in a 'nearest neighbour' fashion.\n *  HEIGHT_FILTER_MINIMUM: Samples a range of heights around each vertex and returns the lowest.\n *   This takes longer than ..._NEAREST, but can be used to create occluders, since it can guarantee the\n *   generated mesh will not extend above or outside the clipmap at any LOD.\n */\nRef<Mesh> Terrain3D::bake_mesh(const int p_lod, const Terrain3DData::HeightFilter p_filter) const {\n\tLOG(INFO, \"Baking mesh at lod: \", p_lod, \" with filter: \", p_filter);\n\tRef<Mesh> result;\n\tERR_FAIL_COND_V(_data == nullptr, result);\n\n\tRef<SurfaceTool> st;\n\tst.instantiate();\n\tst->begin(Mesh::PRIMITIVE_TRIANGLES);\n\n\tPackedVector3Array vertices;\n\tPackedVector2Array uvs;\n\t_generate_triangles(vertices, &uvs, p_lod, p_filter, false, AABB());\n\n\tERR_FAIL_COND_V(vertices.size() != uvs.size(), result);\n\tfor (int i = 0; i < vertices.size(); ++i) {\n\t\tst->set_uv(uvs[i]);\n\t\tst->add_vertex(vertices[i]);\n\t}\n\n\tst->index();\n\tst->generate_normals();\n\tst->generate_tangents();\n\tst->optimize_indices_for_cache();\n\tresult = st->commit();\n\treturn result;\n}\n\n/**\n * Generates source geometry faces for input to nav mesh baking. Geometry is only generated where there\n * are no holes and the terrain has been painted as navigable.\n * p_global_aabb: If non-empty, geometry will be generated only within this AABB. If empty, geometry\n *  will be generated for the entire terrain.\n * p_require_nav: If true, this function will only generate geometry for terrain marked navigable.\n *  Otherwise, geometry is generated for the entire terrain within the AABB (which can be useful for\n *  dynamic and/or runtime nav mesh baking).\n */\nPackedVector3Array Terrain3D::generate_nav_mesh_source_geometry(const AABB &p_global_aabb, const bool p_require_nav) const {\n\tLOG(INFO, \"Generating NavMesh source geometry from terrain\");\n\tPackedVector3Array faces;\n\t_generate_triangles(faces, nullptr, 0, Terrain3DData::HEIGHT_FILTER_NEAREST, p_require_nav, p_global_aabb);\n\treturn faces;\n}\n\nvoid Terrain3D::set_warning(const uint8_t p_warning, const bool p_enabled) {\n\tif (p_enabled) {\n\t\t_warnings |= p_warning;\n\t} else {\n\t\t_warnings &= ~p_warning;\n\t}\n\tupdate_configuration_warnings();\n}\n\nPackedStringArray Terrain3D::_get_configuration_warnings() const {\n\tPackedStringArray psa;\n\tif (_data_directory.is_empty()) {\n\t\tpsa.push_back(\"No data directory specified. Select a directory then save the scene to write data.\");\n\t}\n\tif (_warnings & WARN_MISMATCHED_SIZE) {\n\t\tpsa.push_back(\"Texture dimensions don't match. Double-click a texture in the FileSystem panel to see its size. Read Texture Prep in docs.\");\n\t}\n\tif (_warnings & WARN_MISMATCHED_FORMAT) {\n\t\tpsa.push_back(\"Texture formats don't match. Double-click a texture in the FileSystem panel to see its format. Check Import panel. Read Texture Prep in docs.\");\n\t}\n\tif (_warnings & WARN_MISMATCHED_MIPMAPS) {\n\t\tpsa.push_back(\"Texture mipmap settings don't match. Change on the Import panel.\");\n\t}\n\treturn psa;\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\n// Notifications are defined in individual classes: Object, Node, Node3D\n// Listed below in order of operation\nvoid Terrain3D::_notification(const int p_what) {\n\tswitch (p_what) {\n\t\t\t/// Startup notifications\n\n\t\tcase NOTIFICATION_POSTINITIALIZE: {\n\t\t\t// Object initialized, before script is attached\n\t\t\tLOG(INFO, \"NOTIFICATION_POSTINITIALIZE\");\n\t\t\t_build_containers();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_ENTER_WORLD: {\n\t\t\t// Node3D registered to new World3D resource\n\t\t\t// Sent on scene changes\n\t\t\tLOG(INFO, \"NOTIFICATION_ENTER_WORLD\");\n\t\t\t_is_inside_world = true;\n\t\t\tif (_terrain_mesher) {\n\t\t\t\t_terrain_mesher->update();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_ENTER_TREE: {\n\t\t\t// Node entered a SceneTree\n\t\t\t// Sent on scene changes\n\t\t\tLOG(INFO, \"NOTIFICATION_ENTER_TREE\");\n\t\t\tset_as_top_level(true); // Don't inherit transforms from parent. Global only.\n\t\t\tset_notify_transform(true);\n\t\t\tset_meta(\"_edit_lock_\", true);\n\t\t\t_setup_mouse_picking();\n\t\t\t_setup_displacement_buffer();\n\t\t\t// Reload editor textures - Also see READY\n\t\t\tif (_free_editor_textures && !IS_EDITOR && _assets.is_valid() && !_assets->get_path().contains(\"Terrain3DAssets\")) {\n\t\t\t\tLOG(INFO, \"free_editor_textures enabled, reloading Assets path: \", _assets->get_path());\n\t\t\t\t_assets = ResourceLoader::get_singleton()->load(_assets->get_path(), \"\", ResourceLoader::CACHE_MODE_IGNORE);\n\t\t\t}\n\t\t\t_initialize(); // Rebuild anything freed: meshes, collision, instancer\n\t\t\tset_physics_process(true);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_READY: {\n\t\t\t// Node is ready\n\t\t\tLOG(INFO, \"NOTIFICATION_READY\");\n\t\t\t// Optional: Run the testing suite\n\t\t\t//#include \"unit_testing.h\"\n\t\t\t//test_differs();\n\n\t\t\t// Clear editor textures - also see ENTER_TREE\n\t\t\tif (_free_editor_textures && !IS_EDITOR && _assets.is_valid()) {\n\t\t\t\tif (_assets->get_path().contains(\"Terrain3DAssets\")) {\n\t\t\t\t\tLOG(WARN, \"free_editor_textures requires `Assets` be saved to a file. Do so, or disable the former to turn off this warning\");\n\t\t\t\t} else {\n\t\t\t\t\tLOG(INFO, \"free_editor_textures enabled, clearing texture assets\");\n\t\t\t\t\t_assets->clear_textures();\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t\t/// Game Loop notifications\n\n\t\tcase NOTIFICATION_PHYSICS_PROCESS: {\n\t\t\t// Node is processing one physics frame\n\t\t\t__physics_process(get_physics_process_delta_time());\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_TRANSFORM_CHANGED: {\n\t\t\t// Node3D or parent transform changed\n\t\t\tif (get_transform() != Transform3D()) {\n\t\t\t\tset_transform(Transform3D());\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_VISIBILITY_CHANGED: {\n\t\t\t// Node3D visibility changed\n\t\t\tLOG(INFO, \"NOTIFICATION_VISIBILITY_CHANGED\");\n\t\t\tif (_terrain_mesher) {\n\t\t\t\t_terrain_mesher->update();\n\t\t\t}\n\t\t\tif (_instancer) {\n\t\t\t\tif (!is_visible_in_tree()) {\n\t\t\t\t\t_instancer->destroy();\n\t\t\t\t} else {\n\t\t\t\t\t_instancer->update_mmis(-1, V2I_MAX, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_EXTENSION_RELOADED: {\n\t\t\t// Object finished hot reloading\n\t\t\tLOG(INFO, \"NOTIFICATION_EXTENSION_RELOADED\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_EDITOR_PRE_SAVE: {\n\t\t\t// Editor Node is about to save the current scene\n\t\t\tLOG(INFO, \"NOTIFICATION_EDITOR_PRE_SAVE\");\n\t\t\tif (_data_directory.is_empty()) {\n\t\t\t\tLOG(ERROR, \"Data directory is empty. Set it to save regions to disk.\");\n\t\t\t} else if (!_data) {\n\t\t\t\tLOG(DEBUG, \"Save requested, but no valid data object. Skipping\");\n\t\t\t} else {\n\t\t\t\t_data->save_directory(_data_directory);\n\t\t\t}\n\t\t\tif (!_material.is_valid()) {\n\t\t\t\tLOG(DEBUG, \"Save requested, but no valid material. Skipping\");\n\t\t\t} else {\n\t\t\t\t_material->save();\n\t\t\t}\n\t\t\tif (!_assets.is_valid()) {\n\t\t\t\tLOG(DEBUG, \"Save requested, but no valid texture list. Skipping\");\n\t\t\t} else {\n\t\t\t\t_assets->save();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_EDITOR_POST_SAVE: {\n\t\t\t// Editor Node finished saving current scene\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_CRASH: {\n\t\t\t// Godot's crash handler reports engine is about to crash\n\t\t\t// Only works on desktop if the crash handler is enabled\n\t\t\tLOG(INFO, \"NOTIFICATION_CRASH\");\n\t\t\tbreak;\n\t\t}\n\n\t\t\t/// Shut down notifications\n\n\t\tcase NOTIFICATION_EXIT_TREE: {\n\t\t\t// Node is about to exit a SceneTree\n\t\t\t// Sent on scene changes\n\t\t\tLOG(INFO, \"NOTIFICATION_EXIT_TREE\");\n\t\t\tset_physics_process(false);\n\t\t\t_destroy_terrain_mesher();\n\t\t\t_destroy_ocean_mesher();\n\t\t\t_destroy_instancer();\n\t\t\t_destroy_mouse_picking();\n\t\t\t_destroy_displacement_buffer();\n\t\t\tif (_assets.is_valid()) {\n\t\t\t\t_assets->uninitialize();\n\t\t\t}\n\t\t\tif (_material.is_valid()) {\n\t\t\t\t_material->uninitialize();\n\t\t\t}\n\t\t\t_initialized = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_EXIT_WORLD: {\n\t\t\t// Node3D unregistered from current World3D\n\t\t\t// Sent on scene changes\n\t\t\tLOG(INFO, \"NOTIFICATION_EXIT_WORLD\");\n\t\t\t_is_inside_world = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase NOTIFICATION_PREDELETE: {\n\t\t\t// Object is about to be deleted\n\t\t\tLOG(INFO, \"NOTIFICATION_PREDELETE\");\n\t\t\t_destroy_terrain_mesher(true);\n\t\t\t_destroy_ocean_mesher(true);\n\t\t\t_destroy_instancer();\n\t\t\t_destroy_collision(true);\n\t\t\t_assets.unref();\n\t\t\t_material.unref();\n\t\t\tmemdelete_safely(_data);\n\t\t\t_destroy_labels();\n\t\t\t_destroy_containers();\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\nvoid Terrain3D::_validate_property(PropertyInfo &p_property) const {\n\tif (_tessellation_level == 0) {\n\t\t// Hide all displacement properties\n\t\tif (p_property.name == StringName(\"displacement_scale\") ||\n\t\t\t\tp_property.name == StringName(\"displacement_sharpness\") ||\n\t\t\t\tp_property.name == StringName(\"buffer_shader_override_enabled\") ||\n\t\t\t\tp_property.name == StringName(\"buffer_shader_override\")) {\n\t\t\tp_property.usage = PROPERTY_USAGE_NO_EDITOR;\n\t\t}\n\t}\n\tif (!_ocean_enabled) {\n\t\t// Hide all ocean properties\n\t\tif (p_property.name == StringName(\"ocean_mesh_lods\") ||\n\t\t\t\tp_property.name == StringName(\"ocean_mesh_size\") ||\n\t\t\t\tp_property.name == StringName(\"ocean_tessellation_level\") ||\n\t\t\t\tp_property.name == StringName(\"ocean_material\") ||\n\t\t\t\tp_property.name == StringName(\"ocean_vertex_spacing\")) {\n\t\t\tp_property.usage = PROPERTY_USAGE_NO_EDITOR;\n\t\t}\n\t}\n}\n\nvoid Terrain3D::_bind_methods() {\n\tBIND_ENUM_CONSTANT(ERROR);\n\tBIND_ENUM_CONSTANT(INFO);\n\tBIND_ENUM_CONSTANT(DEBUG);\n\tBIND_ENUM_CONSTANT(EXTREME);\n\n\tBIND_ENUM_CONSTANT(SIZE_64);\n\tBIND_ENUM_CONSTANT(SIZE_128);\n\tBIND_ENUM_CONSTANT(SIZE_256);\n\tBIND_ENUM_CONSTANT(SIZE_512);\n\tBIND_ENUM_CONSTANT(SIZE_1024);\n\tBIND_ENUM_CONSTANT(SIZE_2048);\n\n\tClassDB::bind_method(D_METHOD(\"get_version\"), &Terrain3D::get_version);\n\tClassDB::bind_method(D_METHOD(\"set_debug_level\", \"level\"), &Terrain3D::set_debug_level);\n\tClassDB::bind_method(D_METHOD(\"get_debug_level\"), &Terrain3D::get_debug_level);\n\tClassDB::bind_method(D_METHOD(\"set_data_directory\", \"directory\"), &Terrain3D::set_data_directory);\n\tClassDB::bind_method(D_METHOD(\"get_data_directory\"), &Terrain3D::get_data_directory);\n\n\t// Object references\n\tClassDB::bind_method(D_METHOD(\"get_data\"), &Terrain3D::get_data);\n\tClassDB::bind_method(D_METHOD(\"set_material\", \"material\"), &Terrain3D::set_material);\n\tClassDB::bind_method(D_METHOD(\"get_material\"), &Terrain3D::get_material);\n\tClassDB::bind_method(D_METHOD(\"set_assets\", \"assets\"), &Terrain3D::set_assets);\n\tClassDB::bind_method(D_METHOD(\"get_assets\"), &Terrain3D::get_assets);\n\tClassDB::bind_method(D_METHOD(\"get_collision\"), &Terrain3D::get_collision);\n\tClassDB::bind_method(D_METHOD(\"get_instancer\"), &Terrain3D::get_instancer);\n\tClassDB::bind_method(D_METHOD(\"set_editor\", \"editor\"), &Terrain3D::set_editor);\n\tClassDB::bind_method(D_METHOD(\"get_editor\"), &Terrain3D::get_editor);\n\tClassDB::bind_method(D_METHOD(\"set_plugin\", \"plugin\"), &Terrain3D::set_plugin);\n\tClassDB::bind_method(D_METHOD(\"get_plugin\"), &Terrain3D::get_plugin);\n\n\t// Regions\n\tClassDB::bind_method(D_METHOD(\"change_region_size\", \"size\"), &Terrain3D::change_region_size);\n\tClassDB::bind_method(D_METHOD(\"get_region_size\"), &Terrain3D::get_region_size);\n\tClassDB::bind_method(D_METHOD(\"set_save_16_bit\", \"enabled\"), &Terrain3D::set_save_16_bit);\n\tClassDB::bind_method(D_METHOD(\"get_save_16_bit\"), &Terrain3D::get_save_16_bit);\n\tClassDB::bind_method(D_METHOD(\"set_label_distance\", \"distance\"), &Terrain3D::set_label_distance);\n\tClassDB::bind_method(D_METHOD(\"get_label_distance\"), &Terrain3D::get_label_distance);\n\tClassDB::bind_method(D_METHOD(\"set_label_size\", \"size\"), &Terrain3D::set_label_size);\n\tClassDB::bind_method(D_METHOD(\"get_label_size\"), &Terrain3D::get_label_size);\n\n\t// Target Tracking\n\tClassDB::bind_method(D_METHOD(\"set_camera\", \"camera\"), &Terrain3D::set_camera);\n\tClassDB::bind_method(D_METHOD(\"get_camera\"), &Terrain3D::get_camera);\n\tClassDB::bind_method(D_METHOD(\"set_clipmap_target\", \"node\"), &Terrain3D::set_clipmap_target);\n\tClassDB::bind_method(D_METHOD(\"get_clipmap_target\"), &Terrain3D::get_clipmap_target);\n\tClassDB::bind_method(D_METHOD(\"get_clipmap_target_position\"), &Terrain3D::get_clipmap_target_position);\n\tClassDB::bind_method(D_METHOD(\"set_collision_target\", \"node\"), &Terrain3D::set_collision_target);\n\tClassDB::bind_method(D_METHOD(\"get_collision_target\"), &Terrain3D::get_collision_target);\n\tClassDB::bind_method(D_METHOD(\"get_collision_target_position\"), &Terrain3D::get_collision_target_position);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_light_target\", \"node\"), &Terrain3D::set_ocean_light_target);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_light_target\"), &Terrain3D::get_ocean_light_target);\n\tClassDB::bind_method(D_METHOD(\"snap\"), &Terrain3D::snap);\n\n\t// Collision\n\tClassDB::bind_method(D_METHOD(\"set_collision_mode\", \"mode\"), &Terrain3D::set_collision_mode);\n\tClassDB::bind_method(D_METHOD(\"get_collision_mode\"), &Terrain3D::get_collision_mode);\n\tClassDB::bind_method(D_METHOD(\"set_collision_shape_size\", \"size\"), &Terrain3D::set_collision_shape_size);\n\tClassDB::bind_method(D_METHOD(\"get_collision_shape_size\"), &Terrain3D::get_collision_shape_size);\n\tClassDB::bind_method(D_METHOD(\"set_collision_radius\", \"radius\"), &Terrain3D::set_collision_radius);\n\tClassDB::bind_method(D_METHOD(\"get_collision_radius\"), &Terrain3D::get_collision_radius);\n\tClassDB::bind_method(D_METHOD(\"set_collision_layer\", \"layers\"), &Terrain3D::set_collision_layer);\n\tClassDB::bind_method(D_METHOD(\"get_collision_layer\"), &Terrain3D::get_collision_layer);\n\tClassDB::bind_method(D_METHOD(\"set_collision_mask\", \"mask\"), &Terrain3D::set_collision_mask);\n\tClassDB::bind_method(D_METHOD(\"get_collision_mask\"), &Terrain3D::get_collision_mask);\n\tClassDB::bind_method(D_METHOD(\"set_collision_priority\", \"priority\"), &Terrain3D::set_collision_priority);\n\tClassDB::bind_method(D_METHOD(\"get_collision_priority\"), &Terrain3D::get_collision_priority);\n\tClassDB::bind_method(D_METHOD(\"set_physics_material\", \"material\"), &Terrain3D::set_physics_material);\n\tClassDB::bind_method(D_METHOD(\"get_physics_material\"), &Terrain3D::get_physics_material);\n\n\t// Terrain Mesh\n\tClassDB::bind_method(D_METHOD(\"set_mesh_lods\", \"count\"), &Terrain3D::set_mesh_lods);\n\tClassDB::bind_method(D_METHOD(\"get_mesh_lods\"), &Terrain3D::get_mesh_lods);\n\tClassDB::bind_method(D_METHOD(\"set_mesh_size\", \"size\"), &Terrain3D::set_mesh_size);\n\tClassDB::bind_method(D_METHOD(\"get_mesh_size\"), &Terrain3D::get_mesh_size);\n\tClassDB::bind_method(D_METHOD(\"set_tessellation_level\", \"size\"), &Terrain3D::set_tessellation_level);\n\tClassDB::bind_method(D_METHOD(\"get_tessellation_level\"), &Terrain3D::get_tessellation_level);\n\tClassDB::bind_method(D_METHOD(\"set_vertex_spacing\", \"scale\"), &Terrain3D::set_vertex_spacing);\n\tClassDB::bind_method(D_METHOD(\"get_vertex_spacing\"), &Terrain3D::get_vertex_spacing);\n\tClassDB::bind_method(D_METHOD(\"set_cull_margin\", \"margin\"), &Terrain3D::set_cull_margin);\n\tClassDB::bind_method(D_METHOD(\"get_cull_margin\"), &Terrain3D::get_cull_margin);\n\tClassDB::bind_method(D_METHOD(\"set_cast_shadows\", \"shadow_casting_setting\"), &Terrain3D::set_cast_shadows);\n\tClassDB::bind_method(D_METHOD(\"get_cast_shadows\"), &Terrain3D::get_cast_shadows);\n\tClassDB::bind_method(D_METHOD(\"set_gi_mode\", \"gi_mode\"), &Terrain3D::set_gi_mode);\n\tClassDB::bind_method(D_METHOD(\"get_gi_mode\"), &Terrain3D::get_gi_mode);\n\tClassDB::bind_method(D_METHOD(\"set_render_layers\", \"layers\"), &Terrain3D::set_render_layers);\n\tClassDB::bind_method(D_METHOD(\"get_render_layers\"), &Terrain3D::get_render_layers);\n\n\t// Terrain Displacement\n\tClassDB::bind_method(D_METHOD(\"set_displacement_scale\", \"scale\"), &Terrain3D::set_displacement_scale);\n\tClassDB::bind_method(D_METHOD(\"get_displacement_scale\"), &Terrain3D::get_displacement_scale);\n\tClassDB::bind_method(D_METHOD(\"set_displacement_sharpness\", \"sharpness\"), &Terrain3D::set_displacement_sharpness);\n\tClassDB::bind_method(D_METHOD(\"get_displacement_sharpness\"), &Terrain3D::get_displacement_sharpness);\n\tClassDB::bind_method(D_METHOD(\"set_buffer_shader_override_enabled\", \"enabled\"), &Terrain3D::set_buffer_shader_override_enabled);\n\tClassDB::bind_method(D_METHOD(\"is_buffer_shader_override_enabled\"), &Terrain3D::is_buffer_shader_override_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_buffer_shader_override\", \"shader\"), &Terrain3D::set_buffer_shader_override);\n\tClassDB::bind_method(D_METHOD(\"get_buffer_shader_override\"), &Terrain3D::get_buffer_shader_override);\n\n\t// Ocean Mesh\n\tClassDB::bind_method(D_METHOD(\"set_ocean_enabled\", \"enabled\"), &Terrain3D::set_ocean_enabled);\n\tClassDB::bind_method(D_METHOD(\"is_ocean_enabled\"), &Terrain3D::is_ocean_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_mesh_lods\", \"count\"), &Terrain3D::set_ocean_mesh_lods);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_mesh_lods\"), &Terrain3D::get_ocean_mesh_lods);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_tessellation_level\", \"size\"), &Terrain3D::set_ocean_tessellation_level);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_tessellation_level\"), &Terrain3D::get_ocean_tessellation_level);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_mesh_size\", \"size\"), &Terrain3D::set_ocean_mesh_size);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_mesh_size\"), &Terrain3D::get_ocean_mesh_size);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_vertex_spacing\", \"scale\"), &Terrain3D::set_ocean_vertex_spacing);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_vertex_spacing\"), &Terrain3D::get_ocean_vertex_spacing);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_cull_margin\", \"margin\"), &Terrain3D::set_ocean_cull_margin);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_cull_margin\"), &Terrain3D::get_ocean_cull_margin);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_cast_shadows\", \"shadow_casting_setting\"), &Terrain3D::set_ocean_cast_shadows);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_cast_shadows\"), &Terrain3D::get_ocean_cast_shadows);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_gi_mode\", \"gi_mode\"), &Terrain3D::set_ocean_gi_mode);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_gi_mode\"), &Terrain3D::get_ocean_gi_mode);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_render_layers\", \"layers\"), &Terrain3D::set_ocean_render_layers);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_render_layers\"), &Terrain3D::get_ocean_render_layers);\n\tClassDB::bind_method(D_METHOD(\"set_ocean_material\", \"material\"), &Terrain3D::set_ocean_material);\n\tClassDB::bind_method(D_METHOD(\"get_ocean_material\"), &Terrain3D::get_ocean_material);\n\n\t// Rendering\n\tClassDB::bind_method(D_METHOD(\"set_mouse_layer\", \"layer\"), &Terrain3D::set_mouse_layer);\n\tClassDB::bind_method(D_METHOD(\"get_mouse_layer\"), &Terrain3D::get_mouse_layer);\n\tClassDB::bind_method(D_METHOD(\"set_free_editor_textures\"), &Terrain3D::set_free_editor_textures);\n\tClassDB::bind_method(D_METHOD(\"get_free_editor_textures\"), &Terrain3D::get_free_editor_textures);\n\tClassDB::bind_method(D_METHOD(\"set_instancer_mode\", \"mode\"), &Terrain3D::set_instancer_mode);\n\tClassDB::bind_method(D_METHOD(\"get_instancer_mode\"), &Terrain3D::get_instancer_mode);\n\n\t// Overlays\n\tClassDB::bind_method(D_METHOD(\"set_show_region_grid\", \"enabled\"), &Terrain3D::set_show_region_grid);\n\tClassDB::bind_method(D_METHOD(\"get_show_region_grid\"), &Terrain3D::get_show_region_grid);\n\tClassDB::bind_method(D_METHOD(\"set_show_instancer_grid\", \"enabled\"), &Terrain3D::set_show_instancer_grid);\n\tClassDB::bind_method(D_METHOD(\"get_show_instancer_grid\"), &Terrain3D::get_show_instancer_grid);\n\tClassDB::bind_method(D_METHOD(\"set_show_vertex_grid\", \"enabled\"), &Terrain3D::set_show_vertex_grid);\n\tClassDB::bind_method(D_METHOD(\"get_show_vertex_grid\"), &Terrain3D::get_show_vertex_grid);\n\tClassDB::bind_method(D_METHOD(\"set_show_contours\", \"enabled\"), &Terrain3D::set_show_contours);\n\tClassDB::bind_method(D_METHOD(\"get_show_contours\"), &Terrain3D::get_show_contours);\n\tClassDB::bind_method(D_METHOD(\"set_show_navigation\", \"enabled\"), &Terrain3D::set_show_navigation);\n\tClassDB::bind_method(D_METHOD(\"get_show_navigation\"), &Terrain3D::get_show_navigation);\n\n\t// Debug Views\n\tClassDB::bind_method(D_METHOD(\"set_show_checkered\", \"enabled\"), &Terrain3D::set_show_checkered);\n\tClassDB::bind_method(D_METHOD(\"get_show_checkered\"), &Terrain3D::get_show_checkered);\n\tClassDB::bind_method(D_METHOD(\"set_show_grey\", \"enabled\"), &Terrain3D::set_show_grey);\n\tClassDB::bind_method(D_METHOD(\"get_show_grey\"), &Terrain3D::get_show_grey);\n\tClassDB::bind_method(D_METHOD(\"set_show_heightmap\", \"enabled\"), &Terrain3D::set_show_heightmap);\n\tClassDB::bind_method(D_METHOD(\"get_show_heightmap\"), &Terrain3D::get_show_heightmap);\n\tClassDB::bind_method(D_METHOD(\"set_show_jaggedness\", \"enabled\"), &Terrain3D::set_show_jaggedness);\n\tClassDB::bind_method(D_METHOD(\"get_show_jaggedness\"), &Terrain3D::get_show_jaggedness);\n\tClassDB::bind_method(D_METHOD(\"set_show_autoshader\", \"enabled\"), &Terrain3D::set_show_autoshader);\n\tClassDB::bind_method(D_METHOD(\"get_show_autoshader\"), &Terrain3D::get_show_autoshader);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_texture\", \"enabled\"), &Terrain3D::set_show_control_texture);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_texture\"), &Terrain3D::get_show_control_texture);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_blend\", \"enabled\"), &Terrain3D::set_show_control_blend);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_blend\"), &Terrain3D::get_show_control_blend);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_angle\", \"enabled\"), &Terrain3D::set_show_control_angle);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_angle\"), &Terrain3D::get_show_control_angle);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_scale\", \"enabled\"), &Terrain3D::set_show_control_scale);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_scale\"), &Terrain3D::get_show_control_scale);\n\tClassDB::bind_method(D_METHOD(\"set_show_colormap\", \"enabled\"), &Terrain3D::set_show_colormap);\n\tClassDB::bind_method(D_METHOD(\"get_show_colormap\"), &Terrain3D::get_show_colormap);\n\tClassDB::bind_method(D_METHOD(\"set_show_roughmap\", \"enabled\"), &Terrain3D::set_show_roughmap);\n\tClassDB::bind_method(D_METHOD(\"get_show_roughmap\"), &Terrain3D::get_show_roughmap);\n\tClassDB::bind_method(D_METHOD(\"set_show_displacement_buffer\", \"enabled\"), &Terrain3D::set_show_displacement_buffer);\n\tClassDB::bind_method(D_METHOD(\"get_show_displacement_buffer\"), &Terrain3D::get_show_displacement_buffer);\n\n\t// PBR Views\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_albedo\", \"enabled\"), &Terrain3D::set_show_texture_albedo);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_albedo\"), &Terrain3D::get_show_texture_albedo);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_height\", \"enabled\"), &Terrain3D::set_show_texture_height);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_height\"), &Terrain3D::get_show_texture_height);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_normal\", \"enabled\"), &Terrain3D::set_show_texture_normal);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_normal\"), &Terrain3D::get_show_texture_normal);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_rough\", \"enabled\"), &Terrain3D::set_show_texture_rough);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_rough\"), &Terrain3D::get_show_texture_rough);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_ao\", \"enabled\"), &Terrain3D::set_show_texture_ao);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_ao\"), &Terrain3D::get_show_texture_ao);\n\n\t// Utility\n\tClassDB::bind_method(D_METHOD(\"get_intersection\", \"src_pos\", \"direction\", \"gpu_mode\"), &Terrain3D::get_intersection, DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"get_raycast_result\", \"src_pos\", \"direction\", \"collision_mask\", \"exclude_terrain\"),\n\t\t\t&Terrain3D::get_raycast_result, DEFVAL(0xFFFFFFFF), DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"bake_mesh\", \"lod\", \"filter\"), &Terrain3D::bake_mesh, DEFVAL(Terrain3DData::HEIGHT_FILTER_NEAREST));\n\tClassDB::bind_method(D_METHOD(\"generate_nav_mesh_source_geometry\", \"global_aabb\", \"require_nav\"), &Terrain3D::generate_nav_mesh_source_geometry, DEFVAL(true));\n\n\tADD_PROPERTY(PropertyInfo(Variant::STRING, \"version\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY), \"\", \"get_version\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"debug_level\", PROPERTY_HINT_ENUM, \"Errors,Info,Debug,Extreme\"), \"set_debug_level\", \"get_debug_level\");\n\tADD_PROPERTY(PropertyInfo(Variant::STRING, \"data_directory\", PROPERTY_HINT_DIR), \"set_data_directory\", \"get_data_directory\");\n\n\t// Object references\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"material\", PROPERTY_HINT_RESOURCE_TYPE, \"Terrain3DMaterial\"), \"set_material\", \"get_material\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"assets\", PROPERTY_HINT_RESOURCE_TYPE, \"Terrain3DAssets\"), \"set_assets\", \"get_assets\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"data\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE, \"Terrain3DData\"), \"\", \"get_data\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"collision\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE, \"Terrain3DCollision\"), \"\", \"get_collision\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"instancer\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE, \"Terrain3DInstancer\"), \"\", \"get_instancer\");\n\n\tADD_GROUP(\"Regions\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"region_size\", PROPERTY_HINT_ENUM, \"64:64,128:128,256:256,512:512,1024:1024,2048:2048\", PROPERTY_USAGE_EDITOR), \"change_region_size\", \"get_region_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"save_16_bit\"), \"set_save_16_bit\", \"get_save_16_bit\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"label_distance\", PROPERTY_HINT_RANGE, \"0.0,10000.0,0.5,or_greater\"), \"set_label_distance\", \"get_label_distance\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"label_size\", PROPERTY_HINT_RANGE, \"24,128,1\"), \"set_label_size\", \"get_label_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_grid\"), \"set_show_region_grid\", \"get_show_region_grid\");\n\n\tADD_GROUP(\"Collision\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"collision_mode\", PROPERTY_HINT_ENUM, \"Disabled,Dynamic / Game,Dynamic / Editor,Full / Game,Full / Editor\"), \"set_collision_mode\", \"get_collision_mode\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"collision_shape_size\", PROPERTY_HINT_RANGE, \"8,64,8\"), \"set_collision_shape_size\", \"get_collision_shape_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"collision_radius\", PROPERTY_HINT_RANGE, \"16,256,16\"), \"set_collision_radius\", \"get_collision_radius\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"collision_target\", PROPERTY_HINT_NODE_TYPE, \"Node3D\", PROPERTY_USAGE_DEFAULT, \"Node3D\"), \"set_collision_target\", \"get_collision_target\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"collision_layer\", PROPERTY_HINT_LAYERS_3D_PHYSICS), \"set_collision_layer\", \"get_collision_layer\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"collision_mask\", PROPERTY_HINT_LAYERS_3D_PHYSICS), \"set_collision_mask\", \"get_collision_mask\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"collision_priority\", PROPERTY_HINT_RANGE, \"0.1,256,.1\"), \"set_collision_priority\", \"get_collision_priority\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"physics_material\", PROPERTY_HINT_RESOURCE_TYPE, \"PhysicsMaterial\"), \"set_physics_material\", \"get_physics_material\");\n\n\tADD_GROUP(\"Terrain Mesh\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"clipmap_target\", PROPERTY_HINT_NODE_TYPE, \"Node3D\", PROPERTY_USAGE_DEFAULT, \"Node3D\"), \"set_clipmap_target\", \"get_clipmap_target\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"mesh_lods\", PROPERTY_HINT_RANGE, \"1,10,1\"), \"set_mesh_lods\", \"get_mesh_lods\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"tessellation_level\", PROPERTY_HINT_RANGE, \"0,6,1\"), \"set_tessellation_level\", \"get_tessellation_level\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"mesh_size\", PROPERTY_HINT_RANGE, \"8,256,2\"), \"set_mesh_size\", \"get_mesh_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"vertex_spacing\", PROPERTY_HINT_RANGE, \"0.25,10.0,or_greater\"), \"set_vertex_spacing\", \"get_vertex_spacing\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"cull_margin\", PROPERTY_HINT_RANGE, \"0.0,10000.0,.5,or_greater\"), \"set_cull_margin\", \"get_cull_margin\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"cast_shadows\", PROPERTY_HINT_ENUM, \"Off,On,Double-Sided,Shadows Only\"), \"set_cast_shadows\", \"get_cast_shadows\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"gi_mode\", PROPERTY_HINT_ENUM, \"Disabled,Static,Dynamic\"), \"set_gi_mode\", \"get_gi_mode\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"render_layers\", PROPERTY_HINT_LAYERS_3D_RENDER), \"set_render_layers\", \"get_render_layers\");\n\n\tADD_SUBGROUP(\"Displacement\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"displacement_scale\", PROPERTY_HINT_RANGE, \"0.0, 2.0, 0.01\"), \"set_displacement_scale\", \"get_displacement_scale\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"displacement_sharpness\", PROPERTY_HINT_RANGE, \"0.0, 1.0, 0.01\"), \"set_displacement_sharpness\", \"get_displacement_sharpness\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"buffer_shader_override_enabled\"), \"set_buffer_shader_override_enabled\", \"is_buffer_shader_override_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"buffer_shader_override\", PROPERTY_HINT_RESOURCE_TYPE, \"Shader\"), \"set_buffer_shader_override\", \"get_buffer_shader_override\");\n\n\tADD_GROUP(\"Ocean Mesh\", \"ocean_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"ocean_enabled\"), \"set_ocean_enabled\", \"is_ocean_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"ocean_mesh_lods\", PROPERTY_HINT_RANGE, \"1,10,1\"), \"set_ocean_mesh_lods\", \"get_ocean_mesh_lods\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"ocean_tessellation_level\", PROPERTY_HINT_RANGE, \"0,6,1\"), \"set_ocean_tessellation_level\", \"get_ocean_tessellation_level\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"ocean_mesh_size\", PROPERTY_HINT_RANGE, \"8,256,2\"), \"set_ocean_mesh_size\", \"get_ocean_mesh_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"ocean_vertex_spacing\", PROPERTY_HINT_RANGE, \"0.25,10.0,0.05,or_greater\"), \"set_ocean_vertex_spacing\", \"get_ocean_vertex_spacing\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"ocean_cull_margin\", PROPERTY_HINT_RANGE, \"0.0,10000.0,.5,or_greater\"), \"set_ocean_cull_margin\", \"get_ocean_cull_margin\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"ocean_cast_shadows\", PROPERTY_HINT_ENUM, \"Off,On,Double-Sided,Shadows Only\"), \"set_ocean_cast_shadows\", \"get_ocean_cast_shadows\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"ocean_gi_mode\", PROPERTY_HINT_ENUM, \"Disabled,Static,Dynamic\"), \"set_ocean_gi_mode\", \"get_ocean_gi_mode\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"ocean_render_layers\", PROPERTY_HINT_LAYERS_3D_RENDER), \"set_ocean_render_layers\", \"get_ocean_render_layers\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"ocean_material\", PROPERTY_HINT_RESOURCE_TYPE, \"ShaderMaterial,BaseMaterial3D\"), \"set_ocean_material\", \"get_ocean_material\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"ocean_light_target\", PROPERTY_HINT_NODE_TYPE, \"DirectionalLight3D\", PROPERTY_USAGE_DEFAULT, \"Node3D\"), \"set_ocean_light_target\", \"get_ocean_light_target\");\n\n\tADD_GROUP(\"Rendering\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"mouse_layer\", PROPERTY_HINT_RANGE, \"21, 32\"), \"set_mouse_layer\", \"get_mouse_layer\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"free_editor_textures\"), \"set_free_editor_textures\", \"get_free_editor_textures\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"instancer_mode\", PROPERTY_HINT_ENUM, \"Disabled,Normal\"), \"set_instancer_mode\", \"get_instancer_mode\");\n\n\tADD_GROUP(\"Overlays\", \"show_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_region_grid\"), \"set_show_region_grid\", \"get_show_region_grid\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_instancer_grid\"), \"set_show_instancer_grid\", \"get_show_instancer_grid\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_vertex_grid\"), \"set_show_vertex_grid\", \"get_show_vertex_grid\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_contours\"), \"set_show_contours\", \"get_show_contours\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_navigation\"), \"set_show_navigation\", \"get_show_navigation\");\n\n\tADD_GROUP(\"Debug Views\", \"show_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_checkered\"), \"set_show_checkered\", \"get_show_checkered\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_grey\"), \"set_show_grey\", \"get_show_grey\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_heightmap\"), \"set_show_heightmap\", \"get_show_heightmap\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_jaggedness\"), \"set_show_jaggedness\", \"get_show_jaggedness\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_autoshader\"), \"set_show_autoshader\", \"get_show_autoshader\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_texture\"), \"set_show_control_texture\", \"get_show_control_texture\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_blend\"), \"set_show_control_blend\", \"get_show_control_blend\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_angle\"), \"set_show_control_angle\", \"get_show_control_angle\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_scale\"), \"set_show_control_scale\", \"get_show_control_scale\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_colormap\"), \"set_show_colormap\", \"get_show_colormap\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_roughmap\"), \"set_show_roughmap\", \"get_show_roughmap\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_displacement_buffer\"), \"set_show_displacement_buffer\", \"get_show_displacement_buffer\");\n\n\tADD_SUBGROUP(\"PBR Maps\", \"show_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_albedo\"), \"set_show_texture_albedo\", \"get_show_texture_albedo\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_height\"), \"set_show_texture_height\", \"get_show_texture_height\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_normal\"), \"set_show_texture_normal\", \"get_show_texture_normal\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_rough\"), \"set_show_texture_rough\", \"get_show_texture_rough\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_ao\"), \"set_show_texture_ao\", \"get_show_texture_ao\");\n\n\tADD_SIGNAL(MethodInfo(\"material_changed\"));\n\tADD_SIGNAL(MethodInfo(\"assets_changed\"));\n}\n"
  },
  {
    "path": "src/terrain_3d.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_CLASS_H\n#define TERRAIN3D_CLASS_H\n\n#include <godot_cpp/classes/camera3d.hpp>\n#include <godot_cpp/classes/color_rect.hpp>\n#include <godot_cpp/classes/geometry_instance3d.hpp>\n#include <godot_cpp/classes/mesh.hpp>\n#include <godot_cpp/classes/mesh_instance3d.hpp>\n#include <godot_cpp/classes/object.hpp>\n#include <godot_cpp/classes/rendering_server.hpp>\n#include <godot_cpp/classes/sub_viewport.hpp>\n\n#include \"constants.h\"\n#include \"target_node_3d.h\"\n#include \"terrain_3d_assets.h\"\n#include \"terrain_3d_collision.h\"\n#include \"terrain_3d_data.h\"\n#include \"terrain_3d_editor.h\"\n#include \"terrain_3d_instancer.h\"\n#include \"terrain_3d_material.h\"\n#include \"terrain_3d_mesher.h\"\n\nclass Terrain3D : public Node3D {\n\tGDCLASS(Terrain3D, Node3D);\n\tCLASS_NAME();\n\npublic: // Constants\n\tenum DebugLevel {\n\t\tMESG = -2, // Always print except in release builds\n\t\tWARN = -1, // Always print except in release builds\n\t\tERROR = 0, // Always print except in release builds\n\t\tINFO = 1, // Print every function call and important entries\n\t\tDEBUG = 2, // Print details within functions\n\t\tEXTREME = 3, // Continuous operations like snapping\n\t};\n\n\tenum RegionSize {\n\t\tSIZE_64 = 64,\n\t\tSIZE_128 = 128,\n\t\tSIZE_256 = 256,\n\t\tSIZE_512 = 512,\n\t\tSIZE_1024 = 1024,\n\t\tSIZE_2048 = 2048,\n\t};\n\nprivate:\n\tString _version = \"1.1.0-dev\";\n\tString _data_directory;\n\tbool _is_inside_world = false;\n\tbool _initialized = false;\n\tuint8_t _warnings = 0u;\n\n\t// Object references\n\tTerrain3DData *_data = nullptr;\n\tRef<Terrain3DAssets> _assets;\n\tTerrain3DCollision *_collision = nullptr;\n\tTerrain3DInstancer *_instancer = nullptr;\n\tTerrain3DEditor *_editor = nullptr;\n\tObject *_editor_plugin = nullptr;\n\n\t// Regions\n\tRegionSize _region_size = SIZE_256;\n\tbool _save_16_bit = false;\n\treal_t _label_distance = 0.f;\n\tint _label_size = 48;\n\n\t// Tracked Targets\n\tTargetNode3D _clipmap_target;\n\tTargetNode3D _collision_target;\n\tTargetNode3D _ocean_light_target;\n\tTargetNode3D _camera; // Fallback target for clipmap and collision\n\n\t// Terrain Mesh\n\tTerrain3DMesher *_terrain_mesher = nullptr;\n\tRef<Terrain3DMaterial> _material;\n\tint _mesh_lods = 7;\n\tint _tessellation_level = 0;\n\tint _mesh_size = 48;\n\treal_t _vertex_spacing = 1.0f;\n\treal_t _cull_margin = 0.0f;\n\tRenderingServer::ShadowCastingSetting _cast_shadows = RenderingServer::SHADOW_CASTING_SETTING_ON;\n\tGeometryInstance3D::GIMode _gi_mode = GeometryInstance3D::GI_MODE_STATIC;\n\tuint32_t _render_layers = 1u | (1u << 31u); // Bit 1 and 32 for the cursor\n\n\t// Displacement Buffer\n\tSubViewport *_d_buffer_vp = nullptr;\n\tColorRect *_d_buffer_rect = nullptr;\n\tVector2 _last_buffer_position = V2_MAX;\n\n\t// Ocean Mesh\n\tTerrain3DMesher *_ocean_mesher = nullptr;\n\tbool _ocean_enabled = false;\n\tint _ocean_mesh_lods = 7;\n\tint _ocean_tessellation_level = 0;\n\tint _ocean_mesh_size = 32;\n\treal_t _ocean_vertex_spacing = 4.0f;\n\treal_t _ocean_cull_margin = 20.0f;\n\tRenderingServer::ShadowCastingSetting _ocean_cast_shadows = RenderingServer::SHADOW_CASTING_SETTING_OFF;\n\tGeometryInstance3D::GIMode _ocean_gi_mode = GeometryInstance3D::GI_MODE_DISABLED;\n\tuint32_t _ocean_render_layers = 1u;\n\tRef<Material> _ocean_material;\n\n\t// Rendering\n\tbool _free_editor_textures = true;\n\t// Mouse cursor\n\tSubViewport *_mouse_vp = nullptr;\n\tCamera3D *_mouse_cam = nullptr;\n\tMeshInstance3D *_mouse_quad = nullptr;\n\tuint32_t _mouse_layer = 32u;\n\t// Parent containers for child nodes\n\tNode3D *_label_parent;\n\n\tvoid _initialize();\n\tvoid __physics_process(const double p_delta);\n\tvoid _grab_camera();\n\n\tvoid _destroy_collision(const bool p_final = false);\n\n\tvoid _setup_terrain_mesher();\n\tvoid _update_mesher_aabbs() { _terrain_mesher ? _terrain_mesher->update_aabbs() : void(); }\n\tvoid _destroy_terrain_mesher(const bool p_final = false);\n\tvoid _setup_ocean_mesher();\n\tvoid _update_ocean_aabbs() { _ocean_mesher ? _ocean_mesher->update_aabbs() : void(); }\n\tvoid _destroy_ocean_mesher(const bool p_final = false);\n\n\tvoid _setup_displacement_buffer();\n\tvoid _update_displacement_buffer();\n\tvoid _destroy_displacement_buffer();\n\n\tvoid _build_containers();\n\tvoid _destroy_containers();\n\tvoid _destroy_labels();\n\n\tvoid _setup_mouse_picking();\n\tvoid _destroy_mouse_picking();\n\tvoid _destroy_instancer();\n\n\tvoid _generate_triangles(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs, const int32_t p_lod,\n\t\t\tconst Terrain3DData::HeightFilter p_filter, const bool require_nav, const AABB &p_global_aabb) const;\n\tvoid _generate_triangle_pair(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs, const int32_t p_lod,\n\t\t\tconst Terrain3DData::HeightFilter p_filter, const bool require_nav, const int32_t x, const int32_t z) const;\n\npublic:\n\tstatic DebugLevel debug_level; // Initialized in terrain_3d.cpp\n\n\tTerrain3D();\n\t~Terrain3D() {}\n\tbool is_inside_world() const { return _is_inside_world; }\n\n\t// Terrain\n\tString get_version() const { return _version; }\n\tvoid set_debug_level(const DebugLevel p_level);\n\tDebugLevel get_debug_level() const { return debug_level; }\n\tvoid set_data_directory(String p_dir);\n\tString get_data_directory() const { return _data ? _data_directory : \"\"; }\n\n\t// Object references\n\tTerrain3DData *get_data() const { return _data; }\n\tvoid set_assets(const Ref<Terrain3DAssets> &p_assets);\n\tRef<Terrain3DAssets> get_assets() const { return _assets; }\n\tTerrain3DCollision *get_collision() const { return _collision; }\n\tTerrain3DInstancer *get_instancer() const { return _instancer; }\n\tvoid set_editor(Terrain3DEditor *p_editor);\n\tTerrain3DEditor *get_editor() const { return _editor; }\n\tvoid set_plugin(Object *p_plugin);\n\tObject *get_plugin() const { return _editor_plugin; }\n\n\t// Regions\n\tvoid set_region_size(const RegionSize p_size);\n\tRegionSize get_region_size() const { return _region_size; }\n\tvoid change_region_size(const RegionSize p_size) { _data ? _data->change_region_size(p_size) : void(); }\n\tvoid set_save_16_bit(const bool p_enabled);\n\tbool get_save_16_bit() const { return _save_16_bit; }\n\tvoid set_label_distance(const real_t p_distance);\n\treal_t get_label_distance() const { return _label_distance; }\n\tvoid set_label_size(const int p_size);\n\tint get_label_size() const { return _label_size; }\n\tvoid update_region_labels();\n\n\t// Target Tracking\n\tvoid set_camera(Camera3D *p_camera);\n\tCamera3D *get_camera() const { return cast_to<Camera3D>(_camera.ptr()); }\n\tvoid set_clipmap_target(Node3D *p_node);\n\tNode3D *get_clipmap_target() const { return _clipmap_target.ptr(); }\n\tVector3 get_clipmap_target_position() const;\n\tvoid set_collision_target(Node3D *p_node);\n\tNode3D *get_collision_target() const { return _collision_target.ptr(); }\n\tVector3 get_collision_target_position() const;\n\tvoid set_ocean_light_target(Node3D *p_node);\n\tNode3D *get_ocean_light_target() const { return _ocean_light_target.ptr(); }\n\tvoid snap();\n\n\t// Collision Aliases\n\tvoid set_collision_mode(const CollisionMode p_mode) { _collision ? _collision->set_mode(p_mode) : void(); }\n\tCollisionMode get_collision_mode() const { return _collision ? _collision->get_mode() : CollisionMode::DYNAMIC_GAME; }\n\tvoid set_collision_shape_size(const uint16_t p_size) { _collision ? _collision->set_shape_size(p_size) : void(); }\n\tuint16_t get_collision_shape_size() const { return _collision ? _collision->get_shape_size() : 16; }\n\tvoid set_collision_radius(const uint16_t p_radius) { _collision ? _collision->set_radius(p_radius) : void(); }\n\tuint16_t get_collision_radius() const { return _collision ? _collision->get_radius() : 64; }\n\tvoid set_collision_layer(const uint32_t p_layers) { _collision ? _collision->set_layer(p_layers) : void(); }\n\tuint32_t get_collision_layer() const { return _collision ? _collision->get_layer() : 1; }\n\tvoid set_collision_mask(const uint32_t p_mask) { _collision ? _collision->set_mask(p_mask) : void(); }\n\tuint32_t get_collision_mask() const { return _collision ? _collision->get_mask() : 1; }\n\tvoid set_collision_priority(const real_t p_priority) { _collision ? _collision->set_priority(p_priority) : void(); }\n\treal_t get_collision_priority() const { return _collision ? _collision->get_priority() : 1.f; }\n\tvoid set_physics_material(const Ref<PhysicsMaterial> &p_mat) { _collision ? _collision->set_physics_material(p_mat) : void(); }\n\tRef<PhysicsMaterial> get_physics_material() const { return _collision ? _collision->get_physics_material() : Ref<PhysicsMaterial>(); }\n\n\t// Terrain Mesh\n\tTerrain3DMesher *get_mesher() const { return _terrain_mesher; }\n\tvoid set_material(const Ref<Terrain3DMaterial> &p_material);\n\tRef<Terrain3DMaterial> get_material() const { return _material; }\n\tvoid set_mesh_lods(const int p_count);\n\tint get_mesh_lods() const { return _mesh_lods; }\n\tvoid set_tessellation_level(const int p_level);\n\tint get_tessellation_level() const { return _tessellation_level; }\n\tvoid set_mesh_size(const int p_size);\n\tint get_mesh_size() const { return _mesh_size; }\n\tvoid set_vertex_spacing(const real_t p_spacing);\n\treal_t get_vertex_spacing() const { return _vertex_spacing; }\n\tvoid set_cull_margin(const real_t p_margin);\n\treal_t get_cull_margin() const { return _cull_margin; };\n\tvoid set_cast_shadows(const RenderingServer::ShadowCastingSetting p_cast_shadows);\n\tRenderingServer::ShadowCastingSetting get_cast_shadows() const { return _cast_shadows; };\n\tvoid set_gi_mode(const GeometryInstance3D::GIMode p_gi_mode);\n\tGeometryInstance3D::GIMode get_gi_mode() const { return _gi_mode; }\n\tvoid set_render_layers(const uint32_t p_layers);\n\tuint32_t get_render_layers() const { return _render_layers; };\n\n\t// Material Displacement Aliases\n\tvoid set_displacement_scale(const real_t p_displacement_scale) { _material.is_valid() ? _material->set_displacement_scale(p_displacement_scale) : void(); }\n\treal_t get_displacement_scale() const { return _material.is_valid() ? _material->get_displacement_scale() : 1.f; }\n\tvoid set_displacement_sharpness(const real_t p_displacement_sharpness) { _material.is_valid() ? _material->set_displacement_sharpness(p_displacement_sharpness) : void(); }\n\treal_t get_displacement_sharpness() const { return _material.is_valid() ? _material->get_displacement_sharpness() : 0.25f; }\n\tvoid set_buffer_shader_override_enabled(const bool p_enabled) { _material.is_valid() ? _material->set_buffer_shader_override_enabled(p_enabled) : void(); }\n\tbool is_buffer_shader_override_enabled() const { return _material.is_valid() ? _material->is_buffer_shader_override_enabled() : false; }\n\tvoid set_buffer_shader_override(const Ref<Shader> &p_shader) { return _material.is_valid() ? _material->set_buffer_shader_override(p_shader) : void(); }\n\tRef<Shader> get_buffer_shader_override() const { return _material.is_valid() ? _material->get_buffer_shader_override() : Ref<Shader>(); }\n\n\t// Ocean Mesh\n\tTerrain3DMesher *get_ocean_mesher() const { return _ocean_mesher; }\n\tvoid set_ocean_enabled(const bool p_enabled);\n\tbool is_ocean_enabled() const { return _ocean_enabled; }\n\tvoid set_ocean_mesh_lods(const int p_count);\n\tint get_ocean_mesh_lods() const { return _ocean_mesh_lods; }\n\tvoid set_ocean_tessellation_level(const int p_level);\n\tint get_ocean_tessellation_level() const { return _ocean_tessellation_level; }\n\tvoid set_ocean_mesh_size(const int p_size);\n\tint get_ocean_mesh_size() const { return _ocean_mesh_size; }\n\tvoid set_ocean_vertex_spacing(const real_t p_spacing);\n\treal_t get_ocean_vertex_spacing() const { return _ocean_vertex_spacing; }\n\tvoid set_ocean_cull_margin(const real_t p_margin);\n\treal_t get_ocean_cull_margin() const { return _ocean_cull_margin; };\n\tvoid set_ocean_cast_shadows(const RenderingServer::ShadowCastingSetting p_cast_shadows);\n\tRenderingServer::ShadowCastingSetting get_ocean_cast_shadows() const { return _ocean_cast_shadows; };\n\tvoid set_ocean_gi_mode(const GeometryInstance3D::GIMode p_gi_mode);\n\tGeometryInstance3D::GIMode get_ocean_gi_mode() const { return _ocean_gi_mode; }\n\tvoid set_ocean_render_layers(const uint32_t p_layers);\n\tuint32_t get_ocean_render_layers() const { return _ocean_render_layers; };\n\tvoid set_ocean_material(const Ref<Material> &p_material);\n\tRef<Material> get_ocean_material() const { return _ocean_material; }\n\n\t// Rendering\n\tvoid set_mouse_layer(const uint32_t p_layer);\n\tuint32_t get_mouse_layer() const { return _mouse_layer; };\n\tvoid set_free_editor_textures(const bool p_free_textures) { _free_editor_textures = p_free_textures; }\n\tbool get_free_editor_textures() const { return _free_editor_textures; };\n\tvoid set_instancer_mode(const InstancerMode p_mode) { _instancer ? _instancer->set_mode(p_mode) : void(); }\n\tInstancerMode get_instancer_mode() const { return _instancer ? _instancer->get_mode() : InstancerMode::NORMAL; }\n\n\t// Utility\n\tVector3 get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction, const bool p_gpu_mode = false);\n\tDictionary get_raycast_result(const Vector3 &p_src_pos, const Vector3 &p_direction, const uint32_t p_col_mask = 0xFFFFFFFF, const bool p_exclude_self = false) const;\n\tRef<Mesh> bake_mesh(const int p_lod, const Terrain3DData::HeightFilter p_filter = Terrain3DData::HEIGHT_FILTER_NEAREST) const;\n\tPackedVector3Array generate_nav_mesh_source_geometry(const AABB &p_global_aabb, const bool p_require_nav = true) const;\n\n\t// Warnings\n\tvoid set_warning(const uint8_t p_warning, const bool p_enabled);\n\tuint8_t get_warnings() const { return _warnings; }\n\tPackedStringArray _get_configuration_warnings() const override;\n\n\t// Overlay Aliases\n\tvoid set_show_region_grid(const bool p_enabled) { _material.is_valid() ? _material->set_show_region_grid(p_enabled) : void(); }\n\tbool get_show_region_grid() const { return _material.is_valid() ? _material->get_show_region_grid() : false; }\n\tvoid set_show_instancer_grid(const bool p_enabled) { _material.is_valid() ? _material->set_show_instancer_grid(p_enabled) : void(); }\n\tbool get_show_instancer_grid() const { return _material.is_valid() ? _material->get_show_instancer_grid() : false; }\n\tvoid set_show_vertex_grid(const bool p_enabled) { _material.is_valid() ? _material->set_show_vertex_grid(p_enabled) : void(); }\n\tbool get_show_vertex_grid() const { return _material.is_valid() ? _material->get_show_vertex_grid() : false; }\n\tvoid set_show_contours(const bool p_enabled) { _material.is_valid() ? _material->set_show_contours(p_enabled) : void(); }\n\tbool get_show_contours() const { return _material.is_valid() ? _material->get_show_contours() : false; }\n\tvoid set_show_navigation(const bool p_enabled) { _material.is_valid() ? _material->set_show_navigation(p_enabled) : void(); }\n\tbool get_show_navigation() const { return _material.is_valid() ? _material->get_show_navigation() : false; }\n\n\t// Debug View Aliases\n\tvoid set_show_checkered(const bool p_enabled) { _material.is_valid() ? _material->set_show_checkered(p_enabled) : void(); }\n\tbool get_show_checkered() const { return _material.is_valid() ? _material->get_show_checkered() : false; }\n\tvoid set_show_grey(const bool p_enabled) { _material.is_valid() ? _material->set_show_grey(p_enabled) : void(); }\n\tbool get_show_grey() const { return _material.is_valid() ? _material->get_show_grey() : false; }\n\tvoid set_show_heightmap(const bool p_enabled) { _material.is_valid() ? _material->set_show_heightmap(p_enabled) : void(); }\n\tbool get_show_heightmap() const { return _material.is_valid() ? _material->get_show_heightmap() : false; }\n\tvoid set_show_jaggedness(const bool p_enabled) { _material.is_valid() ? _material->set_show_jaggedness(p_enabled) : void(); }\n\tbool get_show_jaggedness() const { return _material.is_valid() ? _material->get_show_jaggedness() : false; }\n\tvoid set_show_autoshader(const bool p_enabled) { _material.is_valid() ? _material->set_show_autoshader(p_enabled) : void(); }\n\tbool get_show_autoshader() const { return _material.is_valid() ? _material->get_show_autoshader() : false; }\n\tvoid set_show_control_texture(const bool p_enabled) { _material.is_valid() ? _material->set_show_control_texture(p_enabled) : void(); }\n\tbool get_show_control_texture() const { return _material.is_valid() ? _material->get_show_control_texture() : false; }\n\tvoid set_show_control_blend(const bool p_enabled) { _material.is_valid() ? _material->set_show_control_blend(p_enabled) : void(); }\n\tbool get_show_control_blend() const { return _material.is_valid() ? _material->get_show_control_blend() : false; }\n\tvoid set_show_control_angle(const bool p_enabled) { _material.is_valid() ? _material->set_show_control_angle(p_enabled) : void(); }\n\tbool get_show_control_angle() const { return _material.is_valid() ? _material->get_show_control_angle() : false; }\n\tvoid set_show_control_scale(const bool p_enabled) { _material.is_valid() ? _material->set_show_control_scale(p_enabled) : void(); }\n\tbool get_show_control_scale() const { return _material.is_valid() ? _material->get_show_control_scale() : false; }\n\tvoid set_show_colormap(const bool p_enabled) { _material.is_valid() ? _material->set_show_colormap(p_enabled) : void(); }\n\tbool get_show_colormap() const { return _material.is_valid() ? _material->get_show_colormap() : false; }\n\tvoid set_show_roughmap(const bool p_enabled) { _material.is_valid() ? _material->set_show_roughmap(p_enabled) : void(); }\n\tbool get_show_roughmap() const { return _material.is_valid() ? _material->get_show_roughmap() : false; }\n\tvoid set_show_displacement_buffer(const bool p_enabled) { _material.is_valid() ? _material->set_show_displacement_buffer(p_enabled) : void(); }\n\tbool get_show_displacement_buffer() const { return _material.is_valid() ? _material->get_show_displacement_buffer() : false; }\n\n\t// PBR View Aliases\n\tvoid set_show_texture_albedo(const bool p_enabled) { _material.is_valid() ? _material->set_show_texture_albedo(p_enabled) : void(); }\n\tbool get_show_texture_albedo() const { return _material.is_valid() ? _material->get_show_texture_albedo() : false; }\n\tvoid set_show_texture_height(const bool p_enabled) { _material.is_valid() ? _material->set_show_texture_height(p_enabled) : void(); }\n\tbool get_show_texture_height() const { return _material.is_valid() ? _material->get_show_texture_height() : false; }\n\tvoid set_show_texture_normal(const bool p_enabled) { _material.is_valid() ? _material->set_show_texture_normal(p_enabled) : void(); }\n\tbool get_show_texture_normal() const { return _material.is_valid() ? _material->get_show_texture_normal() : false; }\n\tvoid set_show_texture_rough(const bool p_enabled) { _material.is_valid() ? _material->set_show_texture_rough(p_enabled) : void(); }\n\tbool get_show_texture_rough() const { return _material.is_valid() ? _material->get_show_texture_rough() : false; }\n\tvoid set_show_texture_ao(const bool p_enabled) { _material.is_valid() ? _material->set_show_texture_ao(p_enabled) : void(); }\n\tbool get_show_texture_ao() const { return _material.is_valid() ? _material->get_show_texture_ao() : false; }\n\nprotected:\n\tvoid _notification(const int p_what);\n\tvoid _validate_property(PropertyInfo &p_property) const;\n\tstatic void _bind_methods();\n};\n\nVARIANT_ENUM_CAST(Terrain3D::RegionSize);\nVARIANT_ENUM_CAST(Terrain3D::DebugLevel);\n\nconstexpr Terrain3D::DebugLevel MESG = Terrain3D::DebugLevel::MESG;\nconstexpr Terrain3D::DebugLevel WARN = Terrain3D::DebugLevel::WARN;\nconstexpr Terrain3D::DebugLevel ERROR = Terrain3D::DebugLevel::ERROR;\nconstexpr Terrain3D::DebugLevel INFO = Terrain3D::DebugLevel::INFO;\nconstexpr Terrain3D::DebugLevel DEBUG = Terrain3D::DebugLevel::DEBUG;\nconstexpr Terrain3D::DebugLevel EXTREME = Terrain3D::DebugLevel::EXTREME;\n\n#endif // TERRAIN3D_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_asset_resource.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_ASSET_RESOURCE_CLASS_H\n#define TERRAIN3D_ASSET_RESOURCE_CLASS_H\n\n#include <godot_cpp/classes/resource.hpp>\n\n#include \"constants.h\"\n\nclass Terrain3DAssets;\n\n// Parent class of Terrain3DMeshAsset and Terrain3DTextureAsset\nclass Terrain3DAssetResource : public Resource {\n\tfriend class Terrain3DAssets;\n\npublic:\n\tTerrain3DAssetResource() {}\n\t~Terrain3DAssetResource() {}\n\n\tvirtual void initialize() = 0;\n\tvirtual void clear() = 0;\n\tvirtual void set_name(const String &p_name) = 0;\n\tvirtual String get_name() const = 0;\n\tvirtual void set_id(const int p_id) = 0;\n\tvirtual int get_id() const = 0;\n\n\tvirtual void set_highlighted(const bool p_highlighted) = 0;\n\tvirtual bool is_highlighted() const = 0;\n\tvirtual Color get_highlight_color() const = 0;\n\tvirtual Ref<Texture2D> get_thumbnail() const = 0;\n\nprotected:\n\tString _name;\n\tint _id = 0;\n\tbool _highlighted = false;\n\tRef<Texture2D> _thumbnail;\n};\n\n#endif // TERRAIN3D_ASSET_RESOURCE_CLASS_H"
  },
  {
    "path": "src/terrain_3d_assets.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/environment.hpp>\n#include <godot_cpp/classes/image_texture.hpp>\n#include <godot_cpp/classes/rendering_server.hpp>\n#include <godot_cpp/classes/resource_saver.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d_assets.h\"\n#include \"terrain_3d_util.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\nvoid Terrain3DAssets::_swap_ids(const AssetType p_type, const int p_src_id, const int p_dst_id) {\n\tLOG(INFO, \"Swapping asset ID: \", p_src_id, \" and ID: \", p_dst_id);\n\tArray list;\n\tswitch (p_type) {\n\t\tcase TYPE_TEXTURE:\n\t\t\tlist = _texture_list;\n\t\t\tbreak;\n\t\tcase TYPE_MESH:\n\t\t\tlist = _mesh_list;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t}\n\tif (p_src_id < 0 || p_src_id >= list.size()) {\n\t\tLOG(ERROR, \"Source ID out of range: \", p_src_id);\n\t\treturn;\n\t}\n\tRef<Terrain3DAssetResource> res_src = list[p_src_id];\n\tif (res_src.is_null()) {\n\t\tLOG(ERROR, \"Source Asset is null at ID: \", p_src_id);\n\t\treturn;\n\t}\n\t// User can type in any value, -1 means swap with first, >size means swap with last\n\tint dst_id = CLAMP(p_dst_id, 0, list.size() - 1);\n\tif (dst_id == p_src_id) {\n\t\t// They're likely the same due to the clamp, when new ID is <0 or >= array_size)\n\t\tres_src->_id = p_src_id;\n\t\treturn;\n\t}\n\tRef<Terrain3DAssetResource> res_dst = list[dst_id];\n\tif (res_dst.is_valid()) {\n\t\tres_dst->_id = p_src_id;\n\t}\n\tres_src->_id = dst_id;\n\tlist[dst_id] = res_src;\n\tlist[p_src_id] = res_dst;\n\n\tswitch (p_type) {\n\t\tcase TYPE_TEXTURE:\n\t\t\tupdate_texture_list();\n\t\t\tbreak;\n\t\tcase TYPE_MESH:\n\t\t\tif (_terrain) {\n\t\t\t\t_terrain->get_instancer()->swap_ids(p_src_id, dst_id);\n\t\t\t} else {\n\t\t\t\tLOG(ERROR, \"Changing IDs before the terrain is initialized. Meshes on the ground may be wrong.\");\n\t\t\t}\n\t\t\tupdate_mesh_list();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t}\n}\n\n/**\n * _set_asset_list attempts to keep the asset ID as saved in the resource file.\n * But if an ID is invalid or already taken, the new ID is changed to the next available one\n */\nvoid Terrain3DAssets::_set_asset_list(const AssetType p_type, const TypedArray<Terrain3DAssetResource> &p_list) {\n\tArray list;\n\tint max_size;\n\tswitch (p_type) {\n\t\tcase TYPE_TEXTURE:\n\t\t\tlist = _texture_list;\n\t\t\tmax_size = MAX_TEXTURES;\n\t\t\tbreak;\n\t\tcase TYPE_MESH:\n\t\t\tlist = _mesh_list;\n\t\t\tmax_size = MAX_MESHES;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t}\n\tint array_size = CLAMP(p_list.size(), 0, max_size);\n\tlist.resize(array_size);\n\tint filled_id = -1;\n\t// For all provided textures up to MAX SIZE\n\tfor (int i = 0; i < array_size; i++) {\n\t\tRef<Terrain3DAssetResource> res = p_list[i];\n\t\tif (res.is_null()) {\n\t\t\tLOG(ERROR, \"Asset ID: \", i, \" is null\");\n\t\t\tcontinue;\n\t\t}\n\t\tint id = res->get_id();\n\t\t// If saved texture ID is in range and doesn't exist, add it\n\t\tif (id >= 0 && id < array_size && !list[id]) {\n\t\t\tlist[id] = res;\n\t\t} else {\n\t\t\t// Else texture ID is invalid or slot is already taken, insert in first available\n\t\t\tfor (int j = filled_id + 1; j < array_size; j++) {\n\t\t\t\tif (!list[j]) {\n\t\t\t\t\tLOG(ERROR, res->get_class(), \" ID \", id, \" already exists. Setting '\",\n\t\t\t\t\t\t\tres->get_name(), \"' to ID \", j,\n\t\t\t\t\t\t\t\". Textures/Meshes on the ground may be wrong. Review Asset list to ensure each have unique IDs.\");\n\t\t\t\t\tres->set_id(j);\n\t\t\t\t\tlist[j] = res;\n\t\t\t\t\tfilled_id = j;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!res->is_connected(\"id_changed\", callable_mp(this, &Terrain3DAssets::_swap_ids))) {\n\t\t\tLOG(DEBUG, \"Connecting to id_changed, ID: \", id);\n\t\t\tres->connect(\"id_changed\", callable_mp(this, &Terrain3DAssets::_swap_ids));\n\t\t}\n\t\tres->initialize();\n\t}\n\tif (Terrain3D::debug_level >= DEBUG) {\n\t\tfor (int i = 0; i < list.size(); i++) {\n\t\t\tRef<Terrain3DAssetResource> res = list[i];\n\t\t\tint id = res.is_valid() ? res->get_id() : -1;\n\t\t\tString name = res.is_valid() ? res->get_name() : \"\";\n\t\t\tLOG(DEBUG, \"Asset \", i, \": \", name, \", \", res, \", stored ID: \", id);\n\t\t}\n\t}\n}\n\nvoid Terrain3DAssets::_set_asset(const AssetType p_type, const int p_id, const Ref<Terrain3DAssetResource> &p_asset) {\n\tLOG(INFO, \"Setting asset type: \", p_type, \", ID: \", p_id, \", asset: \", p_asset);\n\tArray list;\n\tint max_size;\n\tswitch (p_type) {\n\t\tcase TYPE_TEXTURE:\n\t\t\tlist = _texture_list;\n\t\t\tmax_size = MAX_TEXTURES;\n\t\t\tbreak;\n\t\tcase TYPE_MESH:\n\t\t\tlist = _mesh_list;\n\t\t\tmax_size = MAX_MESHES;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t}\n\n\tif (p_id < 0 || p_id >= max_size) {\n\t\tLOG(ERROR, \"Invalid asset id: \", p_id, \" range is 0-\", max_size);\n\t\treturn;\n\t}\n\tint id = CLAMP(p_id, 0, list.size());\n\t// Delete asset if null\n\tif (p_asset.is_null()) {\n\t\t// If final asset, remove it\n\t\tif (id == list.size() - 1) {\n\t\t\tLOG(DEBUG, \"Deleting asset id: \", id);\n\t\t\tlist.pop_back();\n\t\t} else {\n\t\t\t// Else just clear it\n\t\t\tRef<Terrain3DAssetResource> res = list[id];\n\t\t\tres->clear();\n\t\t\tres->_id = id;\n\t\t}\n\t} else {\n\t\t// Else Insert/Add Asset at end if a high number\n\t\tif (id == list.size()) {\n\t\t\tp_asset->_id = id;\n\t\t\tlist.push_back(p_asset);\n\t\t} else {\n\t\t\t// Else overwrite an existing slot\n\t\t\tp_asset->_id = id;\n\t\t\tlist[id] = p_asset;\n\t\t}\n\t\tif (!p_asset->is_connected(\"id_changed\", callable_mp(this, &Terrain3DAssets::_swap_ids))) {\n\t\t\tLOG(DEBUG, \"Connecting to id_changed\");\n\t\t\tp_asset->connect(\"id_changed\", callable_mp(this, &Terrain3DAssets::_swap_ids));\n\t\t}\n\t\tp_asset->initialize();\n\t}\n}\n\nvoid Terrain3DAssets::_update_texture_files() {\n\tIS_INIT(VOID);\n\tLOG(DEBUG, \"Received texture_changed signal\");\n\t_generated_albedo_textures.clear();\n\t_generated_normal_textures.clear();\n\tif (_texture_list.is_empty()) {\n\t\tLOG(DEBUG, \"Emitting textures_changed\");\n\t\temit_signal(\"textures_changed\");\n\t\treturn;\n\t}\n\n\t// Detect image sizes and formats\n\tLOG(DEBUG, \"Validating texture sizes\");\n\tVector2i albedo_size = V2I_ZERO;\n\tVector2i normal_size = V2I_ZERO;\n\tImage::Format albedo_format = Image::FORMAT_MAX;\n\tImage::Format normal_format = Image::FORMAT_MAX;\n\tbool albedo_mipmaps = true;\n\tbool normal_mipmaps = true;\n\t_terrain->set_warning(WARN_ALL, false);\n\n\tfor (const Ref<Terrain3DTextureAsset> &ta : _texture_list) {\n\t\tif (ta.is_null()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tRef<Texture2D> albedo_tex = ta->_albedo_texture;\n\t\tif (albedo_tex.is_valid()) {\n\t\t\tVector2i tex_size = albedo_tex->get_size();\n\t\t\tRef<Image> img = albedo_tex->get_image();\n\t\t\tImage::Format format = img->get_format();\n\t\t\tbool mipmaps = img->has_mipmaps();\n\n\t\t\t// If this is the first valid texture, set expected size and format for the arrays\n\t\t\tif (albedo_format == Image::FORMAT_MAX) {\n\t\t\t\talbedo_size = tex_size;\n\t\t\t\talbedo_format = format;\n\t\t\t\talbedo_mipmaps = mipmaps;\n\t\t\t} else { // else validate against first texture\n\t\t\t\tif (tex_size != albedo_size) {\n\t\t\t\t\t_terrain->set_warning(WARN_MISMATCHED_SIZE, true);\n\t\t\t\t\tLOG(ERROR, \"Texture ID \", ta->get_id(), \" albedo size: \", tex_size, \" doesn't match size of first texture: \", albedo_size, \". They must be identical. Read Texture Prep in docs.\");\n\t\t\t\t}\n\t\t\t\tif (format != albedo_format) {\n\t\t\t\t\t_terrain->set_warning(WARN_MISMATCHED_FORMAT, true);\n\t\t\t\t\tLOG(ERROR, \"Texture ID \", ta->get_id(), \" albedo format: \", format, \" doesn't match format of first texture: \", albedo_format, \". They must be identical. Read Texture Prep in docs.\");\n\t\t\t\t}\n\t\t\t\tif (mipmaps != albedo_mipmaps) {\n\t\t\t\t\t_terrain->set_warning(WARN_MISMATCHED_MIPMAPS, true);\n\t\t\t\t\tLOG(ERROR, \"Texture ID \", ta->get_id(), \" albedo mipmap setting (\", mipmaps, \") doesn't match first texture (\", albedo_mipmaps, \"). They must be identical. Read Texture Prep in docs.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tRef<Texture2D> normal_tex = ta->_normal_texture;\n\t\tif (normal_tex.is_valid()) {\n\t\t\tVector2i tex_size = normal_tex->get_size();\n\t\t\tRef<Image> img = normal_tex->get_image();\n\t\t\tImage::Format format = img->get_format();\n\t\t\tbool mipmaps = img->has_mipmaps();\n\n\t\t\t// If this is the first valid texture, set expected size and format for the arrays\n\t\t\tif (normal_format == Image::FORMAT_MAX) {\n\t\t\t\tnormal_size = tex_size;\n\t\t\t\tnormal_format = format;\n\t\t\t\tnormal_mipmaps = mipmaps;\n\t\t\t} else { // else validate against first texture\n\t\t\t\tif (tex_size != normal_size) {\n\t\t\t\t\t_terrain->set_warning(WARN_MISMATCHED_SIZE, true);\n\t\t\t\t\tLOG(ERROR, \"Texture ID \", ta->get_id(), \" normal size: \", tex_size, \" doesn't match size of first texture: \", normal_size, \". They must be identical. Read Texture Prep in docs.\");\n\t\t\t\t}\n\t\t\t\tif (format != normal_format) {\n\t\t\t\t\t_terrain->set_warning(WARN_MISMATCHED_FORMAT, true);\n\t\t\t\t\tLOG(ERROR, \"Texture ID \", ta->get_id(), \" normal format: \", format, \" doesn't match format of first texture: \", normal_format, \". They must be identical. Read Texture Prep in docs.\");\n\t\t\t\t}\n\t\t\t\tif (mipmaps != normal_mipmaps) {\n\t\t\t\t\t_terrain->set_warning(WARN_MISMATCHED_MIPMAPS, true);\n\t\t\t\t\tLOG(ERROR, \"Texture ID \", ta->get_id(), \" normal mipmap setting (\", mipmaps, \") doesn't match first texture (\", albedo_mipmaps, \"). They must be identical. Read Texture Prep in docs.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (_terrain->get_warnings()) {\n\t\treturn;\n\t}\n\n\t// Setup defaults for generated texture\n\tif (normal_size == V2I_ZERO) {\n\t\tnormal_size = albedo_size;\n\t} else if (albedo_size == V2I_ZERO) {\n\t\talbedo_size = normal_size;\n\t}\n\tif (albedo_size == V2I_ZERO) {\n\t\talbedo_size = V2I(1024);\n\t\tnormal_size = albedo_size;\n\t}\n\n\t// Generate TextureArrays and replace nulls with a empty image\n\n\tif (_generated_albedo_textures.is_dirty() && albedo_size != V2I_ZERO) {\n\t\tLOG(INFO, \"Regenerating albedo texture array\");\n\t\tArray albedo_texture_array;\n\t\tfor (const Ref<Terrain3DTextureAsset> &ta : _texture_list) {\n\t\t\tif (ta.is_null()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRef<Texture2D> tex = ta->_albedo_texture;\n\t\t\tRef<Image> img;\n\n\t\t\tif (tex.is_null()) {\n\t\t\t\timg = Util::get_filled_image(albedo_size, COLOR_CHECKED, albedo_mipmaps, albedo_format);\n\t\t\t\tLOG(DEBUG, \"Texture ID \", ta->get_id(), \" albedo is null. Creating a new one. Format: \", img->get_format());\n\t\t\t\tta->set_albedo_texture(ImageTexture::create_from_image(img));\n\t\t\t} else {\n\t\t\t\timg = tex->get_image();\n\t\t\t\tLOG(EXTREME, \"Texture ID \", ta->get_id(), \" albedo is valid. Format: \", img->get_format());\n\t\t\t\tif (!IS_EDITOR && tex->get_path().contains(\"ImageTexture\")) {\n\t\t\t\t\tLOG(WARN, \"Texture ID \", ta->get_id(), \" albedo is saved in the scene. Save it as a file and link it.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\talbedo_texture_array.push_back(img);\n\t\t}\n\t\tif (!albedo_texture_array.is_empty()) {\n\t\t\t_generated_albedo_textures.create(albedo_texture_array);\n\t\t}\n\t}\n\n\tif (_generated_normal_textures.is_dirty() && normal_size != V2I_ZERO) {\n\t\tLOG(INFO, \"Regenerating normal texture arrays\");\n\n\t\tArray normal_texture_array;\n\n\t\tfor (const Ref<Terrain3DTextureAsset> &ta : _texture_list) {\n\t\t\tif (ta.is_null()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRef<Texture2D> tex = ta->_normal_texture;\n\t\t\tRef<Image> img;\n\n\t\t\tif (tex.is_null()) {\n\t\t\t\timg = Util::get_filled_image(normal_size, COLOR_NORMAL, normal_mipmaps, normal_format);\n\t\t\t\tLOG(DEBUG, \"Texture ID \", ta->get_id(), \" normal is null. Creating a new one. Format: \", img->get_format());\n\t\t\t\tta->_normal_texture = ImageTexture::create_from_image(img);\n\t\t\t} else {\n\t\t\t\timg = tex->get_image();\n\t\t\t\tLOG(EXTREME, \"Texture ID \", ta->get_id(), \" normal is valid. Format: \", img->get_format());\n\t\t\t\tif (!IS_EDITOR && tex->get_path().contains(\"ImageTexture\")) {\n\t\t\t\t\tLOG(WARN, \"Texture ID \", ta->get_id(), \" normal is saved in the scene. Save it as a file and link it.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tnormal_texture_array.push_back(img);\n\t\t}\n\t\tif (!normal_texture_array.is_empty()) {\n\t\t\t_generated_normal_textures.create(normal_texture_array);\n\t\t}\n\t}\n\tLOG(DEBUG, \"Emitting textures_changed\");\n\temit_signal(\"textures_changed\");\n}\n\nvoid Terrain3DAssets::_update_texture_settings() {\n\tLOG(DEBUG, \"Received setting_changed signal\");\n\tif (!_texture_list.is_empty()) {\n\t\tLOG(INFO, \"Updating texture asset settings arrays\");\n\t\t_texture_colors.clear();\n\t\t_texture_normal_depths.clear();\n\t\t_texture_ao_strengths.clear();\n\t\t_texture_ao_light_affects.clear();\n\t\t_texture_roughness_mods.clear();\n\t\t_texture_uv_scales.clear();\n\t\t_texture_detiles.clear();\n\t\t_texture_displacements.clear();\n\n\t\tfor (const Ref<Terrain3DTextureAsset> &ta : _texture_list) {\n\t\t\tif (ta.is_null()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ta->is_highlighted()) {\n\t\t\t\t_texture_colors.push_back(ta->get_highlight_color());\n\t\t\t} else {\n\t\t\t\t_texture_colors.push_back(ta->get_albedo_color());\n\t\t\t}\n\t\t\t_texture_normal_depths.push_back(ta->get_normal_depth());\n\t\t\t_texture_ao_strengths.push_back(ta->get_ao_strength());\n\t\t\t_texture_ao_light_affects.push_back(ta->get_ao_light_affect());\n\t\t\t_texture_roughness_mods.push_back(ta->get_roughness());\n\t\t\t_texture_uv_scales.push_back(ta->get_uv_scale());\n\t\t\t_texture_detiles.push_back(Vector2(ta->get_detiling_rotation(), ta->get_detiling_shift()));\n\t\t\t_texture_displacements.push_back(Vector2(ta->get_displacement_offset(), ta->get_displacement_scale()));\n\t\t}\n\t}\n\tLOG(DEBUG, \"Emitting textures_changed\");\n\temit_signal(\"textures_changed\");\n}\n\nvoid Terrain3DAssets::_update_mesh(const int p_id) {\n\tif (p_id < 0 || p_id >= _mesh_list.size()) {\n\t\tLOG(ERROR, \"Invalid mesh ID: \", p_id);\n\t\treturn;\n\t}\n\tRef<Terrain3DMeshAsset> ma = _mesh_list[p_id];\n\tcreate_mesh_thumbnails(p_id, V2I(512), true);\n\tIS_INSTANCER_INIT(VOID);\n\tTerrain3DInstancer *instancer = _terrain->get_instancer();\n\tinstancer->update_mmis(p_id, V2I_MAX, false);\n}\n\nvoid Terrain3DAssets::_setup_thumbnail_creation() {\n\tIS_INIT(VOID);\n\tif (_scenario.is_valid()) {\n\t\treturn;\n\t}\n\tLOG(INFO, \"Setting up mesh thumbnail creation viewports\");\n\t// Setup Mesh preview environment\n\t_scenario = RS->scenario_create();\n\n\t_viewport = RS->viewport_create();\n\tRS->viewport_set_update_mode(_viewport, RenderingServer::VIEWPORT_UPDATE_DISABLED);\n\tRS->viewport_set_scenario(_viewport, _scenario);\n\tRS->viewport_set_size(_viewport, 128, 128);\n\tRS->viewport_set_transparent_background(_viewport, true);\n\tRS->viewport_set_active(_viewport, true);\n\t_viewport_texture = RS->viewport_get_texture(_viewport);\n\n\t_camera = RS->camera_create();\n\tRS->viewport_attach_camera(_viewport, _camera);\n\tRS->camera_set_transform(_camera, Transform3D(Basis(), Vector3(0, 0, 3)));\n\tRS->camera_set_orthogonal(_camera, 1.0, 0.01, 1000.0);\n\n\t_key_light = RS->directional_light_create();\n\t_key_light_instance = RS->instance_create2(_key_light, _scenario);\n\tRS->instance_set_transform(_key_light_instance, Transform3D().looking_at(V3(-1), V3_UP));\n\n\t_fill_light = RS->directional_light_create();\n\tRS->light_set_color(_fill_light, Color(0.3, 0.3, 0.3));\n\t_fill_light_instance = RS->instance_create2(_fill_light, _scenario);\n\tRS->instance_set_transform(_fill_light_instance, Transform3D().looking_at(V3_UP, Vector3(0, 0, 1)));\n\n\t_mesh_instance = RS->instance_create();\n\tRS->instance_set_scenario(_mesh_instance, _scenario);\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DAssets::initialize(Terrain3D *p_terrain) {\n\tif (p_terrain) {\n\t\t_terrain = p_terrain;\n\t} else {\n\t\tLOG(ERROR, \"Initialization failed, p_terrain is null\");\n\t\treturn;\n\t}\n\tLOG(INFO, \"Initializing assets\");\n\tif (IS_EDITOR) {\n\t\t_setup_thumbnail_creation();\n\t}\n\n\t// Update assets\n\tupdate_texture_list();\n\tupdate_mesh_list();\n}\n\nvoid Terrain3DAssets::uninitialize() {\n\tLOG(INFO, \"Uninitializing assets\");\n\t_terrain = nullptr;\n}\n\nvoid Terrain3DAssets::destroy() {\n\tLOG(INFO, \"Destroying assets\");\n\t_terrain = nullptr;\n\t_generated_albedo_textures.clear();\n\t_generated_normal_textures.clear();\n\t_texture_list.clear();\n\t_mesh_list.clear();\n\t_texture_colors.clear();\n\t_texture_normal_depths.clear();\n\t_texture_ao_strengths.clear();\n\t_texture_ao_light_affects.clear();\n\t_texture_roughness_mods.clear();\n\t_texture_uv_scales.clear();\n\t_texture_detiles.clear();\n\n\tif (_scenario.is_valid()) {\n\t\tRS->free_rid(_mesh_instance);\n\t\tRS->free_rid(_fill_light_instance);\n\t\tRS->free_rid(_fill_light);\n\t\tRS->free_rid(_key_light_instance);\n\t\tRS->free_rid(_key_light);\n\t\tRS->free_rid(_camera);\n\t\tRS->free_rid(_viewport);\n\t\tRS->free_rid(_scenario);\n\t\t_mesh_instance = RID();\n\t\t_fill_light_instance = RID();\n\t\t_fill_light = RID();\n\t\t_key_light_instance = RID();\n\t\t_key_light = RID();\n\t\t_camera = RID();\n\t\t_viewport = RID();\n\t\t_scenario = RID();\n\t}\n}\n\n// Called when creating a new asset\nvoid Terrain3DAssets::set_texture_asset(const int p_id, const Ref<Terrain3DTextureAsset> &p_texture) {\n\tif (p_id < 0 || p_id >= MAX_TEXTURES) {\n\t\tLOG(ERROR, \"Invalid texture id: \", p_id, \" range is 0-\", MAX_TEXTURES - 1);\n\t\treturn;\n\t}\n\tif (p_id < _texture_list.size() && _texture_list[p_id] == p_texture) {\n\t\treturn;\n\t}\n\tLOG(INFO, \"Setting texture id: \", p_id);\n\t_set_asset(TYPE_TEXTURE, p_id, p_texture);\n\tupdate_texture_list();\n}\n\n// Called when loading a list of assets from an assets resource file on disk\nvoid Terrain3DAssets::set_texture_list(const TypedArray<Terrain3DTextureAsset> &p_texture_list) {\n\tLOG(INFO, \"Setting texture list with \", p_texture_list.size(), \" entries\");\n\tif (!differs(_texture_list, p_texture_list)) {\n\t\treturn;\n\t}\n\t_set_asset_list(TYPE_TEXTURE, p_texture_list);\n\tupdate_texture_list();\n}\n\nvoid Terrain3DAssets::clear_textures(const bool p_update) {\n\tLOG(INFO, \"Clearing texture list\");\n\t_texture_list.clear();\n\tif (p_update) {\n\t\tupdate_texture_list();\n\t}\n}\n\nvoid Terrain3DAssets::update_texture_list() {\n\tLOG(INFO, \"Reconnecting texture signals\");\n\tfor (const Ref<Terrain3DTextureAsset> &ta : _texture_list) {\n\t\tif (ta.is_null()) {\n\t\t\tLOG(ERROR, \"Null TextureAsset found at index: \", _texture_list.find(ta));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!ta->is_connected(\"file_changed\", callable_mp(this, &Terrain3DAssets::_update_texture_files))) {\n\t\t\tLOG(DEBUG, \"Connecting file_changed signal\");\n\t\t\tta->connect(\"file_changed\", callable_mp(this, &Terrain3DAssets::_update_texture_files));\n\t\t}\n\t\tif (!ta->is_connected(\"setting_changed\", callable_mp(this, &Terrain3DAssets::_update_texture_settings))) {\n\t\t\tLOG(DEBUG, \"Connecting setting_changed signal\");\n\t\t\tta->connect(\"setting_changed\", callable_mp(this, &Terrain3DAssets::_update_texture_settings));\n\t\t}\n\t}\n\t_update_texture_files();\n\t_update_texture_settings();\n}\n\n// Called when creating a new asset\nvoid Terrain3DAssets::set_mesh_asset(const int p_id, const Ref<Terrain3DMeshAsset> &p_mesh_asset) {\n\tif (p_id < 0 || p_id >= MAX_MESHES) {\n\t\tLOG(ERROR, \"Invalid mesh id: \", p_id, \" range is 0-\", MAX_MESHES - 1);\n\t\treturn;\n\t}\n\tif (p_id < _mesh_list.size() && _mesh_list[p_id] == p_mesh_asset) {\n\t\treturn;\n\t}\n\tLOG(INFO, \"Setting mesh id: \", p_id, \", \", p_mesh_asset);\n\tif (p_mesh_asset.is_null()) {\n\t\tIS_INSTANCER_INIT(VOID);\n\t\t_terrain->get_instancer()->clear_by_mesh(p_id);\n\t}\n\t_set_asset(TYPE_MESH, p_id, p_mesh_asset);\n\tupdate_mesh_list();\n}\n\n// Called when loading a list of assets from an assets resource file on disk\nvoid Terrain3DAssets::set_mesh_list(const TypedArray<Terrain3DMeshAsset> &p_mesh_list) {\n\tLOG(INFO, \"Setting mesh list with \", p_mesh_list.size(), \" entries\");\n\tif (!differs(_mesh_list, p_mesh_list)) {\n\t\treturn;\n\t}\n\t_set_asset_list(TYPE_MESH, p_mesh_list);\n\tupdate_mesh_list();\n}\n\n// p_id = -1 for all meshes\n// Adapted from godot\\editor\\plugins\\editor_preview_plugins.cpp:EditorMeshPreviewPlugin\nvoid Terrain3DAssets::create_mesh_thumbnails(const int p_id, const Vector2i &p_size, const bool p_force) {\n\tLOG(INFO, \"Creating mesh thumbnails for ID: \", p_id, \", size: \", p_size);\n\tint max = get_mesh_count();\n\tif (p_id < -1 || p_id >= max) {\n\t\treturn;\n\t}\n\tif (!_mesh_instance.is_valid()) {\n\t\t_setup_thumbnail_creation();\n\t}\n\tint start, end;\n\tif (p_id < 0) {\n\t\tstart = 0;\n\t\tend = max;\n\t} else {\n\t\tstart = CLAMP(p_id, 0, max - 1);\n\t\tend = CLAMP(p_id + 1, 0, max);\n\t}\n\tVector2i size = CLAMP(p_size, V2I(2), V2I(4096));\n\n\tLOG(DEBUG, \"Creating thumbnails for ids: \", start, \" through \", end - 1);\n\tfor (int i = start; i < end; i++) {\n\t\tRef<Terrain3DMeshAsset> ma = get_mesh_asset(i);\n\t\tLOG(EXTREME, i, \": Getting Terrain3DMeshAsset: \", ptr_to_str(*ma));\n\t\tif (ma.is_null()) {\n\t\t\tLOG(ERROR, i, \": Terrain3DMeshAsset is null, skipping\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (!p_force && ma->get_thumbnail().is_valid()) {\n\t\t\tLOG(EXTREME, \"Thumbnail already generated, skipping\");\n\t\t\tcontinue;\n\t\t}\n\t\t// Setup mesh\n\t\tRef<Mesh> mesh = ma->get_mesh(0);\n\t\tLOG(EXTREME, i, \": Getting Mesh 0: \", mesh);\n\t\tif (mesh.is_null()) {\n\t\t\tLOG(ERROR, i, \": Mesh is null, skipping\");\n\t\t\tcontinue;\n\t\t}\n\t\tRS->instance_set_base(_mesh_instance, mesh->get_rid());\n\n\t\t// Setup material\n\t\tRef<Material> mat = ma->get_material_override();\n\t\tRID rid = mat.is_valid() ? mat->get_rid() : RID();\n\t\tRS->instance_geometry_set_material_override(_mesh_instance, rid);\n\t\tmat = ma->get_material_overlay();\n\t\trid = mat.is_valid() ? mat->get_rid() : RID();\n\t\tRS->instance_geometry_set_material_overlay(_mesh_instance, rid);\n\n\t\t// Setup scene\n\t\tAABB aabb = mesh->get_aabb();\n\t\tVector3 ofs = aabb.get_center();\n\t\taabb.position -= ofs;\n\t\tTransform3D xform;\n\t\txform.basis = Basis().rotated(V3_UP, -Math_PI * 0.125f);\n\t\txform.basis = Basis().rotated(Vector3(1.f, 0.f, 0.f), Math_PI * 0.125f) * xform.basis;\n\t\tAABB rot_aabb = xform.xform(aabb);\n\t\treal_t m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5f;\n\t\tif (m == 0.f) {\n\t\t\tm = 1.f;\n\t\t}\n\t\tm = .5f / m;\n\t\txform.basis.scale(V3(m));\n\t\txform.origin = -xform.basis.xform(ofs);\n\t\txform.origin.z -= rot_aabb.size.z * 2.f;\n\t\tRS->instance_set_transform(_mesh_instance, xform);\n\n\t\t// Capture image\n\t\tRS->viewport_set_size(_viewport, size.x, size.y);\n\t\tRS->viewport_set_update_mode(_viewport, RenderingServer::VIEWPORT_UPDATE_ONCE);\n\t\tRS->force_draw();\n\n\t\tRef<Image> img = RS->texture_2d_get(_viewport_texture);\n\t\tRS->instance_set_base(_mesh_instance, RID()); // Clear mesh\n\t\tif (img.is_valid()) {\n\t\t\tLOG(EXTREME, i, \": Retrieving image: \", img, \" size: \", img->get_size(), \" format: \", img->get_format());\n\t\t\tma->set_thumbnail(ImageTexture::create_from_image(img));\n\t\t} else {\n\t\t\tLOG(ERROR, \"_viewport_texture is null. Couldn't create thumbnail picture\");\n\t\t}\n\t}\n\treturn;\n}\n\nvoid Terrain3DAssets::update_mesh_list() {\n\tIS_INSTANCER_INIT(VOID);\n\tLOG(INFO, \"Updating mesh list\");\n\tif (_mesh_list.size() == 0) {\n\t\tLOG(DEBUG, \"Mesh list empty, clearing instancer and adding a default mesh\");\n\t\t_terrain->get_instancer()->destroy();\n\t\tRef<Terrain3DMeshAsset> new_ma;\n\t\tnew_ma.instantiate();\n\t\tset_mesh_asset(0, new_ma);\n\t}\n\tLOG(DEBUG, \"Reconnecting mesh instance signals\");\n\tfor (Ref<Terrain3DMeshAsset> ma : _mesh_list) {\n\t\tif (ma.is_null()) {\n\t\t\tLOG(ERROR, \"Null Terrain3DMeshAsset found at index \", _mesh_list.find(ma));\n\t\t\tcontinue;\n\t\t}\n\t\tif (ma->get_mesh().is_null()) {\n\t\t\tLOG(ERROR, \"Terrain3DMeshAsset has null mesh at index \", _mesh_list.find(ma));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!ma->is_connected(\"instancer_setting_changed\", callable_mp(this, &Terrain3DAssets::_update_mesh))) {\n\t\t\tLOG(DEBUG, \"Connecting instancer_setting_changed signal to _update_mesh\");\n\t\t\tma->connect(\"instancer_setting_changed\", callable_mp(this, &Terrain3DAssets::_update_mesh));\n\t\t}\n\t}\n\tLOG(DEBUG, \"Emitting meshes_changed\");\n\temit_signal(\"meshes_changed\");\n}\n\nvoid Terrain3DAssets::load_pending_meshes() {\n\t// Reload meshes for all mesh assets that have changed. Used by the instancer after clearing\n\tfor (const Ref<Terrain3DMeshAsset> &ma : _mesh_list) {\n\t\tif (ma.is_valid() && ma->is_scene_file_pending()) {\n\t\t\tma->commit_meshes();\n\t\t}\n\t}\n}\n\nError Terrain3DAssets::save(const String &p_path) {\n\tif (p_path.is_empty() && get_path().is_empty()) {\n\t\treturn ERR_FILE_NOT_FOUND;\n\t}\n\tif (!p_path.is_empty()) {\n\t\tLOG(DEBUG, \"Setting file path to \", p_path);\n\t\ttake_over_path(p_path);\n\t}\n\t// Save to external resource file if specified\n\tError err = OK;\n\tString path = get_path();\n\tif (path.get_extension() == \"tres\" || path.get_extension() == \"res\") {\n\t\tLOG(DEBUG, \"Attempting to save external file: \" + path);\n\t\terr = ResourceSaver::get_singleton()->save(this, path, ResourceSaver::FLAG_COMPRESS);\n\t\tif (err == OK) {\n\t\t\tLOG(INFO, \"File saved successfully: \", path);\n\t\t} else {\n\t\t\tLOG(ERROR, \"Cannot save file: \", path, \". Error code: \", ERROR, \". Look up @GlobalScope Error enum in the Godot docs\");\n\t\t}\n\t}\n\treturn err;\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DAssets::_bind_methods() {\n\tBIND_ENUM_CONSTANT(TYPE_TEXTURE);\n\tBIND_ENUM_CONSTANT(TYPE_MESH);\n\tBIND_CONSTANT(MAX_TEXTURES);\n\tBIND_CONSTANT(MAX_MESHES);\n\n\tClassDB::bind_method(D_METHOD(\"set_texture_asset\", \"id\", \"texture\"), &Terrain3DAssets::set_texture_asset);\n\tClassDB::bind_method(D_METHOD(\"get_texture_asset\", \"id\"), &Terrain3DAssets::get_texture_asset);\n\tClassDB::bind_method(D_METHOD(\"set_texture_list\", \"texture_list\"), &Terrain3DAssets::set_texture_list);\n\tClassDB::bind_method(D_METHOD(\"get_texture_list\"), &Terrain3DAssets::get_texture_list);\n\tClassDB::bind_method(D_METHOD(\"get_texture_count\"), &Terrain3DAssets::get_texture_count);\n\tClassDB::bind_method(D_METHOD(\"get_albedo_array_rid\"), &Terrain3DAssets::get_albedo_array_rid);\n\tClassDB::bind_method(D_METHOD(\"get_normal_array_rid\"), &Terrain3DAssets::get_normal_array_rid);\n\tClassDB::bind_method(D_METHOD(\"get_texture_colors\"), &Terrain3DAssets::get_texture_colors);\n\tClassDB::bind_method(D_METHOD(\"get_texture_normal_depths\"), &Terrain3DAssets::get_texture_normal_depths);\n\tClassDB::bind_method(D_METHOD(\"get_texture_ao_strengths\"), &Terrain3DAssets::get_texture_ao_strengths);\n\tClassDB::bind_method(D_METHOD(\"get_texture_ao_light_affects\"), &Terrain3DAssets::get_texture_ao_light_affects);\n\tClassDB::bind_method(D_METHOD(\"get_texture_roughness_mods\"), &Terrain3DAssets::get_texture_roughness_mods);\n\tClassDB::bind_method(D_METHOD(\"get_texture_uv_scales\"), &Terrain3DAssets::get_texture_uv_scales);\n\tClassDB::bind_method(D_METHOD(\"get_texture_detiles\"), &Terrain3DAssets::get_texture_detiles);\n\tClassDB::bind_method(D_METHOD(\"get_texture_displacements\"), &Terrain3DAssets::get_texture_displacements);\n\tClassDB::bind_method(D_METHOD(\"clear_textures\", \"update\"), &Terrain3DAssets::clear_textures, DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"update_texture_list\"), &Terrain3DAssets::update_texture_list);\n\n\tClassDB::bind_method(D_METHOD(\"set_mesh_asset\", \"id\", \"mesh\"), &Terrain3DAssets::set_mesh_asset);\n\tClassDB::bind_method(D_METHOD(\"get_mesh_asset\", \"id\"), &Terrain3DAssets::get_mesh_asset);\n\tClassDB::bind_method(D_METHOD(\"set_mesh_list\", \"mesh_list\"), &Terrain3DAssets::set_mesh_list);\n\tClassDB::bind_method(D_METHOD(\"get_mesh_list\"), &Terrain3DAssets::get_mesh_list);\n\tClassDB::bind_method(D_METHOD(\"get_mesh_count\"), &Terrain3DAssets::get_mesh_count);\n\tClassDB::bind_method(D_METHOD(\"create_mesh_thumbnails\", \"id\", \"size\", \"force\"), &Terrain3DAssets::create_mesh_thumbnails, DEFVAL(-1), DEFVAL(V2I(512)), DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"update_mesh_list\"), &Terrain3DAssets::update_mesh_list);\n\n\tClassDB::bind_method(D_METHOD(\"save\", \"path\"), &Terrain3DAssets::save, DEFVAL(\"\"));\n\n\tint ro_flags = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;\n\tADD_PROPERTY(PropertyInfo(Variant::ARRAY, \"mesh_list\", PROPERTY_HINT_ARRAY_TYPE, \"Terrain3DMeshAsset\", ro_flags), \"set_mesh_list\", \"get_mesh_list\");\n\tADD_PROPERTY(PropertyInfo(Variant::ARRAY, \"texture_list\", PROPERTY_HINT_ARRAY_TYPE, \"Terrain3DTextureAsset\", ro_flags), \"set_texture_list\", \"get_texture_list\");\n\n\tADD_SIGNAL(MethodInfo(\"meshes_changed\"));\n\tADD_SIGNAL(MethodInfo(\"textures_changed\"));\n}\n"
  },
  {
    "path": "src/terrain_3d_assets.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_ASSETS_CLASS_H\n#define TERRAIN3D_ASSETS_CLASS_H\n\n#include \"constants.h\"\n#include \"generated_texture.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_mesh_asset.h\"\n#include \"terrain_3d_texture_asset.h\"\n\nclass Terrain3D;\nclass Terrain3DInstancer;\n\nclass Terrain3DAssets : public Resource {\n\tGDCLASS(Terrain3DAssets, Resource);\n\tCLASS_NAME();\n\npublic: // Constants\n\tenum AssetType {\n\t\tTYPE_TEXTURE,\n\t\tTYPE_MESH,\n\t};\n\n\tstatic inline const int MAX_TEXTURES = 32;\n\tstatic inline const int MAX_MESHES = 256;\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\n\tTypedArray<Terrain3DTextureAsset> _texture_list;\n\tTypedArray<Terrain3DMeshAsset> _mesh_list;\n\n\tGeneratedTexture _generated_albedo_textures;\n\tGeneratedTexture _generated_normal_textures;\n\tPackedColorArray _texture_colors;\n\tPackedFloat32Array _texture_normal_depths;\n\tPackedFloat32Array _texture_ao_strengths;\n\tPackedFloat32Array _texture_ao_light_affects;\n\tPackedFloat32Array _texture_roughness_mods;\n\tPackedFloat32Array _texture_uv_scales;\n\tPackedVector2Array _texture_detiles;\n\tPackedVector2Array _texture_displacements;\n\n\t// Mesh Thumbnail Generation\n\tRID _scenario;\n\tRID _viewport;\n\tRID _viewport_texture;\n\tRID _camera;\n\tRID _key_light;\n\tRID _key_light_instance;\n\tRID _fill_light;\n\tRID _fill_light_instance;\n\tRID _mesh_instance;\n\n\tvoid _swap_ids(const AssetType p_type, const int p_src_id, const int p_dst_id);\n\tvoid _set_asset_list(const AssetType p_type, const TypedArray<Terrain3DAssetResource> &p_list);\n\tvoid _set_asset(const AssetType p_type, const int p_id, const Ref<Terrain3DAssetResource> &p_asset);\n\n\tvoid _update_texture_files();\n\tvoid _update_texture_settings();\n\tvoid _update_mesh(const int p_id);\n\tvoid _setup_thumbnail_creation();\n\npublic:\n\tTerrain3DAssets() {}\n\t~Terrain3DAssets() { destroy(); }\n\tvoid initialize(Terrain3D *p_terrain);\n\tbool is_initialized() { return _terrain != nullptr; }\n\tvoid uninitialize();\n\tvoid destroy();\n\n\tvoid set_texture_asset(const int p_id, const Ref<Terrain3DTextureAsset> &p_texture);\n\tRef<Terrain3DTextureAsset> get_texture_asset(const int p_id) const;\n\tvoid set_texture_list(const TypedArray<Terrain3DTextureAsset> &p_texture_list);\n\tTypedArray<Terrain3DTextureAsset> get_texture_list() const { return _texture_list; }\n\tint get_texture_count() const { return _texture_list.size(); }\n\tint get_generated_array_size() const { return _generated_albedo_textures.size(); }\n\tRID get_albedo_array_rid() const { return _generated_albedo_textures.get_rid(); }\n\tRID get_normal_array_rid() const { return _generated_normal_textures.get_rid(); }\n\tPackedColorArray get_texture_colors() const { return _texture_colors; }\n\tPackedFloat32Array get_texture_normal_depths() const { return _texture_normal_depths; }\n\tPackedFloat32Array get_texture_ao_strengths() const { return _texture_ao_strengths; }\n\tPackedFloat32Array get_texture_ao_light_affects() const { return _texture_ao_light_affects; }\n\tPackedFloat32Array get_texture_roughness_mods() const { return _texture_roughness_mods; }\n\tPackedFloat32Array get_texture_uv_scales() const { return _texture_uv_scales; }\n\tPackedVector2Array get_texture_detiles() const { return _texture_detiles; }\n\tPackedVector2Array get_texture_displacements() const { return _texture_displacements; }\n\tvoid clear_textures(const bool p_update = false);\n\tvoid update_texture_list();\n\n\tvoid set_mesh_asset(const int p_id, const Ref<Terrain3DMeshAsset> &p_mesh_asset);\n\tRef<Terrain3DMeshAsset> get_mesh_asset(const int p_id) const;\n\tvoid set_mesh_list(const TypedArray<Terrain3DMeshAsset> &p_mesh_list);\n\tTypedArray<Terrain3DMeshAsset> get_mesh_list() const { return _mesh_list; }\n\tint get_mesh_count() const { return _mesh_list.size(); }\n\tvoid create_mesh_thumbnails(const int p_id = -1, const Vector2i &p_size = V2I(512), const bool p_force = false);\n\tvoid update_mesh_list();\n\tvoid load_pending_meshes();\n\n\tError save(const String &p_path = \"\");\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nVARIANT_ENUM_CAST(Terrain3DAssets::AssetType);\n\ninline Ref<Terrain3DTextureAsset> Terrain3DAssets::get_texture_asset(const int p_id) const {\n\tif (p_id >= 0 && p_id < _texture_list.size()) {\n\t\treturn _texture_list[p_id];\n\t}\n\treturn Ref<Terrain3DTextureAsset>();\n}\n\ninline Ref<Terrain3DMeshAsset> Terrain3DAssets::get_mesh_asset(const int p_id) const {\n\tif (p_id >= 0 && p_id < _mesh_list.size()) {\n\t\treturn _mesh_list[p_id];\n\t}\n\treturn Ref<Terrain3DMeshAsset>();\n}\n\n#endif // TERRAIN3D_ASSETS_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_collision.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/height_map_shape3d.hpp>\n#include <godot_cpp/classes/time.hpp>\n#include <godot_cpp/classes/world3d.hpp>\n\n#include <godot_cpp/classes/scene_tree.hpp>\n\n#include \"constants.h\"\n#include \"logger.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_collision.h\"\n#include \"terrain_3d_data.h\"\n#include \"terrain_3d_util.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\n// Calculates shape data from top left position. Assumes descaled and snapped.\nDictionary Terrain3DCollision::_get_shape_data(const Vector2i &p_position, const int p_size) {\n\tIS_DATA_INIT_MESG(\"Terrain not initialized\", Dictionary());\n\tconst Terrain3DData *data = _terrain->get_data();\n\n\tconst Ref<Terrain3DMaterial> material = _terrain->get_material();\n\tif (!material.is_valid()) {\n\t\treturn Dictionary();\n\t}\n\tconst Terrain3DMaterial::WorldBackground bg_mode = material->get_world_background();\n\tconst bool is_bg_flat_or_noise = bg_mode == Terrain3DMaterial::WorldBackground::FLAT || bg_mode == Terrain3DMaterial::WorldBackground::NOISE;\n\tconst real_t ground_level = material->get(\"ground_level\");\n\tconst real_t region_blend = material->get(\"region_blend\");\n\tconst int region_map_size = Terrain3DData::REGION_MAP_SIZE;\n\tconst PackedInt32Array region_map = data->get_region_map();\n\tconst int region_size = _terrain->get_region_size();\n\tconst real_t region_texel_size = 1.f / real_t(region_size);\n\n\tauto check_region = [&](const Vector2 &uv2) -> real_t {\n\t\tVector2i pos = Vector2i(Math::floor(uv2.x), Math::floor(uv2.y)) + Vector2i(region_map_size / 2, region_map_size / 2);\n\t\tint layer_index = 0;\n\t\tif ((uint32_t)(pos.x | pos.y) < (uint32_t)region_map_size) {\n\t\t\tint v = region_map[pos.y * region_map_size + pos.x];\n\t\t\tlayer_index = Math::clamp(v - 1, -1, 0) + 1;\n\t\t}\n\t\treturn real_t(layer_index);\n\t};\n\n\tauto get_region_blend = [&](Vector2 uv2) -> real_t {\n\t\t// Floating point bias (must match shader)\n\t\tuv2 -= Vector2(0.5011f, 0.5011f);\n\n\t\treal_t a = check_region(uv2 + Vector2(0.0f, 1.0f));\n\t\treal_t b = check_region(uv2 + Vector2(1.0f, 1.0f));\n\t\treal_t c = check_region(uv2 + Vector2(1.0f, 0.0f));\n\t\treal_t d = check_region(uv2 + Vector2(0.0f, 0.0f));\n\n\t\treal_t blend_factor = 2.0f + 126.0f * (1.0f - region_blend);\n\t\tVector2 f = Vector2(uv2.x - Math::floor(uv2.x), uv2.y - Math::floor(uv2.y));\n\t\tf.x = Math::clamp(f.x, 1e-8f, 1.f - 1e-8f);\n\t\tf.y = Math::clamp(f.y, 1e-8f, 1.f - 1e-8f);\n\t\tVector2 w = Vector2(1.f / (1.f + Math::exp(blend_factor * Math::log((1.f - f.x) / f.x))),\n\t\t\t\t1.f / (1.f + Math::exp(blend_factor * Math::log((1.f - f.y) / f.y))));\n\t\treal_t blend = Math::lerp(Math::lerp(d, c, w.x), Math::lerp(a, b, w.x), w.y);\n\n\t\treturn (1.f - blend) * 2.f;\n\t};\n\n\tint hshape_size = p_size + 1; // Calculate last vertex at end\n\tPackedRealArray map_data = PackedRealArray();\n\tmap_data.resize(hshape_size * hshape_size);\n\treal_t min_height = FLT_MAX;\n\treal_t max_height = -FLT_MAX;\n\n\tRef<Image> map, map_x, map_z, map_xz; // height maps\n\tRef<Image> cmap, cmap_x, cmap_z, cmap_xz; // control maps w/ holes\n\n\t// Get region_loc of top left corner of descaled and grid snapped collision shape position\n\tVector2i region_loc = V2I_DIVIDE_FLOOR(p_position, region_size);\n\tconst Terrain3DRegion *region = data->get_region_ptr(region_loc);\n\tif (!region || region->is_deleted()) {\n\t\tLOG(EXTREME, \"Region not found at: \", region_loc, \". Returning blank\");\n\t\treturn Dictionary();\n\t}\n\tmap = region->get_map(TYPE_HEIGHT);\n\tcmap = region->get_map(TYPE_CONTROL);\n\n\t// Get +X, +Z adjacent regions in case we run over\n\tregion = data->get_region_ptr(region_loc + Vector2i(1, 0));\n\tif (region && !region->is_deleted()) {\n\t\tmap_x = region->get_map(TYPE_HEIGHT);\n\t\tcmap_x = region->get_map(TYPE_CONTROL);\n\t}\n\tregion = data->get_region_ptr(region_loc + Vector2i(0, 1));\n\tif (region && !region->is_deleted()) {\n\t\tmap_z = region->get_map(TYPE_HEIGHT);\n\t\tcmap_z = region->get_map(TYPE_CONTROL);\n\t}\n\tregion = data->get_region_ptr(region_loc + Vector2i(1, 1));\n\tif (region && !region->is_deleted()) {\n\t\tmap_xz = region->get_map(TYPE_HEIGHT);\n\t\tcmap_xz = region->get_map(TYPE_CONTROL);\n\t}\n\n\tfor (int z = 0; z < hshape_size; z++) {\n\t\tfor (int x = 0; x < hshape_size; x++) {\n\t\t\t// Choose array indexing to match triangulation of heightmapshape with the mesh\n\t\t\t// https://stackoverflow.com/questions/16684856/rotating-a-2d-pixel-array-by-90-degrees\n\t\t\t// Normal array index rotated Y=0 - shape rotation Y=0 (xform below)\n\t\t\t// int index = z * hshape_size + x;\n\t\t\t// Array Index Rotated Y=-90 - must rotate shape Y=+90 (xform below)\n\t\t\tint index = hshape_size - 1 - z + x * hshape_size;\n\n\t\t\tVector2i shape_pos = p_position + Vector2i(x, z);\n\t\t\tVector2i shape_region_loc = V2I_DIVIDE_FLOOR(shape_pos, region_size);\n\t\t\tint img_x = Math::posmod(shape_pos.x, region_size);\n\t\t\tbool next_x = shape_region_loc.x > region_loc.x;\n\t\t\tint img_y = Math::posmod(shape_pos.y, region_size);\n\t\t\tbool next_z = shape_region_loc.y > region_loc.y;\n\n\t\t\t// Set heights on local map, or adjacent maps if on the last row/col\n\t\t\treal_t height = NAN;\n\t\t\tif (!next_x && !next_z && map.is_valid()) {\n\t\t\t\theight = is_hole(cmap->get_pixel(img_x, img_y).r) ? NAN : map->get_pixel(img_x, img_y).r;\n\t\t\t} else if (next_x && !next_z && map_x.is_valid()) {\n\t\t\t\theight = is_hole(cmap_x->get_pixel(img_x, img_y).r) ? NAN : map_x->get_pixel(img_x, img_y).r;\n\t\t\t} else if (!next_x && next_z && map_z.is_valid()) {\n\t\t\t\theight = is_hole(cmap_z->get_pixel(img_x, img_y).r) ? NAN : map_z->get_pixel(img_x, img_y).r;\n\t\t\t} else if (next_x && next_z && map_xz.is_valid()) {\n\t\t\t\theight = is_hole(cmap_xz->get_pixel(img_x, img_y).r) ? NAN : map_xz->get_pixel(img_x, img_y).r;\n\t\t\t}\n\t\t\tif (!std::isnan(height) && is_bg_flat_or_noise) {\n\t\t\t\tVector2 uv2 = Vector2(shape_pos) * region_texel_size;\n\t\t\t\theight = Math::lerp(height, ground_level, smoothstep(0.f, 1.f, get_region_blend(uv2)));\n\t\t\t}\n\t\t\tmap_data[index] = height;\n\t\t\tif (!std::isnan(height)) {\n\t\t\t\tmin_height = MIN(min_height, height);\n\t\t\t\tmax_height = MAX(max_height, height);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Non rotated shape for normal array index above\n\t//Transform3D xform = Transform3D(Basis(), global_pos);\n\t// Rotated shape Y=90 for -90 rotated array index\n\tTransform3D xform = Transform3D(Basis(V3_UP, Math_PI * .5), v2iv3(p_position + V2I(p_size / 2)));\n\tDictionary shape_data;\n\tshape_data[\"width\"] = hshape_size;\n\tshape_data[\"depth\"] = hshape_size;\n\tshape_data[\"heights\"] = map_data;\n\tshape_data[\"xform\"] = xform;\n\tshape_data[\"min_height\"] = min_height;\n\tshape_data[\"max_height\"] = max_height;\n\treturn shape_data;\n}\n\nvoid Terrain3DCollision::_shape_set_disabled(const int p_shape_id, const bool p_disabled) {\n\tif (is_editor_mode()) {\n\t\tCollisionShape3D *shape = _shapes[p_shape_id];\n\t\tshape->set_disabled(p_disabled);\n\t\tshape->set_visible(!p_disabled);\n\t} else {\n\t\tPS->body_set_shape_disabled(_static_body_rid, p_shape_id, p_disabled);\n\t}\n}\n\nvoid Terrain3DCollision::_shape_set_transform(const int p_shape_id, const Transform3D &p_xform) {\n\tif (is_editor_mode()) {\n\t\tCollisionShape3D *shape = _shapes[p_shape_id];\n\t\tshape->set_transform(p_xform);\n\t} else {\n\t\tPS->body_set_shape_transform(_static_body_rid, p_shape_id, p_xform);\n\t}\n}\n\nVector3 Terrain3DCollision::_shape_get_position(const int p_shape_id) const {\n\tif (is_editor_mode()) {\n\t\tCollisionShape3D *shape = _shapes[p_shape_id];\n\t\treturn shape->get_global_position();\n\t} else {\n\t\treturn PS->body_get_shape_transform(_static_body_rid, p_shape_id).origin;\n\t}\n}\n\nvoid Terrain3DCollision::_shape_set_data(const int p_shape_id, const Dictionary &p_dict) {\n\tif (is_editor_mode()) {\n\t\tCollisionShape3D *shape = _shapes[p_shape_id];\n\t\tRef<HeightMapShape3D> hshape = shape->get_shape();\n\t\thshape->set_map_data(p_dict[\"heights\"]);\n\t} else {\n\t\tRID shape_rid = PS->body_get_shape(_static_body_rid, p_shape_id);\n\t\tPS->shape_set_data(shape_rid, p_dict);\n\t}\n}\n\nvoid Terrain3DCollision::_reload_physics_material() {\n\tif (is_editor_mode()) {\n\t\tif (_static_body) {\n\t\t\t_static_body->set_physics_material_override(_physics_material);\n\t\t}\n\t} else {\n\t\tif (_static_body_rid.is_valid()) {\n\t\t\tif (_physics_material.is_null()) {\n\t\t\t\tPS->body_set_param(_static_body_rid, PhysicsServer3D::BODY_PARAM_BOUNCE, 0.f);\n\t\t\t\tPS->body_set_param(_static_body_rid, PhysicsServer3D::BODY_PARAM_FRICTION, 1.f);\n\t\t\t} else {\n\t\t\t\treal_t computed_bounce = _physics_material->get_bounce() * (_physics_material->is_absorbent() ? -1.f : 1.f);\n\t\t\t\treal_t computed_friction = _physics_material->get_friction() * (_physics_material->is_rough() ? -1.f : 1.f);\n\t\t\t\tPS->body_set_param(_static_body_rid, PhysicsServer3D::BODY_PARAM_BOUNCE, computed_bounce);\n\t\t\t\tPS->body_set_param(_static_body_rid, PhysicsServer3D::BODY_PARAM_FRICTION, computed_friction);\n\t\t\t}\n\t\t}\n\t}\n\tif (_physics_material.is_valid()) {\n\t\tLOG(DEBUG, \"Setting PhysicsMaterial bounce: \", _physics_material->get_bounce(), \", friction: \", _physics_material->get_friction());\n\t}\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DCollision::initialize(Terrain3D *p_terrain) {\n\tif (p_terrain) {\n\t\t_terrain = p_terrain;\n\t} else {\n\t\treturn;\n\t}\n\tif (!IS_EDITOR && is_editor_mode()) {\n\t\tLOG(WARN, \"Change collision mode to a non-editor mode for releases\");\n\t}\n\tbuild();\n}\n\nvoid Terrain3DCollision::build() {\n\tIS_DATA_INIT(VOID);\n\tif (!_terrain->is_inside_world()) {\n\t\tLOG(ERROR, \"Terrain isn't inside world. Returning.\");\n\t\treturn;\n\t}\n\n\t// Clear collision as the user might change modes in the editor\n\tdestroy();\n\n\t// Build only in applicable modes\n\tif (!is_enabled() || (IS_EDITOR && !is_editor_mode())) {\n\t\treturn;\n\t}\n\n\t// Create StaticBody3D\n\tif (is_editor_mode()) {\n\t\tLOG(INFO, \"Building editor collision\");\n\t\t_static_body = memnew(StaticBody3D);\n\t\t_static_body->set_name(\"StaticBody3D\");\n\t\t_static_body->set_as_top_level(true);\n\t\t_terrain->add_child(_static_body, true);\n\t\t_static_body->set_owner(_terrain);\n\t\t_static_body->set_collision_mask(_mask);\n\t\t_static_body->set_collision_layer(_layer);\n\t\t_static_body->set_collision_priority(_priority);\n\t} else {\n\t\tLOG(INFO, \"Building collision with Physics Server\");\n\t\t_static_body_rid = PS->body_create();\n\t\tPS->body_set_mode(_static_body_rid, PhysicsServer3D::BODY_MODE_STATIC);\n\t\tPS->body_set_space(_static_body_rid, _terrain->get_world_3d()->get_space());\n\t\tPS->body_attach_object_instance_id(_static_body_rid, _terrain->get_instance_id());\n\t\tPS->body_set_collision_mask(_static_body_rid, _mask);\n\t\tPS->body_set_collision_layer(_static_body_rid, _layer);\n\t\tPS->body_set_collision_priority(_static_body_rid, _priority);\n\t}\n\t_reload_physics_material();\n\n\t// Create CollisionShape3Ds\n\tint shape_count;\n\tint hshape_size;\n\tif (is_dynamic_mode()) {\n\t\tint grid_width = _radius * 2 / _shape_size;\n\t\tgrid_width = int_ceil_pow2(grid_width, 4);\n\t\tshape_count = grid_width * grid_width;\n\t\thshape_size = _shape_size + 1;\n\t\tLOG(DEBUG, \"Grid width: \", grid_width);\n\t} else {\n\t\tshape_count = _terrain->get_data()->get_region_count();\n\t\thshape_size = _terrain->get_region_size() + 1;\n\t}\n\t// Preallocate memory for push_back()\n\tif (is_editor_mode()) {\n\t\t_shapes.reserve(shape_count);\n\t}\n\tLOG(DEBUG, \"Shape count: \", shape_count);\n\tLOG(DEBUG, \"Shape size: \", _shape_size, \", hshape_size: \", hshape_size);\n\tTransform3D xform(Basis(), V3_MAX);\n\tfor (int i = 0; i < shape_count; i++) {\n\t\tif (is_editor_mode()) {\n\t\t\tCollisionShape3D *col_shape = memnew(CollisionShape3D);\n\t\t\t_shapes.push_back(col_shape);\n\t\t\tcol_shape->set_name(\"CollisionShape3D\");\n\t\t\tcol_shape->set_disabled(true);\n\t\t\tcol_shape->set_visible(true);\n\t\t\tcol_shape->set_enable_debug_fill(false);\n\t\t\tRef<HeightMapShape3D> hshape;\n\t\t\thshape.instantiate();\n\t\t\thshape->set_map_width(hshape_size);\n\t\t\thshape->set_map_depth(hshape_size);\n\t\t\tcol_shape->set_shape(hshape);\n\t\t\t_static_body->add_child(col_shape, true);\n\t\t\tcol_shape->set_owner(_static_body);\n\t\t\tcol_shape->set_transform(xform);\n\t\t} else {\n\t\t\tRID shape_rid = PS->heightmap_shape_create();\n\t\t\tPS->body_add_shape(_static_body_rid, shape_rid, xform, true);\n\t\t\tLOG(DEBUG, \"Adding shape: \", i, \", rid: \", shape_rid.get_id(), \" pos: \", _shape_get_position(i));\n\t\t}\n\t}\n\n\t_initialized = true;\n\tupdate();\n}\n\nvoid Terrain3DCollision::update(const Vector2i &p_region_loc, const bool p_rebuild) {\n\tIS_INIT(VOID);\n\tif (!_initialized) {\n\t\treturn;\n\t}\n\tif (p_rebuild && !is_dynamic_mode()) {\n\t\tbuild();\n\t\treturn;\n\t}\n\tint time = Time::get_singleton()->get_ticks_usec();\n\treal_t spacing = _terrain->get_vertex_spacing();\n\n\tif (is_dynamic_mode()) {\n\t\t// Snap descaled position to a _shape_size grid (eg. multiples of 16)\n\t\tVector2i snapped_pos = _snap_to_grid(_terrain->get_collision_target_position() / spacing);\n\t\tLOG(EXTREME, \"Updating collision at \", snapped_pos);\n\n\t\t// Skip if location hasn't moved to next step\n\t\tif (!p_rebuild && (_last_snapped_pos - snapped_pos).length_squared() < (_shape_size * _shape_size)) {\n\t\t\treturn;\n\t\t}\n\n\t\tLOG(EXTREME, \"---- 1. Defining area as a radius on a grid ----\");\n\t\t// Create a 0-N grid, center on snapped_pos\n\t\tPackedInt32Array grid;\n\t\tint grid_width = _radius * 2 / _shape_size; // 64*2/16 = 8\n\t\tgrid_width = int_ceil_pow2(grid_width, 4);\n\t\tgrid.resize(grid_width * grid_width);\n\t\tgrid.fill(-1);\n\t\tVector2i grid_offset = -V2I(grid_width / 2); // offset # cells to center of grid\n\t\tVector2i shape_offset = V2I(_shape_size / 2); // offset meters to top left corner of shape\n\t\tVector2i grid_pos = snapped_pos + grid_offset * _shape_size; // Top left of grid\n\t\tLOG(EXTREME, \"New Snapped position: \", snapped_pos);\n\t\tLOG(EXTREME, \"Grid_pos: \", grid_pos);\n\t\tLOG(EXTREME, \"Radius: \", _radius, \", Grid_width: \", grid_width, \", Grid_offset: \", grid_offset, \", # cells: \", grid.size());\n\t\tLOG(EXTREME, \"Shape_size: \", _shape_size, \", shape_offset: \", shape_offset);\n\n\t\tLOG(EXTREME, \"---- 2. Checking existing shapes ----\");\n\t\t// If shape is within area, skip\n\t\t// Else, mark unused\n\n\t\t// Stores index into _shapes array\n\t\tTypedArray<int> inactive_shape_ids;\n\n\t\treal_t radius_sqr = real_t(_radius * _radius);\n\t\tint shape_count = is_editor_mode() ? _shapes.size() : PS->body_get_shape_count(_static_body_rid);\n\t\tfor (int i = 0; i < shape_count; i++) {\n\t\t\t// Descaled global position of shape center\n\t\t\tVector3 shape_center = _shape_get_position(i) / spacing;\n\t\t\t// Unique key: Top left corner of shape, snapped to grid\n\t\t\tVector2i shape_pos = _snap_to_grid(v3v2i(shape_center) - shape_offset);\n\t\t\t// Optionally could adjust radius to account for corner (sqrt(_shape_size*2))\n\t\t\tif (!p_rebuild && (shape_center.x < FLT_MAX && v3v2i(shape_center).distance_squared_to(snapped_pos) <= radius_sqr)) {\n\t\t\t\t// Get index into shape array\n\t\t\t\tVector2i grid_loc = (shape_pos - grid_pos) / _shape_size;\n\t\t\t\tgrid[grid_loc.y * grid_width + grid_loc.x] = i;\n\t\t\t\t_shape_set_disabled(i, false);\n\t\t\t\tLOG(EXTREME, \"Shape \", i, \": shape_center: \", shape_center.x < FLT_MAX ? shape_center : V3(-999), \", shape_pos: \", shape_pos,\n\t\t\t\t\t\t\", grid_loc: \", grid_loc, \", index: \", (grid_loc.y * grid_width + grid_loc.x), \" active\");\n\t\t\t} else {\n\t\t\t\tinactive_shape_ids.push_back(i);\n\t\t\t\t_shape_set_disabled(i, true);\n\t\t\t\tLOG(EXTREME, \"Shape \", i, \": shape_center: \", shape_center.x < FLT_MAX ? shape_center : V3(-999), \", shape_pos: \", shape_pos,\n\t\t\t\t\t\t\" out of bounds, marking inactive\");\n\t\t\t}\n\t\t}\n\t\tLOG(EXTREME, \"_inactive_shapes size: \", inactive_shape_ids.size());\n\n\t\tLOG(EXTREME, \"---- 3. Review grid cells in area ----\");\n\t\t// If cell is full, skip\n\t\t// Else assign shape and form it\n\n\t\tfor (int i = 0; i < grid.size(); i++) {\n\t\t\tVector2i grid_loc(i % grid_width, i / grid_width);\n\t\t\t// Unique key: Top left corner of shape, snapped to grid\n\t\t\tVector2i shape_pos = grid_pos + grid_loc * _shape_size;\n\n\t\t\tif ((shape_pos + shape_offset).distance_squared_to(snapped_pos) > radius_sqr) {\n\t\t\t\tLOG(EXTREME, \"grid[\", i, \":\", grid_loc, \"] shape_pos : \", shape_pos, \" out of circle, skipping\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!p_rebuild && grid[i] >= 0) {\n\t\t\t\tVector2i center_pos = v3v2i(_shape_get_position(i));\n\t\t\t\tLOG(EXTREME, \"grid[\", i, \":\", grid_loc, \"] shape_pos : \", shape_pos, \" act \", center_pos - shape_offset, \" Has active shape id: \", grid[i]);\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tif (inactive_shape_ids.size() == 0) {\n\t\t\t\t\tLOG(ERROR, \"No more unused shapes! Aborting!\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tDictionary shape_data = _get_shape_data(shape_pos, _shape_size);\n\t\t\t\tif (shape_data.is_empty()) {\n\t\t\t\t\tLOG(EXTREME, \"grid[\", i, \":\", grid_loc, \"] shape_pos : \", shape_pos, \" No region found\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tint shape_id = inactive_shape_ids.pop_back();\n\t\t\t\tTransform3D xform = shape_data[\"xform\"];\n\t\t\t\tLOG(EXTREME, \"grid[\", i, \":\", grid_loc, \"] shape_pos : \", shape_pos, \" act \", v3v2i(xform.origin) - shape_offset, \" placing shape id \", shape_id);\n\t\t\t\txform.scale(Vector3(spacing, 1.f, spacing));\n\t\t\t\t_shape_set_transform(shape_id, xform);\n\t\t\t\t_shape_set_disabled(shape_id, false);\n\t\t\t\t_shape_set_data(shape_id, shape_data);\n\t\t\t}\n\t\t}\n\t\t_last_snapped_pos = snapped_pos;\n\t\tLOG(EXTREME, \"Setting _last_snapped_pos: \", _last_snapped_pos);\n\t\tLOG(EXTREME, \"inactive_shape_ids size: \", inactive_shape_ids.size());\n\n\t} else {\n\t\t// Full collision\n\t\tint shape_count = _terrain->get_data()->get_region_count();\n\t\tint region_size = _terrain->get_region_size();\n\t\tTypedArray<Vector2i> region_locs = _terrain->get_data()->get_region_locations();\n\t\tfor (int i = 0; i < region_locs.size(); i++) {\n\t\t\tVector2i region_loc = region_locs[i];\n\t\t\tif (p_region_loc != V2I_MAX && region_loc != p_region_loc) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tVector2i shape_pos = region_loc * region_size;\n\t\t\tDictionary shape_data = _get_shape_data(shape_pos, region_size);\n\t\t\tif (shape_data.is_empty()) {\n\t\t\t\tLOG(ERROR, \"Can't get shape data for \", region_loc);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tTransform3D xform = shape_data[\"xform\"];\n\t\t\txform.scale(Vector3(spacing, 1.f, spacing));\n\t\t\t_shape_set_transform(i, xform);\n\t\t\t_shape_set_disabled(i, false);\n\t\t\t_shape_set_data(i, shape_data);\n\t\t}\n\t}\n\tLOG(EXTREME, \"Collision update time: \", Time::get_singleton()->get_ticks_usec() - time, \" us\");\n}\n\nvoid Terrain3DCollision::destroy() {\n\t_initialized = false;\n\t_last_snapped_pos = V2I_MAX;\n\n\t// Physics Server\n\tif (_static_body_rid.is_valid()) {\n\t\t// Shape IDs change as they are freed, so it's not safe to iterate over them while freeing.\n\t\twhile (PS->body_get_shape_count(_static_body_rid) > 0) {\n\t\t\tRID rid = PS->body_get_shape(_static_body_rid, 0);\n\t\t\tLOG(DEBUG, \"Freeing CollisionShape RID \", rid);\n\t\t\tPS->free_rid(rid);\n\t\t}\n\n\t\tLOG(DEBUG, \"Freeing StaticBody RID\");\n\t\tPS->free_rid(_static_body_rid);\n\t\t_static_body_rid = RID();\n\t}\n\n\t// Scene Tree\n\tfor (int i = 0; i < _shapes.size(); i++) {\n\t\tCollisionShape3D *shape = _shapes[i];\n\t\tLOG(DEBUG, \"Freeing CollisionShape3D \", i, \" \", shape->get_name());\n\t\tremove_from_tree(shape);\n\t\tmemdelete_safely(shape);\n\t}\n\t_shapes.clear();\n\tif (_static_body) {\n\t\tLOG(DEBUG, \"Freeing StaticBody3D\");\n\t\tremove_from_tree(_static_body);\n\t\tmemdelete_safely(_static_body);\n\t}\n}\n\nvoid Terrain3DCollision::set_mode(const CollisionMode p_mode) {\n\tSET_IF_DIFF(_mode, p_mode);\n\tLOG(INFO, \"Setting collision mode: \", p_mode);\n\tif (is_enabled()) {\n\t\tbuild();\n\t} else {\n\t\tdestroy();\n\t}\n}\n\nvoid Terrain3DCollision::set_shape_size(const uint16_t p_size) {\n\tuint16_t size = CLAMP(p_size, 8, 64);\n\tsize = int_round_mult(size, uint16_t(8));\n\tSET_IF_DIFF(_shape_size, size);\n\tLOG(INFO, \"Setting collision dynamic shape size: \", _shape_size);\n\t// Ensure size:radius always results in at least one valid shape\n\tif (_shape_size > _radius - 8) {\n\t\tset_radius(_shape_size + 16);\n\t} else if (is_dynamic_mode()) {\n\t\tbuild();\n\t}\n}\n\nvoid Terrain3DCollision::set_radius(const uint16_t p_radius) {\n\tuint16_t radius = CLAMP(p_radius, 16, 256);\n\tradius = int_ceil_pow2(radius, uint16_t(16));\n\tSET_IF_DIFF(_radius, radius);\n\tLOG(INFO, \"Setting collision dynamic radius: \", _radius);\n\t// Ensure size:radius always results in at least one valid shape\n\tif (_radius < _shape_size + 8) {\n\t\tset_shape_size(_radius - 8);\n\t} else if (_shape_size < 16 && _radius > 128) {\n\t\tset_shape_size(16);\n\t} else if (is_dynamic_mode()) {\n\t\tbuild();\n\t}\n}\n\nvoid Terrain3DCollision::set_layer(const uint32_t p_layers) {\n\tSET_IF_DIFF(_layer, p_layers);\n\tLOG(INFO, \"Setting collision layers: \", p_layers);\n\tif (is_editor_mode()) {\n\t\tif (_static_body) {\n\t\t\t_static_body->set_collision_layer(_layer);\n\t\t}\n\t} else {\n\t\tif (_static_body_rid.is_valid()) {\n\t\t\tPS->body_set_collision_layer(_static_body_rid, _layer);\n\t\t}\n\t}\n}\n\nvoid Terrain3DCollision::set_mask(const uint32_t p_mask) {\n\tSET_IF_DIFF(_mask, p_mask);\n\tLOG(INFO, \"Setting collision mask: \", p_mask);\n\tif (is_editor_mode()) {\n\t\tif (_static_body) {\n\t\t\t_static_body->set_collision_mask(_mask);\n\t\t}\n\t} else {\n\t\tif (_static_body_rid.is_valid()) {\n\t\t\tPS->body_set_collision_mask(_static_body_rid, _mask);\n\t\t}\n\t}\n}\n\nvoid Terrain3DCollision::set_priority(const real_t p_priority) {\n\tSET_IF_DIFF(_priority, p_priority);\n\tLOG(INFO, \"Setting collision priority: \", p_priority);\n\tif (is_editor_mode()) {\n\t\tif (_static_body) {\n\t\t\t_static_body->set_collision_priority(_priority);\n\t\t}\n\t} else {\n\t\tif (_static_body_rid.is_valid()) {\n\t\t\tPS->body_set_collision_priority(_static_body_rid, _priority);\n\t\t}\n\t}\n}\n\nvoid Terrain3DCollision::set_physics_material(const Ref<PhysicsMaterial> &p_mat) {\n\tif (_physics_material == p_mat) {\n\t\treturn;\n\t}\n\tif (_physics_material.is_valid()) {\n\t\tif (_physics_material->is_connected(\"changed\", callable_mp(this, &Terrain3DCollision::_reload_physics_material))) {\n\t\t\tLOG(DEBUG, \"Disconnecting _physics_material::changed signal to _reload_physics_material()\");\n\t\t\t_physics_material->disconnect(\"changed\", callable_mp(this, &Terrain3DCollision::_reload_physics_material));\n\t\t}\n\t}\n\t_physics_material = p_mat;\n\tLOG(INFO, \"Setting physics material: \", p_mat);\n\tif (_physics_material.is_valid()) {\n\t\tLOG(DEBUG, \"Connecting _physics_material::changed signal to _reload_physics_material()\");\n\t\t_physics_material->connect(\"changed\", callable_mp(this, &Terrain3DCollision::_reload_physics_material));\n\t}\n\t_reload_physics_material();\n}\n\nRID Terrain3DCollision::get_rid() const {\n\tif (!is_editor_mode()) {\n\t\treturn _static_body_rid;\n\t} else {\n\t\tif (_static_body) {\n\t\t\treturn _static_body->get_rid();\n\t\t}\n\t}\n\treturn RID();\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DCollision::_bind_methods() {\n\tBIND_ENUM_CONSTANT(DISABLED);\n\tBIND_ENUM_CONSTANT(DYNAMIC_GAME);\n\tBIND_ENUM_CONSTANT(DYNAMIC_EDITOR);\n\tBIND_ENUM_CONSTANT(FULL_GAME);\n\tBIND_ENUM_CONSTANT(FULL_EDITOR);\n\n\tClassDB::bind_method(D_METHOD(\"build\"), &Terrain3DCollision::build);\n\tClassDB::bind_method(D_METHOD(\"update\", \"region_location\", \"rebuild\"), &Terrain3DCollision::update, DEFVAL(V2I_MAX), DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"destroy\"), &Terrain3DCollision::destroy);\n\tClassDB::bind_method(D_METHOD(\"set_mode\", \"mode\"), &Terrain3DCollision::set_mode);\n\tClassDB::bind_method(D_METHOD(\"get_mode\"), &Terrain3DCollision::get_mode);\n\tClassDB::bind_method(D_METHOD(\"is_enabled\"), &Terrain3DCollision::is_enabled);\n\tClassDB::bind_method(D_METHOD(\"is_editor_mode\"), &Terrain3DCollision::is_editor_mode);\n\tClassDB::bind_method(D_METHOD(\"is_dynamic_mode\"), &Terrain3DCollision::is_dynamic_mode);\n\n\tClassDB::bind_method(D_METHOD(\"set_shape_size\", \"size\"), &Terrain3DCollision::set_shape_size);\n\tClassDB::bind_method(D_METHOD(\"get_shape_size\"), &Terrain3DCollision::get_shape_size);\n\tClassDB::bind_method(D_METHOD(\"set_radius\", \"radius\"), &Terrain3DCollision::set_radius);\n\tClassDB::bind_method(D_METHOD(\"get_radius\"), &Terrain3DCollision::get_radius);\n\tClassDB::bind_method(D_METHOD(\"set_layer\", \"layers\"), &Terrain3DCollision::set_layer);\n\tClassDB::bind_method(D_METHOD(\"get_layer\"), &Terrain3DCollision::get_layer);\n\tClassDB::bind_method(D_METHOD(\"set_mask\", \"mask\"), &Terrain3DCollision::set_mask);\n\tClassDB::bind_method(D_METHOD(\"get_mask\"), &Terrain3DCollision::get_mask);\n\tClassDB::bind_method(D_METHOD(\"set_priority\", \"priority\"), &Terrain3DCollision::set_priority);\n\tClassDB::bind_method(D_METHOD(\"get_priority\"), &Terrain3DCollision::get_priority);\n\tClassDB::bind_method(D_METHOD(\"set_physics_material\", \"material\"), &Terrain3DCollision::set_physics_material);\n\tClassDB::bind_method(D_METHOD(\"get_physics_material\"), &Terrain3DCollision::get_physics_material);\n\tClassDB::bind_method(D_METHOD(\"get_rid\"), &Terrain3DCollision::get_rid);\n\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"mode\", PROPERTY_HINT_ENUM, \"Disabled,Dynamic / Game,Dynamic / Editor,Full / Game,Full / Editor\"), \"set_mode\", \"get_mode\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"shape_size\", PROPERTY_HINT_RANGE, \"8,64,8\"), \"set_shape_size\", \"get_shape_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"radius\", PROPERTY_HINT_RANGE, \"16,256,16\"), \"set_radius\", \"get_radius\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"layer\", PROPERTY_HINT_LAYERS_3D_PHYSICS), \"set_layer\", \"get_layer\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"mask\", PROPERTY_HINT_LAYERS_3D_PHYSICS), \"set_mask\", \"get_mask\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"priority\", PROPERTY_HINT_RANGE, \"0.1,256,.1\"), \"set_priority\", \"get_priority\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"physics_material\", PROPERTY_HINT_RESOURCE_TYPE, \"PhysicsMaterial\"), \"set_physics_material\", \"get_physics_material\");\n}\n"
  },
  {
    "path": "src/terrain_3d_collision.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_COLLISION_CLASS_H\n#define TERRAIN3D_COLLISION_CLASS_H\n\n#include <godot_cpp/classes/collision_shape3d.hpp>\n#include <godot_cpp/classes/physics_material.hpp>\n#include <godot_cpp/classes/static_body3d.hpp>\n#include <vector>\n\n#include \"constants.h\"\n#include \"terrain_3d_util.h\"\n\nclass Terrain3D;\n\nclass Terrain3DCollision : public Object {\n\tGDCLASS(Terrain3DCollision, Object);\n\tCLASS_NAME();\n\npublic: // Constants\n\tenum CollisionMode {\n\t\tDISABLED,\n\t\tDYNAMIC_GAME,\n\t\tDYNAMIC_EDITOR,\n\t\tFULL_GAME,\n\t\tFULL_EDITOR,\n\t};\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\n\t// Public settings\n\tCollisionMode _mode = DYNAMIC_GAME;\n\tuint16_t _shape_size = 16;\n\tuint16_t _radius = 64;\n\tuint32_t _layer = 1;\n\tuint32_t _mask = 1;\n\treal_t _priority = 1.f;\n\tRef<PhysicsMaterial> _physics_material;\n\n\t// Work data\n\tRID _static_body_rid; // Physics Server Static Body\n\tStaticBody3D *_static_body = nullptr; // Editor mode StaticBody3D\n\tstd::vector<CollisionShape3D *> _shapes; // All CollisionShape3Ds\n\n\tbool _initialized = false;\n\tVector2i _last_snapped_pos = V2I_MAX;\n\n\tVector2i _snap_to_grid(const Vector2i &p_pos) const;\n\tVector2i _snap_to_grid(const Vector3 &p_pos) const;\n\tDictionary _get_shape_data(const Vector2i &p_position, const int p_size);\n\n\tvoid _shape_set_disabled(const int p_shape_id, const bool p_disabled);\n\tvoid _shape_set_transform(const int p_shape_id, const Transform3D &p_xform);\n\tVector3 _shape_get_position(const int p_shape_id) const;\n\tvoid _shape_set_data(const int p_shape_id, const Dictionary &p_dict);\n\n\tvoid _reload_physics_material();\n\npublic:\n\tTerrain3DCollision() {}\n\t~Terrain3DCollision() { destroy(); }\n\tvoid initialize(Terrain3D *p_terrain);\n\n\tvoid build();\n\tvoid reset_target_position() { _last_snapped_pos = V2I_MAX; }\n\tvoid update(const Vector2i &p_region_loc = V2I_MAX, const bool p_rebuild = false);\n\tvoid destroy();\n\n\tvoid set_mode(const CollisionMode p_mode);\n\tCollisionMode get_mode() const { return _mode; }\n\tbool is_enabled() const { return _mode > DISABLED; }\n\tbool is_editor_mode() const { return _mode == DYNAMIC_EDITOR || _mode == FULL_EDITOR; }\n\tbool is_dynamic_mode() const { return _mode == DYNAMIC_GAME || _mode == DYNAMIC_EDITOR; }\n\n\tvoid set_shape_size(const uint16_t p_size);\n\tuint16_t get_shape_size() const { return _shape_size; }\n\tvoid set_radius(const uint16_t p_radius);\n\tuint16_t get_radius() const { return _radius; }\n\tvoid set_layer(const uint32_t p_layers);\n\tuint32_t get_layer() const { return _layer; };\n\tvoid set_mask(const uint32_t p_mask);\n\tuint32_t get_mask() const { return _mask; };\n\tvoid set_priority(const real_t p_priority);\n\treal_t get_priority() const { return _priority; }\n\tvoid set_physics_material(const Ref<PhysicsMaterial> &p_mat);\n\tRef<PhysicsMaterial> get_physics_material() { return _physics_material; }\n\tRID get_rid() const;\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nusing CollisionMode = Terrain3DCollision::CollisionMode;\nVARIANT_ENUM_CAST(Terrain3DCollision::CollisionMode);\n\ninline Vector2i Terrain3DCollision::_snap_to_grid(const Vector2i &p_pos) const {\n\treturn Vector2i(int_round_mult(p_pos.x, int32_t(_shape_size)),\n\t\t\tint_round_mult(p_pos.y, int32_t(_shape_size)));\n}\n\ninline Vector2i Terrain3DCollision::_snap_to_grid(const Vector3 &p_pos) const {\n\treturn Vector2i(Math::floor(p_pos.x / real_t(_shape_size) + 0.5f),\n\t\t\t\t   Math::floor(p_pos.z / real_t(_shape_size) + 0.5f)) *\n\t\t\t_shape_size;\n}\n\n#endif // TERRAIN3D_COLLISION_CLASS_H"
  },
  {
    "path": "src/terrain_3d_data.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/dir_access.hpp>\n#include <godot_cpp/classes/editor_file_system.hpp>\n#include <godot_cpp/classes/editor_interface.hpp>\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/file_access.hpp>\n#include <godot_cpp/classes/resource_saver.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d_data.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\nvoid Terrain3DData::_clear() {\n\tLOG(INFO, \"Clearing data\");\n\t_region_map_dirty = true;\n\t_region_map.clear();\n\t_region_map.resize(REGION_MAP_SIZE * REGION_MAP_SIZE);\n\t_regions.clear();\n\t_region_locations.clear();\n\t_master_height_range = V2_ZERO;\n\t_generated_height_maps.clear();\n\t_generated_control_maps.clear();\n\t_generated_color_maps.clear();\n}\n\n// Structured to work with do_for_regions. Should be renamed when copy_paste is expanded\nvoid Terrain3DData::_copy_paste_dfr(const Terrain3DRegion *p_src_region, const Rect2i &p_src_rect, const Rect2i &p_dst_rect, const Terrain3DRegion *p_dst_region) {\n\tif (!p_src_region || !p_dst_region) {\n\t\treturn;\n\t}\n\tTypedArray<Image> src_maps = p_src_region->get_maps();\n\tTypedArray<Image> dst_maps = p_dst_region->get_maps();\n\tfor (int i = 0; i < dst_maps.size(); i++) {\n\t\tImage *img = cast_to<Image>(dst_maps[i]);\n\t\tif (img) {\n\t\t\timg->blit_rect(src_maps[i], p_src_rect, p_dst_rect.position);\n\t\t}\n\t}\n\t_terrain->get_instancer()->copy_paste_dfr(p_src_region, p_src_rect, p_dst_region);\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DData::initialize(Terrain3D *p_terrain) {\n\tif (!p_terrain) {\n\t\tLOG(ERROR, \"Initialization failed, p_terrain is null\");\n\t\treturn;\n\t}\n\tLOG(INFO, \"Initializing data\");\n\tbool prev_initialized = _terrain != nullptr;\n\t_terrain = p_terrain;\n\t_region_map.resize(REGION_MAP_SIZE * REGION_MAP_SIZE);\n\t_vertex_spacing = _terrain->get_vertex_spacing();\n\tif (!prev_initialized && !_terrain->get_data_directory().is_empty()) {\n\t\tload_directory(_terrain->get_data_directory());\n\t}\n\t_region_size = _terrain->get_region_size();\n\t_region_sizev = V2I(_region_size);\n}\n\nvoid Terrain3DData::set_region_locations(const TypedArray<Vector2i> &p_locations) {\n\tSET_IF_DIFF(_region_locations, p_locations);\n\tLOG(INFO, \"Setting _region_locations with array sized: \", p_locations.size());\n\t_region_map_dirty = true;\n\tupdate_maps(TYPE_MAX, false, false); // only rebuild region map\n}\n\n// Returns an array of active regions, optionally a shallow or deep copy\nTypedArray<Terrain3DRegion> Terrain3DData::get_regions_active(const bool p_copy, const bool p_deep) const {\n\tTypedArray<Terrain3DRegion> region_arr;\n\tfor (const Vector2i &region_loc : _region_locations) {\n\t\tRef<Terrain3DRegion> region = get_region(region_loc);\n\t\tif (region.is_valid()) {\n\t\t\tregion_arr.push_back(p_copy ? region->duplicate(p_deep) : region);\n\t\t}\n\t}\n\treturn region_arr;\n}\n\n// Calls the callback function for every region within the given (descaled) area\n// The callable receives: source Terrain3DRegion, source Rect2i, dest Rect2i, (bindings)\n// Used with change_region_size, dest Terrain3DRegion is bound as the 4th parameter\nvoid Terrain3DData::do_for_regions(const Rect2i &p_area, const Callable &p_callback) {\n\tRect2i location_bounds(V2I_DIVIDE_FLOOR(p_area.position, _region_size), V2I_DIVIDE_CEIL(p_area.size, _region_size));\n\tLOG(DEBUG, \"Processing global area: \", p_area, \" -> \", location_bounds);\n\tPoint2i current_region_loc;\n\tfor (int y = location_bounds.position.y; y < location_bounds.get_end().y; y++) {\n\t\tcurrent_region_loc.y = y;\n\t\tfor (int x = location_bounds.position.x; x < location_bounds.get_end().x; x++) {\n\t\t\tcurrent_region_loc.x = x;\n\t\t\tconst Terrain3DRegion *region = get_region_ptr(current_region_loc);\n\t\t\tif (region && !region->is_deleted()) {\n\t\t\t\tLOG(DEBUG, \"Current region: \", current_region_loc);\n\t\t\t\tRect2i region_area = p_area.intersection(Rect2i(current_region_loc * _region_size, _region_sizev));\n\t\t\t\tLOG(DEBUG, \"Region bounds: \", Rect2i(current_region_loc * _region_size, _region_sizev));\n\t\t\t\tLOG(DEBUG, \"Region area: \", region_area);\n\t\t\t\tRect2i dst_coords(region_area.position - p_area.position, region_area.size);\n\t\t\t\tRect2i src_coords(region_area.position - (region->get_location() * _region_sizev), dst_coords.size);\n\t\t\t\tLOG(DEBUG, \"src map coords: \", src_coords);\n\t\t\t\tLOG(DEBUG, \"dst map coords: \", dst_coords);\n\t\t\t\tp_callback.call(region, src_coords, dst_coords);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terrain3DData::change_region_size(int p_new_size) {\n\tLOG(INFO, \"Changing region size from: \", _region_size, \" to \", p_new_size);\n\tif (!is_valid_region_size(p_new_size)) {\n\t\tLOG(ERROR, \"Invalid region size: \", p_new_size, \". Must be power of 2, 64-2048\");\n\t\treturn;\n\t}\n\tif (p_new_size == _region_size) {\n\t\treturn;\n\t}\n\n\t// Get current region corners expressed in new region_size coordinates\n\tDictionary new_region_locations;\n\tArray region_locations = _regions.keys();\n\tfor (const Vector2i &region_loc : region_locations) {\n\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\tif (region && !region->is_deleted()) {\n\t\t\tVector2i region_position = region->get_location() * _region_size;\n\t\t\tRect2i location_bounds(V2I_DIVIDE_FLOOR(region_position, p_new_size), V2I_DIVIDE_CEIL(_region_sizev, p_new_size));\n\t\t\tfor (int y = location_bounds.position.y; y < location_bounds.get_end().y; y++) {\n\t\t\t\tfor (int x = location_bounds.position.x; x < location_bounds.get_end().x; x++) {\n\t\t\t\t\tnew_region_locations[Vector2i(x, y)] = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Make new regions to receive copied data\n\tTypedArray<Terrain3DRegion> new_regions;\n\tArray new_locations = new_region_locations.keys();\n\tfor (const Vector2i &region_loc : new_locations) {\n\t\tRef<Terrain3DRegion> new_region;\n\t\tnew_region.instantiate();\n\t\tnew_region->set_location(region_loc);\n\t\tnew_region->set_region_size(p_new_size);\n\t\tnew_region->set_vertex_spacing(_vertex_spacing);\n\t\tnew_region->set_modified(true);\n\t\tnew_region->sanitize_maps();\n\n\t\t// Copy current data from current into new region, up to new region size\n\t\tRect2i area;\n\t\tarea.position = region_loc * p_new_size;\n\t\tarea.size = V2I(p_new_size);\n\t\tdo_for_regions(area, callable_mp(this, &Terrain3DData::_copy_paste_dfr).bind(new_region.ptr()));\n\t\tnew_regions.push_back(new_region);\n\t}\n\n\t// Remove old data\n\t_terrain->get_instancer()->destroy();\n\tTypedArray<Terrain3DRegion> old_regions = get_regions_active();\n\tfor (const Ref<Terrain3DRegion> &region : old_regions) {\n\t\tremove_region(region, false);\n\t}\n\n\t// Change region size\n\t_terrain->set_region_size((Terrain3D::RegionSize)p_new_size);\n\n\t// Add new regions and rebuild\n\tfor (const Ref<Terrain3DRegion> &region : new_regions) {\n\t\tadd_region(region, false);\n\t}\n\n\tcalc_height_range(true);\n\tupdate_maps(TYPE_MAX, true, true);\n\t_terrain->get_instancer()->update_mmis(-1, V2I_MAX, true);\n}\n\nvoid Terrain3DData::set_region_modified(const Vector2i &p_region_loc, const bool p_modified) {\n\tTerrain3DRegion *region = get_region_ptr(p_region_loc);\n\tif (!region) {\n\t\tLOG(ERROR, \"Region not found at: \", p_region_loc);\n\t\treturn;\n\t}\n\treturn region->set_modified(p_modified);\n}\n\nbool Terrain3DData::is_region_modified(const Vector2i &p_region_loc) const {\n\tTerrain3DRegion *region = get_region_ptr(p_region_loc);\n\tif (!region) {\n\t\tLOG(ERROR, \"Region not found at: \", p_region_loc);\n\t\treturn false;\n\t}\n\treturn region->is_modified();\n}\n\nvoid Terrain3DData::set_region_deleted(const Vector2i &p_region_loc, const bool p_deleted) {\n\tTerrain3DRegion *region = get_region_ptr(p_region_loc);\n\tif (!region) {\n\t\tLOG(ERROR, \"Region not found at: \", p_region_loc);\n\t\treturn;\n\t}\n\treturn region->set_deleted(p_deleted);\n}\n\nbool Terrain3DData::is_region_deleted(const Vector2i &p_region_loc) const {\n\tconst Terrain3DRegion *region = get_region_ptr(p_region_loc);\n\tif (!region) {\n\t\tLOG(ERROR, \"Region not found at: \", p_region_loc);\n\t\treturn true;\n\t}\n\treturn region->is_deleted();\n}\n\nRef<Terrain3DRegion> Terrain3DData::add_region_blankp(const Vector3 &p_global_position, const bool p_update) {\n\treturn add_region_blank(get_region_location(p_global_position));\n}\n\nRef<Terrain3DRegion> Terrain3DData::add_region_blank(const Vector2i &p_region_loc, const bool p_update) {\n\tRef<Terrain3DRegion> region;\n\tregion.instantiate();\n\tregion->set_location(p_region_loc);\n\tregion->set_region_size(_region_size);\n\tregion->set_vertex_spacing(_vertex_spacing);\n\tif (add_region(region, p_update) == OK) {\n\t\tregion->set_modified(true);\n\t\treturn region;\n\t}\n\treturn Ref<Terrain3DRegion>();\n}\n\n/** Adds a Terrain3DRegion to the terrain\n * Marks region as modified\n *\tp_update - rebuild the maps if true. Set to false if bulk adding many regions.\n */\nError Terrain3DData::add_region(const Ref<Terrain3DRegion> &p_region, const bool p_update) {\n\tif (p_region.is_null()) {\n\t\tLOG(ERROR, \"Provided region is null. Returning\");\n\t\treturn FAILED;\n\t}\n\tVector2i region_loc = p_region->get_location();\n\tLOG(INFO, \"Adding region at location \", region_loc, \", update maps: \", p_update ? \"yes\" : \"no\");\n\n\t// Check bounds and slow report errors\n\tif (get_region_map_index(region_loc) < 0) {\n\t\tLOG(ERROR, \"Location \", region_loc, \" out of bounds. Max: \",\n\t\t\t\t-REGION_MAP_SIZE / 2, \" to \", REGION_MAP_SIZE / 2 - 1);\n\t\treturn FAILED;\n\t}\n\tp_region->sanitize_maps();\n\tp_region->set_deleted(false);\n\tif (!_region_locations.has(region_loc)) {\n\t\t_region_locations.push_back(region_loc);\n\t} else {\n\t\tLOG(INFO, \"Overwriting \", (_regions.has(region_loc)) ? \"deleted\" : \"existing\", \" region at \", region_loc);\n\t}\n\t_regions[region_loc] = p_region;\n\t_region_map_dirty = true;\n\tLOG(DEBUG, \"Storing region \", region_loc, \" version \", vformat(\"%.3f\", p_region->get_version()), \" id: \", _region_locations.size());\n\tif (p_update) {\n\t\tupdate_maps(TYPE_MAX, true, false);\n\t\t_terrain->get_instancer()->update_mmis(-1, V2I_MAX, true);\n\t}\n\treturn OK;\n}\n\nvoid Terrain3DData::remove_regionp(const Vector3 &p_global_position, const bool p_update) {\n\tRef<Terrain3DRegion> region = get_region(get_region_location(p_global_position));\n\tremove_region(region, p_update);\n}\n\nvoid Terrain3DData::remove_regionl(const Vector2i &p_region_loc, const bool p_update) {\n\tRef<Terrain3DRegion> region = get_region(p_region_loc);\n\tremove_region(region, p_update);\n}\n\n// Remove region marks the region for deletion, and removes it from the active arrays indexed by ID\n// It remains stored in _regions and the file remains on disk until saved, when both are removed\nvoid Terrain3DData::remove_region(const Ref<Terrain3DRegion> &p_region, const bool p_update) {\n\tif (p_region.is_null()) {\n\t\tLOG(ERROR, \"Region not found or is null. Returning\");\n\t\treturn;\n\t}\n\n\tVector2i region_loc = p_region->get_location();\n\tint region_id = _region_locations.find(region_loc);\n\tLOG(INFO, \"Marking region \", region_loc, \" for deletion. update_maps: \", p_update ? \"yes\" : \"no\");\n\tif (region_id < 0) {\n\t\tLOG(ERROR, \"Region \", region_loc, \" not found in region_locations. Returning\");\n\t\treturn;\n\t}\n\tp_region->set_deleted(true);\n\t_region_locations.remove_at(region_id);\n\t_region_map_dirty = true;\n\tLOG(DEBUG, \"Removing from region_locations, new size: \", _region_locations.size());\n\tif (p_update) {\n\t\tLOG(DEBUG, \"Updating generated maps\");\n\t\tupdate_maps(TYPE_MAX, true, false);\n\t\t_terrain->get_instancer()->update_mmis(-1, V2I_MAX, true);\n\t}\n}\n\nvoid Terrain3DData::save_directory(const String &p_dir) {\n\tLOG(INFO, \"Saving data files to \", p_dir);\n\tArray locations = _regions.keys();\n\tfor (const Vector2i &region_loc : locations) {\n\t\tsave_region(region_loc, p_dir, _terrain->get_save_16_bit());\n\t}\n\tif (IS_EDITOR && !EditorInterface::get_singleton()->get_resource_filesystem()->is_scanning()) {\n\t\tEditorInterface::get_singleton()->get_resource_filesystem()->scan();\n\t}\n}\n\n// You may need to do a file system scan to update FileSystem panel\nvoid Terrain3DData::save_region(const Vector2i &p_region_loc, const String &p_dir, const bool p_16_bit) {\n\tRef<Terrain3DRegion> region = get_region(p_region_loc);\n\tif (region.is_null()) {\n\t\tLOG(ERROR, \"No region found at: \", p_region_loc);\n\t\treturn;\n\t}\n\tString fname = Util::location_to_filename(p_region_loc);\n\tString path = p_dir + String(\"/\") + fname;\n\t// If region marked for deletion, remove from disk and from _regions, but don't free in case stored in undo\n\tif (region->is_deleted()) {\n\t\tLOG(DEBUG, \"Removing \", p_region_loc, \" from _regions\");\n\t\t_regions.erase(p_region_loc);\n\t\tLOG(DEBUG, \"File to be deleted: \", path);\n\t\tif (!FileAccess::file_exists(path)) {\n\t\t\tLOG(INFO, \"File to delete \", path, \" doesn't exist. (Maybe from add, undo, save)\");\n\t\t\treturn;\n\t\t}\n\t\tRef<DirAccess> da = DirAccess::open(p_dir);\n\t\tif (da.is_null()) {\n\t\t\tLOG(ERROR, \"Cannot open directory for writing: \", p_dir, \" error: \", DirAccess::get_open_error());\n\t\t\treturn;\n\t\t}\n\t\tError err = da->remove(fname);\n\t\tif (err != OK) {\n\t\t\tLOG(ERROR, \"Could not remove file: \", fname, \", error code: \", err);\n\t\t}\n\t\tLOG(INFO, \"File \", path, \" deleted\");\n\t\treturn;\n\t}\n\tError err = region->save(path, p_16_bit);\n\tif (!(err == OK || err == ERR_SKIP)) {\n\t\tLOG(ERROR, \"Could not save file: \", path, \", error: \", UtilityFunctions::error_string(err), \" (\", err, \")\");\n\t}\n}\n\nvoid Terrain3DData::load_directory(const String &p_dir) {\n\tif (p_dir.is_empty()) {\n\t\tLOG(ERROR, \"Specified directory name is blank\");\n\t\treturn;\n\t}\n\n\tLOG(INFO, \"Loading region files from \", p_dir);\n\tPackedStringArray files = Util::get_files(p_dir, \"terrain3d*.res\");\n\tif (files.size() == 0) {\n\t\tLOG(INFO, \"No Terrain3D region files found in: \", p_dir);\n\t\treturn;\n\t}\n\n\t_clear();\n\tfor (const String &fname : files) {\n\t\tString path = p_dir + String(\"/\") + fname;\n\t\tLOG(DEBUG, \"Loading region from \", path);\n\t\tVector2i loc = Util::filename_to_location(fname);\n\t\tif (loc.x == INT32_MAX) {\n\t\t\tLOG(ERROR, \"Cannot get region location from file name: \", fname);\n\t\t\tcontinue;\n\t\t}\n\t\tRef<Terrain3DRegion> region = ResourceLoader::get_singleton()->load(path, \"Terrain3DRegion\", ResourceLoader::CACHE_MODE_IGNORE);\n\t\tif (region.is_null()) {\n\t\t\tLOG(ERROR, \"Cannot load region at \", path);\n\t\t\tcontinue;\n\t\t}\n\t\tLOG(INFO, \"Loaded region: \", loc, \" size: \", region->get_region_size());\n\t\tif (_regions.is_empty()) {\n\t\t\t_terrain->set_region_size((Terrain3D::RegionSize)region->get_region_size());\n\t\t} else {\n\t\t\tif (_terrain->get_region_size() != (Terrain3D::RegionSize)region->get_region_size()) {\n\t\t\t\tLOG(ERROR, \"Region size mismatch. First loaded: \", _terrain->get_region_size(), \" next: \",\n\t\t\t\t\t\tregion->get_region_size(), \" in file: \", path);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tregion->take_over_path(path);\n\t\tregion->set_location(loc);\n\t\tregion->set_version(CURRENT_DATA_VERSION); // Sends upgrade warning if old version\n\t\tadd_region(region, false);\n\t}\n\tupdate_maps(TYPE_MAX, true, false);\n}\n\n//TODO have load_directory call load_region, or make a load_file that loads a specific path\nvoid Terrain3DData::load_region(const Vector2i &p_region_loc, const String &p_dir, const bool p_update) {\n\tLOG(INFO, \"Loading region from location \", p_region_loc);\n\tString path = p_dir + String(\"/\") + Util::location_to_filename(p_region_loc);\n\tif (!FileAccess::file_exists(path)) {\n\t\tLOG(ERROR, \"File \", path, \" doesn't exist\");\n\t\treturn;\n\t}\n\tRef<Terrain3DRegion> region = ResourceLoader::get_singleton()->load(path, \"Terrain3DRegion\", ResourceLoader::CACHE_MODE_IGNORE);\n\tif (region.is_null()) {\n\t\tLOG(ERROR, \"Cannot load region at \", path);\n\t\treturn;\n\t}\n\tif (_regions.is_empty()) {\n\t\t_terrain->set_region_size((Terrain3D::RegionSize)region->get_region_size());\n\t} else {\n\t\tif (_terrain->get_region_size() != (Terrain3D::RegionSize)region->get_region_size()) {\n\t\t\tLOG(ERROR, \"Region size mismatch. First loaded: \", _terrain->get_region_size(), \" next: \",\n\t\t\t\t\tregion->get_region_size(), \" in file: \", path);\n\t\t\treturn;\n\t\t}\n\t}\n\tregion->take_over_path(path);\n\tregion->set_location(p_region_loc);\n\tregion->set_version(CURRENT_DATA_VERSION); // Sends upgrade warning if old version\n\tadd_region(region, p_update);\n}\n\nTypedArray<Image> Terrain3DData::get_maps(const MapType p_map_type) const {\n\tif (p_map_type < 0 || p_map_type >= TYPE_MAX) {\n\t\tLOG(ERROR, \"Specified map type out of range\");\n\t\treturn TypedArray<Image>();\n\t}\n\tswitch (p_map_type) {\n\t\tcase TYPE_HEIGHT:\n\t\t\treturn get_height_maps();\n\t\t\tbreak;\n\t\tcase TYPE_CONTROL:\n\t\t\treturn get_control_maps();\n\t\t\tbreak;\n\t\tcase TYPE_COLOR:\n\t\t\treturn get_color_maps();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\treturn TypedArray<Image>();\n}\n\nvoid Terrain3DData::update_maps(const MapType p_map_type, const bool p_all_regions, const bool p_generate_mipmaps) {\n\t// Generate region color mipmaps\n\tif (p_generate_mipmaps && (p_map_type == TYPE_COLOR || p_map_type == TYPE_MAX)) {\n\t\tLOG(EXTREME, \"Regenerating color mipmaps\");\n\t\tfor (const Vector2i &region_loc : _regions.keys()) {\n\t\t\tTerrain3DRegion *region = get_region_ptr(region_loc);\n\t\t\t// Generate all or only those marked edited\n\t\t\tif (region && !region->is_deleted() && (p_all_regions || region->is_edited())) {\n\t\t\t\tregion->get_color_map()->generate_mipmaps();\n\t\t\t}\n\t\t}\n\t}\n\n\t// Mark texture arrays dirty for rebuilding\n\tif (p_all_regions) {\n\t\tLOG(EXTREME, \"Marking dirty maps of type: \", p_map_type);\n\t\tswitch (p_map_type) {\n\t\t\tcase TYPE_HEIGHT:\n\t\t\t\t_generated_height_maps.clear();\n\t\t\t\tbreak;\n\t\t\tcase TYPE_CONTROL:\n\t\t\t\t_generated_control_maps.clear();\n\t\t\t\tbreak;\n\t\t\tcase TYPE_COLOR:\n\t\t\t\t_generated_color_maps.clear();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t_generated_height_maps.clear();\n\t\t\t\t_generated_control_maps.clear();\n\t\t\t\t_generated_color_maps.clear();\n\t\t\t\t_region_map_dirty = true;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tbool any_changed = false;\n\n\t// Rebuild region map if dirty\n\tif (_region_map_dirty) {\n\t\tLOG(EXTREME, \"Regenerating \", REGION_MAP_VSIZE, \" region map array from active regions\");\n\t\t_region_map.clear();\n\t\t_region_map.resize(REGION_MAP_SIZE * REGION_MAP_SIZE);\n\t\t_region_map_dirty = false;\n\t\t_region_locations = TypedArray<Vector2i>(); // enforce new pointer\n\t\tint region_id = 0;\n\t\tfor (const Vector2i &region_loc : _regions.keys()) {\n\t\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\t\tif (region && !region->is_deleted()) {\n\t\t\t\tregion_id += 1; // Begin at 1 since 0 = no region\n\t\t\t\tint map_index = get_region_map_index(region_loc);\n\t\t\t\tif (map_index >= 0) {\n\t\t\t\t\t_region_map[map_index] = region_id;\n\t\t\t\t\t_region_locations.push_back(region_loc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tany_changed = true;\n\t\tLOG(DEBUG, \"Emitting region_map_changed\");\n\t\temit_signal(\"region_map_changed\");\n\t}\n\n\t// Rebuild height maps if dirty\n\tif (_generated_height_maps.is_dirty()) {\n\t\tLOG(EXTREME, \"Regenerating height texture array from regions\");\n\t\t_height_maps.clear();\n\t\tfor (const Vector2i &region_loc : _region_locations) {\n\t\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\t\tif (region) {\n\t\t\t\t_height_maps.push_back(region->get_height_map());\n\t\t\t} else {\n\t\t\t\tLOG(ERROR, \"Can't find region \", region_loc, \", _regions: \", _regions,\n\t\t\t\t\t\t\", locations: \", _region_locations, \". Please report this error.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t_generated_height_maps.create(_height_maps);\n\t\tcalc_height_range();\n\t\tany_changed = true;\n\t\tLOG(DEBUG, \"Emitting height_maps_changed\");\n\t\temit_signal(\"height_maps_changed\");\n\t}\n\n\t// Rebulid control maps if dirty\n\tif (_generated_control_maps.is_dirty()) {\n\t\tLOG(EXTREME, \"Regenerating control texture array from regions\");\n\t\t_control_maps.clear();\n\t\tfor (const Vector2i &region_loc : _region_locations) {\n\t\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\t\tif (region) {\n\t\t\t\t_control_maps.push_back(region->get_control_map());\n\t\t\t}\n\t\t}\n\t\t_generated_control_maps.create(_control_maps);\n\t\tany_changed = true;\n\t\tLOG(DEBUG, \"Emitting control_maps_changed\");\n\t\temit_signal(\"control_maps_changed\");\n\t}\n\n\t// Rebulid color maps if dirty\n\tif (_generated_color_maps.is_dirty()) {\n\t\tLOG(EXTREME, \"Regenerating color texture array from regions\");\n\t\t_color_maps.clear();\n\t\tfor (const Vector2i &region_loc : _region_locations) {\n\t\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\t\tif (region) {\n\t\t\t\t_color_maps.push_back(region->get_color_map());\n\t\t\t}\n\t\t}\n\t\t_generated_color_maps.create(_color_maps);\n\t\tany_changed = true;\n\t\tLOG(DEBUG, \"Emitting color_maps_changed\");\n\t\temit_signal(\"color_maps_changed\");\n\t}\n\n\t// If no maps have been rebuilt, update only individual regions in the array.\n\t// Regions marked Edited have been changed by Terrain3DEditor::_operate_map or undo / redo processing.\n\tif (!any_changed) {\n\t\tfor (const Vector2i &region_loc : _region_locations) {\n\t\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\t\tif (region && region->is_edited()) {\n\t\t\t\tint region_id = get_region_id(region_loc);\n\t\t\t\tswitch (p_map_type) {\n\t\t\t\t\tcase TYPE_HEIGHT:\n\t\t\t\t\t\t_generated_height_maps.update(region->get_height_map(), region_id);\n\t\t\t\t\t\tLOG(DEBUG, \"Emitting height_maps_changed\");\n\t\t\t\t\t\temit_signal(\"height_maps_changed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TYPE_CONTROL:\n\t\t\t\t\t\t_generated_control_maps.update(region->get_control_map(), region_id);\n\t\t\t\t\t\tLOG(DEBUG, \"Emitting control_maps_changed\");\n\t\t\t\t\t\temit_signal(\"control_maps_changed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TYPE_COLOR:\n\t\t\t\t\t\t_generated_color_maps.update(region->get_color_map(), region_id);\n\t\t\t\t\t\tLOG(DEBUG, \"Emitting color_maps_changed\");\n\t\t\t\t\t\temit_signal(\"color_maps_changed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t_generated_height_maps.update(region->get_height_map(), region_id);\n\t\t\t\t\t\t_generated_control_maps.update(region->get_control_map(), region_id);\n\t\t\t\t\t\t_generated_color_maps.update(region->get_color_map(), region_id);\n\t\t\t\t\t\tLOG(DEBUG, \"Emitting height_maps_changed\");\n\t\t\t\t\t\temit_signal(\"height_maps_changed\");\n\t\t\t\t\t\tLOG(DEBUG, \"Emitting control_maps_changed\");\n\t\t\t\t\t\temit_signal(\"control_maps_changed\");\n\t\t\t\t\t\tLOG(DEBUG, \"Emitting color_maps_changed\");\n\t\t\t\t\t\temit_signal(\"color_maps_changed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (any_changed) {\n\t\tLOG(DEBUG, \"Emitting maps_changed\");\n\t\temit_signal(\"maps_changed\");\n\t\t_terrain->snap();\n\t}\n}\n\nvoid Terrain3DData::set_pixel(const MapType p_map_type, const Vector3 &p_global_position, const Color &p_pixel) {\n\tif (p_map_type < 0 || p_map_type >= TYPE_MAX) {\n\t\tLOG(ERROR, \"Specified map type out of range\");\n\t\treturn;\n\t}\n\tVector2i region_loc = get_region_location(p_global_position);\n\tTerrain3DRegion *region = get_region_ptr(region_loc);\n\tif (!region) {\n\t\tLOG(ERROR, \"No active region found at: \", p_global_position);\n\t\treturn;\n\t}\n\tif (region->is_deleted()) {\n\t\tLOG(ERROR, \"No active region found at: \", p_global_position);\n\t\treturn;\n\t}\n\tVector2i global_offset = region_loc * _region_size;\n\tVector3 descaled_pos = p_global_position / _vertex_spacing;\n\tVector2i img_pos = Vector2i(descaled_pos.x - global_offset.x, descaled_pos.z - global_offset.y);\n\timg_pos = img_pos.clamp(V2I_ZERO, V2I(_region_size - 1));\n\tImage *map = region->get_map_ptr(p_map_type);\n\tif (map) {\n\t\tmap->set_pixelv(img_pos, p_pixel);\n\t\tregion->set_modified(true);\n\t}\n}\n\nColor Terrain3DData::get_pixel(const MapType p_map_type, const Vector3 &p_global_position) const {\n\tif (p_map_type < 0 || p_map_type >= TYPE_MAX) {\n\t\tLOG(ERROR, \"Specified map type out of range\");\n\t\treturn COLOR_NAN;\n\t}\n\tVector2i region_loc = get_region_location(p_global_position);\n\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\tif (!region) {\n\t\treturn COLOR_NAN;\n\t}\n\tif (region->is_deleted()) {\n\t\treturn COLOR_NAN;\n\t}\n\tVector2i global_offset = region_loc * _region_size;\n\tVector3 descaled_pos = p_global_position / _vertex_spacing;\n\tVector2i img_pos = Vector2i(descaled_pos.x - global_offset.x, descaled_pos.z - global_offset.y);\n\timg_pos = img_pos.clamp(V2I_ZERO, V2I(_region_size - 1));\n\tImage *map = region->get_map_ptr(p_map_type);\n\tif (map) {\n\t\treturn map->get_pixelv(img_pos);\n\t} else {\n\t\treturn COLOR_NAN;\n\t}\n}\n\nreal_t Terrain3DData::get_height(const Vector3 &p_global_position) const {\n\tif (is_hole(get_control(p_global_position))) {\n\t\treturn NAN;\n\t}\n\tVector3 pos = p_global_position;\n\tconst real_t &step = _vertex_spacing;\n\tpos.y = 0.f;\n\t// Round to nearest vertex\n\tVector3 pos_round = pos.snapped(Vector3(step, 0.f, step));\n\t// If requested position is close to a vertex, return its height\n\tif ((pos - pos_round).length_squared() < 0.0001f) {\n\t\treturn get_pixel(TYPE_HEIGHT, pos).r;\n\t} else {\n\t\t// Otherwise, bilinearly interpolate 4 surrounding vertices\n\t\tVector3 pos00 = Vector3(floor(pos.x / step) * step, 0.f, floor(pos.z / step) * step);\n\t\treal_t ht00 = get_pixel(TYPE_HEIGHT, pos00).r;\n\t\tVector3 pos01 = pos00 + Vector3(0.f, 0.f, step);\n\t\treal_t ht01 = get_pixel(TYPE_HEIGHT, pos01).r;\n\t\tVector3 pos10 = pos00 + Vector3(step, 0.f, 0.f);\n\t\treal_t ht10 = get_pixel(TYPE_HEIGHT, pos10).r;\n\t\tVector3 pos11 = pos00 + Vector3(step, 0.f, step);\n\t\treal_t ht11 = get_pixel(TYPE_HEIGHT, pos11).r;\n\t\treturn bilerp(ht00, ht01, ht10, ht11, pos00, pos11, pos);\n\t}\n}\n\nVector3 Terrain3DData::get_normal(const Vector3 &p_global_position) const {\n\tif (get_region_idp(p_global_position) < 0 || is_hole(get_control(p_global_position))) {\n\t\treturn V3_NAN;\n\t}\n\treal_t height = get_height(p_global_position);\n\treal_t u = height - get_height(p_global_position + Vector3(_vertex_spacing, 0.0f, 0.0f));\n\treal_t v = height - get_height(p_global_position + Vector3(0.f, 0.f, _vertex_spacing));\n\tVector3 normal = Vector3(u, _vertex_spacing, v);\n\tnormal.normalize();\n\treturn normal;\n}\n\nbool Terrain3DData::is_in_slope(const Vector3 &p_global_position, const Vector2 &p_slope_range, const Vector3 &p_normal) const {\n\t// If slope is full range, nothing to do here\n\tconst Vector2 slope_range = CLAMP(p_slope_range, V2_ZERO, V2(90.f));\n\tif (slope_range.y - slope_range.x > 89.99f) {\n\t\treturn true;\n\t}\n\n\t// Use custom normal if provided\n\tVector3 slope_normal = p_normal;\n\tif (!slope_normal.is_zero_approx()) {\n\t\tslope_normal.normalize();\n\t} else {\n\t\t// Else, compute terrain normal\n\t\tif (get_region_idp(p_global_position) < 0) {\n\t\t\treturn false;\n\t\t}\n\t\t// Adapted from get_height() to work with holes\n\t\tauto get_height = [&](Vector3 pos) -> real_t {\n\t\t\treal_t step = _terrain->get_vertex_spacing();\n\t\t\t// Round to nearest vertex\n\t\t\tVector3 pos_round = pos.snapped(Vector3(step, 0.f, step));\n\t\t\treal_t height = get_pixel(TYPE_HEIGHT, pos_round).r;\n\t\t\treturn std::isnan(height) ? 0.f : height;\n\t\t};\n\n\t\tconst real_t vertex_spacing = _terrain->get_vertex_spacing();\n\t\tconst real_t height = get_height(p_global_position);\n\t\tconst real_t u = height - get_height(p_global_position + Vector3(vertex_spacing, 0.0f, 0.0f));\n\t\tconst real_t v = height - get_height(p_global_position + Vector3(0.f, 0.f, vertex_spacing));\n\t\tslope_normal = Vector3(u, vertex_spacing, v);\n\t\tslope_normal.normalize();\n\t}\n\n\tconst real_t slope_angle = Math::acos(slope_normal.dot(V3_UP));\n\tconst real_t slope_angle_degrees = Math::rad_to_deg(slope_angle);\n\treturn (slope_range.x <= slope_angle_degrees) && (slope_angle_degrees <= slope_range.y);\n}\n\n/**\n * Returns:\n * X = base index\n * Y = overlay index\n * Z = percentage blend between X and Y. Limited to the fixed values in RANGE.\n * Interpretation of this data is up to the gamedev. Unfortunately due to blending, this isn't\n * pixel perfect. I would have your player print this location as you walk around to see how the\n * blending values look, then consider that the overlay texture is visible starting at a blend\n * value of .3-.5, otherwise it's the base texture.\n **/\nVector3 Terrain3DData::get_texture_id(const Vector3 &p_global_position) const {\n\t// Verify in a region\n\tint region_id = get_region_idp(p_global_position);\n\tif (region_id < 0) {\n\t\treturn V3_NAN;\n\t}\n\n\t// Verify not in a hole\n\tfloat src = get_pixel(TYPE_CONTROL, p_global_position).r; // 32-bit float, not double/real\n\tif (is_hole(src)) {\n\t\treturn V3_NAN;\n\t}\n\n\t// If material available, autoshader enabled, and pixel set to auto\n\tif (_terrain) {\n\t\tRef<Terrain3DMaterial> t_material = _terrain->get_material();\n\t\tbool auto_enabled = t_material->get_auto_shader_enabled();\n\t\tbool control_auto = is_auto(src);\n\t\tif (auto_enabled && control_auto) {\n\t\t\treal_t auto_slope = real_t(t_material->get_shader_param(\"auto_slope\"));\n\t\t\treal_t auto_height_reduction = real_t(t_material->get_shader_param(\"auto_height_reduction\"));\n\t\t\treal_t height = get_height(p_global_position);\n\t\t\tVector3 normal = get_normal(p_global_position);\n\t\t\tuint32_t base_id = t_material->get_shader_param(\"auto_base_texture\");\n\t\t\tuint32_t overlay_id = t_material->get_shader_param(\"auto_overlay_texture\");\n\t\t\treal_t blend = CLAMP((auto_slope * 2.f * (normal.y - 1.f) + 1.f) - auto_height_reduction * .01f * height, 0.f, 1.f);\n\t\t\treturn Vector3(real_t(base_id), real_t(overlay_id), blend);\n\t\t}\n\t}\n\n\t// Else, just get textures from control map\n\tuint32_t base_id = get_base(src);\n\tuint32_t overlay_id = get_overlay(src);\n\treal_t blend = real_t(get_blend(src)) / 255.0f;\n\treturn Vector3(real_t(base_id), real_t(overlay_id), blend);\n}\n\n/**\n * Returns the location of a terrain vertex at a certain LOD. If there is a hole at the position, it returns\n * NAN in the vector's Y coordinate.\n * p_lod (0-8): Determines how many heights around the given global position will be sampled.\n * p_filter:\n *  HEIGHT_FILTER_NEAREST: Samples the height map at the exact coordinates given.\n *  HEIGHT_FILTER_MINIMUM: Samples (1 << p_lod) ** 2 heights around the given coordinates and returns the lowest.\n * p_global_position: X and Z coordinates of the vertex. Heights will be sampled around these coordinates.\n */\nVector3 Terrain3DData::get_mesh_vertex(const int32_t p_lod, const HeightFilter p_filter, const Vector3 &p_global_position) const {\n\tLOG(INFO, \"Calculating vertex location\");\n\tint32_t step = 1 << CLAMP(p_lod, 0, 8);\n\treal_t height = 0.0f;\n\n\tswitch (p_filter) {\n\t\tcase HEIGHT_FILTER_NEAREST: {\n\t\t\tif (is_hole(get_control(p_global_position))) {\n\t\t\t\theight = NAN;\n\t\t\t} else {\n\t\t\t\theight = get_height(p_global_position);\n\t\t\t}\n\t\t} break;\n\t\tcase HEIGHT_FILTER_MINIMUM: {\n\t\t\theight = get_height(p_global_position);\n\t\t\tfor (int32_t dx = -step / 2; dx < step / 2; dx += 1) {\n\t\t\t\tfor (int32_t dz = -step / 2; dz < step / 2; dz += 1) {\n\t\t\t\t\tVector3 position = p_global_position + Vector3(dx, 0.f, dz) * _vertex_spacing;\n\t\t\t\t\tif (is_hole(get_control(position))) {\n\t\t\t\t\t\theight = NAN;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\treal_t h = get_height(position);\n\t\t\t\t\tif (h < height) {\n\t\t\t\t\t\theight = h;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} break;\n\t}\n\treturn Vector3(p_global_position.x, height, p_global_position.z);\n}\n\nvoid Terrain3DData::add_edited_area(const AABB &p_area) {\n\tif (_edited_area.has_surface()) {\n\t\t_edited_area = _edited_area.merge(p_area);\n\t} else {\n\t\t_edited_area = p_area;\n\t}\n\tLOG(DEBUG, \"Emitting maps_edited\");\n\temit_signal(\"maps_edited\", p_area);\n}\n\n// Recalculates master height range from all active regions current height ranges\n// Recursive mode has all regions to recalculate from each heightmap pixel\nvoid Terrain3DData::calc_height_range(const bool p_recursive) {\n\t_master_height_range = V2_ZERO;\n\tfor (const Vector2i &region_loc : _region_locations) {\n\t\tTerrain3DRegion *region = get_region_ptr(region_loc);\n\t\tif (!region) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (p_recursive) {\n\t\t\tregion->calc_height_range();\n\t\t}\n\t\tupdate_master_heights(region->get_height_range());\n\t}\n\tLOG(EXTREME, \"Accumulated height range for all regions: \", _master_height_range);\n}\n\n/**\n * Imports an Image set (Height, Control, Color) into Terrain3DData\n * It does NOT normalize values to 0-1. You must do that using get_min_max() and adjusting scale and offset.\n * Parameters:\n *\tp_images - MapType.TYPE_MAX sized array of Images for Height, Control, Color. Images can be blank or null\n *\tp_global_position - X,0,Z location on the region map. Valid range is ~ (+/-8192, +/-8192)\n *\tp_offset - Add this factor to all height values, can be negative\n *\tp_scale - Scale all height values by this factor (applied after offset)\n */\nvoid Terrain3DData::import_images(const TypedArray<Image> &p_images, const Vector3 &p_global_position, const real_t p_offset, const real_t p_scale) {\n\tIS_INIT_MESG(\"Data not initialized\", VOID);\n\tif (p_images.size() != TYPE_MAX) {\n\t\tLOG(ERROR, \"p_images.size() is \", p_images.size(), \". It should be \", TYPE_MAX, \" even if some Images are blank or null\");\n\t\treturn;\n\t}\n\n\tVector2i img_size = V2I_ZERO;\n\tfor (int i = 0; i < TYPE_MAX; i++) {\n\t\tRef<Image> img = p_images[i];\n\t\tif (img.is_valid() && !img->is_empty()) {\n\t\t\tLOG(INFO, \"Importing image type \", TYPESTR[i], \", size: \", img->get_size(), \", format: \", img->get_format());\n\t\t\tif (i == TYPE_HEIGHT) {\n\t\t\t\tLOG(INFO, \"Applying offset: \", p_offset, \", scale: \", p_scale);\n\t\t\t}\n\t\t\tif (img_size == V2I_ZERO) {\n\t\t\t\timg_size = img->get_size();\n\t\t\t} else if (img_size != img->get_size()) {\n\t\t\t\tLOG(ERROR, \"Included Images in p_images have different dimensions. Aborting import\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tif (img_size == V2I_ZERO) {\n\t\tLOG(ERROR, \"All images are empty. Nothing to import\");\n\t\treturn;\n\t}\n\n\tVector3 descaled_position = p_global_position / _vertex_spacing;\n\tint max_dimension = _region_size * REGION_MAP_SIZE / 2;\n\tif ((std::abs(descaled_position.x) > max_dimension) || (std::abs(descaled_position.z) > max_dimension)) {\n\t\tLOG(ERROR, \"Specify a position within +/-\", Vector3(max_dimension, 0.f, max_dimension) * _vertex_spacing);\n\t\treturn;\n\t}\n\tif ((descaled_position.x + img_size.x > max_dimension) ||\n\t\t\t(descaled_position.z + img_size.y > max_dimension)) {\n\t\tLOG(ERROR, img_size, \" image will not fit at \", p_global_position,\n\t\t\t\t\". Try \", -(img_size * _vertex_spacing) / 2.f, \" to center, or increase region_size\");\n\t\treturn;\n\t}\n\n\tTypedArray<Image> src_images;\n\tsrc_images.resize(TYPE_MAX);\n\n\tfor (int i = 0; i < TYPE_MAX; i++) {\n\t\tRef<Image> img = p_images[i];\n\t\tsrc_images[i] = img;\n\t\tif (img.is_null()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Apply scale and offsets to the heightmap and filter out invalid data\n\t\tif (i == TYPE_HEIGHT) {\n\t\t\tLOG(DEBUG, \"Creating new temp image to adjust scale: \", p_scale, \" offset: \", p_offset);\n\t\t\tRef<Image> newimg = Image::create_empty(img->get_size().x, img->get_size().y, false, FORMAT[TYPE_HEIGHT]);\n\t\t\tfor (int y = 0; y < img->get_height(); y++) {\n\t\t\t\tfor (int x = 0; x < img->get_width(); x++) {\n\t\t\t\t\tColor clr = img->get_pixel(x, y);\n\t\t\t\t\tif (std::isnormal(clr.r)) {\n\t\t\t\t\t\tclr.r = (clr.r * p_scale) + p_offset;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclr.r = p_offset;\n\t\t\t\t\t}\n\t\t\t\t\tnewimg->set_pixel(x, y, clr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsrc_images[i] = newimg;\n\t\t}\n\t}\n\n\t// Calculate regions this image will span\n\tint img_start_x = (int)Math::floor(descaled_position.x);\n\tint img_start_z = (int)Math::floor(descaled_position.z);\n\tint img_end_x = img_start_x + img_size.x - 1;\n\tint img_end_z = img_start_z + img_size.y - 1;\n\n\tint start_region_x = (int)Math::floor(real_t(img_start_x) / real_t(_region_size));\n\tint start_region_z = (int)Math::floor(real_t(img_start_z) / real_t(_region_size));\n\tint end_region_x = (int)Math::floor(real_t(img_end_x) / real_t(_region_size));\n\tint end_region_z = (int)Math::floor(real_t(img_end_z) / real_t(_region_size));\n\n\t// Clamp region indices to valid range\n\tint half_region_map = REGION_MAP_SIZE / 2;\n\tstart_region_x = CLAMP(start_region_x, -half_region_map, half_region_map - 1);\n\tstart_region_z = CLAMP(start_region_z, -half_region_map, half_region_map - 1);\n\tend_region_x = CLAMP(end_region_x, -half_region_map, half_region_map - 1);\n\tend_region_z = CLAMP(end_region_z, -half_region_map, half_region_map - 1);\n\n\tLOG(DEBUG, \"Image spans regions (\", start_region_x, \",\", start_region_z, \") to (\", end_region_x, \",\", end_region_z, \")\");\n\n\tbool generate_mipmaps = false;\n\tfor (int rz = start_region_z; rz <= end_region_z; rz++) {\n\t\tfor (int rx = start_region_x; rx <= end_region_x; rx++) {\n\t\t\tVector2i region_loc = Vector2i(rx, rz);\n\n\t\t\tint region_start_x = rx * _region_size;\n\t\t\tint region_start_z = rz * _region_size;\n\t\t\tint region_end_x = region_start_x + _region_size - 1;\n\t\t\tint region_end_z = region_start_z + _region_size - 1;\n\n\t\t\tint overlap_start_x = MAX(region_start_x, img_start_x);\n\t\t\tint overlap_start_z = MAX(region_start_z, img_start_z);\n\t\t\tint overlap_end_x = MIN(region_end_x, img_end_x);\n\t\t\tint overlap_end_z = MIN(region_end_z, img_end_z);\n\n\t\t\t// Skip if no overlap\n\t\t\tif (overlap_end_x < overlap_start_x || overlap_end_z < overlap_start_z) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint copy_width = overlap_end_x - overlap_start_x + 1;\n\t\t\tint copy_height = overlap_end_z - overlap_start_z + 1;\n\n\t\t\tint src_x = overlap_start_x - img_start_x;\n\t\t\tint src_z = overlap_start_z - img_start_z;\n\t\t\tint dst_x = overlap_start_x - region_start_x;\n\t\t\tint dst_z = overlap_start_z - region_start_z;\n\n\t\t\tLOG(DEBUG, \"Region \", region_loc, \": copying \", Vector2i(copy_width, copy_height),\n\t\t\t\t\t\" from img(\", src_x, \",\", src_z, \") to region(\", dst_x, \",\", dst_z, \")\");\n\n\t\t\tRef<Terrain3DRegion> region = get_region(region_loc);\n\t\t\tif (region.is_null()) {\n\t\t\t\tregion.instantiate();\n\t\t\t\tregion->set_location(region_loc);\n\t\t\t\tregion->set_region_size(_region_size);\n\t\t\t\tregion->set_vertex_spacing(_vertex_spacing);\n\t\t\t\tadd_region(region, false);\n\t\t\t} else if (region->is_deleted()) {\n\t\t\t\tregion->clear();\n\t\t\t\tregion->set_location(region_loc);\n\t\t\t\tregion->set_region_size(_region_size);\n\t\t\t\tregion->set_vertex_spacing(_vertex_spacing);\n\t\t\t}\n\t\t\tfor (int i = 0; i < TYPE_MAX; i++) {\n\t\t\t\tRef<Image> img = src_images[i];\n\t\t\t\tif (img.is_valid() && !img->is_empty()) {\n\t\t\t\t\tRef<Image> region_map;\n\n\t\t\t\t\tRef<Image> existing_map = region->get_map(static_cast<MapType>(i));\n\t\t\t\t\tif (existing_map.is_valid() && !existing_map->is_empty()) {\n\t\t\t\t\t\tregion_map.instantiate();\n\t\t\t\t\t\tregion_map->copy_from(existing_map);\n\t\t\t\t\t\tif (region_map->get_format() != img->get_format()) {\n\t\t\t\t\t\t\tregion_map->convert(img->get_format());\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tregion_map = Util::get_filled_image(_region_sizev, COLOR[i], false, img->get_format());\n\t\t\t\t\t}\n\t\t\t\t\tregion_map->blit_rect(img, Rect2i(src_x, src_z, copy_width, copy_height), Vector2i(dst_x, dst_z));\n\t\t\t\t\tregion->set_map(static_cast<MapType>(i), region_map);\n\t\t\t\t\tif (i == TYPE_COLOR) {\n\t\t\t\t\t\tgenerate_mipmaps = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tregion->set_modified(true);\n\t\t\tregion->sanitize_maps();\n\t\t}\n\t}\n\tupdate_maps(TYPE_MAX, true, generate_mipmaps);\n}\n\n/** Exports a specified map as one of r16/raw, exr, jpg, png, webp, res, tres\n * r16 or exr are recommended for roundtrip external editing\n * r16 can be edited by Krita, however you must know the dimensions and min/max before reimporting\n * res/tres stores in Godot's native format.\n */\nError Terrain3DData::export_image(const String &p_file_name, const MapType p_map_type) const {\n\tif (p_map_type < 0 || p_map_type >= TYPE_MAX) {\n\t\tLOG(ERROR, \"Invalid map type specified: \", p_map_type, \" max: \", TYPE_MAX - 1);\n\t\treturn FAILED;\n\t}\n\n\tif (p_file_name.is_empty()) {\n\t\tLOG(ERROR, \"No file specified. Nothing to export\");\n\t\treturn FAILED;\n\t}\n\n\tif (get_region_count() == 0) {\n\t\tLOG(ERROR, \"No valid regions. Nothing to export\");\n\t\treturn FAILED;\n\t}\n\n\t// Simple file name validation\n\tstatic const String bad_chars = \"?*|%<>\\\"\";\n\tfor (int i = 0; i < bad_chars.length(); ++i) {\n\t\tfor (int j = 0; j < p_file_name.length(); ++j) {\n\t\t\tif (bad_chars[i] == p_file_name[j]) {\n\t\t\t\tLOG(ERROR, \"Invalid file path '\" + p_file_name + \"'\");\n\t\t\t\treturn FAILED;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Update path delimeter\n\tString file_name = p_file_name.replace(\"\\\\\", \"/\");\n\n\t// Check if p_file_name has a path and prepend \"res://\" if not\n\tbool is_simple_filename = true;\n\tfor (int i = 0; i < file_name.length(); ++i) {\n\t\tchar32_t c = file_name[i];\n\t\tif (c == '/' || c == ':') {\n\t\t\tis_simple_filename = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (is_simple_filename) {\n\t\tfile_name = \"res://\" + file_name;\n\t}\n\n\t// Check if the file can be opened for writing\n\tRef<FileAccess> file_ref = FileAccess::open(file_name, FileAccess::ModeFlags::WRITE);\n\tif (file_ref.is_null()) {\n\t\tLOG(ERROR, \"Cannot open file '\" + file_name + \"' for writing\");\n\t\treturn FAILED;\n\t}\n\tfile_ref->close();\n\n\t// Filename is validated. Begin export image generation\n\tRef<Image> img = layered_to_image(p_map_type);\n\tif (img.is_null() || img->is_empty()) {\n\t\tLOG(ERROR, \"Cannot create an export image for map type: \", TYPESTR[p_map_type]);\n\t\treturn FAILED;\n\t}\n\n\tString ext = file_name.get_extension().to_lower();\n\tLOG(MESG, \"Saving \", img->get_size(), \" sized \", TYPESTR[p_map_type],\n\t\t\t\" map in format \", img->get_format(), \" as \", ext, \" to: \", file_name);\n\tVector2i minmax = Util::get_min_max(img);\n\tLOG(MESG, \"Minimum height: \", minmax.x, \", Maximum height: \", minmax.y);\n\tif (ext == \"r16\" || ext == \"raw\") {\n\t\tRef<FileAccess> file = FileAccess::open(file_name, FileAccess::WRITE);\n\t\treal_t height_min = minmax.x;\n\t\treal_t height_max = minmax.y;\n\t\treal_t hscale = 65535.0 / (height_max - height_min);\n\t\tfor (int y = 0; y < img->get_height(); y++) {\n\t\t\tfor (int x = 0; x < img->get_width(); x++) {\n\t\t\t\tint h = int((img->get_pixel(x, y).r - height_min) * hscale);\n\t\t\t\th = CLAMP(h, 0, 65535);\n\t\t\t\tfile->store_16(h);\n\t\t\t}\n\t\t}\n\t\treturn file->get_error();\n\t} else if (ext == \"exr\") {\n\t\treturn img->save_exr(file_name, (p_map_type == TYPE_HEIGHT) ? true : false);\n\t} else if (ext == \"png\") {\n\t\treturn img->save_png(file_name);\n\t} else if (ext == \"jpg\") {\n\t\treturn img->save_jpg(file_name);\n\t} else if (ext == \"webp\") {\n\t\treturn img->save_webp(file_name);\n\t} else if ((ext == \"res\") || (ext == \"tres\")) {\n\t\treturn ResourceSaver::get_singleton()->save(img, file_name, ResourceSaver::FLAG_COMPRESS);\n\t}\n\n\tLOG(ERROR, \"No recognized file type. See docs for valid extensions\");\n\treturn FAILED;\n}\n\nRef<Image> Terrain3DData::layered_to_image(const MapType p_map_type) const {\n\tLOG(INFO, \"Generating a full sized image for all regions including empty regions\");\n\tMapType map_type = p_map_type;\n\tif (map_type >= TYPE_MAX) {\n\t\tmap_type = TYPE_HEIGHT;\n\t}\n\tVector2i top_left = V2I_ZERO;\n\tVector2i bottom_right = V2I_ZERO;\n\tfor (const Vector2i &region_loc : _region_locations) {\n\t\tLOG(DEBUG, \"Region location: \", region_loc);\n\t\tif (region_loc.x < top_left.x) {\n\t\t\ttop_left.x = region_loc.x;\n\t\t} else if (region_loc.x > bottom_right.x) {\n\t\t\tbottom_right.x = region_loc.x;\n\t\t}\n\t\tif (region_loc.y < top_left.y) {\n\t\t\ttop_left.y = region_loc.y;\n\t\t} else if (region_loc.y > bottom_right.y) {\n\t\t\tbottom_right.y = region_loc.y;\n\t\t}\n\t}\n\n\tLOG(DEBUG, \"Full range to cover all regions: \", top_left, \" to \", bottom_right);\n\tVector2i img_size = Vector2i(1 + bottom_right.x - top_left.x, 1 + bottom_right.y - top_left.y) * _region_size;\n\tLOG(DEBUG, \"Image size: \", img_size);\n\tRef<Image> img = Util::get_filled_image(img_size, COLOR[map_type], false, FORMAT[map_type]);\n\n\tfor (const Vector2i &region_loc : _region_locations) {\n\t\tVector2i img_location = (region_loc - top_left) * _region_size;\n\t\tLOG(DEBUG, \"Region to blit: \", region_loc, \" Export image coords: \", img_location);\n\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\tif (region) {\n\t\t\timg->blit_rect(region->get_map(map_type), Rect2i(V2I_ZERO, _region_sizev), img_location);\n\t\t}\n\t}\n\treturn img;\n}\n\nvoid Terrain3DData::dump(const bool verbose) const {\n\tLOG(MESG, \"_region_locations (\", _region_locations.size(), \"): \", _region_locations);\n\tArray keys = _regions.keys();\n\tLOG(MESG, \"_regions (\", keys.size(), \"):\");\n\tfor (const Vector2i &region_loc : keys) {\n\t\tconst Terrain3DRegion *region = get_region_ptr(region_loc);\n\t\tif (!region) {\n\t\t\tLOG(WARN, \"No region found at: \", region_loc);\n\t\t\tcontinue;\n\t\t}\n\t\tregion->dump(verbose);\n\t}\n\tif (verbose) {\n\t\tfor (int i = 0; i < _region_map.size(); i++) {\n\t\t\tif (_region_map[i]) {\n\t\t\t\tLOG(MESG, \"Region map array index: \", i, \" / \", _region_map.size() - 1, \", Region id: \", _region_map[i]);\n\t\t\t}\n\t\t}\n\t\tUtil::dump_maps(_height_maps, \"Height maps\");\n\t\tUtil::dump_gentex(_generated_height_maps, \"height\");\n\t\tUtil::dump_maps(_control_maps, \"Control maps\");\n\t\tUtil::dump_gentex(_generated_control_maps, \"control\");\n\t\tUtil::dump_maps(_color_maps, \"Color maps\");\n\t\tUtil::dump_gentex(_generated_color_maps, \"color\");\n\t}\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DData::_bind_methods() {\n\tBIND_ENUM_CONSTANT(HEIGHT_FILTER_NEAREST);\n\tBIND_ENUM_CONSTANT(HEIGHT_FILTER_MINIMUM);\n\n\tBIND_CONSTANT(REGION_MAP_SIZE);\n\n\tClassDB::bind_method(D_METHOD(\"get_region_count\"), &Terrain3DData::get_region_count);\n\tClassDB::bind_method(D_METHOD(\"set_region_locations\", \"region_locations\"), &Terrain3DData::set_region_locations);\n\tClassDB::bind_method(D_METHOD(\"get_region_locations\"), &Terrain3DData::get_region_locations);\n\tClassDB::bind_method(D_METHOD(\"get_regions_active\", \"copy\", \"deep\"), &Terrain3DData::get_regions_active, DEFVAL(false), DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"get_regions_all\"), &Terrain3DData::get_regions_all);\n\tClassDB::bind_method(D_METHOD(\"get_region_map\"), &Terrain3DData::get_region_map);\n\tClassDB::bind_static_method(\"Terrain3DData\", D_METHOD(\"get_region_map_index\", \"region_location\"), &Terrain3DData::get_region_map_index);\n\n\tClassDB::bind_method(D_METHOD(\"do_for_regions\", \"area\", \"callback\"), &Terrain3DData::do_for_regions);\n\tClassDB::bind_method(D_METHOD(\"change_region_size\", \"region_size\"), &Terrain3DData::change_region_size);\n\n\tClassDB::bind_method(D_METHOD(\"get_region_location\", \"global_position\"), &Terrain3DData::get_region_location);\n\tClassDB::bind_method(D_METHOD(\"get_region_id\", \"region_location\"), &Terrain3DData::get_region_id);\n\tClassDB::bind_method(D_METHOD(\"get_region_idp\", \"global_position\"), &Terrain3DData::get_region_idp);\n\n\tClassDB::bind_method(D_METHOD(\"has_region\", \"region_location\"), &Terrain3DData::has_region);\n\tClassDB::bind_method(D_METHOD(\"has_regionp\", \"global_position\"), &Terrain3DData::has_regionp);\n\tClassDB::bind_method(D_METHOD(\"get_region\", \"region_location\"), &Terrain3DData::get_region);\n\tClassDB::bind_method(D_METHOD(\"get_regionp\", \"global_position\"), &Terrain3DData::get_regionp);\n\n\tClassDB::bind_method(D_METHOD(\"set_region_modified\", \"region_location\", \"modified\"), &Terrain3DData::set_region_modified);\n\tClassDB::bind_method(D_METHOD(\"is_region_modified\", \"region_location\"), &Terrain3DData::is_region_modified);\n\tClassDB::bind_method(D_METHOD(\"set_region_deleted\", \"region_location\", \"deleted\"), &Terrain3DData::set_region_deleted);\n\tClassDB::bind_method(D_METHOD(\"is_region_deleted\", \"region_location\"), &Terrain3DData::is_region_deleted);\n\n\tClassDB::bind_method(D_METHOD(\"add_region_blankp\", \"global_position\", \"update\"), &Terrain3DData::add_region_blankp, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"add_region_blank\", \"region_location\", \"update\"), &Terrain3DData::add_region_blank, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"add_region\", \"region\", \"update\"), &Terrain3DData::add_region, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"remove_regionp\", \"global_position\", \"update\"), &Terrain3DData::remove_regionp, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"remove_regionl\", \"region_location\", \"update\"), &Terrain3DData::remove_regionl, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"remove_region\", \"region\", \"update\"), &Terrain3DData::remove_region, DEFVAL(true));\n\n\tClassDB::bind_method(D_METHOD(\"save_directory\", \"directory\"), &Terrain3DData::save_directory);\n\tClassDB::bind_method(D_METHOD(\"save_region\", \"region_location\", \"directory\", \"save_16_bit\"), &Terrain3DData::save_region, DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"load_directory\", \"directory\"), &Terrain3DData::load_directory);\n\tClassDB::bind_method(D_METHOD(\"load_region\", \"region_location\", \"directory\", \"update\"), &Terrain3DData::load_region, DEFVAL(true));\n\n\tClassDB::bind_method(D_METHOD(\"get_height_maps\"), &Terrain3DData::get_height_maps);\n\tClassDB::bind_method(D_METHOD(\"get_control_maps\"), &Terrain3DData::get_control_maps);\n\tClassDB::bind_method(D_METHOD(\"get_color_maps\"), &Terrain3DData::get_color_maps);\n\tClassDB::bind_method(D_METHOD(\"get_maps\", \"map_type\"), &Terrain3DData::get_maps);\n\tClassDB::bind_method(D_METHOD(\"update_maps\", \"map_type\", \"all_regions\", \"generate_mipmaps\"), &Terrain3DData::update_maps, DEFVAL(TYPE_MAX), DEFVAL(true), DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"get_height_maps_rid\"), &Terrain3DData::get_height_maps_rid);\n\tClassDB::bind_method(D_METHOD(\"get_control_maps_rid\"), &Terrain3DData::get_control_maps_rid);\n\tClassDB::bind_method(D_METHOD(\"get_color_maps_rid\"), &Terrain3DData::get_color_maps_rid);\n\n\tClassDB::bind_method(D_METHOD(\"set_pixel\", \"map_type\", \"global_position\", \"pixel\"), &Terrain3DData::set_pixel);\n\tClassDB::bind_method(D_METHOD(\"get_pixel\", \"map_type\", \"global_position\"), &Terrain3DData::get_pixel);\n\tClassDB::bind_method(D_METHOD(\"set_height\", \"global_position\", \"height\"), &Terrain3DData::set_height);\n\tClassDB::bind_method(D_METHOD(\"get_height\", \"global_position\"), &Terrain3DData::get_height);\n\tClassDB::bind_method(D_METHOD(\"set_color\", \"global_position\", \"color\"), &Terrain3DData::set_color);\n\tClassDB::bind_method(D_METHOD(\"get_color\", \"global_position\"), &Terrain3DData::get_color);\n\tClassDB::bind_method(D_METHOD(\"set_control\", \"global_position\", \"control\"), &Terrain3DData::set_control);\n\tClassDB::bind_method(D_METHOD(\"get_control\", \"global_position\"), &Terrain3DData::get_control);\n\tClassDB::bind_method(D_METHOD(\"set_roughness\", \"global_position\", \"roughness\"), &Terrain3DData::set_roughness);\n\tClassDB::bind_method(D_METHOD(\"get_roughness\", \"global_position\"), &Terrain3DData::get_roughness);\n\n\tClassDB::bind_method(D_METHOD(\"set_control_base_id\", \"global_position\", \"texture_id\"), &Terrain3DData::set_control_base_id);\n\tClassDB::bind_method(D_METHOD(\"get_control_base_id\", \"global_position\"), &Terrain3DData::get_control_base_id);\n\tClassDB::bind_method(D_METHOD(\"set_control_overlay_id\", \"global_position\", \"texture_id\"), &Terrain3DData::set_control_overlay_id);\n\tClassDB::bind_method(D_METHOD(\"get_control_overlay_id\", \"global_position\"), &Terrain3DData::get_control_overlay_id);\n\tClassDB::bind_method(D_METHOD(\"set_control_blend\", \"global_position\", \"blend_value\"), &Terrain3DData::set_control_blend);\n\tClassDB::bind_method(D_METHOD(\"get_control_blend\", \"global_position\"), &Terrain3DData::get_control_blend);\n\tClassDB::bind_method(D_METHOD(\"set_control_angle\", \"global_position\", \"degrees\"), &Terrain3DData::set_control_angle);\n\tClassDB::bind_method(D_METHOD(\"get_control_angle\", \"global_position\"), &Terrain3DData::get_control_angle);\n\tClassDB::bind_method(D_METHOD(\"set_control_scale\", \"global_position\", \"percentage_modifier\"), &Terrain3DData::set_control_scale);\n\tClassDB::bind_method(D_METHOD(\"get_control_scale\", \"global_position\"), &Terrain3DData::get_control_scale);\n\tClassDB::bind_method(D_METHOD(\"set_control_hole\", \"global_position\", \"enable\"), &Terrain3DData::set_control_hole);\n\tClassDB::bind_method(D_METHOD(\"get_control_hole\", \"global_position\"), &Terrain3DData::get_control_hole);\n\tClassDB::bind_method(D_METHOD(\"set_control_navigation\", \"global_position\", \"enable\"), &Terrain3DData::set_control_navigation);\n\tClassDB::bind_method(D_METHOD(\"get_control_navigation\", \"global_position\"), &Terrain3DData::get_control_navigation);\n\tClassDB::bind_method(D_METHOD(\"set_control_auto\", \"global_position\", \"enable\"), &Terrain3DData::set_control_auto);\n\tClassDB::bind_method(D_METHOD(\"get_control_auto\", \"global_position\"), &Terrain3DData::get_control_auto);\n\n\tClassDB::bind_method(D_METHOD(\"get_normal\", \"global_position\"), &Terrain3DData::get_normal);\n\tClassDB::bind_method(D_METHOD(\"is_in_slope\", \"global_position\", \"slope_range\", \"normal\"), &Terrain3DData::is_in_slope, DEFVAL(V3_ZERO));\n\tClassDB::bind_method(D_METHOD(\"get_texture_id\", \"global_position\"), &Terrain3DData::get_texture_id);\n\tClassDB::bind_method(D_METHOD(\"get_mesh_vertex\", \"lod\", \"filter\", \"global_position\"), &Terrain3DData::get_mesh_vertex);\n\n\tClassDB::bind_method(D_METHOD(\"get_height_range\"), &Terrain3DData::get_height_range);\n\tClassDB::bind_method(D_METHOD(\"calc_height_range\", \"recursive\"), &Terrain3DData::calc_height_range, DEFVAL(false));\n\n\tClassDB::bind_method(D_METHOD(\"import_images\", \"images\", \"global_position\", \"offset\", \"scale\"), &Terrain3DData::import_images, DEFVAL(V3_ZERO), DEFVAL(0.f), DEFVAL(1.f));\n\tClassDB::bind_method(D_METHOD(\"export_image\", \"file_name\", \"map_type\"), &Terrain3DData::export_image);\n\tClassDB::bind_method(D_METHOD(\"layered_to_image\", \"map_type\"), &Terrain3DData::layered_to_image);\n\tClassDB::bind_method(D_METHOD(\"dump\", \"verbose\"), &Terrain3DData::dump, DEFVAL(false));\n\n\tint ro_flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;\n\tADD_PROPERTY(PropertyInfo(Variant::ARRAY, \"region_locations\", PROPERTY_HINT_ARRAY_TYPE, \"Vector2i\", ro_flags), \"set_region_locations\", \"get_region_locations\");\n\tADD_PROPERTY(PropertyInfo(Variant::ARRAY, \"height_maps\", PROPERTY_HINT_ARRAY_TYPE, \"Image\", ro_flags), \"\", \"get_height_maps\");\n\tADD_PROPERTY(PropertyInfo(Variant::ARRAY, \"control_maps\", PROPERTY_HINT_ARRAY_TYPE, \"Image\", ro_flags), \"\", \"get_control_maps\");\n\tADD_PROPERTY(PropertyInfo(Variant::ARRAY, \"color_maps\", PROPERTY_HINT_ARRAY_TYPE, \"Image\", ro_flags), \"\", \"get_color_maps\");\n\n\tADD_SIGNAL(MethodInfo(\"maps_changed\"));\n\tADD_SIGNAL(MethodInfo(\"region_map_changed\"));\n\tADD_SIGNAL(MethodInfo(\"height_maps_changed\"));\n\tADD_SIGNAL(MethodInfo(\"control_maps_changed\"));\n\tADD_SIGNAL(MethodInfo(\"color_maps_changed\"));\n\tADD_SIGNAL(MethodInfo(\"maps_edited\", PropertyInfo(Variant::AABB, \"edited_area\")));\n}\n"
  },
  {
    "path": "src/terrain_3d_data.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_DATA_CLASS_H\n#define TERRAIN3D_DATA_CLASS_H\n\n#include \"constants.h\"\n#include \"generated_texture.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_region.h\"\n\nclass Terrain3D;\n\nclass Terrain3DData : public Object {\n\tGDCLASS(Terrain3DData, Object);\n\tCLASS_NAME();\n\tfriend Terrain3D;\n\npublic: // Constants\n\tstatic inline const real_t CURRENT_DATA_VERSION = 0.93f; // Current Data format version\n\tstatic inline const int REGION_MAP_SIZE = 32;\n\tstatic inline const Vector2i REGION_MAP_VSIZE = V2I(REGION_MAP_SIZE);\n\n\tenum HeightFilter {\n\t\tHEIGHT_FILTER_NEAREST,\n\t\tHEIGHT_FILTER_MINIMUM\n\t};\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\n\t// Data Settings & flags\n\tint _region_size = 0; // Set by Terrain3D::set_region_size\n\tVector2i _region_sizev = V2I(_region_size);\n\treal_t _vertex_spacing = 1.f; // Set by Terrain3D::set_vertex_spacing\n\n\tAABB _edited_area;\n\tVector2 _master_height_range = V2_ZERO;\n\n\t/////////\n\t// Terrain3DRegions house the maps, instances, and other data for each region.\n\t// Regions are dual indexed:\n\t// 1) By `region_location:Vector2i` as the primary key. This is the only stable index\n\t// so should be the main index for users.\n\t// 2) By `region_id:int`. This index changes on every add/remove, depends on load order,\n\t// and is not stable. It should not be relied on by users and is primarily for internal use.\n\n\t// Private functions should be indexed by region_id or region_location\n\t// Public functions by region_location or global_position\n\n\t// `_regions` stores all loaded Terrain3DRegions, indexed by region_location. If marked for\n\t// deletion they are removed from here upon saving, however they may stay in memory if tracked\n\t// by the Undo system.\n\tDictionary _regions; // Dict[region_location:Vector2i] -> Terrain3DRegion\n\n\t// All _active_ region maps are maintained in these secondary indices.\n\t// Regions are considered active if and only if they exist in `_region_locations`. The other\n\t// arrays are built off of this index; its order defines region_id.\n\t// The image arrays are converted to TextureArrays for the shader.\n\n\tTypedArray<Vector2i> _region_locations;\n\tTypedArray<Image> _height_maps;\n\tTypedArray<Image> _control_maps;\n\tTypedArray<Image> _color_maps;\n\n\t// Editing occurs on the Image arrays above, which are converted to Texture arrays\n\t// below for the shader.\n\n\t// 32x32 grid with region_id:int at its location, no region = 0, region_ids >= 1\n\tPackedInt32Array _region_map;\n\tbool _region_map_dirty = true;\n\n\t// These contain the TextureArray RIDs from the RenderingServer\n\tGeneratedTexture _generated_height_maps;\n\tGeneratedTexture _generated_control_maps;\n\tGeneratedTexture _generated_color_maps;\n\n\t// Functions\n\tvoid _clear();\n\tvoid _copy_paste_dfr(const Terrain3DRegion *p_src_region, const Rect2i &p_src_rect, const Rect2i &p_dst_rect, const Terrain3DRegion *p_dst_region);\n\npublic:\n\tTerrain3DData() {}\n\tvoid initialize(Terrain3D *p_terrain);\n\t~Terrain3DData() { _clear(); }\n\n\t// Regions\n\n\tint get_region_count() const { return _region_locations.size(); }\n\tvoid set_region_locations(const TypedArray<Vector2i> &p_locations);\n\tTypedArray<Vector2i> get_region_locations() const { return _region_locations; }\n\tTypedArray<Terrain3DRegion> get_regions_active(const bool p_copy = false, const bool p_deep = false) const;\n\tDictionary get_regions_all() const { return _regions; }\n\tPackedInt32Array get_region_map() const { return _region_map; }\n\tstatic int get_region_map_index(const Vector2i &p_region_loc);\n\n\tvoid do_for_regions(const Rect2i &p_area, const Callable &p_callback);\n\tvoid change_region_size(int region_size);\n\n\tVector2i get_region_location(const Vector3 &p_global_position) const;\n\tint get_region_id(const Vector2i &p_region_loc) const;\n\tint get_region_idp(const Vector3 &p_global_position) const;\n\n\tbool has_region(const Vector2i &p_region_loc) const { return get_region_id(p_region_loc) != -1; }\n\tbool has_regionp(const Vector3 &p_global_position) const { return get_region_idp(p_global_position) != -1; }\n\tRef<Terrain3DRegion> get_region(const Vector2i &p_region_loc) const;\n\tTerrain3DRegion *get_region_ptr(const Vector2i &p_region_loc) const;\n\ttemplate <typename T> // Catch invalid types. See note below in implementation.\n\tTerrain3DRegion *get_region_ptr(const T &p_region_loc) const = delete;\n\tRef<Terrain3DRegion> get_regionp(const Vector3 &p_global_position) const;\n\n\tvoid set_region_modified(const Vector2i &p_region_loc, const bool p_modified = true);\n\tbool is_region_modified(const Vector2i &p_region_loc) const;\n\tvoid set_region_deleted(const Vector2i &p_region_loc, const bool p_deleted = true);\n\tbool is_region_deleted(const Vector2i &p_region_loc) const;\n\n\tRef<Terrain3DRegion> add_region_blankp(const Vector3 &p_global_position, const bool p_update = true);\n\tRef<Terrain3DRegion> add_region_blank(const Vector2i &p_region_loc, const bool p_update = true);\n\tError add_region(const Ref<Terrain3DRegion> &p_region, const bool p_update = true);\n\tvoid remove_regionp(const Vector3 &p_global_position, const bool p_update = true);\n\tvoid remove_regionl(const Vector2i &p_region_loc, const bool p_update = true);\n\tvoid remove_region(const Ref<Terrain3DRegion> &p_region, const bool p_update = true);\n\n\t// File I/O\n\tvoid save_directory(const String &p_dir);\n\tvoid save_region(const Vector2i &p_region_loc, const String &p_dir, const bool p_16_bit = false);\n\tvoid load_directory(const String &p_dir);\n\tvoid load_region(const Vector2i &p_region_loc, const String &p_dir, const bool p_update = true);\n\n\t// Maps\n\tTypedArray<Image> get_height_maps() const { return _height_maps; }\n\tTypedArray<Image> get_control_maps() const { return _control_maps; }\n\tTypedArray<Image> get_color_maps() const { return _color_maps; }\n\tTypedArray<Image> get_maps(const MapType p_map_type) const;\n\tvoid update_maps(const MapType p_map_type = TYPE_MAX, const bool p_all_regions = true, const bool p_generate_mipmaps = false);\n\tRID get_height_maps_rid() const { return _generated_height_maps.get_rid(); }\n\tRID get_control_maps_rid() const { return _generated_control_maps.get_rid(); }\n\tRID get_color_maps_rid() const { return _generated_color_maps.get_rid(); }\n\n\tvoid set_pixel(const MapType p_map_type, const Vector3 &p_global_position, const Color &p_pixel);\n\tColor get_pixel(const MapType p_map_type, const Vector3 &p_global_position) const;\n\tvoid set_height(const Vector3 &p_global_position, const real_t p_height);\n\treal_t get_height(const Vector3 &p_global_position) const;\n\tvoid set_color(const Vector3 &p_global_position, const Color &p_color);\n\tColor get_color(const Vector3 &p_global_position) const;\n\tvoid set_control(const Vector3 &p_global_position, const uint32_t p_control);\n\tuint32_t get_control(const Vector3 &p_global_position) const;\n\tvoid set_roughness(const Vector3 &p_global_position, const real_t p_roughness);\n\treal_t get_roughness(const Vector3 &p_global_position) const;\n\n\t// Control Map\n\tvoid set_control_base_id(const Vector3 &p_global_position, const uint8_t p_base);\n\tuint32_t get_control_base_id(const Vector3 &p_global_position) const;\n\tvoid set_control_overlay_id(const Vector3 &p_global_position, const uint8_t p_overlay);\n\tuint32_t get_control_overlay_id(const Vector3 &p_global_position) const;\n\tvoid set_control_blend(const Vector3 &p_global_position, const real_t p_blend);\n\treal_t get_control_blend(const Vector3 &p_global_position) const;\n\tvoid set_control_angle(const Vector3 &p_global_position, const real_t p_angle);\n\treal_t get_control_angle(const Vector3 &p_global_position) const;\n\tvoid set_control_scale(const Vector3 &p_global_position, const real_t p_scale);\n\treal_t get_control_scale(const Vector3 &p_global_position) const;\n\tvoid set_control_hole(const Vector3 &p_global_position, const bool p_hole);\n\tbool get_control_hole(const Vector3 &p_global_position) const;\n\tvoid set_control_navigation(const Vector3 &p_global_position, const bool p_navigation);\n\tbool get_control_navigation(const Vector3 &p_global_position) const;\n\tvoid set_control_auto(const Vector3 &p_global_position, const bool p_auto);\n\tbool get_control_auto(const Vector3 &p_global_position) const;\n\n\tVector3 get_normal(const Vector3 &p_global_position) const;\n\tbool is_in_slope(const Vector3 &p_global_position, const Vector2 &p_slope_range, const Vector3 &p_normal = V3_ZERO) const;\n\tVector3 get_texture_id(const Vector3 &p_global_position) const;\n\tVector3 get_mesh_vertex(const int32_t p_lod, const HeightFilter p_filter, const Vector3 &p_global_position) const;\n\n\tvoid add_edited_area(const AABB &p_area);\n\tvoid clear_edited_area() { _edited_area = AABB(); }\n\tAABB get_edited_area() const { return _edited_area; }\n\n\tVector2 get_height_range() const { return _master_height_range; }\n\tvoid update_master_height(const real_t p_height);\n\tvoid update_master_heights(const Vector2 &p_low_high);\n\tvoid calc_height_range(const bool p_recursive = false);\n\n\tvoid import_images(const TypedArray<Image> &p_images, const Vector3 &p_global_position = V3_ZERO,\n\t\t\tconst real_t p_offset = 0.f, const real_t p_scale = 1.f);\n\tError export_image(const String &p_file_name, const MapType p_map_type = TYPE_HEIGHT) const;\n\tRef<Image> layered_to_image(const MapType p_map_type) const;\n\n\t// Utility\n\tvoid dump(const bool verbose = false) const;\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nVARIANT_ENUM_CAST(Terrain3DData::HeightFilter);\n\n// Inline Region Functions\n\n// Verifies the location is within the bounds of the _region_map array and\n// the world, returning the _region_map index, which contains the region_id.\n// Valid region locations are -16, -16 to 15, 15, or when offset: 0, 0 to 31, 31\n// If any bits other than 0x1F are set, it's out of bounds and returns -1\ninline int Terrain3DData::get_region_map_index(const Vector2i &p_region_loc) {\n\t// Offset world to positive values only\n\tVector2i loc = p_region_loc + (REGION_MAP_VSIZE / 2);\n\t// Catch values > 31\n\tif ((uint32_t(loc.x | loc.y) & uint32_t(~0x1F)) > 0) {\n\t\treturn -1;\n\t}\n\treturn loc.y * REGION_MAP_SIZE + loc.x;\n}\n\n// Returns a region location given a global position. No bounds checking nor data access.\ninline Vector2i Terrain3DData::get_region_location(const Vector3 &p_global_position) const {\n\tVector2 descaled_position = v3v2(p_global_position) / _vertex_spacing;\n\treturn Vector2i((descaled_position / real_t(_region_size)).floor());\n}\n\n// Returns id of any active region. -1 if out of bounds or no region, or region id\ninline int Terrain3DData::get_region_id(const Vector2i &p_region_loc) const {\n\tint map_index = get_region_map_index(p_region_loc);\n\tif (map_index >= 0) {\n\t\tint region_id = _region_map[map_index] - 1; // 0 = no region\n\t\tif (region_id >= 0 && region_id < _region_locations.size()) {\n\t\t\treturn region_id;\n\t\t}\n\t}\n\treturn -1;\n}\n\ninline int Terrain3DData::get_region_idp(const Vector3 &p_global_position) const {\n\treturn get_region_id(get_region_location(p_global_position));\n}\n\n// This function is slower than the version below, but safer when interacting with Godot, which requires\n// References. This includes backing up regions in the UndoRedoManager.\n// Ref<> has a pointer constructor, so a reference can be created with Ref<>(ptr). Godot detects the\n// pointer is already tracked and increments the reference counter.\n// Passing the pointer to a function with a Ref<> parameter works, and there's an implicit conversion to Ref.\n// However, let's require explicit conversions for clarity, so wrap a Ref around it:\n// eg. backup_region(Ref<Terrain3D>(raw_ptr));\n// Should be used for most functions in Editor and Instancer.\ninline Ref<Terrain3DRegion> Terrain3DData::get_region(const Vector2i &p_region_loc) const {\n\treturn _regions.get(p_region_loc, Ref<Terrain3DRegion>());\n}\n\n// Using the raw pointer is faster than creating a Ref<>. It can also safely be converted to a Ref as needed\n// with Ref<>(ptr). Use this function when retreiving regions frequently, eg looping over get_pixel().\n// Should be used for most data processing in Data, but not region handling.\n// Re overloaded template\n// get_region_ptr(region_locs[i]) worked with an implicit conversion of Variant::Vector2i to Vector2i.\n// However it also worked for Variant::Object, which silently sent invalid data.\n// The overloaded template was added to catch this. Pulling out of a dictionary/array gives a Variant,\n// so now explicit conversion is required, eg. get_region_ptr(Vector2i(locs[i])).\ninline Terrain3DRegion *Terrain3DData::get_region_ptr(const Vector2i &p_region_loc) const {\n\tif (_regions.has(p_region_loc)) {\n\t\treturn cast_to<Terrain3DRegion>(_regions[p_region_loc]);\n\t}\n\treturn nullptr;\n}\n\ninline Ref<Terrain3DRegion> Terrain3DData::get_regionp(const Vector3 &p_global_position) const {\n\treturn _regions.get(get_region_location(p_global_position), Ref<Terrain3DRegion>());\n}\n\n// Inline Map Functions\n\ninline void Terrain3DData::set_height(const Vector3 &p_global_position, const real_t p_height) {\n\tset_pixel(TYPE_HEIGHT, p_global_position, Color(p_height, 0.f, 0.f, 1.f));\n}\n\ninline void Terrain3DData::set_color(const Vector3 &p_global_position, const Color &p_color) {\n\tColor clr = p_color;\n\tclr.a = get_roughness(p_global_position);\n\tset_pixel(TYPE_COLOR, p_global_position, clr);\n}\n\ninline Color Terrain3DData::get_color(const Vector3 &p_global_position) const {\n\tColor clr = get_pixel(TYPE_COLOR, p_global_position);\n\tclr.a = 1.0f;\n\treturn clr;\n}\n\ninline void Terrain3DData::set_control(const Vector3 &p_global_position, const uint32_t p_control) {\n\tset_pixel(TYPE_CONTROL, p_global_position, Color(as_float(p_control), 0.f, 0.f, 1.f));\n}\n\ninline uint32_t Terrain3DData::get_control(const Vector3 &p_global_position) const {\n\treal_t val = get_pixel(TYPE_CONTROL, p_global_position).r;\n\treturn (std::isnan(val)) ? UINT32_MAX : as_uint(val);\n}\n\ninline void Terrain3DData::set_control_base_id(const Vector3 &p_global_position, const uint8_t p_base) {\n\tuint32_t control = get_control(p_global_position);\n\tuint8_t base = CLAMP(p_base, uint8_t(0), uint8_t(31));\n\tset_control(p_global_position, (control & ~(0x1F << 27)) | enc_base(base));\n}\n\ninline uint32_t Terrain3DData::get_control_base_id(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treturn control == UINT32_MAX ? UINT32_MAX : get_base(control);\n}\n\ninline void Terrain3DData::set_control_overlay_id(const Vector3 &p_global_position, const uint8_t p_overlay) {\n\tuint32_t control = get_control(p_global_position);\n\tuint8_t overlay = CLAMP(p_overlay, uint8_t(0), uint8_t(31));\n\tset_control(p_global_position, (control & ~(0x1F << 22)) | enc_overlay(overlay));\n}\n\ninline uint32_t Terrain3DData::get_control_overlay_id(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treturn control == UINT32_MAX ? UINT32_MAX : get_overlay(control);\n}\n\n// Expects 0.0 to 1.0 range\ninline void Terrain3DData::set_control_blend(const Vector3 &p_global_position, const real_t p_blend) {\n\tuint32_t control = get_control(p_global_position);\n\tuint8_t blend = uint8_t(CLAMP(Math::round(p_blend * 255.f), 0.f, 255.f));\n\tset_control(p_global_position, (control & ~(0xFF << 14)) | enc_blend(blend));\n}\n\ninline real_t Terrain3DData::get_control_blend(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treturn control == UINT32_MAX ? NAN : real_t(get_blend(control)) / 255.f;\n}\n\n// Expects angle in degrees\ninline void Terrain3DData::set_control_angle(const Vector3 &p_global_position, const real_t p_angle) {\n\tuint32_t control = get_control(p_global_position);\n\tuint8_t uvrotation = uint8_t(CLAMP(Math::round(p_angle / 22.5f), 0.f, 15.f));\n\tset_control(p_global_position, (control & ~(0xF << 10)) | enc_uv_rotation(uvrotation));\n}\n\n// returns angle in degrees\ninline real_t Terrain3DData::get_control_angle(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treal_t angle = real_t(get_uv_rotation(control)) * 22.5f;\n\treturn control == UINT32_MAX ? NAN : angle;\n}\n\n// Expects scale as a percentage modifier\ninline void Terrain3DData::set_control_scale(const Vector3 &p_global_position, const real_t p_scale) {\n\tuint32_t control = get_control(p_global_position);\n\tstd::array<uint32_t, 8> scale_align = { 5, 6, 7, 0, 1, 2, 3, 4 };\n\tuint8_t uvscale = scale_align[uint8_t(CLAMP(Math::round((p_scale + 60.f) / 20.f), 0.f, 7.f))];\n\tset_control(p_global_position, (control & ~(0x7 << 7)) | enc_uv_scale(uvscale));\n}\n\ninline real_t Terrain3DData::get_control_scale(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\tstd::array<real_t, 8> scale_values = { 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, -60.0f, -40.0f, -20.0f };\n\treal_t scale = scale_values[get_uv_scale(control)]; //select from array UI return values\n\treturn control == UINT32_MAX ? NAN : scale;\n}\n\ninline void Terrain3DData::set_control_hole(const Vector3 &p_global_position, const bool p_hole) {\n\tuint32_t control = get_control(p_global_position);\n\tset_control(p_global_position, (control & ~(0x1 << 2)) | enc_hole(p_hole));\n}\n\ninline bool Terrain3DData::get_control_hole(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treturn control == UINT32_MAX ? false : is_hole(control);\n}\n\ninline void Terrain3DData::set_control_navigation(const Vector3 &p_global_position, const bool p_navigation) {\n\tuint32_t control = get_control(p_global_position);\n\tset_control(p_global_position, (control & ~(0x1 << 1)) | enc_nav(p_navigation));\n}\n\ninline bool Terrain3DData::get_control_navigation(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treturn control == UINT32_MAX ? false : is_nav(control);\n}\n\ninline void Terrain3DData::set_control_auto(const Vector3 &p_global_position, const bool p_auto) {\n\tuint32_t control = get_control(p_global_position);\n\tset_control(p_global_position, (control & ~(0x1)) | enc_auto(p_auto));\n}\n\ninline bool Terrain3DData::get_control_auto(const Vector3 &p_global_position) const {\n\tuint32_t control = get_control(p_global_position);\n\treturn control == UINT32_MAX ? false : is_auto(control);\n}\n\ninline void Terrain3DData::set_roughness(const Vector3 &p_global_position, const real_t p_roughness) {\n\tColor clr = get_pixel(TYPE_COLOR, p_global_position);\n\tclr.a = p_roughness;\n\tset_pixel(TYPE_COLOR, p_global_position, clr);\n}\n\ninline real_t Terrain3DData::get_roughness(const Vector3 &p_global_position) const {\n\treturn get_pixel(TYPE_COLOR, p_global_position).a;\n}\n\ninline void Terrain3DData::update_master_height(const real_t p_height) {\n\tif (p_height < _master_height_range.x) {\n\t\t_master_height_range.x = p_height;\n\t} else if (p_height > _master_height_range.y) {\n\t\t_master_height_range.y = p_height;\n\t}\n}\n\ninline void Terrain3DData::update_master_heights(const Vector2 &p_low_high) {\n\tif (p_low_high.x < _master_height_range.x) {\n\t\t_master_height_range.x = p_low_high.x;\n\t}\n\tif (p_low_high.y > _master_height_range.y) {\n\t\t_master_height_range.y = p_low_high.y;\n\t}\n}\n\n#endif // TERRAIN3D_DATA_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_editor.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/time.hpp>\n#include <godot_cpp/variant/callable.hpp>\n\n#include \"constants.h\"\n#include \"logger.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_data.h\"\n#include \"terrain_3d_editor.h\"\n#include \"terrain_3d_util.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\n// Sends the whole region aabb to edited_area\nvoid Terrain3DEditor::_send_region_aabb(const Vector2i &p_region_loc, const Vector2 &p_height_range) {\n\tTerrain3D::RegionSize region_size = _terrain->get_region_size();\n\tAABB edited_area;\n\tedited_area.position = Vector3(p_region_loc.x * region_size, p_height_range.x, p_region_loc.y * region_size);\n\tedited_area.size = Vector3(region_size, p_height_range.y - p_height_range.x, region_size);\n\tedited_area.position *= _terrain->get_vertex_spacing();\n\tedited_area.size *= _terrain->get_vertex_spacing();\n\t_terrain->get_data()->add_edited_area(edited_area);\n}\n\n// Process location to add new region, mark as deleted, or just retrieve\nRef<Terrain3DRegion> Terrain3DEditor::_operate_region(const Vector2i &p_region_loc) {\n\tbool changed = false;\n\tVector2 height_range;\n\tTerrain3DData *data = _terrain->get_data();\n\n\t// Check if in bounds, limiting errors\n\tbool can_print = false;\n\tuint64_t ticks = Time::get_singleton()->get_ticks_msec();\n\tif (ticks - _last_region_bounds_error > 1000) {\n\t\t_last_region_bounds_error = ticks;\n\t\tcan_print = true;\n\t}\n\tif (data->get_region_map_index(p_region_loc) < 0) {\n\t\tif (can_print) {\n\t\t\tLOG(INFO, \"Location \", p_region_loc, \" out of bounds. Max: \",\n\t\t\t\t\t-Terrain3DData::REGION_MAP_SIZE / 2, \" to \", Terrain3DData::REGION_MAP_SIZE / 2 - 1);\n\t\t}\n\t\treturn Ref<Terrain3DRegion>();\n\t}\n\n\t// Get Region & dump data if debug\n\tRef<Terrain3DRegion> region = data->get_region(p_region_loc);\n\tif (can_print) {\n\t\tLOG(DEBUG, \"Tool: \", _tool, \" Op: \", _operation, \" processing region \", p_region_loc, \": \", ptr_to_str(*region));\n\t}\n\n\t// Create new region if location is null or deleted\n\tif (region.is_null() || (region.is_valid() && region->is_deleted())) {\n\t\t// And tool is Add Region, or Height + auto_regions\n\t\tif ((_tool == REGION && _operation == ADD) || ((_tool == SCULPT || _tool == HEIGHT) && _brush_data[\"auto_regions\"])) {\n\t\t\tLOG(DEBUG, \"Adding blank region at: \", p_region_loc, \", ptr: \", ptr_to_str(*region));\n\t\t\tregion = data->add_region_blank(p_region_loc);\n\t\t\tif (region.is_null()) {\n\t\t\t\tLOG(ERROR, \"A new region cannot be created\");\n\t\t\t\treturn region;\n\t\t\t}\n\t\t\t_edited_regions.push_back(region); // Ensure new region is added to the redo set\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\t// If removing region\n\telse if (region.is_valid() && _tool == REGION && _operation == SUBTRACT) {\n\t\tLOG(DEBUG, \"Removing region at: \", p_region_loc, \", ptr: \", ptr_to_str(*region));\n\t\t_original_regions.push_back(region);\n\t\theight_range = region->get_height_range();\n\t\t_terrain->get_data()->remove_region(region);\n\t\tchanged = true;\n\t}\n\n\tif (changed) {\n\t\t_added_removed_locations.push_back(p_region_loc);\n\t\tregion->set_modified(true);\n\t\t_send_region_aabb(p_region_loc, height_range);\n\t}\n\treturn region;\n}\n\nvoid Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_t p_camera_direction) {\n\tLOG(EXTREME, \"Operating at \", p_global_position, \" tool type \", _tool, \" op \", _operation);\n\n\tMapType map_type = _get_map_type();\n\tif (map_type == TYPE_MAX) {\n\t\tLOG(ERROR, \"Invalid tool selected\");\n\t\treturn;\n\t}\n\n\tint region_size = _terrain->get_region_size();\n\tVector2i region_vsize = V2I(region_size);\n\n\t// If no region and can't add one, skip whole function. Checked again later\n\tTerrain3DData *data = _terrain->get_data();\n\tif (!data->has_regionp(p_global_position) && (!_brush_data[\"auto_regions\"] || (_tool != SCULPT && _tool != HEIGHT))) {\n\t\treturn;\n\t}\n\n\tbool modifier_alt = _brush_data[\"modifier_alt\"];\n\tbool modifier_ctrl = _brush_data[\"modifier_ctrl\"];\n\t//bool modifier_shift = _brush_data[\"modifier_shift\"];\n\n\tImage *brush_image = cast_to<Image>(_brush_data[\"brush_image\"]);\n\tif (!brush_image) {\n\t\tLOG(ERROR, \"Invalid brush image. Returning\");\n\t\treturn;\n\t}\n\tVector2i img_size = _brush_data[\"brush_image_size\"];\n\treal_t brush_size = CLAMP(real_t(_brush_data.get(\"size\", 10.f)), 2.f, 4096.f); // Meters\n\n\t// Typicall we multiply mouse pressure & strength setting, but\n\t// * Mouse movement w/ button down has a pressure of 1\n\t// * Mouse clicks always have pressure of 0\n\t// * Pen movement pressure varies, sometimes lifting or clicking has a pressure of 0\n\t// If we're operating with a pressure of 0.001-.999 it's a pen\n\t// So if there's a 0 pressure operation >100ms after a pen operation, we assume it's\n\t// a mouse click. This occasionally catches a pen click, but avoids most pen lifts.\n\treal_t mouse_pressure = CLAMP(real_t(_brush_data.get(\"mouse_pressure\", 0.f)), 0.f, 1.f);\n\tif (mouse_pressure > CMP_EPSILON && mouse_pressure < 1.f) {\n\t\t_last_pen_tick = Time::get_singleton()->get_ticks_msec();\n\t}\n\tuint64_t ticks = Time::get_singleton()->get_ticks_msec();\n\tif (mouse_pressure < CMP_EPSILON && ticks - _last_pen_tick >= 100) {\n\t\tmouse_pressure = 1.f;\n\t}\n\treal_t strength = mouse_pressure * (real_t)_brush_data[\"strength\"];\n\n\treal_t height = _brush_data[\"height\"];\n\tColor color = _brush_data[\"color\"];\n\treal_t roughness = _brush_data[\"roughness\"];\n\n\tbool enable_texture = _brush_data[\"enable_texture\"];\n\tbool texture_filter = _brush_data[\"texture_filter\"];\n\tint margin = _brush_data[\"margin\"];\n\tint asset_id = _brush_data[\"asset_id\"];\n\n\tVector2 slope_range = _brush_data[\"slope\"];\n\tbool enable_angle = _brush_data[\"enable_angle\"];\n\tbool dynamic_angle = _brush_data[\"dynamic_angle\"];\n\treal_t angle = _brush_data[\"angle\"];\n\n\tbool enable_scale = _brush_data[\"enable_scale\"];\n\treal_t scale = _brush_data[\"scale\"];\n\n\treal_t gamma = _brush_data[\"gamma\"];\n\tPackedVector3Array gradient_points = _brush_data[\"gradient_points\"];\n\n\treal_t randf = UtilityFunctions::randf();\n\treal_t rot = randf * Math_PI * real_t(_brush_data[\"brush_spin_speed\"]);\n\tif (_brush_data[\"align_to_view\"]) {\n\t\trot += p_camera_direction;\n\t}\n\t// Rotate the decal to align with the brush\n\tif (_terrain->get_plugin()) {\n\t\tNode *node = cast_to<Node>(_terrain->get_plugin()->get(\"ui\"));\n\t\tif (node->has_method(\"set_decal_rotation\")) {\n\t\t\tnode->call(\"set_decal_rotation\", rot);\n\t\t}\n\t}\n\tAABB edited_area;\n\tedited_area.position = p_global_position - Vector3(brush_size, 0.f, brush_size) * .5f;\n\tedited_area.size = Vector3(brush_size, 0.f, brush_size);\n\n\tif (_tool == INSTANCER) {\n\t\tif (modifier_ctrl) {\n\t\t\t_terrain->get_instancer()->remove_instances(p_global_position, _brush_data);\n\t\t} else {\n\t\t\t_terrain->get_instancer()->add_instances(p_global_position, _brush_data);\n\t\t}\n\t\treturn;\n\t}\n\n\t// MAP Operations\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\n\t// save region count before brush pixel loop. Any regions added will have caused an Array\n\t// rebuild at the end of the last _operate() call, but until painting is finished we only\n\t// need to track if _added_removed_locations has changed between now and the end of the loop\n\tint regions_added_removed = _added_removed_locations.size();\n\n\tfor (real_t x = 0.f; x < brush_size; x += vertex_spacing) {\n\t\tfor (real_t y = 0.f; y < brush_size; y += vertex_spacing) {\n\t\t\tVector2 brush_offset = Vector2(x, y) - (V2(brush_size) * .5f);\n\t\t\tVector3 brush_global_position =\n\t\t\t\t\tVector3(p_global_position.x + brush_offset.x + .5f, p_global_position.y,\n\t\t\t\t\t\t\tp_global_position.z + brush_offset.y + .5f);\n\n\t\t\t// Get region for current brush pixel global position\n\t\t\tVector2i region_loc = data->get_region_location(brush_global_position);\n\t\t\tRef<Terrain3DRegion> region = _operate_region(region_loc);\n\t\t\t// If no region and can't make one, skip\n\t\t\tif (region.is_null()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get map for this region and tool\n\t\t\tImage *map = region->get_map_ptr(map_type);\n\t\t\tif (!map) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Identify position on map image\n\t\t\tVector2 uv_position = _get_uv_position(brush_global_position, region_size, vertex_spacing);\n\t\t\tVector2i map_pixel_position = Vector2i(uv_position * region_size);\n\t\t\tif (!_is_in_bounds(map_pixel_position, region_vsize)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVector2 brush_uv = Vector2(x, y) / brush_size;\n\t\t\tVector2i brush_pixel_position = Vector2i(_get_rotated_uv(brush_uv, rot) * img_size);\n\t\t\tif (!_is_in_bounds(brush_pixel_position, img_size)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVector3 edited_position = brush_global_position;\n\t\t\tedited_position.y = data->get_height(edited_position);\n\t\t\tedited_area = edited_area.expand(edited_position);\n\n\t\t\t// Start brushing on the map\n\t\t\treal_t brush_alpha = brush_image->get_pixelv(brush_pixel_position).r;\n\t\t\tbrush_alpha = real_t(Math::pow(double(brush_alpha), double(gamma)));\n\t\t\tbrush_alpha = std::isnan(brush_alpha) ? 0.f : brush_alpha;\n\t\t\tColor src = map->get_pixelv(map_pixel_position);\n\t\t\tColor dest = src;\n\n\t\t\tif (map_type == TYPE_HEIGHT) {\n\t\t\t\treal_t srcf = src.r;\n\t\t\t\t// In case data in existing map has nan or inf saved, check, and reset to real number if required.\n\t\t\t\tsrcf = std::isnan(srcf) ? 0.f : srcf;\n\t\t\t\treal_t destf = srcf;\n\n\t\t\t\tswitch (_operation) {\n\t\t\t\t\tcase ADD: {\n\t\t\t\t\t\tif (_tool == HEIGHT) {\n\t\t\t\t\t\t\t// Height\n\t\t\t\t\t\t\tdestf = Math::lerp(srcf, height, CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t} else if (modifier_alt && !std::isnan(p_global_position.y)) {\n\t\t\t\t\t\t\t// Lift troughs\n\t\t\t\t\t\t\treal_t brush_center_y = p_global_position.y + brush_alpha * strength;\n\t\t\t\t\t\t\tdestf = Math::clamp(brush_center_y, srcf, srcf + brush_alpha * strength);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Raise\n\t\t\t\t\t\t\tdestf = srcf + (brush_alpha * strength);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase SUBTRACT: {\n\t\t\t\t\t\tif (_tool == HEIGHT) {\n\t\t\t\t\t\t\t// Height, but GDScript has already picked height at cursor\n\t\t\t\t\t\t\tdestf = Math::lerp(srcf, height, CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t} else if (modifier_alt && !std::isnan(p_global_position.y)) {\n\t\t\t\t\t\t\t// Flatten peaks\n\t\t\t\t\t\t\treal_t brush_center_y = p_global_position.y - brush_alpha * strength;\n\t\t\t\t\t\t\tdestf = Math::clamp(brush_center_y, srcf - brush_alpha * strength, srcf);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Lower\n\t\t\t\t\t\t\tdestf = srcf - (brush_alpha * strength);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase AVERAGE: {\n\t\t\t\t\t\treal_t avg_default = _terrain->get_material()->get_world_background() == 0u ? srcf : 0.f;\n\t\t\t\t\t\treal_t avg = _average(AVG_HEIGHT, brush_global_position, srcf, avg_default);\n\t\t\t\t\t\tdestf = Math::lerp(srcf, avg, CLAMP(brush_alpha * strength * 2.f, .02f, 1.f));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase GRADIENT: {\n\t\t\t\t\t\tif (gradient_points.size() == 2) {\n\t\t\t\t\t\t\tVector3 point_1 = gradient_points[0];\n\t\t\t\t\t\t\tVector3 point_2 = gradient_points[1];\n\t\t\t\t\t\t\tVector2 point_1_xz = Vector2(point_1.x, point_1.z);\n\t\t\t\t\t\t\tVector2 point_2_xz = Vector2(point_2.x, point_2.z);\n\t\t\t\t\t\t\tVector2 dir = point_2_xz - point_1_xz;\n\t\t\t\t\t\t\tif (dir.length_squared() < 0.01f) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tVector2 brush_xz = Vector2(brush_global_position.x, brush_global_position.z);\n\n\t\t\t\t\t\t\tif (_operation_movement.length_squared() > 0.f) {\n\t\t\t\t\t\t\t\t// Ramp up/down only in the direction of movement, to avoid giving winding\n\t\t\t\t\t\t\t\t// paths one edge higher than the other.\n\t\t\t\t\t\t\t\tVector2 movement_xz = Vector2(_operation_movement.x, _operation_movement.z).normalized();\n\t\t\t\t\t\t\t\tVector2 offset = movement_xz * Vector2(brush_offset).dot(movement_xz);\n\t\t\t\t\t\t\t\tbrush_xz = Vector2(p_global_position.x + offset.x, p_global_position.z + offset.y);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treal_t weight = dir.normalized().dot(brush_xz - point_1_xz) / dir.length();\n\t\t\t\t\t\t\tweight = Math::clamp(weight, (real_t)0.0f, (real_t)1.0f);\n\t\t\t\t\t\t\treal_t height = Math::lerp(point_1.y, point_2.y, weight);\n\t\t\t\t\t\t\tdestf = Math::lerp(srcf, height, CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdest = Color(destf, 0.f, 0.f, 1.f);\n\t\t\t\tregion->update_height(destf);\n\t\t\t\tdata->update_master_height(destf);\n\t\t\t\tedited_position.y = destf;\n\t\t\t\tedited_area = edited_area.expand(edited_position);\n\n\t\t\t} else if (map_type == TYPE_CONTROL) {\n\t\t\t\t// Get current bit field from pixel\n\t\t\t\tuint32_t base_id = get_base(src.r);\n\t\t\t\tuint32_t overlay_id = get_overlay(src.r);\n\t\t\t\treal_t blend = real_t(get_blend(src.r)) / 255.f;\n\t\t\t\tuint32_t uvrotation = get_uv_rotation(src.r);\n\t\t\t\tuint32_t uvscale = get_uv_scale(src.r);\n\t\t\t\tbool hole = is_hole(src.r);\n\t\t\t\tbool navigation = is_nav(src.r);\n\t\t\t\tbool autoshader = is_auto(src.r);\n\t\t\t\t// Lookup to shift values saved to control map so that 0 (default) is the first entry\n\t\t\t\t// Shader scale array is aligned to match this.\n\t\t\t\tstd::array<uint32_t, 8> scale_align = { 5, 6, 7, 0, 1, 2, 3, 4 };\n\n\t\t\t\tswitch (_tool) {\n\t\t\t\t\tcase TEXTURE: {\n\t\t\t\t\t\tif (!data->is_in_slope(brush_global_position, slope_range)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tswitch (_operation) {\n\t\t\t\t\t\t\t// Base Paint\n\t\t\t\t\t\t\tcase REPLACE: {\n\t\t\t\t\t\t\t\tif (brush_alpha > 0.5f) {\n\t\t\t\t\t\t\t\t\tif (enable_texture) {\n\t\t\t\t\t\t\t\t\t\t// Set base & overlay texture\n\t\t\t\t\t\t\t\t\t\tbase_id = asset_id;\n\t\t\t\t\t\t\t\t\t\toverlay_id = asset_id;\n\t\t\t\t\t\t\t\t\t\t// Erase blend value\n\t\t\t\t\t\t\t\t\t\tblend = 0.f;\n\t\t\t\t\t\t\t\t\t\tautoshader = false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// Set angle & scale\n\t\t\t\t\t\t\t\t\tif (base_id == asset_id && enable_angle && !autoshader) {\n\t\t\t\t\t\t\t\t\t\tif (dynamic_angle) {\n\t\t\t\t\t\t\t\t\t\t\t// Angle from mouse movement.\n\t\t\t\t\t\t\t\t\t\t\tangle = Vector2(-_operation_movement.x, _operation_movement.z).angle();\n\t\t\t\t\t\t\t\t\t\t\t// Avoid negative, align texture \"up\" with mouse direction.\n\t\t\t\t\t\t\t\t\t\t\tangle = real_t(Math::fmod(Math::rad_to_deg(angle) + 450.f, real_t(360.f)));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t// Convert from degrees to 0 - 15 value range\n\t\t\t\t\t\t\t\t\t\tuvrotation = uint32_t(CLAMP(Math::round(angle / 22.5f), 0.f, 15.f));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (base_id == asset_id && enable_scale && !autoshader) {\n\t\t\t\t\t\t\t\t\t\t// Offset negative and convert from percentage to 0 - 7 bit value range\n\t\t\t\t\t\t\t\t\t\t// Maintain 0 = 0, remap negatives to end.\n\t\t\t\t\t\t\t\t\t\tuvscale = scale_align[uint8_t(CLAMP(Math::round((scale + 60.f) / 20.f), 0.f, 7.f))];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Add asset id, and increase weighting\n\t\t\t\t\t\t\tcase ADD: {\n\t\t\t\t\t\t\t\treal_t spray_strength = CLAMP(strength * 0.05f, 0.004f, .25f);\n\t\t\t\t\t\t\t\treal_t brush_value = CLAMP(brush_alpha * spray_strength, 0.f, 1.f);\n\t\t\t\t\t\t\t\tif (enable_texture && brush_alpha * strength * 11.f > 0.1f) {\n\t\t\t\t\t\t\t\t\t// Pick lowest weighted id, and lower to zero before setting new asset id.\n\t\t\t\t\t\t\t\t\tif (asset_id != base_id && asset_id != overlay_id) {\n\t\t\t\t\t\t\t\t\t\tif (modifier_alt) {\n\t\t\t\t\t\t\t\t\t\t\tif (blend < 0.5f) {\n\t\t\t\t\t\t\t\t\t\t\t\toverlay_id = asset_id;\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\tbase_id = asset_id;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tif (blend >= 0.5f) {\n\t\t\t\t\t\t\t\t\t\t\t\tblend = CLAMP(blend + brush_value, 0.f, 1.f);\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\tblend = CLAMP(blend - brush_value, 0.f, 1.f);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tif (blend <= 1.0f / 254.f) {\n\t\t\t\t\t\t\t\t\t\t\t\toverlay_id = asset_id;\n\t\t\t\t\t\t\t\t\t\t\t} else if (blend >= (1.f - 1.0f / 254.f)) {\n\t\t\t\t\t\t\t\t\t\t\t\tbase_id = asset_id;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (base_id == asset_id) {\n\t\t\t\t\t\t\t\t\t\tblend = CLAMP(blend - brush_value, 0.f, 1.f);\n\t\t\t\t\t\t\t\t\t\tif (brush_alpha > 0.5f && blend < 0.5f) {\n\t\t\t\t\t\t\t\t\t\t\tautoshader = false;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (overlay_id == asset_id) {\n\t\t\t\t\t\t\t\t\t\tblend = CLAMP(blend + brush_value, 0.f, 1.f);\n\t\t\t\t\t\t\t\t\t\tif (brush_alpha > 0.5f && blend >= 0.5f) {\n\t\t\t\t\t\t\t\t\t\t\tautoshader = false;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif ((base_id == asset_id && blend < 0.5f) || (overlay_id == asset_id && blend >= 0.5f)) {\n\t\t\t\t\t\t\t\t\t// Set angle & scale\n\t\t\t\t\t\t\t\t\tif (enable_angle && !autoshader && brush_alpha > 0.5f) {\n\t\t\t\t\t\t\t\t\t\tif (dynamic_angle) {\n\t\t\t\t\t\t\t\t\t\t\t// Angle from mouse movement.\n\t\t\t\t\t\t\t\t\t\t\tangle = Vector2(-_operation_movement.x, _operation_movement.z).angle();\n\t\t\t\t\t\t\t\t\t\t\t// Avoid negative, align texture \"up\" with mouse direction.\n\t\t\t\t\t\t\t\t\t\t\tangle = real_t(Math::fmod(Math::rad_to_deg(angle) + 450.f, real_t(360.f)));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t// Convert from degrees to 0 - 15 value range\n\t\t\t\t\t\t\t\t\t\tuvrotation = uint32_t(CLAMP(Math::round(angle / 22.5f), 0.f, 15.f));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (enable_scale && !autoshader && brush_alpha > 0.5f) {\n\t\t\t\t\t\t\t\t\t\t// Offset negative and convert from percentage to 0 - 7 bit value range\n\t\t\t\t\t\t\t\t\t\t// Maintain 0 = 0, remap negatives to end.\n\t\t\t\t\t\t\t\t\t\tuvscale = scale_align[uint8_t(CLAMP(Math::round((scale + 60.f) / 20.f), 0.f, 7.f))];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Lower weight of current asset id\n\t\t\t\t\t\t\tcase SUBTRACT: {\n\t\t\t\t\t\t\t\treal_t spray_strength = CLAMP(strength * 0.05f, 0.004f, .25f);\n\t\t\t\t\t\t\t\treal_t brush_value = CLAMP(brush_alpha * spray_strength, 0.f, 1.f);\n\t\t\t\t\t\t\t\tif (base_id == asset_id) {\n\t\t\t\t\t\t\t\t\tblend = CLAMP(blend + brush_value, 0.f, 1.f);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (overlay_id == asset_id) {\n\t\t\t\t\t\t\t\t\tblend = CLAMP(blend - brush_value, 0.f, 1.f);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcase AVERAGE: {\n\t\t\t\t\t\t\t\treal_t avg = _average(AVG_BLEND, brush_global_position, src.r, 0.f, modifier_alt) / 255.f;\n\t\t\t\t\t\t\t\tblend = Math::lerp(blend, avg, CLAMP(brush_alpha * strength * 2.f, .02f, 1.f));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase AUTOSHADER: {\n\t\t\t\t\t\tif (brush_alpha > 0.5f) {\n\t\t\t\t\t\t\tautoshader = (_operation == ADD);\n\t\t\t\t\t\t\tuvscale = 0.f;\n\t\t\t\t\t\t\tuvrotation = 0.f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase HOLES: {\n\t\t\t\t\t\tif (brush_alpha > 0.5f) {\n\t\t\t\t\t\t\thole = (_operation == ADD);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase NAVIGATION: {\n\t\t\t\t\t\tif (brush_alpha > 0.5f) {\n\t\t\t\t\t\t\tnavigation = (_operation == ADD);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Convert back to bitfield\n\t\t\t\tuint32_t blend_int = uint32_t(CLAMP(Math::round(blend * 255.f), 0.f, 255.f));\n\t\t\t\tuint32_t bits = enc_base(base_id) | enc_overlay(overlay_id) |\n\t\t\t\t\t\tenc_blend(blend_int) | enc_uv_rotation(uvrotation) |\n\t\t\t\t\t\tenc_uv_scale(uvscale) | enc_hole(hole) |\n\t\t\t\t\t\tenc_nav(navigation) | enc_auto(autoshader);\n\n\t\t\t\t// Write back to pixel in FORMAT_RF. Must be a 32-bit float\n\t\t\t\tdest = Color(as_float(bits), 0.f, 0.f, 1.f);\n\n\t\t\t} else if (map_type == TYPE_COLOR) {\n\t\t\t\t// Filter by visible texture\n\t\t\t\tif (texture_filter) {\n\t\t\t\t\tImage *cmap = region->get_map_ptr(TYPE_CONTROL);\n\t\t\t\t\tif (!cmap) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfloat src_ctrl = cmap->get_pixelv(map_pixel_position).r; // Must be float\n\t\t\t\t\tint tex_id = (get_blend(src_ctrl) > 110 - margin) ? get_overlay(src_ctrl) : get_base(src_ctrl);\n\t\t\t\t\tif (tex_id != asset_id) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!data->is_in_slope(brush_global_position, slope_range)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tswitch (_tool) {\n\t\t\t\t\tcase COLOR:\n\t\t\t\t\t\tswitch (_operation) {\n\t\t\t\t\t\t\tcase ADD: {\n\t\t\t\t\t\t\t\tdest = src.lerp(color, CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t\t\tdest.a = src.a;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase SUBTRACT: {\n\t\t\t\t\t\t\t\tdest = src.lerp(COLOR_WHITE, CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t\t\tdest.a = src.a;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase AVERAGE: {\n\t\t\t\t\t\t\t\tColor avg_col = _average(brush_global_position, src);\n\t\t\t\t\t\t\t\tdest = src.lerp(avg_col, CLAMP(brush_alpha * strength * 2.f, .02f, 1.f));\n\t\t\t\t\t\t\t\tdest.a = src.a;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ROUGHNESS:\n\t\t\t\t\t\t/* Roughness received from UI is -100 to 100. Changed to 0,1 before storing.\n\t\t\t\t\t\t * To convert 0,1 back to -100,100 use: 200 * (color.a - 0.5)\n\t\t\t\t\t\t * However Godot stores values as 8-bit ints. Roundtrip is = int(a*255)/255.0\n\t\t\t\t\t\t * Roughness 0 is saved as 0.5, but retreived is 0.498, or -0.4 roughness\n\t\t\t\t\t\t * We round the final amount in tool_settings.gd:_on_picked().\n\t\t\t\t\t\t */\n\t\t\t\t\t\tswitch (_operation) {\n\t\t\t\t\t\t\tcase ADD: {\n\t\t\t\t\t\t\t\treal_t target = .5f + .5f * roughness;\n\t\t\t\t\t\t\t\tdest.a = Math::lerp(real_t(src.a), target, CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t\t\tdest.a = float(int(dest.a * 255.f)) / 255.f; // Quantize explicitly so picked values match painted values\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase SUBTRACT: {\n\t\t\t\t\t\t\t\tdest.a = Math::lerp(real_t(src.a), real_t(.5f), CLAMP(brush_alpha * strength, 0.f, 1.f));\n\t\t\t\t\t\t\t\tdest.a = float(int(dest.a * 255.f)) / 255.f;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase AVERAGE: {\n\t\t\t\t\t\t\t\treal_t avg = _average(AVG_ROUGHNESS, brush_global_position, src.a, 0.5f);\n\t\t\t\t\t\t\t\tdest.a = Math::lerp(real_t(dest.a), avg, CLAMP(brush_alpha * strength * 2.f, .0f, 1.f));\n\t\t\t\t\t\t\t\tdest.a = float(int(dest.a * 255.f)) / 255.f;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbackup_region(region);\n\t\t\tmap->set_pixelv(map_pixel_position, dest);\n\t\t}\n\t}\n\t// Regenerate color mipmaps for edited regions\n\tif (map_type == TYPE_COLOR) {\n\t\tfor (Ref<Terrain3DRegion> region : _edited_regions) {\n\t\t\tif (region.is_valid()) {\n\t\t\t\tregion->get_map(map_type)->generate_mipmaps();\n\t\t\t}\n\t\t}\n\t}\n\t// If no added or removed regions, update only changed texture array layers from the edited regions in the rendering server\n\tif (_added_removed_locations.size() == regions_added_removed) {\n\t\tdata->update_maps(map_type, false, false);\n\t} else {\n\t\t// If region qty was changed, must fully rebuild the maps\n\t\tdata->update_maps(map_type, true, map_type == TYPE_COLOR);\n\t}\n\tdata->add_edited_area(edited_area);\n\n\tif (_tool == HOLES || _tool == HEIGHT || _tool == SCULPT) {\n\t\t_terrain->get_instancer()->update_transforms(edited_area);\n\t}\n\t// Update Dynamic / Editor collision\n\tif (_terrain->get_collision_mode() == Terrain3DCollision::DYNAMIC_EDITOR) {\n\t\t_terrain->get_collision()->update(V2I_MAX, true);\n\t}\n\tif (_tool == HEIGHT || _tool == SCULPT || _tool == TEXTURE || _tool == AUTOSHADER) {\n\t\t_terrain->snap();\n\t}\n}\n\nvoid Terrain3DEditor::_store_undo() {\n\tIS_INIT_COND_MESG(!_terrain->get_plugin(), \"_terrain isn't initialized, returning\", VOID);\n\tif (_tool < 0 || _tool >= TOOL_MAX) {\n\t\treturn;\n\t}\n\tLOG(DEBUG, \"Finalize undo & redo snapshots\");\n\tDictionary redo_data;\n\t// Store current locations; Original backed up in start_operation()\n\tredo_data[\"region_locations\"] = _terrain->get_data()->get_region_locations().duplicate();\n\t// Store original and current backups of edited regions\n\t_undo_data[\"edited_regions\"] = _original_regions;\n\tredo_data[\"edited_regions\"] = _edited_regions;\n\n\tif (Terrain3D::debug_level >= DEBUG) {\n\t\tLOG(DEBUG, \"Storing Original Regions:\");\n\t\tfor (const Ref<Terrain3DRegion> &region : _original_regions) {\n\t\t\tif (region.is_valid()) {\n\t\t\t\tregion->dump();\n\t\t\t}\n\t\t}\n\t\tLOG(DEBUG, \"Storing Edited Regions:\");\n\t\tfor (const Ref<Terrain3DRegion> &region : _edited_regions) {\n\t\t\tif (region.is_valid()) {\n\t\t\t\tregion->dump();\n\t\t\t}\n\t\t}\n\t}\n\n\t// Store regions that were removed or added\n\tif (_added_removed_locations.size() > 0) {\n\t\tif (_tool == REGION && _operation == SUBTRACT) {\n\t\t\t_undo_data[\"removed_regions\"] = _added_removed_locations;\n\t\t\tredo_data[\"added_regions\"] = _added_removed_locations;\n\t\t\tLOG(DEBUG, \"Removed regions: \", _added_removed_locations);\n\t\t} else {\n\t\t\t_undo_data[\"added_regions\"] = _added_removed_locations;\n\t\t\tredo_data[\"removed_regions\"] = _added_removed_locations;\n\t\t\tLOG(DEBUG, \"Added regions: \", _added_removed_locations);\n\t\t}\n\t}\n\n\tif (_terrain->get_data()->get_edited_area().has_volume()) {\n\t\t_undo_data[\"edited_area\"] = _terrain->get_data()->get_edited_area();\n\t\tredo_data[\"edited_area\"] = _terrain->get_data()->get_edited_area();\n\t\tLOG(DEBUG, \"Adding edited area to snapshots: \", _undo_data[\"edited_area\"]);\n\t}\n\n\t// Request the plugin store the undo/redo data.\n\tif (_terrain->get_plugin()->has_method(\"create_undo_action\")) {\n\t\tLOG(INFO, \"Storing undo snapshot\");\n\t\tString action_name = String(\"Terrain3D \") + OPNAME[_operation] + String(\" \") + TOOLNAME[_tool];\n\t\tLOG(DEBUG, \"Creating undo action: '\", action_name, \"'\");\n\t\t_terrain->get_plugin()->call(\"create_undo_action\", action_name);\n\n\t\tLOG(DEBUG, \"Storing undo snapshot: \");\n\t\tUtil::print_dict(\"_undo_data snapshot\", _undo_data, DEBUG);\n\t\t_terrain->get_plugin()->call(\"add_undo_method\", Callable(this, \"apply_undo\").bind(_undo_data.duplicate()));\n\n\t\tLOG(DEBUG, \"Storing redo snapshot: \");\n\t\tUtil::print_dict(\"redo_data snapshot\", redo_data, DEBUG);\n\t\t_terrain->get_plugin()->call(\"add_do_method\", Callable(this, \"apply_undo\").bind(redo_data));\n\n\t\tLOG(DEBUG, \"Committing undo action\");\n\t\t_terrain->get_plugin()->call(\"commit_action\", false);\n\t}\n}\n\nvoid Terrain3DEditor::_apply_undo(const Dictionary &p_data) {\n\tIS_INIT_COND_MESG(!_terrain->get_plugin(), \"_terrain isn't initialized, returning\", VOID);\n\tLOG(INFO, \"Applying Undo/Redo data\");\n\n\tTerrain3DData *data = _terrain->get_data();\n\n\tif (p_data.has(\"edited_regions\")) {\n\t\tUtil::print_arr(\"Edited regions\", p_data[\"edited_regions\"]);\n\t\tTypedArray<Terrain3DRegion> undo_regions = p_data[\"edited_regions\"];\n\t\tLOG(DEBUG, \"Backup has \", undo_regions.size(), \" edited regions\");\n\t\tfor (Ref<Terrain3DRegion> region : undo_regions) {\n\t\t\tif (region.is_null()) {\n\t\t\t\tLOG(ERROR, \"Null region saved in undo data. Please report this error.\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tregion->sanitize_maps(); // Live data may not have some maps so must be sanitized\n\t\t\tDictionary regions = data->get_regions_all();\n\t\t\tregions[region->get_location()] = region;\n\t\t\tregion->set_modified(true); // Tell update_maps() this region has layers that can be individually updated\n\t\t\tregion->set_edited(true); // Flag so update_maps() will include it\n\t\t\tregion->set_deleted(false); // Ensure region not marked for deletion\n\t\t\tif (Terrain3D::debug_level >= DEBUG) {\n\t\t\t\tLOG(DEBUG, \"Restoring region:\");\n\t\t\t\tregion->dump();\n\t\t\t}\n\t\t}\n\t}\n\n\tif (p_data.has(\"edited_area\")) {\n\t\tLOG(DEBUG, \"Edited area: \", p_data[\"edited_area\"]);\n\t\tdata->add_edited_area(p_data[\"edited_area\"]);\n\t}\n\n\tif (p_data.has(\"added_regions\")) {\n\t\tLOG(DEBUG, \"Added regions: \", p_data[\"added_regions\"]);\n\t\tTypedArray<Vector2i> region_locs = p_data[\"added_regions\"];\n\t\tfor (const Vector2i region_loc : region_locs) {\n\t\t\tRef<Terrain3DRegion> region = data->get_region(region_loc);\n\t\t\tif (region.is_valid()) {\n\t\t\t\tLOG(DEBUG, \"Marking region: \", region_loc, \" +deleted, +modified, \", ptr_to_str(*region));\n\t\t\t\tregion->set_deleted(true);\n\t\t\t\tregion->set_modified(true);\n\t\t\t}\n\t\t}\n\t}\n\tif (p_data.has(\"removed_regions\")) {\n\t\tLOG(DEBUG, \"Removed regions: \", p_data[\"removed_regions\"]);\n\t\tTypedArray<Vector2i> region_locs = p_data[\"removed_regions\"];\n\t\tfor (const Vector2i region_loc : region_locs) {\n\t\t\tRef<Terrain3DRegion> region = data->get_region(region_loc);\n\t\t\tif (region.is_valid()) {\n\t\t\t\tLOG(DEBUG, \"Marking region: \", region_loc, \" -deleted, +modified, \", ptr_to_str(*region));\n\t\t\t\tregion->set_deleted(false);\n\t\t\t\tregion->set_modified(true);\n\t\t\t\t_send_region_aabb(region_loc, region->get_height_range());\n\t\t\t}\n\t\t}\n\t}\n\n\t// After all regions are in place, reset the region map, which also calls update_maps\n\tif (p_data.has(\"region_locations\")) {\n\t\t// Load w/ duplicate or it gets a bit wonky undoing removed regions w/ saves\n\t\tTypedArray<Vector2i> locations = p_data[\"region_locations\"];\n\t\t_terrain->get_data()->set_region_locations(locations.duplicate());\n\t\tLOG(DEBUG, \"Locations(\", locations.size(), \"): \", locations);\n\t}\n\t// If this undo set modifies the region qty, we must rebuild the arrays. Otherwise we can update individual layers\n\tif (p_data.has(\"added_regions\") || p_data.has(\"removed_regions\")) {\n\t\tdata->update_maps(TYPE_MAX, true, false);\n\t} else {\n\t\tdata->update_maps(TYPE_MAX, false, false);\n\t}\n\t// After TextureArray updates clear edited regions flag.\n\tif (p_data.has(\"edited_regions\")) {\n\t\tTypedArray<Terrain3DRegion> undo_regions = p_data[\"edited_regions\"];\n\t\tfor (Ref<Terrain3DRegion> region : undo_regions) {\n\t\t\tif (region.is_valid()) {\n\t\t\t\tregion->set_edited(false);\n\t\t\t}\n\t\t}\n\t}\n\t_terrain->get_instancer()->update_mmis(-1, V2I_MAX, true);\n}\n\n// Returns average of height, blend (as real_t(0-255)), or roughness. Overloaded version handles average color\nreal_t Terrain3DEditor::_average(const AverageMode p_mode, const Vector3 &p_global_position, const real_t p_base,\n\t\tconst real_t p_nan_val, bool p_alt) const {\n\tIS_DATA_INIT(NAN);\n\tTerrain3DData *data = _terrain->get_data();\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\tVector3 left_position = p_global_position - Vector3(vertex_spacing, 0.f, 0.f);\n\tVector3 right_position = p_global_position + Vector3(vertex_spacing, 0.f, 0.f);\n\tVector3 down_position = p_global_position - Vector3(0.f, 0.f, vertex_spacing);\n\tVector3 up_position = p_global_position + Vector3(0.f, 0.f, vertex_spacing);\n\n\tMapType map_type;\n\tint index;\n\tswitch (p_mode) {\n\t\tcase AVG_HEIGHT:\n\t\t\tmap_type = TYPE_HEIGHT;\n\t\t\tindex = 0; // Red\n\t\t\tbreak;\n\t\tcase AVG_BLEND:\n\t\t\tmap_type = TYPE_CONTROL;\n\t\t\tindex = 0; // Red\n\t\t\tbreak;\n\t\tcase AVG_ROUGHNESS:\n\t\t\tmap_type = TYPE_COLOR;\n\t\t\tindex = 3; // Alpha\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\tColor pixel;\n\treal_t left, right, up, down;\n\tpixel = data->get_pixel(map_type, left_position);\n\tleft = std::isnan(pixel.r) ? p_nan_val : pixel[index];\n\tpixel = data->get_pixel(map_type, right_position);\n\tright = std::isnan(pixel.r) ? p_nan_val : pixel[index];\n\tpixel = data->get_pixel(map_type, up_position);\n\tup = std::isnan(pixel.r) ? p_nan_val : pixel[index];\n\tpixel = data->get_pixel(map_type, down_position);\n\tdown = std::isnan(pixel.r) ? p_nan_val : pixel[index];\n\n\tif (p_mode == AVG_BLEND) {\n\t\tif (p_alt) {\n\t\t\treturn real_t(get_blend(p_base) + get_blend(left) + get_blend(right) + get_blend(up) + get_blend(down)) * 0.2f;\n\t\t} else {\n\t\t\treturn Math::lerp(get_blend(p_base), 128.f, .1f);\n\t\t}\n\t} else {\n\t\treturn (p_base + left + right + up + down) * 0.2f;\n\t}\n}\n\nColor Terrain3DEditor::_average(const Vector3 &p_global_position, const Color &p_base) const {\n\tIS_DATA_INIT(COLOR_NAN);\n\tTerrain3DData *data = _terrain->get_data();\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\tVector3 left_position = p_global_position - Vector3(vertex_spacing, 0.f, 0.f);\n\tVector3 right_position = p_global_position + Vector3(vertex_spacing, 0.f, 0.f);\n\tVector3 down_position = p_global_position - Vector3(0.f, 0.f, vertex_spacing);\n\tVector3 up_position = p_global_position + Vector3(0.f, 0.f, vertex_spacing);\n\n\tColor left = data->get_pixel(TYPE_COLOR, left_position).srgb_to_linear();\n\tif (std::isnan(left.r)) {\n\t\tleft = COLOR_WHITE;\n\t}\n\tColor right = data->get_pixel(TYPE_COLOR, right_position).srgb_to_linear();\n\tif (std::isnan(right.r)) {\n\t\tright = COLOR_WHITE;\n\t}\n\tColor up = data->get_pixel(TYPE_COLOR, up_position).srgb_to_linear();\n\tif (std::isnan(up.r)) {\n\t\tup = COLOR_WHITE;\n\t}\n\tColor down = data->get_pixel(TYPE_COLOR, down_position).srgb_to_linear();\n\tif (std::isnan(down.r)) {\n\t\tdown = COLOR_WHITE;\n\t}\n\tColor base = p_base.srgb_to_linear();\n\treturn Color(\n\t\t\t(base.r + left.r + right.r + up.r + down.r) * 0.2f,\n\t\t\t(base.g + left.g + right.g + up.g + down.g) * 0.2f,\n\t\t\t(base.b + left.b + right.b + up.b + down.b) * 0.2f,\n\t\t\t1.f)\n\t\t\t.linear_to_srgb();\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\n// Santize and set incoming brush data w/ defaults and clamps\n// Only santizes data needed for the editor, other parameters (eg instancer) untouched here\nvoid Terrain3DEditor::set_brush_data(const Dictionary &p_data) {\n\t_brush_data = p_data; // Same instance. Anything could be inserted after this, eg mouse_pressure\n\n\t// Sanitize image and textures\n\tArray brush_images = p_data[\"brush\"];\n\tbool error = false;\n\tif (brush_images.size() == 2) {\n\t\tRef<Image> img = brush_images[0];\n\t\tif (img.is_valid() && !img->is_empty()) {\n\t\t\t_brush_data[\"brush_image\"] = img;\n\t\t\t_brush_data[\"brush_image_size\"] = img->get_size();\n\t\t} else {\n\t\t\tLOG(ERROR, \"Brush data doesn't contain a valid image\");\n\t\t}\n\t\tRef<Texture2D> tex = brush_images[1];\n\t\tif (tex.is_valid() && tex->get_width() > 0 && tex->get_height() > 0) {\n\t\t\t_brush_data[\"brush_texture\"] = tex;\n\t\t} else {\n\t\t\tLOG(ERROR, \"Brush data doesn't contain a valid texture\");\n\t\t}\n\t} else {\n\t\tLOG(ERROR, \"Brush data doesn't contain an image and texture\");\n\t}\n\n\t// Santize settings\n\t// size is redundantly clamped differently in _operate_map and instancer::add_transforms\n\t_brush_data[\"size\"] = CLAMP(real_t(p_data.get(\"size\", 10.f)), 0.1f, 4096.f); // Diameter in meters\n\t_brush_data[\"strength\"] = CLAMP(real_t(p_data.get(\"strength\", .1f)) * .01f, .01f, 1000.f); // 1-100k% (max of 1000m per click)\n\t// mouse_pressure injected in editor.gd and sanitized in _operate_map()\n\tVector2 slope = p_data.get(\"slope\", Vector2(0.f, 90.f));\n\tslope.x = CLAMP(slope.x, 0.f, 90.f);\n\tslope.y = CLAMP(slope.y, 0.f, 90.f);\n\t_brush_data[\"slope\"] = slope; // 0-90 (degrees)\n\t_brush_data[\"height\"] = CLAMP(real_t(p_data.get(\"height\", 0.f)), -65536.f, 65536.f); // Meters\n\tColor col = p_data.get(\"color\", COLOR_ROUGHNESS);\n\tcol.r = CLAMP(col.r, 0.f, 5.f);\n\tcol.g = CLAMP(col.g, 0.f, 5.f);\n\tcol.b = CLAMP(col.b, 0.f, 5.f);\n\tcol.a = CLAMP(col.a, 0.f, 1.f);\n\t_brush_data[\"color\"] = col;\n\t_brush_data[\"roughness\"] = CLAMP(real_t(p_data.get(\"roughness\", 0.f)), -100.f, 100.f) * .01f; // Percentage\n\n\t_brush_data[\"enable_texture\"] = p_data.get(\"enable_texture\", true);\n\t_brush_data[\"texture_filter\"] = p_data.get(\"texture_filter\", false);\n\t_brush_data[\"asset_id\"] = CLAMP(int(p_data.get(\"asset_id\", 0)), 0, ((_tool == INSTANCER) ? Terrain3DAssets::MAX_MESHES : Terrain3DAssets::MAX_TEXTURES) - 1);\n\t_brush_data[\"margin\"] = CLAMP(int(p_data.get(\"margin\", 0)), -100, 100);\n\n\t_brush_data[\"enable_angle\"] = p_data.get(\"enable_angle\", true);\n\t_brush_data[\"dynamic_angle\"] = p_data.get(\"dynamic_angle\", false);\n\t_brush_data[\"angle\"] = CLAMP(real_t(p_data.get(\"angle\", 0.f)), 0.f, 337.5f);\n\n\t_brush_data[\"enable_scale\"] = p_data.get(\"enable_scale\", true);\n\t_brush_data[\"scale\"] = CLAMP(real_t(p_data.get(\"scale\", 0.f)), -60.f, 80.f);\n\n\t_brush_data[\"auto_regions\"] = bool(p_data.get(\"auto_regions\", true));\n\t_brush_data[\"align_to_view\"] = bool(p_data.get(\"align_to_view\", true));\n\t_brush_data[\"gamma\"] = CLAMP(real_t(p_data.get(\"gamma\", 1.f)), 0.1f, 2.f);\n\t_brush_data[\"brush_spin_speed\"] = CLAMP(real_t(p_data.get(\"brush_spin_speed\", 0.f)), 0.f, 1.f);\n\t_brush_data[\"gradient_points\"] = p_data.get(\"gradient_points\", PackedVector3Array());\n\n\tUtil::print_dict(\"set_brush_data() Santized brush data:\", _brush_data, EXTREME);\n}\n\nvoid Terrain3DEditor::set_tool(const Tool p_tool) {\n\tTool old_tool = _tool;\n\tSET_IF_DIFF(_tool, CLAMP(p_tool, Tool(0), TOOL_MAX));\n\tif (_terrain && (_tool == Tool::NAVIGATION || old_tool == Tool::NAVIGATION || _tool == Tool::REGION || old_tool == Tool::REGION)) {\n\t\t_terrain->get_material()->update(Terrain3DMaterial::FULL_REBUILD);\n\t}\n}\n\nvoid Terrain3DEditor::set_operation(const Operation p_operation) {\n\tSET_IF_DIFF(_operation, CLAMP(p_operation, Operation(0), OP_MAX));\n}\n\n// Called on mouse click\nvoid Terrain3DEditor::start_operation(const Vector3 &p_global_position) {\n\tIS_DATA_INIT_MESG(\"Terrain isn't initialized\", VOID);\n\tLOG(INFO, \"Setting up undo snapshot\");\n\t_undo_data.clear();\n\t_undo_data[\"region_locations\"] = _terrain->get_data()->get_region_locations().duplicate();\n\t_is_operating = true;\n\t_original_regions = TypedArray<Terrain3DRegion>(); // New pointers instead of clear\n\t_edited_regions = TypedArray<Terrain3DRegion>();\n\t_added_removed_locations = TypedArray<Vector2i>();\n\t// Reset counter at start to ensure first click places an instance\n\t_terrain->get_instancer()->reset_density_counter();\n\t_terrain->get_data()->clear_edited_area();\n\t_operation_position = p_global_position;\n\t_operation_movement = V3_ZERO;\n}\n\n// Called on mouse movement with left mouse button down\nvoid Terrain3DEditor::operate(const Vector3 &p_global_position, const real_t p_camera_direction) {\n\tIS_DATA_INIT_MESG(\"Terrain isn't initialized\", VOID);\n\tif (!_is_operating) {\n\t\tLOG(ERROR, \"Run start_operation() before operating\");\n\t\treturn;\n\t}\n\t_operation_movement = p_global_position - _operation_position;\n\t_operation_position = p_global_position;\n\n\t// Convolve the last 8 movement events, we dont clear on mouse release\n\t// so as to make repeated mouse strokes in the same direction consistent\n\t_operation_movement_history.push_back(_operation_movement);\n\tif (_operation_movement_history.size() > 8) {\n\t\t_operation_movement_history.pop_front();\n\t}\n\t// size -1, dont add the last appended entry\n\tfor (int i = 0; i < _operation_movement_history.size() - 1; i++) {\n\t\t_operation_movement += _operation_movement_history[i];\n\t}\n\t_operation_movement *= 0.125f; // 1/8th\n\n\tif (_tool == REGION) {\n\t\t_operate_region(_terrain->get_data()->get_region_location(p_global_position));\n\t} else if (_tool >= 0 && _tool < TOOL_MAX) {\n\t\t_operate_map(p_global_position, p_camera_direction);\n\t}\n}\n\nvoid Terrain3DEditor::backup_region(const Ref<Terrain3DRegion> &p_region) {\n\t// Backup region once at the start of an operation. Once Edited is set, this is skipped\n\tif (_is_operating && p_region.is_valid() && !p_region->is_edited()) {\n\t\tLOG(DEBUG, \"Storing original copy of region: \", p_region->get_location());\n\t\tRef<Terrain3DRegion> orig_region = p_region->duplicate(true);\n\t\t_original_regions.push_back(orig_region);\n\t\t_edited_regions.push_back(p_region);\n\t\tp_region->set_edited(true);\n\t\tp_region->set_modified(true);\n\t\tif (Terrain3D::debug_level >= DEBUG) {\n\t\t\tLOG(DEBUG, \"Backup original region\");\n\t\t\torig_region->dump();\n\t\t\tLOG(DEBUG, \"Backup edited region\");\n\t\t\tp_region->dump();\n\t\t}\n\t}\n}\n\n// Called on left mouse button released\nvoid Terrain3DEditor::stop_operation() {\n\tIS_DATA_INIT_MESG(\"Terrain isn't initialized\", VOID);\n\t// If undo was created and terrain actually modified, store it\n\tLOG(DEBUG, \"Backed up regions: \", _original_regions.size(), \", Edited regions: \", _edited_regions.size(),\n\t\t\t\", Added/Removed regions: \", _added_removed_locations.size());\n\tif (_is_operating && (!_added_removed_locations.is_empty() || !_edited_regions.is_empty())) {\n\t\tfor (int i = 0; i < _edited_regions.size(); i++) {\n\t\t\tRef<Terrain3DRegion> region = _edited_regions[i];\n\t\t\tregion->set_edited(false);\n\t\t\t// Make duplicate for redo, necessary or redos won't work\n\t\t\tRef<Terrain3DRegion> redo_region = region->duplicate(true);\n\t\t\t_edited_regions[i] = redo_region;\n\t\t\tif (Terrain3D::debug_level >= DEBUG) {\n\t\t\t\tLOG(DEBUG, \"Edited region:\");\n\t\t\t\tregion->dump();\n\t\t\t\tLOG(DEBUG, \"Redo region:\");\n\t\t\t\tredo_region->dump();\n\t\t\t}\n\t\t}\n\t\t_store_undo();\n\t}\n\t_undo_data.clear();\n\t_original_regions = TypedArray<Terrain3DRegion>(); //New pointers instead of clear\n\t_edited_regions = TypedArray<Terrain3DRegion>();\n\t_added_removed_locations = TypedArray<Vector2i>();\n\t_terrain->get_data()->clear_edited_area();\n\t_is_operating = false;\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DEditor::_bind_methods() {\n\tBIND_ENUM_CONSTANT(ADD);\n\tBIND_ENUM_CONSTANT(SUBTRACT);\n\tBIND_ENUM_CONSTANT(REPLACE);\n\tBIND_ENUM_CONSTANT(AVERAGE);\n\tBIND_ENUM_CONSTANT(GRADIENT);\n\tBIND_ENUM_CONSTANT(OP_MAX);\n\n\tBIND_ENUM_CONSTANT(SCULPT);\n\tBIND_ENUM_CONSTANT(HEIGHT);\n\tBIND_ENUM_CONSTANT(TEXTURE);\n\tBIND_ENUM_CONSTANT(COLOR);\n\tBIND_ENUM_CONSTANT(ROUGHNESS);\n\tBIND_ENUM_CONSTANT(ANGLE);\n\tBIND_ENUM_CONSTANT(SCALE);\n\tBIND_ENUM_CONSTANT(AUTOSHADER);\n\tBIND_ENUM_CONSTANT(HOLES);\n\tBIND_ENUM_CONSTANT(NAVIGATION);\n\tBIND_ENUM_CONSTANT(INSTANCER);\n\tBIND_ENUM_CONSTANT(REGION);\n\tBIND_ENUM_CONSTANT(TOOL_MAX);\n\n\tClassDB::bind_method(D_METHOD(\"set_terrain\", \"terrain\"), &Terrain3DEditor::set_terrain);\n\tClassDB::bind_method(D_METHOD(\"get_terrain\"), &Terrain3DEditor::get_terrain);\n\n\tClassDB::bind_method(D_METHOD(\"set_brush_data\", \"data\"), &Terrain3DEditor::set_brush_data);\n\tClassDB::bind_method(D_METHOD(\"set_tool\", \"tool\"), &Terrain3DEditor::set_tool);\n\tClassDB::bind_method(D_METHOD(\"get_tool\"), &Terrain3DEditor::get_tool);\n\tClassDB::bind_method(D_METHOD(\"set_operation\", \"operation\"), &Terrain3DEditor::set_operation);\n\tClassDB::bind_method(D_METHOD(\"get_operation\"), &Terrain3DEditor::get_operation);\n\tClassDB::bind_method(D_METHOD(\"start_operation\", \"position\"), &Terrain3DEditor::start_operation);\n\tClassDB::bind_method(D_METHOD(\"is_operating\"), &Terrain3DEditor::is_operating);\n\tClassDB::bind_method(D_METHOD(\"operate\", \"position\", \"camera_direction\"), &Terrain3DEditor::operate);\n\tClassDB::bind_method(D_METHOD(\"backup_region\", \"region\"), &Terrain3DEditor::backup_region);\n\tClassDB::bind_method(D_METHOD(\"stop_operation\"), &Terrain3DEditor::stop_operation);\n\n\tClassDB::bind_method(D_METHOD(\"apply_undo\", \"data\"), &Terrain3DEditor::_apply_undo);\n}\n"
  },
  {
    "path": "src/terrain_3d_editor.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_EDITOR_CLASS_H\n#define TERRAIN3D_EDITOR_CLASS_H\n\n#include <godot_cpp/classes/image.hpp>\n#include <godot_cpp/classes/image_texture.hpp>\n\n#include \"terrain_3d.h\"\n#include \"terrain_3d_region.h\"\n\nclass Terrain3DEditor : public Object {\n\tGDCLASS(Terrain3DEditor, Object);\n\tCLASS_NAME();\n\npublic: // Constants\n\tenum Tool {\n\t\tREGION,\n\t\tSCULPT,\n\t\tHEIGHT,\n\t\tTEXTURE,\n\t\tCOLOR,\n\t\tROUGHNESS,\n\t\tAUTOSHADER,\n\t\tHOLES,\n\t\tNAVIGATION,\n\t\tINSTANCER,\n\t\tANGLE, // used for picking, TODO change to a picking tool\n\t\tSCALE, // used for picking\n\t\tTOOL_MAX,\n\t};\n\n\tstatic inline const char *TOOLNAME[] = {\n\t\t\"Region\",\n\t\t\"Sculpt\",\n\t\t\"Height\",\n\t\t\"Texture\",\n\t\t\"Color\",\n\t\t\"Roughness\",\n\t\t\"Auto Shader\",\n\t\t\"Holes\",\n\t\t\"Navigation\",\n\t\t\"Instancer\",\n\t\t\"Angle\",\n\t\t\"Scale\",\n\t\t\"TOOL_MAX\",\n\t};\n\n\tenum Operation {\n\t\tADD,\n\t\tSUBTRACT,\n\t\tREPLACE,\n\t\tAVERAGE,\n\t\tGRADIENT,\n\t\tOP_MAX,\n\t};\n\n\tstatic inline const char *OPNAME[] = {\n\t\t\"Add\",\n\t\t\"Subtract\",\n\t\t\"Replace\",\n\t\t\"Average\",\n\t\t\"Gradient\",\n\t\t\"OP_MAX\",\n\t};\n\n\tenum AverageMode {\n\t\tAVG_HEIGHT,\n\t\tAVG_BLEND,\n\t\tAVG_ROUGHNESS,\n\t};\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\n\t// Painter settings & variables\n\tTool _tool = REGION;\n\tOperation _operation = ADD;\n\tDictionary _brush_data;\n\tVector3 _operation_position = V3_ZERO;\n\tVector3 _operation_movement = V3_ZERO;\n\tArray _operation_movement_history;\n\tbool _is_operating = false;\n\tuint64_t _last_region_bounds_error = 0;\n\tTypedArray<Terrain3DRegion> _original_regions; // Queue for undo\n\tTypedArray<Terrain3DRegion> _edited_regions; // Queue for redo\n\tTypedArray<Vector2i> _added_removed_locations; // Queue for added/removed locations\n\tAABB _modified_area;\n\tDictionary _undo_data; // See _get_undo_data for definition\n\tuint64_t _last_pen_tick = 0;\n\n\tvoid _send_region_aabb(const Vector2i &p_region_loc, const Vector2 &p_height_range = V2_ZERO);\n\tRef<Terrain3DRegion> _operate_region(const Vector2i &p_region_loc);\n\tvoid _operate_map(const Vector3 &p_global_position, const real_t p_camera_direction);\n\tMapType _get_map_type() const;\n\tbool _is_in_bounds(const Point2i &p_pixel, const Point2i &p_size) const;\n\tVector2 _get_uv_position(const Vector3 &p_global_position, const int p_region_size, const real_t p_vertex_spacing) const;\n\tVector2 _get_rotated_uv(const Vector2 &p_uv, const real_t p_angle) const;\n\tvoid _store_undo();\n\tvoid _apply_undo(const Dictionary &p_data);\n\treal_t _average(const AverageMode p_mode, const Vector3 &p_global_position, const real_t p_base, const real_t p_nan_val = 0.f, bool p_alt = false) const;\n\tColor _average(const Vector3 &p_global_position, const Color &p_base) const;\n\npublic:\n\tTerrain3DEditor() {}\n\t~Terrain3DEditor() {}\n\n\tvoid set_terrain(Terrain3D *p_terrain) { _terrain = p_terrain; }\n\tTerrain3D *get_terrain() const { return _terrain; }\n\n\tvoid set_brush_data(const Dictionary &p_data);\n\tDictionary get_brush_data() const { return _brush_data; };\n\tvoid set_tool(const Tool p_tool);\n\tTool get_tool() const { return _tool; }\n\tvoid set_operation(const Operation p_operation);\n\tOperation get_operation() const { return _operation; }\n\n\tvoid start_operation(const Vector3 &p_global_position);\n\tbool is_operating() const { return _is_operating; }\n\tvoid operate(const Vector3 &p_global_position, const real_t p_camera_direction);\n\tvoid backup_region(const Ref<Terrain3DRegion> &p_region);\n\tvoid stop_operation();\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nVARIANT_ENUM_CAST(Terrain3DEditor::Operation);\nVARIANT_ENUM_CAST(Terrain3DEditor::Tool);\n\n// Inline functions\n\ninline MapType Terrain3DEditor::_get_map_type() const {\n\tswitch (_tool) {\n\t\tcase SCULPT:\n\t\tcase HEIGHT:\n\t\tcase INSTANCER:\n\t\t\treturn TYPE_HEIGHT;\n\t\t\tbreak;\n\t\tcase TEXTURE:\n\t\tcase AUTOSHADER:\n\t\tcase HOLES:\n\t\tcase NAVIGATION:\n\t\tcase ANGLE:\n\t\tcase SCALE:\n\t\t\treturn TYPE_CONTROL;\n\t\t\tbreak;\n\t\tcase COLOR:\n\t\tcase ROUGHNESS:\n\t\t\treturn TYPE_COLOR;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn TYPE_MAX;\n\t}\n}\n\ninline bool Terrain3DEditor::_is_in_bounds(const Point2i &p_pixel, const Point2i &p_size) const {\n\tbool positive = p_pixel.x >= 0 && p_pixel.y >= 0;\n\tbool less_than_max = p_pixel.x < p_size.x && p_pixel.y < p_size.y;\n\treturn positive && less_than_max;\n}\n\ninline Vector2 Terrain3DEditor::_get_uv_position(const Vector3 &p_global_position, const int p_region_size, const real_t p_vertex_spacing) const {\n\tVector2 descaled_position_2d = Vector2(p_global_position.x, p_global_position.z) / p_vertex_spacing;\n\tVector2 region_position = descaled_position_2d / real_t(p_region_size);\n\tregion_position = region_position.floor();\n\tVector2 uv_position = (descaled_position_2d / real_t(p_region_size)) - region_position;\n\treturn uv_position;\n}\n\ninline Vector2 Terrain3DEditor::_get_rotated_uv(const Vector2 &p_uv, const real_t p_angle) const {\n\tVector2 rotation_offset = V2(0.5f);\n\tVector2 uv = (p_uv - rotation_offset).rotated(p_angle) + rotation_offset;\n\treturn uv.clamp(V2_ZERO, V2(1.f));\n}\n\n#endif // TERRAIN3D_EDITOR_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_instancer.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/resource_saver.hpp>\n#include <godot_cpp/classes/world3d.hpp>\n\n#include \"constants.h\"\n#include \"logger.h\"\n#include \"terrain_3d_instancer.h\"\n#include \"terrain_3d_region.h\"\n#include \"terrain_3d_util.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\n// Creates MMIs based on Multimesh data stored in Terrain3DRegions\nvoid Terrain3DInstancer::_process_updates() {\n\tif (_queued_updates.empty()) {\n\t\tif (RS->is_connected(\"frame_pre_draw\", callable_mp(this, &Terrain3DInstancer::_process_updates))) {\n\t\t\tLOG(DEBUG, \"Disconnect from RS::frame_pre_draw signal\");\n\t\t\tRS->disconnect(\"frame_pre_draw\", callable_mp(this, &Terrain3DInstancer::_process_updates));\n\t\t}\n\t\treturn;\n\t}\n\tIS_DATA_INIT(VOID);\n\tif (!_terrain->is_inside_tree()) {\n\t\treturn;\n\t}\n\tconst Terrain3DData *data = _terrain->get_data();\n\tTypedArray<Vector2i> region_locations = data->get_region_locations();\n\tint mesh_count = _terrain->get_assets()->get_mesh_count();\n\n\t// Process all regions/mesh_ids sentinel and exit\n\tbool update_all = false;\n\tif (_queued_updates.find({ V2I_MAX, -2 }) != _queued_updates.end()) {\n\t\tdestroy();\n\t\tupdate_all = true;\n\t} else if (_queued_updates.find({ V2I_MAX, -1 }) != _queued_updates.end()) {\n\t\tupdate_all = true;\n\t}\n\n\tif (update_all) {\n\t\tLOG(DEBUG, \"Updating all regions, all mesh_ids\");\n\t\tfor (const Vector2i &region_loc : region_locations) {\n\t\t\tconst Terrain3DRegion *region = data->get_region_ptr(region_loc);\n\t\t\tif (!region) {\n\t\t\t\tLOG(WARN, \"Errant null region found at: \", region_loc);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (int mesh_id = 0; mesh_id < mesh_count; mesh_id++) {\n\t\t\t\tauto pair = std::make_pair(region_loc, mesh_id);\n\t\t\t\tif (region->get_instances().has(mesh_id)) {\n\t\t\t\t\t_update_mmi_by_region(region, mesh_id);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_queued_updates.clear();\n\t\t_terrain->get_assets()->load_pending_meshes();\n\t\treturn;\n\t}\n\n\t// Identify pairs to process in a de-duplicating Set\n\tV2IIntPair to_process;\n\n\t// Process queued pairs with at least one specific element\n\tfor (const auto &[queued_loc, queued_mesh] : _queued_updates) {\n\t\tif (queued_loc == V2I_MAX && queued_mesh < 0 || queued_mesh >= mesh_count) {\n\t\t\tcontinue; // Skip sentinels handled above\n\t\t}\n\t\t// If all regions for specific mesh_id\n\t\tif (queued_loc == V2I_MAX && queued_mesh >= 0) {\n\t\t\tfor (const Vector2i &region_loc : region_locations) {\n\t\t\t\tauto pair = std::make_pair(region_loc, queued_mesh);\n\t\t\t\tto_process.emplace(pair);\n\t\t\t}\n\t\t} else {\n\t\t\t// Else one specific region\n\t\t\tif (queued_mesh == -1) {\n\t\t\t\t// All mesh_ids for this region\n\t\t\t\tfor (int mesh_id = 0; mesh_id < mesh_count; mesh_id++) {\n\t\t\t\t\tauto pair = std::make_pair(queued_loc, mesh_id);\n\t\t\t\t\tto_process.emplace(pair);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Specific region + mesh - most common case\n\t\t\t\tauto pair = std::make_pair(queued_loc, queued_mesh);\n\t\t\t\tto_process.emplace(pair);\n\t\t\t}\n\t\t}\n\t}\n\tLOG(DEBUG, \"Processing \", (int)to_process.size(), \" queued updates\");\n\tfor (const auto &[region_loc, mesh_id] : to_process) {\n\t\tconst Terrain3DRegion *region = data->get_region_ptr(region_loc);\n\t\tif (!region) {\n\t\t\tLOG(WARN, \"Errant null region found at: \", region_loc);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!region->get_instances().has(mesh_id)) {\n\t\t\tcontinue;\n\t\t}\n\t\t_update_mmi_by_region(region, mesh_id);\n\t}\n\t_queued_updates.clear();\n\t_terrain->get_assets()->load_pending_meshes();\n}\n\nvoid Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region, const int p_mesh_id) {\n\tif (!p_region) {\n\t\tLOG(ERROR, \"p_region is null\");\n\t\treturn;\n\t}\n\tif (p_mesh_id < 0 || p_mesh_id >= Terrain3DAssets::MAX_MESHES) {\n\t\tLOG(ERROR, \"p_mesh_id is out of bounds\");\n\t\treturn;\n\t}\n\tVector2i region_loc = p_region->get_location();\n\tDictionary mesh_inst_dict = p_region->get_instances();\n\n\t// Verify mesh id is valid, enabled, and has MeshInstance3Ds\n\tRef<Terrain3DMeshAsset> ma = _terrain->get_assets()->get_mesh_asset(p_mesh_id);\n\tif (!ma.is_valid()) {\n\t\tLOG(WARN, \"MeshAsset \", p_mesh_id, \" is null, destroying MMIs\");\n\t\t_destroy_mmi_by_location(region_loc, p_mesh_id); // Clean up if orphaned\n\t\treturn;\n\t}\n\tif (!ma->is_enabled()) {\n\t\tLOG(DEBUG, \"Disabling mesh \", p_mesh_id, \" in region \", region_loc, \": destroying MMIs\");\n\t\t_destroy_mmi_by_location(region_loc, p_mesh_id);\n\t\treturn;\n\t}\n\tif (ma->get_lod_count() == 0) {\n\t\tLOG(WARN, \"MeshAsset \", p_mesh_id, \" valid but has no meshes, destroying MMIs\");\n\t\t_destroy_mmi_by_location(region_loc, p_mesh_id);\n\t\treturn;\n\t}\n\n\t// Process cells\n\tDictionary cell_inst_dict = mesh_inst_dict[p_mesh_id];\n\tArray cell_locations = cell_inst_dict.keys();\n\n\tfor (const Vector2i &cell : cell_locations) {\n\t\tArray triple = cell_inst_dict[cell];\n\t\tif (triple.size() < 3) {\n\t\t\tLOG(WARN, \"Triple is empty for region, \", region_loc, \", cell \", cell);\n\t\t\tcontinue;\n\t\t}\n\t\tTypedArray<Transform3D> xforms = triple[0];\n\t\tPackedColorArray colors = triple[1];\n\t\tbool modified = triple[2];\n\n\t\t// Clean MMIs if xforms have been removed\n\t\tif (xforms.size() == 0) {\n\t\t\tLOG(EXTREME, \"Empty cell in region \", region_loc, \" mesh \", p_mesh_id, \" cell \", cell, \": destroying MMIs\");\n\t\t\t_destroy_mmi_by_cell(region_loc, p_mesh_id, cell);\n\t\t\tcontinue;\n\t\t}\n\t\t// Clean MMIs f/ LODs not used\n\t\tfor (int lod = 0; lod < Terrain3DMeshAsset::MAX_LOD_COUNT; lod++) {\n\t\t\tif (lod > ma->get_last_lod() || (ma->get_cast_shadows() == SHADOWS_ONLY && (lod > ma->get_last_shadow_lod() || lod < ma->get_shadow_impostor()))) {\n\t\t\t\t_destroy_mmi_by_cell(region_loc, p_mesh_id, cell, lod);\n\t\t\t\tLOG(EXTREME, \"Destroyed old MMIs mesh \", p_mesh_id, \" cell \", cell, \", LOD \", lod);\n\t\t\t}\n\t\t}\n\n\t\t// Clean Shadow MMIs\n\t\tbool shadow_lod_disabled = (ma->get_shadow_impostor() == 0 ||\n\t\t\t\tma->get_cast_shadows() == SHADOWS_OFF);\n\t\tif (shadow_lod_disabled) {\n\t\t\t_destroy_mmi_by_cell(region_loc, p_mesh_id, cell, Terrain3DMeshAsset::SHADOW_LOD_ID);\n\t\t\tLOG(EXTREME, \"Destroyed stale shadow MMI mesh \", p_mesh_id, \" for disabled impostor in cell \", cell);\n\t\t}\n\n\t\t// Setup MMIs for each LOD + shadows\n\n\t\t// Get or create mesh dict (defined here as cleanup above might invalidate it)\n\t\tMeshMMIDict &mesh_mmi_dict = _mmi_rids[region_loc];\n\t\tRID shadow_impostor_source_mm;\n\n\t\tfor (int lod = ma->get_last_lod(); lod >= Terrain3DMeshAsset::SHADOW_LOD_ID; lod--) {\n\t\t\t// Don't create shadow MMI if not needed\n\t\t\tif (lod == Terrain3DMeshAsset::SHADOW_LOD_ID && shadow_lod_disabled) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Don't create MMIs for certain lods in Shadows only\n\t\t\tif (ma->get_cast_shadows() == SHADOWS_ONLY &&\n\t\t\t\t\tlod >= 0 && (lod > ma->get_last_shadow_lod() || lod < ma->get_shadow_impostor())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get or create MMI - [] creates key if missing\n\t\t\tVector2i mesh_key(p_mesh_id, lod);\n\t\t\tCellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];\n\t\t\tRID &mmi = cell_mmi_dict[cell].first; // null if missing\n\t\t\tif (!mmi.is_valid()) {\n\t\t\t\tmmi = RS->instance_create();\n\t\t\t\tRS->instance_set_scenario(mmi, _terrain->get_world_3d()->get_scenario());\n\t\t\t\tmodified = true; // New MMI needs full update\n\t\t\t}\n\n\t\t\t// Always update MMI propertiess\n\t\t\tif (ma->is_highlighted()) {\n\t\t\t\tRS->instance_geometry_set_material_override(mmi, ma->get_highlight_material().is_valid() ? ma->get_highlight_material()->get_rid() : RID());\n\t\t\t\tRS->instance_geometry_set_material_overlay(mmi, RID());\n\t\t\t} else {\n\t\t\t\tRS->instance_geometry_set_material_override(mmi, ma->get_material_override().is_valid() ? ma->get_material_override()->get_rid() : RID());\n\t\t\t\tRS->instance_geometry_set_material_overlay(mmi, ma->get_material_overlay().is_valid() ? ma->get_material_overlay()->get_rid() : RID());\n\t\t\t}\n\t\t\tRS->instance_geometry_set_cast_shadows_setting(mmi, ma->get_lod_cast_shadows(lod));\n\t\t\tRS->instance_set_layer_mask(mmi, ma->get_visibility_layers());\n\t\t\t_set_mmi_lod_ranges(mmi, ma, lod);\n\n\t\t\t// Reposition MMI to region location\n\t\t\tTransform3D t = Transform3D();\n\t\t\tint region_size = p_region->get_region_size();\n\t\t\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\t\t\tt.origin.x += region_loc.x * region_size * vertex_spacing;\n\t\t\tt.origin.z += region_loc.y * region_size * vertex_spacing;\n\t\t\tRS->instance_set_transform(mmi, t);\n\n\t\t\tRID &mm = cell_mmi_dict[cell].second;\n\t\t\t// Only recreate MultiMesh if modified/no existing (with override for shadow)\n\t\t\t// Always update shadow MMI (source may have changed)\n\t\t\tif (modified || !mm.is_valid() || lod == Terrain3DMeshAsset::SHADOW_LOD_ID) {\n\t\t\t\t// Subtract previous instance count for this cell\n\t\t\t\tint instance_count_mod = 0;\n\t\t\t\tif (mm.is_valid() && lod == _get_master_lod(ma)) {\n\t\t\t\t\tinstance_count_mod = -RS->multimesh_get_instance_count(mm);\n\t\t\t\t}\n\t\t\t\tif (lod == Terrain3DMeshAsset::SHADOW_LOD_ID) {\n\t\t\t\t\t// Reuse impostor LOD MM as shadow impostor\n\t\t\t\t\tmm = shadow_impostor_source_mm;\n\t\t\t\t\tif (!mm.is_valid()) {\n\t\t\t\t\t\tLOG(ERROR, \"Shadow MM is null for cell \", cell, \" lod \", lod, \" xforms: \", xforms.size());\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (mm.is_valid()) {\n\t\t\t\t\t\tRS->free_rid(mm);\n\t\t\t\t\t}\n\t\t\t\t\tmm = _create_multimesh(p_mesh_id, lod, xforms, colors);\n\t\t\t\t}\n\t\t\t\tif (!mm.is_valid()) {\n\t\t\t\t\tLOG(ERROR, \"Null MM for cell \", cell, \" lod \", lod, \" xforms: \", xforms.size());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tRS->instance_set_base(mmi, mm);\n\n\t\t\t\t// Add current instance count for this cell\n\t\t\t\tif (lod == _get_master_lod(ma)) {\n\t\t\t\t\tma->update_instance_count(instance_count_mod + RS->multimesh_get_instance_count(mm));\n\t\t\t\t}\n\n\t\t\t\t// Clear modified only for visible LODs\n\t\t\t\tif (lod != Terrain3DMeshAsset::SHADOW_LOD_ID) {\n\t\t\t\t\ttriple[2] = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Needed to update generated mesh changes\n\t\t\t\tRef<Mesh> mesh = ma->get_mesh(lod);\n\t\t\t\tif (!mesh.is_valid()) {\n\t\t\t\t\tLOG(ERROR, \"Mesh is null for LOD \", lod);\n\t\t\t\t\tRS->free_rid(mmi);\n\t\t\t\t\tRS->free_rid(mm);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tRS->multimesh_set_mesh(mm, mesh->get_rid());\n\t\t\t}\n\t\t\t// Capture source MM from shadow impostor LOD\n\t\t\tif (lod == ma->get_shadow_impostor()) {\n\t\t\t\tshadow_impostor_source_mm = mm;\n\t\t\t}\n\t\t} // End for LOD loop\n\n\t\t// Set all LOD mmi AABB to match LOD0 to ensure no gaps between transitions.\n\t\tAABB mm_custom_aabb;\n\t\tfor (int lod = 0; lod <= ma->get_last_lod(); lod++) {\n\t\t\tVector2i mesh_key(p_mesh_id, lod);\n\t\t\tCellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];\n\t\t\tRID &mmi = cell_mmi_dict[cell].first;\n\t\t\tRID &mm = cell_mmi_dict[cell].second;\n\t\t\tif (mm.is_valid() && mmi.is_valid()) {\n\t\t\t\tif (lod == _get_master_lod(ma)) {\n\t\t\t\t\tmm_custom_aabb = RS->multimesh_get_aabb(mm);\n\t\t\t\t} else {\n\t\t\t\t\tRS->multimesh_set_custom_aabb(mm, mm_custom_aabb);\n\t\t\t\t}\n\t\t\t\tRS->instance_set_custom_aabb(mmi, mm_custom_aabb);\n\t\t\t}\n\t\t}\n\t\tif (ma->get_shadow_impostor() > 0) {\n\t\t\tVector2i mesh_key(p_mesh_id, Terrain3DMeshAsset::SHADOW_LOD_ID);\n\t\t\tCellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];\n\t\t\tRID &mmi = cell_mmi_dict[cell].first;\n\t\t\tif (mmi.is_valid()) {\n\t\t\t\tRS->instance_set_custom_aabb(mmi, mm_custom_aabb);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terrain3DInstancer::_set_mmi_lod_ranges(RID p_mmi, const Ref<Terrain3DMeshAsset> &p_ma, const int p_lod) {\n\tif (!p_mmi || p_ma.is_null()) {\n\t\treturn;\n\t}\n\tint source_lod = p_lod;\n\treal_t lod_begin = p_ma->get_lod_range_begin(source_lod);\n\treal_t lod_end = p_ma->get_lod_range_end(source_lod);\n\tif (source_lod == Terrain3DMeshAsset::SHADOW_LOD_ID) {\n\t\tsource_lod = p_ma->get_shadow_impostor();\n\t\tlod_begin = 0.f;\n\t\tlod_end = p_ma->get_lod_range_begin(source_lod);\n\t}\n\treal_t margin = p_ma->get_fade_margin();\n\tif (margin > 0.f) {\n\t\tlod_begin = MAX(lod_begin < 0.001f ? 0.f : lod_begin - margin, 0.f);\n\t\tlod_end = MAX(lod_end < 0.001f ? 0.f : lod_end + margin, 0.f);\n\t\treal_t begin_margin = lod_begin < 0.001f ? 0.f : margin;\n\t\treal_t end_margin = lod_end < 0.001f ? 0.f : margin;\n\t\tRS->instance_geometry_set_visibility_range(p_mmi, lod_begin, lod_end, begin_margin, end_margin, RenderingServer::VISIBILITY_RANGE_FADE_SELF);\n\t} else {\n\t\tRS->instance_geometry_set_visibility_range(p_mmi, lod_begin, lod_end, 0.f, 0.f, RenderingServer::VISIBILITY_RANGE_FADE_DISABLED);\n\t}\n}\n\nvoid Terrain3DInstancer::_update_vertex_spacing(const real_t p_vertex_spacing) {\n\tIS_DATA_INIT(VOID);\n\tTypedArray<Vector2i> region_locations = _terrain->get_data()->get_region_locations();\n\tfor (int r = 0; r < region_locations.size(); r++) {\n\t\tVector2i region_loc = region_locations[r];\n\t\tTerrain3DRegion *region = _terrain->get_data()->get_region_ptr(region_loc);\n\t\tif (!region) {\n\t\t\tLOG(WARN, \"Errant null region found at: \", region_loc);\n\t\t\tcontinue;\n\t\t}\n\t\treal_t old_spacing = region->get_vertex_spacing();\n\t\tif (old_spacing == p_vertex_spacing) {\n\t\t\tLOG(DEBUG, \"region vertex spacing == vertex spacing, skipping update transform spacing for region at: \", region_loc);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// For all mesh_ids in region\n\t\tDictionary mesh_inst_dict = region->get_instances();\n\t\tLOG(DEBUG, \"Updating MMIs from: \", region_loc);\n\t\tArray mesh_types = mesh_inst_dict.keys();\n\t\tfor (int m = 0; m < mesh_types.size(); m++) {\n\t\t\tint mesh_id = mesh_types[m];\n\t\t\tDictionary cell_inst_dict = mesh_inst_dict[mesh_id];\n\t\t\tArray cell_locations = cell_inst_dict.keys();\n\t\t\tfor (int c = 0; c < cell_locations.size(); c++) {\n\t\t\t\t// Get instances\n\t\t\t\tVector2i cell = cell_locations[c];\n\t\t\t\tArray triple = cell_inst_dict[cell];\n\t\t\t\tTypedArray<Transform3D> xforms = triple[0];\n\t\t\t\t// Descale, then Scale to the new value\n\t\t\t\tfor (int i = 0; i < xforms.size(); i++) {\n\t\t\t\t\tTransform3D t = xforms[i];\n\t\t\t\t\tt.origin.x /= old_spacing;\n\t\t\t\t\tt.origin.x *= p_vertex_spacing;\n\t\t\t\t\tt.origin.z /= old_spacing;\n\t\t\t\t\tt.origin.z *= p_vertex_spacing;\n\t\t\t\t\txforms[i] = t;\n\t\t\t\t}\n\t\t\t\ttriple[0] = xforms;\n\t\t\t\ttriple[2] = true;\n\t\t\t\tcell_inst_dict[cell] = triple;\n\t\t\t}\n\t\t}\n\t\t// After all transforms are updated, set the new region vertex spacing value\n\t\tregion->set_vertex_spacing(p_vertex_spacing);\n\t\tregion->set_modified(true);\n\t}\n\tupdate_mmis(-1, V2I_MAX, true);\n}\n\nvoid Terrain3DInstancer::_destroy_mmi_by_mesh(const int p_mesh_id) {\n\tLOG(DEBUG, \"Deleting all MMIs in all regions with mesh_id: \", p_mesh_id);\n\tTypedArray<Vector2i> region_locations = _terrain->get_data()->get_region_locations();\n\tfor (const Vector2i &region_loc : region_locations) {\n\t\tRef<Terrain3DMeshAsset> ma = _terrain->get_assets()->get_mesh_asset(p_mesh_id);\n\t\tma.is_valid() ? ma->set_instance_count(0) : void(); // Reset count for this mesh\n\t\t_destroy_mmi_by_location(region_loc, p_mesh_id);\n\t}\n}\n\nvoid Terrain3DInstancer::_destroy_mmi_by_location(const Vector2i &p_region_loc, const int p_mesh_id) {\n\tLOG(DEBUG, \"Deleting all MMIs in region: \", p_region_loc, \" for mesh_id: \", p_mesh_id);\n\t// Identify cells with matching mesh_id\n\tstd::unordered_set<Vector2i, Vector2iHash> cells;\n\tif (_mmi_rids.count(p_region_loc) > 0) {\n\t\tMeshMMIDict &mesh_mmi_dict = _mmi_rids[p_region_loc];\n\t\tfor (const auto &mesh_entry : mesh_mmi_dict) {\n\t\t\tconst Vector2i &mesh_key = mesh_entry.first;\n\t\t\tif (mesh_key.x != p_mesh_id) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst CellMMIDict &cell_mmi_dict = mesh_entry.second;\n\t\t\tfor (const auto &cell_entry : cell_mmi_dict) {\n\t\t\t\tcells.insert(cell_entry.first);\n\t\t\t}\n\t\t}\n\t}\n\t// Iterate over unique matching cells; each _destroy_mmi_by_cell will handle all LODs\n\tfor (const Vector2i &cell : cells) {\n\t\t_destroy_mmi_by_cell(p_region_loc, p_mesh_id, cell);\n\t}\n\t// After all cells are destroyed, if the region is now empty, erase it\n\tif (_mmi_rids.count(p_region_loc) > 0 && _mmi_rids[p_region_loc].empty()) {\n\t\t_mmi_rids.erase(p_region_loc);\n\t}\n}\n\nvoid Terrain3DInstancer::_destroy_mmi_by_cell(const Vector2i &p_region_loc, const int p_mesh_id, const Vector2i p_cell, const int p_lod) {\n\tif (_mmi_rids.count(p_region_loc) == 0) {\n\t\treturn;\n\t}\n\tMeshMMIDict &mesh_mmi_dict = _mmi_rids[p_region_loc];\n\tRef<Terrain3DMeshAsset> ma = _terrain->get_assets()->get_mesh_asset(p_mesh_id);\n\n\tfor (int lod = Terrain3DMeshAsset::SHADOW_LOD_ID; lod < Terrain3DMeshAsset::MAX_LOD_COUNT; lod++) {\n\t\t// Skip if not all lods, or not matching lod\n\t\tif (p_lod != INT32_MAX && lod != p_lod) {\n\t\t\tcontinue;\n\t\t}\n\t\tVector2i mesh_key(p_mesh_id, lod);\n\t\tif (mesh_mmi_dict.count(mesh_key) == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tCellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];\n\t\tif (cell_mmi_dict.count(p_cell) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tRID &mmi = cell_mmi_dict[p_cell].first;\n\t\tRID &mm = cell_mmi_dict[p_cell].second;\n\t\tif (ma.is_valid() && mm.is_valid()) {\n\t\t\tif (lod == _get_master_lod(ma)) {\n\t\t\t\tma->update_instance_count(-RS->multimesh_get_instance_count(mm));\n\t\t\t}\n\t\t}\n\t\tLOG(EXTREME, \"Freeing mmi:\", mmi, \", mm:\", mm, \" and erasing mmi cell \", p_cell);\n\t\tif (mmi.is_valid()) {\n\t\t\tRS->free_rid(mmi);\n\t\t}\n\t\t// Unlike the Shadow MMI, the Shadow MM is a copy of another lod, not a unique RID to be freed\n\t\tif (lod != Terrain3DMeshAsset::SHADOW_LOD_ID) {\n\t\t\tif (mm.is_valid()) {\n\t\t\t\tRS->free_rid(mm);\n\t\t\t}\n\t\t}\n\t\tcell_mmi_dict.erase(p_cell);\n\n\t\t// If the cell is empty of all MMIs, remove it\n\t\tif (cell_mmi_dict.empty()) {\n\t\t\tLOG(EXTREME, \"Removing mesh \", mesh_key, \" from cell MMI dictionary\");\n\t\t\tmesh_mmi_dict.erase(mesh_key); // invalidates cell_mmi_dict\n\t\t}\n\t}\n\n\t// Clean up region if we've removed the last MMI and cell\n\tif (mesh_mmi_dict.empty()) {\n\t\tLOG(EXTREME, \"Removing region \", p_region_loc, \" from mesh MMI dictionary\");\n\t\t// This invalidates mesh_mmi_dict here and for calling functions\n\t\t_mmi_rids.erase(p_region_loc);\n\t}\n}\n\nvoid Terrain3DInstancer::_backup_region(const Ref<Terrain3DRegion> &p_region) {\n\tif (p_region.is_null()) {\n\t\treturn;\n\t}\n\tif (_terrain && _terrain->get_editor() && _terrain->get_editor()->is_operating()) {\n\t\t_terrain->get_editor()->backup_region(p_region);\n\t} else {\n\t\tp_region->set_modified(true);\n\t}\n}\n\nRID Terrain3DInstancer::_create_multimesh(const int p_mesh_id, const int p_lod, const TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors) const {\n\tRID mm;\n\tIS_INIT(mm);\n\tif (p_xforms.size() == 0) {\n\t\treturn mm;\n\t}\n\tRef<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(p_mesh_id);\n\tif (mesh_asset.is_null()) {\n\t\tLOG(ERROR, \"No mesh id \", p_mesh_id, \" found\");\n\t\treturn mm;\n\t}\n\tRef<Mesh> mesh = mesh_asset->get_mesh(p_lod);\n\tif (mesh.is_null()) {\n\t\tLOG(ERROR, \"No LOD \", p_lod, \" for mesh id \", p_mesh_id, \" found. Max: \", mesh_asset->get_lod_count());\n\t\treturn mm;\n\t}\n\tmm = RS->multimesh_create();\n\tRS->multimesh_allocate_data(mm, p_xforms.size(), RenderingServer::MULTIMESH_TRANSFORM_3D, true, false, false);\n\tRS->multimesh_set_mesh(mm, mesh->get_rid());\n\tfor (int i = 0; i < p_xforms.size(); i++) {\n\t\tRS->multimesh_instance_set_transform(mm, i, p_xforms[i]);\n\t\tif (i < p_colors.size()) {\n\t\t\tRS->multimesh_instance_set_color(mm, i, p_colors[i]);\n\t\t}\n\t}\n\treturn mm;\n}\n\nVector2i Terrain3DInstancer::_get_cell(const Vector3 &p_global_position, const int p_region_size) const {\n\tIS_INIT(V2I_ZERO);\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\tVector2i cell;\n\tcell.x = UtilityFunctions::posmod(UtilityFunctions::floori(p_global_position.x / vertex_spacing), p_region_size) / CELL_SIZE;\n\tcell.y = UtilityFunctions::posmod(UtilityFunctions::floori(p_global_position.z / vertex_spacing), p_region_size) / CELL_SIZE;\n\treturn cell;\n}\n\n// Get appropriate terrain height. Could find terrain (excluding slope or holes) or optional collision\nArray Terrain3DInstancer::_get_usable_height(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_on_collision, const real_t p_raycast_start) const {\n\tIS_DATA_INIT(Array());\n\tconst Terrain3DData *data = _terrain->get_data();\n\treal_t height = data->get_height(p_global_position);\n\tDictionary raycast_result;\n\tbool raycast_hit = false;\n\treal_t raycast_height = -FLT_MAX;\n\tVector3 raycast_normal = V3_UP;\n\t// Raycast physics if using on_collision\n\tif (p_on_collision) {\n\t\tVector3 start_pos = Vector3(p_global_position.x, height + p_raycast_start, p_global_position.z);\n\t\traycast_result = _terrain->get_raycast_result(start_pos, Vector3(0.0f, -p_raycast_start - 1.0f, 0.0f), 0xFFFFFFFF, true);\n\t\tif (raycast_result.has(\"position\")) {\n\t\t\traycast_hit = true;\n\t\t\traycast_height = ((Vector3)raycast_result[\"position\"]).y;\n\t\t\traycast_normal = raycast_result[\"normal\"];\n\t\t}\n\t}\n\t// Hole, use collision if can or quit\n\tif (std::isnan(height)) {\n\t\tif (!raycast_hit) {\n\t\t\treturn Array();\n\t\t}\n\t\theight = raycast_height;\n\t}\n\t// No hole, use collision if higher\n\telse if (raycast_hit && raycast_height > height) {\n\t\theight = raycast_height;\n\t}\n\t// No hole or collision, use height if in slope or quit\n\tif (!data->is_in_slope(p_global_position, p_slope_range, raycast_hit ? raycast_normal : V3_ZERO)) {\n\t\treturn Array();\n\t}\n\tArray triple;\n\ttriple.resize(3);\n\ttriple[0] = height;\n\ttriple[1] = raycast_hit;\n\ttriple[2] = raycast_normal;\n\treturn triple;\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DInstancer::initialize(Terrain3D *p_terrain) {\n\tif (p_terrain) {\n\t\t_terrain = p_terrain;\n\t}\n\tIS_DATA_INIT_MESG(\"Terrain3D not initialized yet\", VOID);\n\tLOG(INFO, \"Initializing Instancer\");\n\tupdate_mmis();\n}\n\nvoid Terrain3DInstancer::destroy() {\n\tIS_DATA_INIT(VOID);\n\t_queued_updates.clear();\n\tLOG(INFO, \"Destroying all MMIs\");\n\tint mesh_count = _terrain->get_assets()->get_mesh_count();\n\tfor (int m = 0; m < mesh_count; m++) {\n\t\t_destroy_mmi_by_mesh(m);\n\t}\n}\n\nvoid Terrain3DInstancer::clear_by_mesh(const int p_mesh_id) {\n\tLOG(INFO, \"Deleting Multimeshes in all regions with mesh_id: \", p_mesh_id);\n\tTypedArray<Vector2i> region_locations = _terrain->get_data()->get_region_locations();\n\tfor (const Vector2i &region_loc : region_locations) {\n\t\tclear_by_location(region_loc, p_mesh_id);\n\t}\n\tRef<Terrain3DMeshAsset> ma = _terrain->get_assets()->get_mesh_asset(p_mesh_id);\n\tma.is_valid() ? ma->set_instance_count(0) : void(); // Reset count for this mesh\n}\n\nvoid Terrain3DInstancer::clear_by_location(const Vector2i &p_region_loc, const int p_mesh_id) {\n\tLOG(INFO, \"Deleting Multimeshes w/ mesh_id: \", p_mesh_id, \" in region: \", p_region_loc);\n\tRef<Terrain3DRegion> region = _terrain->get_data()->get_region(p_region_loc);\n\tclear_by_region(region, p_mesh_id);\n}\n\nvoid Terrain3DInstancer::clear_by_region(const Ref<Terrain3DRegion> &p_region, const int p_mesh_id) {\n\tif (p_region.is_null()) {\n\t\tLOG(ERROR, \"Region is null\");\n\t\treturn;\n\t}\n\tVector2i region_loc = p_region->get_location();\n\tLOG(INFO, \"Deleting Multimeshes w/ mesh_id: \", p_mesh_id, \" in region: \", region_loc);\n\tDictionary mesh_inst_dict = p_region->get_instances();\n\tif (mesh_inst_dict.has(p_mesh_id)) {\n\t\t_backup_region(p_region);\n\t\tmesh_inst_dict.erase(p_mesh_id);\n\t}\n\t_destroy_mmi_by_location(region_loc, p_mesh_id);\n}\n\nvoid Terrain3DInstancer::set_mode(const InstancerMode p_mode) {\n\tLOG(INFO, \"Setting instancer mode: \", p_mode);\n\tif (p_mode != _mode) {\n\t\t_mode = p_mode;\n\t\tswitch (_mode) {\n\t\t\tcase NORMAL:\n\t\t\t\tupdate_mmis(-1, V2I_MAX, true);\n\t\t\t\tbreak;\n\t\t\t//case PLACEHOLDER:\n\t\t\t//\tbreak;\n\t\t\tdefault:\n\t\t\t\tdestroy();\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid Terrain3DInstancer::add_instances(const Vector3 &p_global_position, const Dictionary &p_params) {\n\tIS_DATA_INIT_MESG(\"Instancer isn't initialized.\", VOID);\n\tint mesh_id = p_params.get(\"asset_id\", 0);\n\tif (mesh_id < 0 || mesh_id >= _terrain->get_assets()->get_mesh_count()) {\n\t\tLOG(ERROR, \"Mesh ID out of range: \", mesh_id, \", valid: 0 to \", _terrain->get_assets()->get_mesh_count() - 1);\n\t\treturn;\n\t}\n\n\treal_t brush_size = CLAMP(real_t(p_params.get(\"size\", 10.f)), 0.1f, 4096.f); // Meters\n\treal_t radius = brush_size * .5f;\n\treal_t strength = CLAMP(real_t(p_params.get(\"strength\", .1f)), .01f, 100.f); // (premul) 1-10k%\n\treal_t fixed_scale = CLAMP(real_t(p_params.get(\"fixed_scale\", 100.f)) * .01f, .01f, 100.f); // 1-10k%\n\treal_t random_scale = CLAMP(real_t(p_params.get(\"random_scale\", 0.f)) * .01f, 0.f, 10.f); // +/- 1000%\n\tRef<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(mesh_id);\n\treal_t density = CLAMP(.1f * brush_size * strength * mesh_asset->get_density() /\n\t\t\t\t\tMAX(0.01f, fixed_scale + .5f * random_scale),\n\t\t\t.001f, 1000.f);\n\t// Density based on strength, mesh AABB and input scale determines how many to place, even fractional\n\tuint32_t count = _get_density_count(density);\n\tif (count <= 0) {\n\t\treturn;\n\t}\n\tLOG(EXTREME, \"Adding \", count, \" instances at \", p_global_position);\n\n\treal_t fixed_spin = CLAMP(real_t(p_params.get(\"fixed_spin\", 0.f)), .0f, 360.f); // degrees\n\treal_t random_spin = CLAMP(real_t(p_params.get(\"random_spin\", 360.f)), 0.f, 360.f); // degrees\n\treal_t fixed_tilt = CLAMP(real_t(p_params.get(\"fixed_tilt\", 0.f)), -180.f, 180.f); // degrees\n\treal_t random_tilt = CLAMP(real_t(p_params.get(\"random_tilt\", 10.f)), 0.f, 180.f); // degrees\n\tbool align_to_normal = bool(p_params.get(\"align_to_normal\", false));\n\n\treal_t height_offset = CLAMP(real_t(p_params.get(\"height_offset\", 0.f)), -100.0f, 100.f); // meters\n\treal_t random_height = CLAMP(real_t(p_params.get(\"random_height\", 0.f)), 0.f, 100.f); // meters\n\n\tColor vertex_color = Color(p_params.get(\"vertex_color\", COLOR_WHITE));\n\treal_t random_hue = CLAMP(real_t(p_params.get(\"random_hue\", 0.f)) / 360.f, 0.f, 1.f); // degrees -> 0-1\n\treal_t random_darken = CLAMP(real_t(p_params.get(\"random_darken\", 0.f)) * .01f, 0.f, 1.f); // 0-100%\n\n\tVector2 slope_range = p_params.get(\"slope\", Vector2(0.f, 90.f)); // 0-90 degrees\n\tslope_range.x = CLAMP(slope_range.x, 0.f, 90.f);\n\tslope_range.y = CLAMP(slope_range.y, 0.f, 90.f);\n\tbool on_collision = bool(p_params.get(\"on_collision\", false));\n\treal_t raycast_height = p_params.get(\"raycast_height\", 10.f);\n\tTerrain3DData *data = _terrain->get_data();\n\n\tTypedArray<Transform3D> xforms;\n\tPackedColorArray colors;\n\tfor (int i = 0; i < count; i++) {\n\t\tTransform3D t;\n\n\t\t// Get random XZ position and height in a circle\n\t\treal_t r_radius = radius * sqrt(UtilityFunctions::randf());\n\t\treal_t r_theta = UtilityFunctions::randf() * Math_TAU;\n\t\tVector3 rand_vec = Vector3(r_radius * cos(r_theta), 0.f, r_radius * sin(r_theta));\n\t\tVector3 position = p_global_position + rand_vec;\n\n\t\t// Get height\n\t\tArray height_data = _get_usable_height(position, slope_range, on_collision, raycast_height);\n\t\tif (height_data.size() != 3) {\n\t\t\tcontinue;\n\t\t}\n\t\tposition.y = height_data[0];\n\t\tbool raycast_hit = height_data[1];\n\n\t\t// Orientation\n\t\tVector3 normal = V3_UP;\n\t\tif (align_to_normal) {\n\t\t\t// Use either collision normal or terrain normal\n\t\t\tnormal = raycast_hit ? (Vector3)height_data[2] : data->get_normal(position);\n\t\t\tif (!normal.is_finite()) {\n\t\t\t\tnormal = V3_UP;\n\t\t\t} else {\n\t\t\t\tnormal = normal.normalized();\n\t\t\t\tVector3 z_axis = Vector3(0.f, 0.f, 1.f);\n\t\t\t\tVector3 x_axis = -z_axis.cross(normal);\n\t\t\t\tif (x_axis.length_squared() > 0.001f) {\n\t\t\t\t\tt.basis = Basis(x_axis, normal, z_axis).orthonormalized();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treal_t spin = (fixed_spin + random_spin * UtilityFunctions::randf()) * Math_PI / 180.f;\n\t\tif (std::abs(spin) > 0.001f) {\n\t\t\tt.basis = t.basis.rotated(normal, spin);\n\t\t}\n\t\treal_t tilt = (fixed_tilt + random_tilt * (2.f * UtilityFunctions::randf() - 1.f)) * Math_PI / 180.f;\n\t\tif (std::abs(tilt) > 0.001f) {\n\t\t\tt.basis = t.basis.rotated(t.basis.get_column(0), tilt); // Rotate pitch, X-axis\n\t\t}\n\n\t\t// Scale\n\t\treal_t t_scale = CLAMP(fixed_scale + random_scale * (2.f * UtilityFunctions::randf() - 1.f), 0.01f, 10.f);\n\t\tt = t.scaled(Vector3(t_scale, t_scale, t_scale));\n\n\t\t// Position. mesh_asset height offset added in add_transforms\n\t\treal_t offset = height_offset + random_height * (2.f * UtilityFunctions::randf() - 1.f);\n\t\tposition += t.basis.get_column(1) * offset; // Offset along UP axis\n\t\tt = t.translated(position);\n\n\t\t// Color\n\t\tColor col = vertex_color;\n\t\tcol.set_v(CLAMP(col.get_v() - random_darken * UtilityFunctions::randf(), 0.f, 1.f));\n\t\tcol.set_h(fmod(col.get_h() + random_hue * (2.f * UtilityFunctions::randf() - 1.f), 1.f));\n\n\t\txforms.push_back(t);\n\t\tcolors.push_back(col);\n\t}\n\n\t// Append multimesh\n\tif (xforms.size() > 0) {\n\t\tadd_transforms(mesh_id, xforms, colors);\n\t}\n}\n\nvoid Terrain3DInstancer::remove_instances(const Vector3 &p_global_position, const Dictionary &p_params) {\n\tIS_DATA_INIT_MESG(\"Instancer isn't initialized.\", VOID);\n\n\tint mesh_id = p_params.get(\"asset_id\", 0);\n\tint mesh_count = _terrain->get_assets()->get_mesh_count();\n\tif (mesh_id < 0 || mesh_id >= mesh_count) {\n\t\tLOG(ERROR, \"Mesh ID out of range: \", mesh_id, \", valid: 0 to \", _terrain->get_assets()->get_mesh_count() - 1);\n\t\treturn;\n\t}\n\n\tTerrain3DData *data = _terrain->get_data();\n\tint region_size = _terrain->get_region_size();\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\tbool modifier_shift = p_params.get(\"modifier_shift\", false);\n\treal_t brush_size = CLAMP(real_t(p_params.get(\"size\", 10.f)), .5f, 4096.f); // Meters\n\treal_t half_brush_size = brush_size * 0.5f + 1.f; // 1m margin\n\treal_t radius = brush_size * .5f;\n\treal_t strength = CLAMP(real_t(p_params.get(\"strength\", .1f)), .01f, 100.f); // (premul) 1-10k%\n\tVector2 slope_range = p_params.get(\"slope\", Vector2(0.f, 90.f)); // 0-90 degrees\n\tslope_range.x = CLAMP(slope_range.x, 0.f, 90.f);\n\tslope_range.y = CLAMP(slope_range.y, 0.f, 90.f);\n\tbool on_collision = bool(p_params.get(\"on_collision\", false));\n\treal_t raycast_height = p_params.get(\"raycast_height\", 10.f);\n\n\t// Build list of potential regions to search, rather than searching the entire terrain, calculate possible regions covered\n\t// and check if they are valid; if so add that location to the dictionary keys.\n\tDictionary r_locs;\n\t// Calculate step distance to ensure every region is checked inside the bounds of brush size.\n\treal_t step = brush_size / ceil(brush_size / real_t(region_size) / vertex_spacing);\n\tfor (real_t x = p_global_position.x - half_brush_size; x <= p_global_position.x + half_brush_size; x += step) {\n\t\tfor (real_t z = p_global_position.z - half_brush_size; z <= p_global_position.z + half_brush_size; z += step) {\n\t\t\tVector2i region_loc = data->get_region_location(Vector3(x, 0.f, z));\n\t\t\tif (data->has_region(region_loc)) {\n\t\t\t\tr_locs[region_loc] = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tArray region_queue = r_locs.keys();\n\tif (region_queue.size() == 0) {\n\t\treturn;\n\t}\n\n\tfor (const Vector2i &region_loc : region_queue) {\n\t\tRef<Terrain3DRegion> region = data->get_region(region_loc);\n\t\tif (region.is_null()) {\n\t\t\tLOG(WARN, \"Errant null region found at: \", region_loc);\n\t\t\tcontinue;\n\t\t}\n\n\t\tDictionary mesh_inst_dict = region->get_instances();\n\t\tArray mesh_types = mesh_inst_dict.keys();\n\t\tif (mesh_types.size() == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tVector3 global_local_offset = Vector3(region_loc.x * region_size * vertex_spacing, 0.f, region_loc.y * region_size * vertex_spacing);\n\t\tVector2 localised_ring_center = Vector2(p_global_position.x - global_local_offset.x, p_global_position.z - global_local_offset.z);\n\t\t// For this mesh id, or all mesh ids\n\t\tfor (int m = (modifier_shift ? 0 : mesh_id); m <= (modifier_shift ? mesh_count - 1 : mesh_id); m++) {\n\t\t\t// Ensure this region has this mesh\n\t\t\tif (!mesh_inst_dict.has(m)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tDictionary cell_inst_dict = mesh_inst_dict[m];\n\t\t\tArray cell_locations = cell_inst_dict.keys();\n\t\t\t// This shouldnt be empty\n\t\t\tif (cell_locations.size() == 0) {\n\t\t\t\tLOG(WARN, \"Region at: \", region_loc, \" has instance dictionary for mesh id: \", m, \" but has no cells.\")\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Check potential cells rather than searching the entire region, whilst marginally\n\t\t\t// slower if there are very few cells for the given mesh present. It is significantly\n\t\t\t// faster when a large number of cells are present.\n\t\t\tDictionary c_locs;\n\t\t\t// Calculate step distance to ensure every cell is checked inside the bounds of brush size.\n\t\t\treal_t cell_step = brush_size / ceil(brush_size / real_t(CELL_SIZE) / vertex_spacing);\n\t\t\tfor (real_t x = p_global_position.x - half_brush_size; x <= p_global_position.x + half_brush_size; x += cell_step) {\n\t\t\t\tfor (real_t z = p_global_position.z - half_brush_size; z <= p_global_position.z + half_brush_size; z += cell_step) {\n\t\t\t\t\tVector3 cell_pos = Vector3(x, 0.f, z) - global_local_offset;\n\t\t\t\t\t// Manually calculate cell pos without modulus, locations not in the current region will not be found.\n\t\t\t\t\tVector2i cell_loc;\n\t\t\t\t\tcell_loc.x = UtilityFunctions::floori(cell_pos.x / vertex_spacing) / CELL_SIZE;\n\t\t\t\t\tcell_loc.y = UtilityFunctions::floori(cell_pos.z / vertex_spacing) / CELL_SIZE;\n\t\t\t\t\tif (cell_locations.has(cell_loc)) {\n\t\t\t\t\t\tc_locs[cell_loc] = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tArray cell_queue = c_locs.keys();\n\t\t\tif (cell_queue.size() == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRef<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(m);\n\t\t\treal_t mesh_height_offset = mesh_asset->get_height_offset();\n\t\t\tfor (int c = 0; c < cell_queue.size(); c++) {\n\t\t\t\tVector2i cell = cell_queue[c];\n\t\t\t\tArray triple = cell_inst_dict[cell];\n\t\t\t\tTypedArray<Transform3D> xforms = triple[0];\n\t\t\t\tPackedColorArray colors = triple[1];\n\t\t\t\tTypedArray<Transform3D> updated_xforms;\n\t\t\t\tPackedColorArray updated_colors;\n\t\t\t\t// Remove transforms if inside ring radius\n\t\t\t\tfor (int i = 0; i < xforms.size(); i++) {\n\t\t\t\t\tTransform3D t = xforms[i];\n\t\t\t\t\t// Use localised ring center\n\t\t\t\t\treal_t radial_distance = localised_ring_center.distance_to(Vector2(t.origin.x, t.origin.z));\n\t\t\t\t\tVector3 height_offset = t.basis.get_column(1) * mesh_height_offset;\n\t\t\t\t\tif (radial_distance >= radius || UtilityFunctions::randf() >= CLAMP(0.175f * strength, 0.005f, 10.f)) {\n\t\t\t\t\t\tupdated_xforms.push_back(t);\n\t\t\t\t\t\tupdated_colors.push_back(colors[i]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tVector3 global_pos = t.origin + global_local_offset - t.basis.get_column(1) * mesh_height_offset;\n\t\t\t\t\tArray height_data = _get_usable_height(global_pos, slope_range, on_collision, raycast_height);\n\t\t\t\t\tif (height_data.size() != 3) {\n\t\t\t\t\t\tupdated_xforms.push_back(t);\n\t\t\t\t\t\tupdated_colors.push_back(colors[i]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t_backup_region(region);\n\t\t\t\t}\n\t\t\t\tif (updated_xforms.size() > 0) {\n\t\t\t\t\ttriple[0] = updated_xforms;\n\t\t\t\t\ttriple[1] = updated_colors;\n\t\t\t\t\ttriple[2] = true;\n\t\t\t\t\tcell_inst_dict[cell] = triple;\n\t\t\t\t} else {\n\t\t\t\t\tcell_inst_dict.erase(cell);\n\t\t\t\t\t_destroy_mmi_by_cell(region_loc, m, cell);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (cell_inst_dict.is_empty()) {\n\t\t\t\tmesh_inst_dict.erase(m);\n\t\t\t}\n\t\t\tupdate_mmis(m, region_loc);\n\t\t}\n\t}\n}\n\nvoid Terrain3DInstancer::add_multimesh(const int p_mesh_id, const Ref<MultiMesh> &p_multimesh, const Transform3D &p_xform, const bool p_update) {\n\tLOG(INFO, \"Extracting \", p_multimesh->get_instance_count(), \" transforms from multimesh\");\n\tTypedArray<Transform3D> xforms;\n\tPackedColorArray colors;\n\tfor (int i = 0; i < p_multimesh->get_instance_count(); i++) {\n\t\txforms.push_back(p_xform * p_multimesh->get_instance_transform(i));\n\t\tColor c = COLOR_WHITE;\n\t\tif (p_multimesh->is_using_colors()) {\n\t\t\tc = p_multimesh->get_instance_color(i);\n\t\t}\n\t\tcolors.push_back(c);\n\t}\n\tadd_transforms(p_mesh_id, xforms, colors, p_update);\n}\n\n// Expects transforms in global space\nvoid Terrain3DInstancer::add_transforms(const int p_mesh_id, const TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors, const bool p_update) {\n\tIS_DATA_INIT_MESG(\"Instancer isn't initialized.\", VOID);\n\tif (p_xforms.size() == 0) {\n\t\treturn;\n\t}\n\tif (p_mesh_id < 0 || p_mesh_id >= _terrain->get_assets()->get_mesh_count()) {\n\t\tLOG(ERROR, \"Mesh ID out of range: \", p_mesh_id, \", valid: 0 to \", _terrain->get_assets()->get_mesh_count() - 1);\n\t\treturn;\n\t}\n\n\tDictionary xforms_dict;\n\tDictionary colors_dict;\n\tRef<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(p_mesh_id);\n\treal_t height_offset = mesh_asset->get_height_offset();\n\n\t// Separate incoming transforms/colors by region Dict{ region_loc => Array() }\n\tLOG(INFO, \"Separating \", p_xforms.size(), \" transforms and \", p_colors.size(), \" colors into regions\");\n\tfor (int i = 0; i < p_xforms.size(); i++) {\n\t\t// Get adjusted xform/color\n\t\tTransform3D trns = p_xforms[i];\n\t\ttrns.origin += trns.basis.get_column(1) * height_offset; // Offset along UP axis\n\t\tColor col = COLOR_WHITE;\n\t\tif (p_colors.size() > i) {\n\t\t\tcol = p_colors[i];\n\t\t}\n\n\t\t// Store by region offset\n\t\tVector2i region_loc = _terrain->get_data()->get_region_location(trns.origin);\n\t\tif (!xforms_dict.has(region_loc)) {\n\t\t\txforms_dict[region_loc] = TypedArray<Transform3D>();\n\t\t\tcolors_dict[region_loc] = PackedColorArray();\n\t\t}\n\t\tTypedArray<Transform3D> xforms = xforms_dict[region_loc];\n\t\tPackedColorArray colors = colors_dict[region_loc];\n\t\txforms.push_back(trns);\n\t\tcolors.push_back(col);\n\t\tcolors_dict[region_loc] = colors; // Note similar bug as godot-cpp#1149 needs this for PCA\n\t}\n\n\t// Merge incoming transforms with existing transforms\n\tArray region_locations = xforms_dict.keys();\n\tfor (const Vector2i &region_loc : region_locations) {\n\t\tTypedArray<Transform3D> xforms = xforms_dict[region_loc];\n\t\tPackedColorArray colors = colors_dict[region_loc];\n\t\t//LOG(MESG, \"Appending \", xforms.size(), \" xforms, \", colors, \" colors to region location: \", region_loc);\n\t\tappend_location(region_loc, p_mesh_id, xforms, colors, p_update);\n\t}\n}\n\n// Appends new global transforms to existing cells, offsetting transforms to region space, scaled by vertex spacing\nvoid Terrain3DInstancer::append_location(const Vector2i &p_region_loc, const int p_mesh_id,\n\t\tconst TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors, const bool p_update) {\n\tIS_DATA_INIT(VOID);\n\tRef<Terrain3DRegion> region = _terrain->get_data()->get_region(p_region_loc);\n\tif (region.is_null()) {\n\t\treturn;\n\t}\n\tint region_size = region->get_region_size();\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\tVector2 global_local_offset = Vector2(p_region_loc.x * region_size * vertex_spacing, p_region_loc.y * region_size * vertex_spacing);\n\tTypedArray<Transform3D> localised_xforms;\n\tfor (Transform3D t : p_xforms) {\n\t\t// Localise the transform to \"region space\"\n\t\tt.origin.x -= global_local_offset.x;\n\t\tt.origin.z -= global_local_offset.y;\n\t\tlocalised_xforms.push_back(t);\n\t}\n\tappend_region(region, p_mesh_id, localised_xforms, p_colors, p_update);\n}\n\n// append_region requires all transforms are in region space, 0 - region_size * vertex_spacing\nvoid Terrain3DInstancer::append_region(const Ref<Terrain3DRegion> &p_region, const int p_mesh_id,\n\t\tconst TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors, const bool p_update) {\n\tif (p_region.is_null()) {\n\t\tLOG(ERROR, \"Null region provided. Doing nothing.\");\n\t\treturn;\n\t}\n\tif (p_xforms.size() == 0) {\n\t\tLOG(ERROR, \"No transforms to add. Doing nothing.\");\n\t\treturn;\n\t}\n\n\t_backup_region(p_region);\n\n\tDictionary cell_locations = p_region->get_instances()[p_mesh_id];\n\tint region_size = p_region->get_region_size();\n\n\tfor (int i = 0; i < p_xforms.size(); i++) {\n\t\tTransform3D xform = p_xforms[i];\n\t\tColor col = p_colors[i];\n\t\tVector2i cell = _get_cell(xform.origin, region_size);\n\n\t\t// Get current instance arrays or create if none\n\t\tArray triple = cell_locations[cell];\n\t\tbool modified = true;\n\t\tif (triple.size() != 3) {\n\t\t\tLOG(DEBUG, \"No data at \", p_region->get_location(), \":\", cell, \". Creating triple\");\n\t\t\ttriple.resize(3);\n\t\t\ttriple[0] = TypedArray<Transform3D>();\n\t\t\ttriple[1] = PackedColorArray();\n\t\t\ttriple[2] = modified;\n\t\t}\n\t\tTypedArray<Transform3D> xforms = triple[0];\n\t\tPackedColorArray colors = triple[1];\n\t\txforms.push_back(xform);\n\t\tcolors.push_back(col);\n\n\t\t// Must write back since there are copy constructors somewhere\n\t\t// see godot-cpp#1149\n\t\ttriple[0] = xforms;\n\t\ttriple[1] = colors;\n\t\ttriple[2] = modified;\n\t\tcell_locations[cell] = triple;\n\t}\n\n\t// Write back dictionary. See above comments\n\tp_region->get_instances()[p_mesh_id] = cell_locations;\n\tif (p_update) {\n\t\tupdate_mmis(p_mesh_id, p_region->get_location());\n\t}\n}\n\n// Review all transforms in one area and adjust their transforms w/ the current height\nvoid Terrain3DInstancer::update_transforms(const AABB &p_aabb) {\n\tIS_DATA_INIT_MESG(\"Instancer isn't initialized.\", VOID);\n\tRect2 rect = aabb2rect(p_aabb);\n\tLOG(EXTREME, \"Updating transforms within \", rect);\n\tVector2 global_position = rect.get_center();\n\tVector2 size = rect.get_size();\n\tVector2 half_size = size * 0.5f + V2(1.f); // 1m margin\n\tif (size.is_zero_approx()) {\n\t\treturn;\n\t}\n\n\tTerrain3DData *data = _terrain->get_data();\n\tDictionary params = _terrain->get_editor() ? _terrain->get_editor()->get_brush_data() : Dictionary();\n\tbool on_collision = params.get(\"on_collision\", false);\n\treal_t raycast_height = params.get(\"raycast_height\", 10.f);\n\tint region_size = _terrain->get_region_size();\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\n\t// Build list of valid regions within AABB; add the locations as dictionary keys.\n\tDictionary r_locs;\n\t// Calculate step distance to ensure every region is checked inside the bounds of AABB size.\n\tVector2 step = Vector2(size.x / ceil(size.x / real_t(region_size) / vertex_spacing), size.y / ceil(size.y / real_t(region_size) / vertex_spacing));\n\tfor (real_t x = global_position.x - half_size.x; x <= global_position.x + half_size.x; x += step.x) {\n\t\tfor (real_t z = global_position.y - half_size.y; z <= global_position.y + half_size.y; z += step.y) {\n\t\t\tVector2i region_loc = data->get_region_location(Vector3(x, 0.f, z));\n\t\t\tif (data->has_region(region_loc)) {\n\t\t\t\tr_locs[region_loc] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tArray region_queue = r_locs.keys();\n\tif (region_queue.size() == 0) {\n\t\treturn;\n\t}\n\n\tfor (const Vector2i &region_loc : region_queue) {\n\t\tRef<Terrain3DRegion> region = data->get_region(region_loc);\n\t\t_backup_region(region);\n\n\t\tDictionary mesh_inst_dict = region->get_instances();\n\t\tArray mesh_types = mesh_inst_dict.keys();\n\t\tif (mesh_types.size() == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tVector3 global_local_offset = Vector3(region_loc.x * region_size * vertex_spacing, 0.f, region_loc.y * region_size * vertex_spacing);\n\n\t\t// For each mesh type in this region\n\t\tfor (const int &region_mesh_id : mesh_types) {\n\t\t\t// Check potential cells rather than searching the entire region, whilst marginally\n\t\t\t// slower if there are very few cells for the given mesh present it is significantly\n\t\t\t// faster when a very large number of cells are present.\n\t\t\tDictionary cell_inst_dict = mesh_inst_dict[region_mesh_id];\n\t\t\tArray cell_locations = cell_inst_dict.keys();\n\t\t\tif (cell_locations.size() == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tDictionary c_locs;\n\t\t\t// Calculate step distance to ensure every cell is checked inside the bounds of brush size.\n\t\t\tVector2 cell_step = Vector2(size.x / ceil(size.x / real_t(CELL_SIZE) / vertex_spacing), size.y / ceil(size.y / real_t(CELL_SIZE) / vertex_spacing));\n\t\t\tfor (real_t x = global_position.x - half_size.x; x <= global_position.x + half_size.x; x += cell_step.x) {\n\t\t\t\tfor (real_t z = global_position.y - half_size.y; z <= global_position.y + half_size.y; z += cell_step.y) {\n\t\t\t\t\tVector3 cell_pos = Vector3(x, 0.f, z) - global_local_offset;\n\t\t\t\t\t// Manually calculate cell pos without modulus, locations not in the current region will not be found.\n\t\t\t\t\tVector2i cell_loc;\n\t\t\t\t\tcell_loc.x = UtilityFunctions::floori(cell_pos.x / vertex_spacing) / CELL_SIZE;\n\t\t\t\t\tcell_loc.y = UtilityFunctions::floori(cell_pos.z / vertex_spacing) / CELL_SIZE;\n\t\t\t\t\tif (cell_locations.has(cell_loc)) {\n\t\t\t\t\t\tc_locs[cell_loc] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tArray cell_queue = c_locs.keys();\n\t\t\tif (cell_queue.size() == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRef<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(region_mesh_id);\n\t\t\treal_t mesh_height_offset = mesh_asset->get_height_offset();\n\t\t\tfor (const Vector2i &cell : cell_queue) {\n\t\t\t\tArray triple = cell_inst_dict[cell];\n\t\t\t\tTypedArray<Transform3D> xforms = triple[0];\n\t\t\t\tPackedColorArray colors = triple[1];\n\t\t\t\tTypedArray<Transform3D> updated_xforms;\n\t\t\t\tPackedColorArray updated_colors;\n\t\t\t\tfor (int i = 0; i < xforms.size(); i++) {\n\t\t\t\t\tTransform3D t = xforms[i];\n\t\t\t\t\tVector3 global_origin(t.origin + global_local_offset);\n\t\t\t\t\tif (rect.has_point(Vector2(global_origin.x, global_origin.z))) {\n\t\t\t\t\t\tVector3 height_offset = t.basis.get_column(1) * mesh_height_offset;\n\t\t\t\t\t\tt.origin -= height_offset;\n\t\t\t\t\t\tArray height_data = _get_usable_height(global_origin, Vector2(0.f, 90.f), on_collision, raycast_height);\n\t\t\t\t\t\tif (height_data.size() != 3) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tt.origin.y = height_data[0];\n\t\t\t\t\t\tt.origin += height_offset;\n\t\t\t\t\t}\n\t\t\t\t\tupdated_xforms.push_back(t);\n\t\t\t\t\tupdated_colors.push_back(colors[i]);\n\t\t\t\t}\n\t\t\t\tif (updated_xforms.size() > 0) {\n\t\t\t\t\ttriple[0] = updated_xforms;\n\t\t\t\t\ttriple[1] = updated_colors;\n\t\t\t\t\ttriple[2] = true;\n\t\t\t\t\tcell_inst_dict[cell] = triple;\n\t\t\t\t} else {\n\t\t\t\t\t// Removed if a hole erased everything\n\t\t\t\t\tcell_inst_dict.erase(cell);\n\t\t\t\t\t_destroy_mmi_by_cell(region_loc, region_mesh_id, cell);\n\t\t\t\t}\n\t\t\t\tif (cell_inst_dict.is_empty()) {\n\t\t\t\t\tmesh_inst_dict.erase(region_mesh_id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdate_mmis(region_mesh_id, region_loc);\n\t\t}\n\t}\n}\n\nint Terrain3DInstancer::get_closest_mesh_id(const Vector3 &p_global_position) const {\n\tLOG(INFO, \"Finding mesh asset ID closest to specified position \", p_global_position);\n\tVector2i region_loc = _terrain->get_data()->get_region_location(p_global_position);\n\tconst Terrain3DRegion *region = _terrain->get_data()->get_region_ptr(region_loc);\n\tif (!region) {\n\t\tLOG(WARN, \"No region found at position: \", p_global_position);\n\t\treturn -1; // No region found\n\t}\n\tint region_size = region->get_region_size();\n\tVector3 region_global_pos = v2iv3(region_loc) * real_t(region_size) * _terrain->get_vertex_spacing();\n\tVector2i cell = _get_cell(p_global_position, region_size);\n\tDictionary mesh_inst_dict = region->get_instances();\n\tif (mesh_inst_dict.is_empty()) {\n\t\treturn -1; // No meshes found\n\t}\n\tif (mesh_inst_dict.size() == 1) {\n\t\t// Only one mesh type in this region, return it\n\t\treturn mesh_inst_dict.keys()[0];\n\t}\n\t//Iterate through all mesh types looking for meshes in cell to find the closest transform\n\tint closest_id = -1;\n\treal_t closest_distance = FLT_MAX;\n\tArray mesh_instance_keys = mesh_inst_dict.keys();\n\tfor (const int &mesh_id : mesh_instance_keys) {\n\t\tDictionary cell_inst_dict = mesh_inst_dict[mesh_id];\n\t\tif (!cell_inst_dict.has(cell)) {\n\t\t\tcontinue; // No instances in this cell\n\t\t}\n\t\tArray triple = cell_inst_dict[cell];\n\t\tif (triple.size() != 3) {\n\t\t\tcontinue; // Invalid triple, skip\n\t\t}\n\t\tTypedArray<Transform3D> xforms = triple[0];\n\t\tif (xforms.is_empty()) {\n\t\t\tcontinue; // No transforms in this cell\n\t\t}\n\t\tfor (const Transform3D &instance_transform : xforms) {\n\t\t\tVector3 instance_origin = instance_transform.origin + region_global_pos; // Convert to global position\n\t\t\treal_t distance = instance_origin.distance_squared_to(p_global_position);\n\t\t\tif (distance < closest_distance) {\n\t\t\t\tclosest_distance = distance;\n\t\t\t\tclosest_id = mesh_id;\n\t\t\t}\n\t\t}\n\t}\n\tLOG(DEBUG, \"Found closest Mesh Asset ID: \", closest_id, \" at: \", closest_distance);\n\treturn closest_id;\n}\n\n// Transfer foliage data from one region to another\n// p_src_rect is the vertex/pixel offset into the region data, NOT a global position\n// Need to update_mmis() after\nvoid Terrain3DInstancer::copy_paste_dfr(const Terrain3DRegion *p_src_region, const Rect2i &p_src_rect, const Terrain3DRegion *p_dst_region) {\n\tif (!p_src_region || !p_dst_region) {\n\t\tLOG(ERROR, \"Source (\", p_src_region, \") or destination (\", p_dst_region, \") regions are null\");\n\t\treturn;\n\t}\n\tLOG(INFO, \"Copying foliage data from src \", p_src_region->get_location(), \" to dest \", p_dst_region->get_location());\n\n\treal_t vertex_spacing = _terrain->get_vertex_spacing();\n\t// Offset to dst from src\n\tVector2i src_region_loc = p_src_region->get_location();\n\tint src_region_size = p_src_region->get_region_size();\n\tVector2i src_offset = Vector2i(src_region_loc.x * src_region_size, src_region_loc.y * src_region_size);\n\tVector2i dst_region_loc = p_dst_region->get_location();\n\tint dst_region_size = p_dst_region->get_region_size();\n\tVector2i dst_offset = src_offset - Vector2i(dst_region_loc.x * dst_region_size, dst_region_loc.y * dst_region_size);\n\tVector3 dst_translate = Vector3(dst_offset.x, 0.f, dst_offset.y) * vertex_spacing;\n\n\t// Get all Cell locations in rect, which is already in region space.\n\tVector2i cell_start = p_src_rect.get_position() / CELL_SIZE;\n\tVector2i steps = p_src_rect.get_size() / CELL_SIZE;\n\tDictionary cells_to_copy;\n\tfor (int x = cell_start.x; x < cell_start.x + steps.x; x++) {\n\t\tfor (int y = cell_start.y; y < cell_start.y + steps.y; y++) {\n\t\t\tcells_to_copy[Vector2i(x, y)] = 0;\n\t\t}\n\t}\n\n\t// For each mesh, for each cell, if in rect, convert xforms to target region space, append to target region.\n\tDictionary mesh_inst_dict = p_src_region->get_instances();\n\tArray mesh_types = mesh_inst_dict.keys();\n\tfor (const int &mesh_id : mesh_types) {\n\t\tTypedArray<Transform3D> xforms;\n\t\tPackedColorArray colors;\n\t\tDictionary cell_inst_dict = p_src_region->get_instances()[mesh_id];\n\t\tArray cell_locs = cell_inst_dict.keys();\n\t\tfor (const Vector2i &cell : cell_locs) {\n\t\t\tif (cells_to_copy.has(cell)) {\n\t\t\t\tArray triple = cell_inst_dict[cell];\n\t\t\t\tTypedArray<Transform3D> cell_xforms = triple[0];\n\t\t\t\tPackedColorArray cell_colors = triple[1];\n\t\t\t\tfor (int i = 0; i < cell_xforms.size(); i++) {\n\t\t\t\t\tTransform3D t = cell_xforms[i];\n\t\t\t\t\tt.origin += dst_translate;\n\t\t\t\t\txforms.push_back(t);\n\t\t\t\t\tcolors.push_back(cell_colors[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (xforms.size() == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tappend_region(Ref<Terrain3DRegion>(p_dst_region), mesh_id, xforms, colors, false);\n\t}\n}\n\n// Changes the ID of a mesh, without changing the mesh on the ground\n// Called when the mesh asset id has changed. Updates Multimeshes and MMIs dictionary keys\nvoid Terrain3DInstancer::swap_ids(const int p_src_id, const int p_dst_id) {\n\tIS_DATA_INIT_MESG(\"Instancer isn't initialized.\", VOID);\n\tRef<Terrain3DAssets> assets = _terrain->get_assets();\n\tint mesh_count = assets->get_mesh_count();\n\tLOG(INFO, \"Swapping IDs of multimeshes: \", p_src_id, \" and \", p_dst_id);\n\tif (p_src_id >= 0 && p_src_id < mesh_count && p_dst_id >= 0 && p_dst_id < mesh_count) {\n\t\tTypedArray<Vector2i> region_locations = _terrain->get_data()->get_region_locations();\n\t\tfor (const Vector2i &region_loc : region_locations) {\n\t\t\tRef<Terrain3DRegion> region = _terrain->get_data()->get_region(region_loc);\n\t\t\tif (region.is_null()) {\n\t\t\t\tLOG(WARN, \"No region found at: \", region_loc);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// mesh_inst_dict could have src, src+dst, dst or nothing. All 4 must be considered\n\t\t\tDictionary mesh_inst_dict = region->get_instances();\n\t\t\tDictionary cells_inst_dict_src;\n\t\t\tDictionary cells_inst_dict_dst;\n\t\t\t// Extract src dict\n\t\t\tif (mesh_inst_dict.has(p_src_id)) {\n\t\t\t\t_backup_region(region);\n\t\t\t\tcells_inst_dict_src = mesh_inst_dict[p_src_id];\n\t\t\t\tmesh_inst_dict.erase(p_src_id);\n\t\t\t}\n\t\t\t// Extract dest dict\n\t\t\tif (mesh_inst_dict.has(p_dst_id)) {\n\t\t\t\t_backup_region(region);\n\t\t\t\tcells_inst_dict_dst = mesh_inst_dict[p_dst_id];\n\t\t\t\tmesh_inst_dict.erase(p_dst_id);\n\t\t\t}\n\t\t\t// If src exists, insert into dst slot\n\t\t\tif (!cells_inst_dict_src.is_empty()) {\n\t\t\t\t_backup_region(region);\n\t\t\t\tmesh_inst_dict[p_dst_id] = cells_inst_dict_src;\n\t\t\t}\n\t\t\t// If dst exists, insert into src slot\n\t\t\tif (!cells_inst_dict_dst.is_empty()) {\n\t\t\t\t_backup_region(region);\n\t\t\t\tmesh_inst_dict[p_src_id] = cells_inst_dict_dst;\n\t\t\t}\n\t\t\tLOG(DEBUG, \"Swapped mesh_ids for region: \", region_loc);\n\t\t}\n\t\tupdate_mmis(-1, V2I_MAX, true);\n\t}\n}\n\n// Defaults to update all regions, all meshes\n// If rebuild is true, will destroy all MMIs, then build everything\n// If region_loc == V2I_MAX, will do all regions for meshes specified\n// If mesh_id < 0, will do all meshes in the specified region\n// You safely can call multiple times per frame, and select any combo of options without fillling up the queue.\nvoid Terrain3DInstancer::update_mmis(const int p_mesh_id, const Vector2i &p_region_loc, const bool p_rebuild) {\n\tif (_mode == DISABLED) {\n\t\tLOG(INFO, \"Instancer is disabled\");\n\t\treturn;\n\t}\n\tLOG(INFO, \"Queueing MMI update for mesh id: \", p_mesh_id < 0 ? \"all\" : String::num_int64(p_mesh_id),\n\t\t\t\", region: \", p_region_loc == V2I_MAX ? \"all\" : String(p_region_loc),\n\t\t\tp_rebuild ? \", destroying first\" : \"\");\n\t// Set to destroy and rebuild everything\n\tif (p_rebuild) {\n\t\t_queued_updates.clear();\n\t\t_queued_updates.emplace(V2I_MAX, -2);\n\t\tif (!RS->is_connected(\"frame_pre_draw\", callable_mp(this, &Terrain3DInstancer::_process_updates))) {\n\t\t\tLOG(DEBUG, \"Connecting to RS::frame_pre_draw signal\");\n\t\t\tRS->connect(\"frame_pre_draw\", callable_mp(this, &Terrain3DInstancer::_process_updates));\n\t\t}\n\t\treturn;\n\t}\n\t// If already set to destroy, build all, quit\n\tif (_queued_updates.find({ V2I_MAX, -2 }) != _queued_updates.end()) {\n\t\treturn;\n\t}\n\t// If already set to build all, quit\n\tif (_queued_updates.find({ V2I_MAX, -1 }) != _queued_updates.end()) {\n\t\treturn;\n\t}\n\t// If all meshes for region are queued, quit\n\tif (_queued_updates.find({ p_region_loc, -1 }) != _queued_updates.end()) {\n\t\treturn;\n\t}\n\t// If all regions for mesh_id are queued, quit\n\tint mesh_id = CLAMP(p_mesh_id, -1, Terrain3DAssets::MAX_MESHES - 1);\n\tif (_queued_updates.find({ V2I_MAX, mesh_id }) != _queued_updates.end()) {\n\t\treturn;\n\t}\n\t// Else queue up this region/mesh combo\n\t_queued_updates.emplace(p_region_loc, mesh_id);\n\tif (!RS->is_connected(\"frame_pre_draw\", callable_mp(this, &Terrain3DInstancer::_process_updates))) {\n\t\tLOG(DEBUG, \"Connecting to RS::frame_pre_draw signal\");\n\t\tRS->connect(\"frame_pre_draw\", callable_mp(this, &Terrain3DInstancer::_process_updates));\n\t}\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DInstancer::_bind_methods() {\n\tBIND_ENUM_CONSTANT(NORMAL);\n\t//BIND_ENUM_CONSTANT(PLACEHOLDER);\n\tBIND_ENUM_CONSTANT(DISABLED);\n\n\tClassDB::bind_method(D_METHOD(\"clear_by_mesh\", \"mesh_id\"), &Terrain3DInstancer::clear_by_mesh);\n\tClassDB::bind_method(D_METHOD(\"clear_by_location\", \"region_location\", \"mesh_id\"), &Terrain3DInstancer::clear_by_location);\n\tClassDB::bind_method(D_METHOD(\"clear_by_region\", \"region\", \"mesh_id\"), &Terrain3DInstancer::clear_by_region);\n\tClassDB::bind_method(D_METHOD(\"set_mode\", \"mode\"), &Terrain3DInstancer::set_mode);\n\tClassDB::bind_method(D_METHOD(\"get_mode\"), &Terrain3DInstancer::get_mode);\n\tClassDB::bind_method(D_METHOD(\"is_enabled\"), &Terrain3DInstancer::is_enabled);\n\tClassDB::bind_method(D_METHOD(\"add_instances\", \"global_position\", \"params\"), &Terrain3DInstancer::add_instances);\n\tClassDB::bind_method(D_METHOD(\"remove_instances\", \"global_position\", \"params\"), &Terrain3DInstancer::remove_instances);\n\tClassDB::bind_method(D_METHOD(\"add_multimesh\", \"mesh_id\", \"multimesh\", \"transform\", \"update\"), &Terrain3DInstancer::add_multimesh, DEFVAL(Transform3D()), DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"add_transforms\", \"mesh_id\", \"transforms\", \"colors\", \"update\"), &Terrain3DInstancer::add_transforms, DEFVAL(PackedColorArray()), DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"append_location\", \"region_location\", \"mesh_id\", \"transforms\", \"colors\", \"update\"), &Terrain3DInstancer::append_location, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"append_region\", \"region\", \"mesh_id\", \"transforms\", \"colors\", \"update\"), &Terrain3DInstancer::append_region, DEFVAL(true));\n\tClassDB::bind_method(D_METHOD(\"update_transforms\", \"aabb\"), &Terrain3DInstancer::update_transforms);\n\tClassDB::bind_method(D_METHOD(\"get_closest_mesh_id\", \"global_position\"), &Terrain3DInstancer::get_closest_mesh_id);\n\tClassDB::bind_method(D_METHOD(\"update_mmis\", \"mesh_id\", \"region_location\", \"rebuild_all\"), &Terrain3DInstancer::update_mmis, DEFVAL(-1), DEFVAL(V2I_MAX), DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"swap_ids\", \"src_id\", \"dest_id\"), &Terrain3DInstancer::swap_ids);\n\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"mode\", PROPERTY_HINT_ENUM, \"Disabled,Normal\"), \"set_mode\", \"get_mode\");\n}\n"
  },
  {
    "path": "src/terrain_3d_instancer.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_INSTANCER_CLASS_H\n#define TERRAIN3D_INSTANCER_CLASS_H\n\n#include <godot_cpp/classes/multi_mesh.hpp>\n#include <godot_cpp/classes/multi_mesh_instance3d.hpp>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"constants.h\"\n#include \"terrain_3d_region.h\"\n\nclass Terrain3D;\nclass Terrain3DAssets;\n\nclass Terrain3DInstancer : public Object {\n\tGDCLASS(Terrain3DInstancer, Object);\n\tCLASS_NAME();\n\tfriend Terrain3D;\n\npublic: // Constants\n\tstatic inline const int CELL_SIZE = 32;\n\n\tenum InstancerMode {\n\t\tDISABLED,\n\t\t//PLACEHOLDER,\n\t\tNORMAL,\n\t};\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\n\t// MM Resources stored in Terrain3DRegion::_instances as\n\t// Region::_instances{mesh_id:int} -> cell{v2i} -> [ TypedArray<Transform3D>, PackedColorArray, modified:bool ]\n\n\t// A pair of MMI and MM RIDs, freed in destructor, stored as\n\t// _mmi_rids{region_loc} -> mesh{v2i(mesh_id,lod)} -> cell{v2i} -> std::pair<mmi_RID, mm_RID>\n\n\tusing CellMMIDict = std::unordered_map<Vector2i, std::pair<RID, RID>, Vector2iHash>;\n\tusing MeshMMIDict = std::unordered_map<Vector2i, CellMMIDict, Vector2iHash>;\n\tstd::unordered_map<Vector2i, MeshMMIDict, Vector2iHash> _mmi_rids;\n\n\t// MMI Updates tracked in a unique Set of <region_location, mesh_id>\n\t// <V2I_MAX, -2> means destroy first, then update everything\n\t// <V2I_MAX, -1> means update everything\n\t// <reg_loc, -1> means update all meshes in that region\n\t// <V2I_MAX, N> means update mesh ID N in all regions\n\tusing V2IIntPair = std::unordered_set<std::pair<Vector2i, int>, PairVector2iIntHash>;\n\tV2IIntPair _queued_updates;\n\n\tInstancerMode _mode = NORMAL;\n\tuint32_t _density_counter = 0;\n\n\tuint32_t _get_density_count(const real_t p_density);\n\tint _get_master_lod(const Ref<Terrain3DMeshAsset> &p_ma);\n\tvoid _process_updates();\n\tvoid _update_mmi_by_region(const Terrain3DRegion *p_region, const int p_mesh_id);\n\tvoid _set_mmi_lod_ranges(RID p_mmi, const Ref<Terrain3DMeshAsset> &p_ma, const int p_lod);\n\tvoid _update_vertex_spacing(const real_t p_vertex_spacing);\n\tvoid _destroy_mmi_by_mesh(const int p_mesh_id);\n\tvoid _destroy_mmi_by_location(const Vector2i &p_region_loc, const int p_mesh_id);\n\tvoid _destroy_mmi_by_cell(const Vector2i &p_region_loc, const int p_mesh_id, const Vector2i p_cell, const int p_lod = INT32_MAX);\n\tvoid _backup_region(const Ref<Terrain3DRegion> &p_region);\n\tRID _create_multimesh(const int p_mesh_id, const int p_lod, const TypedArray<Transform3D> &p_xforms = TypedArray<Transform3D>(), const PackedColorArray &p_colors = PackedColorArray()) const;\n\tVector2i _get_cell(const Vector3 &p_global_position, const int p_region_size) const;\n\tArray _get_usable_height(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_on_collision, const real_t p_raycast_start) const;\n\npublic:\n\tTerrain3DInstancer() {}\n\t~Terrain3DInstancer() { destroy(); }\n\n\tvoid initialize(Terrain3D *p_terrain);\n\tvoid destroy();\n\n\tvoid clear_by_mesh(const int p_mesh_id);\n\tvoid clear_by_location(const Vector2i &p_region_loc, const int p_mesh_id);\n\tvoid clear_by_region(const Ref<Terrain3DRegion> &p_region, const int p_mesh_id);\n\n\tvoid set_mode(const InstancerMode p_mode);\n\tInstancerMode get_mode() const { return _mode; }\n\tbool is_enabled() const { return _mode != DISABLED; }\n\n\tvoid add_instances(const Vector3 &p_global_position, const Dictionary &p_params);\n\tvoid remove_instances(const Vector3 &p_global_position, const Dictionary &p_params);\n\tvoid add_multimesh(const int p_mesh_id, const Ref<MultiMesh> &p_multimesh, const Transform3D &p_xform = Transform3D(), const bool p_update = true);\n\tvoid add_transforms(const int p_mesh_id, const TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors = PackedColorArray(), const bool p_update = true);\n\tvoid append_location(const Vector2i &p_region_loc, const int p_mesh_id, const TypedArray<Transform3D> &p_xforms,\n\t\t\tconst PackedColorArray &p_colors, const bool p_update = true);\n\tvoid append_region(const Ref<Terrain3DRegion> &p_region, const int p_mesh_id, const TypedArray<Transform3D> &p_xforms,\n\t\t\tconst PackedColorArray &p_colors, const bool p_update = true);\n\tvoid update_transforms(const AABB &p_aabb);\n\tint get_closest_mesh_id(const Vector3 &p_global_position) const;\n\tvoid copy_paste_dfr(const Terrain3DRegion *p_src_region, const Rect2i &p_src_rect, const Terrain3DRegion *p_dst_region);\n\n\tvoid swap_ids(const int p_src_id, const int p_dst_id);\n\tvoid update_mmis(const int p_mesh_id = -1, const Vector2i &p_region_loc = V2I_MAX, const bool p_rebuild = false);\n\n\tvoid reset_density_counter() { _density_counter = 0; }\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nusing InstancerMode = Terrain3DInstancer::InstancerMode;\nVARIANT_ENUM_CAST(Terrain3DInstancer::InstancerMode);\n\n// Allows us to instance every X function calls for sparse placement\n// Modifies _density_counter, not const!\ninline uint32_t Terrain3DInstancer::_get_density_count(const real_t p_density) {\n\tuint32_t count = 0;\n\tif (p_density < 1.f && _density_counter++ % uint32_t(1.f / p_density) == 0) {\n\t\tcount = 1;\n\t} else if (p_density >= 1.f) {\n\t\tcount = uint32_t(p_density);\n\t}\n\treturn count;\n}\n\n// Use lod0 to track instance counter and set AABB, but in shadows_only lod0 doesn't exist\ninline int Terrain3DInstancer::_get_master_lod(const Ref<Terrain3DMeshAsset> &p_ma) {\n\tif (p_ma.is_valid() && p_ma->get_cast_shadows() == SHADOWS_ONLY) {\n\t\treturn p_ma->get_shadow_impostor();\n\t}\n\treturn 0;\n}\n\n#endif // TERRAIN3D_INSTANCER_CLASS_H"
  },
  {
    "path": "src/terrain_3d_material.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/fast_noise_lite.hpp>\n#include <godot_cpp/classes/gradient.hpp>\n#include <godot_cpp/classes/image_texture.hpp>\n#include <godot_cpp/classes/noise_texture2d.hpp>\n#include <godot_cpp/classes/reg_ex.hpp>\n#include <godot_cpp/classes/reg_ex_match.hpp>\n#include <godot_cpp/classes/rendering_server.hpp>\n#include <godot_cpp/classes/resource_saver.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d_material.h\"\n#include \"terrain_3d_util.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\nvoid Terrain3DMaterial::_preload_shaders() {\n\t// Preprocessor loading of external shader inserts\n\t_parse_shader(\n#include \"shaders/samplers.glsl\"\n\t\t\t, \"samplers\");\n\t_parse_shader(\n#include \"shaders/backgrounds.glsl\"\n\t\t\t, \"backgrounds\");\n\t_parse_shader(\n#include \"shaders/auto_shader.glsl\"\n\t\t\t, \"auto_shader\");\n\t_parse_shader(\n#include \"shaders/dual_scaling.glsl\"\n\t\t\t, \"dual_scaling\");\n\t_parse_shader(\n#include \"shaders/overlays.glsl\"\n\t\t\t, \"overlays\");\n\t_parse_shader(\n#include \"shaders/displacement.glsl\"\n\t\t\t, \"displacement\");\n\t_parse_shader(\n#include \"shaders/macro_variation.glsl\"\n\t\t\t, \"macro_variation\");\n\t_parse_shader(\n#include \"shaders/projection.glsl\"\n\t\t\t, \"projection\");\n\t_parse_shader(\n#include \"shaders/debug_views.glsl\"\n\t\t\t, \"debug_views\");\n\t_parse_shader(\n#include \"shaders/pbr_views.glsl\"\n\t\t\t, \"pbr_views\");\n\t_parse_shader(\n#include \"shaders/editor_functions.glsl\"\n\t\t\t, \"editor_functions\");\n\n\t// Load main code\n\t_shader_code[\"main\"] = String(\n#include \"shaders/main.glsl\"\n\t);\n\t_shader_code[\"displacement_buffer\"] = String(\n#include \"shaders/displacement_buffer.glsl\"\n\t);\n\n\tif (Terrain3D::debug_level >= DEBUG) {\n\t\tArray keys = _shader_code.keys();\n\t\tfor (const StringName &key : keys) {\n\t\t\tLOG(DEBUG, \"Loaded shader insert: \", key);\n\t\t}\n\t}\n}\n\n/**\n *\tAll `//INSERT: ID` blocks in p_shader are loaded into the DB _shader_code\n */\nvoid Terrain3DMaterial::_parse_shader(const String &p_shader, const String &p_name) {\n\tif (p_name.is_empty()) {\n\t\tLOG(ERROR, \"No dictionary key for saving shader snippets specified\");\n\t\treturn;\n\t}\n\tPackedStringArray parsed = p_shader.split(\"//INSERT:\");\n\tfor (int i = 0; i < parsed.size(); i++) {\n\t\t// First section of the file before any //INSERT:\n\t\tif (i == 0) {\n\t\t\t_shader_code[p_name] = parsed[0];\n\t\t} else {\n\t\t\t// There is at least one //INSERT:\n\t\t\t// Get the first ID on the first line\n\t\t\tPackedStringArray segment = parsed[i].split(\"\\n\", true, 1);\n\t\t\t// If there isn't an ID AND body, skip this insert\n\t\t\tif (segment.size() < 2) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString id = segment[0].strip_edges();\n\t\t\t// Process the insert\n\t\t\tif (!id.is_empty() && !segment[1].is_empty()) {\n\t\t\t\t_shader_code[id] = segment[1];\n\t\t\t}\n\t\t}\n\t}\n\treturn;\n}\n\n/**\n *\t`//INSERT: ID` blocks in p_shader are replaced by the entry in the DB\n *\treturns a shader string with inserts applied\n *  Skips `EDITOR_*` and `DEBUG_*` inserts\n */\nString Terrain3DMaterial::_apply_inserts(const String &p_shader, const Array &p_excludes) const {\n\tPackedStringArray parsed = p_shader.split(\"//INSERT:\");\n\tString shader;\n\tfor (int i = 0; i < parsed.size(); i++) {\n\t\t// First section of the file before any //INSERT:\n\t\tif (i == 0) {\n\t\t\tshader = parsed[0];\n\t\t} else {\n\t\t\t// There is at least one //INSERT:\n\t\t\t// Get the first ID on the first line\n\t\t\tPackedStringArray segment = parsed[i].split(\"\\n\", true, 1);\n\t\t\t// If there isn't an ID AND body, skip this insert\n\t\t\tif (segment.size() < 2) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString id = segment[0].strip_edges();\n\n\t\t\t// Process the insert\n\t\t\tif (!id.is_empty() && !p_excludes.has(id) && _shader_code.has(id)) {\n\t\t\t\tif (!id.begins_with(\"DEBUG_\") && !id.begins_with(\"EDITOR_\")) {\n\t\t\t\t\tString str = _shader_code[id];\n\t\t\t\t\tshader += str;\n\t\t\t\t}\n\t\t\t}\n\t\t\tshader += segment[1];\n\t\t}\n\t}\n\treturn shader;\n}\n\nString Terrain3DMaterial::_generate_shader_code() const {\n\tLOG(INFO, \"Generating default shader code\");\n\tArray excludes;\n\tif (_world_background != NONE) {\n\t\texcludes.push_back(\"NONE_FUNCTIONS\");\n\t\texcludes.push_back(\"NONE_CHECK\");\n\t}\n\tif (_world_background == NONE) {\n\t\texcludes.push_back(\"FLAT_UNIFORMS\");\n\t\texcludes.push_back(\"FLAT_FUNCTIONS\");\n\t\texcludes.push_back(\"FLAT_VERTEX\");\n\t\texcludes.push_back(\"FLAT_FRAGMENT\");\n\t}\n\tif (_world_background != NOISE) {\n\t\texcludes.push_back(\"WORLD_NOISE_UNIFORMS\");\n\t\texcludes.push_back(\"WORLD_NOISE_FUNCTIONS\");\n\t\texcludes.push_back(\"WORLD_NOISE_VERTEX\");\n\t\texcludes.push_back(\"WORLD_NOISE_FRAGMENT\");\n\t}\n\tswitch (_texture_filtering) {\n\t\tcase LINEAR_ANISOTROPIC:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR\");\n\t\t\tbreak;\n\t\tcase LINEAR:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\");\n\t\t\tbreak;\n\t\tcase NEAREST_ANISOTROPIC:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\");\n\t\t\tbreak;\n\t\tcase NEAREST:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\");\n\t\t\tbreak;\n\t}\n\tif (!_auto_shader_enabled) {\n\t\texcludes.push_back(\"AUTO_SHADER_UNIFORMS\");\n\t\texcludes.push_back(\"AUTO_SHADER\");\n\t}\n\tif (!_dual_scaling_enabled) {\n\t\texcludes.push_back(\"DUAL_SCALING_UNIFORMS\");\n\t\texcludes.push_back(\"DUAL_SCALING\");\n\t\texcludes.push_back(\"DUAL_SCALING_CONDITION_0\");\n\t\texcludes.push_back(\"DUAL_SCALING_CONDITION_1\");\n\t\texcludes.push_back(\"DUAL_SCALING_MIX\");\n\t}\n\tif (!_macro_variation_enabled) {\n\t\texcludes.push_back(\"MACRO_VARIATION_UNIFORMS\");\n\t\texcludes.push_back(\"MACRO_VARIATION\");\n\t}\n\tif (!_projection_enabled) {\n\t\texcludes.push_back(\"PROJECTION\");\n\t}\n\tif (_terrain->get_tessellation_level() == 0) {\n\t\texcludes.push_back(\"DISPLACEMENT_UNIFORMS\");\n\t\texcludes.push_back(\"DISPLACEMENT_FUNCTIONS\");\n\t\texcludes.push_back(\"DISPLACEMENT_VERTEX\");\n\t}\n\tif (!_output_albedo_enabled) {\n\t\texcludes.push_back(\"OUTPUT_ALBEDO\");\n\t} else {\n\t\texcludes.push_back(\"OUTPUT_ALBEDO_GREY\");\n\t}\n\tif (!_output_roughness_enabled) {\n\t\texcludes.push_back(\"OUTPUT_ROUGHNESS\");\n\t}\n\tif (!_output_normal_map_enabled) {\n\t\texcludes.push_back(\"OUTPUT_NORMAL_MAP\");\n\t}\n\tif (!_output_ambient_occlusion_enabled) {\n\t\texcludes.push_back(\"OUTPUT_AMBIENT_OCCLUSION\");\n\t}\n\tString shader = _apply_inserts(_shader_code[\"main\"], excludes);\n\treturn shader;\n}\n\n// Ripped from ShaderPreprocessor::CommentRemover::strip()\nString Terrain3DMaterial::_strip_comments(const String &p_shader) const {\n\tVector<char32_t> stripped;\n\tString code = p_shader;\n\tint index = 0;\n\tint line = 0;\n\tint comment_line_open = 0;\n\tint comments_open = 0;\n\tint strings_open = 0;\n\tconst char32_t CURSOR = 0xFFFF;\n\n\t// Embedded supporting functions\n\n\tauto peek = [&]() { return (index < code.length()) ? code[index] : 0; };\n\n\tauto advance = [&](char32_t p_what) {\n\t\twhile (index < code.length()) {\n\t\t\tchar32_t c = code[index++];\n\t\t\tif (c == '\\n') {\n\t\t\t\tline++;\n\t\t\t\tstripped.push_back('\\n');\n\t\t\t}\n\t\t\tif (c == p_what) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t};\n\n\tauto vector_to_string = [](const Vector<char32_t> &p_v, int p_start = 0, int p_end = -1) {\n\t\tconst int stop = (p_end == -1) ? p_v.size() : p_end;\n\t\tconst int count = stop - p_start;\n\t\tString result;\n\t\tresult.resize(count + 1);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tresult[i] = p_v[p_start + i];\n\t\t}\n\t\tresult[count] = 0; // Ensure string is null terminated for length() to work.\n\t\treturn result;\n\t};\n\n\t// Main function\n\n\twhile (index < code.length()) {\n\t\tchar32_t c = code[index++];\n\t\tif (c == CURSOR) {\n\t\t\t// Cursor. Maintain.\n\t\t\tstripped.push_back(c);\n\t\t} else if (c == '\"') {\n\t\t\tif (strings_open <= 0) {\n\t\t\t\tstrings_open++;\n\t\t\t} else {\n\t\t\t\tstrings_open--;\n\t\t\t}\n\t\t\tstripped.push_back(c);\n\t\t} else if (c == '/' && strings_open == 0) {\n\t\t\tchar32_t p = peek();\n\t\t\tif (p == '/') { // Single line comment.\n\t\t\t\tadvance('\\n');\n\t\t\t} else if (p == '*') { // Start of a block comment.\n\t\t\t\tindex++;\n\t\t\t\tcomment_line_open = line;\n\t\t\t\tcomments_open++;\n\t\t\t\twhile (advance('*')) {\n\t\t\t\t\tif (peek() == '/') { // End of a block comment.\n\t\t\t\t\t\tcomments_open--;\n\t\t\t\t\t\tindex++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstripped.push_back(c);\n\t\t\t}\n\t\t} else if (c == '*' && strings_open == 0) {\n\t\t\tif (peek() == '/') { // Unmatched end of a block comment.\n\t\t\t\tcomment_line_open = line;\n\t\t\t\tcomments_open--;\n\t\t\t} else {\n\t\t\t\tstripped.push_back(c);\n\t\t\t}\n\t\t} else if (c == '\\n') {\n\t\t\tline++;\n\t\t\tstripped.push_back(c);\n\t\t} else {\n\t\t\tstripped.push_back(c);\n\t\t}\n\t}\n\treturn vector_to_string(stripped);\n}\n\nString Terrain3DMaterial::_generate_buffer_shader_code() const {\n\tLOG(INFO, \"Generating default displacement buffer shader code\");\n\tArray excludes;\n\tif (_world_background != NONE) {\n\t\texcludes.push_back(\"NONE_FUNCTIONS\");\n\t\texcludes.push_back(\"NONE_CHECK\");\n\t}\n\tif (_world_background == NONE) {\n\t\texcludes.push_back(\"FLAT_UNIFORMS\");\n\t\texcludes.push_back(\"FLAT_FUNCTIONS\");\n\t\texcludes.push_back(\"FLAT_FRAGMENT\");\n\t}\n\tswitch (_texture_filtering) {\n\t\tcase LINEAR_ANISOTROPIC:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR\");\n\t\t\tbreak;\n\t\tcase LINEAR:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\");\n\t\t\tbreak;\n\t\tcase NEAREST_ANISOTROPIC:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\");\n\t\t\tbreak;\n\t\tcase NEAREST:\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_NEAREST_ANISOTROPIC\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR\");\n\t\t\texcludes.push_back(\"TEXTURE_SAMPLERS_LINEAR_ANISOTROPIC\");\n\t\t\tbreak;\n\t}\n\tif (!_auto_shader_enabled) {\n\t\texcludes.push_back(\"AUTO_SHADER_UNIFORMS\");\n\t\texcludes.push_back(\"AUTO_SHADER\");\n\t}\n\tif (!_projection_enabled) {\n\t\texcludes.push_back(\"PROJECTION\");\n\t}\n\tString shader = _apply_inserts(_shader_code[\"displacement_buffer\"], excludes);\n\treturn shader;\n}\n\nString Terrain3DMaterial::_inject_editor_code(const String &p_shader) const {\n\tString shader = _strip_comments(p_shader);\n\n\t// Insert after render_mode\n\tRef<RegEx> regex;\n\tregex.instantiate();\n\tregex->compile(\"render_mode.*;?\");\n\tRef<RegExMatch> match = regex->search(shader);\n\tint idx = match.is_valid() ? match->get_end() : -1;\n\tif (idx < 0) {\n\t\tLOG(DEBUG, \"No render mode; cannot inject editor code\");\n\t\treturn shader;\n\t}\n\tArray insert_names;\n\n\t// Whilst this currently does nothing, it can serve as placeholder for future refactor\n\t// when useing pre-processor headers to control shader features.\n\t//for (int i = 0; i < insert_names.size(); i++) {\n\t//\tString insert = _shader_code[insert_names[i]];\n\t//\tshader = shader.insert(idx, \"\\n\\n\" + insert);\n\t//\tidx += insert.length();\n\t//}\n\t//insert_names.clear();\n\n\t// Insert before vertex()\n\tregex->compile(\"void\\\\s+vertex\\\\s*\\\\(\");\n\tmatch = regex->search(shader);\n\tidx = match.is_valid() ? match->get_start() - 1 : -1;\n\tif (idx < 0) {\n\t\tLOG(DEBUG, \"No void vertex(); cannot inject editor code\");\n\t\treturn shader;\n\t}\n\tif (_terrain && _terrain->get_editor()) {\n\t\tinsert_names.push_back(\"EDITOR_DECAL_SETUP\");\n\t}\n\tif (_debug_view_heightmap) {\n\t\tinsert_names.push_back(\"DEBUG_HEIGHTMAP_SETUP\");\n\t}\n\tif (_show_contours) {\n\t\tinsert_names.push_back(\"OVERLAY_CONTOURS_SETUP\");\n\t}\n\t// Apply pending inserts\n\tfor (const String &name : insert_names) {\n\t\tString insert = _shader_code[name];\n\t\tshader = shader.insert(idx, \"\\n\" + insert);\n\t\tidx += insert.length();\n\t}\n\tinsert_names.clear();\n\n\t// Insert at the end of `fragment(){ }`\n\t// Check for each nested {} pair until the closing } is found.\n\tregex->compile(\"void\\\\s*fragment\\\\s*\\\\(\\\\s*\\\\)\\\\s*{\");\n\tmatch = regex->search(shader);\n\tidx = -1;\n\tif (match.is_valid()) {\n\t\tint start_idx = match->get_end() - 1;\n\t\tint pair = 0;\n\t\tfor (int i = start_idx; i < shader.length(); i++) {\n\t\t\tif (shader[i] == '{') {\n\t\t\t\tpair++;\n\t\t\t} else if (shader[i] == '}') {\n\t\t\t\tpair--;\n\t\t\t}\n\t\t\tif (pair == 0) {\n\t\t\t\tidx = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (idx < 0) {\n\t\tLOG(DEBUG, \"No ending bracket; cannot inject editor code\");\n\t\treturn shader;\n\t}\n\n\t// Debug Views\n\tif (_debug_view_checkered) {\n\t\tinsert_names.push_back(\"DEBUG_CHECKERED\");\n\t}\n\tif (_debug_view_grey) {\n\t\tinsert_names.push_back(\"DEBUG_GREY\");\n\t}\n\tif (_debug_view_heightmap) {\n\t\tinsert_names.push_back(\"DEBUG_HEIGHTMAP\");\n\t}\n\tif (_debug_view_jaggedness) {\n\t\tinsert_names.push_back(\"DEBUG_JAGGEDNESS\");\n\t}\n\tif (_debug_view_autoshader) {\n\t\tinsert_names.push_back(\"DEBUG_AUTOSHADER\");\n\t}\n\tif (_debug_view_control_texture) {\n\t\tinsert_names.push_back(\"DEBUG_CONTROL_TEXTURE\");\n\t}\n\tif (_debug_view_control_blend) {\n\t\tinsert_names.push_back(\"DEBUG_CONTROL_BLEND\");\n\t}\n\tif (_debug_view_control_angle) {\n\t\tinsert_names.push_back(\"DEBUG_CONTROL_ANGLE\");\n\t}\n\tif (_debug_view_control_scale) {\n\t\tinsert_names.push_back(\"DEBUG_CONTROL_SCALE\");\n\t}\n\tif (_debug_view_colormap) {\n\t\tinsert_names.push_back(\"DEBUG_COLORMAP\");\n\t}\n\tif (_debug_view_roughmap) {\n\t\tinsert_names.push_back(\"DEBUG_ROUGHMAP\");\n\t}\n\t// PBR views\n\tif (_pbr_view_tex_albedo) {\n\t\tinsert_names.push_back(\"PBR_TEXTURE_ALBEDO\");\n\t}\n\tif (_pbr_view_tex_height) {\n\t\tinsert_names.push_back(\"PBR_TEXTURE_HEIGHT\");\n\t}\n\tif (_pbr_view_tex_normal) {\n\t\tinsert_names.push_back(\"PBR_TEXTURE_NORMAL\");\n\t}\n\tif (_pbr_view_tex_ao) {\n\t\tinsert_names.push_back(\"PBR_TEXTURE_AO\");\n\t}\n\tif (_pbr_view_tex_rough) {\n\t\tinsert_names.push_back(\"PBR_TEXTURE_ROUGHNESS\");\n\t}\n\tif (_debug_view_displacement_buffer) {\n\t\tinsert_names.push_back(\"DEBUG_DISPLACEMENT_BUFFER\");\n\t}\n\t// Overlays\n\tif (_show_contours) {\n\t\tinsert_names.push_back(\"OVERLAY_CONTOURS_RENDER\");\n\t}\n\tif (_show_instancer_grid) {\n\t\tinsert_names.push_back(\"OVERLAY_INSTANCER_GRID\");\n\t}\n\tif (_show_vertex_grid) {\n\t\tinsert_names.push_back(\"OVERLAY_VERTEX_GRID\");\n\t}\n\t// Editor Functions\n\tif (_show_navigation || (_terrain && _terrain->get_editor() && _terrain->get_editor()->get_tool() == Terrain3DEditor::NAVIGATION)) {\n\t\tinsert_names.push_back(\"EDITOR_NAVIGATION\");\n\t}\n\tif (_show_region_grid || (_terrain && _terrain->get_editor() && _terrain->get_editor()->get_tool() == Terrain3DEditor::REGION)) {\n\t\tinsert_names.push_back(\"EDITOR_REGION_GRID\");\n\t}\n\tif (_terrain && _terrain->get_editor()) {\n\t\tinsert_names.push_back(\"EDITOR_DECAL_RENDER\");\n\t}\n\t// Apply pending inserts\n\tfor (const String &name : insert_names) {\n\t\tString insert = _shader_code[name];\n\t\tshader = shader.insert(idx, \"\\n\" + insert);\n\t\tidx += insert.length();\n\t}\n\treturn shader;\n}\n\nvoid Terrain3DMaterial::_update_shader() {\n\tIS_INIT(VOID);\n\tLOG(INFO, \"Updating shader\");\n\tString code;\n\tRef<RegEx> regex;\n\tRef<RegExMatch> match;\n\tregex.instantiate();\n\t// Terrain Material\n\tif (_shader_override_enabled && _shader_override.is_valid()) {\n\t\tif (_shader_override->get_code().is_empty()) {\n\t\t\t_shader_override->set_code(_generate_shader_code());\n\t\t}\n\t\tcode = _shader_override->get_code();\n\t\tif (!_shader_override->is_connected(\"changed\", callable_mp(this, &Terrain3DMaterial::_update_shader))) {\n\t\t\tLOG(DEBUG, \"Connecting changed signal to _update_shader()\");\n\t\t\t_shader_override->connect(\"changed\", callable_mp(this, &Terrain3DMaterial::_update_shader));\n\t\t}\n\t} else {\n\t\tcode = _generate_shader_code();\n\t}\n\t_shader->set_code(_inject_editor_code(code));\n\tRS->material_set_shader(_material, get_shader_rid());\n\tLOG(DEBUG, \"Material rid: \", _material, \", shader rid: \", get_shader_rid());\n\n\t// Displacement Buffer\n\tif (_terrain->get_tessellation_level() > 0) {\n\t\tif (_buffer_shader_override_enabled && _buffer_shader_override.is_valid()) {\n\t\t\tif (_buffer_shader_override->get_code().is_empty()) {\n\t\t\t\t_buffer_shader_override->set_code(_generate_buffer_shader_code());\n\t\t\t}\n\t\t\tcode = _buffer_shader_override->get_code();\n\t\t\tif (!_buffer_shader_override->is_connected(\"changed\", callable_mp(this, &Terrain3DMaterial::_update_shader))) {\n\t\t\t\tLOG(DEBUG, \"Connecting changed signal to _update_shader()\");\n\t\t\t\t_buffer_shader_override->connect(\"changed\", callable_mp(this, &Terrain3DMaterial::_update_shader));\n\t\t\t}\n\t\t} else {\n\t\t\tcode = _generate_buffer_shader_code();\n\t\t}\n\t\t_buffer_shader->set_code(code);\n\t\tRS->material_set_shader(_buffer_material, get_buffer_shader_rid());\n\t\tLOG(DEBUG, \"Buffer Material rid: \", _buffer_material, \", buffer shader rid: \", get_buffer_shader_rid());\n\t} else {\n\t\t_buffer_shader->set_code(String(\"shader_type spatial;\"));\n\t\tRS->material_set_shader(_buffer_material, RID());\n\t}\n\n\t// Update custom shader params in RenderingServer\n\t{\n\t\t// Populate _active_params\n\t\tList<PropertyInfo> pi;\n\t\t_get_property_list(&pi);\n\t\tLOG(EXTREME, \"_active_params: \", _active_params);\n\t\tUtil::print_dict(\"_shader_params\", _shader_params, EXTREME);\n\t}\n\n\t// Fetch saved shader parameters, converting textures to RIDs\n\tfor (const StringName &param : _active_params) {\n\t\tVariant value = _shader_params[param];\n\t\tif (value.get_type() == Variant::OBJECT) {\n\t\t\tRef<Texture> tex = value;\n\t\t\tif (tex.is_valid()) {\n\t\t\t\tRS->material_set_param(_material, param, tex->get_rid());\n\t\t\t\tRS->material_set_param(_buffer_material, param, tex->get_rid());\n\t\t\t} else {\n\t\t\t\tRS->material_set_param(_material, param, Variant());\n\t\t\t\tRS->material_set_param(_buffer_material, param, Variant());\n\t\t\t}\n\t\t} else {\n\t\t\tRS->material_set_param(_material, param, value);\n\t\t\tRS->material_set_param(_buffer_material, param, value);\n\t\t}\n\t}\n\n\t// Set specific shader parameters\n\tRS->material_set_param(_material, \"_background_mode\", _world_background);\n\n\t// If no noise texture, generate one\n\tif (_active_params.has(\"noise_texture\") && RS->material_get_param(_material, \"noise_texture\").get_type() == Variant::NIL) {\n\t\tLOG(INFO, \"Generating default noise_texture for shader\");\n\t\tRef<FastNoiseLite> fnoise;\n\t\tfnoise.instantiate();\n\t\tfnoise->set_noise_type(FastNoiseLite::TYPE_CELLULAR);\n\t\tfnoise->set_frequency(0.03f);\n\t\tfnoise->set_cellular_jitter(3.0f);\n\t\tfnoise->set_cellular_return_type(FastNoiseLite::RETURN_CELL_VALUE);\n\t\tfnoise->set_domain_warp_enabled(true);\n\t\tfnoise->set_domain_warp_type(FastNoiseLite::DOMAIN_WARP_SIMPLEX_REDUCED);\n\t\tfnoise->set_domain_warp_amplitude(50.f);\n\t\tfnoise->set_domain_warp_fractal_type(FastNoiseLite::DOMAIN_WARP_FRACTAL_INDEPENDENT);\n\t\tfnoise->set_domain_warp_fractal_lacunarity(1.5f);\n\t\tfnoise->set_domain_warp_fractal_gain(1.f);\n\n\t\tRef<Gradient> curve;\n\t\tcurve.instantiate();\n\t\tPackedFloat32Array pfa;\n\t\tpfa.push_back(0.2f);\n\t\tpfa.push_back(1.0f);\n\t\tcurve->set_offsets(pfa);\n\t\tPackedColorArray pca;\n\t\tpca.push_back(Color(1.f, 1.f, 1.f, 1.f));\n\t\tpca.push_back(Color(0.f, 0.f, 0.f, 1.f));\n\t\tcurve->set_colors(pca);\n\n\t\tRef<NoiseTexture2D> noise_tex;\n\t\tnoise_tex.instantiate();\n\t\tnoise_tex->set_seamless(true);\n\t\tnoise_tex->set_generate_mipmaps(true);\n\t\tnoise_tex->set_noise(fnoise);\n\t\tnoise_tex->set_color_ramp(curve);\n\t\t_set(\"noise_texture\", noise_tex);\n\t}\n\n\tnotify_property_list_changed();\n}\n\nvoid Terrain3DMaterial::_update_uniforms(const RID &p_material, const uint32_t p_flags) {\n\tIS_DATA_INIT(VOID);\n\tLOG(EXTREME, \"Updating uniforms in shader\");\n\n\tTerrain3DData *data = _terrain->get_data();\n\tPackedInt32Array region_map = data->get_region_map();\n\tLOG(EXTREME, \"region_map.size(): \", region_map.size());\n\tif (region_map.size() != Terrain3DData::REGION_MAP_SIZE * Terrain3DData::REGION_MAP_SIZE) {\n\t\tLOG(ERROR, \"Expected region_map.size() of \", Terrain3DData::REGION_MAP_SIZE * Terrain3DData::REGION_MAP_SIZE);\n\t\treturn;\n\t}\n\tRS->material_set_param(p_material, \"_region_map\", region_map);\n\tRS->material_set_param(p_material, \"_region_map_size\", Terrain3DData::REGION_MAP_SIZE);\n\tif (Terrain3D::debug_level >= EXTREME) {\n\t\tLOG(EXTREME, \"Region map\");\n\t\tfor (int i = 0; i < region_map.size(); i++) {\n\t\t\tif (region_map[i]) {\n\t\t\t\tLOG(EXTREME, \"Region id: \", region_map[i], \" array index: \", i);\n\t\t\t}\n\t\t}\n\t}\n\n\tTypedArray<Vector2i> region_locations = data->get_region_locations();\n\tLOG(EXTREME, \"Region_locations size: \", region_locations.size(), \" \", region_locations);\n\tRS->material_set_param(p_material, \"_region_locations\", region_locations);\n\n\treal_t region_size = real_t(_terrain->get_region_size());\n\tLOG(EXTREME, \"Setting region size in material: \", region_size);\n\tRS->material_set_param(p_material, \"_region_size\", region_size);\n\tRS->material_set_param(p_material, \"_region_texel_size\", 1.0f / region_size);\n\tif (p_flags & REGION_ARRAYS) {\n\t\tRS->material_set_param(p_material, \"_height_maps\", data->get_height_maps_rid());\n\t\tRS->material_set_param(p_material, \"_control_maps\", data->get_control_maps_rid());\n\t\tRS->material_set_param(p_material, \"_color_maps\", data->get_color_maps_rid());\n\t\tLOG(EXTREME, \"Height map RID: \", data->get_height_maps_rid());\n\t\tLOG(EXTREME, \"Control map RID: \", data->get_control_maps_rid());\n\t\tLOG(EXTREME, \"Color map RID: \", data->get_color_maps_rid());\n\t}\n\n\treal_t spacing = _terrain->get_vertex_spacing();\n\tLOG(EXTREME, \"Setting vertex spacing in material: \", spacing);\n\tRS->material_set_param(p_material, \"_vertex_spacing\", spacing);\n\tRS->material_set_param(p_material, \"_vertex_density\", 1.0f / spacing);\n\n\treal_t mesh_size = real_t(_terrain->get_mesh_size());\n\tRS->material_set_param(p_material, \"_mesh_size\", mesh_size);\n\n\treal_t tessellation_level = real_t(_terrain->get_tessellation_level());\n\treal_t subdiv = pow(2.f, tessellation_level);\n\tRS->material_set_param(p_material, \"_subdiv\", subdiv);\n\tRS->material_set_param(p_material, \"_tessellation_level\", tessellation_level);\n\tRS->material_set_param(p_material, \"_displacement_scale\", _displacement_scale);\n\tRS->material_set_param(p_material, \"_displacement_sharpness\", _displacement_sharpness);\n\n\tRef<Terrain3DAssets> asset_list = _terrain->get_assets();\n\tLOG(INFO, \"Updating texture arrays in shader\");\n\tif (asset_list.is_null() || !asset_list->is_initialized()) {\n\t\tLOG(INFO, \"Asset list is not initialized\");\n\t\treturn;\n\t}\n\n\tif (p_flags & TEXTURE_ARRAYS) {\n\t\tRS->material_set_param(p_material, \"_texture_array_albedo\", asset_list->get_albedo_array_rid());\n\t\tRS->material_set_param(p_material, \"_texture_array_normal\", asset_list->get_normal_array_rid());\n\t}\n\tRS->material_set_param(p_material, \"_texture_color_array\", asset_list->get_texture_colors());\n\tRS->material_set_param(p_material, \"_texture_normal_depth_array\", asset_list->get_texture_normal_depths());\n\tRS->material_set_param(p_material, \"_texture_ao_strength_array\", asset_list->get_texture_ao_strengths());\n\tRS->material_set_param(p_material, \"_texture_ao_affect_array\", asset_list->get_texture_ao_light_affects());\n\tRS->material_set_param(p_material, \"_texture_roughness_mod_array\", asset_list->get_texture_roughness_mods());\n\tRS->material_set_param(p_material, \"_texture_uv_scale_array\", asset_list->get_texture_uv_scales());\n\tRS->material_set_param(p_material, \"_texture_detile_array\", asset_list->get_texture_detiles());\n\tRS->material_set_param(p_material, \"_texture_displacement_array\", asset_list->get_texture_displacements());\n\n\t// Enable checkered view if texture_count is 0, disable if not\n\tif (asset_list->get_generated_array_size() == 0) {\n\t\tif (_debug_view_checkered == false) {\n\t\t\tset_show_checkered(true);\n\t\t\tLOG(DEBUG, \"No textures, enabling checkered view\");\n\t\t}\n\t} else {\n\t\tset_show_checkered(false);\n\t\tLOG(DEBUG, \"Texture count >0: \", asset_list->get_generated_array_size(), \", disabling checkered view\");\n\t}\n}\n\nvoid Terrain3DMaterial::_set_shader_parameters(const Dictionary &p_dict) {\n\tSET_IF_DIFF(_shader_params, p_dict);\n\tLOG(INFO, \"Setting shader params dictionary: \", p_dict.size());\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\n// This function serves as the constructor which is initialized by the class Terrain3D.\n// Godot likes to create resource objects at startup, so this prevents it from creating\n// uninitialized materials.\nvoid Terrain3DMaterial::initialize(Terrain3D *p_terrain) {\n\tif (p_terrain) {\n\t\t_terrain = p_terrain;\n\t} else {\n\t\tLOG(ERROR, \"Initialization failed, p_terrain is null\");\n\t\treturn;\n\t}\n\tLOG(INFO, \"Initializing material\");\n\t_preload_shaders();\n\tif (!_material.is_valid()) {\n\t\t_material = RS->material_create();\n\t}\n\tif (!_buffer_material.is_valid()) {\n\t\t_buffer_material = RS->material_create();\n\t}\n\t_shader.instantiate();\n\t_buffer_shader.instantiate();\n\tupdate(FULL_REBUILD);\n}\n\nvoid Terrain3DMaterial::uninitialize() {\n\tLOG(INFO, \"Uninitializing material\");\n\t_terrain = nullptr;\n}\n\nvoid Terrain3DMaterial::destroy() {\n\tLOG(INFO, \"Destroying material\");\n\t_terrain = nullptr;\n\t_shader.unref();\n\t_buffer_shader.unref();\n\t_shader_code.clear();\n\t_active_params.clear();\n\t_shader_params.clear();\n\tif (_material.is_valid()) {\n\t\tRS->free_rid(_material);\n\t\t_material = RID();\n\t}\n\tif (_buffer_material.is_valid()) {\n\t\tRS->free_rid(_buffer_material);\n\t\t_buffer_material = RID();\n\t}\n}\n\nvoid Terrain3DMaterial::update(uint32_t p_flags) {\n\tif (p_flags & FULL_REBUILD) {\n\t\t_update_shader();\n\t}\n\t_update_uniforms(_material, p_flags);\n\tIS_INIT(VOID);\n\tif (_terrain->get_tessellation_level() > 0) {\n\t\t_update_uniforms(_buffer_material, p_flags);\n\t\t// Snap to update buffer\n\t\t_terrain->snap();\n\t}\n}\n\nvoid Terrain3DMaterial::set_displacement_scale(const real_t p_displacement_scale) {\n\tSET_IF_DIFF(_displacement_scale, CLAMP(p_displacement_scale, 0.f, 2.f));\n\tLOG(INFO, \"Setting displacement scale: \", p_displacement_scale);\n\tupdate();\n}\n\nvoid Terrain3DMaterial::set_displacement_sharpness(const real_t p_displacement_sharpness) {\n\tSET_IF_DIFF(_displacement_sharpness, CLAMP(p_displacement_sharpness, 0.f, 1.f));\n\tLOG(INFO, \"Setting displacement sharpness: \", p_displacement_sharpness);\n\tupdate();\n\tif (_terrain) {\n\t\t_terrain->snap();\n\t}\n}\n\nvoid Terrain3DMaterial::set_world_background(const WorldBackground p_background) {\n\tSET_IF_DIFF(_world_background, p_background);\n\tLOG(INFO, \"Enable world background: \", p_background);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_texture_filtering(const TextureFiltering p_filtering) {\n\tSET_IF_DIFF(_texture_filtering, p_filtering);\n\tLOG(INFO, \"Setting texture filtering: \", p_filtering);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_auto_shader_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_auto_shader_enabled, p_enabled);\n\tLOG(INFO, \"Enable auto shader: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_dual_scaling_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_dual_scaling_enabled, p_enabled);\n\tLOG(INFO, \"Enable dual scaling: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_macro_variation_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_macro_variation_enabled, p_enabled);\n\tLOG(INFO, \"Enable macro variation: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_projection_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_projection_enabled, p_enabled);\n\tLOG(INFO, \"Enable projection: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_shader_override_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_shader_override_enabled, p_enabled);\n\tLOG(INFO, \"Enable shader override: \", p_enabled);\n\tif (_shader_override_enabled && _shader_override.is_null()) {\n\t\tLOG(DEBUG, \"Instantiating new _shader_override\");\n\t\t_shader_override.instantiate();\n\t}\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_shader_override(const Ref<Shader> &p_shader) {\n\tSET_IF_DIFF(_shader_override, p_shader);\n\tLOG(INFO, \"Setting override shader\");\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_buffer_shader_override_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_buffer_shader_override_enabled, p_enabled);\n\tLOG(INFO, \"Enable shader override: \", p_enabled);\n\tif (_buffer_shader_override_enabled && _buffer_shader_override.is_null()) {\n\t\tLOG(DEBUG, \"Instantiating new _shader_override\");\n\t\t_buffer_shader_override.instantiate();\n\t}\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_buffer_shader_override(const Ref<Shader> &p_shader) {\n\tSET_IF_DIFF(_buffer_shader_override, p_shader);\n\tLOG(INFO, \"Setting override shader\");\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_shader_param(const StringName &p_name, const Variant &p_value) {\n\tLOG(INFO, \"Setting shader parameter: \", p_name);\n\t_set(p_name, p_value);\n}\n\nVariant Terrain3DMaterial::get_shader_param(const StringName &p_name) const {\n\tLOG(INFO, \"Getting shader parameter: \", p_name);\n\tVariant value;\n\t_get(p_name, value);\n\treturn value;\n}\n\nvoid Terrain3DMaterial::set_output_albedo_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_output_albedo_enabled, p_enabled);\n\tLOG(INFO, \"Enable PBR output albedo: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_output_roughness_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_output_roughness_enabled, p_enabled);\n\tLOG(INFO, \"Enable PBR output roughness: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_output_normal_map_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_output_normal_map_enabled, p_enabled);\n\tLOG(INFO, \"Enable PBR output normal map: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_output_ambient_occlusion_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_output_ambient_occlusion_enabled, p_enabled);\n\tLOG(INFO, \"Enable PBR output ambient occlusion: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_region_grid(const bool p_enabled) {\n\tSET_IF_DIFF(_show_region_grid, p_enabled);\n\tLOG(INFO, \"Enable show_region_grid: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_instancer_grid(const bool p_enabled) {\n\tSET_IF_DIFF(_show_instancer_grid, p_enabled);\n\tLOG(INFO, \"Enable show_instancer_grid: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_vertex_grid(const bool p_enabled) {\n\tSET_IF_DIFF(_show_vertex_grid, p_enabled);\n\tLOG(INFO, \"Enable show_vertex_grid: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_contours(const bool p_enabled) {\n\tSET_IF_DIFF(_show_contours, p_enabled);\n\tLOG(INFO, \"Enable show_contours: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_navigation(const bool p_enabled) {\n\tSET_IF_DIFF(_show_navigation, p_enabled);\n\tLOG(INFO, \"Enable show_navigation: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_checkered(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_checkered, p_enabled);\n\tLOG(INFO, \"Enable set_show_checkered: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_grey(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_grey, p_enabled);\n\tLOG(INFO, \"Enable show_grey: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_heightmap(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_heightmap, p_enabled);\n\tLOG(INFO, \"Enable show_heightmap: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_jaggedness(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_jaggedness, p_enabled);\n\tLOG(INFO, \"Enable show_jaggedness: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_autoshader(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_autoshader, p_enabled);\n\tLOG(INFO, \"Enable show_autoshader: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_control_texture(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_control_texture, p_enabled);\n\tLOG(INFO, \"Enable show_control_texture: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_control_blend(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_control_blend, p_enabled);\n\tLOG(INFO, \"Enable show_control_blend: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_control_angle(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_control_angle, p_enabled);\n\tLOG(INFO, \"Enable show_control_angle: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_control_scale(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_control_scale, p_enabled);\n\tLOG(INFO, \"Enable show_control_scale: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_colormap(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_colormap, p_enabled);\n\tLOG(INFO, \"Enable show_colormap: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_roughmap(const bool p_enabled) {\n\tSET_IF_DIFF(_debug_view_roughmap, p_enabled);\n\tLOG(INFO, \"Enable show_roughmap: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_texture_albedo(const bool p_enabled) {\n\tSET_IF_DIFF(_pbr_view_tex_albedo, p_enabled);\n\tLOG(INFO, \"Enable show_texture_albedo: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_texture_height(const bool p_enabled) {\n\tSET_IF_DIFF(_pbr_view_tex_height, p_enabled);\n\tLOG(INFO, \"Enable show_texture_height: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_texture_normal(const bool p_enabled) {\n\tSET_IF_DIFF(_pbr_view_tex_normal, p_enabled);\n\tLOG(INFO, \"Enable show_texture_normal: \", p_enabled);\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_texture_rough(const bool p_enabled) {\n\tSET_IF_DIFF(_pbr_view_tex_rough, p_enabled);\n\tLOG(INFO, \"Enable show_texture_rough: \", p_enabled);\n\t_update_shader();\n}\nvoid Terrain3DMaterial::set_show_displacement_buffer(const bool p_enabled) {\n\tLOG(INFO, \"Enable show_texture_rough: \", p_enabled);\n\t_debug_view_displacement_buffer = p_enabled;\n\t_update_shader();\n}\n\nvoid Terrain3DMaterial::set_show_texture_ao(const bool p_enabled) {\n\tSET_IF_DIFF(_pbr_view_tex_ao, p_enabled);\n\tLOG(INFO, \"Enable show_texture_ao: \", p_enabled);\n\t_update_shader();\n}\n\nError Terrain3DMaterial::save(const String &p_path) {\n\tif (p_path.is_empty() && get_path().is_empty()) {\n\t\treturn ERR_FILE_NOT_FOUND;\n\t}\n\tif (!p_path.is_empty()) {\n\t\tLOG(DEBUG, \"Setting file path to \", p_path);\n\t\ttake_over_path(p_path);\n\t}\n\n\tLOG(DEBUG, \"Generating parameter list from shaders\");\n\t// Get shader parameters from default shader (eg world_noise)\n\tArray param_list;\n\tparam_list = RS->get_shader_parameter_list(get_shader_rid());\n\t// Get shader parameters from custom shader if present\n\tif (_shader_override.is_valid()) {\n\t\tparam_list.append_array(_shader_override->get_shader_uniform_list(true));\n\t}\n\tif (_buffer_shader_override.is_valid()) {\n\t\t// Get shader parameters from custom buffer shader\n\t\tparam_list.append_array(_buffer_shader_override->get_shader_uniform_list(true));\n\t} else {\n\t\tif (_terrain && _terrain->get_tessellation_level() > 0) {\n\t\t\t// Get shader parameters from default buffer shader\n\t\t\tparam_list.append_array(RS->get_shader_parameter_list(get_buffer_shader_rid()));\n\t\t}\n\t}\n\n\t// Remove saved shader params that don't exist in either shader\n\tArray keys = _shader_params.keys();\n\tfor (const StringName &name : keys) {\n\t\tbool has = false;\n\t\tfor (const Dictionary &dict : param_list) {\n\t\t\tStringName dname = dict[\"name\"];\n\t\t\tif (name == dname) {\n\t\t\t\thas = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!has) {\n\t\t\tLOG(DEBUG, \"'\", name, \"' not found in shader parameters. Removing from dictionary.\");\n\t\t\t_shader_params.erase(name);\n\t\t}\n\t}\n\n\t// Save to external resource file if specified\n\tError err = OK;\n\tString path = get_path();\n\tif (path.get_extension() == \"tres\" || path.get_extension() == \"res\") {\n\t\tLOG(DEBUG, \"Attempting to save external file: \" + path);\n\t\terr = ResourceSaver::get_singleton()->save(this, path, ResourceSaver::FLAG_COMPRESS);\n\t\tif (err == OK) {\n\t\t\tLOG(INFO, \"File saved successfully: \", path);\n\t\t} else {\n\t\t\tLOG(ERROR, \"Cannot save file: \", path, \". Error code: \", ERROR, \". Look up @GlobalScope Error enum in the Godot docs\");\n\t\t}\n\t}\n\treturn err;\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\n// Add shader uniforms to properties. Hides uniforms that begin with _\nvoid Terrain3DMaterial::_get_property_list(List<PropertyInfo> *p_list) const {\n\tResource::_get_property_list(p_list);\n\tIS_INIT(VOID);\n\tArray param_list;\n\tif (_shader_override_enabled && _shader_override.is_valid()) {\n\t\t// Get shader parameters from custom shader\n\t\tparam_list = _shader_override->get_shader_uniform_list(true);\n\t} else {\n\t\t// Get shader parameters from default shader (eg world_noise)\n\t\tparam_list = RS->get_shader_parameter_list(get_shader_rid());\n\t}\n\tint buffer_param = param_list.size();\n\tif (_buffer_shader_override_enabled && _buffer_shader_override.is_valid()) {\n\t\t// Get shader parameters from custom shader\n\t\tparam_list.append_array(_buffer_shader_override->get_shader_uniform_list(true));\n\t} else {\n\t\tif (_terrain->get_tessellation_level() > 0) {\n\t\t\t// Get shader parameters from default shader (eg world_noise)\n\t\t\tparam_list.append_array(RS->get_shader_parameter_list(get_buffer_shader_rid()));\n\t\t}\n\t}\n\n\tTypedArray<StringName> new_active_params;\n\tDictionary grouped_params;\n\tStringName current_group = StringName(\"shader_uniforms.general\");\n\tgrouped_params[current_group] = Array();\n\tfor (int i = 0; i < param_list.size(); i++) {\n\t\tDictionary dict = param_list[i];\n\t\tStringName name = dict[\"name\"];\n\n\t\t// An empty name indicates a group being closed, reset to the \"general\" group.\n\t\tif (name.is_empty() && i < buffer_param) {\n\t\t\tcurrent_group = StringName(\"shader_uniforms.general\");\n\t\t}\n\n\t\t// Filter out private uniforms that start with _ and nulls\n\t\tif (!name.begins_with(\"_\") && !name.is_empty()) {\n\t\t\tuint64_t use = dict[\"usage\"];\n\t\t\tif (use == PROPERTY_USAGE_GROUP) {\n\t\t\t\tPackedStringArray split_name = name.split(\"::\");\n\t\t\t\tdict[\"name\"] = split_name[MAX(split_name.size() - 1, 0)].capitalize();\n\t\t\t\t// Ensure sub groups are batched with their parent group\n\t\t\t\tcurrent_group = split_name[0].capitalize();\n\t\t\t\tdict[\"usage\"] = name.contains(\"::\") ? PROPERTY_USAGE_SUBGROUP : PROPERTY_USAGE_GROUP;\n\t\t\t} else {\n\t\t\t\t// Filter out duplicate non-groups entries from displacement buffer shader\n\t\t\t\tif (new_active_params.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tdict[\"usage\"] = PROPERTY_USAGE_EDITOR;\n\t\t\t}\n\t\t\t// Filter out extraneous parameters from the inspector if they are not present in the terrain shader.\n\t\t\tif (i >= buffer_param && (!new_active_params.has(name) && use != PROPERTY_USAGE_GROUP) && !current_group.contains(\"Displacement\")) {\n\t\t\t\tLOG(INFO, \"Displacement buffer has active parameter: \", name, \" not present in terrain shader.\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Write dict to grouped arrays to ensure property list groups are populated contigiously.\n\t\t\tArray group;\n\t\t\tif (grouped_params.has(current_group)) {\n\t\t\t\tgroup = grouped_params[current_group];\n\t\t\t} else {\n\t\t\t\tgrouped_params[current_group] = Array();\n\t\t\t\tgroup = grouped_params[current_group];\n\t\t\t}\n\t\t\tgroup.push_back(dict);\n\t\t\tgrouped_params[current_group] = group;\n\n\t\t\t// Populate list of public parameters for current shader\n\t\t\tnew_active_params.push_back(name);\n\n\t\t\t// Store this param in a dictionary that is saved in the resource file\n\t\t\t// Initially set with default value\n\t\t\t// Also acts as a cache for _get\n\t\t\t// Property usage above set to EDITOR so it won't be redundantly saved,\n\t\t\t// which won't get loaded since there is no bound property.\n\t\t\tif (!_shader_params.has(name)) {\n\t\t\t\t_property_get_revert(name, _shader_params[name]);\n\t\t\t}\n\t\t}\n\t}\n\t_active_params = new_active_params;\n\n\t// Populate Godot's property list\n\tArray keys = grouped_params.keys();\n\tfor (int i = 0; i < keys.size(); i++) {\n\t\tStringName key = keys[i];\n\t\tArray group = grouped_params[key];\n\t\tfor (int k = 0; k < group.size(); k++) {\n\t\t\tDictionary dict = group[k];\n\t\t\tPropertyInfo pi;\n\t\t\tpi.class_name = dict[\"class_name\"];\n\t\t\tpi.name = dict[\"name\"];\n\t\t\tpi.type = Variant::Type(int(dict[\"type\"]));\n\t\t\tpi.hint = dict[\"hint\"];\n\t\t\tpi.hint_string = dict[\"hint_string\"];\n\t\t\tpi.usage = dict[\"usage\"];\n\t\t\tp_list->push_back(pi);\n\t\t}\n\t}\n\treturn;\n}\n\n// Flag uniforms with non-default values\n// This is called 10x more than the others, so be efficient\nbool Terrain3DMaterial::_property_can_revert(const StringName &p_name) const {\n\tIS_INIT_COND(!_active_params.has(p_name), Resource::_property_can_revert(p_name));\n\tVariant default_value = RS->shader_get_parameter_default(get_shader_rid(), p_name);\n\tVariant current_value = RS->material_get_param(_material, p_name);\n\treturn default_value != current_value;\n}\n\n// Provide uniform default values in r_property\nbool Terrain3DMaterial::_property_get_revert(const StringName &p_name, Variant &r_property) const {\n\tIS_INIT_COND(!_active_params.has(p_name), Resource::_property_get_revert(p_name, r_property));\n\tVariant prop = RS->shader_get_parameter_default(get_shader_rid(), p_name);\n\t// If the default is nil, check the buffer shader for the default value.\n\tif (prop.get_type() == Variant::NIL) {\n\t\tprop = RS->shader_get_parameter_default(get_buffer_shader_rid(), p_name);\n\t}\n\tr_property = prop;\n\treturn true;\n}\n\nbool Terrain3DMaterial::_set(const StringName &p_name, const Variant &p_property) {\n\tIS_INIT_COND(!_active_params.has(p_name), Resource::_set(p_name, p_property));\n\tif (p_property.get_type() == Variant::NIL) {\n\t\tRS->material_set_param(_material, p_name, Variant());\n\t\t_shader_params.erase(p_name);\n\t\treturn true;\n\t}\n\n\t// If value is an object, assume a Texture. RS only wants RIDs, but\n\t// Inspector wants the object, so set the RID and save the latter for _get\n\tif (p_property.get_type() == Variant::OBJECT) {\n\t\tRef<Texture> tex = p_property;\n\t\tif (tex.is_valid()) {\n\t\t\t_shader_params[p_name] = tex;\n\t\t\tRS->material_set_param(_material, p_name, tex->get_rid());\n\t\t\tRS->material_set_param(_buffer_material, p_name, tex->get_rid());\n\t\t} else {\n\t\t\tRS->material_set_param(_material, p_name, Variant());\n\t\t\tRS->material_set_param(_buffer_material, p_name, Variant());\n\t\t}\n\t} else {\n\t\t_shader_params[p_name] = p_property;\n\t\tRS->material_set_param(_material, p_name, p_property);\n\t\tRS->material_set_param(_buffer_material, p_name, p_property);\n\t}\n\treturn true;\n}\n\n// This is called 200x more than the others, every second the material is open in the\n// inspector, so be efficient\nbool Terrain3DMaterial::_get(const StringName &p_name, Variant &r_property) const {\n\tIS_INIT_COND(!_active_params.has(p_name), Resource::_get(p_name, r_property));\n\n\tr_property = RS->material_get_param(_material, p_name);\n\t// Material server only has RIDs, but inspector needs objects for things like Textures\n\t// So if its an RID, return the object\n\tif (r_property.get_type() == Variant::RID && _shader_params.has(p_name)) {\n\t\tr_property = _shader_params[p_name];\n\t}\n\treturn true;\n}\n\nvoid Terrain3DMaterial::_bind_methods() {\n\tBIND_ENUM_CONSTANT(NONE);\n\tBIND_ENUM_CONSTANT(FLAT);\n\tBIND_ENUM_CONSTANT(NOISE);\n\tBIND_ENUM_CONSTANT(LINEAR_ANISOTROPIC);\n\tBIND_ENUM_CONSTANT(LINEAR);\n\tBIND_ENUM_CONSTANT(NEAREST_ANISOTROPIC);\n\tBIND_ENUM_CONSTANT(NEAREST);\n\tBIND_ENUM_CONSTANT(UNIFORMS_ONLY);\n\tBIND_ENUM_CONSTANT(TEXTURE_ARRAYS);\n\tBIND_ENUM_CONSTANT(REGION_ARRAYS);\n\tBIND_ENUM_CONSTANT(UPDATE_ARRAYS);\n\tBIND_ENUM_CONSTANT(FULL_REBUILD);\n\n\t// Private\n\tClassDB::bind_method(D_METHOD(\"_set_shader_parameters\", \"dict\"), &Terrain3DMaterial::_set_shader_parameters);\n\tClassDB::bind_method(D_METHOD(\"_get_shader_parameters\"), &Terrain3DMaterial::_get_shader_parameters);\n\tADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, \"_shader_parameters\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_STORAGE), \"_set_shader_parameters\", \"_get_shader_parameters\");\n\n\t// Public\n\tClassDB::bind_method(D_METHOD(\"update\", \"flags\"), &Terrain3DMaterial::update, DEFVAL(Terrain3DMaterial::UNIFORMS_ONLY));\n\tClassDB::bind_method(D_METHOD(\"get_material_rid\"), &Terrain3DMaterial::get_material_rid);\n\tClassDB::bind_method(D_METHOD(\"get_shader_rid\"), &Terrain3DMaterial::get_shader_rid);\n\tClassDB::bind_method(D_METHOD(\"get_buffer_material_rid\"), &Terrain3DMaterial::get_buffer_material_rid);\n\tClassDB::bind_method(D_METHOD(\"get_buffer_shader_rid\"), &Terrain3DMaterial::get_buffer_shader_rid);\n\n\tClassDB::bind_method(D_METHOD(\"set_world_background\", \"background\"), &Terrain3DMaterial::set_world_background);\n\tClassDB::bind_method(D_METHOD(\"get_world_background\"), &Terrain3DMaterial::get_world_background);\n\tClassDB::bind_method(D_METHOD(\"set_texture_filtering\", \"filtering\"), &Terrain3DMaterial::set_texture_filtering);\n\tClassDB::bind_method(D_METHOD(\"get_texture_filtering\"), &Terrain3DMaterial::get_texture_filtering);\n\tClassDB::bind_method(D_METHOD(\"set_auto_shader_enabled\", \"enabled\"), &Terrain3DMaterial::set_auto_shader_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_auto_shader_enabled\"), &Terrain3DMaterial::get_auto_shader_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_dual_scaling_enabled\", \"enabled\"), &Terrain3DMaterial::set_dual_scaling_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_dual_scaling_enabled\"), &Terrain3DMaterial::get_dual_scaling_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_macro_variation_enabled\", \"enabled\"), &Terrain3DMaterial::set_macro_variation_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_macro_variation_enabled\"), &Terrain3DMaterial::get_macro_variation_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_projection_enabled\", \"enabled\"), &Terrain3DMaterial::set_projection_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_projection_enabled\"), &Terrain3DMaterial::get_projection_enabled);\n\n\tClassDB::bind_method(D_METHOD(\"set_shader_override_enabled\", \"enabled\"), &Terrain3DMaterial::set_shader_override_enabled);\n\tClassDB::bind_method(D_METHOD(\"is_shader_override_enabled\"), &Terrain3DMaterial::is_shader_override_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_shader_override\", \"shader\"), &Terrain3DMaterial::set_shader_override);\n\tClassDB::bind_method(D_METHOD(\"get_shader_override\"), &Terrain3DMaterial::get_shader_override);\n\n\tClassDB::bind_method(D_METHOD(\"set_buffer_shader_override_enabled\", \"enabled\"), &Terrain3DMaterial::set_buffer_shader_override_enabled);\n\tClassDB::bind_method(D_METHOD(\"is_buffer_shader_override_enabled\"), &Terrain3DMaterial::is_buffer_shader_override_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_buffer_shader_override\", \"shader\"), &Terrain3DMaterial::set_buffer_shader_override);\n\tClassDB::bind_method(D_METHOD(\"get_buffer_shader_override\"), &Terrain3DMaterial::get_buffer_shader_override);\n\tClassDB::bind_method(D_METHOD(\"set_displacement_scale\", \"scale\"), &Terrain3DMaterial::set_displacement_scale);\n\tClassDB::bind_method(D_METHOD(\"get_displacement_scale\"), &Terrain3DMaterial::get_displacement_scale);\n\tClassDB::bind_method(D_METHOD(\"set_displacement_sharpness\", \"sharpness\"), &Terrain3DMaterial::set_displacement_sharpness);\n\tClassDB::bind_method(D_METHOD(\"get_displacement_sharpness\"), &Terrain3DMaterial::get_displacement_sharpness);\n\n\tClassDB::bind_method(D_METHOD(\"set_shader_param\", \"name\", \"value\"), &Terrain3DMaterial::set_shader_param);\n\tClassDB::bind_method(D_METHOD(\"get_shader_param\", \"name\"), &Terrain3DMaterial::get_shader_param);\n\n\t// PBR output\n\tClassDB::bind_method(D_METHOD(\"set_output_albedo_enabled\", \"enabled\"), &Terrain3DMaterial::set_output_albedo_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_output_albedo_enabled\"), &Terrain3DMaterial::get_output_albedo_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_output_roughness_enabled\", \"enabled\"), &Terrain3DMaterial::set_output_roughness_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_output_roughness_enabled\"), &Terrain3DMaterial::get_output_roughness_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_output_normal_map_enabled\", \"enabled\"), &Terrain3DMaterial::set_output_normal_map_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_output_normal_map_enabled\"), &Terrain3DMaterial::get_output_normal_map_enabled);\n\tClassDB::bind_method(D_METHOD(\"set_output_ambient_occlusion_enabled\", \"enabled\"), &Terrain3DMaterial::set_output_ambient_occlusion_enabled);\n\tClassDB::bind_method(D_METHOD(\"get_output_ambient_occlusion_enabled\"), &Terrain3DMaterial::get_output_ambient_occlusion_enabled);\n\n\t// Overlays\n\tClassDB::bind_method(D_METHOD(\"set_show_region_grid\", \"enabled\"), &Terrain3DMaterial::set_show_region_grid);\n\tClassDB::bind_method(D_METHOD(\"get_show_region_grid\"), &Terrain3DMaterial::get_show_region_grid);\n\tClassDB::bind_method(D_METHOD(\"set_show_instancer_grid\", \"enabled\"), &Terrain3DMaterial::set_show_instancer_grid);\n\tClassDB::bind_method(D_METHOD(\"get_show_instancer_grid\"), &Terrain3DMaterial::get_show_instancer_grid);\n\tClassDB::bind_method(D_METHOD(\"set_show_vertex_grid\", \"enabled\"), &Terrain3DMaterial::set_show_vertex_grid);\n\tClassDB::bind_method(D_METHOD(\"get_show_vertex_grid\"), &Terrain3DMaterial::get_show_vertex_grid);\n\tClassDB::bind_method(D_METHOD(\"set_show_contours\", \"enabled\"), &Terrain3DMaterial::set_show_contours);\n\tClassDB::bind_method(D_METHOD(\"get_show_contours\"), &Terrain3DMaterial::get_show_contours);\n\tClassDB::bind_method(D_METHOD(\"set_show_navigation\", \"enabled\"), &Terrain3DMaterial::set_show_navigation);\n\tClassDB::bind_method(D_METHOD(\"get_show_navigation\"), &Terrain3DMaterial::get_show_navigation);\n\n\t// Debug Views\n\tClassDB::bind_method(D_METHOD(\"set_show_checkered\", \"enabled\"), &Terrain3DMaterial::set_show_checkered);\n\tClassDB::bind_method(D_METHOD(\"get_show_checkered\"), &Terrain3DMaterial::get_show_checkered);\n\tClassDB::bind_method(D_METHOD(\"set_show_grey\", \"enabled\"), &Terrain3DMaterial::set_show_grey);\n\tClassDB::bind_method(D_METHOD(\"get_show_grey\"), &Terrain3DMaterial::get_show_grey);\n\tClassDB::bind_method(D_METHOD(\"set_show_heightmap\", \"enabled\"), &Terrain3DMaterial::set_show_heightmap);\n\tClassDB::bind_method(D_METHOD(\"get_show_heightmap\"), &Terrain3DMaterial::get_show_heightmap);\n\tClassDB::bind_method(D_METHOD(\"set_show_jaggedness\", \"enabled\"), &Terrain3DMaterial::set_show_jaggedness);\n\tClassDB::bind_method(D_METHOD(\"get_show_jaggedness\"), &Terrain3DMaterial::get_show_jaggedness);\n\tClassDB::bind_method(D_METHOD(\"set_show_autoshader\", \"enabled\"), &Terrain3DMaterial::set_show_autoshader);\n\tClassDB::bind_method(D_METHOD(\"get_show_autoshader\"), &Terrain3DMaterial::get_show_autoshader);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_texture\", \"enabled\"), &Terrain3DMaterial::set_show_control_texture);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_texture\"), &Terrain3DMaterial::get_show_control_texture);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_blend\", \"enabled\"), &Terrain3DMaterial::set_show_control_blend);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_blend\"), &Terrain3DMaterial::get_show_control_blend);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_angle\", \"enabled\"), &Terrain3DMaterial::set_show_control_angle);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_angle\"), &Terrain3DMaterial::get_show_control_angle);\n\tClassDB::bind_method(D_METHOD(\"set_show_control_scale\", \"enabled\"), &Terrain3DMaterial::set_show_control_scale);\n\tClassDB::bind_method(D_METHOD(\"get_show_control_scale\"), &Terrain3DMaterial::get_show_control_scale);\n\tClassDB::bind_method(D_METHOD(\"set_show_colormap\", \"enabled\"), &Terrain3DMaterial::set_show_colormap);\n\tClassDB::bind_method(D_METHOD(\"get_show_colormap\"), &Terrain3DMaterial::get_show_colormap);\n\tClassDB::bind_method(D_METHOD(\"set_show_roughmap\", \"enabled\"), &Terrain3DMaterial::set_show_roughmap);\n\tClassDB::bind_method(D_METHOD(\"get_show_roughmap\"), &Terrain3DMaterial::get_show_roughmap);\n\n\t// PBR Views\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_albedo\", \"enabled\"), &Terrain3DMaterial::set_show_texture_albedo);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_albedo\"), &Terrain3DMaterial::get_show_texture_albedo);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_height\", \"enabled\"), &Terrain3DMaterial::set_show_texture_height);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_height\"), &Terrain3DMaterial::get_show_texture_height);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_normal\", \"enabled\"), &Terrain3DMaterial::set_show_texture_normal);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_normal\"), &Terrain3DMaterial::get_show_texture_normal);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_ao\", \"enabled\"), &Terrain3DMaterial::set_show_texture_ao);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_ao\"), &Terrain3DMaterial::get_show_texture_ao);\n\tClassDB::bind_method(D_METHOD(\"set_show_texture_rough\", \"enabled\"), &Terrain3DMaterial::set_show_texture_rough);\n\tClassDB::bind_method(D_METHOD(\"get_show_texture_rough\"), &Terrain3DMaterial::get_show_texture_rough);\n\tClassDB::bind_method(D_METHOD(\"set_show_displacement_buffer\", \"enabled\"), &Terrain3DMaterial::set_show_displacement_buffer);\n\tClassDB::bind_method(D_METHOD(\"get_show_displacement_buffer\"), &Terrain3DMaterial::get_show_displacement_buffer);\n\n\tClassDB::bind_method(D_METHOD(\"save\", \"path\"), &Terrain3DMaterial::save, DEFVAL(\"\"));\n\n\t// These must be different from the names of uniform groups\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"world_background\", PROPERTY_HINT_ENUM, \"None,Flat,Noise\"), \"set_world_background\", \"get_world_background\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"texture_filtering\", PROPERTY_HINT_ENUM, \"Linear Anisotropic,Linear,Nearest Anisotropic,Nearest\"), \"set_texture_filtering\", \"get_texture_filtering\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"auto_shader_enabled\"), \"set_auto_shader_enabled\", \"get_auto_shader_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"dual_scaling_enabled\"), \"set_dual_scaling_enabled\", \"get_dual_scaling_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"macro_variation_enabled\"), \"set_macro_variation_enabled\", \"get_macro_variation_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"projection_enabled\"), \"set_projection_enabled\", \"get_projection_enabled\");\n\n\tADD_GROUP(\"PBR Output\", \"output_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"output_albedo\"), \"set_output_albedo_enabled\", \"get_output_albedo_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"output_roughness\"), \"set_output_roughness_enabled\", \"get_output_roughness_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"output_normal_map\"), \"set_output_normal_map_enabled\", \"get_output_normal_map_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"output_ambient_occlusion\"), \"set_output_ambient_occlusion_enabled\", \"get_output_ambient_occlusion_enabled\");\n\n\tADD_GROUP(\"Custom Shader\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"shader_override_enabled\"), \"set_shader_override_enabled\", \"is_shader_override_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"shader_override\", PROPERTY_HINT_RESOURCE_TYPE, \"Shader\"), \"set_shader_override\", \"get_shader_override\");\n\n\t// Hidden in Material, aliased in Terrain3D\n\t//ADD_GROUP(\"Overlays\", \"show_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_region_grid\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_region_grid\", \"get_show_region_grid\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_instancer_grid\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_instancer_grid\", \"get_show_instancer_grid\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_vertex_grid\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_vertex_grid\", \"get_show_vertex_grid\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_contours\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_contours\", \"get_show_contours\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_navigation\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_navigation\", \"get_show_navigation\");\n\n\t// Hidden in Material, aliased in Terrain3D\n\t// Displacement settings\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"displacement_scale\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_displacement_scale\", \"get_displacement_scale\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"displacement_sharpness\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_displacement_sharpness\", \"get_displacement_sharpness\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"buffer_shader_override_enabled\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_buffer_shader_override_enabled\", \"is_buffer_shader_override_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"buffer_shader_override\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_buffer_shader_override\", \"get_buffer_shader_override\");\n\t//ADD_GROUP(\"Debug Views\", \"show_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_checkered\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_checkered\", \"get_show_checkered\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_grey\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_grey\", \"get_show_grey\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_heightmap\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_heightmap\", \"get_show_heightmap\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_jaggedness\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_jaggedness\", \"get_show_jaggedness\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_autoshader\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_autoshader\", \"get_show_autoshader\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_texture\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_control_texture\", \"get_show_control_texture\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_blend\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_control_blend\", \"get_show_control_blend\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_angle\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_control_angle\", \"get_show_control_angle\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_control_scale\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_control_scale\", \"get_show_control_scale\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_colormap\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_colormap\", \"get_show_colormap\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_roughmap\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_roughmap\", \"get_show_roughmap\");\n\n\t// Hidden in Material, aliased in Terrain3D\n\t//ADD_SUBGROUP(\"PBR Views\", \"show_\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_albedo\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_texture_albedo\", \"get_show_texture_albedo\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_height\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_texture_height\", \"get_show_texture_height\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_normal\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_texture_normal\", \"get_show_texture_normal\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_ao\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_texture_ao\", \"get_show_texture_ao\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_texture_rough\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_texture_rough\", \"get_show_texture_rough\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"show_displacement_buffer\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NO_EDITOR), \"set_show_displacement_buffer\", \"get_show_displacement_buffer\");\n}\n"
  },
  {
    "path": "src/terrain_3d_material.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_MATERIAL_CLASS_H\n#define TERRAIN3D_MATERIAL_CLASS_H\n\n#include <godot_cpp/classes/shader.hpp>\n\n#include \"constants.h\"\n#include \"generated_texture.h\"\n\nclass Terrain3D;\n\nclass Terrain3DMaterial : public Resource {\n\tGDCLASS(Terrain3DMaterial, Resource);\n\tCLASS_NAME();\n\npublic: // Constants\n\tenum WorldBackground {\n\t\tNONE,\n\t\tFLAT,\n\t\tNOISE,\n\t};\n\n\tenum TextureFiltering {\n\t\tLINEAR_ANISOTROPIC,\n\t\tLINEAR,\n\t\tNEAREST_ANISOTROPIC,\n\t\tNEAREST,\n\t};\n\n\tenum UpdateFlags {\n\t\tUNIFORMS_ONLY = 0,\n\t\tTEXTURE_ARRAYS = 1 << 0,\n\t\tREGION_ARRAYS = 1 << 1,\n\t\tUPDATE_ARRAYS = TEXTURE_ARRAYS | REGION_ARRAYS,\n\t\tFULL_REBUILD = (1 << 2) | UPDATE_ARRAYS,\n\t};\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\n\tRID _material;\n\tRef<Shader> _shader; // Active shader\n\tDictionary _shader_code; // All loaded shader and INSERT code\n\tbool _shader_override_enabled = false;\n\tRef<Shader> _shader_override; // User's shader we copy code from\n\tmutable TypedArray<StringName> _active_params; // All shader params in the current shader\n\tmutable Dictionary _shader_params; // Public shader params saved to disk\n\n\tRID _buffer_material;\n\tRef<Shader> _buffer_shader; // Active buffer shader\n\tbool _buffer_shader_override_enabled = false;\n\tRef<Shader> _buffer_shader_override; // User's shader we copy code from\n\treal_t _displacement_scale = 1.0f;\n\treal_t _displacement_sharpness = 0.5f;\n\n\t// Material Features\n\tWorldBackground _world_background = FLAT;\n\tTextureFiltering _texture_filtering = LINEAR_ANISOTROPIC;\n\tbool _dual_scaling_enabled = false;\n\tbool _auto_shader_enabled = false;\n\tbool _macro_variation_enabled = false;\n\tbool _projection_enabled = false;\n\n\t// PBR Outputs\n\tbool _output_albedo_enabled = true;\n\tbool _output_roughness_enabled = true;\n\tbool _output_normal_map_enabled = true;\n\tbool _output_ambient_occlusion_enabled = true;\n\n\t// Overlays\n\tbool _show_region_grid = false;\n\tbool _show_instancer_grid = false;\n\tbool _show_vertex_grid = false;\n\tbool _show_contours = false;\n\tbool _show_navigation = false;\n\n\t// Debug Views\n\tbool _debug_view_checkered = false;\n\tbool _debug_view_grey = false;\n\tbool _debug_view_heightmap = false;\n\tbool _debug_view_jaggedness = false;\n\tbool _debug_view_autoshader = false;\n\tbool _debug_view_control_texture = false;\n\tbool _debug_view_control_blend = false;\n\tbool _debug_view_control_angle = false;\n\tbool _debug_view_control_scale = false;\n\tbool _debug_view_holes = false;\n\tbool _debug_view_colormap = false;\n\tbool _debug_view_roughmap = false;\n\tbool _debug_view_displacement_buffer = false;\n\n\t// PBR Views\n\tbool _pbr_view_tex_albedo = false;\n\tbool _pbr_view_tex_height = false;\n\tbool _pbr_view_tex_normal = false;\n\tbool _pbr_view_tex_ao = false;\n\tbool _pbr_view_tex_rough = false;\n\n\t// Functions\n\tvoid _preload_shaders();\n\tvoid _parse_shader(const String &p_shader, const String &p_name);\n\tString _apply_inserts(const String &p_shader, const Array &p_excludes = Array()) const;\n\tString _generate_shader_code() const;\n\tString _generate_buffer_shader_code() const;\n\tString _strip_comments(const String &p_shader) const;\n\tString _inject_editor_code(const String &p_shader) const;\n\tvoid _update_shader();\n\tvoid _update_uniforms(const RID &p_material, const uint32_t p_update = UNIFORMS_ONLY);\n\tvoid _set_shader_parameters(const Dictionary &p_dict);\n\tDictionary _get_shader_parameters() const { return _shader_params; }\n\npublic:\n\tTerrain3DMaterial() {}\n\t~Terrain3DMaterial() { destroy(); }\n\tvoid initialize(Terrain3D *p_terrain);\n\tbool is_initialized() { return _terrain != nullptr; }\n\tvoid uninitialize();\n\tvoid destroy();\n\n\tvoid update(const uint32_t p_flags = UNIFORMS_ONLY);\n\tRID get_material_rid() const { return _material; }\n\tRID get_shader_rid() const { return _shader.is_valid() ? _shader->get_rid() : RID(); }\n\n\tRID get_buffer_material_rid() const { return _buffer_material; }\n\tRID get_buffer_shader_rid() const { return _buffer_shader.is_valid() ? _buffer_shader->get_rid() : RID(); }\n\n\t// Material settings\n\tvoid set_displacement_scale(const real_t p_displacement_scale);\n\treal_t get_displacement_scale() const { return _displacement_scale; }\n\tvoid set_displacement_sharpness(const real_t p_displacement_sharpness);\n\treal_t get_displacement_sharpness() const { return _displacement_sharpness; }\n\tvoid set_world_background(const WorldBackground p_background);\n\tWorldBackground get_world_background() const { return _world_background; }\n\tvoid set_texture_filtering(const TextureFiltering p_filtering);\n\tTextureFiltering get_texture_filtering() const { return _texture_filtering; }\n\tvoid set_auto_shader_enabled(const bool p_enabled);\n\tbool get_auto_shader_enabled() const { return _auto_shader_enabled; }\n\tvoid set_dual_scaling_enabled(const bool p_enabled);\n\tbool get_dual_scaling_enabled() const { return _dual_scaling_enabled; }\n\tvoid set_macro_variation_enabled(const bool p_enabled);\n\tbool get_macro_variation_enabled() const { return _macro_variation_enabled; }\n\tvoid set_projection_enabled(const bool p_enabled);\n\tbool get_projection_enabled() const { return _projection_enabled; }\n\n\tvoid set_shader_override_enabled(const bool p_enabled);\n\tbool is_shader_override_enabled() const { return _shader_override_enabled; }\n\tvoid set_shader_override(const Ref<Shader> &p_shader);\n\tRef<Shader> get_shader_override() const { return _shader_override; }\n\n\tvoid set_buffer_shader_override_enabled(const bool p_enabled);\n\tbool is_buffer_shader_override_enabled() const { return _buffer_shader_override_enabled; }\n\tvoid set_buffer_shader_override(const Ref<Shader> &p_shader);\n\tRef<Shader> get_buffer_shader_override() const { return _buffer_shader_override; }\n\n\tvoid set_shader_param(const StringName &p_name, const Variant &p_value);\n\tVariant get_shader_param(const StringName &p_name) const;\n\n\t// PBR outputs\n\tvoid set_output_albedo_enabled(const bool p_enabled);\n\tbool get_output_albedo_enabled() const { return _output_albedo_enabled; }\n\n\tvoid set_output_roughness_enabled(const bool p_enabled);\n\tbool get_output_roughness_enabled() const { return _output_roughness_enabled; }\n\n\tvoid set_output_normal_map_enabled(const bool p_enabled);\n\tbool get_output_normal_map_enabled() const { return _output_normal_map_enabled; }\n\n\tvoid set_output_ambient_occlusion_enabled(const bool p_enabled);\n\tbool get_output_ambient_occlusion_enabled() const { return _output_ambient_occlusion_enabled; }\n\n\t// Overlays\n\tvoid set_show_region_grid(const bool p_enabled);\n\tbool get_show_region_grid() const { return _show_region_grid; }\n\tvoid set_show_instancer_grid(const bool p_enabled);\n\tbool get_show_instancer_grid() const { return _show_instancer_grid; }\n\tvoid set_show_vertex_grid(const bool p_enabled);\n\tbool get_show_vertex_grid() const { return _show_vertex_grid; }\n\tvoid set_show_contours(const bool p_enabled);\n\tbool get_show_contours() const { return _show_contours; }\n\tvoid set_show_navigation(const bool p_enabled);\n\tbool get_show_navigation() const { return _show_navigation; }\n\n\t// Debug views\n\tvoid set_show_checkered(const bool p_enabled);\n\tbool get_show_checkered() const { return _debug_view_checkered; }\n\tvoid set_show_grey(const bool p_enabled);\n\tbool get_show_grey() const { return _debug_view_grey; }\n\tvoid set_show_heightmap(const bool p_enabled);\n\tbool get_show_heightmap() const { return _debug_view_heightmap; }\n\tvoid set_show_jaggedness(const bool p_enabled);\n\tbool get_show_jaggedness() const { return _debug_view_jaggedness; }\n\tvoid set_show_autoshader(const bool p_enabled);\n\tbool get_show_autoshader() const { return _debug_view_autoshader; }\n\tvoid set_show_control_texture(const bool p_enabled);\n\tbool get_show_control_texture() const { return _debug_view_control_texture; }\n\tvoid set_show_control_blend(const bool p_enabled);\n\tbool get_show_control_blend() const { return _debug_view_control_blend; }\n\tvoid set_show_control_angle(const bool p_enabled);\n\tbool get_show_control_angle() const { return _debug_view_control_angle; }\n\tvoid set_show_control_scale(const bool p_enabled);\n\tbool get_show_control_scale() const { return _debug_view_control_scale; }\n\tvoid set_show_colormap(const bool p_enabled);\n\tbool get_show_colormap() const { return _debug_view_colormap; }\n\tvoid set_show_roughmap(const bool p_enabled);\n\tbool get_show_roughmap() const { return _debug_view_roughmap; }\n\tvoid set_show_displacement_buffer(const bool p_enabled);\n\tbool get_show_displacement_buffer() const { return _debug_view_displacement_buffer; }\n\n\t// PBR Views\n\tvoid set_show_texture_albedo(const bool p_enabled);\n\tbool get_show_texture_albedo() const { return _pbr_view_tex_albedo; }\n\tvoid set_show_texture_height(const bool p_enabled);\n\tbool get_show_texture_height() const { return _pbr_view_tex_height; }\n\tvoid set_show_texture_normal(const bool p_enabled);\n\tbool get_show_texture_normal() const { return _pbr_view_tex_normal; }\n\tvoid set_show_texture_rough(const bool p_enabled);\n\tbool get_show_texture_rough() const { return _pbr_view_tex_rough; }\n\tvoid set_show_texture_ao(const bool p_enabled);\n\tbool get_show_texture_ao() const { return _pbr_view_tex_ao; }\n\n\tError save(const String &p_path = \"\");\n\nprotected:\n\tvoid _get_property_list(List<PropertyInfo> *p_list) const;\n\tbool _property_can_revert(const StringName &p_name) const;\n\tbool _property_get_revert(const StringName &p_name, Variant &r_property) const;\n\tbool _set(const StringName &p_name, const Variant &p_property);\n\tbool _get(const StringName &p_name, Variant &r_property) const;\n\n\tstatic void _bind_methods();\n};\n\nVARIANT_ENUM_CAST(Terrain3DMaterial::WorldBackground);\nVARIANT_ENUM_CAST(Terrain3DMaterial::TextureFiltering);\nVARIANT_ENUM_CAST(Terrain3DMaterial::UpdateFlags);\n\n#endif // TERRAIN3D_MATERIAL_CLASS_H"
  },
  {
    "path": "src/terrain_3d_mesh_asset.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/editor_interface.hpp>\n#include <godot_cpp/classes/editor_paths.hpp>\n#include <godot_cpp/classes/file_access.hpp>\n#include <godot_cpp/classes/image_texture.hpp>\n#include <godot_cpp/classes/material.hpp>\n#include <godot_cpp/classes/project_settings.hpp>\n#include <godot_cpp/classes/quad_mesh.hpp>\n#include <godot_cpp/classes/standard_material3d.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d_mesh_asset.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\nvoid Terrain3DMeshAsset::_clear_lod_ranges() {\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Clearing LOD Ranges, setting last_lod to 128\");\n\t_lod_ranges.resize(MAX_LOD_COUNT);\n\tfor (int i = 0; i < MAX_LOD_COUNT; i++) {\n\t\t_lod_ranges[i] = (i + 1) * Terrain3DInstancer::CELL_SIZE;\n\t}\n\t_lod_ranges[_last_lod] = MAX(_lod_ranges[_last_lod], 128.f);\n}\n\nbool Terrain3DMeshAsset::_sort_lod_nodes(const Node *a, const Node *b) {\n\tASSERT(a && b, false);\n\treturn a->get_name().right(1) < b->get_name().right(1);\n}\n\nRef<ArrayMesh> Terrain3DMeshAsset::_create_generated_mesh(const GenType p_type) const {\n\tif (p_type != TYPE_TEXTURE_CARD) {\n\t\tLOG(ERROR, \"Only TYPE_TEXTURE_CARD is currently implemented\");\n\t\treturn Ref<ArrayMesh>();\n\t}\n\tLOG(EXTREME, \"Regenerating new mesh\");\n\tRef<ArrayMesh> array_mesh;\n\tarray_mesh.instantiate();\n\tPackedVector3Array vertices;\n\tPackedVector3Array normals;\n\tPackedFloat32Array tangents;\n\tPackedVector2Array uvs;\n\tPackedInt32Array indices;\n\n\tint prevrow, thisrow, point = 0;\n\tfloat x, z;\n\tSize2 start_pos = Vector2(_generated_size.x * -0.5f, -0.5f);\n\tVector3 normal = Vector3(0.f, 0.f, 1.f);\n\tthisrow = point;\n\tprevrow = 0;\n\tfor (int m = 1; m <= _generated_faces; m++) {\n\t\tz = start_pos.y;\n\t\treal_t angle = 0.f;\n\t\tif (m > 1) {\n\t\t\tangle = (m - 1) * Math_PI / _generated_faces;\n\t\t}\n\t\tfor (int j = 0; j <= 1; j++) {\n\t\t\tx = start_pos.x;\n\t\t\tfor (int i = 0; i <= 1; i++) {\n\t\t\t\tfloat u = i;\n\t\t\t\tfloat v = j;\n\t\t\t\tvertices.push_back(Vector3(-x, z, 0.f).rotated(V3_UP, angle));\n\t\t\t\tnormals.push_back(normal);\n\t\t\t\ttangents.push_back(1.f);\n\t\t\t\ttangents.push_back(0.f);\n\t\t\t\ttangents.push_back(0.f);\n\t\t\t\ttangents.push_back(1.f);\n\t\t\t\tuvs.push_back(Vector2(1.f - u, 1.f - v));\n\t\t\t\tpoint++;\n\t\t\t\tif (i > 0 && j > 0) {\n\t\t\t\t\tindices.push_back(prevrow + i - 1);\n\t\t\t\t\tindices.push_back(prevrow + i);\n\t\t\t\t\tindices.push_back(thisrow + i - 1);\n\t\t\t\t\tindices.push_back(prevrow + i);\n\t\t\t\t\tindices.push_back(thisrow + i);\n\t\t\t\t\tindices.push_back(thisrow + i - 1);\n\t\t\t\t}\n\t\t\t\tx += _generated_size.x;\n\t\t\t}\n\t\t\tz += _generated_size.y;\n\t\t\tprevrow = thisrow;\n\t\t\tthisrow = point;\n\t\t}\n\t}\n\n\tArray arrays;\n\tarrays.resize(Mesh::ARRAY_MAX);\n\tarrays[Mesh::ARRAY_VERTEX] = vertices;\n\tarrays[Mesh::ARRAY_NORMAL] = normals;\n\tarrays[Mesh::ARRAY_TANGENT] = tangents;\n\tarrays[Mesh::ARRAY_TEX_UV] = uvs;\n\tarrays[Mesh::ARRAY_INDEX] = indices;\n\tarray_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);\n\treturn array_mesh;\n}\n\nvoid Terrain3DMeshAsset::_assign_generated_mesh() {\n\tLOG(DEBUG, \"Assiging generated mesh & lod settings\");\n\t_packed_scene.unref();\n\t_pending_meshes.clear();\n\t_pending_meshes.push_back(_create_generated_mesh());\n\t_last_lod = 0;\n\t_last_shadow_lod = 0;\n\t_shadow_impostor = 0;\n\tif (_material_override.is_null()) {\n\t\t_material_override = _get_material();\n\t}\n\t// If no existing meshes, commit immediately. Otherwise, will trigger on an instancer update\n\tif (_meshes.is_empty()) {\n\t\tcommit_meshes();\n\t}\n}\n\nRef<Material> Terrain3DMeshAsset::_get_material() {\n\tif (_material_override.is_null()) {\n\t\tRef<StandardMaterial3D> mat;\n\t\tmat.instantiate();\n\t\tmat->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);\n\t\tmat->set_cull_mode(BaseMaterial3D::CULL_DISABLED);\n\t\tmat->set_feature(BaseMaterial3D::FEATURE_BACKLIGHT, true);\n\t\tmat->set_backlight(Color(.5f, .5f, .5f));\n\t\tmat->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);\n\t\tmat->set_distance_fade(BaseMaterial3D::DISTANCE_FADE_PIXEL_ALPHA);\n\t\tmat->set_distance_fade_min_distance(128.f);\n\t\tmat->set_distance_fade_max_distance(96.f);\n\t\tmat->set_meta(\"terrain3d_generated_material\", true);\n\t\treturn mat;\n\t} else {\n\t\treturn _material_override;\n\t}\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\n// Called by Terrain3DAssets::_set_asset_list() and _set_asset()\nvoid Terrain3DMeshAsset::initialize() {\n\tLOG(INFO, _id, \": \", _name, \": initializing asset\");\n\tif (_packed_scene.is_null() && _generated_type == TYPE_NONE) {\n\t\tLOG(DEBUG, \"Blank mesh, setting up default texture card\");\n\t\tset_generated_type(TYPE_TEXTURE_CARD);\n\t\t_height_offset = 0.5f;\n\t\t_clear_lod_ranges();\n\t}\n}\n\nvoid Terrain3DMeshAsset::clear() {\n\tLOG(INFO, \"Clearing MeshAsset\");\n\t_name = \"New Mesh\";\n\t_id = 0;\n\t_enabled = true;\n\t_highlighted = false;\n\t_highlight_mat = Ref<Material>();\n\t_packed_scene.unref();\n\t_meshes.clear();\n\t_pending_meshes.clear();\n\t_generated_type = TYPE_NONE;\n\t_generated_faces = 2;\n\t_generated_size = V2(1.f);\n\t_height_offset = 0.f;\n\t_density = 0.f;\n\t_cast_shadows = SHADOWS_ON;\n\t_visibility_layers = 1;\n\t_material_override.unref();\n\t_material_overlay.unref();\n\t_last_lod = MAX_LOD_COUNT - 1;\n\t_last_shadow_lod = MAX_LOD_COUNT - 1;\n\t_shadow_impostor = 0;\n\t_clear_lod_ranges();\n\t_fade_margin = 0.f;\n\t_thumbnail.unref();\n}\n\nvoid Terrain3DMeshAsset::set_name(const String &p_name) {\n\tif (p_name.length() > 96) {\n\t\tLOG(WARN, \"Name too long, truncating to 96 characters\");\n\t}\n\tSET_IF_DIFF(_name, p_name.left(96));\n\tLOG(INFO, \"ID \", _id, \": Setting name: \", _name);\n\tLOG(DEBUG, \"Emitting setting_changed, ID: \", _id);\n\temit_signal(\"setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_id(const int p_new_id) {\n\tint old_id = _id;\n\tSET_IF_DIFF(_id, CLAMP(p_new_id, 0, Terrain3DAssets::MAX_MESHES - 1));\n\tLOG(INFO, _name, \": Setting mesh ID: \", _id);\n\tLOG(DEBUG, \"Emitting id_changed, TYPE_MESH, \", old_id, \", \", p_new_id);\n\temit_signal(\"id_changed\", Terrain3DAssets::TYPE_MESH, old_id, p_new_id);\n}\n\nvoid Terrain3DMeshAsset::set_highlighted(const bool p_highlighted) {\n\tSET_IF_DIFF(_highlighted, p_highlighted);\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Set mesh ID highlight: \", p_highlighted);\n\tif (_highlighted && _highlight_mat.is_null()) {\n\t\tRef<StandardMaterial3D> mat;\n\t\tmat.instantiate();\n\t\tmat->set_cull_mode(BaseMaterial3D::CULL_DISABLED);\n\t\tColor color;\n\t\treal_t random_float = real_t(rand()) / real_t(RAND_MAX);\n\t\tcolor.set_hsv(random_float, 1.f, 1.f, 1.f);\n\t\tmat->set_albedo(color);\n\t\t_highlight_mat = mat;\n\t}\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nColor Terrain3DMeshAsset::get_highlight_color() const {\n\tStandardMaterial3D *mat = cast_to<StandardMaterial3D>(_highlight_mat.ptr());\n\tif (_highlighted && mat) {\n\t\treturn mat->get_albedo();\n\t}\n\treturn COLOR_WHITE;\n}\n\nvoid Terrain3DMeshAsset::set_enabled(const bool p_enabled) {\n\tSET_IF_DIFF(_enabled, p_enabled);\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting enabled: \", _enabled);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::update_instance_count(const int p_amount) {\n\tif (p_amount == 0) {\n\t\treturn;\n\t}\n\tint new_count = _instance_count + p_amount;\n\t_instance_count = CLAMP(new_count, 0, UINT32_MAX);\n\tLOG(EXTREME, \"Emitting instance_count_changed, ID: \", _id, \", count: \", _instance_count);\n\temit_signal(\"instance_count_changed\");\n}\n\nvoid Terrain3DMeshAsset::set_instance_count(const uint32_t p_amount) {\n\tSET_IF_DIFF(_instance_count, CLAMP(p_amount, 0, UINT32_MAX));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting instance_count: \", _instance_count);\n\tLOG(DEBUG, \"Emitting instance_count_changed\");\n\temit_signal(\"instance_count_changed\");\n}\n\nvoid Terrain3DMeshAsset::set_scene_file(const Ref<PackedScene> &p_scene_file) {\n\tSET_IF_DIFF(_packed_scene, p_scene_file);\n\t_pending_meshes.clear();\n\tif (_packed_scene.is_valid()) {\n\t\tNode *node = _packed_scene->instantiate();\n\t\tif (!node) {\n\t\t\tLOG(ERROR, \"Drag a non-empty glb, fbx, scn, or tscn file into the scene_file slot\");\n\t\t\t_packed_scene.unref();\n\t\t\treturn;\n\t\t}\n\t\tLOG(INFO, \"ID \", _id, \", \", _name, \": Instantiating scene root node: \", _packed_scene->get_path());\n\t\t_height_offset = 0.0f;\n\t\t_generated_type = TYPE_NONE;\n\t\tif (_material_override.is_valid() && _material_override->has_meta(\"terrain3d_generated_material\")) {\n\t\t\t_material_override.unref();\n\t\t}\n\n\t\t// Look for MeshInstance3D nodes\n\t\tLOG(DEBUG, \"Loaded scene with parent node: \", node);\n\t\tTypedArray<Node> mesh_instances;\n\n\t\t// First look for XXXXLOD# meshes, sorted by last digit\n\t\tmesh_instances = node->find_children(\"*LOD?\", \"MeshInstance3D\");\n\t\tif (mesh_instances.size() > 0) {\n\t\t\tLOG(INFO, \"Found \", mesh_instances.size(), \" meshes using LOD# naming convention, using the first \", MAX_LOD_COUNT);\n\t\t\tmesh_instances.sort_custom(callable_mp_static(&Terrain3DMeshAsset::_sort_lod_nodes));\n\t\t}\n\n\t\t// Fallback to using all the meshes in provided order\n\t\tif (mesh_instances.size() == 0) {\n\t\t\tmesh_instances = node->find_children(\"*\", \"MeshInstance3D\");\n\t\t\tif (mesh_instances.size() > 0) {\n\t\t\t\tLOG(INFO, \"No meshes with LOD# suffixes found, using the first \", MAX_LOD_COUNT, \" meshes as LOD0-LOD3\");\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to the scene root mesh\n\t\tif (mesh_instances.size() == 0) {\n\t\t\tif (node->is_class(\"MeshInstance3D\")) {\n\t\t\t\tLOG(INFO, \"No LOD# meshes found, assuming the root mesh is LOD0\");\n\t\t\t\tmesh_instances.push_back(node);\n\t\t\t}\n\t\t}\n\t\tif (mesh_instances.size() == 0) {\n\t\t\tLOG(ERROR, \"No MeshInstance3D found in scene file\");\n\t\t}\n\n\t\t// Now process the meshes\n\t\tfor (int i = 0, count = MIN(mesh_instances.size(), MAX_LOD_COUNT); i < count; i++) {\n\t\t\tMeshInstance3D *mi = cast_to<MeshInstance3D>(mesh_instances[i]);\n\t\t\tLOG(DEBUG, \"Found mesh: \", mi->get_name());\n\t\t\tString filename = _packed_scene->get_path().get_file().get_basename();\n\t\t\tif (_name == \"New Mesh\" && !_packed_scene->get_path().contains(\"::\")) {\n\t\t\t\t_name = filename;\n\t\t\t\tLOG(INFO, \"Setting name based on filename: \", _name);\n\t\t\t}\n\t\t\tRef<Mesh> mesh = mi->get_mesh();\n\t\t\tif (mesh.is_null()) {\n\t\t\t\tLOG(WARN, \"MeshInstance3D \", mi->get_name(), \" has no mesh, skipping\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Duplicate the mesh to make each Terrain3DMeshAsset unique\n\t\t\tmesh = mesh->duplicate();\n\t\t\t// Apply the active material from the scene to the mesh, including MI or Geom overrides\n\t\t\tfor (int j = 0; j < mi->get_surface_override_material_count(); j++) {\n\t\t\t\tRef<Material> mat = mi->get_active_material(j);\n\t\t\t\tmesh->surface_set_material(j, mat);\n\t\t\t}\n\t\t\t_pending_meshes.push_back(mesh);\n\t\t}\n\t\tnode->queue_free();\n\t}\n\tif (_pending_meshes.size() > 0) {\n\t\tRef<Mesh> mesh = _pending_meshes[0];\n\t\tif (mesh.is_null()) {\n\t\t\tLOG(ERROR, \"First mesh is null after loading scene\");\n\t\t\treturn;\n\t\t}\n\t\tAABB aabb = mesh->get_aabb();\n\t\t_density = CLAMP(10.f / (aabb.has_volume() ? aabb.get_volume() : 1.f), 0.01f, 10.0f);\n\t\t_last_lod = _pending_meshes.size() - 1;\n\t\t_last_shadow_lod = _last_lod;\n\t\t_shadow_impostor = 0;\n\t\t_clear_lod_ranges();\n\t} else {\n\t\tset_generated_type(TYPE_TEXTURE_CARD);\n\t\t_height_offset = 0.5f;\n\t\t_clear_lod_ranges();\n\t}\n\t// If no existing meshes, commit immediately. Otherwise, will trigger on an instancer update\n\tif (_meshes.is_empty()) {\n\t\tcommit_meshes();\n\t}\n\tnotify_property_list_changed(); // Call _validate_property to update inspector\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::commit_meshes() {\n\tLOG(INFO, _name, \": Committing \", _pending_meshes.size(), \" pending meshes\");\n\t_meshes.clear();\n\t_meshes = _pending_meshes;\n\t_pending_meshes = TypedArray<Mesh>();\n}\n\nvoid Terrain3DMeshAsset::set_generated_type(const GenType p_type) {\n\tSET_IF_DIFF(_generated_type, CLAMP(p_type, GenType(TYPE_NONE + 1), GenType(TYPE_MAX - 1)));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting generated type: \", _generated_type);\n\t_assign_generated_mesh();\n\t_density = 10.f;\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n\tnotify_property_list_changed();\n}\n\nRef<Mesh> Terrain3DMeshAsset::get_mesh(const int p_lod) const {\n\tif (p_lod >= 0 && p_lod < _get_meshes().size()) {\n\t\treturn _get_meshes()[p_lod];\n\t}\n\treturn Ref<Mesh>();\n}\n\nvoid Terrain3DMeshAsset::set_height_offset(const real_t p_offset) {\n\tSET_IF_DIFF(_height_offset, CLAMP(p_offset, -50.f, 50.f));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting height offset: \", _height_offset);\n\tLOG(DEBUG, \"Emitting setting_changed, ID: \", _id);\n\temit_signal(\"setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_density(const real_t p_density) {\n\tSET_IF_DIFF(_density, CLAMP(p_density, 0.01f, 10.f));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting mesh density: \", p_density);\n\tLOG(DEBUG, \"Emitting setting_changed, ID: \", _id);\n\temit_signal(\"setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_cast_shadows(const ShadowCasting p_cast_shadows) {\n\tSET_IF_DIFF(_cast_shadows, p_cast_shadows);\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting shadow casting mode: \", _cast_shadows);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\n// Returns the approproate cast_shadows setting for the given LOD ID\nShadowCasting Terrain3DMeshAsset::get_lod_cast_shadows(const int p_lod_id) const {\n\t// If cast shadows is off, disable all shadows\n\tif (_cast_shadows == SHADOWS_OFF) {\n\t\treturn _cast_shadows;\n\t}\n\t// Return shadows only if set, ensuring shadow impostor and last lod are processed first\n\tif (_cast_shadows == SHADOWS_ONLY) {\n\t\treturn _cast_shadows;\n\t}\n\t// Set to shadows only if this lod is the shadow impostor, which is only ever set to shadows only\n\tif (p_lod_id == SHADOW_LOD_ID) {\n\t\treturn SHADOWS_ONLY;\n\t}\n\t// Disable shadows if this lod uses the shadow impostor\n\tif (p_lod_id < _shadow_impostor) {\n\t\treturn SHADOWS_OFF;\n\t}\n\t// Disable shadows if this lod is too far\n\tif (p_lod_id > _last_shadow_lod) {\n\t\treturn SHADOWS_OFF;\n\t}\n\treturn _cast_shadows;\n}\n\ninline void Terrain3DMeshAsset::set_visibility_layers(const uint32_t p_layers) {\n\tSET_IF_DIFF(_visibility_layers, p_layers);\n\tLOG(INFO, _name, \": Setting visibility layers: \", p_layers);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_material_override(const Ref<Material> &p_material) {\n\tSET_IF_DIFF(_material_override, p_material);\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting material override: \", p_material);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_material_overlay(const Ref<Material> &p_material) {\n\tSET_IF_DIFF(_material_overlay, p_material);\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting material overlay: \", p_material);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_generated_faces(const int p_count) {\n\tSET_IF_DIFF(_generated_faces, CLAMP(p_count, 1, 3));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting generated face count: \", _generated_faces);\n\t// If already a texture card, just recreate the mesh w/o other settings\n\tif (_generated_type > TYPE_NONE && _generated_type < TYPE_MAX && _get_meshes().size() == 1) {\n\t\t_assign_generated_mesh();\n\t}\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_generated_size(const Vector2 &p_size) {\n\tSET_IF_DIFF(_generated_size, p_size);\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting generated size: \", _generated_size);\n\t// If already a texture card, just recreate the mesh w/o other settings\n\tif (_generated_type > TYPE_NONE && _generated_type < TYPE_MAX && _get_meshes().size() == 1) {\n\t\t_assign_generated_mesh();\n\t}\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_last_lod(const int p_lod) {\n\tint max_lod = _generated_type != TYPE_NONE ? 0 : CLAMP(_get_meshes().size(), 1, MAX_LOD_COUNT) - 1;\n\tSET_IF_DIFF(_last_lod, CLAMP(p_lod, 0, max_lod));\n\tif (_last_shadow_lod > _last_lod) {\n\t\t_last_shadow_lod = _last_lod;\n\t}\n\tif (_shadow_impostor > _last_lod) {\n\t\t_shadow_impostor = _last_lod;\n\t}\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting last LOD: \", _last_lod);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n\tnotify_property_list_changed();\n}\n\nvoid Terrain3DMeshAsset::set_last_shadow_lod(const int p_lod) {\n\tSET_IF_DIFF(_last_shadow_lod, CLAMP(p_lod, 0, _last_lod));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting last shadow LOD: \", _last_shadow_lod);\n\tif (_shadow_impostor > _last_shadow_lod) {\n\t\t_shadow_impostor = _last_shadow_lod;\n\t}\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_shadow_impostor(const int p_lod) {\n\tSET_IF_DIFF(_shadow_impostor, CLAMP(p_lod, 0, MIN(_last_lod, _last_shadow_lod)));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting shadow imposter LOD: \", _shadow_impostor);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nvoid Terrain3DMeshAsset::set_lod_range(const int p_lod, const real_t p_distance) {\n\tif (p_lod < 0 || p_lod >= _lod_ranges.size()) {\n\t\tLOG(ERROR, \"p_lod out of range. Valid range is 0 - \", _lod_ranges.size() - 1);\n\t\treturn;\n\t}\n\tSET_IF_DIFF(_lod_ranges[p_lod], CLAMP(p_distance, 0.f, 100000.f));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting LOD \", p_lod, \" visibility range: \", _lod_ranges[p_lod]);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\nreal_t Terrain3DMeshAsset::get_lod_range(const int p_lod) const {\n\tif (p_lod < 0 || p_lod >= _lod_ranges.size()) {\n\t\treturn -1.f;\n\t}\n\treturn _lod_ranges[p_lod];\n}\n\nreal_t Terrain3DMeshAsset::get_lod_range_begin(const int p_lod) const {\n\tif (p_lod <= 0) {\n\t\treturn 0.f;\n\t}\n\tASSERT(p_lod <= _last_lod, 0.f);\n\treturn _lod_ranges[p_lod - 1];\n}\n\nreal_t Terrain3DMeshAsset::get_lod_range_end(const int p_lod) const {\n\tif (p_lod == SHADOW_LOD_ID) {\n\t\treturn _lod_ranges[MAX(_shadow_impostor - 1, 0)];\n\t}\n\tASSERT(p_lod >= 0 && p_lod <= _last_lod, 0.f);\n\treturn _lod_ranges[p_lod];\n}\n\nvoid Terrain3DMeshAsset::set_fade_margin(const real_t p_fade_margin) {\n\tint max_range = CLAMP(_lod_ranges[1] - _lod_ranges[0], 0.f, 64.f);\n\tSET_IF_DIFF(_fade_margin, CLAMP(p_fade_margin, 0.f, max_range));\n\tLOG(INFO, \"ID \", _id, \", \", _name, \": Setting visibility margin: \", _fade_margin);\n\tLOG(DEBUG, \"Emitting instancer_setting_changed, ID: \", _id);\n\temit_signal(\"instancer_setting_changed\", _id);\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DMeshAsset::_validate_property(PropertyInfo &p_property) const {\n\tif (p_property.name != StringName(\"generated_type\") && p_property.name.begins_with(\"generated_\")) {\n\t\tif (_generated_type == TYPE_NONE) {\n\t\t\tp_property.usage = PROPERTY_USAGE_NO_EDITOR;\n\t\t} else {\n\t\t\tp_property.usage = PROPERTY_USAGE_DEFAULT;\n\t\t}\n\t\treturn;\n\t} else if (p_property.name.match(\"lod?_range\")) {\n\t\tint lod = p_property.name.substr(3, 1).to_int();\n\t\tif (_last_lod < lod) {\n\t\t\tp_property.usage = PROPERTY_USAGE_NO_EDITOR;\n\t\t} else {\n\t\t\tp_property.usage = PROPERTY_USAGE_DEFAULT;\n\t\t}\n\t}\n}\n\nbool Terrain3DMeshAsset::_property_can_revert(const StringName &p_name) const {\n\treturn (\n\t\t\tp_name.match(\"last_lod\") ||\n\t\t\tp_name.match(\"last_shadow_lod\") ||\n\t\t\tp_name.match(\"density\") ||\n\t\t\tp_name.match(\"generated_type\") ||\n\t\t\t(_generated_type > TYPE_NONE && (p_name.match(\"height_offset\") || p_name.match(\"lod0_range\"))));\n}\n\nbool Terrain3DMeshAsset::_property_get_revert(const StringName &p_name, Variant &r_property) const {\n\tif (p_name.match(\"last_lod\")) {\n\t\tr_property = get_lod_count() - 1;\n\t\treturn true;\n\t} else if (p_name.match(\"last_shadow_lod\")) {\n\t\tr_property = get_lod_count() - 1;\n\t\treturn true;\n\t} else if (p_name.match(\"density\")) {\n\t\tr_property = 10.f;\n\t\treturn true;\n\t} else if (p_name.match(\"generated_type\")) {\n\t\tr_property = TYPE_TEXTURE_CARD;\n\t\treturn true;\n\t} else if (_generated_type > TYPE_NONE) {\n\t\tif (p_name.match(\"height_offset\")) {\n\t\t\tr_property = 0.5f;\n\t\t\treturn true;\n\t\t} else if (p_name.match(\"lod0_range\")) {\n\t\t\tr_property = 128.f;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid Terrain3DMeshAsset::_bind_methods() {\n\tBIND_ENUM_CONSTANT(TYPE_NONE);\n\tBIND_ENUM_CONSTANT(TYPE_TEXTURE_CARD);\n\tBIND_ENUM_CONSTANT(TYPE_MAX);\n\n\tADD_SIGNAL(MethodInfo(\"id_changed\"));\n\tADD_SIGNAL(MethodInfo(\"setting_changed\"));\n\tADD_SIGNAL(MethodInfo(\"instancer_setting_changed\"));\n\tADD_SIGNAL(MethodInfo(\"instance_count_changed\"));\n\n\tClassDB::bind_method(D_METHOD(\"clear\"), &Terrain3DMeshAsset::clear);\n\tClassDB::bind_method(D_METHOD(\"set_name\", \"name\"), &Terrain3DMeshAsset::set_name);\n\tClassDB::bind_method(D_METHOD(\"get_name\"), &Terrain3DMeshAsset::get_name);\n\tClassDB::bind_method(D_METHOD(\"set_id\", \"id\"), &Terrain3DMeshAsset::set_id);\n\tClassDB::bind_method(D_METHOD(\"get_id\"), &Terrain3DMeshAsset::get_id);\n\tClassDB::bind_method(D_METHOD(\"set_highlighted\", \"enabled\"), &Terrain3DMeshAsset::set_highlighted);\n\tClassDB::bind_method(D_METHOD(\"is_highlighted\"), &Terrain3DMeshAsset::is_highlighted);\n\tClassDB::bind_method(D_METHOD(\"get_highlight_color\"), &Terrain3DMeshAsset::get_highlight_color);\n\tClassDB::bind_method(D_METHOD(\"get_thumbnail\"), &Terrain3DMeshAsset::get_thumbnail);\n\n\tClassDB::bind_method(D_METHOD(\"set_enabled\", \"enabled\"), &Terrain3DMeshAsset::set_enabled);\n\tClassDB::bind_method(D_METHOD(\"is_enabled\"), &Terrain3DMeshAsset::is_enabled);\n\n\tClassDB::bind_method(D_METHOD(\"set_scene_file\", \"scene_file\"), &Terrain3DMeshAsset::set_scene_file);\n\tClassDB::bind_method(D_METHOD(\"get_scene_file\"), &Terrain3DMeshAsset::get_scene_file);\n\tClassDB::bind_method(D_METHOD(\"set_generated_type\", \"type\"), &Terrain3DMeshAsset::set_generated_type);\n\tClassDB::bind_method(D_METHOD(\"get_generated_type\"), &Terrain3DMeshAsset::get_generated_type);\n\tClassDB::bind_method(D_METHOD(\"get_mesh\", \"lod\"), &Terrain3DMeshAsset::get_mesh, DEFVAL(0));\n\tClassDB::bind_method(D_METHOD(\"set_height_offset\", \"offset\"), &Terrain3DMeshAsset::set_height_offset);\n\tClassDB::bind_method(D_METHOD(\"get_height_offset\"), &Terrain3DMeshAsset::get_height_offset);\n\tClassDB::bind_method(D_METHOD(\"set_density\", \"density\"), &Terrain3DMeshAsset::set_density);\n\tClassDB::bind_method(D_METHOD(\"get_density\"), &Terrain3DMeshAsset::get_density);\n\tClassDB::bind_method(D_METHOD(\"set_cast_shadows\", \"mode\"), &Terrain3DMeshAsset::set_cast_shadows);\n\tClassDB::bind_method(D_METHOD(\"get_cast_shadows\"), &Terrain3DMeshAsset::get_cast_shadows);\n\tClassDB::bind_method(D_METHOD(\"set_visibility_layers\", \"layers\"), &Terrain3DMeshAsset::set_visibility_layers);\n\tClassDB::bind_method(D_METHOD(\"get_visibility_layers\"), &Terrain3DMeshAsset::get_visibility_layers);\n\tClassDB::bind_method(D_METHOD(\"set_material_override\", \"material\"), &Terrain3DMeshAsset::set_material_override);\n\tClassDB::bind_method(D_METHOD(\"get_material_override\"), &Terrain3DMeshAsset::get_material_override);\n\tClassDB::bind_method(D_METHOD(\"set_material_overlay\", \"material\"), &Terrain3DMeshAsset::set_material_overlay);\n\tClassDB::bind_method(D_METHOD(\"get_material_overlay\"), &Terrain3DMeshAsset::get_material_overlay);\n\n\tClassDB::bind_method(D_METHOD(\"set_generated_faces\", \"count\"), &Terrain3DMeshAsset::set_generated_faces);\n\tClassDB::bind_method(D_METHOD(\"get_generated_faces\"), &Terrain3DMeshAsset::get_generated_faces);\n\tClassDB::bind_method(D_METHOD(\"set_generated_size\", \"size\"), &Terrain3DMeshAsset::set_generated_size);\n\tClassDB::bind_method(D_METHOD(\"get_generated_size\"), &Terrain3DMeshAsset::get_generated_size);\n\n\tClassDB::bind_method(D_METHOD(\"get_lod_count\"), &Terrain3DMeshAsset::get_lod_count);\n\tClassDB::bind_method(D_METHOD(\"set_last_lod\", \"lod\"), &Terrain3DMeshAsset::set_last_lod);\n\tClassDB::bind_method(D_METHOD(\"get_last_lod\"), &Terrain3DMeshAsset::get_last_lod);\n\tClassDB::bind_method(D_METHOD(\"set_last_shadow_lod\", \"lod\"), &Terrain3DMeshAsset::set_last_shadow_lod);\n\tClassDB::bind_method(D_METHOD(\"get_last_shadow_lod\"), &Terrain3DMeshAsset::get_last_shadow_lod);\n\tClassDB::bind_method(D_METHOD(\"set_shadow_impostor\", \"lod\"), &Terrain3DMeshAsset::set_shadow_impostor);\n\tClassDB::bind_method(D_METHOD(\"get_shadow_impostor\"), &Terrain3DMeshAsset::get_shadow_impostor);\n\n\tClassDB::bind_method(D_METHOD(\"set_lod_range\", \"lod\", \"distance\"), &Terrain3DMeshAsset::set_lod_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod_range\", \"lod\"), &Terrain3DMeshAsset::get_lod_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod0_range\", \"distance\"), &Terrain3DMeshAsset::set_lod0_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod0_range\"), &Terrain3DMeshAsset::get_lod0_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod1_range\", \"distance\"), &Terrain3DMeshAsset::set_lod1_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod1_range\"), &Terrain3DMeshAsset::get_lod1_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod2_range\", \"distance\"), &Terrain3DMeshAsset::set_lod2_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod2_range\"), &Terrain3DMeshAsset::get_lod2_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod3_range\", \"distance\"), &Terrain3DMeshAsset::set_lod3_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod3_range\"), &Terrain3DMeshAsset::get_lod3_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod4_range\", \"distance\"), &Terrain3DMeshAsset::set_lod4_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod4_range\"), &Terrain3DMeshAsset::get_lod4_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod5_range\", \"distance\"), &Terrain3DMeshAsset::set_lod5_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod5_range\"), &Terrain3DMeshAsset::get_lod5_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod6_range\", \"distance\"), &Terrain3DMeshAsset::set_lod6_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod6_range\"), &Terrain3DMeshAsset::get_lod6_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod7_range\", \"distance\"), &Terrain3DMeshAsset::set_lod7_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod7_range\"), &Terrain3DMeshAsset::get_lod7_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod8_range\", \"distance\"), &Terrain3DMeshAsset::set_lod8_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod8_range\"), &Terrain3DMeshAsset::get_lod8_range);\n\tClassDB::bind_method(D_METHOD(\"set_lod9_range\", \"distance\"), &Terrain3DMeshAsset::set_lod9_range);\n\tClassDB::bind_method(D_METHOD(\"get_lod9_range\"), &Terrain3DMeshAsset::get_lod9_range);\n\tClassDB::bind_method(D_METHOD(\"set_fade_margin\", \"distance\"), &Terrain3DMeshAsset::set_fade_margin);\n\tClassDB::bind_method(D_METHOD(\"get_fade_margin\"), &Terrain3DMeshAsset::get_fade_margin);\n\n\tClassDB::bind_method(D_METHOD(\"get_instance_count\"), &Terrain3DMeshAsset::get_instance_count);\n\n\tADD_PROPERTY(PropertyInfo(Variant::STRING, \"name\", PROPERTY_HINT_NONE), \"set_name\", \"get_name\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"id\", PROPERTY_HINT_NONE), \"set_id\", \"get_id\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"enabled\", PROPERTY_HINT_NONE), \"set_enabled\", \"is_enabled\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"scene_file\", PROPERTY_HINT_RESOURCE_TYPE, \"PackedScene\"), \"set_scene_file\", \"get_scene_file\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"generated_type\", PROPERTY_HINT_ENUM, \"None,Texture Card\", PROPERTY_USAGE_STORAGE), \"set_generated_type\", \"get_generated_type\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"height_offset\", PROPERTY_HINT_RANGE, \"-20.0,20.0,.005\"), \"set_height_offset\", \"get_height_offset\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"density\", PROPERTY_HINT_RANGE, \".01,10.0,.005\"), \"set_density\", \"get_density\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"cast_shadows\", PROPERTY_HINT_ENUM, \"Off,On,Double-Sided,Shadows Only\"), \"set_cast_shadows\", \"get_cast_shadows\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"visibility_layers\", PROPERTY_HINT_LAYERS_3D_RENDER), \"set_visibility_layers\", \"get_visibility_layers\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"material_override\", PROPERTY_HINT_RESOURCE_TYPE, \"BaseMaterial3D,ShaderMaterial\"), \"set_material_override\", \"get_material_override\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"material_overlay\", PROPERTY_HINT_RESOURCE_TYPE, \"BaseMaterial3D,ShaderMaterial\"), \"set_material_overlay\", \"get_material_overlay\");\n\n\tADD_GROUP(\"Generated Mesh\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"generated_faces\", PROPERTY_HINT_NONE), \"set_generated_faces\", \"get_generated_faces\");\n\tADD_PROPERTY(PropertyInfo(Variant::VECTOR2, \"generated_size\", PROPERTY_HINT_NONE), \"set_generated_size\", \"get_generated_size\");\n\n\tADD_GROUP(\"LODs\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"lod_count\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY), \"\", \"get_lod_count\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"last_lod\", PROPERTY_HINT_NONE), \"set_last_lod\", \"get_last_lod\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"last_shadow_lod\", PROPERTY_HINT_NONE), \"set_last_shadow_lod\", \"get_last_shadow_lod\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"shadow_impostor\", PROPERTY_HINT_NONE), \"set_shadow_impostor\", \"get_shadow_impostor\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod0_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod0_range\", \"get_lod0_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod1_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod1_range\", \"get_lod1_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod2_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod2_range\", \"get_lod2_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod3_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod3_range\", \"get_lod3_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod4_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod4_range\", \"get_lod4_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod5_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod5_range\", \"get_lod5_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod6_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod6_range\", \"get_lod6_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod7_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod7_range\", \"get_lod7_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod8_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod8_range\", \"get_lod8_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"lod9_range\", PROPERTY_HINT_RANGE, \"0.,4096.0,.05,or_greater\"), \"set_lod9_range\", \"get_lod9_range\");\n\t// Fade disabled until https://github.com/godotengine/godot/issues/102799 is fixed\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"fade_margin\", PROPERTY_HINT_RANGE, \"0.,64.0,.05,or_greater\", PROPERTY_USAGE_NO_EDITOR), \"set_fade_margin\", \"get_fade_margin\");\n}\n"
  },
  {
    "path": "src/terrain_3d_mesh_asset.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_MESH_ASSET_CLASS_H\n#define TERRAIN3D_MESH_ASSET_CLASS_H\n\n#include <godot_cpp/classes/array_mesh.hpp>\n#include <godot_cpp/classes/material.hpp>\n#include <godot_cpp/classes/packed_scene.hpp>\n#include <godot_cpp/classes/resource.hpp>\n#include <godot_cpp/classes/texture2d.hpp>\n\n#include \"constants.h\"\n#include \"terrain_3d_asset_resource.h\"\n\nusing ShadowCasting = RenderingServer::ShadowCastingSetting;\nconstexpr ShadowCasting SHADOWS_ON = RenderingServer::SHADOW_CASTING_SETTING_ON;\nconstexpr ShadowCasting SHADOWS_OFF = RenderingServer::SHADOW_CASTING_SETTING_OFF;\nconstexpr ShadowCasting SHADOWS_ONLY = RenderingServer::SHADOW_CASTING_SETTING_SHADOWS_ONLY;\n\n/* This class requires a bit of special care because:\n * - We want custom defaults depending on if it's a texture card or a scene file\n * - Any settings saved in the assets resource file need to override the defaults\n * - generated_type = TEXTURE_CARD is the default and isn't saved in the scene file\n *\n * The caveat of this is a texture card MeshAsset needs to determine if it is\n * new, and should apply defaults, or loaded, and should retain settings.\n * The specific defaults of concern are height_offset, density, lod0_range.\n *\n * The current solution to make the distinction is,\n * Loaded Assets go through Terrain3DAssets::set_mesh_list(), _set_asset_list()\n * New Assets go through Terrain3DAssets::set_mesh_asset(), _set_asset()\n * Both call initialize() with a new/loaded flag.\n *\n * New assets might be loaded from the API, AssetDock, or Terrain3DAssets::update_mesh_list()\n * when the mesh list is empty.\n */\n\nclass Terrain3DMeshAsset : public Terrain3DAssetResource {\n\tGDCLASS(Terrain3DMeshAsset, Terrain3DAssetResource);\n\tCLASS_NAME();\n\npublic:\n\tenum GenType {\n\t\tTYPE_NONE,\n\t\tTYPE_TEXTURE_CARD,\n\t\tTYPE_MAX,\n\t};\n\n\tstatic constexpr int MAX_LOD_COUNT = 10;\n\tstatic constexpr int SHADOW_LOD_ID = -1; // ID used for the shadow lod in instancer\n\nprivate:\n\t// Saved data\n\tbool _enabled = true;\n\tRef<PackedScene> _packed_scene;\n\tGenType _generated_type = TYPE_NONE;\n\tint _generated_faces = 2;\n\tVector2 _generated_size = V2(1.f);\n\treal_t _height_offset = 0.f;\n\treal_t _density = 0.f;\n\tShadowCasting _cast_shadows = SHADOWS_ON;\n\tuint32_t _visibility_layers = 1;\n\tRef<Material> _material_override;\n\tRef<Material> _material_overlay;\n\tint _last_lod = MAX_LOD_COUNT - 1;\n\tint _last_shadow_lod = MAX_LOD_COUNT - 1;\n\tint _shadow_impostor = 0;\n\tPackedFloat32Array _lod_ranges;\n\treal_t _fade_margin = 0.f;\n\n\t// Working data\n\tRef<Material> _highlight_mat;\n\tTypedArray<Mesh> _meshes;\n\tTypedArray<Mesh> _pending_meshes; // Queue to avoid warnings from RS on mesh swap\n\tuint32_t _instance_count = 0;\n\n\tvoid _clear_lod_ranges();\n\tstatic bool _sort_lod_nodes(const Node *a, const Node *b);\n\tRef<ArrayMesh> _create_generated_mesh(const GenType p_type = TYPE_TEXTURE_CARD) const;\n\tvoid _assign_generated_mesh();\n\tRef<Material> _get_material();\n\tTypedArray<Mesh> _get_meshes() const;\n\npublic:\n\tTerrain3DMeshAsset() { clear(); }\n\t~Terrain3DMeshAsset() {}\n\tvoid initialize() override;\n\tvoid clear() override;\n\n\tvoid set_name(const String &p_name) override;\n\tString get_name() const override { return _name; }\n\tvoid set_id(const int p_new_id) override;\n\tint get_id() const override { return _id; }\n\tvoid set_highlighted(const bool p_highlighted) override;\n\tbool is_highlighted() const override { return _highlighted; }\n\tRef<Material> get_highlight_material() const { return _highlighted ? _highlight_mat : Ref<Material>(); }\n\tColor get_highlight_color() const override;\n\tRef<Texture2D> get_thumbnail() const override { return _thumbnail; }\n\n\tvoid set_enabled(const bool p_enabled);\n\tbool is_enabled() const { return _enabled; }\n\n\tvoid update_instance_count(const int p_amount);\n\tvoid set_instance_count(const uint32_t p_amount);\n\tuint32_t get_instance_count() const { return _instance_count; }\n\n\tvoid set_scene_file(const Ref<PackedScene> &p_scene_file);\n\tRef<PackedScene> get_scene_file() const { return _packed_scene; }\n\tbool is_scene_file_pending() const { return _pending_meshes.size() > 0; }\n\tvoid commit_meshes();\n\tvoid set_generated_type(const GenType p_type);\n\tGenType get_generated_type() const { return _generated_type; }\n\tRef<Mesh> get_mesh(const int p_lod = 0) const;\n\tvoid set_thumbnail(Ref<Texture2D> p_tex) { _thumbnail = p_tex; }\n\tvoid set_height_offset(const real_t p_offset);\n\treal_t get_height_offset() const { return _height_offset; }\n\tvoid set_density(const real_t p_density);\n\treal_t get_density() const { return _density; }\n\tvoid set_cast_shadows(const ShadowCasting p_cast_shadows);\n\tShadowCasting get_cast_shadows() const { return _cast_shadows; };\n\tShadowCasting get_lod_cast_shadows(const int p_lod_id) const;\n\tvoid set_visibility_layers(const uint32_t p_layers);\n\tuint32_t get_visibility_layers() const { return _visibility_layers; }\n\tvoid set_material_override(const Ref<Material> &p_material);\n\tRef<Material> get_material_override() const { return _material_override; }\n\tvoid set_material_overlay(const Ref<Material> &p_material);\n\tRef<Material> get_material_overlay() const { return _material_overlay; }\n\n\tvoid set_generated_faces(const int p_count);\n\tint get_generated_faces() const { return _generated_faces; }\n\tvoid set_generated_size(const Vector2 &p_size);\n\tVector2 get_generated_size() const { return _generated_size; }\n\n\tint get_lod_count() const { return _get_meshes().size(); }\n\tvoid set_last_lod(const int p_lod);\n\tint get_last_lod() const { return _last_lod; }\n\tvoid set_last_shadow_lod(const int p_lod);\n\tint get_last_shadow_lod() const { return _last_shadow_lod; }\n\tvoid set_shadow_impostor(const int p_lod);\n\tint get_shadow_impostor() const { return _shadow_impostor; }\n\n\tvoid set_lod_range(const int p_lod, const real_t p_distance);\n\treal_t get_lod_range(const int p_lod) const;\n\treal_t get_lod_range_begin(const int p_lod) const;\n\treal_t get_lod_range_end(const int p_lod) const;\n\tvoid set_lod0_range(const real_t p_distance) { set_lod_range(0, p_distance); }\n\treal_t get_lod0_range() const { return _lod_ranges[0]; }\n\tvoid set_lod1_range(const real_t p_distance) { set_lod_range(1, p_distance); }\n\treal_t get_lod1_range() const { return _lod_ranges[1]; }\n\tvoid set_lod2_range(const real_t p_distance) { set_lod_range(2, p_distance); }\n\treal_t get_lod2_range() const { return _lod_ranges[2]; }\n\tvoid set_lod3_range(const real_t p_distance) { set_lod_range(3, p_distance); }\n\treal_t get_lod3_range() const { return _lod_ranges[3]; }\n\tvoid set_lod4_range(const real_t p_distance) { set_lod_range(4, p_distance); }\n\treal_t get_lod4_range() const { return _lod_ranges[4]; }\n\tvoid set_lod5_range(const real_t p_distance) { set_lod_range(5, p_distance); }\n\treal_t get_lod5_range() const { return _lod_ranges[5]; }\n\tvoid set_lod6_range(const real_t p_distance) { set_lod_range(6, p_distance); }\n\treal_t get_lod6_range() const { return _lod_ranges[6]; }\n\tvoid set_lod7_range(const real_t p_distance) { set_lod_range(7, p_distance); }\n\treal_t get_lod7_range() const { return _lod_ranges[7]; }\n\tvoid set_lod8_range(const real_t p_distance) { set_lod_range(8, p_distance); }\n\treal_t get_lod8_range() const { return _lod_ranges[8]; }\n\tvoid set_lod9_range(const real_t p_distance) { set_lod_range(9, p_distance); }\n\treal_t get_lod9_range() const { return _lod_ranges[9]; }\n\tvoid set_fade_margin(const real_t p_fade_margin);\n\treal_t get_fade_margin() const { return _fade_margin; };\n\nprotected:\n\tvoid _validate_property(PropertyInfo &p_property) const;\n\tbool _property_can_revert(const StringName &p_name) const;\n\tbool _property_get_revert(const StringName &p_name, Variant &r_property) const;\n\tstatic void _bind_methods();\n};\n\nVARIANT_ENUM_CAST(Terrain3DMeshAsset::GenType);\n\ninline TypedArray<Mesh> Terrain3DMeshAsset::_get_meshes() const {\n\tif (!_pending_meshes.is_empty()) {\n\t\treturn _pending_meshes;\n\t}\n\treturn _meshes;\n}\n\n#endif // TERRAIN3D_MESH_ASSET_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_mesher.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/rendering_server.hpp>\n#include <godot_cpp/classes/world3d.hpp>\n#include <godot_cpp/core/version.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_mesher.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\nvoid Terrain3DMesher::_generate_mesh_types() {\n\t_clear_mesh_types();\n\tLOG(INFO, \"Generating all Mesh segments for clipmap of size \", _mesh_size);\n\t// Create initial set of Mesh blocks to build the clipmap\n\t// # 0 TILE - mesh_size x mesh_size tiles\n\t_mesh_rids.push_back(_generate_mesh(V2I(_mesh_size)));\n\t// # 1 EDGE_A - 2 by (mesh_size * 4 + 8) strips to bridge LOD transitions along +-Z axis\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(2, _mesh_size * 4 + 8)));\n\t// # 2 EDGE_B - (mesh_size * 4 + 4) by 2 strips to bridge LOD transitions along +-X axis\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(_mesh_size * 4 + 4, 2)));\n\t// # 3 FILL_A - 4 by mesh_size\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(4, _mesh_size)));\n\t// # 4 FILL_B - mesh_size by 4\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(_mesh_size, 4)));\n\t// # 5 STANDARD_TRIM_A - 2 by (mesh_size * 4 + 2) strips for LOD0 +-Z axis edge\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(2, _mesh_size * 4 + 2), true));\n\t// # 6 STANDARD_TRIM_B - (mesh_size * 4 + 2) by 2 strips for LOD0 +-X axis edge\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(_mesh_size * 4 + 2, 2), true));\n\t// # 7 STANDARD_TILE - mesh_size x mesh_size tiles\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(_mesh_size, _mesh_size), true));\n\t// # 8 STANDARD_EDGE_A - 2 by (mesh_size * 4 + 8) strips to bridge LOD transitions along +-Z axis\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(2, _mesh_size * 4 + 8), true));\n\t// # 9 STANDARD_EDGE_B - (mesh_size * 4 + 4) by 2 strips to bridge LOD transitions along +-X axis\n\t_mesh_rids.push_back(_generate_mesh(Vector2i(_mesh_size * 4 + 4, 2), true));\n\treturn;\n}\n\nRID Terrain3DMesher::_generate_mesh(const Vector2i &p_size, const bool p_standard_grid) {\n\tPackedVector3Array vertices;\n\tPackedInt32Array indices;\n\tAABB aabb = AABB(V3_ZERO, Vector3(p_size.x, 0.1f, p_size.y));\n\tLOG(DEBUG, \"Generating verticies and indices for a\", p_standard_grid ? \" symmetric \" : \" standard \", \"grid mesh of width: \", p_size.x, \" and height: \", p_size.y);\n\n\t// Generate vertices\n\tfor (int y = 0; y <= p_size.y; ++y) {\n\t\tfor (int x = 0; x <= p_size.x; ++x) {\n\t\t\t// Match GDScript vertex definitions\n\t\t\tvertices.push_back(Vector3(x, 0.f, y)); // bottom-left\n\t\t}\n\t}\n\n\t// Generate indices for quads with alternating diagonals\n\tfor (int y = 0; y < p_size.y; ++y) {\n\t\tfor (int x = 0; x < p_size.x; ++x) {\n\t\t\tint bottomLeft = y * (p_size.x + 1) + x;\n\t\t\tint bottomRight = bottomLeft + 1;\n\t\t\tint topLeft = (y + 1) * (p_size.x + 1) + x;\n\t\t\tint topRight = topLeft + 1;\n\n\t\t\tif ((x + y) % 2 == 0 || p_standard_grid) {\n\t\t\t\tindices.push_back(bottomLeft);\n\t\t\t\tindices.push_back(topRight);\n\t\t\t\tindices.push_back(topLeft);\n\n\t\t\t\tindices.push_back(bottomLeft);\n\t\t\t\tindices.push_back(bottomRight);\n\t\t\t\tindices.push_back(topRight);\n\t\t\t} else {\n\t\t\t\tindices.push_back(bottomLeft);\n\t\t\t\tindices.push_back(bottomRight);\n\t\t\t\tindices.push_back(topLeft);\n\n\t\t\t\tindices.push_back(topLeft);\n\t\t\t\tindices.push_back(bottomRight);\n\t\t\t\tindices.push_back(topRight);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn _instantiate_mesh(vertices, indices, aabb);\n}\n\nRID Terrain3DMesher::_instantiate_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, const AABB &p_aabb) {\n\tArray arrays;\n\tarrays.resize(RenderingServer::ARRAY_MAX);\n\tarrays[RenderingServer::ARRAY_VERTEX] = p_vertices;\n\tarrays[RenderingServer::ARRAY_INDEX] = p_indices;\n\n\tPackedVector3Array normals;\n\tnormals.resize(p_vertices.size());\n\tnormals.fill(V3_UP);\n\tarrays[RenderingServer::ARRAY_NORMAL] = normals;\n\n\tPackedFloat32Array tangents;\n\ttangents.resize(p_vertices.size() * 4);\n\ttangents.fill(0.0f);\n\tarrays[RenderingServer::ARRAY_TANGENT] = tangents;\n\n\tLOG(DEBUG, \"Creating mesh via the Rendering server\");\n\tRID mesh = RS->mesh_create();\n\tRS->mesh_add_surface_from_arrays(mesh, RenderingServer::PRIMITIVE_TRIANGLES, arrays);\n\n\tLOG(DEBUG, \"Setting custom aabb: \", p_aabb.position, \", \", p_aabb.size);\n\tRS->mesh_set_custom_aabb(mesh, p_aabb);\n\tRS->mesh_surface_set_material(mesh, 0, _material.is_valid() ? _material : RID());\n\n\treturn mesh;\n}\n\nvoid Terrain3DMesher::_generate_clipmap() {\n\t_clear_clipmap();\n\t_generate_mesh_types();\n\t_generate_offset_data();\n\tLOG(DEBUG, \"Creating instances for all mesh segments for clipmap of size \", _mesh_size, \" for \", _lods, \" LODs\");\n\tfor (int level = 0; level < _lods + _tessellation_level; level++) {\n\t\tArray lod;\n\t\t// 12 Tiles LOD1+, 16 for LOD0\n\t\tArray tile_rids;\n\t\tint tile_amount = (level == 0) ? 16 : 12;\n\n\t\tfor (int i = 0; i < tile_amount; i++) {\n\t\t\tRID tile_rid = RS->instance_create2(_mesh_rids[level == 0 ? STANDARD_TILE : TILE], _scenario);\n\t\t\ttile_rids.append(tile_rid);\n\t\t}\n\t\tlod.append(tile_rids); // index 0 TILE\n\n\t\t// 4 Edges present on all LODs\n\t\tArray edge_a_rids;\n\t\tfor (int i = 0; i < 2; i++) {\n\t\t\tRID edge_a_rid = RS->instance_create2(_mesh_rids[level == 0 ? STANDARD_EDGE_A : EDGE_A], _scenario);\n\t\t\tedge_a_rids.append(edge_a_rid);\n\t\t}\n\t\tlod.append(edge_a_rids); // index 1 EDGE_A\n\n\t\tArray edge_b_rids;\n\t\tfor (int i = 0; i < 2; i++) {\n\t\t\tRID edge_b_rid = RS->instance_create2(_mesh_rids[level == 0 ? STANDARD_EDGE_B : EDGE_B], _scenario);\n\t\t\tedge_b_rids.append(edge_b_rid);\n\t\t}\n\t\tlod.append(edge_b_rids); // index 2 EDGE_B\n\n\t\t// Fills only present on LODs 1+\n\t\tif (level > 0) {\n\t\t\tArray fill_a_rids;\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tRID fill_a_rid = RS->instance_create2(_mesh_rids[FILL_A], _scenario);\n\t\t\t\tfill_a_rids.append(fill_a_rid);\n\t\t\t}\n\t\t\tlod.append(fill_a_rids); // index 4 FILL_A\n\n\t\t\tArray fill_b_rids;\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tRID fill_b_rid = RS->instance_create2(_mesh_rids[FILL_B], _scenario);\n\t\t\t\tfill_b_rids.append(fill_b_rid);\n\t\t\t}\n\t\t\tlod.append(fill_b_rids); // index 5 FILL_B\n\t\t\t// Trims only on LOD 0 These share the indices of the fills for the offsets.\n\t\t\t// When snapping LOD 0 Trim a/b positions are looked up instead of Fill a/b\n\t\t} else {\n\t\t\tArray trim_a_rids;\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tRID trim_a_rid = RS->instance_create2(_mesh_rids[STANDARD_TRIM_A], _scenario);\n\t\t\t\ttrim_a_rids.append(trim_a_rid);\n\t\t\t}\n\t\t\tlod.append(trim_a_rids); // index 4 TRIM_A\n\n\t\t\tArray trim_b_rids;\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tRID trim_b_rid = RS->instance_create2(_mesh_rids[STANDARD_TRIM_B], _scenario);\n\t\t\t\ttrim_b_rids.append(trim_b_rid);\n\t\t\t}\n\t\t\tlod.append(trim_b_rids); // index 5 TRIM_B\n\t\t}\n\n\t\t// Append LOD to _lod_rids array\n\t\t_clipmap_rids.append(lod);\n\t}\n}\n\n// Precomputes all instance offset data into lookup arrays that match created instances.\n// All meshes are created with 0,0 as their origin and grow along +xz. Offsets account for this.\nvoid Terrain3DMesher::_generate_offset_data() {\n\tLOG(INFO, \"Computing all clipmap instance positioning offsets\");\n\t_tile_pos_lod_0.clear();\n\t_trim_a_pos.clear();\n\t_trim_b_pos.clear();\n\t_edge_pos.clear();\n\t_fill_a_pos.clear();\n\t_fill_b_pos.clear();\n\t_tile_pos.clear();\n\n\t// LOD0 Tiles: Full 4x4 Grid of mesh size tiles\n\t_tile_pos_lod_0.push_back(Vector3(0, 0, _mesh_size));\n\t_tile_pos_lod_0.push_back(Vector3(_mesh_size, 0, _mesh_size));\n\t_tile_pos_lod_0.push_back(Vector3(_mesh_size, 0, 0));\n\t_tile_pos_lod_0.push_back(Vector3(_mesh_size, 0, -_mesh_size));\n\t_tile_pos_lod_0.push_back(Vector3(_mesh_size, 0, -_mesh_size * 2));\n\t_tile_pos_lod_0.push_back(Vector3(0, 0, -_mesh_size * 2));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size, 0, -_mesh_size * 2));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size * 2, 0, -_mesh_size * 2));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size * 2, 0, -_mesh_size));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size * 2, 0, 0));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size * 2, 0, _mesh_size));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size, 0, _mesh_size));\n\t// Inner tiles\n\t_tile_pos_lod_0.push_back(V3_ZERO);\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size, 0, 0));\n\t_tile_pos_lod_0.push_back(Vector3(0, 0, -_mesh_size));\n\t_tile_pos_lod_0.push_back(Vector3(-_mesh_size, 0, -_mesh_size));\n\n\t// LOD0 Trims: Fixed 2 unit wide ring around LOD0 tiles.\n\t_trim_a_pos.push_back(Vector3(_mesh_size * 2, 0, -_mesh_size * 2));\n\t_trim_a_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, -_mesh_size * 2 - 2));\n\t_trim_b_pos.push_back(Vector3(-_mesh_size * 2, 0, -_mesh_size * 2 - 2));\n\t_trim_b_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, _mesh_size * 2));\n\n\t// LOD1+: 4x4 Ring of mesh size tiles, with one 2 unit wide gap on each axis for fill meshes.\n\t_tile_pos.push_back(Vector3(2, 0, _mesh_size + 2));\n\t_tile_pos.push_back(Vector3(_mesh_size + 2, 0, _mesh_size + 2));\n\t_tile_pos.push_back(Vector3(_mesh_size + 2, 0, -2));\n\t_tile_pos.push_back(Vector3(_mesh_size + 2, 0, -_mesh_size - 2));\n\t_tile_pos.push_back(Vector3(_mesh_size + 2, 0, -_mesh_size * 2 - 2));\n\t_tile_pos.push_back(Vector3(-2, 0, -_mesh_size * 2 - 2));\n\t_tile_pos.push_back(Vector3(-_mesh_size - 2, 0, -_mesh_size * 2 - 2));\n\t_tile_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, -_mesh_size * 2 - 2));\n\t_tile_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, -_mesh_size + 2));\n\t_tile_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, +2));\n\t_tile_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, _mesh_size + 2));\n\t_tile_pos.push_back(Vector3(-_mesh_size + 2, 0, _mesh_size + 2));\n\n\t// Edge offsets set edge pair positions to either both before, straddle, or both after\n\t// Depending on current LOD position within the next LOD, (via test_x or test_z in snap())\n\t_offset_a = real_t(_mesh_size * 2) + 2.f;\n\t_offset_b = real_t(_mesh_size * 2) + 4.f;\n\t_offset_c = real_t(_mesh_size * 2) + 6.f;\n\t_edge_pos.push_back(Vector3(_offset_a, _offset_a, -_offset_b));\n\t_edge_pos.push_back(Vector3(_offset_b, -_offset_b, -_offset_c));\n\n\t// Fills: Occupies the gaps between tiles for LOD1+ to complete the ring.\n\t_fill_a_pos.push_back(Vector3(_mesh_size - 2, 0, -_mesh_size * 2 - 2));\n\t_fill_a_pos.push_back(Vector3(-_mesh_size - 2, 0, _mesh_size + 2));\n\t_fill_b_pos.push_back(Vector3(_mesh_size + 2, 0, _mesh_size - 2));\n\t_fill_b_pos.push_back(Vector3(-_mesh_size * 2 - 2, 0, -_mesh_size - 2));\n\n\treturn;\n}\n\n// Frees all clipmap instance RIDs. Mesh rids must be freed separately.\nvoid Terrain3DMesher::_clear_clipmap() {\n\tLOG(INFO, \"Freeing all clipmap instances\");\n\tfor (const Array &lod_array : _clipmap_rids) {\n\t\tfor (const Array &mesh_array : lod_array) {\n\t\t\tfor (const RID &rid : mesh_array) {\n\t\t\t\tRS->free_rid(rid);\n\t\t\t}\n\t\t}\n\t}\n\t_clipmap_rids.clear();\n\treturn;\n}\n\n// Frees all Mesh RIDs use for clipmap instances.\nvoid Terrain3DMesher::_clear_mesh_types() {\n\tLOG(INFO, \"Freeing all clipmap meshes\");\n\tfor (const RID &rid : _mesh_rids) {\n\t\tRS->free_rid(rid);\n\t}\n\t_mesh_rids.clear();\n\treturn;\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DMesher::initialize(Terrain3D *p_terrain, const int p_mesh_size, const int p_lods, const int p_tessellation_level,\n\t\tconst real_t p_vertex_spacing, const RID &p_material, const uint32_t p_render_layers) {\n\tif (p_terrain) {\n\t\t_terrain = p_terrain;\n\t} else {\n\t\treturn;\n\t}\n\tif (!_terrain->is_inside_world()) {\n\t\tLOG(DEBUG, \"Terrain3D's world3D is null\");\n\t\treturn;\n\t}\n\n\tLOG(INFO, \"Initializing GeoMesh\");\n\t_scenario = _terrain->get_world_3d()->get_scenario();\n\t_material = p_material;\n\t_lods = p_lods;\n\t_tessellation_level = p_tessellation_level;\n\t_mesh_size = p_mesh_size;\n\t_vertex_spacing = p_vertex_spacing;\n\t_render_layers = p_render_layers;\n\t_generate_clipmap();\n\tupdate();\n\tupdate_aabbs();\n\treset_target_position();\n\tsnap();\n}\n\nvoid Terrain3DMesher::destroy() {\n\tLOG(INFO, \"Destroying clipmap\");\n\t_clear_clipmap();\n\t_clear_mesh_types();\n\t_tile_pos_lod_0.clear();\n\t_trim_a_pos.clear();\n\t_trim_b_pos.clear();\n\t_edge_pos.clear();\n\t_fill_a_pos.clear();\n\t_fill_b_pos.clear();\n}\n\nvoid Terrain3DMesher::snap() {\n\tIS_INIT(VOID);\n\t// Always update target position in shader\n\tVector3 target_pos = _terrain->get_clipmap_target_position();\n\tif (_material.is_valid()) {\n\t\tRS->material_set_param(_material, \"_target_pos\", target_pos);\n\t}\n\t// If clipmap target hasn't moved enough, skip\n\tVector2 target_pos_2d = v3v2(target_pos);\n\treal_t tessellation_density = 1.f / pow(2.f, _tessellation_level);\n\treal_t vertex_spacing = _vertex_spacing * tessellation_density;\n\tif (MAX(std::abs(_last_target_position.x - target_pos_2d.x), std::abs(_last_target_position.y - target_pos_2d.y)) < vertex_spacing) {\n\t\treturn;\n\t}\n\n\t// Recenter terrain on the target\n\t_last_target_position = target_pos_2d;\n\tVector3 snapped_pos = (target_pos / vertex_spacing).floor() * vertex_spacing;\n\tVector3 pos = V3_ZERO;\n\tfor (int lod = 0; lod < _clipmap_rids.size(); ++lod) {\n\t\treal_t snap_step = pow(2.f, lod + 1.f) * vertex_spacing;\n\t\tVector3 lod_scale = Vector3(pow(2.f, lod) * vertex_spacing, 1.f, pow(2.f, lod) * vertex_spacing);\n\n\t\t// Snap pos.xz\n\t\tpos.x = round(snapped_pos.x / snap_step) * snap_step;\n\t\tpos.z = round(snapped_pos.z / snap_step) * snap_step;\n\n\t\tLOG(EXTREME, \"Snapping clipmap LOD\", lod, \" to position: \", pos);\n\n\t\t// test_x and test_z for edge strip positions\n\t\treal_t next_snap_step = pow(2.f, lod + 2.f) * vertex_spacing;\n\t\treal_t next_x = round(snapped_pos.x / next_snap_step) * next_snap_step;\n\t\treal_t next_z = round(snapped_pos.z / next_snap_step) * next_snap_step;\n\t\tint test_x = CLAMP(int(round((pos.x - next_x) / snap_step)) + 1, 0, 2);\n\t\tint test_z = CLAMP(int(round((pos.z - next_z) / snap_step)) + 1, 0, 2);\n\t\tArray lod_array = _clipmap_rids[lod];\n\t\tfor (int mesh = 0; mesh < lod_array.size(); ++mesh) {\n\t\t\tArray mesh_array = lod_array[mesh];\n\t\t\tfor (int instance = 0; instance < mesh_array.size(); ++instance) {\n\t\t\t\tTransform3D t = Transform3D();\n\t\t\t\tswitch (mesh) {\n\t\t\t\t\tcase TILE: {\n\t\t\t\t\t\tt.origin = (lod == 0) ? _tile_pos_lod_0[instance] : _tile_pos[instance];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase EDGE_A: {\n\t\t\t\t\t\tVector3 edge_pos_instance = _edge_pos[instance];\n\t\t\t\t\t\tt.origin.z -= _offset_a + (test_z * 2.f);\n\t\t\t\t\t\tt.origin.x = edge_pos_instance[test_x];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase EDGE_B: {\n\t\t\t\t\t\tVector3 edge_pos_instance = _edge_pos[instance];\n\t\t\t\t\t\tt.origin.z = edge_pos_instance[test_z];\n\t\t\t\t\t\tt.origin.x -= _offset_a;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t// LOD0 doesnt have fills so the trims share the same index.\n\t\t\t\t\tcase FILL_A: {\n\t\t\t\t\t\tif (lod > 0) {\n\t\t\t\t\t\t\tt.origin = _fill_a_pos[instance];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tt.origin = _trim_a_pos[instance];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase FILL_B: {\n\t\t\t\t\t\tif (lod > 0) {\n\t\t\t\t\t\t\tt.origin = _fill_b_pos[instance];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tt.origin = _trim_b_pos[instance];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tt = t.scaled(lod_scale);\n\t\t\t\tt.origin += pos;\n\t\t\t\tRS->instance_set_transform(mesh_array[instance], t);\n\t\t\t\tRS->instance_teleport(mesh_array[instance]);\n\t\t\t}\n\t\t}\n\t}\n\treturn;\n}\n\n// Iterates over every instance of every mesh and updates all properties.\nvoid Terrain3DMesher::update() {\n\tIS_INIT(VOID);\n\tif (!_terrain->is_inside_world()) {\n\t\tLOG(DEBUG, \"Terrain3D's world3D is null\");\n\t\treturn;\n\t}\n\tbool baked_light;\n\tbool dynamic_gi;\n\tswitch (_terrain->get_gi_mode()) {\n\t\tcase GeometryInstance3D::GI_MODE_DISABLED: {\n\t\t\tbaked_light = false;\n\t\t\tdynamic_gi = false;\n\t\t} break;\n\t\tcase GeometryInstance3D::GI_MODE_DYNAMIC: {\n\t\t\tbaked_light = false;\n\t\t\tdynamic_gi = true;\n\t\t} break;\n\t\tcase GeometryInstance3D::GI_MODE_STATIC:\n\t\tdefault: {\n\t\t\tbaked_light = true;\n\t\t\tdynamic_gi = false;\n\t\t} break;\n\t}\n\n\tRenderingServer::ShadowCastingSetting cast_shadows = _terrain->get_cast_shadows();\n\tbool visible = _terrain->is_visible_in_tree();\n\n\tLOG(INFO, \"Updating all mesh instances for \", _clipmap_rids.size(), \" LODs\");\n\tfor (const Array &lod_array : _clipmap_rids) {\n\t\tfor (const Array &mesh_array : lod_array) {\n\t\t\tfor (const RID &rid : mesh_array) {\n\t\t\t\tRS->instance_set_visible(rid, visible);\n\t\t\t\tRS->instance_set_scenario(rid, _scenario);\n\t\t\t\tRS->instance_set_layer_mask(rid, _render_layers);\n\t\t\t\tRS->instance_geometry_set_cast_shadows_setting(rid, cast_shadows);\n\t\t\t\tRS->instance_geometry_set_flag(rid, RenderingServer::INSTANCE_FLAG_USE_BAKED_LIGHT, baked_light);\n\t\t\t\tRS->instance_geometry_set_flag(rid, RenderingServer::INSTANCE_FLAG_USE_DYNAMIC_GI, dynamic_gi);\n\t\t\t}\n\t\t}\n\t}\n\treturn;\n}\n\n// Iterates over all meshes and updates their AABBs\n// All instances of each mesh inherit the updated AABB\n// Defaults to using the terrain parameters\nvoid Terrain3DMesher::update_aabbs(const real_t p_cull_margin, const Vector2 &p_height_range) {\n\tIS_DATA_INIT(VOID);\n\tLOG(INFO, \"Updating \", _mesh_rids.size(), \" meshes AABBs\")\n\treal_t cull_margin;\n\tVector2 height_range;\n\tif (p_cull_margin < 0.f) {\n\t\tcull_margin = _terrain->get_cull_margin();\n\t} else {\n\t\tcull_margin = p_cull_margin;\n\t}\n\tif (p_height_range.x == FLT_MAX) {\n\t\theight_range = _terrain->get_data()->get_height_range();\n\t} else {\n\t\theight_range = p_height_range;\n\t}\n\theight_range.y += std::abs(height_range.x);\n\tfor (const RID &rid : _mesh_rids) {\n\t\tAABB aabb = RS->mesh_get_custom_aabb(rid);\n\t\taabb.position.y = height_range.x - cull_margin;\n\t\taabb.size.y = height_range.y + cull_margin * 2.f;\n\t\tRS->mesh_set_custom_aabb(rid, aabb);\n\t}\n\treturn;\n}\n"
  },
  {
    "path": "src/terrain_3d_mesher.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_MESHER_CLASS_H\n#define TERRAIN3D_MESHER_CLASS_H\n\n#include \"constants.h\"\n\nclass Terrain3D;\n\nclass Terrain3DMesher {\n\tCLASS_NAME_STATIC(\"Terrain3DMesher\");\n\npublic: // Constants\n\tenum MeshType {\n\t\tTILE,\n\t\tEDGE_A,\n\t\tEDGE_B,\n\t\tFILL_A,\n\t\tFILL_B,\n\t\tSTANDARD_TRIM_A,\n\t\tSTANDARD_TRIM_B,\n\t\tSTANDARD_TILE,\n\t\tSTANDARD_EDGE_A,\n\t\tSTANDARD_EDGE_B,\n\t};\n\nprivate:\n\tTerrain3D *_terrain = nullptr;\n\tRID _scenario = RID();\n\tVector2 _last_target_position = V2_MAX;\n\n\tArray _mesh_rids;\n\t// LODs -> MeshTypes -> Instances\n\tArray _clipmap_rids;\n\n\t// Mesh offset data\n\t// LOD0 only\n\tPackedVector3Array _trim_a_pos;\n\tPackedVector3Array _trim_b_pos;\n\tPackedVector3Array _tile_pos_lod_0;\n\t// LOD1 +\n\tPackedVector3Array _fill_a_pos;\n\tPackedVector3Array _fill_b_pos;\n\tPackedVector3Array _tile_pos;\n\t// All LOD Levels\n\treal_t _offset_a = 0.f;\n\treal_t _offset_b = 0.f;\n\treal_t _offset_c = 0.f;\n\tPackedVector3Array _edge_pos;\n\n\tRID _material;\n\tint _tessellation_level = 0;\n\tint _lods = 0;\n\tint _mesh_size = 0;\n\treal_t _vertex_spacing = 1.f;\n\tuint32_t _render_layers = 1u; // Bit 1 only\n\n\tvoid _generate_mesh_types();\n\tRID _generate_mesh(const Vector2i &p_size, const bool p_standard_grid = false);\n\tRID _instantiate_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, const AABB &p_aabb);\n\tvoid _generate_clipmap();\n\tvoid _generate_offset_data();\n\n\tvoid _clear_clipmap();\n\tvoid _clear_mesh_types();\n\npublic:\n\tTerrain3DMesher() {}\n\t~Terrain3DMesher() { destroy(); }\n\n\tvoid initialize(Terrain3D *p_terrain, const int p_mesh_size, const int p_lods, const int p_tessellation_level,\n\t\t\tconst real_t p_vertex_spacing, const RID &p_material, const uint32_t p_render_layers);\n\tvoid destroy();\n\n\tvoid snap();\n\tvoid reset_target_position() { _last_target_position = V2_MAX; }\n\tvoid update();\n\tvoid update_aabbs(const real_t p_cull_margin = -1.f, const Vector2 &p_height_range = V2_MAX);\n\n\tvoid set_material(const RID &p_material) { _material = p_material; }\n\tRID get_material() const { return _material; }\n\tvoid set_lods(const int p_lods) { _lods = p_lods; }\n\tint get_lods() const { return _lods; }\n\tvoid set_tessellation_level(const int p_level) { _tessellation_level = p_level; }\n\tint get_tessellation_level() const { return _tessellation_level; }\n\tvoid set_mesh_size(const int p_size) { _mesh_size = p_size; }\n\tint get_mesh_size() const { return _mesh_size; }\n\tvoid set_vertex_spacing(const real_t p_spacing) { _vertex_spacing = p_spacing; }\n\treal_t get_vertex_spacing() const { return _vertex_spacing; }\n\tvoid set_render_layers(const uint32_t p_layers) { _render_layers = p_layers; }\n\tuint32_t get_render_layers() const { return _render_layers; }\n};\n// Inline Functions\n\n#endif // TERRAIN3D_MESHER_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_region.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/resource_saver.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d_data.h\"\n#include \"terrain_3d_region.h\"\n#include \"terrain_3d_util.h\"\n\n/////////////////////\n// Public Functions\n/////////////////////\n\nvoid Terrain3DRegion::clear() {\n\t_version = 0.8f;\n\t_region_size = 0;\n\t_height_range = V2_ZERO;\n\t_height_map.unref();\n\t_control_map.unref();\n\t_color_map.unref();\n\t_instances.clear();\n\t_vertex_spacing = 1.f;\n\t_deleted = false;\n\t_edited = false;\n\t_modified = false;\n\t_location = V2I_MAX;\n}\n\nvoid Terrain3DRegion::set_version(const real_t p_version) {\n\treal_t version = CLAMP(p_version, 0.8f, 100.f);\n\tif (_version == version) {\n\t\treturn;\n\t}\n\t// Mark modified if already initialized and we get a new value\n\tif (_version > 0.8f) {\n\t\t_modified = true;\n\t}\n\t_version = version;\n\tLOG(INFO, vformat(\"%.3f\", _version));\n\tif (_version < Terrain3DData::CURRENT_DATA_VERSION) {\n\t\tLOG(WARN, \"Region Data \", get_path(), \" version \", vformat(\"%.3f\", _version),\n\t\t\t\t\" will be updated to \", vformat(\"%.3f\", Terrain3DData::CURRENT_DATA_VERSION), \" upon save\");\n\t}\n}\n\nvoid Terrain3DRegion::set_region_size(const int p_region_size) {\n\tif (!is_valid_region_size(p_region_size)) {\n\t\tLOG(ERROR, \"Invalid region size: \", p_region_size, \". Must be power of 2, 64-2048\");\n\t\treturn;\n\t}\n\t// Mark modified if already initialized and we get a new value\n\tif (_region_size > 0 && _region_size != p_region_size) {\n\t\t_modified = true;\n\t}\n\tSET_IF_DIFF(_region_size, p_region_size);\n\tLOG(INFO, \"Setting region \", _location, \" size: \", p_region_size);\n}\n\nvoid Terrain3DRegion::set_map(const MapType p_map_type, const Ref<Image> &p_image) {\n\tswitch (p_map_type) {\n\t\tcase TYPE_HEIGHT:\n\t\t\tset_height_map(p_image);\n\t\t\tbreak;\n\t\tcase TYPE_CONTROL:\n\t\t\tset_control_map(p_image);\n\t\t\tbreak;\n\t\tcase TYPE_COLOR:\n\t\t\tset_color_map(p_image);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tLOG(ERROR, \"Requested map type is invalid\");\n\t\t\tbreak;\n\t}\n}\n\nRef<Image> Terrain3DRegion::get_map(const MapType p_map_type) const {\n\tswitch (p_map_type) {\n\t\tcase TYPE_HEIGHT:\n\t\t\treturn get_height_map();\n\t\tcase TYPE_CONTROL:\n\t\t\treturn get_control_map();\n\t\tcase TYPE_COLOR:\n\t\t\treturn get_color_map();\n\t\tdefault:\n\t\t\tLOG(ERROR, \"Requested map type \", p_map_type, \", is invalid\");\n\t\t\treturn Ref<Image>();\n\t}\n}\n\nImage *Terrain3DRegion::get_map_ptr(const MapType p_map_type) const {\n\tswitch (p_map_type) {\n\t\tcase TYPE_HEIGHT:\n\t\t\treturn *_height_map;\n\t\tcase TYPE_CONTROL:\n\t\t\treturn *_control_map;\n\t\tcase TYPE_COLOR:\n\t\t\treturn *_color_map;\n\t\tdefault:\n\t\t\tLOG(ERROR, \"Requested map type \", p_map_type, \", is invalid\");\n\t\t\treturn nullptr;\n\t}\n}\n\nvoid Terrain3DRegion::set_maps(const TypedArray<Image> &p_maps) {\n\tif (p_maps.size() != TYPE_MAX) {\n\t\tLOG(ERROR, \"Expected \", TYPE_MAX - 1, \" maps. Received \", p_maps.size());\n\t\treturn;\n\t}\n\t_region_size = 0;\n\tset_height_map(p_maps[TYPE_HEIGHT]);\n\tset_control_map(p_maps[TYPE_CONTROL]);\n\tset_color_map(p_maps[TYPE_COLOR]);\n}\n\nTypedArray<Image> Terrain3DRegion::get_maps() const {\n\tLOG(INFO, \"Retrieving maps from region: \", _location);\n\tTypedArray<Image> maps;\n\tmaps.push_back(_height_map);\n\tmaps.push_back(_control_map);\n\tmaps.push_back(_color_map);\n\treturn maps;\n}\n\nvoid Terrain3DRegion::set_height_map(const Ref<Image> &p_map) {\n\tSET_IF_DIFF(_height_map, p_map);\n\tLOG(INFO, \"Setting height map for region: \", (_location.x != INT32_MAX) ? String(_location) : \"(new)\");\n\tif (_region_size == 0 && p_map.is_valid()) {\n\t\tset_region_size(p_map->get_width());\n\t}\n\tRef<Image> map = sanitize_map(TYPE_HEIGHT, p_map);\n\t// If already initialized and receiving a new map, or the map was sanitized\n\tif (_height_map.is_valid() && _height_map != p_map || map != p_map) {\n\t\t_modified = true;\n\t}\n\t_height_map = map;\n\tcalc_height_range();\n}\n\nvoid Terrain3DRegion::set_control_map(const Ref<Image> &p_map) {\n\tSET_IF_DIFF(_control_map, p_map);\n\tLOG(INFO, \"Setting control map for region: \", (_location.x != INT32_MAX) ? String(_location) : \"(new)\");\n\tif (_region_size == 0 && p_map.is_valid()) {\n\t\tset_region_size(p_map->get_width());\n\t}\n\tRef<Image> map = sanitize_map(TYPE_CONTROL, p_map);\n\t// If already initialized and receiving a new map, or the map was sanitized\n\tif (_control_map.is_valid() && _control_map != p_map || map != p_map) {\n\t\t_modified = true;\n\t}\n\t_control_map = map;\n}\n\nvoid Terrain3DRegion::set_color_map(const Ref<Image> &p_map) {\n\tSET_IF_DIFF(_color_map, p_map);\n\tLOG(INFO, \"Setting color map for region: \", (_location.x != INT32_MAX) ? String(_location) : \"(new)\");\n\tif (_region_size == 0 && p_map.is_valid()) {\n\t\tset_region_size(p_map->get_width());\n\t}\n\tRef<Image> map = sanitize_map(TYPE_COLOR, p_map);\n\t// If already initialized and receiving a new map, or the map was sanitized\n\tif (_color_map.is_valid() && _color_map != p_map || map != p_map) {\n\t\t_modified = true;\n\t}\n\t_color_map = map;\n}\n\nvoid Terrain3DRegion::sanitize_maps() {\n\tif (_region_size == 0) { // blank region, no set_*_map has been called\n\t\tLOG(ERROR, \"Set region_size first\");\n\t\treturn;\n\t}\n\tRef<Image> map = sanitize_map(TYPE_HEIGHT, _height_map);\n\tif (_height_map != map) {\n\t\t_modified = true;\n\t}\n\t_height_map = map;\n\tmap = sanitize_map(TYPE_CONTROL, _control_map);\n\tif (_control_map != map) {\n\t\t_modified = true;\n\t}\n\t_control_map = map;\n\tmap = sanitize_map(TYPE_COLOR, _color_map);\n\tif (_color_map != map) {\n\t\t_modified = true;\n\t}\n\t_color_map = map;\n}\n\nRef<Image> Terrain3DRegion::sanitize_map(const MapType p_map_type, const Ref<Image> &p_map) const {\n\tLOG(INFO, \"Sanitizing map type: \", p_map_type, \", map: \", p_map);\n\tif (!is_valid_region_size(_region_size)) {\n\t\tLOG(ERROR, \"Invalid region size: \", _region_size, \". Set it or set a map first. Must be power of 2, 64-2048\");\n\t\treturn Ref<Image>();\n\t}\n\tconst char *type_str = TYPESTR[p_map_type];\n\tImage::Format format = FORMAT[p_map_type];\n\tColor color = COLOR[p_map_type];\n\tRef<Image> map;\n\n\tif (p_map.is_valid()) {\n\t\tif (validate_map_size(p_map)) {\n\t\t\tif (p_map->get_format() == format) {\n\t\t\t\tLOG(DEBUG, \"Map type \", type_str, \" correct format, size. Mipmaps: \", p_map->has_mipmaps());\n\t\t\t\tmap = p_map;\n\t\t\t} else {\n\t\t\t\tLOG(DEBUG, \"Provided \", type_str, \" map wrong format: \", p_map->get_format(), \". Converting copy to: \", format);\n\t\t\t\tmap.instantiate();\n\t\t\t\tmap->copy_from(p_map);\n\t\t\t\tmap->convert(format);\n\t\t\t\tif (map->get_format() != format) {\n\t\t\t\t\tLOG(DEBUG, \"Cannot convert image to format: \", format, \". Creating blank \");\n\t\t\t\t\tmap.unref();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tLOG(DEBUG, \"Provided \", type_str, \" map wrong size: \", p_map->get_size(), \". Creating blank\");\n\t\t}\n\t} else {\n\t\tLOG(DEBUG, \"No provided \", type_str, \" map. Creating blank\");\n\t}\n\tif (map.is_null()) {\n\t\tLOG(DEBUG, \"Making new image of type: \", type_str, \" and generating mipmaps: \", p_map_type == TYPE_COLOR);\n\t\treturn Util::get_filled_image(V2I(_region_size), color, p_map_type == TYPE_COLOR, format);\n\t} else {\n\t\tif (p_map_type == TYPE_COLOR && !map->has_mipmaps()) {\n\t\t\tLOG(DEBUG, \"Color map does not have mipmaps. Generating\");\n\t\t\tmap->generate_mipmaps();\n\t\t}\n\t\treturn map;\n\t}\n}\n\nbool Terrain3DRegion::validate_map_size(const Ref<Image> &p_map) const {\n\tVector2i region_sizev = p_map->get_size();\n\tif (region_sizev.x != region_sizev.y) {\n\t\tLOG(ERROR, \"Image width doesn't match height: \", region_sizev);\n\t\treturn false;\n\t}\n\tif (!is_valid_region_size(region_sizev.x) || !is_valid_region_size(region_sizev.y)) {\n\t\tLOG(ERROR, \"Invalid image size: \", region_sizev, \". Must be power of 2, 64-2048 and square\");\n\t\treturn false;\n\t}\n\tif (_region_size != region_sizev.x || _region_size != region_sizev.y) {\n\t\tLOG(ERROR, \"Image size doesn't match existing images in this region\", region_sizev);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nvoid Terrain3DRegion::set_height_range(const Vector2 &p_range) {\n\tif (differs(_height_range, p_range)) {\n\t\t// Mark modified if setting after initialization\n\t\tif (!_height_range.is_zero_approx()) {\n\t\t\t_modified = true;\n\t\t}\n\t\t_height_range = p_range;\n\t\tLOG(INFO, vformat(\"%.2v\", p_range));\n\t} else {\n\t\treturn;\n\t};\n}\n\nvoid Terrain3DRegion::calc_height_range() {\n\tVector2 range = Util::get_min_max(_height_map);\n\tif (_height_range != range) {\n\t\t_height_range = range;\n\t\t_modified = true;\n\t\tLOG(DEBUG, \"Recalculated new height range: \", _height_range, \" for region: \", (_location.x != INT32_MAX) ? String(_location) : \"(new)\", \". Marking modified\");\n\t}\n}\n\nvoid Terrain3DRegion::set_instances(const Dictionary &p_instances) {\n\tif (!_instances.is_empty() && differs(_instances, p_instances)) {\n\t\t_modified = true;\n\t}\n\tSET_IF_DIFF(_instances, p_instances);\n\tLOG(INFO, \"Region \", _location, \" setting instances ptr: \", ptr_to_str(p_instances._native_ptr()));\n}\n\nvoid Terrain3DRegion::set_location(const Vector2i &p_location) {\n\t// In the future anywhere they want to put the location might be fine, but because of region_map\n\t// We have a limitation of 32x32.\n\tif (Terrain3DData::get_region_map_index(p_location) < 0) {\n\t\tLOG(ERROR, \"Location \", p_location, \" out of bounds. Max: \",\n\t\t\t\t-Terrain3DData::REGION_MAP_SIZE / 2, \" to \", Terrain3DData::REGION_MAP_SIZE / 2 - 1);\n\t\treturn;\n\t}\n\t// Marked modified if setting after initialized\n\tif (_location < V2I_MAX && _location != p_location) {\n\t\t_modified = true;\n\t}\n\tSET_IF_DIFF(_location, p_location);\n\tLOG(INFO, \"Set location: \", p_location);\n}\n\nError Terrain3DRegion::save(const String &p_path, const bool p_16_bit) {\n\t// Initiate save to external file. The scene will save itself.\n\tif (_location.x == INT32_MAX) {\n\t\tLOG(ERROR, \"Region has not been setup. Location is INT32_MAX. Skipping \", p_path);\n\t}\n\tif (!_modified) {\n\t\tLOG(DEBUG, \"Region \", _location, \" not modified. Skipping \", p_path);\n\t\treturn ERR_SKIP;\n\t}\n\tif (p_path.is_empty() && get_path().is_empty()) {\n\t\tLOG(ERROR, \"No valid path provided\");\n\t\treturn ERR_FILE_NOT_FOUND;\n\t}\n\tif (!p_path.is_empty()) {\n\t\tLOG(DEBUG, \"Setting file path for region \", _location, \" to \", p_path);\n\t\ttake_over_path(p_path);\n\t\t// Set region path and take over the path from any other cached resources,\n\t\t// incuding those in the undo queue\n\t}\n\tLOG(MESG, \"Writing\", (p_16_bit) ? \" 16-bit\" : \"\", \" region \", _location, \" to \", get_path());\n\tset_version(Terrain3DData::CURRENT_DATA_VERSION);\n\tError err = OK;\n\tif (p_16_bit) {\n\t\tRef<Image> original_map;\n\t\toriginal_map.instantiate();\n\t\toriginal_map->copy_from(_height_map);\n\t\t_height_map->convert(Image::FORMAT_RH);\n\t\terr = ResourceSaver::get_singleton()->save(this, get_path(), ResourceSaver::FLAG_COMPRESS);\n\t\t_height_map = original_map;\n\t} else {\n\t\terr = ResourceSaver::get_singleton()->save(this, get_path(), ResourceSaver::FLAG_COMPRESS);\n\t}\n\tif (err == OK) {\n\t\t_modified = false;\n\t\tLOG(INFO, \"File saved successfully\");\n\t} else {\n\t\tLOG(ERROR, \"Cannot save region file: \", get_path(), \". Error code: \", ERROR, \". Look up @GlobalScope Error enum in the Godot docs\");\n\t}\n\treturn err;\n}\n\nvoid Terrain3DRegion::set_data(const Dictionary &p_data) {\n#define SET_IF_HAS(var, str) \\\n\tif (p_data.has(str)) {   \\\n\t\tvar = p_data[str];   \\\n\t}\n\tSET_IF_HAS(_location, \"location\");\n\tSET_IF_HAS(_deleted, \"deleted\");\n\tSET_IF_HAS(_edited, \"edited\");\n\tSET_IF_HAS(_modified, \"modified\");\n\tSET_IF_HAS(_version, \"version\");\n\tSET_IF_HAS(_region_size, \"region_size\");\n\tSET_IF_HAS(_vertex_spacing, \"vertex_spacing\");\n\tSET_IF_HAS(_height_range, \"height_range\");\n\tSET_IF_HAS(_height_map, \"height_map\");\n\tSET_IF_HAS(_control_map, \"control_map\");\n\tSET_IF_HAS(_color_map, \"color_map\");\n\tSET_IF_HAS(_instances, \"instances\");\n}\n\nDictionary Terrain3DRegion::get_data() const {\n\tDictionary dict;\n\tdict[\"location\"] = _location;\n\tdict[\"deleted\"] = _deleted;\n\tdict[\"edited\"] = _edited;\n\tdict[\"modified\"] = _modified;\n\tdict[\"version\"] = _version;\n\tdict[\"region_size\"] = _region_size;\n\tdict[\"vertex_spacing\"] = _vertex_spacing;\n\tdict[\"height_range\"] = _height_range;\n\tdict[\"height_map\"] = _height_map;\n\tdict[\"control_map\"] = _control_map;\n\tdict[\"color_map\"] = _color_map;\n\tdict[\"instances\"] = _instances;\n\treturn dict;\n}\n\nRef<Terrain3DRegion> Terrain3DRegion::duplicate(const bool p_deep) {\n\tRef<Terrain3DRegion> region;\n\tregion.instantiate();\n\tif (!p_deep) {\n\t\tregion->set_data(get_data());\n\t} else {\n\t\tDictionary dict;\n\t\t// Native type copies\n\t\tdict[\"version\"] = _version;\n\t\tdict[\"region_size\"] = _region_size;\n\t\tdict[\"vertex_spacing\"] = _vertex_spacing;\n\t\tdict[\"height_range\"] = _height_range;\n\t\tdict[\"modified\"] = _modified;\n\t\tdict[\"deleted\"] = _deleted;\n\t\tdict[\"location\"] = _location;\n\t\t// Resource duplicates\n\t\tdict[\"height_map\"] = _height_map->duplicate();\n\t\tdict[\"control_map\"] = _control_map->duplicate();\n\t\tdict[\"color_map\"] = _color_map->duplicate();\n\t\tdict[\"instances\"] = _instances.duplicate(true);\n\t\tregion->set_data(dict);\n\t}\n\treturn region;\n}\n\nvoid Terrain3DRegion::dump(const bool verbose) const {\n\tLOG(MESG, \"Region: \", _location, \", version: \", vformat(\"%.2f\", _version), \", size: \", _region_size,\n\t\t\t\", spacing: \", vformat(\"%.1f\", _vertex_spacing), \", range: \", vformat(\"%.2v\", _height_range),\n\t\t\t\", flags (\", _edited ? \"ed,\" : \"\", _modified ? \"mod,\" : \"\", _deleted ? \"del\" : \"\", \"), \",\n\t\t\tptr_to_str(this));\n\tLOG(MESG, \"Height map: \", ptr_to_str(*_height_map), \", Control map: \", ptr_to_str(*_control_map),\n\t\t\t\", Color map: \", ptr_to_str(*_color_map));\n\tLOG(MESG, \"Instances: Mesh IDs: \", _instances.size(), \", \", ptr_to_str(_instances._native_ptr()));\n\tArray mesh_ids = _instances.keys();\n\tfor (const int &mesh_id : mesh_ids) {\n\t\tint counter = 0;\n\t\tDictionary cell_inst_dict = _instances[mesh_id];\n\t\tArray cells = cell_inst_dict.keys();\n\t\tfor (const Vector2i &cell : cells) {\n\t\t\tArray triple = cell_inst_dict[cell];\n\t\t\tif (triple.size() == 3) {\n\t\t\t\tcounter += Array(triple[0]).size();\n\t\t\t} else {\n\t\t\t\tLOG(WARN, \"Malformed triple at cell \", cell, \": \", triple);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (verbose) {\n\t\t\t\tArray xforms = triple[0];\n\t\t\t\tArray colors = triple[1];\n\t\t\t\tbool modified = triple[2];\n\t\t\t\tLOG(MESG, \"Mesh ID: \", mesh_id, \" cell: \", cell, \" xforms: \", xforms.size(),\n\t\t\t\t\t\t\", colors: \", colors.size(), modified ? \", modified\" : \"\");\n\t\t\t}\n\t\t}\n\t\tLOG(MESG, \"Mesh ID: \", mesh_id, \", instance count: \", counter);\n\t}\n}\n\n/////////////////////\n// Protected Functions\n/////////////////////\n\nvoid Terrain3DRegion::_bind_methods() {\n\tBIND_ENUM_CONSTANT(TYPE_HEIGHT);\n\tBIND_ENUM_CONSTANT(TYPE_CONTROL);\n\tBIND_ENUM_CONSTANT(TYPE_COLOR);\n\tBIND_ENUM_CONSTANT(TYPE_MAX);\n\n\tClassDB::bind_method(D_METHOD(\"clear\"), &Terrain3DRegion::clear);\n\tClassDB::bind_method(D_METHOD(\"set_version\", \"version\"), &Terrain3DRegion::set_version);\n\tClassDB::bind_method(D_METHOD(\"get_version\"), &Terrain3DRegion::get_version);\n\tClassDB::bind_method(D_METHOD(\"set_region_size\", \"region_size\"), &Terrain3DRegion::set_region_size);\n\tClassDB::bind_method(D_METHOD(\"get_region_size\"), &Terrain3DRegion::get_region_size);\n\tClassDB::bind_method(D_METHOD(\"set_vertex_spacing\", \"vertex_spacing\"), &Terrain3DRegion::set_vertex_spacing);\n\tClassDB::bind_method(D_METHOD(\"get_vertex_spacing\"), &Terrain3DRegion::get_vertex_spacing);\n\n\tClassDB::bind_method(D_METHOD(\"set_map\", \"map_type\", \"map\"), &Terrain3DRegion::set_map);\n\tClassDB::bind_method(D_METHOD(\"get_map\", \"map_type\"), &Terrain3DRegion::get_map);\n\tClassDB::bind_method(D_METHOD(\"set_maps\", \"maps\"), &Terrain3DRegion::set_maps);\n\tClassDB::bind_method(D_METHOD(\"get_maps\"), &Terrain3DRegion::get_maps);\n\tClassDB::bind_method(D_METHOD(\"set_height_map\", \"map\"), &Terrain3DRegion::set_height_map);\n\tClassDB::bind_method(D_METHOD(\"get_height_map\"), &Terrain3DRegion::get_height_map);\n\tClassDB::bind_method(D_METHOD(\"set_control_map\", \"map\"), &Terrain3DRegion::set_control_map);\n\tClassDB::bind_method(D_METHOD(\"get_control_map\"), &Terrain3DRegion::get_control_map);\n\tClassDB::bind_method(D_METHOD(\"set_color_map\", \"map\"), &Terrain3DRegion::set_color_map);\n\tClassDB::bind_method(D_METHOD(\"get_color_map\"), &Terrain3DRegion::get_color_map);\n\tClassDB::bind_method(D_METHOD(\"sanitize_maps\"), &Terrain3DRegion::sanitize_maps);\n\tClassDB::bind_method(D_METHOD(\"sanitize_map\", \"map_type\", \"map\"), &Terrain3DRegion::sanitize_map);\n\tClassDB::bind_method(D_METHOD(\"validate_map_size\", \"map\"), &Terrain3DRegion::validate_map_size);\n\n\tClassDB::bind_method(D_METHOD(\"set_height_range\", \"range\"), &Terrain3DRegion::set_height_range);\n\tClassDB::bind_method(D_METHOD(\"get_height_range\"), &Terrain3DRegion::get_height_range);\n\tClassDB::bind_method(D_METHOD(\"update_height\", \"height\"), &Terrain3DRegion::update_height);\n\tClassDB::bind_method(D_METHOD(\"update_heights\", \"low_high\"), &Terrain3DRegion::update_heights);\n\tClassDB::bind_method(D_METHOD(\"calc_height_range\"), &Terrain3DRegion::calc_height_range);\n\n\tClassDB::bind_method(D_METHOD(\"set_instances\", \"instances\"), &Terrain3DRegion::set_instances);\n\tClassDB::bind_method(D_METHOD(\"get_instances\"), &Terrain3DRegion::get_instances);\n\n\tClassDB::bind_method(D_METHOD(\"save\", \"path\", \"save_16_bit\"), &Terrain3DRegion::save, DEFVAL(\"\"), DEFVAL(false));\n\n\tClassDB::bind_method(D_METHOD(\"set_deleted\", \"deleted\"), &Terrain3DRegion::set_deleted);\n\tClassDB::bind_method(D_METHOD(\"is_deleted\"), &Terrain3DRegion::is_deleted);\n\tClassDB::bind_method(D_METHOD(\"set_edited\", \"edited\"), &Terrain3DRegion::set_edited);\n\tClassDB::bind_method(D_METHOD(\"is_edited\"), &Terrain3DRegion::is_edited);\n\tClassDB::bind_method(D_METHOD(\"set_modified\", \"modified\"), &Terrain3DRegion::set_modified);\n\tClassDB::bind_method(D_METHOD(\"is_modified\"), &Terrain3DRegion::is_modified);\n\tClassDB::bind_method(D_METHOD(\"set_location\", \"location\"), &Terrain3DRegion::set_location);\n\tClassDB::bind_method(D_METHOD(\"get_location\"), &Terrain3DRegion::get_location);\n\n\tClassDB::bind_method(D_METHOD(\"set_data\", \"data\"), &Terrain3DRegion::set_data);\n\tClassDB::bind_method(D_METHOD(\"get_data\"), &Terrain3DRegion::get_data);\n\tClassDB::bind_method(D_METHOD(\"duplicate\", \"deep\"), &Terrain3DRegion::duplicate, DEFVAL(false));\n\tClassDB::bind_method(D_METHOD(\"dump\", \"verbose\"), &Terrain3DRegion::dump, DEFVAL(false));\n\n\tint ro_flags = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"version\", PROPERTY_HINT_NONE, \"\", ro_flags), \"set_version\", \"get_version\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"region_size\", PROPERTY_HINT_NONE, \"\", ro_flags), \"set_region_size\", \"get_region_size\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"vertex_spacing\", PROPERTY_HINT_NONE, \"\", ro_flags), \"set_vertex_spacing\", \"get_vertex_spacing\");\n\tADD_PROPERTY(PropertyInfo(Variant::VECTOR2, \"height_range\", PROPERTY_HINT_NONE, \"\", ro_flags), \"set_height_range\", \"get_height_range\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"height_map\", PROPERTY_HINT_RESOURCE_TYPE, \"Image\", ro_flags), \"set_height_map\", \"get_height_map\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"control_map\", PROPERTY_HINT_RESOURCE_TYPE, \"Image\", ro_flags), \"set_control_map\", \"get_control_map\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"color_map\", PROPERTY_HINT_RESOURCE_TYPE, \"Image\", ro_flags), \"set_color_map\", \"get_color_map\");\n\tADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, \"instances\", PROPERTY_HINT_NONE, \"\", ro_flags), \"set_instances\", \"get_instances\");\n\n\t// Double-clicking a region .res file shows what's on disk, the defaults, not in memory. So these are hidden\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"edited\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE), \"set_edited\", \"is_edited\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"deleted\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE), \"set_deleted\", \"is_deleted\");\n\tADD_PROPERTY(PropertyInfo(Variant::BOOL, \"modified\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE), \"set_modified\", \"is_modified\");\n\tADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, \"location\", PROPERTY_HINT_NONE, \"\", PROPERTY_USAGE_NONE), \"set_location\", \"get_location\");\n}\n"
  },
  {
    "path": "src/terrain_3d_region.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_REGION_CLASS_H\n#define TERRAIN3D_REGION_CLASS_H\n\n#include \"constants.h\"\n#include \"terrain_3d_util.h\"\n\nclass Terrain3DRegion : public Resource {\n\tGDCLASS(Terrain3DRegion, Resource);\n\tCLASS_NAME();\n\npublic: // Constants\n\tenum MapType {\n\t\tTYPE_HEIGHT,\n\t\tTYPE_CONTROL,\n\t\tTYPE_COLOR,\n\t\tTYPE_MAX,\n\t};\n\n\tstatic inline const Image::Format FORMAT[] = {\n\t\tImage::FORMAT_RF, // TYPE_HEIGHT\n\t\tImage::FORMAT_RF, // TYPE_CONTROL\n\t\tImage::FORMAT_RGBA8, // TYPE_COLOR\n\t\tImage::Format(TYPE_MAX), // Proper size of array instead of FORMAT_MAX\n\t};\n\n\tstatic inline const char *TYPESTR[] = {\n\t\t\"TYPE_HEIGHT\",\n\t\t\"TYPE_CONTROL\",\n\t\t\"TYPE_COLOR\",\n\t\t\"TYPE_MAX\",\n\t};\n\n\tstatic inline const Color COLOR[] = {\n\t\tCOLOR_BLACK, // TYPE_HEIGHT\n\t\tCOLOR_CONTROL, // TYPE_CONTROL\n\t\tCOLOR_ROUGHNESS, // TYPE_COLOR\n\t\tCOLOR_NAN, // TYPE_MAX, unused just in case someone indexes the array\n\t};\n\nprivate:\n\t// Saved data\n\treal_t _version = 0.8f; // Set to first version to ensure we always upgrades this\n\tint _region_size = 0;\n\tVector2 _height_range = V2_ZERO;\n\t// Maps\n\tRef<Image> _height_map;\n\tRef<Image> _control_map;\n\tRef<Image> _color_map;\n\t// Instancer\n\tDictionary _instances; // Meshes{int} -> Cells{v2i} -> [ Transform3D, Color, Modified ]\n\treal_t _vertex_spacing = 1.f; // Spacing that instancer transforms are currently scaled by.\n\n\t// Working data not saved to disk\n\tbool _deleted = false; // Marked for deletion on save\n\tbool _edited = false; // Marked for undo/redo storage\n\tbool _modified = false; // Marked for saving\n\tVector2i _location = V2I_MAX;\n\npublic:\n\tTerrain3DRegion() {}\n\t~Terrain3DRegion() {}\n\n\tvoid clear();\n\tvoid set_version(const real_t p_version);\n\treal_t get_version() const { return _version; }\n\tvoid set_region_size(const int p_region_size);\n\tint get_region_size() const { return _region_size; }\n\n\t// Maps\n\tvoid set_map(const MapType p_map_type, const Ref<Image> &p_image);\n\tRef<Image> get_map(const MapType p_map_type) const;\n\tImage *get_map_ptr(const MapType p_map_type) const;\n\tvoid set_maps(const TypedArray<Image> &p_maps);\n\tTypedArray<Image> get_maps() const;\n\tvoid set_height_map(const Ref<Image> &p_map);\n\tRef<Image> get_height_map() const { return _height_map; }\n\tvoid set_control_map(const Ref<Image> &p_map);\n\tRef<Image> get_control_map() const { return _control_map; }\n\tvoid set_color_map(const Ref<Image> &p_map);\n\tRef<Image> get_color_map() const { return _color_map; }\n\tvoid sanitize_maps();\n\tRef<Image> sanitize_map(const MapType p_map_type, const Ref<Image> &p_map) const;\n\tbool validate_map_size(const Ref<Image> &p_map) const;\n\n\tvoid set_height_range(const Vector2 &p_range);\n\tVector2 get_height_range() const { return _height_range; }\n\tvoid update_height(const real_t p_height);\n\tvoid update_heights(const Vector2 &p_low_high);\n\tvoid calc_height_range();\n\n\t// Instancer\n\tvoid set_instances(const Dictionary &p_instances);\n\tDictionary get_instances() const { return _instances; }\n\tvoid set_vertex_spacing(const real_t p_vertex_spacing) { _vertex_spacing = CLAMP(p_vertex_spacing, 0.25f, 100.f); }\n\treal_t get_vertex_spacing() const { return _vertex_spacing; }\n\n\t// Working Data\n\tvoid set_deleted(const bool p_deleted) { _deleted = p_deleted; }\n\tbool is_deleted() const { return _deleted; }\n\tvoid set_edited(const bool p_edited) { _edited = p_edited; }\n\tbool is_edited() const { return _edited; }\n\tvoid set_modified(const bool p_modified) { _modified = p_modified; }\n\tbool is_modified() const { return _modified; }\n\tvoid set_location(const Vector2i &p_location);\n\tVector2i get_location() const { return _location; }\n\n\t// File I/O\n\tError save(const String &p_path = \"\", const bool p_16_bit = false);\n\n\t// Utility\n\tvoid set_data(const Dictionary &p_data);\n\tDictionary get_data() const;\n\tRef<Terrain3DRegion> duplicate(const bool p_deep = false);\n\tvoid dump(const bool verbose = false) const;\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nusing MapType = Terrain3DRegion::MapType;\nVARIANT_ENUM_CAST(Terrain3DRegion::MapType);\nconstexpr Terrain3DRegion::MapType TYPE_HEIGHT = Terrain3DRegion::MapType::TYPE_HEIGHT;\nconstexpr Terrain3DRegion::MapType TYPE_CONTROL = Terrain3DRegion::MapType::TYPE_CONTROL;\nconstexpr Terrain3DRegion::MapType TYPE_COLOR = Terrain3DRegion::MapType::TYPE_COLOR;\nconstexpr Terrain3DRegion::MapType TYPE_MAX = Terrain3DRegion::MapType::TYPE_MAX;\nconstexpr inline const Image::Format *FORMAT = Terrain3DRegion::FORMAT;\nconstexpr inline const char **TYPESTR = Terrain3DRegion::TYPESTR;\nconstexpr inline const Color *COLOR = Terrain3DRegion::COLOR;\n\n// Inline functions\n\ninline void Terrain3DRegion::update_height(const real_t p_height) {\n\tif (p_height < _height_range.x) {\n\t\t_height_range.x = p_height;\n\t\t_modified = true;\n\t} else if (p_height > _height_range.y) {\n\t\t_height_range.y = p_height;\n\t\t_modified = true;\n\t}\n}\n\ninline void Terrain3DRegion::update_heights(const Vector2 &p_low_high) {\n\tif (p_low_high.x < _height_range.x) {\n\t\t_height_range.x = p_low_high.x;\n\t\t_modified = true;\n\t}\n\tif (p_low_high.y > _height_range.y) {\n\t\t_height_range.y = p_low_high.y;\n\t\t_modified = true;\n\t}\n}\n\n#endif // TERRAIN3D_REGION_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_texture_asset.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/image.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d.h\"\n#include \"terrain_3d_texture_asset.h\"\n\n///////////////////////////\n// Private Functions\n///////////////////////////\n\n// Note a null texture is considered a valid format\nbool Terrain3DTextureAsset::_is_valid_format(const Ref<Texture2D> &p_texture) const {\n\tif (p_texture.is_null()) {\n\t\tLOG(DEBUG, \"Provided texture is null.\");\n\t\treturn true;\n\t}\n\tRef<Image> img = p_texture->get_image();\n\tImage::Format format = Image::FORMAT_MAX;\n\tif (img.is_valid()) {\n\t\tformat = img->get_format();\n\t}\n\tif (format < 0 || format >= Image::FORMAT_MAX) {\n\t\tLOG(ERROR, \"Invalid texture format. See documentation for format specification.\");\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DTextureAsset::initialize() {\n\tLOG(INFO, _id, \": \", _name, \": initializing asset\");\n}\n\nvoid Terrain3DTextureAsset::clear() {\n\tLOG(INFO, \"Clearing TextureAsset\");\n\t_name = \"New Texture\";\n\t_id = 0;\n\t_highlighted = false;\n\t_highlight_color = Color();\n\t_albedo_color = Color(1.0f, 1.0f, 1.0f, 1.0f);\n\t_albedo_texture.unref();\n\t_normal_texture.unref();\n\t_thumbnail.unref();\n\n\t_normal_depth = 1.0f;\n\t_ao_strength = 0.5f;\n\t_ao_light_affect = 0.0f;\n\t_roughness = 0.f;\n\t_displacement_scale = 0.f;\n\t_displacement_offset = 0.f;\n\t_uv_scale = 0.1f;\n\t_detiling_rotation = 0.0f;\n\t_detiling_shift = 0.0f;\n}\n\nvoid Terrain3DTextureAsset::set_name(const String &p_name) {\n\tif (p_name.length() > 96) {\n\t\tLOG(WARN, \"Name too long, truncating to 96 characters\");\n\t}\n\tSET_IF_DIFF(_name, p_name.left(96));\n\tLOG(INFO, \"Setting name: \", _name);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_id(const int p_new_id) {\n\tint old_id = _id;\n\tSET_IF_DIFF(_id, CLAMP(p_new_id, 0, Terrain3DAssets::MAX_TEXTURES - 1));\n\tLOG(INFO, \"Setting texture id: \", _id);\n\tLOG(DEBUG, \"Emitting id_changed, TYPE_TEXTURE, \", old_id, \", \", _id);\n\temit_signal(\"id_changed\", Terrain3DAssets::TYPE_TEXTURE, old_id, _id);\n}\n\nvoid Terrain3DTextureAsset::set_highlighted(const bool p_highlighted) {\n\tSET_IF_DIFF(_highlighted, p_highlighted);\n\tLOG(INFO, \"Set mesh ID \", _id, \" highlight: \", p_highlighted);\n\treal_t random_float = real_t(rand()) / real_t(RAND_MAX);\n\t_highlight_color.set_hsv(random_float, 1.f, 1.f, 1.f);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_albedo_color(const Color &p_color) {\n\tSET_IF_DIFF(_albedo_color, p_color);\n\tLOG(INFO, \"Setting color: \", p_color);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_albedo_texture(const Ref<Texture2D> &p_texture) {\n\tif (_is_valid_format(p_texture)) {\n\t\tSET_IF_DIFF(_albedo_texture, p_texture);\n\t\tLOG(INFO, \"Setting albedo texture: \", p_texture);\n\t\tif (p_texture.is_valid()) {\n\t\t\tString filename = p_texture->get_path().get_file().get_basename();\n\t\t\tif (_name == \"New Texture\" && !p_texture->get_path().contains(\"::\") && !filename.is_empty()) {\n\t\t\t\t_name = filename;\n\t\t\t\tLOG(INFO, \"Setting name based on filename: \", _name);\n\t\t\t}\n\t\t\tRef<Image> img = p_texture->get_image();\n\t\t\tif (!img->has_mipmaps()) {\n\t\t\t\tLOG(WARN, \"Albedo texture '\", filename, \"' has no mipmaps. Change on the Import panel if desired.\");\n\t\t\t}\n\t\t\tif (img->get_width() != img->get_height()) {\n\t\t\t\tLOG(WARN, \"Albedo texture '\", filename, \"' is not square. Mipmaps might have artifacts.\");\n\t\t\t}\n\t\t\tif (!is_power_of_2(img->get_width()) || !is_power_of_2(img->get_height())) {\n\t\t\t\tLOG(WARN, \"Albedo texture '\", filename, \"' size is not power of 2. This is sub-optimal.\");\n\t\t\t}\n\t\t\tif (IS_EDITOR) {\n\t\t\t\timg = img->duplicate();\n\t\t\t\timg->decompress();\n\t\t\t\timg->resize(512, 512, Image::INTERPOLATE_LANCZOS);\n\t\t\t\timg->convert(Image::FORMAT_RGB8);\n\t\t\t\t_thumbnail = ImageTexture::create_from_image(img);\n\t\t\t}\n\t\t} else {\n\t\t\t_thumbnail.unref();\n\t\t}\n\t\tLOG(DEBUG, \"Emitting file_changed\");\n\t\temit_signal(\"file_changed\");\n\t}\n}\n\nvoid Terrain3DTextureAsset::set_normal_texture(const Ref<Texture2D> &p_texture) {\n\tif (_is_valid_format(p_texture)) {\n\t\tSET_IF_DIFF(_normal_texture, p_texture);\n\t\tLOG(INFO, \"Setting normal texture: \", p_texture);\n\t\tif (p_texture.is_valid()) {\n\t\t\tString filename = p_texture->get_path().get_file().get_basename();\n\t\t\tRef<Image> img = p_texture->get_image();\n\t\t\tif (!img->has_mipmaps()) {\n\t\t\t\tLOG(WARN, \"Normal texture '\", filename, \"' has no mipmaps. Change on the Import panel if desired.\");\n\t\t\t}\n\t\t\tif (img->get_width() != img->get_height()) {\n\t\t\t\tLOG(WARN, \"Normal texture '\", filename, \"' is not square. Not recommended. Mipmaps might have artifacts.\");\n\t\t\t}\n\t\t\tif (!is_power_of_2(img->get_width()) || !is_power_of_2(img->get_height())) {\n\t\t\t\tLOG(WARN, \"Normal texture '\", filename, \"' dimensions are not power of 2. This is sub-optimal.\");\n\t\t\t}\n\t\t}\n\t\tLOG(DEBUG, \"Emitting file_changed\");\n\t\temit_signal(\"file_changed\");\n\t}\n}\n\nvoid Terrain3DTextureAsset::set_normal_depth(const real_t p_normal_depth) {\n\tSET_IF_DIFF(_normal_depth, CLAMP(p_normal_depth, 0.0f, 2.0f));\n\tLOG(INFO, \"Setting normal_depth: \", _normal_depth);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_roughness(const real_t p_roughness) {\n\tSET_IF_DIFF(_roughness, CLAMP(p_roughness, -1.0f, 1.0f));\n\tLOG(INFO, \"Setting roughness modifier: \", _roughness);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_ao_strength(const real_t p_ao_strength) {\n\tSET_IF_DIFF(_ao_strength, CLAMP(p_ao_strength, 0.0f, 1.0f));\n\tLOG(INFO, \"Setting ao_strength: \", _ao_strength);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_ao_light_affect(const real_t p_ao_light_affect) {\n\tSET_IF_DIFF(_ao_light_affect, CLAMP(p_ao_light_affect, 0.0f, 1.0f));\n\tLOG(INFO, \"Setting ao_light_affect: \", _ao_light_affect);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_displacement_scale(const real_t p_displacement_scale) {\n\tSET_IF_DIFF(_displacement_scale, CLAMP(p_displacement_scale, 0.0f, 2.0f));\n\tLOG(INFO, \"Setting displacement_scale: \", _displacement_scale);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_displacement_offset(const real_t p_displacement_offset) {\n\tSET_IF_DIFF(_displacement_offset, CLAMP(p_displacement_offset, -1.0f, 1.0f));\n\tLOG(INFO, \"Setting displacement_offset: \", _displacement_offset);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_uv_scale(const real_t p_scale) {\n\tSET_IF_DIFF(_uv_scale, CLAMP(p_scale, 0.001f, 100.0f));\n\tLOG(INFO, \"Setting uv_scale: \", _uv_scale);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_detiling_rotation(const real_t p_detiling_rotation) {\n\tSET_IF_DIFF(_detiling_rotation, CLAMP(p_detiling_rotation, 0.0f, 1.0f));\n\tLOG(INFO, \"Setting detiling_rotation: \", _detiling_rotation);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\nvoid Terrain3DTextureAsset::set_detiling_shift(const real_t p_detiling_shift) {\n\tSET_IF_DIFF(_detiling_shift, CLAMP(p_detiling_shift, 0.0f, 1.0f));\n\tLOG(INFO, \"Setting detiling_shift: \", _detiling_shift);\n\tLOG(DEBUG, \"Emitting setting_changed\");\n\temit_signal(\"setting_changed\");\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DTextureAsset::_bind_methods() {\n\tADD_SIGNAL(MethodInfo(\"id_changed\"));\n\tADD_SIGNAL(MethodInfo(\"file_changed\"));\n\tADD_SIGNAL(MethodInfo(\"setting_changed\"));\n\n\tClassDB::bind_method(D_METHOD(\"clear\"), &Terrain3DTextureAsset::clear);\n\tClassDB::bind_method(D_METHOD(\"set_name\", \"name\"), &Terrain3DTextureAsset::set_name);\n\tClassDB::bind_method(D_METHOD(\"get_name\"), &Terrain3DTextureAsset::get_name);\n\tClassDB::bind_method(D_METHOD(\"set_id\", \"id\"), &Terrain3DTextureAsset::set_id);\n\tClassDB::bind_method(D_METHOD(\"get_id\"), &Terrain3DTextureAsset::get_id);\n\tClassDB::bind_method(D_METHOD(\"set_highlighted\", \"enabled\"), &Terrain3DTextureAsset::set_highlighted);\n\tClassDB::bind_method(D_METHOD(\"is_highlighted\"), &Terrain3DTextureAsset::is_highlighted);\n\tClassDB::bind_method(D_METHOD(\"get_highlight_color\"), &Terrain3DTextureAsset::get_highlight_color);\n\tClassDB::bind_method(D_METHOD(\"get_thumbnail\"), &Terrain3DTextureAsset::get_thumbnail);\n\tClassDB::bind_method(D_METHOD(\"set_albedo_color\", \"color\"), &Terrain3DTextureAsset::set_albedo_color);\n\tClassDB::bind_method(D_METHOD(\"get_albedo_color\"), &Terrain3DTextureAsset::get_albedo_color);\n\tClassDB::bind_method(D_METHOD(\"set_albedo_texture\", \"texture\"), &Terrain3DTextureAsset::set_albedo_texture);\n\tClassDB::bind_method(D_METHOD(\"get_albedo_texture\"), &Terrain3DTextureAsset::get_albedo_texture);\n\tClassDB::bind_method(D_METHOD(\"set_normal_texture\", \"texture\"), &Terrain3DTextureAsset::set_normal_texture);\n\tClassDB::bind_method(D_METHOD(\"get_normal_texture\"), &Terrain3DTextureAsset::get_normal_texture);\n\tClassDB::bind_method(D_METHOD(\"set_normal_depth\", \"normal_depth\"), &Terrain3DTextureAsset::set_normal_depth);\n\tClassDB::bind_method(D_METHOD(\"get_normal_depth\"), &Terrain3DTextureAsset::get_normal_depth);\n\tClassDB::bind_method(D_METHOD(\"set_ao_strength\", \"ao_strength\"), &Terrain3DTextureAsset::set_ao_strength);\n\tClassDB::bind_method(D_METHOD(\"get_ao_strength\"), &Terrain3DTextureAsset::get_ao_strength);\n\tClassDB::bind_method(D_METHOD(\"set_ao_light_affect\", \"ao_light_affect\"), &Terrain3DTextureAsset::set_ao_light_affect);\n\tClassDB::bind_method(D_METHOD(\"get_ao_light_affect\"), &Terrain3DTextureAsset::get_ao_light_affect);\n\tClassDB::bind_method(D_METHOD(\"set_roughness\", \"roughness\"), &Terrain3DTextureAsset::set_roughness);\n\tClassDB::bind_method(D_METHOD(\"get_roughness\"), &Terrain3DTextureAsset::get_roughness);\n\tClassDB::bind_method(D_METHOD(\"set_displacement_scale\", \"displacement_scale\"), &Terrain3DTextureAsset::set_displacement_scale);\n\tClassDB::bind_method(D_METHOD(\"get_displacement_scale\"), &Terrain3DTextureAsset::get_displacement_scale);\n\tClassDB::bind_method(D_METHOD(\"set_displacement_offset\", \"displacement_offset\"), &Terrain3DTextureAsset::set_displacement_offset);\n\tClassDB::bind_method(D_METHOD(\"get_displacement_offset\"), &Terrain3DTextureAsset::get_displacement_offset);\n\tClassDB::bind_method(D_METHOD(\"set_uv_scale\", \"scale\"), &Terrain3DTextureAsset::set_uv_scale);\n\tClassDB::bind_method(D_METHOD(\"get_uv_scale\"), &Terrain3DTextureAsset::get_uv_scale);\n\tClassDB::bind_method(D_METHOD(\"set_detiling_rotation\", \"detiling_rotation\"), &Terrain3DTextureAsset::set_detiling_rotation);\n\tClassDB::bind_method(D_METHOD(\"get_detiling_rotation\"), &Terrain3DTextureAsset::get_detiling_rotation);\n\tClassDB::bind_method(D_METHOD(\"set_detiling_shift\", \"detiling_shift\"), &Terrain3DTextureAsset::set_detiling_shift);\n\tClassDB::bind_method(D_METHOD(\"get_detiling_shift\"), &Terrain3DTextureAsset::get_detiling_shift);\n\n\tADD_PROPERTY(PropertyInfo(Variant::STRING, \"name\", PROPERTY_HINT_NONE), \"set_name\", \"get_name\");\n\tADD_PROPERTY(PropertyInfo(Variant::INT, \"id\", PROPERTY_HINT_NONE), \"set_id\", \"get_id\");\n\tADD_PROPERTY(PropertyInfo(Variant::COLOR, \"albedo_color\", PROPERTY_HINT_COLOR_NO_ALPHA), \"set_albedo_color\", \"get_albedo_color\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"albedo_texture\", PROPERTY_HINT_RESOURCE_TYPE, \"ImageTexture,CompressedTexture2D\"), \"set_albedo_texture\", \"get_albedo_texture\");\n\tADD_PROPERTY(PropertyInfo(Variant::OBJECT, \"normal_texture\", PROPERTY_HINT_RESOURCE_TYPE, \"ImageTexture,CompressedTexture2D\"), \"set_normal_texture\", \"get_normal_texture\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"normal_depth\", PROPERTY_HINT_RANGE, \"0.0, 2.0\"), \"set_normal_depth\", \"get_normal_depth\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"ao_strength\", PROPERTY_HINT_RANGE, \"0.0, 1.0\"), \"set_ao_strength\", \"get_ao_strength\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"ao_light_affect\", PROPERTY_HINT_RANGE, \"0.0, 1.0\"), \"set_ao_light_affect\", \"get_ao_light_affect\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"roughness\", PROPERTY_HINT_RANGE, \"-1.0, 1.0\"), \"set_roughness\", \"get_roughness\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"displacement_scale\", PROPERTY_HINT_RANGE, \"0.0, 2.0\"), \"set_displacement_scale\", \"get_displacement_scale\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"displacement_offset\", PROPERTY_HINT_RANGE, \"-1.0, 1.0\"), \"set_displacement_offset\", \"get_displacement_offset\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"uv_scale\", PROPERTY_HINT_RANGE, \"0.001, 2.0, or_greater\"), \"set_uv_scale\", \"get_uv_scale\");\n\tADD_GROUP(\"Detiling\", \"\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"detiling_rotation\", PROPERTY_HINT_RANGE, \"0.0, 1.0\"), \"set_detiling_rotation\", \"get_detiling_rotation\");\n\tADD_PROPERTY(PropertyInfo(Variant::FLOAT, \"detiling_shift\", PROPERTY_HINT_RANGE, \"0.0, 1.0\"), \"set_detiling_shift\", \"get_detiling_shift\");\n}\n"
  },
  {
    "path": "src/terrain_3d_texture_asset.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_TEXTURE_CLASS_H\n#define TERRAIN3D_TEXTURE_CLASS_H\n\n#include <godot_cpp/classes/texture2d.hpp>\n\n#include \"constants.h\"\n#include \"terrain_3d_asset_resource.h\"\n\nclass Terrain3DTextureAsset : public Terrain3DAssetResource {\n\tGDCLASS(Terrain3DTextureAsset, Terrain3DAssetResource);\n\tCLASS_NAME();\n\tfriend class Terrain3DAssets;\n\n\t// Saved Data\n\tColor _albedo_color = Color(1.f, 1.f, 1.f, 1.f);\n\tRef<Texture2D> _albedo_texture;\n\tRef<Texture2D> _normal_texture;\n\treal_t _normal_depth = 1.0f;\n\treal_t _ao_strength = 0.5f;\n\treal_t _ao_light_affect = 0.0f;\n\treal_t _roughness = 0.f;\n\treal_t _displacement_scale = 0.0f;\n\treal_t _displacement_offset = 0.0f;\n\treal_t _uv_scale = 0.1f;\n\treal_t _detiling_rotation = 0.0f;\n\treal_t _detiling_shift = 0.0f;\n\n\t// Working Data\n\tColor _highlight_color = Color();\n\n\tbool _is_valid_format(const Ref<Texture2D> &p_texture) const;\n\npublic:\n\tTerrain3DTextureAsset() { clear(); }\n\t~Terrain3DTextureAsset() {}\n\tvoid initialize() override;\n\tvoid clear() override;\n\n\tvoid set_name(const String &p_name) override;\n\tString get_name() const override { return _name; }\n\n\tvoid set_id(const int p_new_id) override;\n\tint get_id() const override { return _id; }\n\n\tvoid set_highlighted(const bool p_highlighted) override;\n\tbool is_highlighted() const override { return _highlighted; }\n\tColor get_highlight_color() const override { return _highlighted ? _highlight_color : COLOR_WHITE; }\n\n\tRef<Texture2D> get_thumbnail() const override { return _thumbnail; }\n\n\tvoid set_albedo_color(const Color &p_color);\n\tColor get_albedo_color() const { return _albedo_color; }\n\n\tvoid set_albedo_texture(const Ref<Texture2D> &p_texture);\n\tRef<Texture2D> get_albedo_texture() const { return _albedo_texture; }\n\n\tvoid set_normal_texture(const Ref<Texture2D> &p_texture);\n\tRef<Texture2D> get_normal_texture() const { return _normal_texture; }\n\n\tvoid set_normal_depth(const real_t p_normal_depth);\n\treal_t get_normal_depth() const { return _normal_depth; }\n\n\tvoid set_ao_strength(const real_t p_ao_strength);\n\treal_t get_ao_strength() const { return _ao_strength; }\n\n\tvoid set_ao_light_affect(const real_t p_ao_light_affect);\n\treal_t get_ao_light_affect() const { return _ao_light_affect; }\n\n\tvoid set_roughness(const real_t p_roughness);\n\treal_t get_roughness() const { return _roughness; }\n\n\tvoid set_displacement_scale(const real_t p_displacement_scale);\n\treal_t get_displacement_scale() const { return _displacement_scale; }\n\n\tvoid set_displacement_offset(const real_t p_displacement_offset);\n\treal_t get_displacement_offset() const { return _displacement_offset; }\n\n\tvoid set_uv_scale(const real_t p_scale);\n\treal_t get_uv_scale() const { return _uv_scale; }\n\n\tvoid set_detiling_rotation(const real_t p_detiling_rotation);\n\treal_t get_detiling_rotation() const { return _detiling_rotation; }\n\n\tvoid set_detiling_shift(const real_t p_detiling_shift);\n\treal_t get_detiling_shift() const { return _detiling_shift; }\n\nprotected:\n\tstatic void _bind_methods();\n};\n\n#endif // TERRAIN3D_TEXTURE_CLASS_H\n"
  },
  {
    "path": "src/terrain_3d_util.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include <godot_cpp/classes/dir_access.hpp>\n#include <godot_cpp/classes/engine.hpp>\n#include <godot_cpp/classes/file_access.hpp>\n#include <godot_cpp/classes/time.hpp>\n\n#include \"logger.h\"\n#include \"terrain_3d_util.h\"\n\n///////////////////////////\n// Public Functions\n///////////////////////////\n\nvoid Terrain3DUtil::print_arr(const String &p_name, const Array &p_arr, const int p_level) {\n\tLOG(p_level, \"Array[\", p_arr.size(), \"]: \", p_name);\n\tfor (int i = 0; i < p_arr.size(); i++) {\n\t\tVariant var = p_arr[i];\n\t\tswitch (var.get_type()) {\n\t\t\tcase Variant::ARRAY: {\n\t\t\t\tprint_arr(p_name + String::num_int64(i), var, p_level);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Variant::DICTIONARY: {\n\t\t\t\tprint_dict(p_name + String::num_int64(i), var, p_level);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Variant::OBJECT: {\n\t\t\t\tObject *obj = cast_to<Object>(var);\n\t\t\t\tString str = \"Object#\" + String::num_uint64(obj->get_instance_id()) + \", \" + ptr_to_str(obj);\n\t\t\t\tLOG(p_level, i, \": \", str);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tLOG(p_level, i, \": \", p_arr[i]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terrain3DUtil::print_dict(const String &p_name, const Dictionary &p_dict, const int p_level) {\n\tLOG(p_level, \"Dictionary: \", p_name);\n\tArray keys = p_dict.keys();\n\tfor (const StringName &key : keys) {\n\t\tVariant var = p_dict[key];\n\t\tswitch (var.get_type()) {\n\t\t\tcase Variant::ARRAY: {\n\t\t\t\tprint_arr(String(key), var, p_level);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Variant::DICTIONARY: {\n\t\t\t\tprint_dict(String(key), var, p_level);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Variant::OBJECT: {\n\t\t\t\tObject *obj = cast_to<Object>(var);\n\t\t\t\tString str = \"Object#\" + String::num_uint64(obj->get_instance_id()) + \", \" + ptr_to_str(obj);\n\t\t\t\tLOG(p_level, \"\\\"\", key, \"\\\": \", str);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tLOG(p_level, \"\\\"\", key, \"\\\": Value: \", var);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terrain3DUtil::dump_gentex(const GeneratedTexture &p_gen, const String &p_name) {\n\tLOG(MESG, \"Generated \", p_name, \" RID: \", p_gen.get_rid(), \", dirty: \", p_gen.is_dirty(),\n\t\t\t\", image: \", ptr_to_str(*p_gen.get_image()));\n}\n\nvoid Terrain3DUtil::dump_maps(const TypedArray<Image> &p_maps, const String &p_name) {\n\tLOG(MESG, \"Dumping \", p_name, \" array. Size: \", p_maps.size());\n\tfor (const Ref<Image> &img : p_maps) {\n\t\tLOG(MESG, \"Map size: \", img->get_size(), \", format: \", img->get_format(),\n\t\t\t\t\", \", ptr_to_str(*img));\n\t}\n}\n\n// Expects a filename in a String like: \"terrain3d-01_02.res\" which returns (-1, 2)\nVector2i Terrain3DUtil::filename_to_location(const String &p_filename) {\n\tString location_string = p_filename.trim_prefix(\"terrain3d\").trim_suffix(\".res\");\n\treturn string_to_location(location_string);\n}\n\n// Expects a string formatted as: \"±##±##\" which returns (##,##)\nVector2i Terrain3DUtil::string_to_location(const String &p_string) {\n\tString x_str = p_string.left(3).replace(\"_\", \"\");\n\tString y_str = p_string.right(3).replace(\"_\", \"\");\n\tif (!x_str.is_valid_int() || !y_str.is_valid_int()) {\n\t\tLOG(ERROR, \"Malformed string '\", p_string, \"'. Result: \", x_str, \", \", y_str);\n\t\treturn V2I_MAX;\n\t}\n\treturn Vector2i(x_str.to_int(), y_str.to_int());\n}\n\n// Expects a v2i(-1,2) and returns terrain3d-01_02.res\nString Terrain3DUtil::location_to_filename(const Vector2i &p_region_loc) {\n\treturn \"terrain3d\" + location_to_string(p_region_loc) + \".res\";\n}\n\n// Expects a v2i(-1,2) and returns -01_02\nString Terrain3DUtil::location_to_string(const Vector2i &p_region_loc) {\n\tconst String POS_REGION_FORMAT = \"_%02d\";\n\tconst String NEG_REGION_FORMAT = \"%03d\";\n\tString x_str, y_str;\n\tx_str = vformat((p_region_loc.x >= 0) ? POS_REGION_FORMAT : NEG_REGION_FORMAT, p_region_loc.x);\n\ty_str = vformat((p_region_loc.y >= 0) ? POS_REGION_FORMAT : NEG_REGION_FORMAT, p_region_loc.y);\n\treturn x_str + y_str;\n}\n\nPackedStringArray Terrain3DUtil::get_files(const String &p_dir, const String &p_glob) {\n\tPackedStringArray files;\n\tRef<DirAccess> da = DirAccess::open(p_dir);\n\tif (da.is_null()) {\n\t\tLOG(ERROR, \"Cannot open directory: \", p_dir);\n\t\treturn files;\n\t}\n\tPackedStringArray dir_files = da->get_files();\n\tfor (const String &file_name : dir_files) {\n\t\tString fname = file_name.trim_suffix(\".remap\");\n\t\tif (!fname.matchn(p_glob)) {\n\t\t\tcontinue;\n\t\t}\n\t\tLOG(DEBUG, \"Found file: \", p_dir + String(\"/\") + fname);\n\t\tfiles.push_back(fname);\n\t}\n\treturn files;\n}\n\nRef<Image> Terrain3DUtil::black_to_alpha(const Ref<Image> &p_image) {\n\tif (p_image.is_null()) {\n\t\treturn Ref<Image>();\n\t}\n\tRef<Image> img = Image::create_empty(p_image->get_width(), p_image->get_height(), false, Image::FORMAT_RGBAF);\n\tfor (int y = 0; y < img->get_height(); y++) {\n\t\tfor (int x = 0; x < img->get_width(); x++) {\n\t\t\tColor pixel = p_image->get_pixel(x, y);\n\t\t\tpixel.a = pixel.get_luminance();\n\t\t\timg->set_pixel(x, y, pixel);\n\t\t}\n\t}\n\tif (p_image->has_mipmaps()) {\n\t\timg->generate_mipmaps();\n\t}\n\treturn img;\n}\n\n/**\n * Returns the minimum and maximum values for a heightmap (red channel only)\n */\nVector2 Terrain3DUtil::get_min_max(const Ref<Image> &p_image) {\n\tif (p_image.is_null()) {\n\t\tLOG(ERROR, \"Provided image is not valid. Nothing to analyze\");\n\t\treturn V2(INFINITY);\n\t} else if (p_image->is_empty()) {\n\t\tLOG(ERROR, \"Provided image is empty. Nothing to analyze\");\n\t\treturn V2(INFINITY);\n\t}\n\n\tVector2 min_max = Vector2(FLT_MAX, -FLT_MAX);\n\n\tfor (int y = 0; y < p_image->get_height(); y++) {\n\t\tfor (int x = 0; x < p_image->get_width(); x++) {\n\t\t\tColor col = p_image->get_pixel(x, y);\n\t\t\tif (col.r < min_max.x) {\n\t\t\t\tmin_max.x = col.r;\n\t\t\t}\n\t\t\tif (col.r > min_max.y) {\n\t\t\t\tmin_max.y = col.r;\n\t\t\t}\n\t\t}\n\t}\n\n\tLOG(INFO, \"Calculating minimum and maximum values of the image: \", min_max);\n\treturn min_max;\n}\n\n/**\n * Returns a Image of a float heightmap normalized to RGB8 greyscale and scaled\n * Minimum of 8x8\n */\nRef<Image> Terrain3DUtil::get_thumbnail(const Ref<Image> &p_image, const Vector2i &p_size) {\n\tif (p_image.is_null()) {\n\t\tLOG(ERROR, \"Provided image is not valid. Nothing to process\");\n\t\treturn Ref<Image>();\n\t} else if (p_image->is_empty()) {\n\t\tLOG(ERROR, \"Provided image is empty. Nothing to process\");\n\t\treturn Ref<Image>();\n\t}\n\tVector2i size = Vector2i(CLAMP(p_size.x, 8, 16384), CLAMP(p_size.y, 8, 16384));\n\n\tLOG(INFO, \"Drawing a thumbnail sized: \", size);\n\t// Create a temporary work image scaled to desired width\n\tRef<Image> img;\n\timg.instantiate();\n\timg->copy_from(p_image);\n\timg->resize(size.x, size.y, Image::INTERPOLATE_LANCZOS);\n\n\t// Get minimum and maximum height values on the scaled image\n\tVector2 minmax = get_min_max(img);\n\treal_t hmin = minmax.x;\n\treal_t hmax = minmax.y;\n\t// Define maximum range\n\thmin = std::abs(hmin);\n\thmax = std::abs(hmax) + hmin;\n\t// Avoid divide by zero\n\thmax = (hmax == 0) ? 0.001f : hmax;\n\n\t// Create a new image w / normalized values\n\tRef<Image> thumb = Image::create_empty(size.x, size.y, false, Image::FORMAT_RGB8);\n\tfor (int y = 0; y < thumb->get_height(); y++) {\n\t\tfor (int x = 0; x < thumb->get_width(); x++) {\n\t\t\tColor col = img->get_pixel(x, y);\n\t\t\tcol.r = (col.r + hmin) / hmax;\n\t\t\tcol.g = col.r;\n\t\t\tcol.b = col.r;\n\t\t\tthumb->set_pixel(x, y, col);\n\t\t}\n\t}\n\treturn thumb;\n}\n\n/* Get an Image filled with specified color and format\n * If p_color.a < 0, fill with checkered pattern multiplied by p_color.rgb\n *\n * Behavior changes if a compressed format is requested:\n * If the editor is running and format is DXT1/5, BPTC_RGBA, it returns a filled image.\n * Otherwise, it returns a blank image in that format.\n *\n * The reason is the Image compression library is available only in the editor. And it is\n * unreliable, offering little control over the output format, choosing automatically and\n * often wrong. We have selected a few compressed formats it gets right.\n */\nRef<Image> Terrain3DUtil::get_filled_image(const Vector2i &p_size, const Color &p_color,\n\t\tconst bool p_create_mipmaps, const Image::Format p_format) {\n\tImage::Format format = p_format;\n\tif (format < 0 || format >= Image::FORMAT_MAX) {\n\t\tformat = Image::FORMAT_DXT5;\n\t}\n\n\tImage::CompressMode compression_format = Image::COMPRESS_MAX;\n\tImage::UsedChannels channels = Image::USED_CHANNELS_RGBA;\n\tbool compress = false;\n\tbool fill_image = true;\n\n\tif (format >= Image::Format::FORMAT_DXT1) {\n\t\tswitch (format) {\n\t\t\tcase Image::FORMAT_DXT1:\n\t\t\t\tformat = Image::FORMAT_RGB8;\n\t\t\t\tchannels = Image::USED_CHANNELS_RGB;\n\t\t\t\tcompression_format = Image::COMPRESS_S3TC;\n\t\t\t\tcompress = true;\n\t\t\t\tbreak;\n\t\t\tcase Image::FORMAT_DXT5:\n\t\t\t\tformat = Image::FORMAT_RGBA8;\n\t\t\t\tchannels = Image::USED_CHANNELS_RGBA;\n\t\t\t\tcompression_format = Image::COMPRESS_S3TC;\n\t\t\t\tcompress = true;\n\t\t\t\tbreak;\n\t\t\tcase Image::FORMAT_BPTC_RGBA:\n\t\t\t\tformat = Image::FORMAT_RGBA8;\n\t\t\t\tchannels = Image::USED_CHANNELS_RGBA;\n\t\t\t\tcompression_format = Image::COMPRESS_BPTC;\n\t\t\t\tcompress = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcompress = false;\n\t\t\t\tfill_image = false;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tRef<Image> img = Image::create_empty(p_size.x, p_size.y, p_create_mipmaps, format);\n\n\tColor color = p_color;\n\tif (fill_image) {\n\t\tif (color.a < 0.0f) {\n\t\t\tcolor.a = 1.0f;\n\t\t\tColor col_a = Color(0.8f, 0.8f, 0.8f, 1.0) * color;\n\t\t\tColor col_b = Color(0.5f, 0.5f, 0.5f, 1.0) * color;\n\t\t\timg->fill_rect(Rect2i(V2I_ZERO, p_size / 2), col_a);\n\t\t\timg->fill_rect(Rect2i(p_size / 2, p_size / 2), col_a);\n\t\t\timg->fill_rect(Rect2i(Vector2(p_size.x, 0) / 2, p_size / 2), col_b);\n\t\t\timg->fill_rect(Rect2i(Vector2(0, p_size.y) / 2, p_size / 2), col_b);\n\t\t} else {\n\t\t\timg->fill(color);\n\t\t}\n\t\tif (p_create_mipmaps) {\n\t\t\timg->generate_mipmaps();\n\t\t}\n\t}\n\tif (compress && IS_EDITOR) {\n\t\timg->compress_from_channels(compression_format, channels);\n\t}\n\treturn img;\n}\n\n/**\n * Loads a file from disk and returns an Image\n * Parameters:\n *\tp_filename - file on disk to load. EXR, R16/RAW, PNG, or a ResourceLoader format (jpg, res, tres, etc)\n *\tp_cache_mode - Send this flag to the resource loader to force caching or not\n *\tp_height_range - R16 format: x=Min & y=Max value ranges. Required for R16 import\n *\tp_size - R16 format: Image dimensions. Default (0,0) auto detects f/ square images. Required f/ non-square R16\n */\nRef<Image> Terrain3DUtil::load_image(const String &p_file_name, const int p_cache_mode, const Vector2 &p_r16_height_range, const Vector2i &p_r16_size) {\n\tif (p_file_name.is_empty()) {\n\t\tLOG(ERROR, \"No file specified. Nothing imported\");\n\t\treturn Ref<Image>();\n\t}\n\tif (!FileAccess::file_exists(p_file_name)) {\n\t\tLOG(ERROR, \"File \", p_file_name, \" does not exist. Nothing to import\");\n\t\treturn Ref<Image>();\n\t}\n\n\t// Load file based on extension\n\tRef<Image> img;\n\tLOG(INFO, \"Attempting to load: \", p_file_name);\n\tString ext = p_file_name.get_extension().to_lower();\n\tPackedStringArray imgloader_extensions = PackedStringArray(Array::make(\"bmp\", \"dds\", \"exr\", \"hdr\", \"jpg\", \"jpeg\", \"png\", \"tga\", \"svg\", \"webp\"));\n\n\t// If R16 integer format (read/writeable by Krita)\n\tif (ext == \"r16\" || ext == \"raw\") {\n\t\tLOG(DEBUG, \"Loading file as an r16\");\n\t\tRef<FileAccess> file = FileAccess::open(p_file_name, FileAccess::READ);\n\t\t// If p_size is zero, assume square and try to auto detect size\n\t\tVector2i r16_size = p_r16_size;\n\t\tif (r16_size <= V2I_ZERO) {\n\t\t\tfile->seek_end();\n\t\t\tint fsize = file->get_position();\n\t\t\tint fwidth = sqrt(fsize / 2);\n\t\t\tr16_size = V2I(fwidth);\n\t\t\tLOG(DEBUG, \"Total file size is: \", fsize, \" calculated width: \", fwidth, \" dimensions: \", r16_size);\n\t\t\tfile->seek(0);\n\t\t}\n\t\timg = Image::create_empty(r16_size.x, r16_size.y, false, FORMAT[TYPE_HEIGHT]);\n\t\tfor (int y = 0; y < r16_size.y; y++) {\n\t\t\tfor (int x = 0; x < r16_size.x; x++) {\n\t\t\t\treal_t h = real_t(file->get_16()) / 65535.0f;\n\t\t\t\th = h * (p_r16_height_range.y - p_r16_height_range.x) + p_r16_height_range.x;\n\t\t\t\timg->set_pixel(x, y, Color(h, 0.f, 0.f));\n\t\t\t}\n\t\t}\n\n\t\t// If an Image extension, use Image loader\n\t} else if (imgloader_extensions.has(ext)) {\n\t\tLOG(DEBUG, \"ImageFormatLoader loading recognized file type: \", ext);\n\t\timg = Image::load_from_file(p_file_name);\n\n\t\t// Else, see if Godot's resource loader will read it as an image: RES, TRES, etc\n\t} else {\n\t\tLOG(DEBUG, \"Loading file as a resource\");\n\t\timg = ResourceLoader::get_singleton()->load(p_file_name, \"\", static_cast<ResourceLoader::CacheMode>(p_cache_mode));\n\t}\n\n\tif (!img.is_valid()) {\n\t\tLOG(ERROR, \"File\", p_file_name, \" cannot be loaded\");\n\t\treturn Ref<Image>();\n\t}\n\tif (img->is_empty()) {\n\t\tLOG(ERROR, \"File\", p_file_name, \" is empty\");\n\t\treturn Ref<Image>();\n\t}\n\tLOG(DEBUG, \"Loaded Image size: \", img->get_size(), \" format: \", img->get_format());\n\treturn img;\n}\n\n/* From source RGB and selected source for Alpha channel, create a new RGBA image.\n * If p_invert_green is true, the destination green channel will be 1.0 - input green channel.\n * If p_invert_alpha is true, the destination alpha channel will be 1.0 - input source channel.\n */\nRef<Image> Terrain3DUtil::pack_image(const Ref<Image> &p_src_rgb, const Ref<Image> &p_src_a, const Ref<Image> &p_src_ao,\n\t\tconst bool p_invert_green, const bool p_invert_alpha, const bool p_normalize_alpha, const int p_alpha_channel, const int p_ao_channel) {\n\tif (!p_src_rgb.is_valid() || !p_src_a.is_valid()) {\n\t\tLOG(ERROR, \"Provided images are not valid. Cannot pack\");\n\t\treturn Ref<Image>();\n\t}\n\tif (p_src_rgb->get_size() != p_src_a->get_size()) {\n\t\tLOG(ERROR, \"Provided images are not the same size. Cannot pack\");\n\t\treturn Ref<Image>();\n\t}\n\tif (p_src_rgb->is_empty() || p_src_a->is_empty()) {\n\t\tLOG(ERROR, \"Provided images are empty. Cannot pack\");\n\t\treturn Ref<Image>();\n\t}\n\tif (p_alpha_channel < 0 || p_alpha_channel > 3) {\n\t\tLOG(ERROR, \"Source Channel of Height/Roughness invalid. Cannot Pack\");\n\t\treturn Ref<Image>();\n\t}\n\n\tbool pack_ao = p_src_ao.is_valid();\n\tif (pack_ao) {\n\t\tif (p_src_rgb->get_size() != p_src_ao->get_size()) {\n\t\t\tLOG(ERROR, \"Provided AO and normal images are not the same size. Cannot pack\");\n\t\t\treturn Ref<Image>();\n\t\t}\n\t}\n\n\treal_t a_max = 0.0f;\n\treal_t a_min = 0.0f;\n\treal_t contrast = 1.0f;\n\tif (p_normalize_alpha) {\n\t\ta_min = 1.0f;\n\t\t// Calculate contrast and offset so that we can make full use of the height channel range.\n\t\tfor (int y = 0; y < p_src_rgb->get_height(); y++) {\n\t\t\tfor (int x = 0; x < p_src_rgb->get_width(); x++) {\n\t\t\t\treal_t h = p_src_a->get_pixel(x, y)[p_alpha_channel];\n\t\t\t\ta_max = MAX(h, a_max);\n\t\t\t\ta_min = MIN(h, a_min);\n\t\t\t}\n\t\t}\n\t\tcontrast /= MAX(a_max - a_min, 1e-6f);\n\t}\n\n\tRef<Image> dst = Image::create_empty(p_src_rgb->get_width(), p_src_rgb->get_height(), false, Image::FORMAT_RGBA8);\n\tLOG(INFO, \"Creating image from source RGB + source channel images\");\n\tfor (int y = 0; y < p_src_rgb->get_height(); y++) {\n\t\tfor (int x = 0; x < p_src_rgb->get_width(); x++) {\n\t\t\tColor col = p_src_rgb->get_pixel(x, y);\n\t\t\tcol.a = p_src_a->get_pixel(x, y)[p_alpha_channel];\n\t\t\tif (p_normalize_alpha) {\n\t\t\t\tcol.a = CLAMP((col.a * contrast - a_min), 0.0f, 1.0f);\n\t\t\t}\n\t\t\tif (p_invert_green) {\n\t\t\t\tcol.g = 1.0f - col.g;\n\t\t\t}\n\t\t\tif (p_invert_alpha) {\n\t\t\t\tcol.a = 1.0f - col.a;\n\t\t\t}\n\t\t\tif (pack_ao) {\n\t\t\t\t// Compress range to avoid low AO values completely destroying normal vector precision - recovered in shader.\n\t\t\t\treal_t ao = sqrt(p_src_ao->get_pixel(x, y)[p_ao_channel]) * 0.5 + 0.5;\n\t\t\t\tcol.r = col.r * ao + (1.0f - ao) * 0.5f;\n\t\t\t\tcol.g = col.g * ao + (1.0f - ao) * 0.5f;\n\t\t\t\tcol.b = col.b * ao + (1.0f - ao) * 0.5f;\n\t\t\t}\n\t\t\tdst->set_pixel(x, y, col);\n\t\t}\n\t}\n\treturn dst;\n}\n\n// From source RGB, create a new L image that is scaled to use full 0 - 1 range.\nRef<Image> Terrain3DUtil::luminance_to_height(const Ref<Image> &p_src_rgb) {\n\tif (!p_src_rgb.is_valid()) {\n\t\tLOG(ERROR, \"Provided images are not valid. Cannot pack\");\n\t\treturn Ref<Image>();\n\t}\n\tif (p_src_rgb->is_empty()) {\n\t\tLOG(ERROR, \"Provided images are empty. Cannot pack\");\n\t\treturn Ref<Image>();\n\t}\n\treal_t lum_contrast;\n\treal_t l_max = 0.0f;\n\treal_t l_min = 1.0f;\n\t// Calculate contrast and offset so that we can make the most use of the height channel range.\n\tfor (int y = 0; y < p_src_rgb->get_height(); y++) {\n\t\tfor (int x = 0; x < p_src_rgb->get_width(); x++) {\n\t\t\tColor col = p_src_rgb->get_pixel(x, y);\n\t\t\treal_t l = 0.299f * col.r + 0.587f * col.g + 0.114f * col.b;\n\t\t\tl_max = MAX(l, l_max);\n\t\t\tl_min = MIN(l, l_min);\n\t\t}\n\t}\n\tlum_contrast = 1.0f / MAX(l_max - l_min, 1e-6);\n\tRef<Image> dst = Image::create_empty(p_src_rgb->get_width(), p_src_rgb->get_height(), false, Image::FORMAT_RGB8);\n\tfor (int y = 0; y < p_src_rgb->get_height(); y++) {\n\t\tfor (int x = 0; x < p_src_rgb->get_width(); x++) {\n\t\t\tColor col = p_src_rgb->get_pixel(x, y);\n\t\t\treal_t lum = 0.299f * col.r + 0.587f * col.g + 0.114f * col.b;\n\t\t\tlum = CLAMP((lum * lum_contrast - l_min), 0.0f, 1.0f);\n\t\t\t// some shaping\n\t\t\tcol.r = 0.5f - sin(asin(1.0f - 2.0f * lum) / 3.0f);\n\t\t\tcol.g = col.r;\n\t\t\tcol.b = col.r;\n\t\t\tcol.a = col.r;\n\t\t\tdst->set_pixel(x, y, col);\n\t\t}\n\t}\n\treturn dst;\n}\n\nvoid Terrain3DUtil::benchmark(Terrain3D *p_terrain) {\n\tif (!p_terrain) {\n\t\treturn;\n\t}\n\tconst Terrain3DData *data = p_terrain->get_data();\n\tif (!data) {\n\t\treturn;\n\t}\n\tuint64_t start_time;\n\tVector3 vec;\n\tColor col;\n\tfor (int i = 0; i < 3; i++) {\n\t\tstart_time = Time::get_singleton()->get_ticks_msec();\n\t\tfor (int j = 0; j < 10000000; j++) {\n\t\t\tcol = data->get_pixel(TYPE_HEIGHT, vec);\n\t\t}\n\t\tLOG(MESG, \"get_pixel() 10M: \", Time::get_singleton()->get_ticks_msec() - start_time, \"ms\");\n\t}\n\n\tvec = Vector3(0.5f, 0.f, 0.5f);\n\tfor (int i = 0; i < 3; i++) {\n\t\tstart_time = Time::get_singleton()->get_ticks_msec();\n\t\tfor (int j = 0; j < 1000000; j++) {\n\t\t\tdata->get_height(vec);\n\t\t}\n\t\tLOG(MESG, \"get_height() 1M interpolated: \", Time::get_singleton()->get_ticks_msec() - start_time, \"ms\");\n\t}\n\n\tfor (int i = 0; i < 2; i++) {\n\t\tstart_time = Time::get_singleton()->get_ticks_msec();\n\t\tp_terrain->bake_mesh(0);\n\t\tLOG(MESG, \"Bake ArrayMesh: \", Time::get_singleton()->get_ticks_msec() - start_time, \"ms\");\n\t}\n}\n\n///////////////////////////\n// Protected Functions\n///////////////////////////\n\nvoid Terrain3DUtil::_bind_methods() {\n\t// Control map converters\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"as_float\", \"value\"), &as_float);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"as_uint\", \"value\"), &as_uint);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_base\", \"pixel\"), &gd_get_base);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_base\", \"base\"), &gd_enc_base);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_overlay\", \"pixel\"), &gd_get_overlay);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_overlay\", \"overlay\"), &gd_enc_overlay);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_blend\", \"pixel\"), &gd_get_blend);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_blend\", \"blend\"), &gd_enc_blend);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_uv_rotation\", \"pixel\"), &gd_get_uv_rotation);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_uv_rotation\", \"rotation\"), &gd_enc_uv_rotation);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_uv_scale\", \"pixel\"), &gd_get_uv_scale);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_uv_scale\", \"scale\"), &gd_enc_uv_scale);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"is_hole\", \"pixel\"), &gd_is_hole);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_hole\", \"pixel\"), &enc_hole);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"is_nav\", \"pixel\"), &gd_is_nav);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_nav\", \"pixel\"), &enc_nav);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"is_auto\", \"pixel\"), &gd_is_auto);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"enc_auto\", \"pixel\"), &enc_auto);\n\n\t// String functions\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"filename_to_location\", \"filename\"), &Terrain3DUtil::filename_to_location);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"location_to_filename\", \"region_location\"), &Terrain3DUtil::location_to_filename);\n\n\t// Image handling\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"black_to_alpha\", \"image\"), &Terrain3DUtil::black_to_alpha);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_min_max\", \"image\"), &Terrain3DUtil::get_min_max);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_thumbnail\", \"image\", \"size\"), &Terrain3DUtil::get_thumbnail, DEFVAL(V2I(256)));\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"get_filled_image\", \"size\", \"color\", \"create_mipmaps\", \"format\"), &Terrain3DUtil::get_filled_image);\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"load_image\", \"file_name\", \"cache_mode\", \"r16_height_range\", \"r16_size\"), &Terrain3DUtil::load_image, DEFVAL(ResourceLoader::CACHE_MODE_IGNORE), DEFVAL(Vector2(0.f, 255.f)), DEFVAL(V2I_ZERO));\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"pack_image\", \"src_rgb\", \"src_a\", \"src_ao\", \"invert_green\", \"invert_alpha\", \"normalize_alpha\", \"alpha_channel\", \"ao_channel\"), &Terrain3DUtil::pack_image, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(0), DEFVAL(0));\n\tClassDB::bind_static_method(\"Terrain3DUtil\", D_METHOD(\"luminance_to_height\", \"src_rgb\"), &Terrain3DUtil::luminance_to_height);\n}\n"
  },
  {
    "path": "src/terrain_3d_util.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef TERRAIN3D_UTIL_CLASS_H\n#define TERRAIN3D_UTIL_CLASS_H\n\n#include <godot_cpp/classes/image.hpp>\n#include <godot_cpp/classes/node.hpp>\n#include <godot_cpp/classes/resource_loader.hpp>\n#include <godot_cpp/variant/utility_functions.hpp>\n\n#include \"constants.h\"\n#include \"generated_texture.h\"\n\nclass Terrain3D;\n\n// This file holds stateless utility functions for both C++ and GDScript\n// The class exposes static member and inline functions to GDscript\n// The inline functions below are not part of the class but are in the namespace, eg bilerp\n// However some of these inline functions are also exposed to GDScript\n\nclass Terrain3DUtil : public Object {\n\tGDCLASS(Terrain3DUtil, Object);\n\tCLASS_NAME_STATIC(\"Terrain3DUtil\");\n\npublic:\n\t// Print info to the console\n\tstatic void print_arr(const String &p_name, const Array &p_arr, const int p_level = 2); // Level 2: DEBUG\n\tstatic void print_dict(const String &p_name, const Dictionary &p_dict, const int p_level = 2); // Level 2: DEBUG\n\tstatic void dump_gentex(const GeneratedTexture &p_gen, const String &p_name = \"\");\n\tstatic void dump_maps(const TypedArray<Image> &p_maps, const String &p_name = \"\");\n\n\t// String functions\n\tstatic Vector2i filename_to_location(const String &p_filename);\n\tstatic Vector2i string_to_location(const String &p_string);\n\tstatic String location_to_filename(const Vector2i &p_region_loc);\n\tstatic String location_to_string(const Vector2i &p_region_loc);\n\tstatic PackedStringArray get_files(const String &p_dir, const String &p_glob = \"*\");\n\n\t// Image operations\n\tstatic Ref<Image> black_to_alpha(const Ref<Image> &p_image);\n\tstatic Vector2 get_min_max(const Ref<Image> &p_image);\n\tstatic Ref<Image> get_thumbnail(const Ref<Image> &p_image, const Vector2i &p_size = V2I(256));\n\tstatic Ref<Image> get_filled_image(const Vector2i &p_size,\n\t\t\tconst Color &p_color = COLOR_BLACK,\n\t\t\tconst bool p_create_mipmaps = true,\n\t\t\tconst Image::Format p_format = Image::FORMAT_MAX);\n\tstatic Ref<Image> load_image(const String &p_file_name, const int p_cache_mode = ResourceLoader::CACHE_MODE_IGNORE,\n\t\t\tconst Vector2 &p_r16_height_range = Vector2(0.f, 255.f), const Vector2i &p_r16_size = V2I_ZERO);\n\tstatic Ref<Image> pack_image(const Ref<Image> &p_src_rgb,\n\t\t\tconst Ref<Image> &p_src_a,\n\t\t\tconst Ref<Image> &p_src_ao,\n\t\t\tconst bool p_invert_green = false,\n\t\t\tconst bool p_invert_alpha = false,\n\t\t\tconst bool p_normalize_alpha = false,\n\t\t\tconst int p_alpha_channel = 0,\n\t\t\tconst int p_ao_channel = 0);\n\tstatic Ref<Image> luminance_to_height(const Ref<Image> &p_src_rgb);\n\tstatic void benchmark(Terrain3D *p_terrain);\n\nprotected:\n\tstatic void _bind_methods();\n};\n\nusing Util = Terrain3DUtil;\n\n// Inline Functions\n\n///////////////////////////\n// Type Conversion\n///////////////////////////\n\n// Convert Vector3 to Vector2i, ignoring Y\ninline Vector2i v3v2i(const Vector3 &p_v3) {\n\treturn Vector2i(p_v3.x, p_v3.z);\n}\n\n// Convert Vector2i to Vector3, ignoring Y\ninline Vector3 v2iv3(const Vector2i &p_v2) {\n\treturn Vector3(p_v2.x, 0., p_v2.y);\n}\n\n// Convert Vector3 to Vector2, ignoring Y\ninline Vector2 v3v2(const Vector3 &p_v3) {\n\treturn Vector2(p_v3.x, p_v3.z);\n}\n\n// Convert Vector2 to Vector3, ignoring Y\ninline Vector3 v2v3(const Vector2 &p_v2) {\n\treturn Vector3(p_v2.x, 0., p_v2.y);\n}\n\n///////////////////////////\n// Math\n///////////////////////////\n\ninline bool is_valid_region_size(int value) {\n\treturn value >= 64 && value <= 2048 && is_power_of_2(value);\n}\n\n// Integer round to multiples\n// https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number\n\n// Integer round up to a multiple\ntemplate <typename T>\ninline T int_ceil_mult(const T numToRound, const T multiple) {\n\tstatic_assert(std::numeric_limits<T>::is_integer, \"Only integer types are allowed\");\n\tASSERT(multiple != 0, 0);\n\tT isPositive = (T)(numToRound >= 0);\n\treturn ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple;\n}\n\n// Integer round up to a power of 2 multiple (3.7x faster)\ntemplate <typename T>\ninline T int_ceil_pow2(T numToRound, T multiple) {\n\tstatic_assert(std::numeric_limits<T>::is_integer, \"Only integer types are allowed\");\n\tASSERT(is_power_of_2(multiple), int_ceil_mult(numToRound, multiple));\n\treturn (numToRound + multiple - 1) & -multiple;\n}\n\n// Integer round to nearest +/- multiple\n// https://stackoverflow.com/questions/29557459/round-to-nearest-multiple-of-a-number\ntemplate <typename T>\ninline T int_round_mult(const T numToRound, const T multiple) {\n\tstatic_assert(std::numeric_limits<T>::is_integer, \"Only integer types are allowed\");\n\tASSERT(multiple != 0, 0);\n\tT abs_num;\n\tif constexpr (std::is_signed_v<T>) {\n\t\tabs_num = std::abs(numToRound);\n\t} else {\n\t\tabs_num = numToRound;\n\t}\n\tT result = abs_num + multiple / 2;\n\tresult -= result % multiple;\n\tresult *= numToRound > 0 ? 1 : -1;\n\treturn result;\n}\n\n// Integer division with rounding up, down, nearest\n// https : //stackoverflow.com/questions/2422712/rounding-integer-division-instead-of-truncating/58568736#58568736\n#define V2I_DIVIDE_CEIL(v, f) Vector2i(int_divide_ceil(v.x, int32_t(f)), int_divide_ceil(v.y, int32_t(f)))\n#define V2I_DIVIDE_FLOOR(v, f) Vector2i(int_divide_floor(v.x, int32_t(f)), int_divide_floor(v.y, int32_t(f)))\n\n// Integer division rounding up\ntemplate <typename T>\ninline T int_divide_ceil(const T numer, const T denom) {\n\tstatic_assert(std::numeric_limits<T>::is_integer, \"Only integer types are allowed\");\n\tT result = ((numer) < 0) != ((denom) < 0) ? (numer) / (denom) : ((numer) + ((denom) < 0 ? (denom) + 1 : (denom)-1)) / (denom);\n\treturn result;\n}\n\n// Integer division rounding down\ntemplate <typename T>\ninline T int_divide_floor(const T numer, const T denom) {\n\tstatic_assert(std::numeric_limits<T>::is_integer, \"Only integer types are allowed\");\n\tT result = ((numer) < 0) != ((denom) < 0) ? ((numer) - ((denom) < 0 ? (denom) + 1 : (denom)-1)) / (denom) : (numer) / (denom);\n\treturn result;\n}\n\n// Integer division rounding to nearest int\ntemplate <typename T>\ninline T int_divide_round(const T numer, const T denom) {\n\tstatic_assert(std::numeric_limits<T>::is_integer, \"Only integer types are allowed\");\n\tT result = ((numer) < 0) != ((denom) < 0) ? ((numer) - ((denom) / 2)) / (denom) : ((numer) + ((denom) / 2)) / (denom);\n\treturn result;\n}\n\n// Returns the bilinearly interpolated value derived from parameters:\n// * 4 values to be interpolated\n// * Positioned at the 4 corners of the p_pos00 - p_pos11 rectangle\n// * Interpolated to the position p_pos, which is global, not a 0-1 percentage\ninline real_t bilerp(const real_t p_v00, const real_t p_v01, const real_t p_v10, const real_t p_v11,\n\t\tconst Vector2 &p_pos00, const Vector2 &p_pos11, const Vector2 &p_pos) {\n\treal_t x2x1 = p_pos11.x - p_pos00.x;\n\treal_t y2y1 = p_pos11.y - p_pos00.y;\n\treal_t x2x = p_pos11.x - p_pos.x;\n\treal_t y2y = p_pos11.y - p_pos.y;\n\treal_t xx1 = p_pos.x - p_pos00.x;\n\treal_t yy1 = p_pos.y - p_pos00.y;\n\treturn (p_v00 * x2x * y2y +\n\t\t\t\t   p_v01 * x2x * yy1 +\n\t\t\t\t   p_v10 * xx1 * y2y +\n\t\t\t\t   p_v11 * xx1 * yy1) /\n\t\t\t(x2x1 * y2y1);\n}\n\ninline real_t bilerp(const real_t p_v00, const real_t p_v01, const real_t p_v10, const real_t p_v11,\n\t\tconst Vector3 &p_pos00, const Vector3 &p_pos11, const Vector3 &p_pos) {\n\tVector2 pos00 = Vector2(p_pos00.x, p_pos00.z);\n\tVector2 pos11 = Vector2(p_pos11.x, p_pos11.z);\n\tVector2 pos = Vector2(p_pos.x, p_pos.z);\n\treturn bilerp(p_v00, p_v01, p_v10, p_v11, pos00, pos11, pos);\n}\n\ninline Rect2 aabb2rect(const AABB &p_aabb) {\n\tRect2 rect;\n\trect.position = Vector2(p_aabb.position.x, p_aabb.position.z);\n\trect.size = Vector2(p_aabb.size.x, p_aabb.size.z);\n\treturn rect;\n}\n\n// Functions to match GLSL when needing to duplicate shader code CPU side.\ninline real_t smoothstep(const real_t p_low, const real_t p_high, const real_t p_value) {\n\treal_t t = CLAMP((p_value - p_low) / (p_high - p_low), 0.f, 1.f);\n\treturn t * t * (3.f - 2.f * t);\n}\n\ninline Vector2 smoothstep(const real_t p_low, const real_t p_high, const Vector2 &p_value) {\n\tVector2 t = (p_value - Vector2(p_low, p_low)) / (p_high - p_low);\n\tt.x = CLAMP(t.x, 0.f, 1.f);\n\tt.y = CLAMP(t.y, 0.f, 1.f);\n\treturn t * t * (Vector2(3.f, 3.f) - 2.f * t);\n}\n\ninline Vector3 smoothstep(const real_t p_low, const real_t p_high, const Vector3 &p_value) {\n\tVector3 t = (p_value - Vector3(p_low, p_low, p_low)) / (p_high - p_low);\n\tt.x = CLAMP(t.x, 0.f, 1.f);\n\tt.y = CLAMP(t.y, 0.f, 1.f);\n\tt.z = CLAMP(t.z, 0.f, 1.f);\n\treturn t * t * (Vector3(3.f, 3.f, 3.f) - 2.f * t);\n}\n\n///////////////////////////\n// Controlmap Handling\n///////////////////////////\n\n// Getters read the 32-bit float as a 32-bit uint, then mask bits to retreive value\n// Encoders return a full 32-bit uint with bits in the proper place for ORing\n// Aliases for GDScript prefixed with gd_ since it can't handle overridden functions\ninline float as_float(const uint32_t p_value) { return *(float *)&p_value; }\ninline uint32_t as_uint(const float p_value) { return *(uint32_t *)&p_value; }\n\ninline uint8_t get_base(const uint32_t p_pixel) { return p_pixel >> 27 & 0x1F; }\ninline uint8_t get_base(const float p_pixel) { return get_base(as_uint(p_pixel)); }\ninline uint32_t enc_base(const uint8_t p_base) { return (p_base & 0x1F) << 27; }\ninline uint32_t gd_get_base(const uint32_t p_pixel) { return get_base(p_pixel); }\ninline uint32_t gd_enc_base(const uint32_t p_base) { return enc_base(p_base); }\n\ninline uint8_t get_overlay(const uint32_t p_pixel) { return p_pixel >> 22 & 0x1F; }\ninline uint8_t get_overlay(const float p_pixel) { return get_overlay(as_uint(p_pixel)); }\ninline uint32_t enc_overlay(const uint8_t p_over) { return (p_over & 0x1F) << 22; }\ninline uint32_t gd_get_overlay(const uint32_t p_pixel) { return get_overlay(p_pixel); }\ninline uint32_t gd_enc_overlay(const uint32_t p_over) { return enc_overlay(p_over); }\n\ninline uint8_t get_blend(const uint32_t p_pixel) { return p_pixel >> 14 & 0xFF; }\ninline uint8_t get_blend(const float p_pixel) { return get_blend(as_uint(p_pixel)); }\ninline uint32_t enc_blend(const uint8_t p_blend) { return (p_blend & 0xFF) << 14; }\ninline uint32_t gd_get_blend(const uint32_t p_pixel) { return get_blend(p_pixel); }\ninline uint32_t gd_enc_blend(const uint32_t p_blend) { return enc_blend(p_blend); }\n\ninline uint8_t get_uv_rotation(const uint32_t p_pixel) { return p_pixel >> 10 & 0xF; }\ninline uint8_t get_uv_rotation(const float p_pixel) { return get_uv_rotation(as_uint(p_pixel)); }\ninline uint32_t enc_uv_rotation(const uint8_t p_rotation) { return (p_rotation & 0xF) << 10; }\ninline uint32_t gd_get_uv_rotation(const uint32_t p_pixel) { return get_uv_rotation(p_pixel); }\ninline uint32_t gd_enc_uv_rotation(const uint32_t p_rotation) { return enc_uv_rotation(p_rotation); }\n\ninline uint8_t get_uv_scale(const uint32_t p_pixel) { return p_pixel >> 7 & 0x7; }\ninline uint8_t get_uv_scale(const float p_pixel) { return get_uv_scale(as_uint(p_pixel)); }\ninline uint32_t enc_uv_scale(const uint8_t p_scale) { return (p_scale & 0x7) << 7; }\ninline uint32_t gd_get_uv_scale(const uint32_t p_pixel) { return get_uv_scale(p_pixel); }\ninline uint32_t gd_enc_uv_scale(const uint32_t p_scale) { return enc_uv_scale(p_scale); }\n\ninline bool is_hole(const uint32_t p_pixel) { return (p_pixel >> 2 & 0x1) == 1; }\ninline bool is_hole(const float p_pixel) { return is_hole(as_uint(p_pixel)); }\ninline uint32_t enc_hole(const bool p_hole) { return (p_hole & 0x1) << 2; }\ninline bool gd_is_hole(const uint32_t p_pixel) { return is_hole(p_pixel); }\n\ninline bool is_nav(const uint32_t p_pixel) { return (p_pixel >> 1 & 0x1) == 1; }\ninline bool is_nav(const float p_pixel) { return is_nav(as_uint(p_pixel)); }\ninline uint32_t enc_nav(const bool p_nav) { return (p_nav & 0x1) << 1; }\ninline bool gd_is_nav(const uint32_t p_pixel) { return is_nav(p_pixel); }\n\ninline bool is_auto(const uint32_t p_pixel) { return (p_pixel & 0x1) == 1; }\ninline bool is_auto(const float p_pixel) { return is_auto(as_uint(p_pixel)); }\ninline uint32_t enc_auto(const bool p_auto) { return p_auto & 0x1; }\ninline bool gd_is_auto(const uint32_t p_pixel) { return is_auto(p_pixel); }\n\n///////////////////////////\n// Memory\n///////////////////////////\n\ntemplate <typename TType>\n_FORCE_INLINE_ bool memdelete_safely(TType *&p_ptr) {\n\tif (p_ptr) {\n\t\tmemdelete(p_ptr);\n\t\tp_ptr = nullptr;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n_FORCE_INLINE_ bool remove_from_tree(Node *p_node) {\n\t// Note: is_in_tree() doesn't work in Godot-cpp 4.1.3\n\tif (p_node) {\n\t\tNode *parent = p_node->get_parent();\n\t\tif (parent) {\n\t\t\tparent->remove_child(p_node);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n_FORCE_INLINE_ String ptr_to_str(const void *p_ptr) {\n\treturn \"0x\" + String::num_uint64(uint64_t(p_ptr), 16, true);\n}\n\n// Returns if A is shallow different from B\n// O(1) size and pointer compare for Array/Dictionary/String/StringName/NodePath\n// Operator==() otherwise\n// Two arrays/dicts with different pointers could have the same value, this will return true\n// Two strings or ints with different pointers but the same value will return false\n// Could be extended for PackedArray and other special types\ntemplate <typename T>\n_FORCE_INLINE_ bool differs(const T &a, const T &b) {\n\tif constexpr (std::is_base_of_v<String, T>) {\n\t\tif (a.length() != b.length()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (a.is_empty()) { // Both empty\n\t\t\treturn false;\n\t\t}\n\t\t// Both non-empty, but same length\n\t\tif (a.ptr() == b.ptr()) { // Same COW buffer\n\t\t\treturn false;\n\t\t}\n\t\treturn a != b; // Full comparison\n\t} else if constexpr (std::is_base_of_v<Array, T> || std::is_base_of_v<Dictionary, T>) {\n\t\tif (a.size() != b.size()) { // Redundant but common path predictable\n\t\t\treturn true;\n\t\t}\n\t\t// These store a real GDExtensionTypePtr (void**) at offset 0\n\t\treturn *(const void **)a._native_ptr() != *(const void **)b._native_ptr();\n\t} else {\n\t\treturn a != b;\n\t}\n}\n\n// Sets A if different from B, otherwise returns\n#define SET_IF_DIFF(a, b) \\\n\tif (differs(a, b)) {  \\\n\t\ta = b;            \\\n\t} else {              \\\n\t\treturn;           \\\n\t}\n\n#endif // TERRAIN3D_UTIL_CLASS_H\n"
  },
  {
    "path": "src/unit_testing.cpp",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#include \"unit_testing.h\"\n#include \"terrain_3d_util.h\"\n\nvoid test_differs() {\n\tUtilityFunctions::print(\"=== Testing differs function ===\");\n\n\t// Helper to log differs result with expected and PASS/FAIL\n\tauto log_differs = [](const auto &a, const auto &b, const String &desc, bool expected) {\n\t\tbool d = differs(a, b);\n\t\tString result = (d == expected) ? \"PASSED\" : \"FAILED\";\n\t\tUtilityFunctions::print(desc, \": differs(\", a, \", \", b, \") = \", d, \" - \", result);\n\t};\n\n\t// 1. Scalars: int, float (should use ==, differs if values differ)\n\t{\n\t\tint i1 = 42;\n\t\tint i2 = 42; // Same\n\t\tint i3 = 43; // Diff\n\t\tlog_differs(i1, i2, \"int same\", false);\n\t\tlog_differs(i1, i3, \"int diff\", true);\n\n\t\tdouble f1 = 3.14;\n\t\tdouble f2 = 3.14; // Same\n\t\tdouble f3 = 3.14159; // Diff\n\t\tlog_differs(f1, f2, \"float same\", false);\n\t\tlog_differs(f1, f3, \"float diff\", true);\n\t}\n\n\t// 2. Vectors: Vector2, Vector2i, Vector3, Vector3i (should use ==)\n\t{\n\t\tVector2 v2_1(1.0, 2.0);\n\t\tVector2 v2_2(1.0, 2.0); // Same\n\t\tVector2 v2_3(1.0, 3.0); // Diff\n\t\tlog_differs(v2_1, v2_2, \"Vector2 same\", false);\n\t\tlog_differs(v2_1, v2_3, \"Vector2 diff\", true);\n\n\t\tVector2i v2i_1(1, 2);\n\t\tVector2i v2i_2(1, 2); // Same\n\t\tVector2i v2i_3(1, 3); // Diff\n\t\tlog_differs(v2i_1, v2i_2, \"Vector2i same\", false);\n\t\tlog_differs(v2i_1, v2i_3, \"Vector2i diff\", true);\n\n\t\tVector3 v3_1(1.0, 2.0, 3.0);\n\t\tVector3 v3_2(1.0, 2.0, 3.0); // Same\n\t\tVector3 v3_3(1.0, 2.0, 4.0); // Diff\n\t\tlog_differs(v3_1, v3_2, \"Vector3 same\", false);\n\t\tlog_differs(v3_1, v3_3, \"Vector3 diff\", true);\n\n\t\tVector3i v3i_1(1, 2, 3);\n\t\tVector3i v3i_2(1, 2, 3); // Same\n\t\tVector3i v3i_3(1, 2, 4); // Diff\n\t\tlog_differs(v3i_1, v3i_2, \"Vector3i same\", false);\n\t\tlog_differs(v3i_1, v3i_3, \"Vector3i diff\", true);\n\t}\n\n\t// 3. String: Share (COW), same value diff ptr, diff value\n\t{\n\t\tString s1 = \"test\";\n\t\tString s2 = s1; // Shared (COW)\n\t\tString s3(\"test\"); // Separate alloc, same value\n\t\tString s4 = \"diff\";\n\t\tlog_differs(String(), String(), \"String() vs String()\", false);\n\t\tlog_differs(String(), String(\"a\"), \"String() vs String('a')\", true);\n\t\tlog_differs(s1, s2, \"String shared\", false);\n\t\tlog_differs(s1, s3, \"String same value diff ptr\", false); // Length match + == true\n\t\tlog_differs(s1, s4, \"String diff value\", true); // Length may match, but == false\n\t}\n\n\t// 4. StringName: Similar to String (uses ==, but test sharing if applicable)\n\t{\n\t\tStringName sn1 = \"test\";\n\t\tStringName sn2 = sn1; // Shared\n\t\tStringName sn3(\"test\"); // Separate, same value\n\t\tStringName sn4 = \"diff\";\n\t\tlog_differs(sn1, sn2, \"StringName shared\", false);\n\t\tlog_differs(sn1, sn3, \"StringName same value diff ptr\", false); // == handles\n\t\tlog_differs(sn1, sn4, \"StringName diff value\", true);\n\t}\n\n\t// 5. Array: Share vs mutate\n\t{\n\t\tArray arr1;\n\t\tarr1.push_back(42);\n\t\tArray arr2 = arr1; // Shared\n\t\tArray arr3; // Empty diff\n\t\tarr3.push_back(42); // Same content, but separate (differs=true, conservative)\n\t\tArray empty_arr; // Explicit empty for size diff\n\t\tlog_differs(arr1, arr2, \"Array shared\", false); // Same self ptr\n\t\tlog_differs(arr1, arr3, \"Array same content diff ptr\", true); // Diff self ptr (no fallback for Array)\n\t\tlog_differs(arr1, empty_arr, \"Array size diff\", true); // Size mismatch\n\t}\n\n\t// 6. TypedArray: e.g., TypedArray<int>\n\t{\n\t\tTypedArray<int> ta1;\n\t\tta1.push_back(42);\n\t\tTypedArray<int> ta2 = ta1; // Shared\n\t\tTypedArray<int> ta3; // Empty\n\t\tta3.push_back(42); // Separate\n\t\tTypedArray<int> empty_ta; // Explicit empty\n\t\tlog_differs(ta1, ta2, \"TypedArray shared\", false); // Via Array base\n\t\tlog_differs(ta1, ta3, \"TypedArray same content diff ptr\", true); // Diff self\n\t\tlog_differs(ta1, empty_ta, \"TypedArray size diff\", true); // Size mismatch\n\t}\n\n\t// 7. Dictionary: Share vs mutate\n\t{\n\t\tDictionary dict1;\n\t\tdict1[\"key\"] = 42;\n\t\tDictionary dict2 = dict1; // Shared\n\t\tDictionary dict3; // Empty\n\t\tdict3[\"key\"] = 42; // Separate\n\t\tDictionary empty_dict; // Explicit empty\n\t\tlog_differs(dict1, dict2, \"Dictionary shared\", false);\n\t\tlog_differs(dict1, dict3, \"Dictionary same content diff ptr\", true); // Diff self\n\t\tlog_differs(dict1, empty_dict, \"Dictionary size diff\", true); // Size mismatch\n\t}\n\n\t// 8. Variant types: Wrap above (falls to ==)\n\t{\n\t\tVariant v_int1 = 42;\n\t\tVariant v_int2 = 42; // Same\n\t\tVariant v_int3 = 43; // Diff\n\t\tlog_differs(v_int1, v_int2, \"Variant int same\", false);\n\t\tlog_differs(v_int1, v_int3, \"Variant int diff\", true);\n\n\t\tVariant v_float1 = 3.14;\n\t\tVariant v_float2 = 3.14;\n\t\tVariant v_float3 = 3.14159;\n\t\tlog_differs(v_float1, v_float2, \"Variant float same\", false);\n\t\tlog_differs(v_float1, v_float3, \"Variant float diff\", true);\n\n\t\tVariant v_str1 = String(\"test\");\n\t\tVariant v_str2 = String(\"test\");\n\t\tVariant v_str3 = String(\"diff\");\n\t\tlog_differs(v_str1, v_str2, \"Variant String same\", false);\n\t\tlog_differs(v_str1, v_str3, \"Variant String diff\", true);\n\n\t\t// Variant Object (use RefCounted for ref support)\n\t\tRef<RefCounted> rc1 = memnew(RefCounted);\n\t\tRef<RefCounted> rc2 = rc1; // Same ref\n\t\tRef<RefCounted> rc3 = memnew(RefCounted); // Diff\n\t\tVariant v_rc1 = rc1;\n\t\tVariant v_rc2 = rc2;\n\t\tVariant v_rc3 = rc3;\n\t\tlog_differs(v_rc1, v_rc2, \"Variant RefCounted same ref\", false); // Ref == true\n\t\tlog_differs(v_rc1, v_rc3, \"Variant RefCounted diff ref\", true);\n\n\t\t// Variant Array\n\t\tArray arr_var1;\n\t\tarr_var1.push_back(42);\n\t\tVariant v_arr1 = arr_var1;\n\t\tArray arr_var2 = arr_var1;\n\t\tVariant v_arr2 = arr_var2;\n\t\tlog_differs(v_arr1, v_arr2, \"Variant Array shared\", false); // Variant == checks inner\n\n\t\t// Variant Dictionary\n\t\tDictionary dict_var1;\n\t\tdict_var1[\"key\"] = 42;\n\t\tVariant v_dict1 = dict_var1;\n\t\tDictionary dict_var2 = dict_var1;\n\t\tVariant v_dict2 = dict_var2;\n\t\tlog_differs(v_dict1, v_dict2, \"Variant Dictionary shared\", false);\n\t}\n\n\tUtilityFunctions::print(\"=== End differs tests ===\");\n}"
  },
  {
    "path": "src/unit_testing.h",
    "content": "// Copyright © 2023-2026 Cory Petkovsek, Roope Palmroos, and Contributors.\n\n#ifndef UNIT_TESTING_H\n#define UNIT_TESTING_H\n\n#define EXPECT_FALSE(cond)                              \\\n\tdo {                                                \\\n\t\tif (cond) {                                     \\\n\t\t\tUtilityFunctions::print(\"FAILED: \", #cond); \\\n\t\t} else {                                        \\\n\t\t\tUtilityFunctions::print(\"PASSED: \", #cond); \\\n\t\t}                                               \\\n\t} while (0)\n\n#define EXPECT_TRUE(cond)                               \\\n\tdo {                                                \\\n\t\tif (cond) {                                     \\\n\t\t\tUtilityFunctions::print(\"PASSED: \", #cond); \\\n\t\t} else {                                        \\\n\t\t\tUtilityFunctions::print(\"FAILED: \", #cond); \\\n\t\t}                                               \\\n\t} while (0)\n\nvoid test_differs();\n\n#endif // UNIT_TESTING_H\n"
  },
  {
    "path": "tools/build_release.sh",
    "content": "#!/usr/bin/bash\n\n## This file has been superseded by the github automated builds configured under /.github\n\nif [ \"$3\" == \"\" ]; then\n\techo \"Usage: $0 <platform> <version> <godot version>\"\n\techo \"Platforms: linux|windows|all\"\n\techo \"e.g. $0 linux 0.8.1-alpha 4.1.1\"\n\texit 1\nfi\n\nNAME=terrain3d\nPLATFORM=$1\nVERSION=$2\nGDVER=$3\nZIPFILE=\"${NAME}_${VERSION}_gd${GDVER}\"\nBIN=\"addons/terrain_3d/bin\"\nSTRIP=`which strip 2>/dev/null`\nINCLUDE=\"addons/terrain_3d demo project.godot\"\nADD=\"README.md LICENSE CONTRIBUTORS.md\"\nEXCLUDE=\"$BIN/*.lib $BIN/*.exp\"\nWIN_DBG_FILE=\"$BIN/libterrain.windows.debug.x86_64.dll\"\nWIN_REL_FILE=\"$BIN/libterrain.windows.release.x86_64.dll\"\nLIN_DBG_FILE=\"$BIN/libterrain.linux.debug.x86_64.so\"\nLIN_REL_FILE=\"$BIN/libterrain.linux.release.x86_64.so\"\n\nif [ ! -e .git ]; then\n\techo .git dir not found. May not be in git root.\n\texit 1\nfi\nif [ ! -e \"project/$BIN\" ]; then\n\techo Bin dir not found. May not be in git root.\n\texit 1\nfi\n\n\n# Build Windows\nif [ \"$PLATFORM\" == \"windows\" ] || [ \"$PLATFORM\" == \"all\" ]; then \n\t#if [ ! -e $WIN_DBG_FILE ] || [ ! -e $WIN_REL_FILE ]; then \n\t\tscons platform=windows && scons platform=windows target=template_release debug_symbols=false\n\t\tif [ $? -gt 0 ]; then\n\t\t\techo Problem building. Exiting packaging script.\n\t\t\texit $?\n\t\tfi\n\t\tif [ \"$STRIP\" != \"\" ]; then \n\t\t\tstrip project/$WIN_REL_FILE\n\t\tfi\n\t#fi\n\tpushd project > /dev/null\n\tzip -r ../${ZIPFILE}_win64.zip $INCLUDE -x $EXCLUDE $LIN_DBG_FILE $LIN_REL_FILE\n\tpopd > /dev/null\n\tzip -u ${ZIPFILE}_win64.zip $ADD\nfi\n\n# Build Linux\nif [ \"$PLATFORM\" == \"linux\" ] || [ \"$PLATFORM\" == \"all\" ]; then \n\t#if [ ! -e $LIN_DBG_FILE ] || [ ! -e $LIN_REL_FILE ]; then \n\t\tscons platform=linux && scons platform=linux target=template_release debug_symbols=false\n\t\tif [ $? -gt 0 ]; then\n\t\t\techo Problem building. Exiting packaging script.\n\t\t\texit $?\n\t\tfi\n\t\tif [ \"$STRIP\" != \"\" ]; then \n\t\t\tstrip project/$LIN_REL_FILE\n\t\tfi\n\t#fi\n\tpushd project > /dev/null\n\tzip -r ../${ZIPFILE}_linux.x86-64.zip $INCLUDE -x $EXCLUDE $WIN_DBG_FILE $WIN_REL_FILE\n\tpopd > /dev/null\n\tzip -u ${ZIPFILE}_linux.x86-64.zip $ADD\nfi\n\n"
  },
  {
    "path": "tools/hooks/asmessage.applescript",
    "content": "on run argv\n    set vButtons to { \"OK\" }\n    set vButtonCodes to { 0 }\n    set vDbutton to \"OK\"\n    set vText to \"\"\n    set vTitle to \"\"\n    set vTimeout to -1\n\n    repeat with i from 1 to length of argv\n        try\n            set vArg to item i of argv\n            if vArg = \"-buttons\" then\n                set vButtonsAndCodes to my fSplit(item (i + 1) of argv, \",\")\n                set vButtons to {}\n                set vButtonCodes to {}\n                repeat with j from 1 to length of vButtonsAndCodes\n                    set vBtn to my fSplit(item j of vButtonsAndCodes, \":\")\n                    copy (item 1 of vBtn) to the end of the vButtons\n                    copy (item 2 of vBtn) to the end of the vButtonCodes\n                end repeat\n            else if vArg = \"-title\" then\n                set vTitle to item (i + 1) of argv\n            else if vArg = \"-center\" then\n                -- not supported\n            else if vArg = \"-default\" then\n                set vDbutton to item (i + 1) of argv\n            else if vArg = \"-geometry\" then\n                -- not supported\n            else if vArg = \"-nearmouse\" then\n                -- not supported\n            else if vArg = \"-timeout\" then\n                set vTimeout to item (i + 1) of argv as integer\n            else if vArg = \"-file\" then\n                set vText to read (item (i + 1) of argv) as string\n            else if vArg = \"-text\" then\n                set vText to item (i + 1) of argv\n            end if\n        end try\n    end repeat\n\n    set vDlg to display dialog vText buttons vButtons default button vDbutton with title vTitle giving up after vTimeout with icon stop\n    set vRet to button returned of vDlg\n    repeat with i from 1 to length of vButtons\n        set vBtn to item i of vButtons\n        if vBtn = vRet\n            return item i of vButtonCodes\n        end if\n    end repeat\n\n    return 0\nend run\n\non fSplit(vString, vDelimiter)\n    set oldDelimiters to AppleScript's text item delimiters\n    set AppleScript's text item delimiters to vDelimiter\n    set vArray to every text item of vString\n    set AppleScript's text item delimiters to oldDelimiters\n    return vArray\nend fSplit\n"
  },
  {
    "path": "tools/hooks/canonicalize_filename.sh",
    "content": "#!/bin/sh\n\n# Provide the canonicalize filename (physical filename with out any symlinks)\n# like the GNU version readlink with the -f option regardless of the version of\n# readlink (GNU or BSD).\n\n# This file is part of a set of unofficial pre-commit hooks available\n# at github.\n# Link:    https://github.com/githubbrowser/Pre-commit-hooks\n# Contact: David Martin, david.martin.mailbox@googlemail.com\n\n###########################################################\n# There should be no need to change anything below this line.\n\n# Canonicalize by recursively following every symlink in every component of the\n# specified filename. This should reproduce the results of the GNU version of\n# readlink with the -f option.\n#\n# Reference: https://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac\ncanonicalize_filename () {\n    local target_file=\"$1\"\n    local physical_directory=\"\"\n    local result=\"\"\n\n    # Need to restore the working directory after work.\n    local working_dir=\"`pwd`\"\n\n    cd -- \"$(dirname -- \"$target_file\")\"\n    target_file=\"$(basename -- \"$target_file\")\"\n\n    # Iterate down a (possible) chain of symlinks\n    while [ -L \"$target_file\" ]\n    do\n        target_file=\"$(readlink -- \"$target_file\")\"\n        cd -- \"$(dirname -- \"$target_file\")\"\n        target_file=\"$(basename -- \"$target_file\")\"\n    done\n\n    # Compute the canonicalized name by finding the physical path\n    # for the directory we're in and appending the target file.\n    physical_directory=\"`pwd -P`\"\n    result=\"$physical_directory/$target_file\"\n\n    # restore the working directory after work.\n    cd -- \"$working_dir\"\n\n    echo \"$result\"\n}\n"
  },
  {
    "path": "tools/hooks/pre-commit",
    "content": "#!/bin/sh\n# Git pre-commit hook that runs multiple hooks specified in $HOOKS.\n# Make sure this script is executable. Bypass hooks with git commit --no-verify.\n\n# This file is part of a set of unofficial pre-commit hooks available\n# at github.\n# Link:    https://github.com/githubbrowser/Pre-commit-hooks\n# Contact: David Martin, david.martin.mailbox@googlemail.com\n\n\n###########################################################\n# CONFIGURATION:\n# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder\n# as this script. Hooks should return 0 if successful and nonzero to cancel the\n# commit. They are executed in the order in which they are listed.\nHOOKS=\"pre-commit-clang-format\"\nHOOKS=\"$HOOKS $(find $(dirname -- \"$0\") -type f -name 'pre-commit-custom-*' -exec basename {} \\;)\"\n###########################################################\n# There should be no need to change anything below this line.\n\n. \"$(dirname -- \"$0\")/canonicalize_filename.sh\"\n\n# exit on error\nset -e\n\n# Absolute path to this script, e.g. /home/user/bin/foo.sh\nSCRIPT=\"$(canonicalize_filename \"$0\")\"\n\n# Absolute path this script is in, thus /home/user/bin\nSCRIPTPATH=\"$(dirname -- \"$SCRIPT\")\"\n\n\nfor hook in $HOOKS\ndo\n    echo \"Running hook: $hook\"\n    # run hook if it exists\n    # if it returns with nonzero exit with 1 and thus abort the commit\n    if [ -f \"$SCRIPTPATH/$hook\" ]; then\n        \"$SCRIPTPATH/$hook\"\n        if [ $? != 0 ]; then\n            exit 1\n        fi\n    else\n        echo \"Error: file $hook not found.\"\n        echo \"Aborting commit. Make sure the hook is in $SCRIPTPATH and executable.\"\n        echo \"You can disable it by removing it from the list in $SCRIPT.\"\n        echo \"You can skip all pre-commit hooks with --no-verify (not recommended).\"\n        exit 1\n    fi\ndone\n"
  },
  {
    "path": "tools/hooks/pre-commit-clang-format",
    "content": "#!/usr/bin/env bash\n\n# git pre-commit hook that runs a clang-format stylecheck.\n# Features:\n#  - abort commit when commit does not comply with the style guidelines\n#  - create a patch of the proposed style changes\n# Modifications for clang-format by rene.milk@wwu.de\n\n# This file is part of a set of unofficial pre-commit hooks available\n# at github.\n# Link:    https://github.com/githubbrowser/Pre-commit-hooks\n# Contact: David Martin, david.martin.mailbox@googlemail.com\n\n# Some quality of life modifications made for Godot Engine.\n\n##################################################################\n# SETTINGS\n# Set path to clang-format binary.\nCLANG_FORMAT=`which clang-format 2>/dev/null`\n\n# Remove any older patches from previous commits. Set to true or false.\nDELETE_OLD_PATCHES=false\n\n# Only parse files with the extensions in FILE_EXTS. Set to true or false.\n# If false every changed file in the commit will be parsed with clang-format.\n# If true only files matching one of the extensions are parsed with clang-format.\nPARSE_EXTS=true\n\n# File types to parse. Only effective when PARSE_EXTS is true.\nFILE_EXTS=\".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl\"\n\n# Use pygmentize instead of cat to parse diff with highlighting.\n# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac)\nPYGMENTIZE=`which pygmentize 2>/dev/null`\nif [ ! -z \"$PYGMENTIZE\" ]; then\n  READER=\"pygmentize -l diff\"\nelse\n  READER=cat\nfi\n\n# Path to zenity\nZENITY=`which zenity 2>/dev/null`\n\n# Path to xmessage\nXMSG=`which xmessage 2>/dev/null`\n\n# Path to powershell (Windows only)\nPWSH=`which powershell 2>/dev/null`\n\n# Path to osascript (macOS only)\nOSA=`which osascript 2>/dev/null`\n\n##################################################################\n# There should be no need to change anything below this line.\n\n. \"$(dirname -- \"$0\")/canonicalize_filename.sh\"\n\n# exit on error\nset -e\n\n# check whether the given file matches any of the set extensions\nmatches_extension() {\n    local filename=$(basename \"$1\")\n    local extension=\".${filename##*.}\"\n    local ext\n\n    for ext in $FILE_EXTS; do [[ \"$ext\" == \"$extension\" ]] && return 0; done\n\n    return 1\n}\n\n# necessary check for initial commit\nif git rev-parse --verify HEAD >/dev/null 2>&1 ; then\n    against=HEAD\nelse\n    # Initial commit: diff against an empty tree object\n    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# To get consistent formatting, we recommend contributors to use the same\n# clang-format version as CI.\nRECOMMENDED_CLANG_FORMAT_MAJOR_MIN=\"13\"\nRECOMMENDED_CLANG_FORMAT_MAJOR_MAX=\"15\"\n\nif [ ! -x \"$CLANG_FORMAT\" ] ; then\n\tmessage=\"Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX.\"\n\n    if [ ! -t 1 ] ; then\n        if [ -x \"$ZENITY\" ] ; then\n            $ZENITY --error --title=\"Error\" --text=\"$message\"\n            exit 1\n        elif [ -x \"$XMSG\" ] ; then\n            $XMSG -center -title \"Error\" \"$message\"\n            exit 1\n        elif [ -x \"$OSA\" ] ; then\n            asmessage=\"$(canonicalize_filename \"$(dirname -- \"$0\")/asmessage.applescript\")\"\n            $OSA \"$asmessage\" -center -title \"Error\" --text \"$message\"\n            exit 1\n        elif [ \\( \\( \"$OSTYPE\" = \"msys\" \\) -o \\( \"$OSTYPE\" = \"win32\" \\) \\) -a \\( -x \"$PWSH\" \\) ]; then\n            winmessage=\"$(canonicalize_filename \"$(dirname -- \"$0\")/winmessage.ps1\")\"\n            $PWSH -noprofile -executionpolicy bypass -file \"$winmessage\" -center -title \"Error\" --text \"$message\"\n            exit 1\n        fi\n    fi\n    printf \"$message\\n\"\n    printf \"Set the correct path in $(canonicalize_filename \"$0\").\\n\"\n    exit 1\nfi\n\n# The returned string can be inconsistent depending on where clang-format comes from.\n# Example output strings reported by `clang-format --version`:\n# - Ubuntu: \"Ubuntu clang-format version 11.0.0-2\"\n# - Fedora: \"clang-format version 11.0.0 (Fedora 11.0.0-2.fc33)\"\nCLANG_FORMAT_VERSION=\"$(clang-format --version | sed \"s/[^0-9\\.]*\\([0-9\\.]*\\).*/\\1/\")\"\nCLANG_FORMAT_MAJOR=\"$(echo \"$CLANG_FORMAT_VERSION\" | cut -d. -f1)\"\n\nif [[ \"$CLANG_FORMAT_MAJOR\" -lt \"$RECOMMENDED_CLANG_FORMAT_MAJOR_MIN\" || \"$CLANG_FORMAT_MAJOR\" -gt \"$RECOMMENDED_CLANG_FORMAT_MAJOR_MAX\" ]]; then\n    echo \"Warning: Your clang-format binary is the wrong version ($CLANG_FORMAT_VERSION, expected between $RECOMMENDED_CLANG_FORMAT_MAJOR_MIN and $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX).\"\n    echo \"         Consider upgrading or downgrading clang-format as formatting may not be applied correctly.\"\nfi\n\n# create a random filename to store our generated patch\nprefix=\"pre-commit-clang-format\"\nsuffix=\"$(date +%s)\"\npatch=\"/tmp/$prefix-$suffix.patch\"\n\n# clean up any older clang-format patches\n$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch\n\n# create one patch containing all changes to the files\ngit diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;\ndo\n    # ignore thirdparty files\n    if grep -q \"thirdparty\" <<< $file; then\n        continue;\n    fi\n    if grep -q \"platform/android/java/lib/src/com\" <<< $file; then\n        continue;\n    fi\n    if grep -q \"\\-so_wrap.\" <<< $file; then\n        continue;\n    fi\n\n    # ignore file if we do check for file extensions and the file\n    # does not match any of the extensions specified in $FILE_EXTS\n    if $PARSE_EXTS && ! matches_extension \"$file\"; then\n        continue;\n    fi\n\n    # clang-format our sourcefile, create a patch with diff and append it to our $patch\n    # The sed call is necessary to transform the patch from\n    #    --- $file timestamp\n    #    +++ - timestamp\n    # to both lines working on the same file and having a/ and b/ prefix.\n    # Else it can not be applied with 'git apply'.\n    \"$CLANG_FORMAT\" -style=file \"$file\" --Wno-error=unknown | \\\n        diff -u \"$file\" - | \\\n        sed -e \"1s|--- |--- a/|\" -e \"2s|+++ -|+++ b/$file|\" >> \"$patch\"\ndone\n\n# if no patch has been generated all is ok, clean up the file stub and exit\nif [ ! -s \"$patch\" ] ; then\n    printf \"Files in this commit comply with the clang-format rules.\\n\"\n    rm -f \"$patch\"\n    exit 0\nfi\n\n# a patch has been created, notify the user and exit\nprintf \"\\nThe following differences were found between the code to commit \"\nprintf \"and the clang-format rules:\\n\\n\"\n\nif [ -t 1 ] ; then\n    $READER \"$patch\"\n    printf \"\\n\"\n    # Allows us to read user input below, assigns stdin to keyboard\n    exec < /dev/tty\n    terminal=\"1\"\nelse\n    cat \"$patch\"\n    printf \"\\n\"\n    # Allows non zero zenity/powershell output\n    set +e\n    terminal=\"0\"\nfi\n\nwhile true; do\n    if [ $terminal = \"0\" ] ; then\n        if [ -x \"$ZENITY\" ] ; then\n            choice=$($ZENITY --text-info --filename=\"$patch\" --width=800 --height=600 --title=\"Do you want to apply that patch?\" --ok-label=\"Apply\" --cancel-label=\"Do not apply\" --extra-button=\"Apply and stage\")\n            if [ \"$?\" = \"0\" ] ; then\n                yn=\"Y\"\n            else\n                if [ \"$choice\" = \"Apply and stage\" ] ; then\n                    yn=\"S\"\n                else\n                    yn=\"N\"\n                fi\n            fi\n        elif [ -x \"$XMSG\" ] ; then\n            $XMSG -file \"$patch\" -buttons \"Apply\":100,\"Apply and stage\":200,\"Do not apply\":0 -center -default \"Do not apply\" -geometry 800x600 -title \"Do you want to apply that patch?\"\n            choice=$?\n            if [ \"$choice\" = \"100\" ] ; then\n                yn=\"Y\"\n            elif [ \"$choice\" = \"200\" ] ; then\n                yn=\"S\"\n            else\n                yn=\"N\"\n            fi\n        elif [ -x \"$OSA\" ] ; then\n            asmessage=\"$(canonicalize_filename \"$(dirname -- \"$0\")/asmessage.applescript\")\"\n            choice=`$OSA \"$asmessage\" -file \"$patch\" -buttons \"Apply\":100,\"Apply and stage\":200,\"Do not apply\":0 -center -default \"Do not apply\" -geometry 800x600 -title \"Do you want to apply that patch?\"`\n            if [ \"$choice\" = \"100\" ] ; then\n                yn=\"Y\"\n            elif [ \"$choice\" = \"200\" ] ; then\n                yn=\"S\"\n            else\n                yn=\"N\"\n            fi\n        elif [ \\( \\( \"$OSTYPE\" = \"msys\" \\) -o \\( \"$OSTYPE\" = \"win32\" \\) \\) -a \\( -x \"$PWSH\" \\) ]; then\n            winmessage=\"$(canonicalize_filename \"$(dirname -- \"$0\")/winmessage.ps1\")\"\n            $PWSH -noprofile -executionpolicy bypass -file \"$winmessage\" -file \"$patch\" -buttons \"Apply\":100,\"Apply and stage\":200,\"Do not apply\":0 -center -default \"Do not apply\" -geometry 800x600 -title \"Do you want to apply that patch?\"\n            choice=$?\n            if [ \"$choice\" = \"100\" ] ; then\n                yn=\"Y\"\n            elif [ \"$choice\" = \"200\" ] ; then\n                yn=\"S\"\n            else\n                yn=\"N\"\n            fi\n        else\n            printf \"Error: zenity, xmessage, osascript, or powershell executable not found.\\n\"\n            exit 1\n        fi\n    else\n        read -p \"Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] \" yn\n    fi\n    case $yn in\n        [Yy] ) git apply $patch;\n        printf \"The patch was applied. You can now stage the changes and commit again.\\n\\n\";\n        break\n        ;;\n        [Nn] ) printf \"\\nYou can apply these changes with:\\n  git apply $patch\\n\";\n        printf \"(may need to be called from the root directory of your repository)\\n\";\n        printf \"Aborting commit. Apply changes and commit again or skip checking with\";\n        printf \" --no-verify (not recommended).\\n\\n\";\n        break\n        ;;\n        [Ss] ) git apply $patch;\n        git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;\n        do git add $file;\n        done\n        printf \"The patch was applied and the changed files staged. You can now commit.\\n\\n\";\n        break\n        ;;\n        * ) echo \"Please answer yes or no.\"\n        ;;\n    esac\ndone\nexit 1 # we don't commit in any case\n"
  },
  {
    "path": "tools/hooks/winmessage.ps1",
    "content": "Param (\n\t[string]$file = \"\",\n\t[string]$text = \"\",\n\t[string]$buttons = \"OK:0\",\n\t[string]$default = \"\",\n\t[switch]$nearmouse = $false,\n\t[switch]$center = $false,\n\t[string]$geometry = \"\",\n\t[int32]$timeout = 0,\n\t[string]$title = \"Message\"\n)\nAdd-Type -assembly System.Windows.Forms\n\n$global:Result = 0\n\n$main_form = New-Object System.Windows.Forms.Form\n$main_form.Text = $title\n\n$geometry_data = $geometry.Split(\"+\")\nif ($geometry_data.Length -ge 1) {\n\t$size_data = $geometry_data[0].Split(\"x\")\n\tif ($size_data.Length -eq 2) {\n\t\t$main_form.Width = $size_data[0]\n\t\t$main_form.Height = $size_data[1]\n\t}\n}\nif ($geometry_data.Length -eq 3) {\n\t$main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual\n\t$main_form.Location = New-Object System.Drawing.Point($geometry_data[1], $geometry_data[2])\n}\nif ($nearmouse) {\n\t$main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual\n\t$main_form.Location = System.Windows.Forms.Cursor.Position\n}\nif ($center) {\n\t$main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen\n}\n\n$main_form.SuspendLayout()\n\n$button_panel = New-Object System.Windows.Forms.FlowLayoutPanel\n$button_panel.SuspendLayout()\n$button_panel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft\n$button_panel.Dock = [System.Windows.Forms.DockStyle]::Bottom\n$button_panel.Autosize = $true\n\nif ($file -ne \"\") {\n\t$text = [IO.File]::ReadAllText($file).replace(\"`n\", \"`r`n\")\n}\n\nif ($text -ne \"\") {\n\t$text_box = New-Object System.Windows.Forms.TextBox\n\t$text_box.Multiline = $true\n\t$text_box.ReadOnly = $true\n\t$text_box.Autosize = $true\n\t$text_box.Text = $text\n\t$text_box.Select(0,0)\n\t$text_box.Dock = [System.Windows.Forms.DockStyle]::Fill\n\t$main_form.Controls.Add($text_box)\n}\n\n$buttons_array = $buttons.Split(\",\")\nforeach ($button in $buttons_array) {\n\t$button_data = $button.Split(\":\")\n\t$button_ctl = New-Object System.Windows.Forms.Button\n\tif ($button_data.Length -eq 2) {\n\t\t$button_ctl.Tag = $button_data[1]\n\t} else {\n\t\t$button_ctl.Tag = 100 + $buttons_array.IndexOf($button)\n\t}\n\tif ($default -eq $button_data[0]) {\n\t\t$main_form.AcceptButton = $button_ctl\n\t}\n\t$button_ctl.Autosize = $true\n\t$button_ctl.Text = $button_data[0]\n\t$button_ctl.Add_Click(\n\t\t{\n\t\t\tParam($sender)\n\t\t\t$global:Result = $sender.Tag\n\t\t\t$main_form.Close()\n\t\t}\n\t)\n\t$button_panel.Controls.Add($button_ctl)\n}\n$main_form.Controls.Add($button_panel)\n\n$button_panel.ResumeLayout($false)\n$main_form.ResumeLayout($false)\n\nif ($timeout -gt 0) {\n\t$timer = New-Object System.Windows.Forms.Timer\n\t$timer.Add_Tick(\n\t\t{\n\t\t\t$global:Result = 0\n\t\t\t$main_form.Close()\n\t\t}\n\t)\n\t$timer.Interval = $timeout\n\t$timer.Start()\n}\n$dlg_res = $main_form.ShowDialog()\n\n[Environment]::Exit($global:Result)\n"
  },
  {
    "path": "tools/install-hooks.py",
    "content": "import os\nimport stat\nimport sys\nimport shutil\n\nsrc_dir = 'tools/hooks'\ndist_dir = '.git/hooks'\n\nif not os.path.exists(dist_dir):\n    print(\"Error: this directory is not a git repository!\")\n    sys.exit(1)\n\nfor file in os.listdir(src_dir):\n    shutil.copy(os.path.join(src_dir, file), dist_dir)\n\n# Unix user needs the execute bit set.\nif os.name == \"posix\":\n    mode = stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH;\n    os.chmod(os.path.join(dist_dir, \"pre-commit\"), mode);\n    os.chmod(os.path.join(dist_dir, \"pre-commit-clang-format\"), mode);\n\nprint(\"Copied tools/hooks/* to .git/hooks\")\n"
  }
]