Compare commits
1 Commits
master
...
overlay-te
| Author | SHA1 | Date | |
|---|---|---|---|
| 33fa18fe97 |
140
.github/workflows/ci.yaml
vendored
140
.github/workflows/ci.yaml
vendored
@@ -1,140 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Reduce compile time and cache size.
|
|
||||||
RUSTFLAGS: -Zshare-generics=y -Zthreads=0
|
|
||||||
RUSTDOCFLAGS: -Zshare-generics=y -Zthreads=0
|
|
||||||
# Use the same Rust toolchain across jobs so they can share a cache.
|
|
||||||
toolchain: nightly-2025-04-03
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Run Clippy lints.
|
|
||||||
clippy-lints:
|
|
||||||
name: Clippy lints
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
RUSTFLAGS: "-Dwarnings"
|
|
||||||
timeout-minutes: 20
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: ${{ env.toolchain }}
|
|
||||||
components: clippy
|
|
||||||
|
|
||||||
- name: Restore Rust cache
|
|
||||||
id: cache
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
shared-key: ci
|
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
run: sudo apt-get -y update; sudo apt-get -y install --no-install-recommends libasound2-dev libudev-dev libwayland-dev
|
|
||||||
|
|
||||||
- name: Run Clippy lints
|
|
||||||
run: cargo clippy --locked --workspace --all-targets --profile ci --all-features
|
|
||||||
|
|
||||||
# Run Bevy lints.
|
|
||||||
bevy-lints:
|
|
||||||
name: Bevy lints
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
RUSTFLAGS: "-Dwarnings"
|
|
||||||
timeout-minutes: 20
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Rust toolchain (plus bevy_lint)
|
|
||||||
uses: TheBevyFlock/bevy_cli/bevy_lint@lint-v0.3.0
|
|
||||||
|
|
||||||
- name: Restore Rust cache
|
|
||||||
id: cache
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
shared-key: ci
|
|
||||||
save-if: false
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
run: sudo apt-get -y update; sudo apt-get -y install --no-install-recommends libasound2-dev libudev-dev libwayland-dev
|
|
||||||
|
|
||||||
- name: Run Bevy lints
|
|
||||||
run: bevy_lint --locked --workspace --all-targets --profile ci --all-features
|
|
||||||
|
|
||||||
# Run tests.
|
|
||||||
tests:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 40
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up environment
|
|
||||||
run: echo "RUSTFLAGS=${RUSTFLAGS:+$RUSTFLAGS }-Zcodegen-backend=cranelift" >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: ${{ env.toolchain }}
|
|
||||||
components: rustc-codegen-cranelift-preview
|
|
||||||
|
|
||||||
- name: Restore Rust cache
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
shared-key: test
|
|
||||||
cache-directories: ${{ env.LD_LIBRARY_PATH }}
|
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
|
||||||
run: sudo apt-get -y update; sudo apt-get -y install --no-install-recommends libasound2-dev libudev-dev libwayland-dev
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --locked --workspace --all-targets --profile ci --no-fail-fast
|
|
||||||
|
|
||||||
# Check that the web build compiles.
|
|
||||||
# check-web:
|
|
||||||
# name: Check web
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# env:
|
|
||||||
# RUSTFLAGS: -Zshare-generics=y -Zthreads=0 --cfg getrandom_backend="wasm_js"
|
|
||||||
# timeout-minutes: 20
|
|
||||||
# steps:
|
|
||||||
# - name: Checkout repository
|
|
||||||
# uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# - name: Install Rust toolchain
|
|
||||||
# uses: dtolnay/rust-toolchain@master
|
|
||||||
# with:
|
|
||||||
# toolchain: ${{ env.toolchain }}
|
|
||||||
# targets: wasm32-unknown-unknown
|
|
||||||
|
|
||||||
# - name: Restore Rust cache
|
|
||||||
# id: cache
|
|
||||||
# uses: Swatinem/rust-cache@v2
|
|
||||||
# with:
|
|
||||||
# shared-key: web-ci
|
|
||||||
# save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
# - name: Install build dependencies
|
|
||||||
# if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
# run: sudo apt-get -y update; sudo apt-get -y install --no-install-recommends libasound2-dev libudev-dev libwayland-dev
|
|
||||||
|
|
||||||
# - name: Check web
|
|
||||||
# run: cargo check --config 'profile.web.inherits="dev"' --profile ci --no-default-features --features "dev web" --target wasm32-unknown-unknown
|
|
||||||
335
.github/workflows/release.yaml
vendored
335
.github/workflows/release.yaml
vendored
@@ -1,335 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Trigger this workflow when a tag is pushed in the format `v1.2.3`.
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
# Pattern syntax: <https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet>.
|
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+*"
|
|
||||||
# Trigger this workflow manually via workflow dispatch.
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: "Version number in the format `v1.2.3`"
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
# Configure the release workflow by editing the following values.
|
|
||||||
env:
|
|
||||||
# The base filename of the binary produced by `cargo build`.
|
|
||||||
cargo_build_binary_name: phos
|
|
||||||
|
|
||||||
# The path to the assets directory.
|
|
||||||
assets_path: assets
|
|
||||||
|
|
||||||
# Whether to build and package a release for a given target platform.
|
|
||||||
build_for_web: false
|
|
||||||
build_for_linux: true
|
|
||||||
build_for_windows: true
|
|
||||||
build_for_macos: false
|
|
||||||
|
|
||||||
# Whether to upload the packages produced by this workflow to a GitHub release.
|
|
||||||
upload_to_github: true
|
|
||||||
|
|
||||||
# The itch.io project to upload to in the format `user-name/project-name`.
|
|
||||||
# There will be no upload to itch.io if this is commented out.
|
|
||||||
# upload_to_itch: amatsugu/phos
|
|
||||||
|
|
||||||
############
|
|
||||||
# ADVANCED #
|
|
||||||
############
|
|
||||||
|
|
||||||
# The ID of the app produced by this workflow.
|
|
||||||
# Applies to macOS releases.
|
|
||||||
# Must contain only A-Z, a-z, 0-9, hyphen, and period: <https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier>.
|
|
||||||
app_id: amatsugu.phos
|
|
||||||
|
|
||||||
# The base filename of the binary in the package produced by this workflow.
|
|
||||||
# Applies to Windows, macOS, and Linux releases.
|
|
||||||
# Defaults to `cargo_build_binary_name` if commented out.
|
|
||||||
#app_binary_name: bevy_jam_6
|
|
||||||
|
|
||||||
# The name of the `.zip` or `.dmg` file produced by this workflow.
|
|
||||||
# Defaults to `app_binary_name` if commented out.
|
|
||||||
#app_package_name: bevy-new-2d
|
|
||||||
|
|
||||||
# The display name of the app produced by this workflow.
|
|
||||||
# Applies to macOS releases.
|
|
||||||
# Defaults to `app_package_name` if commented out.
|
|
||||||
app_display_name: phos
|
|
||||||
|
|
||||||
# The short display name of the app produced by this workflow.
|
|
||||||
# Applies to macOS releases.
|
|
||||||
# Must be 15 or fewer characters: <https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundlename>.
|
|
||||||
# Defaults to `app_display_name` if commented out.
|
|
||||||
#app_short_name: Phos
|
|
||||||
|
|
||||||
# Before enabling LFS, please take a look at GitHub's documentation for costs and quota limits:
|
|
||||||
# <https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-storage-and-bandwidth-usage>
|
|
||||||
git_lfs: false
|
|
||||||
|
|
||||||
# Enabling this only helps with consecutive releases to the same tag (and takes up cache storage space).
|
|
||||||
# See: <https://github.com/orgs/community/discussions/27059>.
|
|
||||||
use_github_cache: false
|
|
||||||
|
|
||||||
# Reduce compile time.
|
|
||||||
RUSTFLAGS: -Zshare-generics=y -Zthreads=0
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Forward some environment variables as outputs of this job.
|
|
||||||
# This is needed because the `env` context can't be used in the `if:` condition of a job:
|
|
||||||
# <https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability>
|
|
||||||
forward-env:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Do nothing
|
|
||||||
run: "true"
|
|
||||||
outputs:
|
|
||||||
upload_to_itch: ${{ env.upload_to_itch }}
|
|
||||||
|
|
||||||
# Determine the version number for this workflow.
|
|
||||||
get-version:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Determine version number
|
|
||||||
id: tag
|
|
||||||
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "${GITHUB_OUTPUT}"
|
|
||||||
outputs:
|
|
||||||
# Use the input from workflow dispatch, or fall back to the git tag.
|
|
||||||
version: ${{ inputs.version || steps.tag.outputs.tag }}
|
|
||||||
|
|
||||||
# Build and package a release for each platform.
|
|
||||||
build:
|
|
||||||
needs:
|
|
||||||
- get-version
|
|
||||||
env:
|
|
||||||
version: ${{ needs.get-version.outputs.version }}
|
|
||||||
# Avoid rate-limiting. See: <https://github.com/cargo-bins/cargo-binstall/issues/2045>.
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: web
|
|
||||||
targets: wasm32-unknown-unknown
|
|
||||||
package_ext: .zip
|
|
||||||
runner: ubuntu-latest
|
|
||||||
|
|
||||||
- platform: linux
|
|
||||||
targets: x86_64-unknown-linux-gnu
|
|
||||||
package_ext: .zip
|
|
||||||
runner: ubuntu-latest
|
|
||||||
|
|
||||||
- platform: windows
|
|
||||||
targets: x86_64-pc-windows-msvc
|
|
||||||
binary_ext: .exe
|
|
||||||
package_ext: .zip
|
|
||||||
runner: windows-latest
|
|
||||||
|
|
||||||
- platform: macos
|
|
||||||
targets: x86_64-apple-darwin aarch64-apple-darwin
|
|
||||||
app_suffix: .app/Contents/MacOS
|
|
||||||
package_ext: .dmg
|
|
||||||
runner: macos-latest
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
# Required to create a GitHub release: <https://docs.github.com/en/rest/releases/releases#create-a-release>.
|
|
||||||
contents: write
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up environment
|
|
||||||
run: |
|
|
||||||
# Default values:
|
|
||||||
echo "app_binary_name=${app_binary_name:=${{ env.cargo_build_binary_name }}}" >> "${GITHUB_ENV}"
|
|
||||||
echo "app_package_name=${app_package_name:=${app_binary_name}}" >> "${GITHUB_ENV}"
|
|
||||||
echo "app_display_name=${app_display_name:=${app_package_name}}" >> "${GITHUB_ENV}"
|
|
||||||
echo "app_short_name=${app_short_name:=${app_display_name}}" >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
# File paths:
|
|
||||||
echo "app=tmp/app/${app_package_name}"'${{ matrix.app_suffix }}' >> "${GITHUB_ENV}"
|
|
||||||
echo "package=${app_package_name}-"'${{ matrix.platform }}${{ matrix.package_ext }}' >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
# macOS environment:
|
|
||||||
if [ '${{ matrix.platform }}' = 'macos' ]; then
|
|
||||||
echo 'MACOSX_DEPLOYMENT_TARGET=11.0' >> "${GITHUB_ENV}" # macOS 11.0 Big Sur is the first version to support universal binaries.
|
|
||||||
echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> "${GITHUB_ENV}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if building for this platform is enabled.
|
|
||||||
echo 'is_platform_enabled=${{
|
|
||||||
(matrix.platform == 'web' && env.build_for_web == 'true') ||
|
|
||||||
(matrix.platform == 'linux' && env.build_for_linux == 'true') ||
|
|
||||||
(matrix.platform == 'windows' && env.build_for_windows == 'true') ||
|
|
||||||
(matrix.platform == 'macos' && env.build_for_macos == 'true')
|
|
||||||
}}' >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: ${{ env.git_lfs }}
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
targets: ${{ matrix.targets }}
|
|
||||||
|
|
||||||
- name: Restore Rust cache
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && env.use_github_cache == 'true' }}
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
shared-key: release
|
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Install build dependencies (Linux)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'linux' }}
|
|
||||||
run: sudo apt-get -y update; sudo apt-get -y install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
|
||||||
|
|
||||||
- name: Prepare output directories
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
run: rm -rf tmp; mkdir -p tmp/binary '${{ env.app }}'
|
|
||||||
|
|
||||||
- name: Install cargo-binstall
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: cargo-bins/cargo-binstall@main
|
|
||||||
|
|
||||||
- name: Install Bevy CLI
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
run: cargo binstall --locked --no-confirm --force --git='https://github.com/TheBevyFlock/bevy_cli' bevy_cli
|
|
||||||
|
|
||||||
- name: Build and add web bundle to app (Web)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'web' }}
|
|
||||||
run: |
|
|
||||||
cargo binstall --locked --no-confirm --force wasm-bindgen-cli
|
|
||||||
cargo binstall --locked --no-confirm --force wasm-opt
|
|
||||||
RUSTFLAGS='-Zshare-generics=y -Zthreads=0 --cfg getrandom_backend="wasm_js"' bevy build --locked --release --features='${{ matrix.features }} web' --yes web --bundle
|
|
||||||
mv 'target/bevy_web/web-release/${{ env.cargo_build_binary_name }}' '${{ env.app }}'
|
|
||||||
|
|
||||||
- name: Build and add binaries to app (non-Web)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform != 'web' }}
|
|
||||||
run: |
|
|
||||||
for target in ${{ matrix.targets }}; do
|
|
||||||
bevy build --locked --release --target="${target}" --features='${{ matrix.features }}'
|
|
||||||
mv target/"${target}"/release/'${{ env.cargo_build_binary_name }}${{ matrix.binary_ext }}' tmp/binary/"${target}"'${{ matrix.binary_ext }}'
|
|
||||||
done
|
|
||||||
if [ '${{ matrix.platform }}' = 'macos' ]; then
|
|
||||||
lipo tmp/binary/*'${{ matrix.binary_ext }}' -create -output '${{ env.app }}/${{ env.app_binary_name }}${{ matrix.binary_ext }}'
|
|
||||||
else
|
|
||||||
mv tmp/binary/*'${{ matrix.binary_ext }}' '${{ env.app }}/${{ env.app_binary_name }}${{ matrix.binary_ext }}'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Add assets to app (non-Web)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform != 'web' }}
|
|
||||||
run: cp -R ./'${{ env.assets_path }}' '${{ env.app }}' || true # Ignore error if assets folder does not exist.
|
|
||||||
|
|
||||||
- name: Add metadata to app (macOS)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'macos' }}
|
|
||||||
run: |
|
|
||||||
cat >'${{ env.app }}/../Info.plist' <<EOF
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>${{ env.app_display_name }}</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${{ env.app_binary_name }}</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>${{ env.app_id }}</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${{ env.app_short_name }}</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>${{ env.version }}</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${{ env.version }}</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
|
||||||
<array>
|
|
||||||
<string>MacOSX</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Package app (non-Windows)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform != 'windows' }}
|
|
||||||
working-directory: tmp/app
|
|
||||||
run: |
|
|
||||||
if [ '${{ matrix.platform }}' = 'macos' ]; then
|
|
||||||
ln -s /Applications .
|
|
||||||
hdiutil create -fs HFS+ -volname '${{ env.app_package_name }}' -srcfolder . '${{ env.package }}'
|
|
||||||
else
|
|
||||||
zip --recurse-paths '${{ env.package }}' '${{ env.app_package_name }}'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Package app (Windows)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'windows' }}
|
|
||||||
working-directory: tmp/app
|
|
||||||
shell: pwsh
|
|
||||||
run: Compress-Archive -Path '${{ env.app_package_name }}' -DestinationPath '${{ env.package }}'
|
|
||||||
|
|
||||||
- name: Upload package to workflow artifacts
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
path: tmp/app/${{ env.package }}
|
|
||||||
name: package-${{ matrix.platform }}
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
- name: Upload package to GitHub release
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && env.upload_to_github == 'true' }}
|
|
||||||
uses: svenstaro/upload-release-action@v2
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
file: tmp/app/${{ env.package }}
|
|
||||||
asset_name: ${{ env.package }}
|
|
||||||
release_name: ${{ env.version }}
|
|
||||||
tag: ${{ env.version }}
|
|
||||||
overwrite: true
|
|
||||||
|
|
||||||
# Upload all packages to itch.io.
|
|
||||||
upload-to-itch:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- forward-env
|
|
||||||
- get-version
|
|
||||||
- build
|
|
||||||
if: ${{ needs.forward-env.outputs.upload_to_itch != '' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download all packages
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
pattern: package-*
|
|
||||||
path: tmp
|
|
||||||
|
|
||||||
- name: Install butler
|
|
||||||
run: |
|
|
||||||
curl -L -o butler.zip 'https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default'
|
|
||||||
unzip butler.zip
|
|
||||||
chmod +x butler
|
|
||||||
./butler -V
|
|
||||||
|
|
||||||
- name: Upload all packages to itch.io
|
|
||||||
env:
|
|
||||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
|
||||||
run: |
|
|
||||||
for channel in $(ls tmp); do
|
|
||||||
./butler push \
|
|
||||||
--fix-permissions \
|
|
||||||
--userversion='${{ needs.get-version.outputs.version }}' \
|
|
||||||
tmp/"${channel}"/* \
|
|
||||||
'${{ env.upload_to_itch }}':"${channel#package-}"
|
|
||||||
done
|
|
||||||
347
.github/workflows/release.yaml.template
vendored
347
.github/workflows/release.yaml.template
vendored
@@ -1,347 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Trigger this workflow when a tag is pushed in the format `v1.2.3`.
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
# Pattern syntax: <https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet>.
|
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+*"
|
|
||||||
# Trigger this workflow manually via workflow dispatch.
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: "Version number in the format `v1.2.3`"
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
{% raw -%}
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
{%- endraw %}
|
|
||||||
|
|
||||||
# Configure the release workflow by editing the following values.
|
|
||||||
env:
|
|
||||||
# The base filename of the binary produced by `cargo build`.
|
|
||||||
cargo_build_binary_name: {{project-name}}
|
|
||||||
|
|
||||||
# The path to the assets directory.
|
|
||||||
assets_path: assets
|
|
||||||
|
|
||||||
# Whether to build and package a release for a given target platform.
|
|
||||||
build_for_web: true
|
|
||||||
build_for_linux: true
|
|
||||||
build_for_windows: true
|
|
||||||
build_for_macos: true
|
|
||||||
|
|
||||||
# Whether to upload the packages produced by this workflow to a GitHub release.
|
|
||||||
upload_to_github: true
|
|
||||||
|
|
||||||
# The itch.io project to upload to in the format `user-name/project-name`.
|
|
||||||
# There will be no upload to itch.io if this is commented out.
|
|
||||||
{%- if itch_username != "" %}
|
|
||||||
{%- if itch_project != "" %}
|
|
||||||
upload_to_itch: {{itch_username}}/{{itch_project}}
|
|
||||||
{%- else %}
|
|
||||||
upload_to_itch: {{itch_username}}/{{project-name}}
|
|
||||||
{%- endif %}
|
|
||||||
{%- else %}
|
|
||||||
#upload_to_itch: your-itch-username/{{project-name}}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
############
|
|
||||||
# ADVANCED #
|
|
||||||
############
|
|
||||||
|
|
||||||
# The ID of the app produced by this workflow.
|
|
||||||
# Applies to macOS releases.
|
|
||||||
# Must contain only A-Z, a-z, 0-9, hyphen, and period: <https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier>.
|
|
||||||
app_id: {{itch_username | kebab_case}}.{{project-name | kebab_case}}
|
|
||||||
|
|
||||||
# The base filename of the binary in the package produced by this workflow.
|
|
||||||
# Applies to Windows, macOS, and Linux releases.
|
|
||||||
# Defaults to `cargo_build_binary_name` if commented out.
|
|
||||||
#app_binary_name: {{project-name}}
|
|
||||||
|
|
||||||
# The name of the `.zip` or `.dmg` file produced by this workflow.
|
|
||||||
# Defaults to `app_binary_name` if commented out.
|
|
||||||
#app_package_name: {{project-name | kebab_case}}
|
|
||||||
|
|
||||||
# The display name of the app produced by this workflow.
|
|
||||||
# Applies to macOS releases.
|
|
||||||
# Defaults to `app_package_name` if commented out.
|
|
||||||
#app_display_name: {{project-name | title_case}}
|
|
||||||
|
|
||||||
# The short display name of the app produced by this workflow.
|
|
||||||
# Applies to macOS releases.
|
|
||||||
# Must be 15 or fewer characters: <https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundlename>.
|
|
||||||
# Defaults to `app_display_name` if commented out.
|
|
||||||
#app_short_name: {{project-name | title_case | truncate: 15, "…"}}
|
|
||||||
|
|
||||||
# Before enabling LFS, please take a look at GitHub's documentation for costs and quota limits:
|
|
||||||
# <https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-storage-and-bandwidth-usage>
|
|
||||||
git_lfs: false
|
|
||||||
|
|
||||||
# Enabling this only helps with consecutive releases to the same tag (and takes up cache storage space).
|
|
||||||
# See: <https://github.com/orgs/community/discussions/27059>.
|
|
||||||
use_github_cache: false
|
|
||||||
|
|
||||||
# Reduce compile time.
|
|
||||||
RUSTFLAGS: -Dwarnings -Zshare-generics=y -Zthreads=0
|
|
||||||
|
|
||||||
{% raw -%}
|
|
||||||
jobs:
|
|
||||||
# Forward some environment variables as outputs of this job.
|
|
||||||
# This is needed because the `env` context can't be used in the `if:` condition of a job:
|
|
||||||
# <https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability>
|
|
||||||
forward-env:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Do nothing
|
|
||||||
run: "true"
|
|
||||||
outputs:
|
|
||||||
upload_to_itch: ${{ env.upload_to_itch }}
|
|
||||||
|
|
||||||
# Determine the version number for this workflow.
|
|
||||||
get-version:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Determine version number
|
|
||||||
id: tag
|
|
||||||
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "${GITHUB_OUTPUT}"
|
|
||||||
outputs:
|
|
||||||
# Use the input from workflow dispatch, or fall back to the git tag.
|
|
||||||
version: ${{ inputs.version || steps.tag.outputs.tag }}
|
|
||||||
|
|
||||||
# Build and package a release for each platform.
|
|
||||||
build:
|
|
||||||
needs:
|
|
||||||
- get-version
|
|
||||||
env:
|
|
||||||
version: ${{ needs.get-version.outputs.version }}
|
|
||||||
# Avoid rate-limiting. See: <https://github.com/cargo-bins/cargo-binstall/issues/2045>.
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: web
|
|
||||||
targets: wasm32-unknown-unknown
|
|
||||||
package_ext: .zip
|
|
||||||
runner: ubuntu-latest
|
|
||||||
|
|
||||||
- platform: linux
|
|
||||||
targets: x86_64-unknown-linux-gnu
|
|
||||||
package_ext: .zip
|
|
||||||
runner: ubuntu-latest
|
|
||||||
|
|
||||||
- platform: windows
|
|
||||||
targets: x86_64-pc-windows-msvc
|
|
||||||
binary_ext: .exe
|
|
||||||
package_ext: .zip
|
|
||||||
runner: windows-latest
|
|
||||||
|
|
||||||
- platform: macos
|
|
||||||
targets: x86_64-apple-darwin aarch64-apple-darwin
|
|
||||||
app_suffix: .app/Contents/MacOS
|
|
||||||
package_ext: .dmg
|
|
||||||
runner: macos-latest
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
# Required to create a GitHub release: <https://docs.github.com/en/rest/releases/releases#create-a-release>.
|
|
||||||
contents: write
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up environment
|
|
||||||
run: |
|
|
||||||
# Default values:
|
|
||||||
echo "app_binary_name=${app_binary_name:=${{ env.cargo_build_binary_name }}}" >> "${GITHUB_ENV}"
|
|
||||||
echo "app_package_name=${app_package_name:=${app_binary_name}}" >> "${GITHUB_ENV}"
|
|
||||||
echo "app_display_name=${app_display_name:=${app_package_name}}" >> "${GITHUB_ENV}"
|
|
||||||
echo "app_short_name=${app_short_name:=${app_display_name}}" >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
# File paths:
|
|
||||||
echo "app=tmp/app/${app_package_name}"'${{ matrix.app_suffix }}' >> "${GITHUB_ENV}"
|
|
||||||
echo "package=${app_package_name}-"'${{ matrix.platform }}${{ matrix.package_ext }}' >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
# macOS environment:
|
|
||||||
if [ '${{ matrix.platform }}' = 'macos' ]; then
|
|
||||||
echo 'MACOSX_DEPLOYMENT_TARGET=11.0' >> "${GITHUB_ENV}" # macOS 11.0 Big Sur is the first version to support universal binaries.
|
|
||||||
echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> "${GITHUB_ENV}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if building for this platform is enabled.
|
|
||||||
echo 'is_platform_enabled=${{
|
|
||||||
(matrix.platform == 'web' && env.build_for_web == 'true') ||
|
|
||||||
(matrix.platform == 'linux' && env.build_for_linux == 'true') ||
|
|
||||||
(matrix.platform == 'windows' && env.build_for_windows == 'true') ||
|
|
||||||
(matrix.platform == 'macos' && env.build_for_macos == 'true')
|
|
||||||
}}' >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: ${{ env.git_lfs }}
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
targets: ${{ matrix.targets }}
|
|
||||||
|
|
||||||
- name: Restore Rust cache
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && env.use_github_cache == 'true' }}
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
shared-key: release
|
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Install build dependencies (Linux)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'linux' }}
|
|
||||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
|
||||||
|
|
||||||
- name: Prepare output directories
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
run: rm -rf tmp; mkdir -p tmp/binary '${{ env.app }}'
|
|
||||||
|
|
||||||
- name: Install cargo-binstall
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: cargo-bins/cargo-binstall@main
|
|
||||||
|
|
||||||
- name: Install Bevy CLI
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
run: cargo binstall --locked --no-confirm --force --git='https://github.com/TheBevyFlock/bevy_cli' bevy_cli
|
|
||||||
|
|
||||||
- name: Build and add web bundle to app (Web)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'web' }}
|
|
||||||
run: |
|
|
||||||
cargo binstall --locked --no-confirm --force wasm-bindgen-cli
|
|
||||||
cargo binstall --locked --no-confirm --force wasm-opt
|
|
||||||
bevy build --locked --release --features='${{ matrix.features }}' --yes web --bundle
|
|
||||||
mv 'target/bevy_web/web-release/${{ env.cargo_build_binary_name }}' '${{ env.app }}'
|
|
||||||
|
|
||||||
- name: Build and add binaries to app (non-Web)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform != 'web' }}
|
|
||||||
run: |
|
|
||||||
for target in ${{ matrix.targets }}; do
|
|
||||||
bevy build --locked --release --target="${target}" --features='${{ matrix.features }}'
|
|
||||||
mv target/"${target}"/release/'${{ env.cargo_build_binary_name }}${{ matrix.binary_ext }}' tmp/binary/"${target}"'${{ matrix.binary_ext }}'
|
|
||||||
done
|
|
||||||
if [ '${{ matrix.platform }}' = 'macos' ]; then
|
|
||||||
lipo tmp/binary/*'${{ matrix.binary_ext }}' -create -output '${{ env.app }}/${{ env.app_binary_name }}${{ matrix.binary_ext }}'
|
|
||||||
else
|
|
||||||
mv tmp/binary/*'${{ matrix.binary_ext }}' '${{ env.app }}/${{ env.app_binary_name }}${{ matrix.binary_ext }}'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Add assets to app (non-Web)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform != 'web' }}
|
|
||||||
run: cp -R ./'${{ env.assets_path }}' '${{ env.app }}' || true # Ignore error if assets folder does not exist.
|
|
||||||
|
|
||||||
- name: Add metadata to app (macOS)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'macos' }}
|
|
||||||
run: |
|
|
||||||
cat >'${{ env.app }}/../Info.plist' <<EOF
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>${{ env.app_display_name }}</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${{ env.app_binary_name }}</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>${{ env.app_id }}</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${{ env.app_short_name }}</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>${{ env.version }}</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${{ env.version }}</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
|
||||||
<array>
|
|
||||||
<string>MacOSX</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Package app (non-Windows)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform != 'windows' }}
|
|
||||||
working-directory: tmp/app
|
|
||||||
run: |
|
|
||||||
if [ '${{ matrix.platform }}' = 'macos' ]; then
|
|
||||||
ln -s /Applications .
|
|
||||||
hdiutil create -fs HFS+ -volname '${{ env.app_package_name }}' -srcfolder . '${{ env.package }}'
|
|
||||||
else
|
|
||||||
zip --recurse-paths '${{ env.package }}' '${{ env.app_package_name }}'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Package app (Windows)
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && matrix.platform == 'windows' }}
|
|
||||||
working-directory: tmp/app
|
|
||||||
shell: pwsh
|
|
||||||
run: Compress-Archive -Path '${{ env.app_package_name }}' -DestinationPath '${{ env.package }}'
|
|
||||||
|
|
||||||
- name: Upload package to workflow artifacts
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
path: tmp/app/${{ env.package }}
|
|
||||||
name: package-${{ matrix.platform }}
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
- name: Upload package to GitHub release
|
|
||||||
if: ${{ env.is_platform_enabled == 'true' && env.upload_to_github == 'true' }}
|
|
||||||
uses: svenstaro/upload-release-action@v2
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
file: tmp/app/${{ env.package }}
|
|
||||||
asset_name: ${{ env.package }}
|
|
||||||
release_name: ${{ env.version }}
|
|
||||||
tag: ${{ env.version }}
|
|
||||||
overwrite: true
|
|
||||||
|
|
||||||
# Upload all packages to itch.io.
|
|
||||||
upload-to-itch:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- forward-env
|
|
||||||
- get-version
|
|
||||||
- build
|
|
||||||
if: ${{ needs.forward-env.outputs.upload_to_itch != '' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download all packages
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
pattern: package-*
|
|
||||||
path: tmp
|
|
||||||
|
|
||||||
- name: Install butler
|
|
||||||
run: |
|
|
||||||
curl -L -o butler.zip 'https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default'
|
|
||||||
unzip butler.zip
|
|
||||||
chmod +x butler
|
|
||||||
./butler -V
|
|
||||||
|
|
||||||
- name: Upload all packages to itch.io
|
|
||||||
env:
|
|
||||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
|
||||||
run: |
|
|
||||||
for channel in $(ls tmp); do
|
|
||||||
./butler push \
|
|
||||||
--fix-permissions \
|
|
||||||
--userversion='${{ needs.get-version.outputs.version }}' \
|
|
||||||
tmp/"${channel}"/* \
|
|
||||||
'${{ env.upload_to_itch }}':"${channel#package-}"
|
|
||||||
done
|
|
||||||
{%- endraw %}
|
|
||||||
24
.github/workflows/rust.yml
vendored
Normal file
24
.github/workflows/rust.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: sudo apt install libasound2-dev libudev-dev
|
||||||
|
- name: Build
|
||||||
|
run: cargo build
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test
|
||||||
4896
Cargo.lock
generated
4896
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,15 +2,10 @@
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"game/main",
|
"game/main",
|
||||||
"game/shared",
|
|
||||||
"game/buildings",
|
"game/buildings",
|
||||||
"game/units",
|
"game/shared",
|
||||||
"game/resources",
|
|
||||||
"engine/world_generation",
|
"engine/world_generation",
|
||||||
"engine/asset_loader",
|
"engine/asset_loader", "game/buildings", "game/shared", "game/units", "engine/data", "game/resources", "engine/asset_loader_proc"]
|
||||||
"engine/data",
|
|
||||||
"engine/hex",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Enable a small amount of optimization in debug mode
|
# Enable a small amount of optimization in debug mode
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# Phos
|
# Phos
|
||||||
|
|
||||||
You play as a robot colonizer that has been sent out to an alien planet with one goal. Harvest its resources and send them back home. To accomplish this you must build a base on the planet. As your base develops you will be able to build more advanced structures to harvest at an increased scale. And eventually you will be able to accomplish your goal of sending resources back to your mother planet. But your invasion wont go unchallenged. The native inhabitants of the planet won’t sit idly by as you strip their world of its resources.
|
You play as a robot colonizer that has been sent out to an alien planet with one goal. Harvest its resources and send them back home. To accomplish this you must build a base on the planet. As your base develops you will be able to build more advanced structures to harvest at an increased scale. And eventually you will be able to accomplish your goal of sending resources back to your mother planet. But your invasion wont go unchallenged. The native inhabitants of the planet won’t sit idly by as you strip their world of its resources.
|
||||||
|
|
||||||

|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "1.0.228"
|
serde = "1.0.204"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.120"
|
||||||
bevy = "0.18.0"
|
bevy = "0.14.2"
|
||||||
ron = "0.12.0"
|
ron = "0.8.1"
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ macro_rules! create_asset_loader {
|
|||||||
$($string_array_name: ident -> $handle_array_name: ident)* ?
|
$($string_array_name: ident -> $handle_array_name: ident)* ?
|
||||||
) => {
|
) => {
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::asset::{AssetLoader, AssetEvent, LoadContext, LoadState, AsyncReadExt, io::Reader};
|
use bevy::asset::{AssetLoader, AssetEvent, AssetEvents, LoadContext, LoadState, AsyncReadExt, io::Reader};
|
||||||
|
use bevy::utils::BoxedFuture;
|
||||||
pub struct $plugin_name;
|
pub struct $plugin_name;
|
||||||
impl Plugin for $plugin_name {
|
impl Plugin for $plugin_name {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
@@ -18,7 +19,7 @@ macro_rules! create_asset_loader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, TypePath)]
|
#[derive(Default)]
|
||||||
pub struct $loader_name;
|
pub struct $loader_name;
|
||||||
|
|
||||||
impl AssetLoader for $loader_name {
|
impl AssetLoader for $loader_name {
|
||||||
@@ -28,12 +29,12 @@ macro_rules! create_asset_loader {
|
|||||||
|
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
async fn load(
|
async fn load<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
reader: &mut dyn bevy::asset::io::Reader,
|
reader: &'a mut Reader<'_>,
|
||||||
settings: &Self::Settings,
|
_settings: &'a Self::Settings,
|
||||||
load_context: &mut bevy::asset::LoadContext<'_>,
|
load_context: &'a mut LoadContext<'_>,
|
||||||
) -> Result<Self::Asset, Self::Error>{
|
) -> Result<Self::Asset, Self::Error> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
let read_result = reader.read_to_end(&mut bytes).await;
|
let read_result = reader.read_to_end(&mut bytes).await;
|
||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
|
|||||||
13
engine/asset_loader_proc/Cargo.toml
Normal file
13
engine/asset_loader_proc/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "asset_loader_proc"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1.0.204"
|
||||||
|
serde_json = "1.0.120"
|
||||||
|
bevy = "0.14.2"
|
||||||
|
ron = "0.8.1"
|
||||||
1
engine/asset_loader_proc/src/lib.rs
Normal file
1
engine/asset_loader_proc/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bevy = "0.18.1"
|
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
mod chunk;
|
|
||||||
mod hex_coord;
|
|
||||||
pub mod prelude
|
|
||||||
{
|
|
||||||
pub use crate::chunk::*;
|
|
||||||
pub use crate::hex_coord::*;
|
|
||||||
pub use crate::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const OUTER_RADIUS: f32 = 1.;
|
|
||||||
pub const INNER_RADIUS: f32 = OUTER_RADIUS * (SQRT_3 / 2.);
|
|
||||||
pub const SHORT_DIAGONAL: f32 = 1. * SQRT_3;
|
|
||||||
pub const LONG_DIAGONAL: f32 = 2. * OUTER_RADIUS;
|
|
||||||
pub const SQRT_3: f32 = 1.7320508076;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
use super::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create_coord()
|
|
||||||
{
|
|
||||||
let center = HexCoord::from_offset_pos(3, 3);
|
|
||||||
for dir in 0..6
|
|
||||||
{
|
|
||||||
assert_eq!(center.get_neighbor(dir).get_neighbor(dir), center.scale(dir, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,21 +6,20 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.18.0"
|
bevy = "0.14.2"
|
||||||
noise = "0.9.0"
|
noise = "0.9.0"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.115"
|
||||||
asset_loader = { path = "../asset_loader" }
|
asset_loader = { path = "../asset_loader" }
|
||||||
rayon = "1.11.0"
|
rayon = "1.10.0"
|
||||||
bevy-inspector-egui = "0.36.0"
|
bevy-inspector-egui = "0.25.0"
|
||||||
bevy_asset_loader = { version = "0.25.0", features = [
|
bevy_asset_loader = { version = "0.21.0", features = [
|
||||||
"standard_dynamic_assets",
|
"standard_dynamic_assets",
|
||||||
"3d",
|
"3d",
|
||||||
] }
|
] }
|
||||||
ron = "0.12.0"
|
ron = "0.8.1"
|
||||||
image = "0.25.9"
|
image = "0.25.2"
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
hex = { path = "../hex" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tracing = ["bevy/trace_tracy"]
|
tracing = ["bevy/trace_tracy"]
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use bevy::{mesh::MeshVertexAttribute, prelude::*, render::render_resource::VertexFormat};
|
use bevy::{
|
||||||
use hex::{INNER_RADIUS, OUTER_RADIUS};
|
prelude::*,
|
||||||
|
render::{mesh::MeshVertexAttribute, render_resource::VertexFormat},
|
||||||
|
};
|
||||||
|
|
||||||
// use crate::hex_utils::{INNER_RADIUS, OUTER_RADIUS};
|
use crate::hex_utils::{INNER_RADIUS, OUTER_RADIUS};
|
||||||
|
|
||||||
pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.);
|
pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.);
|
||||||
|
|
||||||
@@ -39,9 +41,9 @@ pub const HEX_NORMALS: [Vec3; 6] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute =
|
pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute =
|
||||||
MeshVertexAttribute::new("PackedVertexData", 7, VertexFormat::Uint32);
|
MeshVertexAttribute::new("PackedVertexData", 988540817, VertexFormat::Uint32);
|
||||||
pub const ATTRIBUTE_VERTEX_HEIGHT: MeshVertexAttribute =
|
pub const ATTRIBUTE_VERTEX_HEIGHT: MeshVertexAttribute =
|
||||||
MeshVertexAttribute::new("VertexHeight", 8, VertexFormat::Float32);
|
MeshVertexAttribute::new("VertexHeight", 988540717, VertexFormat::Float32);
|
||||||
|
|
||||||
pub const ATTRIBUTE_TEXTURE_INDEX: MeshVertexAttribute =
|
pub const ATTRIBUTE_TEXTURE_INDEX: MeshVertexAttribute =
|
||||||
MeshVertexAttribute::new("TextureIndex", 988540917, VertexFormat::Uint32);
|
MeshVertexAttribute::new("TextureIndex", 988540917, VertexFormat::Uint32);
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
use crate::prelude::*;
|
use crate::{hex_utils::*, prelude::*};
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use bevy::log::*;
|
use bevy::log::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hex::prelude::*;
|
|
||||||
|
|
||||||
const CHUNK_TOTAL: usize = Chunk::SIZE * Chunk::SIZE;
|
const CHUNK_TOTAL: usize = Chunk::SIZE * Chunk::SIZE;
|
||||||
|
|
||||||
pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3]>)
|
pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3]>) {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let span = info_span!("generate_chunk_collider").entered();
|
let span = info_span!("generate_chunk_collider").entered();
|
||||||
let vertex_count: usize = CHUNK_TOTAL * 6;
|
let vertex_count: usize = CHUNK_TOTAL * 6;
|
||||||
let mut verts = Vec::with_capacity(vertex_count);
|
let mut verts = Vec::with_capacity(vertex_count);
|
||||||
let mut indices = Vec::with_capacity(vertex_count);
|
let mut indices = Vec::with_capacity(vertex_count);
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let height = chunk.heights[x + z * Chunk::SIZE];
|
let height = chunk.heights[x + z * Chunk::SIZE];
|
||||||
let coord = HexCoord::from_offset_pos(x, z);
|
let coord = HexCoord::from_grid_pos(x, z);
|
||||||
let neighbors = chunk.get_neighbors(&coord);
|
let neighbors = chunk.get_neighbors(&coord);
|
||||||
let off_pos = Vec3::new(x as f32, height, z as f32);
|
let off_pos = Vec3::new(x as f32, height, z as f32);
|
||||||
let tile_pos = offset3d_to_world(off_pos);
|
let tile_pos = offset3d_to_world(off_pos);
|
||||||
@@ -28,11 +24,9 @@ pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3
|
|||||||
return (verts, indices);
|
return (verts, indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>, neighbors: &[f32; 6])
|
fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>, neighbors: &[f32; 6]) {
|
||||||
{
|
|
||||||
let idx = verts.len() as u32;
|
let idx = verts.len() as u32;
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let p = pos + HEX_CORNERS[i];
|
let p = pos + HEX_CORNERS[i];
|
||||||
verts.push(p);
|
verts.push(p);
|
||||||
}
|
}
|
||||||
@@ -43,11 +37,9 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32
|
|||||||
indices.push([idx + 2, idx + 4, idx + 5]);
|
indices.push([idx + 2, idx + 4, idx + 5]);
|
||||||
indices.push([idx + 2, idx + 3, idx + 4]);
|
indices.push([idx + 2, idx + 3, idx + 4]);
|
||||||
|
|
||||||
for i in 0..neighbors.len()
|
for i in 0..neighbors.len() {
|
||||||
{
|
|
||||||
let n_height = neighbors[i];
|
let n_height = neighbors[i];
|
||||||
if n_height < pos.y
|
if n_height < pos.y {
|
||||||
{
|
|
||||||
create_tile_wall_collider(
|
create_tile_wall_collider(
|
||||||
idx,
|
idx,
|
||||||
Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z),
|
Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z),
|
||||||
@@ -59,8 +51,7 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_tile_wall_collider(idx: u32, pos: Vec3, dir: usize, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>)
|
fn create_tile_wall_collider(idx: u32, pos: Vec3, dir: usize, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>) {
|
||||||
{
|
|
||||||
let idx2 = verts.len() as u32;
|
let idx2 = verts.len() as u32;
|
||||||
|
|
||||||
verts.push(pos + HEX_CORNERS[(dir) % 6]);
|
verts.push(pos + HEX_CORNERS[(dir) % 6]);
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
use crate::prelude::*;
|
use crate::hex_utils::HexCoord;
|
||||||
use bevy::asset::RenderAssetUsages;
|
use crate::{hex_utils::offset3d_to_world, prelude::*};
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use bevy::log::*;
|
use bevy::log::*;
|
||||||
use bevy::mesh::{Indices, PrimitiveTopology};
|
use bevy::{
|
||||||
use bevy::prelude::*;
|
prelude::*,
|
||||||
use hex::prelude::*;
|
render::{
|
||||||
|
mesh::{Indices, PrimitiveTopology},
|
||||||
|
render_asset::RenderAssetUsages,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh
|
pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let span = info_span!("generate_chunk_mesh").entered();
|
let span = info_span!("generate_chunk_mesh").entered();
|
||||||
|
|
||||||
@@ -17,15 +20,13 @@ pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh
|
|||||||
let mut indices = Vec::with_capacity(vertex_count);
|
let mut indices = Vec::with_capacity(vertex_count);
|
||||||
let mut normals = Vec::with_capacity(vertex_count);
|
let mut normals = Vec::with_capacity(vertex_count);
|
||||||
|
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let idx = x + z * Chunk::SIZE;
|
let idx = x + z * Chunk::SIZE;
|
||||||
let height = chunk.heights[idx];
|
let height = chunk.heights[idx];
|
||||||
let off_pos = Vec3::new(x as f32, height, z as f32);
|
let off_pos = Vec3::new(x as f32, height, z as f32);
|
||||||
let tile_pos = offset3d_to_world(off_pos);
|
let tile_pos = offset3d_to_world(off_pos);
|
||||||
let coord = HexCoord::from_offset_pos(x, z);
|
let coord = HexCoord::from_grid_pos(x, z);
|
||||||
let n = chunk.get_neighbors(&coord);
|
let n = chunk.get_neighbors(&coord);
|
||||||
|
|
||||||
create_tile(
|
create_tile(
|
||||||
@@ -38,6 +39,7 @@ pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh
|
|||||||
// &mut tex,
|
// &mut tex,
|
||||||
chunk.textures[idx][0],
|
chunk.textures[idx][0],
|
||||||
chunk.textures[idx][1],
|
chunk.textures[idx][1],
|
||||||
|
chunk.overlay_textures[idx],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,23 +64,24 @@ fn create_tile(
|
|||||||
normals: &mut Vec<Vec3>,
|
normals: &mut Vec<Vec3>,
|
||||||
texture_index: u32,
|
texture_index: u32,
|
||||||
side_texture_index: u32,
|
side_texture_index: u32,
|
||||||
)
|
side_overlay_texture_index: Option<u32>,
|
||||||
{
|
) {
|
||||||
let uv_offset = Vec2::splat(0.5);
|
let uv_offset = Vec2::splat(0.5);
|
||||||
let tex_off = Vec2::new(texture_index as f32, 0.);
|
let tex_off = Vec2::new(texture_index as f32, 0.);
|
||||||
let side_tex_off = Vec2::new(side_texture_index as f32, 0.);
|
let side_tex_off = Vec2::new(
|
||||||
|
pack_texture_data(side_texture_index, side_overlay_texture_index) as f32,
|
||||||
|
0.,
|
||||||
|
);
|
||||||
|
|
||||||
let idx = verts.len() as u32;
|
let idx = verts.len() as u32;
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let p = pos + HEX_CORNERS[i];
|
let p = pos + HEX_CORNERS[i];
|
||||||
verts.push(p);
|
verts.push(p);
|
||||||
let uv = (HEX_CORNERS[i].xz() / 2.) + uv_offset;
|
let uv = (HEX_CORNERS[i].xz() / 2.) + uv_offset;
|
||||||
uvs.push((uv / TEX_MULTI) + tex_off);
|
uvs.push((uv / TEX_MULTI) + tex_off);
|
||||||
normals.push(Vec3::Y);
|
normals.push(Vec3::Y);
|
||||||
}
|
}
|
||||||
for i in 0..3
|
for i in 0..3 {
|
||||||
{
|
|
||||||
let off = i * 2;
|
let off = i * 2;
|
||||||
indices.push(off + idx);
|
indices.push(off + idx);
|
||||||
indices.push(((off + 1) % 6) + idx);
|
indices.push(((off + 1) % 6) + idx);
|
||||||
@@ -88,19 +91,15 @@ fn create_tile(
|
|||||||
indices.push(idx + 2);
|
indices.push(idx + 2);
|
||||||
indices.push(idx + 4);
|
indices.push(idx + 4);
|
||||||
|
|
||||||
for i in 0..neighbors.len()
|
for i in 0..neighbors.len() {
|
||||||
{
|
|
||||||
let n_height = neighbors[i];
|
let n_height = neighbors[i];
|
||||||
if n_height < pos.y
|
if n_height < pos.y {
|
||||||
{
|
|
||||||
create_tile_wall(pos, i, n_height, verts, uvs, indices, normals, side_tex_off);
|
create_tile_wall(pos, i, n_height, verts, uvs, indices, normals, side_tex_off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
pub fn generate_chunk_water_mesh(chunk: &MeshChunkData, sealevel: f32, map_width: usize, map_height: usize) -> Mesh {
|
||||||
pub fn generate_chunk_water_mesh(chunk: &MeshChunkData, sealevel: f32, map_width: usize, map_height: usize) -> Mesh
|
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _gen_mesh = info_span!("Generate Water Surface Mesh").entered();
|
let _gen_mesh = info_span!("Generate Water Surface Mesh").entered();
|
||||||
let vertex_count: usize = Chunk::SIZE * Chunk::SIZE * 7;
|
let vertex_count: usize = Chunk::SIZE * Chunk::SIZE * 7;
|
||||||
@@ -109,19 +108,16 @@ pub fn generate_chunk_water_mesh(chunk: &MeshChunkData, sealevel: f32, map_width
|
|||||||
let mut indices = Vec::with_capacity(vertex_count);
|
let mut indices = Vec::with_capacity(vertex_count);
|
||||||
let mut normals = Vec::with_capacity(vertex_count);
|
let mut normals = Vec::with_capacity(vertex_count);
|
||||||
|
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let idx = x + z * Chunk::SIZE;
|
let idx = x + z * Chunk::SIZE;
|
||||||
let height = chunk.heights[idx];
|
let height = chunk.heights[idx];
|
||||||
if height > sealevel
|
if height > sealevel {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let off_pos = Vec3::new(x as f32, sealevel, z as f32);
|
let off_pos = Vec3::new(x as f32, sealevel, z as f32);
|
||||||
let tile_pos = offset3d_to_world(off_pos);
|
let tile_pos = offset3d_to_world(off_pos);
|
||||||
let coord = HexCoord::from_offset_pos(x, z);
|
let coord = HexCoord::from_grid_pos(x, z);
|
||||||
let (n, neighbor_has_land) = chunk.get_neighbors_with_water_info(&coord);
|
let (n, neighbor_has_land) = chunk.get_neighbors_with_water_info(&coord);
|
||||||
|
|
||||||
create_tile_water_surface(
|
create_tile_water_surface(
|
||||||
@@ -156,10 +152,8 @@ fn create_tile_water_surface(
|
|||||||
uvs: &mut Vec<Vec2>,
|
uvs: &mut Vec<Vec2>,
|
||||||
indices: &mut Vec<u32>,
|
indices: &mut Vec<u32>,
|
||||||
normals: &mut Vec<Vec3>,
|
normals: &mut Vec<Vec3>,
|
||||||
)
|
) {
|
||||||
{
|
if !neighbor_has_land {
|
||||||
if !neighbor_has_land
|
|
||||||
{
|
|
||||||
crate_tile_water_inner_surface(pos, dist_to_land, neighbors, verts, uvs, indices, normals);
|
crate_tile_water_inner_surface(pos, dist_to_land, neighbors, verts, uvs, indices, normals);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -174,29 +168,23 @@ fn crate_tile_water_inner_surface(
|
|||||||
uvs: &mut Vec<Vec2>,
|
uvs: &mut Vec<Vec2>,
|
||||||
indices: &mut Vec<u32>,
|
indices: &mut Vec<u32>,
|
||||||
normals: &mut Vec<Vec3>,
|
normals: &mut Vec<Vec3>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
//todo: share verts
|
//todo: share verts
|
||||||
let idx = verts.len() as u32;
|
let idx = verts.len() as u32;
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let p = pos + HEX_CORNERS[i];
|
let p = pos + HEX_CORNERS[i];
|
||||||
verts.push(p);
|
verts.push(p);
|
||||||
let n1 = if let Some(v) = neighbors[i].1 { v } else { dist_to_land };
|
let n1 = if let Some(v) = neighbors[i].1 { v } else { dist_to_land };
|
||||||
let n2 = if let Some(v) = neighbors[(i + 5) % 6].1
|
let n2 = if let Some(v) = neighbors[(i + 5) % 6].1 {
|
||||||
{
|
|
||||||
v
|
v
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
dist_to_land
|
dist_to_land
|
||||||
};
|
};
|
||||||
let d = (n1 + n2 + dist_to_land) / 3.0;
|
let d = (n1 + n2 + dist_to_land) / 3.0;
|
||||||
uvs.push(Vec2::new(0.0, d.remap(0., 4., 1.0, 0.0)));
|
uvs.push(Vec2::new(0.0, d.remap(0., 4., 1.0, 0.0)));
|
||||||
normals.push(Vec3::Y);
|
normals.push(Vec3::Y);
|
||||||
}
|
}
|
||||||
for i in 0..3
|
for i in 0..3 {
|
||||||
{
|
|
||||||
let off = i * 2;
|
let off = i * 2;
|
||||||
indices.push(off + idx);
|
indices.push(off + idx);
|
||||||
indices.push(((off + 1) % 6) + idx);
|
indices.push(((off + 1) % 6) + idx);
|
||||||
@@ -215,15 +203,13 @@ fn crate_tile_water_shore_surface(
|
|||||||
uvs: &mut Vec<Vec2>,
|
uvs: &mut Vec<Vec2>,
|
||||||
indices: &mut Vec<u32>,
|
indices: &mut Vec<u32>,
|
||||||
normals: &mut Vec<Vec3>,
|
normals: &mut Vec<Vec3>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let idx = verts.len() as u32;
|
let idx = verts.len() as u32;
|
||||||
//todo: only use triangle fan when on shoreline
|
//todo: only use triangle fan when on shoreline
|
||||||
verts.push(pos);
|
verts.push(pos);
|
||||||
uvs.push(Vec2::new(0.0, dist_to_land.remap(0., 4., 1.0, 0.0)));
|
uvs.push(Vec2::new(0.0, dist_to_land.remap(0., 4., 1.0, 0.0)));
|
||||||
normals.push(Vec3::Y);
|
normals.push(Vec3::Y);
|
||||||
for i in 0..12
|
for i in 0..12 {
|
||||||
{
|
|
||||||
let p = pos + WATER_HEX_CORNERS[i];
|
let p = pos + WATER_HEX_CORNERS[i];
|
||||||
verts.push(p);
|
verts.push(p);
|
||||||
let ni = i / 2;
|
let ni = i / 2;
|
||||||
@@ -231,21 +217,16 @@ fn crate_tile_water_shore_surface(
|
|||||||
let nn = neighbors[(ni + 5) % 6];
|
let nn = neighbors[(ni + 5) % 6];
|
||||||
let mut uv = Vec2::new(0.0, dist_to_land.remap(0., 4., 1.0, 0.0));
|
let mut uv = Vec2::new(0.0, dist_to_land.remap(0., 4., 1.0, 0.0));
|
||||||
|
|
||||||
if nn.0 > pos.y || n.0 > pos.y
|
if nn.0 > pos.y || n.0 > pos.y {
|
||||||
{
|
|
||||||
uv.x = 1.0;
|
uv.x = 1.0;
|
||||||
}
|
}
|
||||||
if ni * 2 != i
|
if ni * 2 != i {
|
||||||
{
|
if n.0 <= pos.y {
|
||||||
if n.0 <= pos.y
|
|
||||||
{
|
|
||||||
uv.x = 0.0;
|
uv.x = 0.0;
|
||||||
}
|
}
|
||||||
let d = if let Some(v) = n.1 { v } else { dist_to_land };
|
let d = if let Some(v) = n.1 { v } else { dist_to_land };
|
||||||
uv.y = ((d + dist_to_land) / 2.0).remap(0., 4., 1.0, 0.0);
|
uv.y = ((d + dist_to_land) / 2.0).remap(0., 4., 1.0, 0.0);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
let d = if let Some(v) = n.1 { v } else { dist_to_land };
|
let d = if let Some(v) = n.1 { v } else { dist_to_land };
|
||||||
let d2 = if let Some(v) = nn.1 { v } else { dist_to_land };
|
let d2 = if let Some(v) = nn.1 { v } else { dist_to_land };
|
||||||
uv.y = ((d + d2 + dist_to_land) / 3.0).remap(0., 4., 1.0, 0.0);
|
uv.y = ((d + d2 + dist_to_land) / 3.0).remap(0., 4., 1.0, 0.0);
|
||||||
@@ -269,8 +250,7 @@ fn create_tile_wall(
|
|||||||
indices: &mut Vec<u32>,
|
indices: &mut Vec<u32>,
|
||||||
normals: &mut Vec<Vec3>,
|
normals: &mut Vec<Vec3>,
|
||||||
tex_off: Vec2,
|
tex_off: Vec2,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let p1 = HEX_CORNERS[(dir) % 6] + pos;
|
let p1 = HEX_CORNERS[(dir) % 6] + pos;
|
||||||
let p2 = HEX_CORNERS[(dir + 1) % 6] + pos;
|
let p2 = HEX_CORNERS[(dir + 1) % 6] + pos;
|
||||||
let p3 = Vec3::new(p1.x, height, p1.z);
|
let p3 = Vec3::new(p1.x, height, p1.z);
|
||||||
@@ -303,15 +283,20 @@ fn create_tile_wall(
|
|||||||
uvs.push((Vec2::new(1., pos.y - height) / TEX_MULTI) + tex_off);
|
uvs.push((Vec2::new(1., pos.y - height) / TEX_MULTI) + tex_off);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pack_texture_data(texture: u32, overlay: Option<u32>) -> u32 {
|
||||||
|
if let Some(ovr) = overlay {
|
||||||
|
return texture + (ovr << 5);
|
||||||
|
}
|
||||||
|
return texture + (texture << 5);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests
|
mod tests {
|
||||||
{
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_tile_wall()
|
fn generate_tile_wall() {
|
||||||
{
|
|
||||||
let mut verts = Vec::new();
|
let mut verts = Vec::new();
|
||||||
let mut uvs = Vec::new();
|
let mut uvs = Vec::new();
|
||||||
let mut normals = Vec::new();
|
let mut normals = Vec::new();
|
||||||
@@ -338,8 +323,7 @@ mod tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_tile()
|
fn generate_tile() {
|
||||||
{
|
|
||||||
let mut verts = Vec::new();
|
let mut verts = Vec::new();
|
||||||
let mut uvs = Vec::new();
|
let mut uvs = Vec::new();
|
||||||
let mut normals = Vec::new();
|
let mut normals = Vec::new();
|
||||||
@@ -348,7 +332,17 @@ mod tests
|
|||||||
//4 side faces
|
//4 side faces
|
||||||
let nbors = [2.0, 2.0, 0.0, 0.0, 0.0, 0.0];
|
let nbors = [2.0, 2.0, 0.0, 0.0, 0.0, 0.0];
|
||||||
|
|
||||||
create_tile(Vec3::Y, &nbors, &mut verts, &mut uvs, &mut indices, &mut normals, 3, 7);
|
create_tile(
|
||||||
|
Vec3::Y,
|
||||||
|
&nbors,
|
||||||
|
&mut verts,
|
||||||
|
&mut uvs,
|
||||||
|
&mut indices,
|
||||||
|
&mut normals,
|
||||||
|
3,
|
||||||
|
7,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
assert!(verts.len() == (6 + 4 * 4), "Number of verts don't match");
|
assert!(verts.len() == (6 + 4 * 4), "Number of verts don't match");
|
||||||
assert!(uvs.len() == (6 + 4 * 4), "Number of uvs don't match");
|
assert!(uvs.len() == (6 + 4 * 4), "Number of uvs don't match");
|
||||||
|
|||||||
@@ -1,26 +1,43 @@
|
|||||||
|
use crate::hex_utils::HexCoord;
|
||||||
|
use crate::map::biome_map::BiomeChunk;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy::asset::RenderAssetUsages;
|
use crate::tile_manager::TileAsset;
|
||||||
|
use crate::tile_mapper::TileMapperAsset;
|
||||||
|
use crate::{biome_asset::BiomeAsset, biome_painter::BiomePainterAsset};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
mesh::{Indices, PrimitiveTopology},
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
mesh::{Indices, PrimitiveTopology},
|
||||||
|
render_asset::RenderAssetUsages,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use hex::prelude::*;
|
|
||||||
|
|
||||||
pub fn generate_packed_chunk_mesh(chunk: &MeshChunkData) -> Mesh
|
pub fn generate_packed_chunk_mesh(
|
||||||
{
|
chunk: &Chunk,
|
||||||
|
map: &Map,
|
||||||
|
biome_chunk: &BiomeChunk,
|
||||||
|
painter: &BiomePainterAsset,
|
||||||
|
tiles: &Res<Assets<TileAsset>>,
|
||||||
|
biomes: &Res<Assets<BiomeAsset>>,
|
||||||
|
mappers: &Res<Assets<TileMapperAsset>>,
|
||||||
|
) -> Mesh {
|
||||||
let vertex_count: usize = Chunk::SIZE * Chunk::SIZE * 6;
|
let vertex_count: usize = Chunk::SIZE * Chunk::SIZE * 6;
|
||||||
let mut packed_data = Vec::with_capacity(vertex_count);
|
let mut packed_data = Vec::with_capacity(vertex_count);
|
||||||
let mut indices = Vec::with_capacity(vertex_count);
|
let mut indices = Vec::with_capacity(vertex_count);
|
||||||
let mut heights = Vec::with_capacity(vertex_count);
|
let mut heights = Vec::with_capacity(vertex_count);
|
||||||
|
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
let height = chunk.heights[x + z * Chunk::SIZE];
|
||||||
{
|
let data = biome_chunk.data[x + z * Chunk::SIZE];
|
||||||
let idx = x + z * Chunk::SIZE;
|
let coord =
|
||||||
let height = chunk.heights[idx];
|
HexCoord::from_offset(IVec2::new(x as i32, z as i32) + (chunk.chunk_offset * Chunk::SIZE as i32));
|
||||||
let coord = HexCoord::from_offset_pos(x, z);
|
let n = map.get_neighbors(&coord);
|
||||||
let n = chunk.get_neighbors(&coord);
|
let biome = biomes.get(painter.sample_biome(biomes, &data)).unwrap();
|
||||||
|
|
||||||
|
let mapper = mappers.get(biome.tile_mapper.id());
|
||||||
|
let tile_handle = mapper.unwrap().sample_tile(height);
|
||||||
|
let tile = tiles.get(tile_handle).unwrap();
|
||||||
|
|
||||||
create_packed_tile(
|
create_packed_tile(
|
||||||
UVec2::new(x as u32, z as u32),
|
UVec2::new(x as u32, z as u32),
|
||||||
@@ -29,8 +46,8 @@ pub fn generate_packed_chunk_mesh(chunk: &MeshChunkData) -> Mesh
|
|||||||
&mut packed_data,
|
&mut packed_data,
|
||||||
&mut indices,
|
&mut indices,
|
||||||
&mut heights,
|
&mut heights,
|
||||||
chunk.textures[idx][0],
|
tile.texture_id,
|
||||||
chunk.textures[idx][1],
|
tile.side_texture_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,20 +65,18 @@ pub fn generate_packed_chunk_mesh(chunk: &MeshChunkData) -> Mesh
|
|||||||
fn create_packed_tile(
|
fn create_packed_tile(
|
||||||
offset: UVec2,
|
offset: UVec2,
|
||||||
height: f32,
|
height: f32,
|
||||||
neighbors: &[f32; 6],
|
neighbors: &[Option<f32>; 6],
|
||||||
packed_data: &mut Vec<u32>,
|
packed_data: &mut Vec<u32>,
|
||||||
indices: &mut Vec<u32>,
|
indices: &mut Vec<u32>,
|
||||||
heights: &mut Vec<f32>,
|
heights: &mut Vec<f32>,
|
||||||
texture_index: u32,
|
texture_index: u32,
|
||||||
side_texture_index: u32,
|
side_texture_index: u32,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let idx = packed_data.len() as u32;
|
let idx = packed_data.len() as u32;
|
||||||
|
|
||||||
packed_data.push(pack_vertex_data(offset, 0, texture_index));
|
packed_data.push(pack_vertex_data(offset, 0, texture_index));
|
||||||
heights.push(height);
|
heights.push(height);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
packed_data.push(pack_vertex_data(offset, i + 1, texture_index));
|
packed_data.push(pack_vertex_data(offset, i + 1, texture_index));
|
||||||
indices.push(idx);
|
indices.push(idx);
|
||||||
indices.push(idx + 1 + i as u32);
|
indices.push(idx + 1 + i as u32);
|
||||||
@@ -69,11 +84,11 @@ fn create_packed_tile(
|
|||||||
heights.push(height);
|
heights.push(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..neighbors.len()
|
for i in 0..neighbors.len() {
|
||||||
{
|
let cur_n = neighbors[i];
|
||||||
let n_height = neighbors[i];
|
match cur_n {
|
||||||
if n_height < height
|
Some(n_height) => {
|
||||||
{
|
if n_height < height {
|
||||||
create_packed_tile_wall(
|
create_packed_tile_wall(
|
||||||
offset,
|
offset,
|
||||||
height,
|
height,
|
||||||
@@ -86,6 +101,9 @@ fn create_packed_tile(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_packed_tile_wall(
|
fn create_packed_tile_wall(
|
||||||
@@ -97,8 +115,7 @@ fn create_packed_tile_wall(
|
|||||||
indices: &mut Vec<u32>,
|
indices: &mut Vec<u32>,
|
||||||
heights: &mut Vec<f32>,
|
heights: &mut Vec<f32>,
|
||||||
side_texture_index: u32,
|
side_texture_index: u32,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let idx = packed_data.len() as u32;
|
let idx = packed_data.len() as u32;
|
||||||
|
|
||||||
let side_2 = ((side + 1) % 6) + 1;
|
let side_2 = ((side + 1) % 6) + 1;
|
||||||
@@ -121,8 +138,7 @@ fn create_packed_tile_wall(
|
|||||||
indices.push(idx + 3);
|
indices.push(idx + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pack_vertex_data(offset: UVec2, vert: usize, tex: u32) -> u32
|
fn pack_vertex_data(offset: UVec2, vert: usize, tex: u32) -> u32 {
|
||||||
{
|
|
||||||
//6 + 6 bits offset
|
//6 + 6 bits offset
|
||||||
//4 bits vert
|
//4 bits vert
|
||||||
//12 bits texture
|
//12 bits texture
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use core::f32;
|
|||||||
use bevy::math::{IVec2, UVec2};
|
use bevy::math::{IVec2, UVec2};
|
||||||
use bevy::prelude::{FloatExt, Vec2};
|
use bevy::prelude::{FloatExt, Vec2};
|
||||||
use bevy::utils::default;
|
use bevy::utils::default;
|
||||||
use hex::prelude::*;
|
|
||||||
use noise::{NoiseFn, Simplex, SuperSimplex};
|
use noise::{NoiseFn, Simplex, SuperSimplex};
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
@@ -11,8 +10,7 @@ use crate::biome_painter::BiomePainter;
|
|||||||
use crate::map::biome_map::{BiomeChunk, BiomeData, BiomeMap};
|
use crate::map::biome_map::{BiomeChunk, BiomeData, BiomeMap};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> (Map, BiomeMap)
|
pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> (Map, BiomeMap) {
|
||||||
{
|
|
||||||
let biomes = generate_biomes(cfg, seed, painter);
|
let biomes = generate_biomes(cfg, seed, painter);
|
||||||
let biomes_borrow = &biomes;
|
let biomes_borrow = &biomes;
|
||||||
// let mut chunks: Vec<Chunk> = Vec::with_capacity(cfg.size.length_squared() as usize);
|
// let mut chunks: Vec<Chunk> = Vec::with_capacity(cfg.size.length_squared() as usize);
|
||||||
@@ -27,14 +25,11 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePain
|
|||||||
.collect();
|
.collect();
|
||||||
let mut min = f32::MAX;
|
let mut min = f32::MAX;
|
||||||
let mut max = f32::MIN;
|
let mut max = f32::MIN;
|
||||||
for chunk in &chunks
|
for chunk in &chunks {
|
||||||
{
|
if chunk.min_level < min {
|
||||||
if chunk.min_level < min
|
|
||||||
{
|
|
||||||
min = chunk.min_level;
|
min = chunk.min_level;
|
||||||
}
|
}
|
||||||
if chunk.max_level > max
|
if chunk.max_level > max {
|
||||||
{
|
|
||||||
max = chunk.max_level;
|
max = chunk.max_level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,8 +48,7 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePain
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_biomes(cfg: &GenerationConfig, seed: u32, biome_painter: &BiomePainter) -> BiomeMap
|
pub fn generate_biomes(cfg: &GenerationConfig, seed: u32, biome_painter: &BiomePainter) -> BiomeMap {
|
||||||
{
|
|
||||||
let mut map = BiomeMap::new(cfg.size, biome_painter.biomes.len());
|
let mut map = BiomeMap::new(cfg.size, biome_painter.biomes.len());
|
||||||
map.chunks = (0..cfg.size.y)
|
map.chunks = (0..cfg.size.y)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
@@ -74,8 +68,7 @@ pub fn generate_biome_chunk(
|
|||||||
cfg: &GenerationConfig,
|
cfg: &GenerationConfig,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
biome_painter: &BiomePainter,
|
biome_painter: &BiomePainter,
|
||||||
) -> BiomeChunk
|
) -> BiomeChunk {
|
||||||
{
|
|
||||||
let mut chunk = BiomeChunk {
|
let mut chunk = BiomeChunk {
|
||||||
offset: UVec2::new(chunk_x as u32, chunk_y as u32),
|
offset: UVec2::new(chunk_x as u32, chunk_y as u32),
|
||||||
data: [BiomeData::default(); Chunk::AREA],
|
data: [BiomeData::default(); Chunk::AREA],
|
||||||
@@ -85,10 +78,8 @@ pub fn generate_biome_chunk(
|
|||||||
let noise_t = Simplex::new(seed + 2);
|
let noise_t = Simplex::new(seed + 2);
|
||||||
let noise_c = Simplex::new(seed + 3);
|
let noise_c = Simplex::new(seed + 3);
|
||||||
|
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let moisture = sample_point(
|
let moisture = sample_point(
|
||||||
x as f64 + chunk_x as f64 * Chunk::SIZE as f64,
|
x as f64 + chunk_x as f64 * Chunk::SIZE as f64,
|
||||||
z as f64 + chunk_y as f64 * Chunk::SIZE as f64,
|
z as f64 + chunk_y as f64 * Chunk::SIZE as f64,
|
||||||
@@ -132,16 +123,14 @@ pub fn generate_biome_chunk(
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_noise_map(size: UVec2, seed: u32, cfg: &NoiseConfig, border_size: f32) -> Vec<f32>
|
pub fn generate_noise_map(size: UVec2, seed: u32, cfg: &NoiseConfig, border_size: f32) -> Vec<f32> {
|
||||||
{
|
|
||||||
let noise = SuperSimplex::new(seed);
|
let noise = SuperSimplex::new(seed);
|
||||||
|
|
||||||
let data: Vec<_> = (0..(size.y as usize * Chunk::SIZE))
|
let data: Vec<_> = (0..(size.y as usize * Chunk::SIZE))
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.flat_map(|y| {
|
.flat_map(|y| {
|
||||||
let mut row = Vec::with_capacity(size.x as usize * Chunk::SIZE);
|
let mut row = Vec::with_capacity(size.x as usize * Chunk::SIZE);
|
||||||
for x in 0..row.capacity()
|
for x in 0..row.capacity() {
|
||||||
{
|
|
||||||
row.push(sample_point(
|
row.push(sample_point(
|
||||||
x as f64,
|
x as f64,
|
||||||
y as f64,
|
y as f64,
|
||||||
@@ -165,26 +154,21 @@ pub fn generate_chunk(
|
|||||||
seed: u32,
|
seed: u32,
|
||||||
biome_chunk: &BiomeChunk,
|
biome_chunk: &BiomeChunk,
|
||||||
biome_painter: &BiomePainter,
|
biome_painter: &BiomePainter,
|
||||||
) -> Chunk
|
) -> Chunk {
|
||||||
{
|
|
||||||
let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::AREA];
|
let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::AREA];
|
||||||
let mut data = [BiomeData::default(); Chunk::AREA];
|
let mut data = [BiomeData::default(); Chunk::AREA];
|
||||||
let mut biome_ids = [0; Chunk::AREA];
|
let mut biome_ids = [0; Chunk::AREA];
|
||||||
let noise = Simplex::new(seed);
|
let noise = Simplex::new(seed);
|
||||||
let mut min = f32::MAX;
|
let mut min = f32::MAX;
|
||||||
let mut max = f32::MIN;
|
let mut max = f32::MIN;
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let biome_data = biome_chunk.get_biome_data(x, z);
|
let biome_data = biome_chunk.get_biome_data(x, z);
|
||||||
let biome_blend = biome_chunk.get_biome(x, z);
|
let biome_blend = biome_chunk.get_biome(x, z);
|
||||||
let mut sample = 0.;
|
let mut sample = 0.;
|
||||||
for i in 0..biome_blend.len()
|
for i in 0..biome_blend.len() {
|
||||||
{
|
|
||||||
let blend = biome_blend[i];
|
let blend = biome_blend[i];
|
||||||
if blend == 0.
|
if blend == 0. {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let biome = &biome_painter.biomes[i];
|
let biome = &biome_painter.biomes[i];
|
||||||
@@ -201,12 +185,10 @@ pub fn generate_chunk(
|
|||||||
let idx = x + z * Chunk::SIZE;
|
let idx = x + z * Chunk::SIZE;
|
||||||
biome_ids[idx] = biome_chunk.get_biome_id_dithered(x, z, &noise, cfg.biome_dither);
|
biome_ids[idx] = biome_chunk.get_biome_id_dithered(x, z, &noise, cfg.biome_dither);
|
||||||
result[idx] = sample;
|
result[idx] = sample;
|
||||||
if sample > max
|
if sample > max {
|
||||||
{
|
|
||||||
max = sample;
|
max = sample;
|
||||||
}
|
}
|
||||||
if sample < min
|
if sample < min {
|
||||||
{
|
|
||||||
min = sample;
|
min = sample;
|
||||||
}
|
}
|
||||||
data[idx] = biome_data.clone();
|
data[idx] = biome_data.clone();
|
||||||
@@ -230,29 +212,23 @@ fn sample_point(
|
|||||||
size: Vec2,
|
size: Vec2,
|
||||||
border_size: f32,
|
border_size: f32,
|
||||||
border_value: f32,
|
border_value: f32,
|
||||||
) -> f32
|
) -> f32 {
|
||||||
{
|
|
||||||
let x_s = x / cfg.scale;
|
let x_s = x / cfg.scale;
|
||||||
let z_s = z / cfg.scale;
|
let z_s = z / cfg.scale;
|
||||||
|
|
||||||
let mut elevation: f64 = 0.;
|
let mut elevation: f64 = 0.;
|
||||||
for i in 0..cfg.layers.len()
|
for i in 0..cfg.layers.len() {
|
||||||
{
|
|
||||||
let value: f64;
|
let value: f64;
|
||||||
let layer = &cfg.layers[i];
|
let layer = &cfg.layers[i];
|
||||||
if layer.is_rigid
|
if layer.is_rigid {
|
||||||
{
|
|
||||||
value = sample_rigid(x_s, z_s, layer, noise);
|
value = sample_rigid(x_s, z_s, layer, noise);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
value = sample_simple(x_s, z_s, layer, noise);
|
value = sample_simple(x_s, z_s, layer, noise);
|
||||||
}
|
}
|
||||||
elevation += value;
|
elevation += value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if border_size == 0.0
|
if border_size == 0.0 {
|
||||||
{
|
|
||||||
return elevation as f32;
|
return elevation as f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,14 +243,12 @@ fn sample_point(
|
|||||||
return border_value.lerp(elevation as f32, d);
|
return border_value.lerp(elevation as f32, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64
|
fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64 {
|
||||||
{
|
|
||||||
let mut freq: f64 = cfg.base_roughness;
|
let mut freq: f64 = cfg.base_roughness;
|
||||||
let mut amp: f64 = 1.;
|
let mut amp: f64 = 1.;
|
||||||
let mut value = 0.;
|
let mut value = 0.;
|
||||||
|
|
||||||
for _ in 0..cfg.layers
|
for _ in 0..cfg.layers {
|
||||||
{
|
|
||||||
let v = noise.get([x * freq, z * freq]);
|
let v = noise.get([x * freq, z * freq]);
|
||||||
value += (v + 1.) * 0.5 * amp;
|
value += (v + 1.) * 0.5 * amp;
|
||||||
freq *= cfg.roughness;
|
freq *= cfg.roughness;
|
||||||
@@ -283,14 +257,12 @@ fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64,
|
|||||||
value -= cfg.min_value;
|
value -= cfg.min_value;
|
||||||
return value * cfg.strength;
|
return value * cfg.strength;
|
||||||
}
|
}
|
||||||
fn sample_rigid(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64
|
fn sample_rigid(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64 {
|
||||||
{
|
|
||||||
let mut freq: f64 = cfg.base_roughness;
|
let mut freq: f64 = cfg.base_roughness;
|
||||||
let mut amp: f64 = 1.;
|
let mut amp: f64 = 1.;
|
||||||
let mut value = 0.;
|
let mut value = 0.;
|
||||||
let mut weight = 1.;
|
let mut weight = 1.;
|
||||||
for _ in 0..cfg.layers
|
for _ in 0..cfg.layers {
|
||||||
{
|
|
||||||
let mut v = 1. - noise.get([x * freq, z * freq]).abs();
|
let mut v = 1. - noise.get([x * freq, z * freq]).abs();
|
||||||
v *= v;
|
v *= v;
|
||||||
v *= weight;
|
v *= weight;
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::prelude::Chunk;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{INNER_RADIUS, OUTER_RADIUS, chunk::Chunk};
|
pub const OUTER_RADIUS: f32 = 1.;
|
||||||
|
pub const INNER_RADIUS: f32 = OUTER_RADIUS * (SQRT_3 / 2.);
|
||||||
|
pub const SHORT_DIAGONAL: f32 = 1. * SQRT_3;
|
||||||
|
pub const LONG_DIAGONAL: f32 = 2. * OUTER_RADIUS;
|
||||||
|
const SQRT_3: f32 = 1.7320508076;
|
||||||
|
|
||||||
pub fn offset3d_to_world(offset: Vec3) -> Vec3
|
pub fn offset3d_to_world(offset: Vec3) -> Vec3 {
|
||||||
{
|
|
||||||
let x = (offset.x + (offset.z * 0.5) - (offset.z / 2.).floor()) * (INNER_RADIUS * 2.);
|
let x = (offset.x + (offset.z * 0.5) - (offset.z / 2.).floor()) * (INNER_RADIUS * 2.);
|
||||||
return Vec3::new(x, offset.y, offset.z * OUTER_RADIUS * 1.5);
|
return Vec3::new(x, offset.y, offset.z * OUTER_RADIUS * 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset_to_world(offset: IVec2, height: f32) -> Vec3
|
pub fn offset_to_world(offset: IVec2, height: f32) -> Vec3 {
|
||||||
{
|
|
||||||
let off = offset.as_vec2();
|
let off = offset.as_vec2();
|
||||||
let x = (off.x + (off.y * 0.5) - (off.y / 2.).floor()) * (INNER_RADIUS * 2.);
|
let x = (off.x + (off.y * 0.5) - (off.y / 2.).floor()) * (INNER_RADIUS * 2.);
|
||||||
return Vec3::new(x, height, off.y * OUTER_RADIUS * 1.5);
|
return Vec3::new(x, height, off.y * OUTER_RADIUS * 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset_to_hex(offset: IVec2) -> IVec3
|
pub fn offset_to_hex(offset: IVec2) -> IVec3 {
|
||||||
{
|
|
||||||
let mut v = IVec3 {
|
let mut v = IVec3 {
|
||||||
x: offset.x - (offset.y / 2),
|
x: offset.x - (offset.y / 2),
|
||||||
y: offset.y,
|
y: offset.y,
|
||||||
@@ -29,18 +31,15 @@ pub fn offset_to_hex(offset: IVec2) -> IVec3
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset_to_index(offset: IVec2, width: usize) -> usize
|
pub fn offset_to_index(offset: IVec2, width: usize) -> usize {
|
||||||
{
|
|
||||||
return offset.x as usize + offset.y as usize * width;
|
return offset.x as usize + offset.y as usize * width;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snap_to_hex_grid(world_pos: Vec3) -> Vec3
|
pub fn snap_to_hex_grid(world_pos: Vec3) -> Vec3 {
|
||||||
{
|
|
||||||
return offset_to_world(world_to_offset_pos(world_pos), world_pos.y);
|
return offset_to_world(world_to_offset_pos(world_pos), world_pos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn world_to_offset_pos(world_pos: Vec3) -> IVec2
|
pub fn world_to_offset_pos(world_pos: Vec3) -> IVec2 {
|
||||||
{
|
|
||||||
let offset = world_pos.z / (OUTER_RADIUS * 3.);
|
let offset = world_pos.z / (OUTER_RADIUS * 3.);
|
||||||
let x = (world_pos.x / (INNER_RADIUS * 2.)) - offset;
|
let x = (world_pos.x / (INNER_RADIUS * 2.)) - offset;
|
||||||
let z = -world_pos.x - offset;
|
let z = -world_pos.x - offset;
|
||||||
@@ -52,32 +51,26 @@ pub fn world_to_offset_pos(world_pos: Vec3) -> IVec2
|
|||||||
return IVec2::new(ox, oz);
|
return IVec2::new(ox, oz);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tile_to_world_distance(dist: u32) -> f32
|
pub fn tile_to_world_distance(dist: u32) -> f32 {
|
||||||
{
|
|
||||||
return dist as f32 * (2. * INNER_RADIUS);
|
return dist as f32 * (2. * INNER_RADIUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tile_count_in_range(radius: usize) -> usize
|
pub fn get_tile_count_in_range(radius: usize) -> usize {
|
||||||
{
|
|
||||||
return 1 + 3 * (radius + 1) * radius;
|
return 1 + 3 * (radius + 1) * radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
pub struct HexCoord
|
pub struct HexCoord {
|
||||||
{
|
|
||||||
pub hex: IVec3,
|
pub hex: IVec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for HexCoord
|
impl Display for HexCoord {
|
||||||
{
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
|
||||||
{
|
|
||||||
f.write_fmt(format_args!("HexCoord{}", self.hex))
|
f.write_fmt(format_args!("HexCoord{}", self.hex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HexCoord
|
impl HexCoord {
|
||||||
{
|
|
||||||
pub const DIRECTIONS: [IVec3; 6] = [
|
pub const DIRECTIONS: [IVec3; 6] = [
|
||||||
IVec3::new(0, 1, -1),
|
IVec3::new(0, 1, -1),
|
||||||
IVec3::new(1, 0, -1),
|
IVec3::new(1, 0, -1),
|
||||||
@@ -89,33 +82,27 @@ impl HexCoord
|
|||||||
|
|
||||||
pub const ZERO: HexCoord = HexCoord { hex: IVec3::ZERO };
|
pub const ZERO: HexCoord = HexCoord { hex: IVec3::ZERO };
|
||||||
|
|
||||||
pub fn new(x: i32, z: i32) -> Self
|
pub fn new(x: i32, z: i32) -> Self {
|
||||||
{
|
|
||||||
return HexCoord {
|
return HexCoord {
|
||||||
hex: IVec3::new(x, z, -x - z),
|
hex: IVec3::new(x, z, -x - z),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_axial(hex: IVec2) -> Self
|
pub fn from_hex(hex: IVec2) -> Self {
|
||||||
{
|
|
||||||
return HexCoord {
|
return HexCoord {
|
||||||
hex: IVec3::new(hex.x, hex.y, -hex.x - hex.y),
|
hex: IVec3::new(hex.x, hex.y, -hex.x - hex.y),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub fn from_grid_pos(x: usize, z: usize) -> Self {
|
||||||
pub fn from_offset_pos(x: usize, z: usize) -> Self
|
|
||||||
{
|
|
||||||
return HexCoord::new(x as i32 - (z as i32 / 2), z as i32);
|
return HexCoord::new(x as i32 - (z as i32 / 2), z as i32);
|
||||||
}
|
}
|
||||||
pub fn from_offset(offset_pos: IVec2) -> Self
|
pub fn from_offset(offset_pos: IVec2) -> Self {
|
||||||
{
|
|
||||||
return HexCoord {
|
return HexCoord {
|
||||||
hex: offset_to_hex(offset_pos),
|
hex: offset_to_hex(offset_pos),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_world_pos(world_pos: Vec3) -> Self
|
pub fn from_world_pos(world_pos: Vec3) -> Self {
|
||||||
{
|
|
||||||
let offset = world_pos.z / (OUTER_RADIUS * 3.);
|
let offset = world_pos.z / (OUTER_RADIUS * 3.);
|
||||||
let mut x = world_pos.x / (INNER_RADIUS * 2.);
|
let mut x = world_pos.x / (INNER_RADIUS * 2.);
|
||||||
let mut z = -x;
|
let mut z = -x;
|
||||||
@@ -129,31 +116,26 @@ impl HexCoord
|
|||||||
return Self::from_offset(offset_pos);
|
return Self::from_offset(offset_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_in_bounds(&self, map_height: usize, map_width: usize) -> bool
|
pub fn is_in_bounds(&self, map_height: usize, map_width: usize) -> bool {
|
||||||
{
|
|
||||||
let off = self.to_offset();
|
let off = self.to_offset();
|
||||||
if off.x < 0 || off.y < 0
|
if off.x < 0 || off.y < 0 {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if off.x >= map_width as i32 || off.y >= map_height as i32
|
if off.x >= map_width as i32 || off.y >= map_height as i32 {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_on_chunk_edge(&self) -> bool
|
pub fn is_on_chunk_edge(&self) -> bool {
|
||||||
{
|
|
||||||
let offset = self.to_offset().rem_euclid(IVec2::splat(Chunk::SIZE as i32));
|
let offset = self.to_offset().rem_euclid(IVec2::splat(Chunk::SIZE as i32));
|
||||||
let e = (Chunk::SIZE - 1) as i32;
|
let e = (Chunk::SIZE - 1) as i32;
|
||||||
return offset.x == 0 || offset.y == 0 || offset.x == e || offset.y == e;
|
return offset.x == 0 || offset.y == 0 || offset.x == e || offset.y == e;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_chunk_pos(&self) -> IVec2
|
pub fn to_chunk_pos(&self) -> IVec2 {
|
||||||
{
|
|
||||||
let off = self.to_offset();
|
let off = self.to_offset();
|
||||||
|
|
||||||
return IVec2 {
|
return IVec2 {
|
||||||
@@ -163,8 +145,7 @@ impl HexCoord
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Converts this coordinate to it's chunk local equivalent
|
/// Converts this coordinate to it's chunk local equivalent
|
||||||
pub fn to_chunk(&self) -> HexCoord
|
pub fn to_chunk(&self) -> HexCoord {
|
||||||
{
|
|
||||||
let c_pos = self.to_chunk_pos();
|
let c_pos = self.to_chunk_pos();
|
||||||
let off = self.to_offset();
|
let off = self.to_offset();
|
||||||
return HexCoord::from_offset(
|
return HexCoord::from_offset(
|
||||||
@@ -176,94 +157,76 @@ impl HexCoord
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_world(&self, height: f32) -> Vec3
|
pub fn to_world(&self, height: f32) -> Vec3 {
|
||||||
{
|
|
||||||
return offset_to_world(self.to_offset(), height);
|
return offset_to_world(self.to_offset(), height);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_offset(&self) -> IVec2
|
pub fn to_offset(&self) -> IVec2 {
|
||||||
{
|
|
||||||
return IVec2::new(self.hex.x + (self.hex.y / 2), self.hex.y);
|
return IVec2::new(self.hex.x + (self.hex.y / 2), self.hex.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the current coordiante to an index
|
/// Convert the current coordiante to an index
|
||||||
pub fn to_index(&self, width: usize) -> usize
|
pub fn to_index(&self, width: usize) -> usize {
|
||||||
{
|
|
||||||
return ((self.hex.x + self.hex.y * width as i32) + (self.hex.y / 2)) as usize;
|
return ((self.hex.x + self.hex.y * width as i32) + (self.hex.y / 2)) as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the index of this coord in the chunk array.
|
/// Gets the index of this coord in the chunk array.
|
||||||
///
|
///
|
||||||
/// [`width`] is in number of chunks
|
/// [`width`] is in number of chunks
|
||||||
pub fn to_chunk_index(&self, width: usize) -> usize
|
pub fn to_chunk_index(&self, width: usize) -> usize {
|
||||||
{
|
|
||||||
let pos = self.to_chunk_pos();
|
let pos = self.to_chunk_pos();
|
||||||
return (pos.x + pos.y * width as i32) as usize;
|
return (pos.x + pos.y * width as i32) as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the index of this tile in the chunk
|
/// Gets the index of this tile in the chunk
|
||||||
pub fn to_chunk_local_index(&self) -> usize
|
pub fn to_chunk_local_index(&self) -> usize {
|
||||||
{
|
|
||||||
return self.to_chunk().to_index(Chunk::SIZE);
|
return self.to_chunk().to_index(Chunk::SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn distance(&self, other: &HexCoord) -> i32
|
pub fn distance(&self, other: &HexCoord) -> i32 {
|
||||||
{
|
|
||||||
return (self.hex.x - other.hex.x).abs() + (self.hex.y - other.hex.y).abs() + (self.hex.z - other.hex.z).abs();
|
return (self.hex.x - other.hex.x).abs() + (self.hex.y - other.hex.y).abs() + (self.hex.z - other.hex.z).abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotate_around(&self, center: &HexCoord, angle: i32) -> HexCoord
|
pub fn rotate_around(&self, center: &HexCoord, angle: i32) -> HexCoord {
|
||||||
{
|
if self == center || angle == 0 {
|
||||||
if self == center || angle == 0
|
|
||||||
{
|
|
||||||
return self.clone();
|
return self.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut a = angle % 6;
|
let mut a = angle % 6;
|
||||||
let mut pc = self.hex - center.hex;
|
let mut pc = self.hex - center.hex;
|
||||||
|
|
||||||
if a > 0
|
if a > 0 {
|
||||||
{
|
for _ in 0..a {
|
||||||
for _ in 0..a
|
|
||||||
{
|
|
||||||
pc = Self::slide_right(pc);
|
pc = Self::slide_right(pc);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
a = a.abs();
|
a = a.abs();
|
||||||
for _ in 0..a
|
for _ in 0..a {
|
||||||
{
|
|
||||||
pc = Self::slide_left(pc);
|
pc = Self::slide_left(pc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return HexCoord::from_axial(pc.xy() + center.hex.xy());
|
return HexCoord::from_hex(pc.xy() + center.hex.xy());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slide_left(hex: IVec3) -> IVec3
|
fn slide_left(hex: IVec3) -> IVec3 {
|
||||||
{
|
|
||||||
return (hex * -1).yzx();
|
return (hex * -1).yzx();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slide_right(hex: IVec3) -> IVec3
|
fn slide_right(hex: IVec3) -> IVec3 {
|
||||||
{
|
|
||||||
return (hex * -1).zxy();
|
return (hex * -1).zxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale(&self, dir: usize, radius: usize) -> HexCoord
|
pub fn scale(&self, dir: i32, radius: usize) -> HexCoord {
|
||||||
{
|
let s = Self::DIRECTIONS[(dir % 6) as usize] * radius as i32;
|
||||||
let s = Self::DIRECTIONS[dir % 6] * radius as i32;
|
return Self::from_hex(self.hex.xy() + s.xy());
|
||||||
return Self::from_axial(self.hex.xy() + s.xy());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neighbor(&self, dir: usize) -> HexCoord
|
pub fn get_neighbor(&self, dir: usize) -> HexCoord {
|
||||||
{
|
|
||||||
let d = Self::DIRECTIONS[dir % 6];
|
let d = Self::DIRECTIONS[dir % 6];
|
||||||
return Self::from_axial(self.hex.xy() + d.xy());
|
return Self::from_hex(self.hex.xy() + d.xy());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neighbors(&self) -> [HexCoord; 6]
|
pub fn get_neighbors(&self) -> [HexCoord; 6] {
|
||||||
{
|
|
||||||
return [
|
return [
|
||||||
self.get_neighbor(0),
|
self.get_neighbor(0),
|
||||||
self.get_neighbor(1),
|
self.get_neighbor(1),
|
||||||
@@ -274,23 +237,18 @@ impl HexCoord
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hex_select(&self, radius: usize, include_center: bool) -> Vec<HexCoord>
|
pub fn hex_select(&self, radius: usize, include_center: bool) -> Vec<HexCoord> {
|
||||||
{
|
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
let mut result = Vec::with_capacity(get_tile_count_in_range(radius));
|
let mut result = Vec::with_capacity(get_tile_count_in_range(radius));
|
||||||
|
|
||||||
if include_center
|
if include_center {
|
||||||
{
|
|
||||||
result.push(*self);
|
result.push(*self);
|
||||||
}
|
}
|
||||||
|
|
||||||
for k in 0..(radius + 1)
|
for k in 0..(radius + 1) {
|
||||||
{
|
|
||||||
let mut p = self.scale(4, k);
|
let mut p = self.scale(4, k);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..k {
|
||||||
for _j in 0..k
|
|
||||||
{
|
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
result.push(p);
|
result.push(p);
|
||||||
}
|
}
|
||||||
@@ -300,30 +258,28 @@ impl HexCoord
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hex_select_bounded(&self, radius: usize, include_center: bool, height: usize, width: usize)
|
pub fn hex_select_bounded(
|
||||||
-> Vec<HexCoord>
|
&self,
|
||||||
{
|
radius: usize,
|
||||||
|
include_center: bool,
|
||||||
|
height: usize,
|
||||||
|
width: usize,
|
||||||
|
) -> Vec<HexCoord> {
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
let mut result = Vec::with_capacity(get_tile_count_in_range(radius));
|
let mut result = Vec::with_capacity(get_tile_count_in_range(radius));
|
||||||
|
|
||||||
if include_center
|
if include_center {
|
||||||
{
|
if self.is_in_bounds(height, width) {
|
||||||
if self.is_in_bounds(height, width)
|
|
||||||
{
|
|
||||||
result.push(*self);
|
result.push(*self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for k in 0..(radius + 1)
|
for k in 0..(radius + 1) {
|
||||||
{
|
|
||||||
let mut p = self.scale(4, k);
|
let mut p = self.scale(4, k);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..k {
|
||||||
for _j in 0..k
|
|
||||||
{
|
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
if p.is_in_bounds(height, width)
|
if p.is_in_bounds(height, width) {
|
||||||
{
|
|
||||||
result.push(p);
|
result.push(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,8 +289,7 @@ impl HexCoord
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_ring(&self, radius: usize) -> Vec<HexCoord>
|
pub fn select_ring(&self, radius: usize) -> Vec<HexCoord> {
|
||||||
{
|
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
let mut result = Vec::with_capacity(radius * 6);
|
let mut result = Vec::with_capacity(radius * 6);
|
||||||
|
|
||||||
@@ -345,10 +300,8 @@ impl HexCoord
|
|||||||
// return result;
|
// return result;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..radius {
|
||||||
for _j in 0..radius
|
|
||||||
{
|
|
||||||
result.push(p);
|
result.push(p);
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
pub mod biome_asset;
|
|
||||||
pub mod biome_painter;
|
pub mod biome_painter;
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod generators;
|
pub mod generators;
|
||||||
pub mod heightmap;
|
pub mod heightmap;
|
||||||
|
pub mod hex_utils;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod states;
|
pub mod states;
|
||||||
pub mod tile_manager;
|
pub mod tile_manager;
|
||||||
pub mod tile_mapper;
|
pub mod tile_mapper;
|
||||||
|
pub mod biome_asset;
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ use bevy::{
|
|||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
use hex::prelude::*;
|
use super::chunk::Chunk;
|
||||||
|
|
||||||
#[derive(Clone, Resource)]
|
#[derive(Clone, Resource)]
|
||||||
pub struct BiomeMap
|
pub struct BiomeMap {
|
||||||
{
|
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub size: UVec2,
|
pub size: UVec2,
|
||||||
@@ -18,33 +17,26 @@ pub struct BiomeMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
pub struct BiomeData
|
pub struct BiomeData {
|
||||||
{
|
|
||||||
pub moisture: f32,
|
pub moisture: f32,
|
||||||
pub temperature: f32,
|
pub temperature: f32,
|
||||||
pub continentality: f32,
|
pub continentality: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Vec3> for &BiomeData
|
impl Into<Vec3> for &BiomeData {
|
||||||
{
|
fn into(self) -> Vec3 {
|
||||||
fn into(self) -> Vec3
|
|
||||||
{
|
|
||||||
return Vec3::new(self.moisture, self.temperature, self.continentality);
|
return Vec3::new(self.moisture, self.temperature, self.continentality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Vec3> for BiomeData
|
impl Into<Vec3> for BiomeData {
|
||||||
{
|
fn into(self) -> Vec3 {
|
||||||
fn into(self) -> Vec3
|
|
||||||
{
|
|
||||||
return Vec3::new(self.moisture, self.temperature, self.continentality);
|
return Vec3::new(self.moisture, self.temperature, self.continentality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BiomeMap
|
impl BiomeMap {
|
||||||
{
|
pub fn new(size: UVec2, biome_count: usize) -> Self {
|
||||||
pub fn new(size: UVec2, biome_count: usize) -> Self
|
|
||||||
{
|
|
||||||
let len = size.x as usize * size.y as usize * Chunk::AREA;
|
let len = size.x as usize * size.y as usize * Chunk::AREA;
|
||||||
return BiomeMap {
|
return BiomeMap {
|
||||||
size,
|
size,
|
||||||
@@ -55,17 +47,14 @@ impl BiomeMap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blend(&mut self, count: usize)
|
pub fn blend(&mut self, count: usize) {
|
||||||
{
|
|
||||||
assert!(count != 0, "Count cannot be 0");
|
assert!(count != 0, "Count cannot be 0");
|
||||||
for _ in 0..count
|
for _ in 0..count {
|
||||||
{
|
|
||||||
self.blend_once();
|
self.blend_once();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blend_once(&mut self)
|
fn blend_once(&mut self) {
|
||||||
{
|
|
||||||
let c: Vec<BiomeChunk> = (0..self.chunks.len())
|
let c: Vec<BiomeChunk> = (0..self.chunks.len())
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|i| &self.chunks[i])
|
.map(|i| &self.chunks[i])
|
||||||
@@ -74,8 +63,7 @@ impl BiomeMap
|
|||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|y| {
|
.map(|y| {
|
||||||
let mut new_tiles = Vec::with_capacity(self.width);
|
let mut new_tiles = Vec::with_capacity(self.width);
|
||||||
for x in 0..Chunk::SIZE
|
for x in 0..Chunk::SIZE {
|
||||||
{
|
|
||||||
let tx = x as u32 + chunk.offset.x * Chunk::SIZE as u32;
|
let tx = x as u32 + chunk.offset.x * Chunk::SIZE as u32;
|
||||||
let ty = y as u32 + chunk.offset.y * Chunk::SIZE as u32;
|
let ty = y as u32 + chunk.offset.y * Chunk::SIZE as u32;
|
||||||
let kernel = self.get_kernel(tx as i32, ty as i32);
|
let kernel = self.get_kernel(tx as i32, ty as i32);
|
||||||
@@ -88,8 +76,7 @@ impl BiomeMap
|
|||||||
});
|
});
|
||||||
|
|
||||||
let sum: f32 = r.iter().sum();
|
let sum: f32 = r.iter().sum();
|
||||||
if sum == 0.
|
if sum == 0. {
|
||||||
{
|
|
||||||
new_tiles.push(vec![0.; self.biome_count]);
|
new_tiles.push(vec![0.; self.biome_count]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -109,8 +96,7 @@ impl BiomeMap
|
|||||||
self.chunks = c;
|
self.chunks = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_kernel(&self, x: i32, y: i32) -> [Option<&Vec<f32>>; 9]
|
fn get_kernel(&self, x: i32, y: i32) -> [Option<&Vec<f32>>; 9] {
|
||||||
{
|
|
||||||
return [
|
return [
|
||||||
self.get_biome(x - 1, y - 1),
|
self.get_biome(x - 1, y - 1),
|
||||||
self.get_biome(x, y - 1),
|
self.get_biome(x, y - 1),
|
||||||
@@ -124,14 +110,11 @@ impl BiomeMap
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome(&self, x: i32, y: i32) -> Option<&Vec<f32>>
|
pub fn get_biome(&self, x: i32, y: i32) -> Option<&Vec<f32>> {
|
||||||
{
|
if x < 0 || y < 0 {
|
||||||
if x < 0 || y < 0
|
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if x >= self.width as i32 || y >= self.height as i32
|
if x >= self.width as i32 || y >= self.height as i32 {
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,8 +125,7 @@ impl BiomeMap
|
|||||||
return Some(chunk.get_biome(x as usize - cx * Chunk::SIZE, y as usize - cy * Chunk::SIZE));
|
return Some(chunk.get_biome(x as usize - cx * Chunk::SIZE, y as usize - cy * Chunk::SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_id(&self, x: usize, y: usize) -> usize
|
pub fn get_biome_id(&self, x: usize, y: usize) -> usize {
|
||||||
{
|
|
||||||
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
|
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
|
||||||
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
|
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
|
||||||
|
|
||||||
@@ -152,8 +134,7 @@ impl BiomeMap
|
|||||||
return chunk.get_biome_id(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE));
|
return chunk.get_biome_id(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize
|
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize {
|
||||||
{
|
|
||||||
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
|
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
|
||||||
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
|
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
|
||||||
|
|
||||||
@@ -162,8 +143,7 @@ impl BiomeMap
|
|||||||
return chunk.get_biome_id_dithered(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE), noise, scale);
|
return chunk.get_biome_id_dithered(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE), noise, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData
|
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData {
|
||||||
{
|
|
||||||
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
|
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
|
||||||
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
|
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
|
||||||
|
|
||||||
@@ -174,35 +154,28 @@ impl BiomeMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BiomeChunk
|
pub struct BiomeChunk {
|
||||||
{
|
|
||||||
pub tiles: Vec<Vec<f32>>,
|
pub tiles: Vec<Vec<f32>>,
|
||||||
pub offset: UVec2,
|
pub offset: UVec2,
|
||||||
pub data: [BiomeData; Chunk::AREA],
|
pub data: [BiomeData; Chunk::AREA],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BiomeChunk
|
impl BiomeChunk {
|
||||||
{
|
pub fn get_biome(&self, x: usize, y: usize) -> &Vec<f32> {
|
||||||
pub fn get_biome(&self, x: usize, y: usize) -> &Vec<f32>
|
|
||||||
{
|
|
||||||
return &self.tiles[x + y * Chunk::SIZE];
|
return &self.tiles[x + y * Chunk::SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData
|
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData {
|
||||||
{
|
|
||||||
return &self.data[x + y * Chunk::SIZE];
|
return &self.data[x + y * Chunk::SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_id(&self, x: usize, y: usize) -> usize
|
pub fn get_biome_id(&self, x: usize, y: usize) -> usize {
|
||||||
{
|
|
||||||
let b = self.get_biome(x, y);
|
let b = self.get_biome(x, y);
|
||||||
let mut max = 0.;
|
let mut max = 0.;
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
for i in 0..b.len()
|
for i in 0..b.len() {
|
||||||
{
|
|
||||||
let blend = b[i];
|
let blend = b[i];
|
||||||
if blend > max
|
if blend > max {
|
||||||
{
|
|
||||||
max = blend;
|
max = blend;
|
||||||
idx = i;
|
idx = i;
|
||||||
}
|
}
|
||||||
@@ -210,21 +183,17 @@ impl BiomeChunk
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize
|
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize {
|
||||||
{
|
|
||||||
let mut cur_id = self.get_biome_id(x, y);
|
let mut cur_id = self.get_biome_id(x, y);
|
||||||
let b = self.get_biome(x, y);
|
let b = self.get_biome(x, y);
|
||||||
let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32 - 0.5) / 2.0;
|
let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32 - 0.5)/ 2.0;
|
||||||
let mut max = b[cur_id] + n;
|
let mut max = b[cur_id] + n;
|
||||||
for i in 0..b.len()
|
for i in 0..b.len() {
|
||||||
{
|
|
||||||
let blend = b[i];
|
let blend = b[i];
|
||||||
if blend == 0.
|
if blend == 0. {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if blend > max
|
if blend > max {
|
||||||
{
|
|
||||||
max = blend + n;
|
max = blend + n;
|
||||||
cur_id = i;
|
cur_id = i;
|
||||||
}
|
}
|
||||||
@@ -235,22 +204,18 @@ impl BiomeChunk
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests
|
mod tests {
|
||||||
{
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn biome_blend()
|
fn biome_blend() {
|
||||||
{
|
|
||||||
let mut biome = BiomeMap::new(UVec2::splat(4), 8);
|
let mut biome = BiomeMap::new(UVec2::splat(4), 8);
|
||||||
let w = biome.size.x as usize;
|
let w = biome.size.x as usize;
|
||||||
let h = biome.size.y as usize;
|
let h = biome.size.y as usize;
|
||||||
|
|
||||||
for y in 0..h
|
for y in 0..h {
|
||||||
{
|
for x in 0..w {
|
||||||
for x in 0..w
|
|
||||||
{
|
|
||||||
let mut b = vec![0.; biome.biome_count];
|
let mut b = vec![0.; biome.biome_count];
|
||||||
let idx = (x + y) % biome.biome_count;
|
let idx = (x + y) % biome.biome_count;
|
||||||
b[idx] = 1.;
|
b[idx] = 1.;
|
||||||
@@ -262,8 +227,7 @@ mod tests
|
|||||||
assert!(biome.chunks.iter().all(|f| f.tiles.len() == Chunk::AREA), "Data Lost");
|
assert!(biome.chunks.iter().all(|f| f.tiles.len() == Chunk::AREA), "Data Lost");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_chunk(x: usize, y: usize, biome: Vec<f32>) -> BiomeChunk
|
fn generate_chunk(x: usize, y: usize, biome: Vec<f32>) -> BiomeChunk {
|
||||||
{
|
|
||||||
let chunk = BiomeChunk {
|
let chunk = BiomeChunk {
|
||||||
offset: UVec2::new(x as u32, y as u32),
|
offset: UVec2::new(x as u32, y as u32),
|
||||||
data: [BiomeData::default(); Chunk::AREA],
|
data: [BiomeData::default(); Chunk::AREA],
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::SHORT_DIAGONAL;
|
use crate::hex_utils::SHORT_DIAGONAL;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Chunk
|
pub struct Chunk {
|
||||||
{
|
|
||||||
pub heights: [f32; Chunk::AREA],
|
pub heights: [f32; Chunk::AREA],
|
||||||
pub textures: [[u32; 2]; Chunk::AREA],
|
pub textures: [[u32; 2]; Chunk::AREA],
|
||||||
|
pub overlay_textures: [Option<u32>; Chunk::AREA],
|
||||||
// pub biome_data: [BiomeData; Chunk::AREA],
|
// pub biome_data: [BiomeData; Chunk::AREA],
|
||||||
pub biome_id: [usize; Chunk::AREA],
|
pub biome_id: [usize; Chunk::AREA],
|
||||||
pub chunk_offset: IVec2,
|
pub chunk_offset: IVec2,
|
||||||
@@ -13,13 +13,12 @@ pub struct Chunk
|
|||||||
pub max_level: f32,
|
pub max_level: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Chunk
|
impl Default for Chunk {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
heights: [0.; Chunk::AREA],
|
heights: [0.; Chunk::AREA],
|
||||||
textures: [[0; 2]; Chunk::AREA],
|
textures: [[0; 2]; Chunk::AREA],
|
||||||
|
overlay_textures: [None; Chunk::AREA],
|
||||||
// biome_data: [BiomeData::default(); Chunk::AREA],
|
// biome_data: [BiomeData::default(); Chunk::AREA],
|
||||||
biome_id: [0; Chunk::AREA],
|
biome_id: [0; Chunk::AREA],
|
||||||
chunk_offset: Default::default(),
|
chunk_offset: Default::default(),
|
||||||
@@ -29,20 +28,17 @@ impl Default for Chunk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk
|
impl Chunk {
|
||||||
{
|
|
||||||
pub const SIZE: usize = 64;
|
pub const SIZE: usize = 64;
|
||||||
pub const AREA: usize = Chunk::SIZE * Chunk::SIZE;
|
pub const AREA: usize = Chunk::SIZE * Chunk::SIZE;
|
||||||
pub const WORLD_WIDTH: f32 = Chunk::SIZE as f32 * SHORT_DIAGONAL;
|
pub const WORLD_WIDTH: f32 = Chunk::SIZE as f32 * SHORT_DIAGONAL;
|
||||||
pub const WORLD_HEIGHT: f32 = Chunk::SIZE as f32 * 1.5;
|
pub const WORLD_HEIGHT: f32 = Chunk::SIZE as f32 * 1.5;
|
||||||
pub const WORLD_SIZE: Vec2 = Vec2::new(Chunk::WORLD_WIDTH, Chunk::WORLD_HEIGHT);
|
pub const WORLD_SIZE: Vec2 = Vec2::new(Chunk::WORLD_WIDTH, Chunk::WORLD_HEIGHT);
|
||||||
|
|
||||||
pub fn get_pos_z_edge(&self) -> [f32; Chunk::SIZE]
|
pub fn get_pos_z_edge(&self) -> [f32; Chunk::SIZE] {
|
||||||
{
|
|
||||||
let mut data = [0.; Chunk::SIZE];
|
let mut data = [0.; Chunk::SIZE];
|
||||||
|
|
||||||
for x in 0..Chunk::SIZE
|
for x in 0..Chunk::SIZE {
|
||||||
{
|
|
||||||
let idx = x + (Chunk::SIZE - 1) * Chunk::SIZE;
|
let idx = x + (Chunk::SIZE - 1) * Chunk::SIZE;
|
||||||
data[x] = self.heights[idx];
|
data[x] = self.heights[idx];
|
||||||
}
|
}
|
||||||
@@ -50,24 +46,20 @@ impl Chunk
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neg_z_edge(&self) -> [f32; Chunk::SIZE]
|
pub fn get_neg_z_edge(&self) -> [f32; Chunk::SIZE] {
|
||||||
{
|
|
||||||
let mut data = [0.; Chunk::SIZE];
|
let mut data = [0.; Chunk::SIZE];
|
||||||
|
|
||||||
for x in 0..Chunk::SIZE
|
for x in 0..Chunk::SIZE {
|
||||||
{
|
|
||||||
data[x] = self.heights[x];
|
data[x] = self.heights[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pos_x_edge(&self) -> [f32; Chunk::SIZE]
|
pub fn get_pos_x_edge(&self) -> [f32; Chunk::SIZE] {
|
||||||
{
|
|
||||||
let mut data = [0.; Chunk::SIZE];
|
let mut data = [0.; Chunk::SIZE];
|
||||||
|
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
|
||||||
let idx = (Chunk::SIZE - 1) + z * Chunk::SIZE;
|
let idx = (Chunk::SIZE - 1) + z * Chunk::SIZE;
|
||||||
data[z] = self.heights[idx];
|
data[z] = self.heights[idx];
|
||||||
}
|
}
|
||||||
@@ -75,12 +67,10 @@ impl Chunk
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neg_x_edge(&self) -> [f32; Chunk::SIZE]
|
pub fn get_neg_x_edge(&self) -> [f32; Chunk::SIZE] {
|
||||||
{
|
|
||||||
let mut data = [0.; Chunk::SIZE];
|
let mut data = [0.; Chunk::SIZE];
|
||||||
|
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
|
||||||
let idx = z * Chunk::SIZE;
|
let idx = z * Chunk::SIZE;
|
||||||
data[z] = self.heights[idx];
|
data[z] = self.heights[idx];
|
||||||
}
|
}
|
||||||
@@ -2,12 +2,11 @@ use bevy::prelude::*;
|
|||||||
use bevy_inspector_egui::InspectorOptions;
|
use bevy_inspector_egui::InspectorOptions;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use hex::prelude::*;
|
use super::chunk::Chunk;
|
||||||
|
|
||||||
#[derive(Resource, Reflect, Default, Clone)]
|
#[derive(Resource, Reflect, Default, Clone)]
|
||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
pub struct GenerationConfig
|
pub struct GenerationConfig {
|
||||||
{
|
|
||||||
pub sea_level: f64,
|
pub sea_level: f64,
|
||||||
pub border_size: f32,
|
pub border_size: f32,
|
||||||
pub biome_blend: usize,
|
pub biome_blend: usize,
|
||||||
@@ -18,28 +17,23 @@ pub struct GenerationConfig
|
|||||||
pub size: UVec2,
|
pub size: UVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenerationConfig
|
impl GenerationConfig {
|
||||||
{
|
pub fn get_total_width(&self) -> usize {
|
||||||
pub fn get_total_width(&self) -> usize
|
|
||||||
{
|
|
||||||
return self.size.x as usize * Chunk::SIZE;
|
return self.size.x as usize * Chunk::SIZE;
|
||||||
}
|
}
|
||||||
pub fn get_total_height(&self) -> usize
|
pub fn get_total_height(&self) -> usize {
|
||||||
{
|
|
||||||
return self.size.y as usize * Chunk::SIZE;
|
return self.size.y as usize * Chunk::SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, Reflect, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Default, Reflect, Clone, Debug)]
|
||||||
pub struct NoiseConfig
|
pub struct NoiseConfig {
|
||||||
{
|
|
||||||
pub scale: f64,
|
pub scale: f64,
|
||||||
pub layers: Vec<GeneratorLayer>,
|
pub layers: Vec<GeneratorLayer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Reflect, InspectorOptions, Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Reflect, InspectorOptions, Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
pub struct GeneratorLayer
|
pub struct GeneratorLayer {
|
||||||
{
|
|
||||||
pub strength: f64,
|
pub strength: f64,
|
||||||
pub min_value: f64,
|
pub min_value: f64,
|
||||||
pub base_roughness: f64,
|
pub base_roughness: f64,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hex::prelude::*;
|
|
||||||
|
|
||||||
use super::mesh_chunk::MeshChunkData;
|
use crate::hex_utils::*;
|
||||||
|
|
||||||
|
use super::{chunk::Chunk, mesh_chunk::MeshChunkData};
|
||||||
|
|
||||||
#[derive(Resource, Clone)]
|
#[derive(Resource, Clone)]
|
||||||
pub struct Map
|
pub struct Map {
|
||||||
{
|
|
||||||
pub chunks: Vec<Chunk>,
|
pub chunks: Vec<Chunk>,
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
@@ -15,25 +15,20 @@ pub struct Map
|
|||||||
pub biome_count: usize,
|
pub biome_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map
|
impl Map {
|
||||||
{
|
pub fn get_tile_count(&self) -> usize {
|
||||||
pub fn get_tile_count(&self) -> usize
|
|
||||||
{
|
|
||||||
return self.get_tile_width() * self.get_tile_height();
|
return self.get_tile_width() * self.get_tile_height();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tile_width(&self) -> usize
|
pub fn get_tile_width(&self) -> usize {
|
||||||
{
|
|
||||||
return self.width * Chunk::SIZE;
|
return self.width * Chunk::SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tile_height(&self) -> usize
|
pub fn get_tile_height(&self) -> usize {
|
||||||
{
|
|
||||||
return self.height * Chunk::SIZE;
|
return self.height * Chunk::SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_chunk_mesh_data(&self, chunk_index: usize) -> MeshChunkData
|
pub fn get_chunk_mesh_data(&self, chunk_index: usize) -> MeshChunkData {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _spawn_span = info_span!("Chunk Mesh Data").entered();
|
let _spawn_span = info_span!("Chunk Mesh Data").entered();
|
||||||
let chunk = &self.chunks[chunk_index];
|
let chunk = &self.chunks[chunk_index];
|
||||||
@@ -43,49 +38,41 @@ impl Map
|
|||||||
sealevel: self.sealevel,
|
sealevel: self.sealevel,
|
||||||
heights: chunk.heights.clone(),
|
heights: chunk.heights.clone(),
|
||||||
textures: chunk.textures.clone(),
|
textures: chunk.textures.clone(),
|
||||||
|
overlay_textures: chunk.overlay_textures.clone(),
|
||||||
distance_to_land: self.get_distance_from_land(chunk.chunk_offset, 4),
|
distance_to_land: self.get_distance_from_land(chunk.chunk_offset, 4),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_distance_from_land(&self, chunk_offset: IVec2, range: usize) -> [f32; Chunk::AREA]
|
fn get_distance_from_land(&self, chunk_offset: IVec2, range: usize) -> [f32; Chunk::AREA] {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _spawn_span = info_span!("Chunk Land Dist Data").entered();
|
let _spawn_span = info_span!("Chunk Land Dist Data").entered();
|
||||||
let mut dists = [0.0; Chunk::AREA];
|
let mut dists = [0.0; Chunk::AREA];
|
||||||
let cx = chunk_offset.x as usize * Chunk::SIZE;
|
let cx = chunk_offset.x as usize * Chunk::SIZE;
|
||||||
let cz = chunk_offset.y as usize * Chunk::SIZE;
|
let cz = chunk_offset.y as usize * Chunk::SIZE;
|
||||||
for z in 0..Chunk::SIZE
|
for z in 0..Chunk::SIZE {
|
||||||
{
|
for x in 0..Chunk::SIZE {
|
||||||
for x in 0..Chunk::SIZE
|
let coord = HexCoord::from_grid_pos(x + cx, z + cz);
|
||||||
{
|
|
||||||
let coord = HexCoord::from_offset_pos(x + cx, z + cz);
|
|
||||||
let index = coord.to_chunk_local_index();
|
let index = coord.to_chunk_local_index();
|
||||||
|
|
||||||
if !self.is_in_bounds(&coord)
|
if !self.is_in_bounds(&coord) {
|
||||||
{
|
|
||||||
warn!("Coord is not in bounds!?");
|
warn!("Coord is not in bounds!?");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Current tile is land tile
|
//Current tile is land tile
|
||||||
if self.sample_height(&coord) > self.sealevel
|
if self.sample_height(&coord) > self.sealevel {
|
||||||
{
|
|
||||||
dists[index] = 0.0;
|
dists[index] = 0.0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Find closest land tile
|
//Find closest land tile
|
||||||
if let Some(d) = self.hex_select_first(&coord, range, false, |_t, h, r| {
|
if let Some(d) = self.hex_select_first(&coord, range, false, |_t, h, r| {
|
||||||
if h > self.sealevel
|
if h > self.sealevel {
|
||||||
{
|
|
||||||
return Some(r as f32);
|
return Some(r as f32);
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
})
|
}) {
|
||||||
{
|
|
||||||
dists[index] = d;
|
dists[index] = d;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
dists[index] = range as f32;
|
dists[index] = range as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,17 +80,14 @@ impl Map
|
|||||||
return dists;
|
return dists;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neighbors(&self, pos: &HexCoord) -> [Option<f32>; 6]
|
pub fn get_neighbors(&self, pos: &HexCoord) -> [Option<f32>; 6] {
|
||||||
{
|
|
||||||
let mut results: [Option<f32>; 6] = [None; 6];
|
let mut results: [Option<f32>; 6] = [None; 6];
|
||||||
let w = self.width * Chunk::SIZE;
|
let w = self.width * Chunk::SIZE;
|
||||||
let h = self.height * Chunk::SIZE;
|
let h = self.height * Chunk::SIZE;
|
||||||
let n_tiles = pos.get_neighbors();
|
let n_tiles = pos.get_neighbors();
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let n_tile = n_tiles[i];
|
let n_tile = n_tiles[i];
|
||||||
if !n_tile.is_in_bounds(h, w)
|
if !n_tile.is_in_bounds(h, w) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let c_idx = n_tile.to_chunk_index(self.width);
|
let c_idx = n_tile.to_chunk_index(self.width);
|
||||||
@@ -114,8 +98,7 @@ impl Map
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_height(&self, pos: &HexCoord) -> f32
|
pub fn sample_height(&self, pos: &HexCoord) -> f32 {
|
||||||
{
|
|
||||||
assert!(
|
assert!(
|
||||||
self.is_in_bounds(pos),
|
self.is_in_bounds(pos),
|
||||||
"The provided coordinate is not within the map bounds"
|
"The provided coordinate is not within the map bounds"
|
||||||
@@ -125,8 +108,7 @@ impl Map
|
|||||||
return chunk.heights[pos.to_chunk_local_index()];
|
return chunk.heights[pos.to_chunk_local_index()];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_height_mut(&mut self, pos: &HexCoord) -> &mut f32
|
pub fn sample_height_mut(&mut self, pos: &HexCoord) -> &mut f32 {
|
||||||
{
|
|
||||||
assert!(
|
assert!(
|
||||||
self.is_in_bounds(pos),
|
self.is_in_bounds(pos),
|
||||||
"The provided coordinate is not within the map bounds"
|
"The provided coordinate is not within the map bounds"
|
||||||
@@ -136,13 +118,11 @@ impl Map
|
|||||||
return &mut chunk.heights[pos.to_chunk_local_index()];
|
return &mut chunk.heights[pos.to_chunk_local_index()];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool
|
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool {
|
||||||
{
|
|
||||||
return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE);
|
return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_biome_id(&self, pos: &HexCoord) -> usize
|
pub fn get_biome_id(&self, pos: &HexCoord) -> usize {
|
||||||
{
|
|
||||||
assert!(
|
assert!(
|
||||||
self.is_in_bounds(pos),
|
self.is_in_bounds(pos),
|
||||||
"The provided coordinate is not within the map bounds"
|
"The provided coordinate is not within the map bounds"
|
||||||
@@ -152,15 +132,13 @@ impl Map
|
|||||||
return chunk.biome_id[pos.to_chunk_local_index()];
|
return chunk.biome_id[pos.to_chunk_local_index()];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_center(&self) -> Vec3
|
pub fn get_center(&self) -> Vec3 {
|
||||||
{
|
|
||||||
let w = self.get_world_width();
|
let w = self.get_world_width();
|
||||||
let h = self.get_world_height();
|
let h = self.get_world_height();
|
||||||
return Vec3::new(w / 2., self.sealevel, h / 2.);
|
return Vec3::new(w / 2., self.sealevel, h / 2.);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_center_with_height(&self) -> Vec3
|
pub fn get_center_with_height(&self) -> Vec3 {
|
||||||
{
|
|
||||||
let w = self.get_world_width();
|
let w = self.get_world_width();
|
||||||
let h = self.get_world_height();
|
let h = self.get_world_height();
|
||||||
let mut pos = Vec3::new(w / 2., self.sealevel, h / 2.);
|
let mut pos = Vec3::new(w / 2., self.sealevel, h / 2.);
|
||||||
@@ -168,27 +146,22 @@ impl Map
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_world_width(&self) -> f32
|
pub fn get_world_width(&self) -> f32 {
|
||||||
{
|
|
||||||
return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL;
|
return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL;
|
||||||
}
|
}
|
||||||
pub fn get_world_height(&self) -> f32
|
pub fn get_world_height(&self) -> f32 {
|
||||||
{
|
|
||||||
return (self.height * Chunk::SIZE) as f32 * 1.5;
|
return (self.height * Chunk::SIZE) as f32 * 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_world_size(&self) -> Vec2
|
pub fn get_world_size(&self) -> Vec2 {
|
||||||
{
|
|
||||||
return Vec2::new(self.get_world_width(), self.get_world_height());
|
return Vec2::new(self.get_world_width(), self.get_world_height());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_height(&mut self, pos: &HexCoord, height: f32)
|
pub fn set_height(&mut self, pos: &HexCoord, height: f32) {
|
||||||
{
|
|
||||||
self.chunks[pos.to_chunk_index(self.width)].heights[pos.to_chunk_local_index()] = height;
|
self.chunks[pos.to_chunk_index(self.width)].heights[pos.to_chunk_local_index()] = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec<(HexCoord, f32)>
|
pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec<(HexCoord, f32)> {
|
||||||
{
|
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
|
|
||||||
let tiles = self.hex_select_mut(pos, radius, true, |p, h, r| {
|
let tiles = self.hex_select_mut(pos, radius, true, |p, h, r| {
|
||||||
@@ -209,30 +182,22 @@ impl Map
|
|||||||
{
|
{
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
|
|
||||||
let mut result = if include_center
|
let mut result = if include_center {
|
||||||
{
|
|
||||||
Vec::with_capacity(get_tile_count_in_range(radius) + 1)
|
Vec::with_capacity(get_tile_count_in_range(radius) + 1)
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Vec::with_capacity(get_tile_count_in_range(radius))
|
Vec::with_capacity(get_tile_count_in_range(radius))
|
||||||
};
|
};
|
||||||
if include_center
|
if include_center {
|
||||||
{
|
|
||||||
let h = self.sample_height(¢er);
|
let h = self.sample_height(¢er);
|
||||||
result.push((op)(center, h, 0));
|
result.push((op)(center, h, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for k in 0..(radius + 1)
|
for k in 0..(radius + 1) {
|
||||||
{
|
|
||||||
let mut p = center.scale(4, k);
|
let mut p = center.scale(4, k);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..k {
|
||||||
for _j in 0..k
|
|
||||||
{
|
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
if self.is_in_bounds(&p)
|
if self.is_in_bounds(&p) {
|
||||||
{
|
|
||||||
let h = self.sample_height(&p);
|
let h = self.sample_height(&p);
|
||||||
result.push((op)(&p, h, k));
|
result.push((op)(&p, h, k));
|
||||||
}
|
}
|
||||||
@@ -255,30 +220,23 @@ impl Map
|
|||||||
{
|
{
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
|
|
||||||
if include_center
|
if include_center {
|
||||||
{
|
|
||||||
let h = self.sample_height(¢er);
|
let h = self.sample_height(¢er);
|
||||||
let r = (op)(center, h, 0);
|
let r = (op)(center, h, 0);
|
||||||
if r.is_some()
|
if r.is_some() {
|
||||||
{
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for k in 0..(radius + 1)
|
for k in 0..(radius + 1) {
|
||||||
{
|
|
||||||
let mut p = center.scale(4, k);
|
let mut p = center.scale(4, k);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..k {
|
||||||
for _j in 0..k
|
|
||||||
{
|
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
if self.is_in_bounds(&p)
|
if self.is_in_bounds(&p) {
|
||||||
{
|
|
||||||
let h = self.sample_height(&p);
|
let h = self.sample_height(&p);
|
||||||
let r = (op)(&p, h, k);
|
let r = (op)(&p, h, k);
|
||||||
if r.is_some()
|
if r.is_some() {
|
||||||
{
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,20 +263,15 @@ impl Map
|
|||||||
"Start radius cannot be lower than end radius"
|
"Start radius cannot be lower than end radius"
|
||||||
);
|
);
|
||||||
|
|
||||||
for k in start_radius..(end_radius + 1)
|
for k in start_radius..(end_radius + 1) {
|
||||||
{
|
|
||||||
let mut p = center.scale(4, k);
|
let mut p = center.scale(4, k);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..k {
|
||||||
for _j in 0..k
|
|
||||||
{
|
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
if self.is_in_bounds(&p)
|
if self.is_in_bounds(&p) {
|
||||||
{
|
|
||||||
let h = self.sample_height(&p);
|
let h = self.sample_height(&p);
|
||||||
let r = (op)(&p, h, k);
|
let r = (op)(&p, h, k);
|
||||||
if r.is_some()
|
if r.is_some() {
|
||||||
{
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,30 +294,22 @@ impl Map
|
|||||||
{
|
{
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
|
|
||||||
let mut result = if include_center
|
let mut result = if include_center {
|
||||||
{
|
|
||||||
Vec::with_capacity(get_tile_count_in_range(radius) + 1)
|
Vec::with_capacity(get_tile_count_in_range(radius) + 1)
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Vec::with_capacity(get_tile_count_in_range(radius))
|
Vec::with_capacity(get_tile_count_in_range(radius))
|
||||||
};
|
};
|
||||||
if include_center
|
if include_center {
|
||||||
{
|
|
||||||
let h = self.sample_height_mut(¢er);
|
let h = self.sample_height_mut(¢er);
|
||||||
result.push((op)(center, h, 0));
|
result.push((op)(center, h, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for k in 0..(radius + 1)
|
for k in 0..(radius + 1) {
|
||||||
{
|
|
||||||
let mut p = center.scale(4, k);
|
let mut p = center.scale(4, k);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
for _j in 0..k {
|
||||||
for _j in 0..k
|
|
||||||
{
|
|
||||||
p = p.get_neighbor(i);
|
p = p.get_neighbor(i);
|
||||||
if self.is_in_bounds(&p)
|
if self.is_in_bounds(&p) {
|
||||||
{
|
|
||||||
let h = self.sample_height_mut(&p);
|
let h = self.sample_height_mut(&p);
|
||||||
result.push((op)(&p, h, k));
|
result.push((op)(&p, h, k));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,20 @@ use bevy::{math::VectorSpace, prelude::*};
|
|||||||
use image::ImageBuffer;
|
use image::ImageBuffer;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use hex::prelude::*;
|
use crate::hex_utils::HexCoord;
|
||||||
|
|
||||||
use super::{biome_map::BiomeMap, map::Map};
|
use super::{
|
||||||
|
biome_map::BiomeMap,
|
||||||
|
chunk::Chunk,
|
||||||
|
map::Map,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn render_image(
|
pub fn render_image(
|
||||||
size: UVec2,
|
size: UVec2,
|
||||||
data: &Vec<f32>,
|
data: &Vec<f32>,
|
||||||
color1: LinearRgba,
|
color1: LinearRgba,
|
||||||
color2: LinearRgba,
|
color2: LinearRgba,
|
||||||
) -> ImageBuffer<image::Rgba<u8>, Vec<u8>>
|
) -> ImageBuffer<image::Rgba<u8>, Vec<u8>> {
|
||||||
{
|
|
||||||
let mut image = ImageBuffer::new(size.x * Chunk::SIZE as u32, size.y * Chunk::SIZE as u32);
|
let mut image = ImageBuffer::new(size.x * Chunk::SIZE as u32, size.y * Chunk::SIZE as u32);
|
||||||
update_image(size, data, color1, color2, &mut image);
|
update_image(size, data, color1, color2, &mut image);
|
||||||
|
|
||||||
@@ -27,8 +30,7 @@ pub fn update_image(
|
|||||||
color1: LinearRgba,
|
color1: LinearRgba,
|
||||||
color2: LinearRgba,
|
color2: LinearRgba,
|
||||||
image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>,
|
image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let min = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&0.0);
|
let min = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&0.0);
|
||||||
let max = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&1.0);
|
let max = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&1.0);
|
||||||
|
|
||||||
@@ -38,13 +40,12 @@ pub fn update_image(
|
|||||||
let idx = (y * w + x) as usize;
|
let idx = (y * w + x) as usize;
|
||||||
let v = data[idx];
|
let v = data[idx];
|
||||||
let t = v.remap(min, max, 0.0, 1.0);
|
let t = v.remap(min, max, 0.0, 1.0);
|
||||||
let col = LinearRgba::lerp(color1, color2, t);
|
let col = LinearRgba::lerp(&color1, color2, t);
|
||||||
*pixel = to_pixel(&col);
|
*pixel = to_pixel(&col);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_pixel(col: &LinearRgba) -> image::Rgba<u8>
|
fn to_pixel(col: &LinearRgba) -> image::Rgba<u8> {
|
||||||
{
|
|
||||||
return image::Rgba([
|
return image::Rgba([
|
||||||
(col.red * 255.0) as u8,
|
(col.red * 255.0) as u8,
|
||||||
(col.green * 255.0) as u8,
|
(col.green * 255.0) as u8,
|
||||||
@@ -52,8 +53,7 @@ fn to_pixel(col: &LinearRgba) -> image::Rgba<u8>
|
|||||||
255,
|
255,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer<image::Rgba<u8>, Vec<u8>>
|
pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer<image::Rgba<u8>, Vec<u8>> {
|
||||||
{
|
|
||||||
let mut image = ImageBuffer::new(
|
let mut image = ImageBuffer::new(
|
||||||
map.width as u32 * Chunk::SIZE as u32,
|
map.width as u32 * Chunk::SIZE as u32,
|
||||||
map.height as u32 * Chunk::SIZE as u32,
|
map.height as u32 * Chunk::SIZE as u32,
|
||||||
@@ -61,21 +61,18 @@ pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer<image::Rgba<u8>, Vec<u8
|
|||||||
update_map(map, smooth, &mut image);
|
update_map(map, smooth, &mut image);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>)
|
pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>) {
|
||||||
{
|
|
||||||
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
|
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
|
||||||
let coord = HexCoord::from_offset_pos(x as usize, y as usize);
|
let coord = HexCoord::from_grid_pos(x as usize, y as usize);
|
||||||
let right = coord.get_neighbor(1);
|
let right = coord.get_neighbor(1);
|
||||||
let height = map.sample_height(&coord);
|
let height = map.sample_height(&coord);
|
||||||
|
|
||||||
let mut color = Hsla::hsl(138.0, 1.0, 0.4);
|
let mut color = Hsla::hsl(138.0, 1.0, 0.4);
|
||||||
if height < map.sealevel
|
if height < map.sealevel {
|
||||||
{
|
|
||||||
color.hue = 217.0;
|
color.hue = 217.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if map.is_in_bounds(&right)
|
if map.is_in_bounds(&right) {
|
||||||
{
|
|
||||||
let h2 = map.sample_height(&right);
|
let h2 = map.sample_height(&right);
|
||||||
color = get_height_color_blend(color, height, h2, smooth);
|
color = get_height_color_blend(color, height, h2, smooth);
|
||||||
}
|
}
|
||||||
@@ -84,35 +81,24 @@ pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer<image::Rgba<u8
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_height_color_blend(base_color: Hsla, height: f32, height2: f32, smooth: f32) -> Hsla
|
fn get_height_color_blend(base_color: Hsla, height: f32, height2: f32, smooth: f32) -> Hsla {
|
||||||
{
|
|
||||||
let mut color = base_color;
|
let mut color = base_color;
|
||||||
let mut d = height2 - height;
|
let mut d = height2 - height;
|
||||||
if smooth == 0.0 || d.abs() > smooth
|
if smooth == 0.0 || d.abs() > smooth {
|
||||||
{
|
if d > 0.0 {
|
||||||
if d > 0.0
|
|
||||||
{
|
|
||||||
color.lightness += 0.1;
|
color.lightness += 0.1;
|
||||||
}
|
} else if d < 0.0 {
|
||||||
else if d < 0.0
|
|
||||||
{
|
|
||||||
color.lightness -= 0.1;
|
color.lightness -= 0.1;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
if d.abs() <= smooth {
|
||||||
{
|
|
||||||
if d.abs() <= smooth
|
|
||||||
{
|
|
||||||
d /= smooth;
|
d /= smooth;
|
||||||
if d > 0.0
|
if d > 0.0 {
|
||||||
{
|
|
||||||
let c2: LinearRgba = color.with_lightness(color.lightness + 0.1).into();
|
let c2: LinearRgba = color.with_lightness(color.lightness + 0.1).into();
|
||||||
color = LinearRgba::lerp(color.into(), c2, d).into();
|
color = LinearRgba::lerp(&color.into(), c2, d).into();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
let c2: LinearRgba = color.with_lightness(color.lightness - 0.1).into();
|
let c2: LinearRgba = color.with_lightness(color.lightness - 0.1).into();
|
||||||
color = LinearRgba::lerp(color.into(), c2, d.abs()).into();
|
color = LinearRgba::lerp(&color.into(), c2, d.abs()).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,15 +106,13 @@ fn get_height_color_blend(base_color: Hsla, height: f32, height2: f32, smooth: f
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_biome_noise_map(map: &BiomeMap, multi: Vec3) -> ImageBuffer<image::Rgba<u8>, Vec<u8>>
|
pub fn render_biome_noise_map(map: &BiomeMap, multi: Vec3) -> ImageBuffer<image::Rgba<u8>, Vec<u8>> {
|
||||||
{
|
|
||||||
let mut image = ImageBuffer::new(map.width as u32, map.height as u32);
|
let mut image = ImageBuffer::new(map.width as u32, map.height as u32);
|
||||||
update_biome_noise_map(map, multi, &mut image);
|
update_biome_noise_map(map, multi, &mut image);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_biome_noise_map(map: &BiomeMap, multi: Vec3, image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>)
|
pub fn update_biome_noise_map(map: &BiomeMap, multi: Vec3, image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>) {
|
||||||
{
|
|
||||||
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
|
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
|
||||||
let tile = map.get_biome_data(x as usize, y as usize);
|
let tile = map.get_biome_data(x as usize, y as usize);
|
||||||
|
|
||||||
@@ -141,8 +125,7 @@ pub fn update_biome_noise_map(map: &BiomeMap, multi: Vec3, image: &mut ImageBuff
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_biome_map(map: &Map, biome_map: &BiomeMap) -> ImageBuffer<image::Rgba<u8>, Vec<u8>>
|
pub fn render_biome_map(map: &Map, biome_map: &BiomeMap) -> ImageBuffer<image::Rgba<u8>, Vec<u8>> {
|
||||||
{
|
|
||||||
let mut image = ImageBuffer::new(
|
let mut image = ImageBuffer::new(
|
||||||
map.width as u32 * Chunk::SIZE as u32,
|
map.width as u32 * Chunk::SIZE as u32,
|
||||||
map.height as u32 * Chunk::SIZE as u32,
|
map.height as u32 * Chunk::SIZE as u32,
|
||||||
@@ -151,22 +134,19 @@ pub fn render_biome_map(map: &Map, biome_map: &BiomeMap) -> ImageBuffer<image::R
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_biome_map(map: &Map, biome_map: &BiomeMap, image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>)
|
pub fn update_biome_map(map: &Map, biome_map: &BiomeMap, image: &mut ImageBuffer<image::Rgba<u8>, Vec<u8>>) {
|
||||||
{
|
|
||||||
let map_biome_count = map.biome_count as f32;
|
let map_biome_count = map.biome_count as f32;
|
||||||
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
|
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
|
||||||
let coord = HexCoord::from_offset_pos(x as usize, y as usize);
|
let coord = HexCoord::from_grid_pos(x as usize, y as usize);
|
||||||
let biome_blend = biome_map.get_biome(x as i32, y as i32).unwrap();
|
let biome_blend = biome_map.get_biome(x as i32, y as i32).unwrap();
|
||||||
let right = coord.get_neighbor(1);
|
let right = coord.get_neighbor(1);
|
||||||
let mut color = Oklaba::BLACK;
|
let mut color = Oklaba::BLACK;
|
||||||
for i in 0..biome_blend.len()
|
for i in 0..biome_blend.len() {
|
||||||
{
|
|
||||||
let mut c: Oklaba = Hsla::hsl((i as f32 / map_biome_count) * 360.0, 0.8, 0.7).into();
|
let mut c: Oklaba = Hsla::hsl((i as f32 / map_biome_count) * 360.0, 0.8, 0.7).into();
|
||||||
c *= biome_blend[i];
|
c *= biome_blend[i];
|
||||||
color = Oklaba::add(c, color.into()).into();
|
color = Oklaba::add(c, color.into()).into();
|
||||||
}
|
}
|
||||||
if map.is_in_bounds(&right)
|
if map.is_in_bounds(&right) {
|
||||||
{
|
|
||||||
let h1 = map.sample_height(&coord);
|
let h1 = map.sample_height(&coord);
|
||||||
let h2 = map.sample_height(&right);
|
let h2 = map.sample_height(&right);
|
||||||
color = get_height_color_blend(color.into(), h1, h2, 0.5).into();
|
color = get_height_color_blend(color.into(), h1, h2, 0.5).into();
|
||||||
|
|||||||
@@ -2,28 +2,26 @@ use std::collections::VecDeque;
|
|||||||
|
|
||||||
use bevy::math::IVec2;
|
use bevy::math::IVec2;
|
||||||
|
|
||||||
use hex::prelude::*;
|
use crate::hex_utils::HexCoord;
|
||||||
|
|
||||||
pub struct MeshChunkData
|
use super::chunk::Chunk;
|
||||||
{
|
|
||||||
|
pub struct MeshChunkData {
|
||||||
pub heights: [f32; Chunk::AREA],
|
pub heights: [f32; Chunk::AREA],
|
||||||
pub textures: [[u32; 2]; Chunk::AREA],
|
pub textures: [[u32; 2]; Chunk::AREA],
|
||||||
|
pub overlay_textures: [Option<u32>; Chunk::AREA],
|
||||||
pub min_height: f32,
|
pub min_height: f32,
|
||||||
pub sealevel: f32,
|
pub sealevel: f32,
|
||||||
pub distance_to_land: [f32; Chunk::AREA],
|
pub distance_to_land: [f32; Chunk::AREA],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MeshChunkData
|
impl MeshChunkData {
|
||||||
{
|
pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6] {
|
||||||
pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6]
|
|
||||||
{
|
|
||||||
let mut data = [self.min_height; 6];
|
let mut data = [self.min_height; 6];
|
||||||
let n_tiles = coord.get_neighbors();
|
let n_tiles = coord.get_neighbors();
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let n = n_tiles[i];
|
let n = n_tiles[i];
|
||||||
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE)
|
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
data[i] = self.heights[n.to_index(Chunk::SIZE)];
|
data[i] = self.heights[n.to_index(Chunk::SIZE)];
|
||||||
@@ -32,70 +30,56 @@ impl MeshChunkData
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neighbors_with_water_info(&self, coord: &HexCoord) -> ([(f32, Option<f32>); 6], bool)
|
pub fn get_neighbors_with_water_info(&self, coord: &HexCoord) -> ([(f32, Option<f32>); 6], bool) {
|
||||||
{
|
|
||||||
let mut has_land = false;
|
let mut has_land = false;
|
||||||
let mut data = [(self.min_height, None); 6];
|
let mut data = [(self.min_height, None); 6];
|
||||||
let n_tiles = coord.get_neighbors();
|
let n_tiles = coord.get_neighbors();
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let n = n_tiles[i];
|
let n = n_tiles[i];
|
||||||
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE)
|
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let idx = n.to_index(Chunk::SIZE);
|
let idx = n.to_index(Chunk::SIZE);
|
||||||
data[i] = (self.heights[idx], Some(self.distance_to_land[idx]));
|
data[i] = (self.heights[idx], Some(self.distance_to_land[idx]));
|
||||||
if data[i].0 > self.sealevel
|
if data[i].0 > self.sealevel {
|
||||||
{
|
|
||||||
has_land = true;
|
has_land = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (data, has_land);
|
return (data, has_land);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn caluclate_water_distances(data: &mut Vec<MeshChunkData>, height: usize, width: usize, _range: usize)
|
pub fn caluclate_water_distances(data: &mut Vec<MeshChunkData>, height: usize, width: usize, range: usize) {
|
||||||
{
|
|
||||||
let mut open: VecDeque<(HexCoord, f32, usize)> = VecDeque::new();
|
let mut open: VecDeque<(HexCoord, f32, usize)> = VecDeque::new();
|
||||||
// let mut closed: Vec<(HexCoord, f32)> = Vec::new();
|
let mut closed: Vec<(HexCoord, f32)> = Vec::new();
|
||||||
for z in 0..height
|
for z in 0..height {
|
||||||
{
|
for x in 0..width {
|
||||||
for x in 0..width
|
|
||||||
{
|
|
||||||
let chunk = &mut data[z * height + x];
|
let chunk = &mut data[z * height + x];
|
||||||
chunk.prepare_chunk_open(x * Chunk::SIZE, z * Chunk::SIZE, &mut open);
|
chunk.prepare_chunk_open(x * Chunk::SIZE, z * Chunk::SIZE, &mut open);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_chunk_open(&mut self, offset_x: usize, offset_z: usize, open: &mut VecDeque<(HexCoord, f32, usize)>)
|
fn prepare_chunk_open(&mut self, offset_x: usize, offset_z: usize, open: &mut VecDeque<(HexCoord, f32, usize)>) {
|
||||||
{
|
for z in 0..Chunk::SIZE {
|
||||||
for z in 0..Chunk::SIZE
|
for x in 0..Chunk::SIZE {
|
||||||
{
|
let coord = HexCoord::from_grid_pos(x + offset_x, z + offset_z);
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let coord = HexCoord::from_offset_pos(x + offset_x, z + offset_z);
|
|
||||||
let idx = coord.to_chunk_local_index();
|
let idx = coord.to_chunk_local_index();
|
||||||
let h = self.heights[idx];
|
let h = self.heights[idx];
|
||||||
self.distance_to_land[idx] = if h > self.sealevel { 0.0 } else { 4.0 };
|
self.distance_to_land[idx] = if h > self.sealevel { 0.0 } else { 4.0 };
|
||||||
if h > self.sealevel
|
if h > self.sealevel {
|
||||||
{
|
|
||||||
open.push_back((coord, h, 0));
|
open.push_back((coord, h, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(unused)]
|
|
||||||
fn fill_chunk_borders(
|
fn fill_chunk_borders(
|
||||||
&mut self,
|
&mut self,
|
||||||
chunks: &Vec<MeshChunkData>,
|
chunks: &Vec<MeshChunkData>,
|
||||||
offset: IVec2,
|
offset: IVec2,
|
||||||
open: &mut VecDeque<(HexCoord, f32, usize)>,
|
open: &mut VecDeque<(HexCoord, f32, usize)>,
|
||||||
closed: &mut Vec<(HexCoord, f32)>,
|
closed: &mut Vec<(HexCoord, f32)>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
self.prepare_chunk_open(offset.x as usize * Chunk::SIZE, offset.y as usize * Chunk::SIZE, open);
|
self.prepare_chunk_open(offset.x as usize * Chunk::SIZE, offset.y as usize * Chunk::SIZE, open);
|
||||||
todo!("Fill closed list with bordering tiles")
|
todo!("Fill closed list with bordering tiles")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod biome_map;
|
pub mod chunk;
|
||||||
|
pub mod mesh_chunk;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
|
pub mod biome_map;
|
||||||
pub mod map_utils;
|
pub mod map_utils;
|
||||||
pub mod mesh_chunk;
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub use crate::consts::*;
|
pub use crate::consts::*;
|
||||||
|
pub use crate::map::chunk::*;
|
||||||
pub use crate::map::config::*;
|
pub use crate::map::config::*;
|
||||||
pub use crate::map::map::*;
|
pub use crate::map::map::*;
|
||||||
pub use crate::map::mesh_chunk::*;
|
pub use crate::map::mesh_chunk::*;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use asset_loader::create_asset_loader;
|
use asset_loader::create_asset_loader;
|
||||||
use bevy::{asset::Asset, reflect::TypePath};
|
use bevy::{asset::Asset, ecs::system::Resource, reflect::TypePath};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[derive(Resource, Debug)]
|
#[derive(Resource, Debug)]
|
||||||
pub struct TileManager {
|
pub struct TileManager {
|
||||||
@@ -29,6 +29,7 @@ pub struct TileAsset {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub texture: String,
|
pub texture: String,
|
||||||
pub side_texture_id: u32,
|
pub side_texture_id: u32,
|
||||||
|
pub side_overlay_id: Option<u32>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub side_texture: String,
|
pub side_texture: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.18.0"
|
bevy = "0.14.2"
|
||||||
world_generation = { path = "../../engine/world_generation" }
|
world_generation = { path = "../../engine/world_generation" }
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
bevy_rapier3d = "0.33.0"
|
bevy_rapier3d = "0.27.0"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
asset_loader = { path = "../../engine/asset_loader" }
|
asset_loader = { path = "../../engine/asset_loader" }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.120"
|
||||||
ron = "0.12.0"
|
ron = "0.8.1"
|
||||||
bevy_asset_loader = { version = "0.25.0", features = [
|
bevy_asset_loader = { version = "0.21.0", features = [
|
||||||
"standard_dynamic_assets",
|
"standard_dynamic_assets",
|
||||||
"3d",
|
"3d",
|
||||||
] }
|
] }
|
||||||
hex = { path = "../../engine/hex" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tracing = []
|
tracing = []
|
||||||
|
|||||||
@@ -1,151 +1,22 @@
|
|||||||
use asset_loader::create_asset_loader;
|
use asset_loader::create_asset_loader;
|
||||||
use bevy::{
|
use bevy::prelude::*;
|
||||||
ecs::relationship::RelatedSpawnerCommands,
|
|
||||||
gltf::{GltfMesh, GltfNode},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use shared::{component_defination::ComponentDefination, identifiers::ResourceIdentifier};
|
use shared::identifiers::ResourceIdentifier;
|
||||||
|
|
||||||
use crate::{
|
use crate::footprint::BuildingFootprint;
|
||||||
buildings::{
|
|
||||||
basic_building::BasicBuildingInfo, conduit_building::ResourceConduitInfo,
|
|
||||||
factory_building::FactoryBuildingInfo, resource_gathering::ResourceGatheringBuildingInfo,
|
|
||||||
tech_building::TechBuildingInfo,
|
|
||||||
},
|
|
||||||
footprint::BuildingFootprint,
|
|
||||||
prelude::Building,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Asset, TypePath, Debug, Serialize, Deserialize)]
|
#[derive(Asset, TypePath, Debug, Serialize, Deserialize)]
|
||||||
pub struct BuildingAsset
|
pub struct BuildingAsset {
|
||||||
{
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub footprint: BuildingFootprint,
|
pub footprint: BuildingFootprint,
|
||||||
pub prefab_path: String,
|
pub prefab_path: String,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub prefab: Handle<Gltf>,
|
pub prefab: Handle<Scene>,
|
||||||
pub base_mesh_path: String,
|
|
||||||
|
|
||||||
pub cost: Vec<ResourceIdentifier>,
|
pub cost: Vec<ResourceIdentifier>,
|
||||||
pub consumption: Vec<ResourceIdentifier>,
|
pub consumption: Vec<ResourceIdentifier>,
|
||||||
pub production: Vec<ResourceIdentifier>,
|
pub production: Vec<ResourceIdentifier>,
|
||||||
|
|
||||||
pub health: u32,
|
|
||||||
|
|
||||||
pub building_type: BuildingType,
|
|
||||||
pub components: Option<Vec<ComponentDefination>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuildingAsset
|
|
||||||
{
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn spawn(
|
|
||||||
&self,
|
|
||||||
pos: Vec3,
|
|
||||||
rot: Quat,
|
|
||||||
gltf: &Gltf,
|
|
||||||
commands: &mut Commands,
|
|
||||||
meshes: &Assets<GltfMesh>,
|
|
||||||
nodes: &Assets<GltfNode>,
|
|
||||||
) -> Option<Entity>
|
|
||||||
{
|
|
||||||
let base_node = &gltf.named_nodes[&self.base_mesh_path.clone().into_boxed_str()];
|
|
||||||
if let Some(node) = nodes.get(base_node.id()) {
|
|
||||||
if let Some(mesh_handle) = &node.mesh {
|
|
||||||
if let Some(gltf_mesh) = meshes.get(mesh_handle.id()) {
|
|
||||||
if let Some(primitive) = gltf_mesh.primitives.first() {
|
|
||||||
let mesh = primitive.mesh.clone();
|
|
||||||
let mat = primitive
|
|
||||||
.material
|
|
||||||
.clone()
|
|
||||||
.expect(format!("Mesh '{}' does not have a meterial", primitive.name.as_str()).as_str());
|
|
||||||
let mut entity = commands.spawn((
|
|
||||||
Mesh3d(mesh),
|
|
||||||
MeshMaterial3d(mat),
|
|
||||||
Transform::from_translation(pos).with_rotation(rot),
|
|
||||||
Building,
|
|
||||||
));
|
|
||||||
entity.with_children(|b| {
|
|
||||||
for child in &node.children {
|
|
||||||
let child_node = nodes.get(child.id());
|
|
||||||
if child_node.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
self.process_node(child_node.unwrap(), meshes, nodes, b, &node.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if let Some(component) = self.get_component_def(&format!("/{0}", &node.name)) {
|
|
||||||
component.apply(&mut entity);
|
|
||||||
}
|
|
||||||
return Some(entity.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_node(
|
|
||||||
&self,
|
|
||||||
node: &GltfNode,
|
|
||||||
meshes: &Assets<GltfMesh>,
|
|
||||||
nodes: &Assets<GltfNode>,
|
|
||||||
commands: &mut RelatedSpawnerCommands<ChildOf>,
|
|
||||||
parent: &String,
|
|
||||||
) -> Option<Entity>
|
|
||||||
{
|
|
||||||
let path = format!("{0}/{1}", parent, node.name);
|
|
||||||
if let Some(mesh) = &node.mesh {
|
|
||||||
if let Some(gltf_mesh) = meshes.get(mesh.id()) {
|
|
||||||
if let Some(primitive) = gltf_mesh.primitives.first() {
|
|
||||||
let mesh = primitive.mesh.clone();
|
|
||||||
let mat = primitive
|
|
||||||
.material
|
|
||||||
.clone()
|
|
||||||
.expect(format!("Mesh '{}' does not have a meterial", primitive.name.as_str()).as_str());
|
|
||||||
let mut entity = commands.spawn((Mesh3d(mesh), MeshMaterial3d(mat), node.transform, Building));
|
|
||||||
entity.with_children(|b| {
|
|
||||||
for child in &node.children {
|
|
||||||
let child_node = nodes.get(child.id());
|
|
||||||
if child_node.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
self.process_node(child_node.unwrap(), meshes, nodes, b, &path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if let Some(component) = self.get_component_def(&path) {
|
|
||||||
component.apply(&mut entity);
|
|
||||||
}
|
|
||||||
return Some(entity.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_component_def(&self, path: &String) -> Option<&ComponentDefination>
|
|
||||||
{
|
|
||||||
if let Some(components) = &self.components {
|
|
||||||
for c in components {
|
|
||||||
if c.path.ends_with(path) {
|
|
||||||
return Some(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, TypePath)]
|
|
||||||
pub enum BuildingType
|
|
||||||
{
|
|
||||||
Basic(BasicBuildingInfo),
|
|
||||||
Gathering(ResourceGatheringBuildingInfo),
|
|
||||||
FactoryBuildingInfo(FactoryBuildingInfo),
|
|
||||||
ResourceConduit(ResourceConduitInfo),
|
|
||||||
Tech(TechBuildingInfo),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_asset_loader!(
|
create_asset_loader!(
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
|
|
||||||
use bevy::prelude::Resource;
|
use bevy::prelude::Resource;
|
||||||
use hex::prelude::*;
|
|
||||||
use shared::building::BuildingIdentifier;
|
use shared::building::BuildingIdentifier;
|
||||||
|
use world_generation::hex_utils::HexCoord;
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct BuildQueue
|
pub struct BuildQueue {
|
||||||
{
|
|
||||||
pub queue: Vec<QueueEntry>,
|
pub queue: Vec<QueueEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BuildQueue
|
impl Default for BuildQueue {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
queue: Default::default(),
|
queue: Default::default(),
|
||||||
}
|
}
|
||||||
@@ -19,8 +17,7 @@ impl Default for BuildQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct QueueEntry
|
pub struct QueueEntry {
|
||||||
{
|
|
||||||
pub building: BuildingIdentifier,
|
pub building: BuildingIdentifier,
|
||||||
pub pos: HexCoord,
|
pub pos: HexCoord,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
use bevy::{
|
use std::f32::consts::E;
|
||||||
ecs::world::CommandQueue,
|
|
||||||
gltf::{GltfMesh, GltfNode},
|
use bevy::{ecs::world::CommandQueue, prelude::*, window::PrimaryWindow};
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use bevy_asset_loader::loading_state::{
|
use bevy_asset_loader::loading_state::{
|
||||||
config::{ConfigureLoadingState, LoadingStateConfig},
|
config::{ConfigureLoadingState, LoadingStateConfig},
|
||||||
LoadingStateAppExt,
|
LoadingStateAppExt,
|
||||||
};
|
};
|
||||||
|
use bevy_rapier3d::{parry::transformation::utils::transform, pipeline::QueryFilter, plugin::RapierContext};
|
||||||
use shared::{
|
use shared::{
|
||||||
despawn::Despawn,
|
despawn::Despawn,
|
||||||
events::TileModifiedEvent,
|
events::TileModifiedEvent,
|
||||||
resources::TileUnderCursor,
|
resources::TileUnderCursor,
|
||||||
states::{AssetLoadState, GameplayState},
|
states::{AssetLoadState, GameplayState},
|
||||||
|
tags::MainCamera,
|
||||||
|
};
|
||||||
|
use world_generation::{
|
||||||
|
heightmap, hex_utils::HexCoord, map::map::Map, prelude::GenerationConfig, states::GeneratorState,
|
||||||
};
|
};
|
||||||
use world_generation::{map::map::Map, prelude::GenerationConfig, states::GeneratorState};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{
|
assets::{
|
||||||
@@ -27,10 +29,8 @@ use crate::{
|
|||||||
|
|
||||||
pub struct BuildingPugin;
|
pub struct BuildingPugin;
|
||||||
|
|
||||||
impl Plugin for BuildingPugin
|
impl Plugin for BuildingPugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.insert_resource(BuildQueue::default());
|
app.insert_resource(BuildQueue::default());
|
||||||
app.add_plugins(BuildingAssetPlugin);
|
app.add_plugins(BuildingAssetPlugin);
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ impl Plugin for BuildingPugin
|
|||||||
app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading)));
|
app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading)));
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
hq_placement.run_if(in_state(GameplayState::PlaceHQ).and(in_state(GeneratorState::Idle))),
|
hq_placement.run_if(in_state(GameplayState::PlaceHQ).and_then(in_state(GeneratorState::Idle))),
|
||||||
);
|
);
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
@@ -57,15 +57,12 @@ impl Plugin for BuildingPugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_building_map(mut commands: Commands, cfg: Res<GenerationConfig>)
|
fn prepare_building_map(mut commands: Commands, cfg: Res<GenerationConfig>) {
|
||||||
{
|
|
||||||
commands.insert_resource(BuildingMap::new(cfg.size));
|
commands.insert_resource(BuildingMap::new(cfg.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn regernerate(mut commands: Commands, buildings: Query<Entity, With<Building>>, cfg: Res<GenerationConfig>)
|
fn regernerate(mut commands: Commands, buildings: Query<Entity, With<Building>>, cfg: Res<GenerationConfig>) {
|
||||||
{
|
for e in buildings.iter() {
|
||||||
for e in buildings.iter()
|
|
||||||
{
|
|
||||||
commands.entity(e).despawn();
|
commands.entity(e).despawn();
|
||||||
}
|
}
|
||||||
commands.insert_resource(BuildingMap::new(cfg.size));
|
commands.insert_resource(BuildingMap::new(cfg.size));
|
||||||
@@ -74,8 +71,7 @@ fn regernerate(mut commands: Commands, buildings: Query<Entity, With<Building>>,
|
|||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct IndicatorCube(Handle<Mesh>, Handle<StandardMaterial>);
|
struct IndicatorCube(Handle<Mesh>, Handle<StandardMaterial>);
|
||||||
|
|
||||||
fn init(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>)
|
fn init(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>) {
|
||||||
{
|
|
||||||
let cube = Cuboid::from_size(Vec3::splat(1.));
|
let cube = Cuboid::from_size(Vec3::splat(1.));
|
||||||
let mesh_handle = meshes.add(cube);
|
let mesh_handle = meshes.add(cube);
|
||||||
let mat_handle = materials.add(Color::WHITE);
|
let mat_handle = materials.add(Color::WHITE);
|
||||||
@@ -90,15 +86,13 @@ fn hq_placement(
|
|||||||
indicator: Res<IndicatorCube>,
|
indicator: Res<IndicatorCube>,
|
||||||
mut build_queue: ResMut<BuildQueue>,
|
mut build_queue: ResMut<BuildQueue>,
|
||||||
mut next_state: ResMut<NextState<GameplayState>>,
|
mut next_state: ResMut<NextState<GameplayState>>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
if let Some(contact) = tile_under_cursor.0
|
if let Some(contact) = tile_under_cursor.0 {
|
||||||
{
|
|
||||||
let positions = map.hex_select(&contact.tile, 3, true, |pos, h, _| pos.to_world(h));
|
let positions = map.hex_select(&contact.tile, 3, true, |pos, h, _| pos.to_world(h));
|
||||||
show_indicators(positions, &mut commands, &indicator);
|
show_indicators(positions, &mut commands, &indicator);
|
||||||
|
|
||||||
if mouse.just_pressed(MouseButton::Left)
|
if mouse.just_pressed(MouseButton::Left) {
|
||||||
{
|
|
||||||
build_queue.queue.push(QueueEntry {
|
build_queue.queue.push(QueueEntry {
|
||||||
building: 0.into(),
|
building: 0.into(),
|
||||||
pos: contact.tile,
|
pos: contact.tile,
|
||||||
@@ -109,14 +103,15 @@ fn hq_placement(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_indicators(positions: Vec<Vec3>, commands: &mut Commands, indicator: &IndicatorCube)
|
fn show_indicators(positions: Vec<Vec3>, commands: &mut Commands, indicator: &IndicatorCube) {
|
||||||
{
|
for p in positions {
|
||||||
for p in positions
|
|
||||||
{
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh3d(indicator.0.clone()),
|
PbrBundle {
|
||||||
MeshMaterial3d(indicator.1.clone()),
|
mesh: indicator.0.clone(),
|
||||||
Transform::from_translation(p),
|
material: indicator.1.clone(),
|
||||||
|
transform: Transform::from_translation(p),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
Despawn,
|
Despawn,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -127,65 +122,44 @@ fn process_build_queue(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
db: Res<BuildingDatabase>,
|
db: Res<BuildingDatabase>,
|
||||||
building_assets: Res<Assets<BuildingAsset>>,
|
building_assets: Res<Assets<BuildingAsset>>,
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
|
||||||
gltf_meshes: Res<Assets<GltfMesh>>,
|
|
||||||
gltf_nodes: Res<Assets<GltfNode>>,
|
|
||||||
mut building_map: ResMut<BuildingMap>,
|
mut building_map: ResMut<BuildingMap>,
|
||||||
heightmap: Res<Map>,
|
heightmap: Res<Map>,
|
||||||
)
|
) {
|
||||||
{
|
for item in &queue.queue {
|
||||||
for item in &queue.queue
|
|
||||||
{
|
|
||||||
let handle = &db.buildings[item.building.0];
|
let handle = &db.buildings[item.building.0];
|
||||||
if let Some(building) = building_assets.get(handle.id())
|
if let Some(building) = building_assets.get(handle.id()) {
|
||||||
{
|
|
||||||
let h = heightmap.sample_height(&item.pos);
|
let h = heightmap.sample_height(&item.pos);
|
||||||
println!("Spawning {} at {}", building.name, item.pos);
|
println!("Spawning {} at {}", building.name, item.pos);
|
||||||
if let Some(gltf) = gltf_assets.get(building.prefab.id())
|
let e = commands.spawn((
|
||||||
{
|
SceneBundle {
|
||||||
let e = building.spawn(
|
scene: building.prefab.clone(),
|
||||||
item.pos.to_world(h),
|
transform: Transform::from_translation(item.pos.to_world(h)),
|
||||||
Quat::IDENTITY,
|
..Default::default()
|
||||||
gltf,
|
},
|
||||||
&mut commands,
|
Building,
|
||||||
&gltf_meshes,
|
));
|
||||||
&gltf_nodes,
|
|
||||||
);
|
building_map.add_building(BuildingEntry::new(item.pos, e.id()));
|
||||||
if let Some(b) = e
|
|
||||||
{
|
|
||||||
building_map.add_building(BuildingEntry::new(item.pos, b));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warn!("Failed to spawn building");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queue.queue.clear();
|
queue.queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_building_heights(
|
fn update_building_heights(
|
||||||
mut tile_updates: MessageReader<TileModifiedEvent>,
|
mut tile_updates: EventReader<TileModifiedEvent>,
|
||||||
building_map: Res<BuildingMap>,
|
building_map: Res<BuildingMap>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
)
|
) {
|
||||||
{
|
for event in tile_updates.read() {
|
||||||
for event in tile_updates.read()
|
match event {
|
||||||
{
|
TileModifiedEvent::HeightChanged(coord, new_height) => {
|
||||||
match event
|
if let Some(building) = building_map.get_building(coord) {
|
||||||
{
|
|
||||||
TileModifiedEvent::HeightChanged(coord, new_height) =>
|
|
||||||
{
|
|
||||||
if let Some(building) = building_map.get_building(coord)
|
|
||||||
{
|
|
||||||
let mut queue = CommandQueue::default();
|
let mut queue = CommandQueue::default();
|
||||||
let e = building.entity.clone();
|
let e = building.entity.clone();
|
||||||
let h = *new_height;
|
let h = *new_height;
|
||||||
queue.push(move |world: &mut World| {
|
queue.push(move |world: &mut World| {
|
||||||
let mut emut = world.entity_mut(e);
|
let mut emut = world.entity_mut(e);
|
||||||
if let Some(mut transform) = emut.get_mut::<Transform>()
|
if let Some(mut transform) = emut.get_mut::<Transform>() {
|
||||||
{
|
|
||||||
transform.translation.y = h;
|
transform.translation.y = h;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct BasicBuildingInfo
|
|
||||||
{
|
|
||||||
health: u32,
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct ResourceConduitInfo {
|
|
||||||
pub range: usize,
|
|
||||||
pub connection_range: usize,
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct FactoryBuildingInfo {
|
|
||||||
pub units_to_build: Vec<()>
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
pub mod basic_building;
|
|
||||||
pub mod conduit_building;
|
|
||||||
pub mod factory_building;
|
|
||||||
pub mod resource_gathering;
|
|
||||||
pub mod tech_building;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use shared::identifiers::ResourceIdentifier;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct ResourceGatheringBuildingInfo {
|
|
||||||
pub resources_to_gather: Vec<ResourceIdentifier>,
|
|
||||||
pub gather_range: usize,
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use shared::{building::BuildingIdentifier, StatusEffect};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct TechBuildingInfo {
|
|
||||||
pub effect_range: usize,
|
|
||||||
pub buildings_to_unlock: Vec<BuildingIdentifier>,
|
|
||||||
pub buffs: Vec<StatusEffect>,
|
|
||||||
}
|
|
||||||
@@ -1,26 +1,21 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hex::prelude::*;
|
use world_generation::{hex_utils::HexCoord, prelude::Chunk};
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct BuildingMap
|
pub struct BuildingMap {
|
||||||
{
|
|
||||||
pub chunks: Vec<BuildingChunk>,
|
pub chunks: Vec<BuildingChunk>,
|
||||||
pub size: UVec2,
|
pub size: UVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildingMap
|
impl BuildingMap {
|
||||||
{
|
pub fn new(size: UVec2) -> Self {
|
||||||
pub fn new(size: UVec2) -> Self
|
|
||||||
{
|
|
||||||
let mut db = BuildingMap {
|
let mut db = BuildingMap {
|
||||||
size,
|
size,
|
||||||
chunks: Vec::with_capacity(size.length_squared() as usize),
|
chunks: Vec::with_capacity(size.length_squared() as usize),
|
||||||
};
|
};
|
||||||
|
|
||||||
for y in 0..size.y as i32
|
for y in 0..size.y as i32 {
|
||||||
{
|
for x in 0..size.x as i32 {
|
||||||
for x in 0..size.x as i32
|
|
||||||
{
|
|
||||||
let offset = IVec2::new(x, y);
|
let offset = IVec2::new(x, y);
|
||||||
let index = (x + y * size.x as i32) as usize;
|
let index = (x + y * size.x as i32) as usize;
|
||||||
db.chunks.push(BuildingChunk::new(offset, index));
|
db.chunks.push(BuildingChunk::new(offset, index));
|
||||||
@@ -30,8 +25,7 @@ impl BuildingMap
|
|||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_buildings_in_range(&self, coord: &HexCoord, radius: usize) -> Vec<&BuildingEntry>
|
pub fn get_buildings_in_range(&self, coord: &HexCoord, radius: usize) -> Vec<&BuildingEntry> {
|
||||||
{
|
|
||||||
assert!(radius != 0, "Radius cannot be zero");
|
assert!(radius != 0, "Radius cannot be zero");
|
||||||
|
|
||||||
let w = self.size.x as usize * Chunk::SIZE;
|
let w = self.size.x as usize * Chunk::SIZE;
|
||||||
@@ -40,13 +34,10 @@ impl BuildingMap
|
|||||||
return self.get_buildings_in_coords(coords);
|
return self.get_buildings_in_coords(coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_buildings_in_coords(&self, coords: Vec<HexCoord>) -> Vec<&BuildingEntry>
|
pub fn get_buildings_in_coords(&self, coords: Vec<HexCoord>) -> Vec<&BuildingEntry> {
|
||||||
{
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for coord in &coords
|
for coord in &coords {
|
||||||
{
|
if let Some(buidling) = self.get_building(coord) {
|
||||||
if let Some(buidling) = self.get_building(coord)
|
|
||||||
{
|
|
||||||
result.push(buidling);
|
result.push(buidling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,30 +45,25 @@ impl BuildingMap
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry>
|
pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry> {
|
||||||
{
|
|
||||||
let chunk = &self.chunks[coord.to_chunk_index(self.size.x as usize)];
|
let chunk = &self.chunks[coord.to_chunk_index(self.size.x as usize)];
|
||||||
return chunk.get_building(coord);
|
return chunk.get_building(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_building(&mut self, entry: BuildingEntry)
|
pub fn add_building(&mut self, entry: BuildingEntry) {
|
||||||
{
|
|
||||||
let chunk = &mut self.chunks[entry.coord.to_chunk_index(self.size.x as usize)];
|
let chunk = &mut self.chunks[entry.coord.to_chunk_index(self.size.x as usize)];
|
||||||
chunk.add_building(entry);
|
chunk.add_building(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BuildingChunk
|
pub struct BuildingChunk {
|
||||||
{
|
|
||||||
pub entries: Vec<BuildingEntry>,
|
pub entries: Vec<BuildingEntry>,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub offset: IVec2,
|
pub offset: IVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildingChunk
|
impl BuildingChunk {
|
||||||
{
|
pub fn new(offset: IVec2, index: usize) -> Self {
|
||||||
pub fn new(offset: IVec2, index: usize) -> Self
|
|
||||||
{
|
|
||||||
return BuildingChunk {
|
return BuildingChunk {
|
||||||
entries: Vec::new(),
|
entries: Vec::new(),
|
||||||
index,
|
index,
|
||||||
@@ -85,19 +71,16 @@ impl BuildingChunk
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry>
|
pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry> {
|
||||||
{
|
|
||||||
return self.entries.iter().find(|b| &b.coord == coord);
|
return self.entries.iter().find(|b| &b.coord == coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_building(&mut self, entry: BuildingEntry)
|
pub fn add_building(&mut self, entry: BuildingEntry) {
|
||||||
{
|
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BuildingEntry
|
pub struct BuildingEntry {
|
||||||
{
|
|
||||||
pub coord: HexCoord,
|
pub coord: HexCoord,
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub is_main: bool,
|
pub is_main: bool,
|
||||||
@@ -106,10 +89,8 @@ pub struct BuildingEntry
|
|||||||
pub child_entities: Option<Vec<Entity>>,
|
pub child_entities: Option<Vec<Entity>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildingEntry
|
impl BuildingEntry {
|
||||||
{
|
pub fn new(coord: HexCoord, entity: Entity) -> Self {
|
||||||
pub fn new(coord: HexCoord, entity: Entity) -> Self
|
|
||||||
{
|
|
||||||
return BuildingEntry {
|
return BuildingEntry {
|
||||||
coord,
|
coord,
|
||||||
entity,
|
entity,
|
||||||
@@ -120,8 +101,7 @@ impl BuildingEntry
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_children(coord: HexCoord, entity: Entity, children: Vec<Entity>) -> BuildingEntry
|
pub fn new_with_children(coord: HexCoord, entity: Entity, children: Vec<Entity>) -> BuildingEntry {
|
||||||
{
|
|
||||||
return BuildingEntry {
|
return BuildingEntry {
|
||||||
coord,
|
coord,
|
||||||
entity,
|
entity,
|
||||||
@@ -132,8 +112,7 @@ impl BuildingEntry
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_parent(coord: HexCoord, entity: Entity, main: Entity) -> BuildingEntry
|
pub fn new_with_parent(coord: HexCoord, entity: Entity, main: Entity) -> BuildingEntry {
|
||||||
{
|
|
||||||
return BuildingEntry {
|
return BuildingEntry {
|
||||||
coord,
|
coord,
|
||||||
entity,
|
entity,
|
||||||
|
|||||||
@@ -1,39 +1,32 @@
|
|||||||
use bevy::math::{IVec2, Vec3Swizzles};
|
use bevy::math::{IVec2, Vec3Swizzles};
|
||||||
use hex::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use shared::coords::CoordsCollection;
|
use world_generation::hex_utils::HexCoord;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct BuildingFootprint
|
pub struct BuildingFootprint {
|
||||||
{
|
|
||||||
pub footprint: Vec<IVec2>,
|
pub footprint: Vec<IVec2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildingFootprint
|
impl BuildingFootprint {
|
||||||
{
|
pub fn get_footprint(&self, center: &HexCoord) -> Vec<HexCoord> {
|
||||||
pub fn get_footprint(&self, position: &HexCoord) -> CoordsCollection
|
let c = center.hex.xy();
|
||||||
{
|
return self.footprint.iter().map(|p| HexCoord::from_hex(*p + c)).collect();
|
||||||
CoordsCollection::from_points(self.footprint.clone()).with_translation(position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_neighbors(&self, position: &HexCoord) -> CoordsCollection
|
pub fn get_footprint_rotated(&self, center: &HexCoord, rotation: i32) -> Vec<HexCoord> {
|
||||||
{
|
let c = center.hex.xy();
|
||||||
let n_points: Vec<IVec2> = self
|
return self
|
||||||
.footprint
|
.footprint
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|p| HexCoord::from_axial(*p).get_neighbors())
|
.map(|p| HexCoord::from_hex(*p + c).rotate_around(center, rotation))
|
||||||
.map(|c| c.hex.xy())
|
|
||||||
.filter(|p| !self.footprint.contains(p))
|
|
||||||
.collect();
|
.collect();
|
||||||
let mut out_points: Vec<IVec2> = Vec::with_capacity(n_points.len());
|
|
||||||
for p in n_points
|
|
||||||
{
|
|
||||||
if out_points.contains(&p)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
out_points.push(p);
|
|
||||||
|
pub fn get_neighbors(&self, center: &HexCoord) -> Vec<HexCoord> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
return CoordsCollection::from_points(out_points).with_translation(position);
|
|
||||||
|
pub fn get_neighbors_rotated(&self, center: &HexCoord, rotation: i32) -> Vec<HexCoord> {
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,4 @@ pub mod building_plugin;
|
|||||||
pub mod buildings_map;
|
pub mod buildings_map;
|
||||||
pub mod footprint;
|
pub mod footprint;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
mod buildings;
|
|
||||||
pub use building_plugin::*;
|
pub use building_plugin::*;
|
||||||
|
|||||||
@@ -7,32 +7,28 @@ build = "build.rs"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.18.0", features = ["file_watcher"] }
|
bevy = { version = "0.14.2", features = ["file_watcher"] }
|
||||||
bevy-inspector-egui = "0.36.0"
|
bevy-inspector-egui = "0.25.0"
|
||||||
# iyes_perf_ui = "0.3.0"
|
iyes_perf_ui = "0.3.0"
|
||||||
noise = "0.9.0"
|
noise = "0.8.2"
|
||||||
world_generation = { path = "../../engine/world_generation" }
|
world_generation = { path = "../../engine/world_generation" }
|
||||||
bevy_rapier3d = { version = "0.33.0", features = [
|
bevy_rapier3d = { version = "0.27.0", features = [
|
||||||
"simd-stable",
|
"simd-stable",
|
||||||
"parallel",
|
"parallel",
|
||||||
"debug-render-3d",
|
"debug-render-3d",
|
||||||
] }
|
] }
|
||||||
rayon = "1.11.0"
|
rayon = "1.10.0"
|
||||||
buildings = { path = "../buildings" }
|
buildings = { path = "../buildings" }
|
||||||
units = { path = "../units" }
|
units = { path = "../units" }
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
bevy_asset_loader = { version = "0.25.0", features = [
|
bevy_asset_loader = { version = "0.21.0", features = [
|
||||||
"standard_dynamic_assets",
|
"standard_dynamic_assets",
|
||||||
"3d",
|
"3d",
|
||||||
] }
|
] }
|
||||||
ron = "0.12.0"
|
ron = "0.8.1"
|
||||||
image = "0.25.9"
|
image = "0.25.2"
|
||||||
hex = { path = "../../engine/hex" }
|
|
||||||
|
|
||||||
# bevy_lunex = "0.2.4"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
editor = []
|
|
||||||
tracing = [
|
tracing = [
|
||||||
"bevy/trace_tracy",
|
"bevy/trace_tracy",
|
||||||
"world_generation/tracing",
|
"world_generation/tracing",
|
||||||
|
|||||||
Submodule game/main/assets updated: 9cd8a544d5...67cf2d46ce
@@ -13,10 +13,6 @@ where
|
|||||||
|
|
||||||
for path in fs::read_dir(from).unwrap() {
|
for path in fs::read_dir(from).unwrap() {
|
||||||
let path = path.unwrap().path();
|
let path = path.unwrap().path();
|
||||||
println!("{path:?}");
|
|
||||||
if path.starts_with("assets/raw_assets") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let to = to.clone().join(path.file_name().unwrap());
|
let to = to.clone().join(path.file_name().unwrap());
|
||||||
|
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use bevy::anti_alias::taa::TemporalAntiAliasing;
|
use bevy::core_pipeline::experimental::taa::{TemporalAntiAliasBundle, TemporalAntiAliasPlugin};
|
||||||
use bevy::camera::visibility::RenderLayers;
|
|
||||||
use bevy::core_pipeline::prepass::DepthPrepass;
|
use bevy::core_pipeline::prepass::DepthPrepass;
|
||||||
use bevy::input::mouse::{MouseMotion, MouseScrollUnit, MouseWheel};
|
use bevy::input::mouse::{MouseMotion, MouseScrollUnit, MouseWheel};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::window::{CursorGrabMode, CursorOptions, PrimaryWindow};
|
use bevy::window::{CursorGrabMode, PrimaryWindow};
|
||||||
use hex::prelude::*;
|
|
||||||
use shared::sets::GameplaySet;
|
use shared::sets::GameplaySet;
|
||||||
use shared::tags::MainCamera;
|
use shared::tags::MainCamera;
|
||||||
|
use world_generation::hex_utils::HexCoord;
|
||||||
use world_generation::prelude::Map;
|
use world_generation::prelude::Map;
|
||||||
use world_generation::states::GeneratorState;
|
use world_generation::states::GeneratorState;
|
||||||
|
|
||||||
@@ -14,24 +13,31 @@ use super::components::*;
|
|||||||
|
|
||||||
pub struct PhosCameraPlugin;
|
pub struct PhosCameraPlugin;
|
||||||
|
|
||||||
impl Plugin for PhosCameraPlugin
|
impl Plugin for PhosCameraPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.register_type::<PhosCamera>();
|
app.register_type::<PhosCamera>();
|
||||||
app.register_type::<PhosOrbitCamera>();
|
app.register_type::<PhosOrbitCamera>();
|
||||||
|
|
||||||
app.add_systems(PreStartup, setup);
|
app.add_systems(PreStartup, setup);
|
||||||
|
|
||||||
|
// app.add_systems(Update, rts_camera_system.in_set(GameplaySet));
|
||||||
|
// app.add_systems(PostUpdate, limit_camera_bounds.in_set(GameplaySet));
|
||||||
app.add_systems(Update, orbit_camera_upate.in_set(GameplaySet));
|
app.add_systems(Update, orbit_camera_upate.in_set(GameplaySet));
|
||||||
|
|
||||||
app.add_systems(Update, init_bounds.run_if(in_state(GeneratorState::SpawnMap)));
|
app.add_systems(Update, init_bounds.run_if(in_state(GeneratorState::SpawnMap)));
|
||||||
|
//Free Cam
|
||||||
|
//app.add_systems(Update, (grab_mouse, (update_camera, update_camera_mouse).chain()));
|
||||||
|
|
||||||
|
app.add_plugins(TemporalAntiAliasPlugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_bounds(mut commands: Commands, cam: Single<(&mut Transform, Entity), With<PhosCamera>>, heightmap: Res<Map>)
|
fn init_bounds(
|
||||||
{
|
mut commands: Commands,
|
||||||
let (mut cam_t, cam_entity) = cam.into_inner();
|
mut cam: Query<(&mut Transform, Entity), With<PhosCamera>>,
|
||||||
|
heightmap: Res<Map>,
|
||||||
|
) {
|
||||||
|
let (mut cam_t, cam_entity) = cam.single_mut();
|
||||||
cam_t.translation = heightmap.get_center();
|
cam_t.translation = heightmap.get_center();
|
||||||
commands
|
commands
|
||||||
.entity(cam_entity)
|
.entity(cam_entity)
|
||||||
@@ -42,35 +48,36 @@ fn init_bounds(mut commands: Commands, cam: Single<(&mut Transform, Entity), Wit
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands)
|
fn setup(mut commands: Commands, mut msaa: ResMut<Msaa>) {
|
||||||
{
|
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
Camera3d::default(),
|
Camera3dBundle {
|
||||||
Transform::from_xyz(0., 30., 0.).looking_to(Vec3::NEG_Z, Vec3::Y),
|
transform: Transform::from_xyz(0., 30., 0.).looking_to(Vec3::NEG_Z, Vec3::Y),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
PhosCamera::default(),
|
PhosCamera::default(),
|
||||||
MainCamera,
|
MainCamera,
|
||||||
DepthPrepass,
|
DepthPrepass,
|
||||||
PhosOrbitCamera::default(),
|
PhosOrbitCamera::default(),
|
||||||
TemporalAntiAliasing::default(),
|
|
||||||
))
|
))
|
||||||
.insert(Msaa::Off)
|
.insert(TemporalAntiAliasBundle::default());
|
||||||
.insert(RenderLayers::default());
|
|
||||||
|
*msaa = Msaa::Off;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orbit_camera_upate(
|
fn orbit_camera_upate(
|
||||||
cam_query: Single<(&mut Transform, &PhosCamera, &mut PhosOrbitCamera, &CameraBounds)>,
|
mut cam_query: Query<(&mut Transform, &PhosCamera, &mut PhosOrbitCamera, &CameraBounds)>,
|
||||||
mut wheel: MessageReader<MouseWheel>,
|
mut wheel: EventReader<MouseWheel>,
|
||||||
mut mouse_motion: MessageReader<MouseMotion>,
|
mut mouse_motion: EventReader<MouseMotion>,
|
||||||
mouse: Res<ButtonInput<MouseButton>>,
|
mouse: Res<ButtonInput<MouseButton>>,
|
||||||
mut cursor_options: Single<&mut CursorOptions, With<PrimaryWindow>>,
|
mut window_query: Query<&mut Window, With<PrimaryWindow>>,
|
||||||
key: Res<ButtonInput<KeyCode>>,
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
map: Res<Map>,
|
map: Res<Map>,
|
||||||
#[cfg(debug_assertions)] mut gizmos: Gizmos,
|
#[cfg(debug_assertions)] mut gizmos: Gizmos,
|
||||||
)
|
) {
|
||||||
{
|
let (mut transform, config, mut orbit, bounds) = cam_query.single_mut();
|
||||||
let (mut transform, config, mut orbit, bounds) = cam_query.into_inner();
|
let mut window = window_query.single_mut();
|
||||||
|
|
||||||
let target = orbit.target;
|
let target = orbit.target;
|
||||||
let mut cam_pos = target;
|
let mut cam_pos = target;
|
||||||
@@ -78,70 +85,53 @@ fn orbit_camera_upate(
|
|||||||
//Apply Camera Dist
|
//Apply Camera Dist
|
||||||
cam_pos -= orbit.forward * orbit.distance;
|
cam_pos -= orbit.forward * orbit.distance;
|
||||||
|
|
||||||
if mouse.pressed(MouseButton::Middle)
|
if mouse.pressed(MouseButton::Middle) {
|
||||||
{
|
|
||||||
let mut orbit_move = Vec2::ZERO;
|
let mut orbit_move = Vec2::ZERO;
|
||||||
for e in mouse_motion.read()
|
for e in mouse_motion.read() {
|
||||||
{
|
|
||||||
orbit_move += e.delta;
|
orbit_move += e.delta;
|
||||||
}
|
}
|
||||||
orbit_move *= config.pan_speed * time.delta_secs() * -1.0;
|
orbit_move *= config.pan_speed * time.delta_seconds() * -1.0;
|
||||||
let rot_y = Quat::from_axis_angle(Vec3::Y, orbit_move.x);
|
let rot_y = Quat::from_axis_angle(Vec3::Y, orbit_move.x);
|
||||||
let right = orbit.forward.cross(Vec3::Y).normalize();
|
let right = orbit.forward.cross(Vec3::Y).normalize();
|
||||||
let rot_x = Quat::from_axis_angle(right, orbit_move.y);
|
let rot_x = Quat::from_axis_angle(right, orbit_move.y);
|
||||||
orbit.forward = rot_x * rot_y * orbit.forward;
|
orbit.forward = rot_x * rot_y * orbit.forward;
|
||||||
// orbit.forward.y = orbit.forward.y.clamp(-0.9, 0.0);
|
// orbit.forward.y = orbit.forward.y.clamp(-0.9, 0.0);
|
||||||
orbit.forward = orbit.forward.normalize();
|
orbit.forward = orbit.forward.normalize();
|
||||||
cursor_options.grab_mode = CursorGrabMode::Locked;
|
window.cursor.grab_mode = CursorGrabMode::Locked;
|
||||||
cursor_options.visible = false;
|
window.cursor.visible = false;
|
||||||
|
} else {
|
||||||
|
window.cursor.grab_mode = CursorGrabMode::None;
|
||||||
|
window.cursor.visible = true;
|
||||||
}
|
}
|
||||||
else
|
if key.pressed(KeyCode::KeyE) {
|
||||||
{
|
let rot = Quat::from_axis_angle(Vec3::Y, f32::to_radians(config.speed) * time.delta_seconds());
|
||||||
cursor_options.grab_mode = CursorGrabMode::None;
|
|
||||||
cursor_options.visible = true;
|
|
||||||
}
|
|
||||||
if key.pressed(KeyCode::KeyE)
|
|
||||||
{
|
|
||||||
let rot = Quat::from_axis_angle(Vec3::Y, f32::to_radians(config.speed) * time.delta_secs());
|
|
||||||
orbit.forward = rot * orbit.forward;
|
orbit.forward = rot * orbit.forward;
|
||||||
}
|
} else if key.pressed(KeyCode::KeyQ) {
|
||||||
else if key.pressed(KeyCode::KeyQ)
|
let rot = Quat::from_axis_angle(Vec3::Y, f32::to_radians(-config.speed) * time.delta_seconds());
|
||||||
{
|
|
||||||
let rot = Quat::from_axis_angle(Vec3::Y, f32::to_radians(-config.speed) * time.delta_secs());
|
|
||||||
orbit.forward = rot * orbit.forward;
|
orbit.forward = rot * orbit.forward;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cam_move = Vec3::ZERO;
|
let mut cam_move = Vec3::ZERO;
|
||||||
|
|
||||||
if key.pressed(KeyCode::KeyA)
|
if key.pressed(KeyCode::KeyA) {
|
||||||
{
|
|
||||||
cam_move.x = 1.;
|
cam_move.x = 1.;
|
||||||
}
|
} else if key.pressed(KeyCode::KeyD) {
|
||||||
else if key.pressed(KeyCode::KeyD)
|
|
||||||
{
|
|
||||||
cam_move.x = -1.;
|
cam_move.x = -1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
if key.pressed(KeyCode::KeyW)
|
if key.pressed(KeyCode::KeyW) {
|
||||||
{
|
|
||||||
cam_move.z = 1.;
|
cam_move.z = 1.;
|
||||||
}
|
} else if key.pressed(KeyCode::KeyS) {
|
||||||
else if key.pressed(KeyCode::KeyS)
|
|
||||||
{
|
|
||||||
cam_move.z = -1.;
|
cam_move.z = -1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
let move_speed = if key.pressed(KeyCode::ShiftLeft)
|
let move_speed = if key.pressed(KeyCode::ShiftLeft) {
|
||||||
{
|
|
||||||
config.speed * 2.0
|
config.speed * 2.0
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
config.speed
|
config.speed
|
||||||
};
|
};
|
||||||
|
|
||||||
if cam_move != Vec3::ZERO
|
if cam_move != Vec3::ZERO {
|
||||||
{
|
|
||||||
cam_move = cam_move.normalize();
|
cam_move = cam_move.normalize();
|
||||||
let move_fwd = Vec3::new(orbit.forward.x, 0., orbit.forward.z).normalize();
|
let move_fwd = Vec3::new(orbit.forward.x, 0., orbit.forward.z).normalize();
|
||||||
let move_rot = Quat::from_rotation_arc(Vec3::NEG_Z, move_fwd);
|
let move_rot = Quat::from_rotation_arc(Vec3::NEG_Z, move_fwd);
|
||||||
@@ -150,7 +140,7 @@ fn orbit_camera_upate(
|
|||||||
gizmos.arrow(orbit.target, orbit.target + move_fwd, LinearRgba::WHITE.with_alpha(0.5));
|
gizmos.arrow(orbit.target, orbit.target + move_fwd, LinearRgba::WHITE.with_alpha(0.5));
|
||||||
gizmos.arrow(orbit.target, orbit.target - (move_rot * cam_move), LinearRgba::BLUE);
|
gizmos.arrow(orbit.target, orbit.target - (move_rot * cam_move), LinearRgba::BLUE);
|
||||||
}
|
}
|
||||||
orbit.target -= (move_rot * cam_move) * move_speed * time.delta_secs();
|
orbit.target -= (move_rot * cam_move) * move_speed * time.delta_seconds();
|
||||||
orbit.target.y = sample_ground(orbit.target, &map);
|
orbit.target.y = sample_ground(orbit.target, &map);
|
||||||
|
|
||||||
orbit.target.x = orbit.target.x.clamp(bounds.min.x, bounds.max.x);
|
orbit.target.x = orbit.target.x.clamp(bounds.min.x, bounds.max.x);
|
||||||
@@ -158,16 +148,14 @@ fn orbit_camera_upate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut scroll = 0.0;
|
let mut scroll = 0.0;
|
||||||
for e in wheel.read()
|
for e in wheel.read() {
|
||||||
{
|
match e.unit {
|
||||||
match e.unit
|
|
||||||
{
|
|
||||||
MouseScrollUnit::Line => scroll += e.y * 5.,
|
MouseScrollUnit::Line => scroll += e.y * 5.,
|
||||||
MouseScrollUnit::Pixel => scroll += e.y,
|
MouseScrollUnit::Pixel => scroll += e.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
orbit.distance -= scroll * time.delta_secs() * config.zoom_speed;
|
orbit.distance -= scroll * time.delta_seconds() * config.zoom_speed;
|
||||||
orbit.distance = orbit.distance.clamp(config.min_height, config.max_height);
|
orbit.distance = orbit.distance.clamp(config.min_height, config.max_height);
|
||||||
|
|
||||||
// let ground_below_cam = sample_ground(cam_pos, &map) + config.min_height;
|
// let ground_below_cam = sample_ground(cam_pos, &map) + config.min_height;
|
||||||
@@ -183,31 +171,23 @@ fn orbit_camera_upate(
|
|||||||
transform.look_at(target, Vec3::Y);
|
transform.look_at(target, Vec3::Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_ground(pos: Vec3, heightmap: &Map) -> f32
|
fn sample_ground(pos: Vec3, heightmap: &Map) -> f32 {
|
||||||
{
|
|
||||||
let tile_under = HexCoord::from_world_pos(pos);
|
let tile_under = HexCoord::from_world_pos(pos);
|
||||||
let neighbors = heightmap.get_neighbors(&tile_under);
|
let neighbors = heightmap.get_neighbors(&tile_under);
|
||||||
let mut ground_height = if heightmap.is_in_bounds(&tile_under)
|
let mut ground_height = if heightmap.is_in_bounds(&tile_under) {
|
||||||
{
|
|
||||||
heightmap.sample_height(&tile_under)
|
heightmap.sample_height(&tile_under)
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
heightmap.sealevel
|
heightmap.sealevel
|
||||||
};
|
};
|
||||||
|
|
||||||
for n in neighbors
|
for n in neighbors {
|
||||||
{
|
if let Some(h) = n {
|
||||||
if let Some(h) = n
|
if h > ground_height {
|
||||||
{
|
|
||||||
if h > ground_height
|
|
||||||
{
|
|
||||||
ground_height = h;
|
ground_height = h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ground_height < heightmap.sealevel
|
if ground_height < heightmap.sealevel {
|
||||||
{
|
|
||||||
ground_height = heightmap.sealevel;
|
ground_height = heightmap.sealevel;
|
||||||
}
|
}
|
||||||
return ground_height;
|
return ground_height;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::{math::Direction3d, prelude::*};
|
||||||
use hex::prelude::*;
|
|
||||||
use rayon::str;
|
use rayon::str;
|
||||||
|
use world_generation::{hex_utils::SHORT_DIAGONAL, prelude::Chunk};
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct PhosCamera
|
pub struct PhosCamera {
|
||||||
{
|
|
||||||
pub min_height: f32,
|
pub min_height: f32,
|
||||||
pub max_height: f32,
|
pub max_height: f32,
|
||||||
pub speed: f32,
|
pub speed: f32,
|
||||||
@@ -15,33 +14,28 @@ pub struct PhosCamera
|
|||||||
pub max_angle: f32,
|
pub max_angle: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PhosCamera
|
impl Default for PhosCamera {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
min_height: 10.,
|
min_height: 10.,
|
||||||
max_height: 420.,
|
max_height: 420.,
|
||||||
speed: 100.,
|
speed: 100.,
|
||||||
pan_speed: Vec2::new(0.8, 0.5),
|
pan_speed: Vec2::new(0.8, 0.5),
|
||||||
zoom_speed: 20.,
|
zoom_speed: 200.,
|
||||||
min_angle: (20. as f32).to_radians(),
|
min_angle: (20. as f32).to_radians(),
|
||||||
max_angle: 1.,
|
max_angle: 1.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
pub struct PhosOrbitCamera
|
pub struct PhosOrbitCamera {
|
||||||
{
|
|
||||||
pub target: Vec3,
|
pub target: Vec3,
|
||||||
pub distance: f32,
|
pub distance: f32,
|
||||||
pub forward: Vec3,
|
pub forward: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PhosOrbitCamera
|
impl Default for PhosOrbitCamera {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
target: Default::default(),
|
target: Default::default(),
|
||||||
distance: 40.0,
|
distance: 40.0,
|
||||||
@@ -51,16 +45,13 @@ impl Default for PhosOrbitCamera
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
#[derive(Component, Default)]
|
||||||
pub struct CameraBounds
|
pub struct CameraBounds {
|
||||||
{
|
|
||||||
pub min: Vec2,
|
pub min: Vec2,
|
||||||
pub max: Vec2,
|
pub max: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CameraBounds
|
impl CameraBounds {
|
||||||
{
|
pub fn from_size(world_size: Vec2) -> Self {
|
||||||
pub fn from_size(world_size: Vec2) -> Self
|
|
||||||
{
|
|
||||||
let padding = Chunk::WORLD_SIZE;
|
let padding = Chunk::WORLD_SIZE;
|
||||||
return Self {
|
return Self {
|
||||||
min: Vec2::ZERO - padding,
|
min: Vec2::ZERO - padding,
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
use bevy::image::{ImageAddressMode, ImageFilterMode, ImageSamplerDescriptor};
|
|
||||||
use bevy::pbr::wireframe::WireframePlugin;
|
use bevy::pbr::wireframe::WireframePlugin;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::texture::{ImageAddressMode, ImageFilterMode, ImageSamplerDescriptor};
|
||||||
use bevy::window::PresentMode;
|
use bevy::window::PresentMode;
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
use bevy::window::WindowResolution;
|
|
||||||
use bevy_inspector_egui::bevy_egui::EguiPlugin;
|
|
||||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||||
use phos::PhosGamePlugin;
|
use phos::PhosGamePlugin;
|
||||||
|
|
||||||
@@ -13,11 +10,10 @@ mod map_rendering;
|
|||||||
mod phos;
|
mod phos;
|
||||||
mod prelude;
|
mod prelude;
|
||||||
mod shader_extensions;
|
mod shader_extensions;
|
||||||
mod ui;
|
|
||||||
mod utlis;
|
mod utlis;
|
||||||
|
mod ui;
|
||||||
|
|
||||||
fn main()
|
fn main() {
|
||||||
{
|
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins
|
DefaultPlugins
|
||||||
@@ -26,7 +22,7 @@ fn main()
|
|||||||
title: "Phos".into(),
|
title: "Phos".into(),
|
||||||
name: Some("phos".into()),
|
name: Some("phos".into()),
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
resolution: WindowResolution::new(1920, 1080),
|
resolution: (1920., 1080.).into(),
|
||||||
present_mode: PresentMode::AutoNoVsync,
|
present_mode: PresentMode::AutoNoVsync,
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
mode: bevy::window::WindowMode::BorderlessFullscreen,
|
mode: bevy::window::WindowMode::BorderlessFullscreen,
|
||||||
@@ -47,9 +43,8 @@ fn main()
|
|||||||
watch_for_changes_override: Some(true),
|
watch_for_changes_override: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
EguiPlugin::default(),
|
|
||||||
WorldInspectorPlugin::new(),
|
WorldInspectorPlugin::new(),
|
||||||
WireframePlugin::default(),
|
WireframePlugin,
|
||||||
PhosGamePlugin,
|
PhosGamePlugin,
|
||||||
))
|
))
|
||||||
.run();
|
.run();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use bevy::ecs::world::CommandQueue;
|
use bevy::ecs::world::CommandQueue;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::tasks::*;
|
use bevy::tasks::*;
|
||||||
|
use bevy::utils::futures;
|
||||||
use bevy_rapier3d::geometry::Collider;
|
use bevy_rapier3d::geometry::Collider;
|
||||||
use bevy_rapier3d::geometry::TriMeshFlags;
|
use bevy_rapier3d::geometry::TriMeshFlags;
|
||||||
use shared::events::ChunkModifiedEvent;
|
use shared::events::ChunkModifiedEvent;
|
||||||
@@ -9,7 +10,6 @@ use world_generation::prelude::Map;
|
|||||||
use world_generation::states::GeneratorState;
|
use world_generation::states::GeneratorState;
|
||||||
|
|
||||||
use crate::prelude::RebuildChunk;
|
use crate::prelude::RebuildChunk;
|
||||||
use crate::prelude::WaterMesh;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::{PhosChunk, PhosChunkRegistry},
|
prelude::{PhosChunk, PhosChunkRegistry},
|
||||||
utlis::chunk_utils::prepare_chunk_mesh,
|
utlis::chunk_utils::prepare_chunk_mesh,
|
||||||
@@ -17,13 +17,11 @@ use crate::{
|
|||||||
|
|
||||||
pub struct ChunkRebuildPlugin;
|
pub struct ChunkRebuildPlugin;
|
||||||
|
|
||||||
impl Plugin for ChunkRebuildPlugin
|
impl Plugin for ChunkRebuildPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.init_resource::<PhosChunkRegistry>();
|
app.init_resource::<PhosChunkRegistry>();
|
||||||
app.add_message::<ChunkModifiedEvent>();
|
app.add_event::<ChunkModifiedEvent>();
|
||||||
app.add_message::<TileModifiedEvent>();
|
app.add_event::<TileModifiedEvent>();
|
||||||
app.add_systems(PreUpdate, chunk_rebuilder.run_if(in_state(GeneratorState::Idle)));
|
app.add_systems(PreUpdate, chunk_rebuilder.run_if(in_state(GeneratorState::Idle)));
|
||||||
app.add_systems(PostUpdate, collider_task_resolver);
|
app.add_systems(PostUpdate, collider_task_resolver);
|
||||||
}
|
}
|
||||||
@@ -33,13 +31,11 @@ fn chunk_rebuilder(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
chunk_query: Query<(Entity, &PhosChunk), (With<RebuildChunk>, Without<ChunkRebuildTask>)>,
|
chunk_query: Query<(Entity, &PhosChunk), (With<RebuildChunk>, Without<ChunkRebuildTask>)>,
|
||||||
heightmap: Res<Map>,
|
heightmap: Res<Map>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let pool = AsyncComputeTaskPool::get();
|
let pool = AsyncComputeTaskPool::get();
|
||||||
let map_size = UVec2::new(heightmap.width as u32, heightmap.height as u32);
|
let map_size = UVec2::new(heightmap.width as u32, heightmap.height as u32);
|
||||||
|
|
||||||
for (chunk_entity, idx) in &chunk_query
|
for (chunk_entity, idx) in &chunk_query {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _spawn_span = info_span!("Rebuild Chunk").entered();
|
let _spawn_span = info_span!("Rebuild Chunk").entered();
|
||||||
info!("Rebuilding Chunk");
|
info!("Rebuilding Chunk");
|
||||||
@@ -59,15 +55,14 @@ fn chunk_rebuilder(
|
|||||||
collider_data.0,
|
collider_data.0,
|
||||||
collider_data.1,
|
collider_data.1,
|
||||||
TriMeshFlags::DELETE_DUPLICATE_TRIANGLES,
|
TriMeshFlags::DELETE_DUPLICATE_TRIANGLES,
|
||||||
)
|
);
|
||||||
.expect("Failed to build chunk mesh");
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
drop(trimesh_span);
|
drop(trimesh_span);
|
||||||
queue.push(move |world: &mut World| {
|
queue.push(move |world: &mut World| {
|
||||||
world.entity_mut(chunk_entity).insert(c).remove::<ChunkRebuildTask>();
|
world.entity_mut(chunk_entity).insert(c).remove::<ChunkRebuildTask>();
|
||||||
});
|
});
|
||||||
|
|
||||||
return (queue, mesh, water_mesh);
|
return (queue, mesh);
|
||||||
});
|
});
|
||||||
|
|
||||||
commands
|
commands
|
||||||
@@ -78,28 +73,19 @@ fn chunk_rebuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn collider_task_resolver(
|
fn collider_task_resolver(
|
||||||
mut chunks: Query<(&mut ChunkRebuildTask, &Mesh3d, &WaterMesh), With<PhosChunk>>,
|
mut chunks: Query<(&mut ChunkRebuildTask, &Handle<Mesh>), With<PhosChunk>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
)
|
) {
|
||||||
{
|
for (mut task, mesh_handle) in &mut chunks {
|
||||||
for (mut task, mesh_handle, water_mesh_handle) in &mut chunks
|
if let Some((mut c, mesh)) = futures::check_ready(&mut task.task) {
|
||||||
{
|
|
||||||
if let Some((mut c, chunk_mesh, water_mesh)) = futures::check_ready(&mut task.task)
|
|
||||||
{
|
|
||||||
commands.append(&mut c);
|
commands.append(&mut c);
|
||||||
meshes
|
meshes.insert(mesh_handle, mesh);
|
||||||
.insert(mesh_handle.id(), chunk_mesh)
|
|
||||||
.expect("Failed to update chunk mesh");
|
|
||||||
meshes
|
|
||||||
.insert(water_mesh_handle.0, water_mesh)
|
|
||||||
.expect("Failed to update chink water mesh");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct ChunkRebuildTask
|
struct ChunkRebuildTask {
|
||||||
{
|
pub task: Task<(CommandQueue, Mesh)>,
|
||||||
pub task: Task<(CommandQueue, Mesh, Mesh)>,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use bevy::log::*;
|
use bevy::log::*;
|
||||||
use bevy::{light::NotShadowCaster, pbr::ExtendedMaterial, prelude::*};
|
use bevy::{
|
||||||
|
pbr::{ExtendedMaterial, NotShadowCaster},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use bevy_asset_loader::prelude::*;
|
use bevy_asset_loader::prelude::*;
|
||||||
|
|
||||||
use bevy_inspector_egui::quick::ResourceInspectorPlugin;
|
use bevy_inspector_egui::quick::ResourceInspectorPlugin;
|
||||||
use hex::prelude::*;
|
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use shared::states::{AssetLoadState, GameplayState, MenuState};
|
use shared::states::{AssetLoadState, GameplayState, MenuState};
|
||||||
|
|
||||||
use world_generation::{
|
use world_generation::{
|
||||||
biome_asset::{BiomeAsset, BiomeAssetPlugin},
|
biome_asset::{BiomeAsset, BiomeAssetPlugin},
|
||||||
biome_painter::*,
|
biome_painter::*,
|
||||||
heightmap::generate_heightmap,
|
heightmap::generate_heightmap,
|
||||||
|
hex_utils::{offset_to_index, SHORT_DIAGONAL},
|
||||||
map::biome_map::BiomeMap,
|
map::biome_map::BiomeMap,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
tile_manager::*,
|
tile_manager::*,
|
||||||
@@ -19,7 +21,7 @@ use world_generation::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::{PhosAssets, PhosChunk, PhosChunkRegistry, WaterMesh},
|
prelude::{PhosAssets, PhosChunk, PhosChunkRegistry},
|
||||||
shader_extensions::{
|
shader_extensions::{
|
||||||
chunk_material::ChunkMaterial,
|
chunk_material::ChunkMaterial,
|
||||||
water_material::{WaterMaterial, WaterSettings},
|
water_material::{WaterMaterial, WaterSettings},
|
||||||
@@ -31,10 +33,8 @@ use super::{chunk_rebuild::ChunkRebuildPlugin, render_distance_system::RenderDis
|
|||||||
|
|
||||||
pub struct MapInitPlugin;
|
pub struct MapInitPlugin;
|
||||||
|
|
||||||
impl Plugin for MapInitPlugin
|
impl Plugin for MapInitPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.insert_state(GeneratorState::Startup);
|
app.insert_state(GeneratorState::Startup);
|
||||||
app.insert_state(AssetLoadState::Loading);
|
app.insert_state(AssetLoadState::Loading);
|
||||||
|
|
||||||
@@ -50,7 +50,10 @@ impl Plugin for MapInitPlugin
|
|||||||
ChunkRebuildPlugin,
|
ChunkRebuildPlugin,
|
||||||
// TerraFormingTestPlugin,
|
// TerraFormingTestPlugin,
|
||||||
MaterialPlugin::<ExtendedMaterial<StandardMaterial, ChunkMaterial>>::default(),
|
MaterialPlugin::<ExtendedMaterial<StandardMaterial, ChunkMaterial>>::default(),
|
||||||
MaterialPlugin::<ExtendedMaterial<StandardMaterial, WaterMaterial>>::default(),
|
MaterialPlugin::<ExtendedMaterial<StandardMaterial, WaterMaterial>> {
|
||||||
|
prepass_enabled: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
app.configure_loading_state(
|
app.configure_loading_state(
|
||||||
@@ -74,7 +77,7 @@ impl Plugin for MapInitPlugin
|
|||||||
app.add_systems(Update, despawn_map.run_if(in_state(GeneratorState::Regenerate)));
|
app.add_systems(Update, despawn_map.run_if(in_state(GeneratorState::Regenerate)));
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
spawn_map.run_if(in_state(AssetLoadState::LoadComplete).and(in_state(GeneratorState::SpawnMap))),
|
spawn_map.run_if(in_state(AssetLoadState::LoadComplete).and_then(in_state(GeneratorState::SpawnMap))),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.insert_resource(TileManager::default());
|
app.insert_resource(TileManager::default());
|
||||||
@@ -84,8 +87,7 @@ impl Plugin for MapInitPlugin
|
|||||||
fn setup_materials(
|
fn setup_materials(
|
||||||
mut phos_assets: ResMut<PhosAssets>,
|
mut phos_assets: ResMut<PhosAssets>,
|
||||||
mut water_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, WaterMaterial>>>,
|
mut water_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, WaterMaterial>>>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let water_material = water_materials.add(ExtendedMaterial {
|
let water_material = water_materials.add(ExtendedMaterial {
|
||||||
base: StandardMaterial {
|
base: StandardMaterial {
|
||||||
base_color: Color::srgb(0., 0.878, 1.),
|
base_color: Color::srgb(0., 0.878, 1.),
|
||||||
@@ -111,8 +113,7 @@ fn finalize_biome_painter(
|
|||||||
mut next_generator_state: ResMut<NextState<GeneratorState>>,
|
mut next_generator_state: ResMut<NextState<GeneratorState>>,
|
||||||
biome_painter: Res<BiomePainterAsset>,
|
biome_painter: Res<BiomePainterAsset>,
|
||||||
biomes: Res<Assets<BiomeAsset>>,
|
biomes: Res<Assets<BiomeAsset>>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let painter = biome_painter.build(&biomes);
|
let painter = biome_painter.build(&biomes);
|
||||||
commands.insert_resource(painter);
|
commands.insert_resource(painter);
|
||||||
next_generator_state.set(GeneratorState::GenerateHeightmap);
|
next_generator_state.set(GeneratorState::GenerateHeightmap);
|
||||||
@@ -123,14 +124,11 @@ fn finalize_texture(
|
|||||||
mut images: ResMut<Assets<Image>>,
|
mut images: ResMut<Assets<Image>>,
|
||||||
mut chunk_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, ChunkMaterial>>>,
|
mut chunk_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, ChunkMaterial>>>,
|
||||||
mut next_load_state: ResMut<NextState<AssetLoadState>>,
|
mut next_load_state: ResMut<NextState<AssetLoadState>>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let image = images.get_mut(atlas.handle.id()).unwrap();
|
let image = images.get_mut(atlas.handle.id()).unwrap();
|
||||||
|
|
||||||
let array_layers = image.height() / image.width();
|
let array_layers = image.height() / image.width();
|
||||||
image
|
image.reinterpret_stacked_2d_as_array(array_layers);
|
||||||
.reinterpret_stacked_2d_as_array(array_layers)
|
|
||||||
.expect("Failed to reinterpret as array");
|
|
||||||
|
|
||||||
let chunk_material = chunk_materials.add(ExtendedMaterial {
|
let chunk_material = chunk_materials.add(ExtendedMaterial {
|
||||||
base: StandardMaterial::default(),
|
base: StandardMaterial::default(),
|
||||||
@@ -147,8 +145,7 @@ fn create_heightmap(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut next_state: ResMut<NextState<GeneratorState>>,
|
mut next_state: ResMut<NextState<GeneratorState>>,
|
||||||
biome_painter: Res<BiomePainter>,
|
biome_painter: Res<BiomePainter>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let config = GenerationConfig {
|
let config = GenerationConfig {
|
||||||
biome_blend: 32,
|
biome_blend: 32,
|
||||||
biome_dither: 10.,
|
biome_dither: 10.,
|
||||||
@@ -219,11 +216,9 @@ fn spawn_map(
|
|||||||
mut game_state: ResMut<NextState<MenuState>>,
|
mut game_state: ResMut<NextState<MenuState>>,
|
||||||
mut gameplay_state: ResMut<NextState<GameplayState>>,
|
mut gameplay_state: ResMut<NextState<GameplayState>>,
|
||||||
biome_painter: Res<BiomePainter>,
|
biome_painter: Res<BiomePainter>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
paint_map(&mut heightmap, &biome_painter, &tile_assets, &tile_mappers);
|
paint_map(&mut heightmap, &biome_painter, &tile_assets, &tile_mappers);
|
||||||
|
|
||||||
//Prepare Mesh Data
|
|
||||||
let map_size = UVec2::new(heightmap.width as u32, heightmap.height as u32);
|
let map_size = UVec2::new(heightmap.width as u32, heightmap.height as u32);
|
||||||
let chunk_meshes: Vec<_> = heightmap
|
let chunk_meshes: Vec<_> = heightmap
|
||||||
.chunks
|
.chunks
|
||||||
@@ -241,8 +236,6 @@ fn spawn_map(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut registry = PhosChunkRegistry::new(chunk_meshes.len());
|
let mut registry = PhosChunkRegistry::new(chunk_meshes.len());
|
||||||
|
|
||||||
//Spawn Chunks
|
|
||||||
{
|
{
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _spawn_span = info_span!("Spawn Chunks").entered();
|
let _spawn_span = info_span!("Spawn Chunks").entered();
|
||||||
@@ -251,26 +244,29 @@ fn spawn_map(
|
|||||||
0.,
|
0.,
|
||||||
(Chunk::SIZE / 2) as f32 * 1.5,
|
(Chunk::SIZE / 2) as f32 * 1.5,
|
||||||
);
|
);
|
||||||
for (chunk_mesh, water_mesh, collider, pos, index) in chunk_meshes
|
for (chunk_mesh, water_mesh, collider, pos, index) in chunk_meshes {
|
||||||
{
|
|
||||||
// let mesh_handle = meshes.a
|
// let mesh_handle = meshes.a
|
||||||
let water_mesh_handle = meshes.add(water_mesh);
|
|
||||||
let chunk = commands
|
let chunk = commands
|
||||||
.spawn((
|
.spawn((
|
||||||
Mesh3d(meshes.add(chunk_mesh)),
|
MaterialMeshBundle {
|
||||||
MeshMaterial3d(atlas.chunk_material_handle.clone()),
|
mesh: meshes.add(chunk_mesh),
|
||||||
Transform::from_translation(pos),
|
material: atlas.chunk_material_handle.clone(),
|
||||||
|
transform: Transform::from_translation(pos),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
PhosChunk::new(index),
|
PhosChunk::new(index),
|
||||||
WaterMesh(water_mesh_handle.id()),
|
|
||||||
RenderDistanceVisibility::default().with_offset(visibility_offset),
|
RenderDistanceVisibility::default().with_offset(visibility_offset),
|
||||||
collider,
|
collider,
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
let water = commands
|
let water = commands
|
||||||
.spawn((
|
.spawn((
|
||||||
Mesh3d(water_mesh_handle),
|
MaterialMeshBundle {
|
||||||
MeshMaterial3d(atlas.water_material.clone()),
|
mesh: meshes.add(water_mesh),
|
||||||
Transform::from_translation(pos),
|
material: atlas.water_material.clone(),
|
||||||
|
transform: Transform::from_translation(pos),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
PhosChunk::new(index),
|
PhosChunk::new(index),
|
||||||
NotShadowCaster,
|
NotShadowCaster,
|
||||||
RenderDistanceVisibility::default().with_offset(visibility_offset),
|
RenderDistanceVisibility::default().with_offset(visibility_offset),
|
||||||
@@ -281,10 +277,23 @@ fn spawn_map(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commands.spawn((
|
||||||
|
// MaterialMeshBundle {
|
||||||
|
// transform: Transform::from_translation(heightmap.get_center()),
|
||||||
|
// mesh: meshes.add(
|
||||||
|
// Plane3d::default()
|
||||||
|
// .mesh()
|
||||||
|
// .size(heightmap.get_world_width(), heightmap.get_world_height()),
|
||||||
|
// ),
|
||||||
|
// material: atlas.water_material.clone(),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// NotShadowCaster,
|
||||||
|
// ));
|
||||||
|
|
||||||
commands.insert_resource(registry);
|
commands.insert_resource(registry);
|
||||||
generator_state.set(GeneratorState::Idle);
|
generator_state.set(GeneratorState::Idle);
|
||||||
if cur_game_state.get() != &MenuState::InGame
|
if cur_game_state.get() != &MenuState::InGame {
|
||||||
{
|
|
||||||
game_state.set(MenuState::InGame);
|
game_state.set(MenuState::InGame);
|
||||||
gameplay_state.set(GameplayState::PlaceHQ);
|
gameplay_state.set(GameplayState::PlaceHQ);
|
||||||
}
|
}
|
||||||
@@ -298,10 +307,8 @@ fn despawn_map(
|
|||||||
chunks: Query<Entity, With<PhosChunk>>,
|
chunks: Query<Entity, With<PhosChunk>>,
|
||||||
mut next_state: ResMut<NextState<GeneratorState>>,
|
mut next_state: ResMut<NextState<GeneratorState>>,
|
||||||
biome_painter: Res<BiomePainter>,
|
biome_painter: Res<BiomePainter>,
|
||||||
)
|
) {
|
||||||
{
|
for chunk in chunks.iter() {
|
||||||
for chunk in chunks.iter()
|
|
||||||
{
|
|
||||||
commands.entity(chunk).despawn();
|
commands.entity(chunk).despawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use world_generation::biome_painter::BiomePainterAsset;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use shared::tags::MainCamera;
|
|
||||||
|
use crate::camera_system::components::PhosCamera;
|
||||||
|
|
||||||
pub struct RenderDistancePlugin;
|
pub struct RenderDistancePlugin;
|
||||||
|
|
||||||
impl Plugin for RenderDistancePlugin
|
impl Plugin for RenderDistancePlugin {
|
||||||
{
|
fn build(&self, app: &mut bevy::prelude::App) {
|
||||||
fn build(&self, app: &mut bevy::prelude::App)
|
|
||||||
{
|
|
||||||
app.register_type::<RenderDistanceSettings>();
|
app.register_type::<RenderDistanceSettings>();
|
||||||
app.add_systems(PostUpdate, render_distance_system)
|
app.add_systems(PostUpdate, render_distance_system)
|
||||||
.insert_resource(RenderDistanceSettings::default());
|
.insert_resource(RenderDistanceSettings::default());
|
||||||
@@ -15,68 +14,54 @@ impl Plugin for RenderDistancePlugin
|
|||||||
|
|
||||||
#[derive(Resource, Reflect)]
|
#[derive(Resource, Reflect)]
|
||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
pub struct RenderDistanceSettings
|
pub struct RenderDistanceSettings {
|
||||||
{
|
|
||||||
pub render_distance: f32,
|
pub render_distance: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderDistanceSettings
|
impl RenderDistanceSettings {
|
||||||
{
|
pub fn new(distance: f32) -> Self {
|
||||||
pub fn new(distance: f32) -> Self
|
|
||||||
{
|
|
||||||
return Self {
|
return Self {
|
||||||
render_distance: distance,
|
render_distance: distance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RenderDistanceSettings
|
impl Default for RenderDistanceSettings {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self::new(500.)
|
Self::new(500.)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct RenderDistanceVisibility
|
pub struct RenderDistanceVisibility {
|
||||||
{
|
|
||||||
pub offset: Vec3,
|
pub offset: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderDistanceVisibility
|
impl RenderDistanceVisibility {
|
||||||
{
|
pub fn with_offset(mut self, offset: Vec3) -> Self {
|
||||||
pub fn with_offset(mut self, offset: Vec3) -> Self
|
|
||||||
{
|
|
||||||
self.offset = offset;
|
self.offset = offset;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RenderDistanceVisibility
|
impl Default for RenderDistanceVisibility {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self { offset: Vec3::ZERO }
|
Self { offset: Vec3::ZERO }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_distance_system(
|
fn render_distance_system(
|
||||||
mut objects: Query<(&Transform, &mut Visibility, &RenderDistanceVisibility)>,
|
mut objects: Query<(&Transform, &mut Visibility, &RenderDistanceVisibility)>,
|
||||||
camera: Single<&Transform, With<MainCamera>>,
|
camera_query: Query<&Transform, With<PhosCamera>>,
|
||||||
settings: Res<RenderDistanceSettings>,
|
settings: Res<RenderDistanceSettings>,
|
||||||
)
|
) {
|
||||||
{
|
let camera = camera_query.single();
|
||||||
let cam_pos = Vec3::new(camera.translation.x, 0.0, camera.translation.z);
|
let cam_pos = Vec3::new(camera.translation.x, 0.0, camera.translation.z);
|
||||||
for (t, mut vis, r) in objects.iter_mut()
|
for (t, mut vis, r) in objects.iter_mut() {
|
||||||
{
|
|
||||||
let dist = (cam_pos - (t.translation + r.offset)).length();
|
let dist = (cam_pos - (t.translation + r.offset)).length();
|
||||||
if settings.render_distance < dist
|
if settings.render_distance < dist {
|
||||||
{
|
|
||||||
*vis = Visibility::Hidden;
|
*vis = Visibility::Hidden;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
*vis = Visibility::Visible;
|
*vis = Visibility::Visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
use bevy::{platform::collections::HashSet, prelude::*};
|
use bevy::{prelude::*, utils::hashbrown::HashSet, window::PrimaryWindow};
|
||||||
|
use bevy_rapier3d::{pipeline::QueryFilter, plugin::RapierContext};
|
||||||
use shared::{
|
use shared::{
|
||||||
events::{ChunkModifiedEvent, TileModifiedEvent},
|
events::{ChunkModifiedEvent, TileModifiedEvent},
|
||||||
resources::TileUnderCursor,
|
resources::TileUnderCursor,
|
||||||
states::GameplayState,
|
states::GameplayState,
|
||||||
};
|
};
|
||||||
use world_generation::{prelude::Map, states::GeneratorState};
|
use world_generation::{hex_utils::HexCoord, prelude::Map, states::GeneratorState};
|
||||||
|
|
||||||
use crate::prelude::{PhosChunkRegistry, RebuildChunk};
|
use crate::{
|
||||||
|
camera_system::components::PhosCamera,
|
||||||
|
prelude::{PhosChunkRegistry, RebuildChunk},
|
||||||
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct TerraFormingTestPlugin;
|
pub struct TerraFormingTestPlugin;
|
||||||
|
|
||||||
impl Plugin for TerraFormingTestPlugin
|
impl Plugin for TerraFormingTestPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
deform
|
deform
|
||||||
@@ -24,48 +25,39 @@ impl Plugin for TerraFormingTestPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn deform(
|
fn deform(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mouse: Res<ButtonInput<MouseButton>>,
|
mouse: Res<ButtonInput<MouseButton>>,
|
||||||
mut heightmap: ResMut<Map>,
|
mut heightmap: ResMut<Map>,
|
||||||
chunks: Res<PhosChunkRegistry>,
|
chunks: Res<PhosChunkRegistry>,
|
||||||
tile_under_cursor: Res<TileUnderCursor>,
|
tile_under_cursor: Res<TileUnderCursor>,
|
||||||
mut chunk_modified: MessageWriter<ChunkModifiedEvent>,
|
mut chunk_modified: EventWriter<ChunkModifiedEvent>,
|
||||||
mut tile_modified: MessageWriter<TileModifiedEvent>,
|
mut tile_modified: EventWriter<TileModifiedEvent>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let mut multi = 0.;
|
let mut multi = 0.;
|
||||||
if mouse.just_pressed(MouseButton::Left)
|
if mouse.just_pressed(MouseButton::Left) {
|
||||||
{
|
|
||||||
multi = 1.;
|
multi = 1.;
|
||||||
}
|
} else if mouse.just_pressed(MouseButton::Right) {
|
||||||
else if mouse.just_pressed(MouseButton::Right)
|
|
||||||
{
|
|
||||||
multi = -1.;
|
multi = -1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
if multi == 0.
|
if multi == 0. {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(contact) = tile_under_cursor.0
|
if let Some(contact) = tile_under_cursor.0 {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let span = info_span!("Deform Mesh").entered();
|
let span = info_span!("Deform Mesh").entered();
|
||||||
let modified_tiles = heightmap.create_crater(&contact.tile, 5, 5. * multi);
|
let modified_tiles = heightmap.create_crater(&contact.tile, 5, 5. * multi);
|
||||||
let mut chunk_set: HashSet<usize> = HashSet::new();
|
let mut chunk_set: HashSet<usize> = HashSet::new();
|
||||||
for (tile, height) in modified_tiles
|
for (tile, height) in modified_tiles {
|
||||||
{
|
|
||||||
let chunk = tile.to_chunk_index(heightmap.width);
|
let chunk = tile.to_chunk_index(heightmap.width);
|
||||||
if !chunk_set.contains(&chunk)
|
if !chunk_set.contains(&chunk) {
|
||||||
{
|
chunk_modified.send(ChunkModifiedEvent { index: chunk });
|
||||||
chunk_modified.write(ChunkModifiedEvent { index: chunk });
|
|
||||||
chunk_set.insert(chunk);
|
chunk_set.insert(chunk);
|
||||||
commands.entity(chunks.chunks[chunk]).insert(RebuildChunk);
|
commands.entity(chunks.chunks[chunk]).insert(RebuildChunk);
|
||||||
}
|
}
|
||||||
tile_modified.write(TileModifiedEvent::HeightChanged(tile, height));
|
tile_modified.send(TileModifiedEvent::HeightChanged(tile, height));
|
||||||
}
|
}
|
||||||
// commands.entity(e).insert(RebuildChunk);
|
// commands.entity(e).insert(RebuildChunk);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
use crate::camera_system::components::PhosCamera;
|
use crate::camera_system::components::PhosCamera;
|
||||||
use crate::map_rendering::map_init::MapInitPlugin;
|
use crate::map_rendering::map_init::MapInitPlugin;
|
||||||
use crate::map_rendering::render_distance_system::RenderDistancePlugin;
|
use crate::map_rendering::render_distance_system::RenderDistancePlugin;
|
||||||
use crate::ui::build_ui::BuildUIPlugin;
|
use crate::utlis::editor_plugin::EditorPlugin;
|
||||||
use crate::utlis::tile_selection_plugin::TileSelectionPlugin;
|
use crate::utlis::tile_selection_plugin::TileSelectionPlugin;
|
||||||
use crate::{camera_system::camera_plugin::PhosCameraPlugin, utlis::debug_plugin::DebugPlugin};
|
use crate::{camera_system::camera_plugin::PhosCameraPlugin, utlis::debug_plugin::DebugPlugin};
|
||||||
use bevy::diagnostic::{EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin};
|
use bevy::{
|
||||||
use bevy::light::CascadeShadowConfig;
|
pbr::{wireframe::WireframeConfig, CascadeShadowConfig},
|
||||||
use bevy::{pbr::wireframe::WireframeConfig, prelude::*};
|
prelude::*,
|
||||||
|
};
|
||||||
use bevy_asset_loader::prelude::*;
|
use bevy_asset_loader::prelude::*;
|
||||||
use bevy_rapier3d::dynamics::{Ccd, RigidBody, Velocity};
|
use bevy_rapier3d::dynamics::{Ccd, RigidBody, Velocity};
|
||||||
use bevy_rapier3d::geometry::Collider;
|
use bevy_rapier3d::geometry::Collider;
|
||||||
use bevy_rapier3d::plugin::{NoUserData, RapierPhysicsPlugin};
|
use bevy_rapier3d::plugin::{NoUserData, RapierPhysicsPlugin};
|
||||||
|
use buildings::BuildingPugin;
|
||||||
|
use iyes_perf_ui::prelude::*;
|
||||||
use shared::sets::GameplaySet;
|
use shared::sets::GameplaySet;
|
||||||
use shared::states::{GameplayState, MenuState};
|
use shared::states::{GameplayState, MenuState};
|
||||||
use shared::{despawn::DespawnPuglin, states::AssetLoadState};
|
use shared::{despawn::DespawnPuglin, states::AssetLoadState};
|
||||||
|
use units::units_plugin::UnitsPlugin;
|
||||||
use world_generation::states::GeneratorState;
|
use world_generation::states::GeneratorState;
|
||||||
|
|
||||||
pub struct PhosGamePlugin;
|
pub struct PhosGamePlugin;
|
||||||
|
|
||||||
impl Plugin for PhosGamePlugin
|
impl Plugin for PhosGamePlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.insert_state(AssetLoadState::Loading);
|
app.insert_state(AssetLoadState::Loading);
|
||||||
app.insert_state(MenuState::Loading);
|
app.insert_state(MenuState::Loading);
|
||||||
app.insert_state(GameplayState::Waiting);
|
app.insert_state(GameplayState::Waiting);
|
||||||
@@ -34,14 +36,12 @@ impl Plugin for PhosGamePlugin
|
|||||||
PhosCameraPlugin,
|
PhosCameraPlugin,
|
||||||
MapInitPlugin,
|
MapInitPlugin,
|
||||||
RenderDistancePlugin,
|
RenderDistancePlugin,
|
||||||
// BuildingPugin,
|
BuildingPugin,
|
||||||
BuildUIPlugin,
|
UnitsPlugin,
|
||||||
// SimpleAnimationPlugin,
|
|
||||||
// UnitsPlugin,
|
|
||||||
DespawnPuglin,
|
DespawnPuglin,
|
||||||
TileSelectionPlugin,
|
TileSelectionPlugin,
|
||||||
#[cfg(feature = "editor")]
|
#[cfg(debug_assertions)]
|
||||||
crate::utlis::editor_plugin::EditorPlugin,
|
EditorPlugin,
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
DebugPlugin,
|
DebugPlugin,
|
||||||
));
|
));
|
||||||
@@ -55,10 +55,10 @@ impl Plugin for PhosGamePlugin
|
|||||||
app.add_systems(Update, spawn_sphere);
|
app.add_systems(Update, spawn_sphere);
|
||||||
|
|
||||||
//Perf UI
|
//Perf UI
|
||||||
app.add_plugins(FrameTimeDiagnosticsPlugin::default())
|
app.add_plugins(bevy::diagnostic::FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugins(EntityCountDiagnosticsPlugin::default())
|
.add_plugins(bevy::diagnostic::EntityCountDiagnosticsPlugin)
|
||||||
.add_plugins(bevy::diagnostic::SystemInformationDiagnosticsPlugin);
|
.add_plugins(bevy::diagnostic::SystemInformationDiagnosticsPlugin)
|
||||||
// .add_plugins(PerfUiPlugin);
|
.add_plugins(PerfUiPlugin);
|
||||||
|
|
||||||
//Physics
|
//Physics
|
||||||
app.add_plugins(RapierPhysicsPlugin::<NoUserData>::default());
|
app.add_plugins(RapierPhysicsPlugin::<NoUserData>::default());
|
||||||
@@ -71,56 +71,55 @@ impl Plugin for PhosGamePlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_gameplay_set(app: &mut App)
|
fn configure_gameplay_set(app: &mut App) {
|
||||||
{
|
|
||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
Update,
|
Update,
|
||||||
GameplaySet.run_if(in_state(GeneratorState::Idle).and(in_state(MenuState::InGame))),
|
GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))),
|
||||||
);
|
);
|
||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
GameplaySet.run_if(in_state(GeneratorState::Idle).and(in_state(MenuState::InGame))),
|
GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))),
|
||||||
);
|
);
|
||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
PostUpdate,
|
PostUpdate,
|
||||||
GameplaySet.run_if(in_state(GeneratorState::Idle).and(in_state(MenuState::InGame))),
|
GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
FixedUpdate,
|
FixedUpdate,
|
||||||
GameplaySet.run_if(in_state(GeneratorState::Idle).and(in_state(MenuState::InGame))),
|
GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))),
|
||||||
);
|
);
|
||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
FixedPreUpdate,
|
FixedPreUpdate,
|
||||||
GameplaySet.run_if(in_state(GeneratorState::Idle).and(in_state(MenuState::InGame))),
|
GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))),
|
||||||
);
|
);
|
||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
FixedPostUpdate,
|
FixedPostUpdate,
|
||||||
GameplaySet.run_if(in_state(GeneratorState::Idle).and(in_state(MenuState::InGame))),
|
GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_game(mut commands: Commands, mut materials: ResMut<Assets<StandardMaterial>>)
|
fn init_game(mut commands: Commands, mut materials: ResMut<Assets<StandardMaterial>>) {
|
||||||
{
|
|
||||||
// commands.spawn((
|
|
||||||
// PerfUiRoot::default(),
|
|
||||||
// PerfUiEntryFPS::default(),
|
|
||||||
// PerfUiEntryFPSWorst::default(),
|
|
||||||
// PerfUiEntryFrameTime::default(),
|
|
||||||
// PerfUiEntryFrameTimeWorst::default(),
|
|
||||||
// ));
|
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
DirectionalLight {
|
PerfUiRoot::default(),
|
||||||
|
PerfUiEntryFPS::default(),
|
||||||
|
PerfUiEntryFPSWorst::default(),
|
||||||
|
PerfUiEntryFrameTime::default(),
|
||||||
|
PerfUiEntryFrameTimeWorst::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.spawn(DirectionalLightBundle {
|
||||||
|
directional_light: DirectionalLight {
|
||||||
shadows_enabled: true,
|
shadows_enabled: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
CascadeShadowConfig {
|
cascade_shadow_config: CascadeShadowConfig {
|
||||||
bounds: vec![200., 400., 600., 800.],
|
bounds: vec![200., 400., 600., 800.],
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
Transform::from_xyz(500., 260.0, 500.).looking_at(Vec3::ZERO, Vec3::Y),
|
transform: Transform::from_xyz(500., 260.0, 500.).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
));
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
let sphere_mat = StandardMaterial {
|
let sphere_mat = StandardMaterial {
|
||||||
base_color: Color::srgb(1., 1., 0.),
|
base_color: Color::srgb(1., 1., 0.),
|
||||||
@@ -135,21 +134,24 @@ struct SphereMat(Handle<StandardMaterial>);
|
|||||||
|
|
||||||
fn spawn_sphere(
|
fn spawn_sphere(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
cam: Single<&Transform, With<PhosCamera>>,
|
cam: Query<&Transform, With<PhosCamera>>,
|
||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mat: Res<SphereMat>,
|
mat: Res<SphereMat>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
if keyboard_input.just_pressed(KeyCode::KeyF) {
|
if keyboard_input.just_pressed(KeyCode::KeyF) {
|
||||||
|
let cam_transform = cam.single();
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh3d(meshes.add(Sphere::new(0.3))),
|
MaterialMeshBundle {
|
||||||
MeshMaterial3d(mat.0.clone()),
|
mesh: meshes.add(Sphere::new(0.3)),
|
||||||
Transform::from_translation(cam.translation),
|
material: mat.0.clone(),
|
||||||
|
transform: Transform::from_translation(cam_transform.translation),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
Collider::ball(0.3),
|
Collider::ball(0.3),
|
||||||
RigidBody::Dynamic,
|
RigidBody::Dynamic,
|
||||||
Ccd::enabled(),
|
Ccd::enabled(),
|
||||||
Velocity::linear(cam.forward() * 50.),
|
Velocity::linear(cam_transform.forward() * 50.),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use bevy::pbr::ExtendedMaterial;
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::prelude::{Component, Image, Resource};
|
use bevy::prelude::{Component, Image, Resource};
|
||||||
use bevy_asset_loader::asset_collection::AssetCollection;
|
use bevy_asset_loader::asset_collection::AssetCollection;
|
||||||
|
use world_generation::biome_painter::BiomePainterAsset;
|
||||||
|
|
||||||
use crate::shader_extensions::chunk_material::ChunkMaterial;
|
use crate::shader_extensions::chunk_material::ChunkMaterial;
|
||||||
use crate::shader_extensions::water_material::WaterMaterial;
|
use crate::shader_extensions::water_material::WaterMaterial;
|
||||||
|
|
||||||
#[derive(AssetCollection, Resource, Default)]
|
#[derive(AssetCollection, Resource, Default)]
|
||||||
pub struct PhosAssets
|
pub struct PhosAssets {
|
||||||
{
|
|
||||||
#[asset(key = "chunk_atlas")]
|
#[asset(key = "chunk_atlas")]
|
||||||
pub handle: Handle<Image>,
|
pub handle: Handle<Image>,
|
||||||
pub chunk_material_handle: Handle<ExtendedMaterial<StandardMaterial, ChunkMaterial>>,
|
pub chunk_material_handle: Handle<ExtendedMaterial<StandardMaterial, ChunkMaterial>>,
|
||||||
@@ -17,33 +17,24 @@ pub struct PhosAssets
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct PhosChunk
|
pub struct PhosChunk {
|
||||||
{
|
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhosChunk
|
impl PhosChunk {
|
||||||
{
|
pub fn new(index: usize) -> Self {
|
||||||
pub fn new(index: usize) -> Self
|
|
||||||
{
|
|
||||||
return Self { index };
|
return Self { index };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct WaterMesh(pub AssetId<Mesh>);
|
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub struct PhosChunkRegistry
|
pub struct PhosChunkRegistry {
|
||||||
{
|
|
||||||
pub chunks: Vec<Entity>,
|
pub chunks: Vec<Entity>,
|
||||||
pub waters: Vec<Entity>,
|
pub waters: Vec<Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhosChunkRegistry
|
impl PhosChunkRegistry {
|
||||||
{
|
pub fn new(size: usize) -> Self {
|
||||||
pub fn new(size: usize) -> Self
|
|
||||||
{
|
|
||||||
return Self {
|
return Self {
|
||||||
chunks: Vec::with_capacity(size),
|
chunks: Vec::with_capacity(size),
|
||||||
waters: Vec::with_capacity(size),
|
waters: Vec::with_capacity(size),
|
||||||
|
|||||||
@@ -1,76 +1,39 @@
|
|||||||
use bevy::asset::{Asset, Handle};
|
use bevy::asset::{Asset, Handle};
|
||||||
use bevy::image::Image;
|
use bevy::pbr::MaterialExtension;
|
||||||
use bevy::pbr::{Material, MaterialExtension};
|
|
||||||
use bevy::reflect::TypePath;
|
use bevy::reflect::TypePath;
|
||||||
use bevy::render::render_resource::AsBindGroup;
|
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
|
||||||
use bevy::shader::ShaderRef;
|
use bevy::render::texture::Image;
|
||||||
use world_generation::consts::{ATTRIBUTE_PACKED_VERTEX_DATA, ATTRIBUTE_VERTEX_HEIGHT};
|
|
||||||
|
|
||||||
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
||||||
pub struct ChunkMaterial
|
pub struct ChunkMaterial {
|
||||||
{
|
|
||||||
#[texture(100, dimension = "2d_array")]
|
#[texture(100, dimension = "2d_array")]
|
||||||
#[sampler(101)]
|
#[sampler(101)]
|
||||||
pub array_texture: Handle<Image>,
|
pub array_texture: Handle<Image>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialExtension for ChunkMaterial
|
impl MaterialExtension for ChunkMaterial {
|
||||||
{
|
fn fragment_shader() -> ShaderRef {
|
||||||
fn fragment_shader() -> ShaderRef
|
|
||||||
{
|
|
||||||
"shaders/world/chunk.wgsl".into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
|
||||||
pub struct PackedChunkMaterial
|
|
||||||
{
|
|
||||||
#[texture(100, dimension = "2d_array")]
|
|
||||||
#[sampler(101)]
|
|
||||||
pub array_texture: Handle<Image>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Material for PackedChunkMaterial
|
|
||||||
{
|
|
||||||
fn fragment_shader() -> ShaderRef
|
|
||||||
{
|
|
||||||
"shaders/world/chunk.wgsl".into()
|
"shaders/world/chunk.wgsl".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vertex_shader() -> ShaderRef
|
// fn vertex_shader() -> ShaderRef {
|
||||||
{
|
|
||||||
"shaders/world/chunk_packed.wgsl".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepass_vertex_shader() -> ShaderRef
|
|
||||||
{
|
|
||||||
"shaders/world/chunk_packed.wgsl".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn deferred_vertex_shader() -> ShaderRef {
|
|
||||||
// "shaders/world/chunk_packed.wgsl".into()
|
// "shaders/world/chunk_packed.wgsl".into()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// fn opaque_render_method(&self) -> bevy::pbr::OpaqueRendererMethod {
|
// fn specialize(
|
||||||
// return OpaqueRendererMethod::Auto;
|
// _pipeline: &bevy::pbr::MaterialExtensionPipeline,
|
||||||
|
// descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
|
||||||
|
// layout: &bevy::render::mesh::MeshVertexBufferLayout,
|
||||||
|
// _key: bevy::pbr::MaterialExtensionKey<Self>,
|
||||||
|
// ) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> {
|
||||||
|
// let vertex_layout = layout.get_layout(&[
|
||||||
|
// // Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
|
||||||
|
// // Mesh::ATTRIBUTE_UV_0.at_shader_location(1),
|
||||||
|
// // Mesh::ATTRIBUTE_NORMAL.at_shader_location(2),
|
||||||
|
// ATTRIBUTE_PACKED_VERTEX_DATA.at_shader_location(7),
|
||||||
|
// ATTRIBUTE_VERTEX_HEIGHT.at_shader_location(8),
|
||||||
|
// ])?;
|
||||||
|
// descriptor.vertex.buffers = vec![vertex_layout];
|
||||||
|
// Ok(())
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fn specialize(
|
|
||||||
_pipeline: &bevy::pbr::MaterialPipeline,
|
|
||||||
descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
|
|
||||||
layout: &bevy::mesh::MeshVertexBufferLayoutRef,
|
|
||||||
_key: bevy::pbr::MaterialPipelineKey<Self>,
|
|
||||||
) -> bevy::ecs::error::Result<(), bevy::render::render_resource::SpecializedMeshPipelineError>
|
|
||||||
{
|
|
||||||
let vertex_layout = layout.0.get_layout(&[
|
|
||||||
// Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
|
|
||||||
// Mesh::ATTRIBUTE_UV_0.at_shader_location(1),
|
|
||||||
// Mesh::ATTRIBUTE_NORMAL.at_shader_location(2),
|
|
||||||
ATTRIBUTE_PACKED_VERTEX_DATA.at_shader_location(7),
|
|
||||||
ATTRIBUTE_VERTEX_HEIGHT.at_shader_location(8),
|
|
||||||
])?;
|
|
||||||
descriptor.vertex.buffers = vec![vertex_layout];
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
use bevy::asset::Asset;
|
use bevy::asset::Asset;
|
||||||
|
use bevy::math::VectorSpace;
|
||||||
use bevy::pbr::MaterialExtension;
|
use bevy::pbr::MaterialExtension;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::reflect::Reflect;
|
use bevy::reflect::Reflect;
|
||||||
use bevy::render::render_resource::{AsBindGroup, ShaderType};
|
use bevy::render::render_resource::{AsBindGroup, ShaderRef, ShaderType};
|
||||||
use bevy::shader::ShaderRef;
|
|
||||||
|
|
||||||
#[derive(Asset, Reflect, AsBindGroup, Debug, Clone, Default)]
|
#[derive(Asset, Reflect, AsBindGroup, Debug, Clone, Default)]
|
||||||
pub struct WaterMaterial
|
pub struct WaterMaterial {
|
||||||
{
|
|
||||||
#[uniform(100)]
|
#[uniform(100)]
|
||||||
pub settings: WaterSettings,
|
pub settings: WaterSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, ShaderType, Reflect)]
|
#[derive(Debug, Clone, ShaderType, Reflect)]
|
||||||
pub struct WaterSettings
|
pub struct WaterSettings {
|
||||||
{
|
|
||||||
pub offset: f32,
|
pub offset: f32,
|
||||||
pub scale: f32,
|
pub scale: f32,
|
||||||
pub f_power: f32,
|
pub f_power: f32,
|
||||||
pub deep_color: LinearRgba,
|
pub deep_color: LinearRgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WaterSettings
|
impl Default for WaterSettings {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
offset: 0.0,
|
offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
@@ -34,21 +30,18 @@ impl Default for WaterSettings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialExtension for WaterMaterial
|
impl MaterialExtension for WaterMaterial {
|
||||||
{
|
fn fragment_shader() -> ShaderRef {
|
||||||
fn fragment_shader() -> ShaderRef
|
|
||||||
{
|
|
||||||
"shaders/world/water.wgsl".into()
|
"shaders/world/water.wgsl".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn specialize(
|
// fn specialize(
|
||||||
// pipeline: &bevy::pbr::MaterialExtensionPipeline,
|
// _pipeline: &bevy::pbr::MaterialExtensionPipeline,
|
||||||
// descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
|
// descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
|
||||||
// layout: &bevy::mesh::MeshVertexBufferLayoutRef,
|
// layout: &bevy::render::mesh::MeshVertexBufferLayout,
|
||||||
// key: bevy::pbr::MaterialExtensionKey<Self>,
|
// _key: bevy::pbr::MaterialExtensionKey<Self>,
|
||||||
// ) -> std::result::Result<(), bevy::render::render_resource::SpecializedMeshPipelineError>
|
// ) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> {
|
||||||
// {
|
// let vertex_layout = layout.get_layout(&[
|
||||||
// let vertex_layout = layout.0.get_layout(&[
|
|
||||||
// // Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
|
// // Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
|
||||||
// // Mesh::ATTRIBUTE_UV_0.at_shader_location(1),
|
// // Mesh::ATTRIBUTE_UV_0.at_shader_location(1),
|
||||||
// // Mesh::ATTRIBUTE_NORMAL.at_shader_location(2),
|
// // Mesh::ATTRIBUTE_NORMAL.at_shader_location(2),
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
use bevy::{
|
|
||||||
camera::{visibility::RenderLayers, CameraOutputMode},
|
|
||||||
prelude::*,
|
|
||||||
render::render_resource::BlendState,
|
|
||||||
};
|
|
||||||
use shared::states::AssetLoadState;
|
|
||||||
|
|
||||||
use crate::ui::states::BuildUIState;
|
|
||||||
pub struct BuildUIPlugin;
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
struct BuildUIItem;
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
#[require(BuildUIItem)]
|
|
||||||
struct BuildUIMenuItem;
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
struct BuildMenuRoot;
|
|
||||||
|
|
||||||
#[derive(Resource, Reflect, Debug, Default)]
|
|
||||||
struct BuildUIInfo
|
|
||||||
{
|
|
||||||
menu: BuildUIMenu,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Reflect, Debug)]
|
|
||||||
enum BuildUIMenu
|
|
||||||
{
|
|
||||||
#[default]
|
|
||||||
Structure,
|
|
||||||
Defense,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin for BuildUIPlugin
|
|
||||||
{
|
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.add_systems(Startup, setup_cameras);
|
|
||||||
app.init_resource::<BuildUIInfo>();
|
|
||||||
app.insert_state(BuildUIState::Init);
|
|
||||||
app.add_systems(
|
|
||||||
Update,
|
|
||||||
(
|
|
||||||
spawn_ui.run_if(in_state(AssetLoadState::LoadComplete).and(in_state(BuildUIState::Init))),
|
|
||||||
draw_menu_ui.run_if(in_state(AssetLoadState::LoadComplete).and(in_state(BuildUIState::DrawMenu))),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
app.add_systems(PostUpdate, cleanup_ui.run_if(in_state(BuildUIState::Cleanup)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_cameras(mut commands: Commands)
|
|
||||||
{
|
|
||||||
commands
|
|
||||||
.spawn((
|
|
||||||
Camera2d,
|
|
||||||
Camera {
|
|
||||||
order: 1,
|
|
||||||
clear_color: ClearColorConfig::None,
|
|
||||||
msaa_writeback: MsaaWriteback::Always,
|
|
||||||
output_mode: CameraOutputMode::Write {
|
|
||||||
blend_state: Some(BlendState::ALPHA_BLENDING),
|
|
||||||
clear_color: ClearColorConfig::None,
|
|
||||||
},
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
IsDefaultUiCamera,
|
|
||||||
))
|
|
||||||
.insert(RenderLayers::layer(1))
|
|
||||||
.insert(Msaa::Off);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_ui(mut commands: Commands, mut next_state: ResMut<NextState<BuildUIState>>)
|
|
||||||
{
|
|
||||||
commands
|
|
||||||
.spawn((
|
|
||||||
Node {
|
|
||||||
width: Val::Percent(100.),
|
|
||||||
height: Val::Percent(100.),
|
|
||||||
justify_content: JustifyContent::Center,
|
|
||||||
align_items: AlignItems::End,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
RenderLayers::layer(1),
|
|
||||||
Name::new("Build UI Root"),
|
|
||||||
BuildUIItem,
|
|
||||||
))
|
|
||||||
// .insert(PickingBehavior::IGNORE)
|
|
||||||
.with_children(|build_root| {
|
|
||||||
build_root
|
|
||||||
.spawn((
|
|
||||||
Name::new("Build UI"),
|
|
||||||
Node {
|
|
||||||
width: Val::Px(500.),
|
|
||||||
height: Val::Px(100.),
|
|
||||||
justify_content: JustifyContent::Stretch,
|
|
||||||
flex_direction: FlexDirection::Column,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BackgroundColor(LinearRgba::GREEN.into()),
|
|
||||||
))
|
|
||||||
.with_children(|build_ui| {
|
|
||||||
build_ui.spawn((
|
|
||||||
Name::new("Menu Root"),
|
|
||||||
BuildMenuRoot,
|
|
||||||
Node {
|
|
||||||
width: Val::Percent(100.),
|
|
||||||
height: Val::Px(70.),
|
|
||||||
column_gap: Val::Px(5.),
|
|
||||||
padding: UiRect::all(Val::Px(2.)),
|
|
||||||
flex_direction: FlexDirection::Row,
|
|
||||||
overflow: Overflow::scroll_x(),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
));
|
|
||||||
build_ui
|
|
||||||
.spawn((
|
|
||||||
Name::new("Toolbar"),
|
|
||||||
Node {
|
|
||||||
width: Val::Percent(100.),
|
|
||||||
height: Val::Px(30.),
|
|
||||||
column_gap: Val::Px(5.),
|
|
||||||
padding: UiRect::horizontal(Val::Px(10.)),
|
|
||||||
justify_content: JustifyContent::Stretch,
|
|
||||||
align_self: AlignSelf::End,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BackgroundColor(LinearRgba::BLUE.into()),
|
|
||||||
))
|
|
||||||
.with_children(|toolbar| {
|
|
||||||
for i in 0..6
|
|
||||||
{
|
|
||||||
toolbar.spawn((
|
|
||||||
Name::new(format!("Button {}", i)),
|
|
||||||
Button,
|
|
||||||
Node {
|
|
||||||
height: Val::Percent(100.),
|
|
||||||
width: Val::Percent(100.),
|
|
||||||
align_items: AlignItems::Center,
|
|
||||||
justify_content: JustifyContent::Center,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BackgroundColor(LinearRgba::WHITE.into()),
|
|
||||||
children![(
|
|
||||||
Text::new(format!("Button {}", i)),
|
|
||||||
TextFont {
|
|
||||||
font_size: 15.,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
TextColor(LinearRgba::BLACK.into()),
|
|
||||||
TextShadow {
|
|
||||||
offset: Vec2::splat(2.),
|
|
||||||
..default()
|
|
||||||
}
|
|
||||||
)],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
next_state.set(BuildUIState::DrawMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_menu_ui(
|
|
||||||
mut commands: Commands,
|
|
||||||
menu_root: Single<Entity, With<BuildMenuRoot>>,
|
|
||||||
menu_items: Query<Entity, With<BuildUIMenuItem>>,
|
|
||||||
menu_info: Res<BuildUIInfo>,
|
|
||||||
mut next: ResMut<NextState<BuildUIState>>,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
for entity in menu_items.iter()
|
|
||||||
{
|
|
||||||
commands.entity(entity).despawn();
|
|
||||||
}
|
|
||||||
info!("Draw Menu");
|
|
||||||
match menu_info.menu
|
|
||||||
{
|
|
||||||
BuildUIMenu::Structure => draw_structure_ui(commands, menu_root.into_inner()),
|
|
||||||
BuildUIMenu::Defense => (),
|
|
||||||
}
|
|
||||||
next.set(BuildUIState::Update);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_structure_ui(mut commands: Commands, root: Entity)
|
|
||||||
{
|
|
||||||
commands.entity(root).with_children(|root| {
|
|
||||||
for i in 0..10
|
|
||||||
{
|
|
||||||
root.spawn((
|
|
||||||
BuildUIMenuItem,
|
|
||||||
Node {
|
|
||||||
height: Val::Percent(100.),
|
|
||||||
width: Val::Px(100.),
|
|
||||||
display: Display::Grid,
|
|
||||||
grid_template_rows: vec![RepeatedGridTrack::px(1, 100.), RepeatedGridTrack::fr(1, 1.)],
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
children![
|
|
||||||
(
|
|
||||||
Node {
|
|
||||||
height: Val::Px(100.),
|
|
||||||
width: Val::Px(100.),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BackgroundColor(LinearRgba::RED.into())
|
|
||||||
),
|
|
||||||
(Text::new(format!("Icon {}", i))),
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup_ui(mut commands: Commands, ui_items: Query<Entity, With<BuildUIItem>>)
|
|
||||||
{
|
|
||||||
for entity in ui_items.iter()
|
|
||||||
{
|
|
||||||
commands.entity(entity).despawn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
game/main/src/ui/game/build_ui.rs
Normal file
11
game/main/src/ui/game/build_ui.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub struct BuildUiPlugin;
|
||||||
|
|
||||||
|
impl Plugin for BuildUiPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(PostStartup, setup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands) {}
|
||||||
2
game/main/src/ui/game/mod.rs
Normal file
2
game/main/src/ui/game/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
pub mod build_ui;
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
pub mod build_ui;
|
|
||||||
pub mod states;
|
pub mod game;
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum BuildUIState
|
|
||||||
{
|
|
||||||
Init,
|
|
||||||
DrawMenu,
|
|
||||||
Update,
|
|
||||||
Cleanup,
|
|
||||||
}
|
|
||||||
@@ -4,10 +4,9 @@ use bevy::{
|
|||||||
asset::Assets,
|
asset::Assets,
|
||||||
ecs::system::Res,
|
ecs::system::Res,
|
||||||
math::{IVec2, UVec2, Vec3},
|
math::{IVec2, UVec2, Vec3},
|
||||||
mesh::Mesh,
|
render::mesh::Mesh,
|
||||||
};
|
};
|
||||||
use bevy_rapier3d::geometry::{Collider, TriMeshFlags};
|
use bevy_rapier3d::geometry::{Collider, TriMeshFlags};
|
||||||
use hex::prelude::*;
|
|
||||||
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||||
use world_generation::{
|
use world_generation::{
|
||||||
biome_painter::BiomePainter,
|
biome_painter::BiomePainter,
|
||||||
@@ -15,7 +14,8 @@ use world_generation::{
|
|||||||
chunk_colliders::generate_chunk_collider,
|
chunk_colliders::generate_chunk_collider,
|
||||||
mesh_generator::{generate_chunk_mesh, generate_chunk_water_mesh},
|
mesh_generator::{generate_chunk_mesh, generate_chunk_water_mesh},
|
||||||
},
|
},
|
||||||
prelude::{Map, MeshChunkData},
|
hex_utils::offset_to_world,
|
||||||
|
prelude::{Chunk, Map, MeshChunkData},
|
||||||
tile_manager::TileAsset,
|
tile_manager::TileAsset,
|
||||||
tile_mapper::TileMapperAsset,
|
tile_mapper::TileMapperAsset,
|
||||||
};
|
};
|
||||||
@@ -25,8 +25,7 @@ pub fn paint_map(
|
|||||||
painter: &BiomePainter,
|
painter: &BiomePainter,
|
||||||
tiles: &Res<Assets<TileAsset>>,
|
tiles: &Res<Assets<TileAsset>>,
|
||||||
mappers: &Res<Assets<TileMapperAsset>>,
|
mappers: &Res<Assets<TileMapperAsset>>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
map.chunks.par_iter_mut().for_each(|chunk: &mut Chunk| {
|
map.chunks.par_iter_mut().for_each(|chunk: &mut Chunk| {
|
||||||
paint_chunk(chunk, painter, tiles, mappers);
|
paint_chunk(chunk, painter, tiles, mappers);
|
||||||
});
|
});
|
||||||
@@ -37,12 +36,9 @@ pub fn paint_chunk(
|
|||||||
painter: &BiomePainter,
|
painter: &BiomePainter,
|
||||||
tiles: &Res<Assets<TileAsset>>,
|
tiles: &Res<Assets<TileAsset>>,
|
||||||
mappers: &Res<Assets<TileMapperAsset>>,
|
mappers: &Res<Assets<TileMapperAsset>>,
|
||||||
)
|
) {
|
||||||
{
|
for z in 0..Chunk::SIZE {
|
||||||
for z in 0..Chunk::SIZE
|
for x in 0..Chunk::SIZE {
|
||||||
{
|
|
||||||
for x in 0..Chunk::SIZE
|
|
||||||
{
|
|
||||||
let idx = x + z * Chunk::SIZE;
|
let idx = x + z * Chunk::SIZE;
|
||||||
let height = chunk.heights[idx];
|
let height = chunk.heights[idx];
|
||||||
let biome_id = chunk.biome_id[idx];
|
let biome_id = chunk.biome_id[idx];
|
||||||
@@ -51,6 +47,7 @@ pub fn paint_chunk(
|
|||||||
let tile_handle = mapper.unwrap().sample_tile(height);
|
let tile_handle = mapper.unwrap().sample_tile(height);
|
||||||
let tile = tiles.get(tile_handle).unwrap();
|
let tile = tiles.get(tile_handle).unwrap();
|
||||||
chunk.textures[idx] = [tile.texture_id, tile.side_texture_id];
|
chunk.textures[idx] = [tile.texture_id, tile.side_texture_id];
|
||||||
|
chunk.overlay_textures[idx] = tile.side_overlay_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,8 +58,7 @@ pub fn prepare_chunk_mesh(
|
|||||||
chunk_offset: IVec2,
|
chunk_offset: IVec2,
|
||||||
chunk_index: usize,
|
chunk_index: usize,
|
||||||
map_size: UVec2,
|
map_size: UVec2,
|
||||||
) -> (Mesh, Mesh, (Vec<Vec3>, Vec<[u32; 3]>), Vec3, usize)
|
) -> (Mesh, Mesh, (Vec<Vec3>, Vec<[u32; 3]>), Vec3, usize) {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _gen_mesh = info_span!("Generate Chunk").entered();
|
let _gen_mesh = info_span!("Generate Chunk").entered();
|
||||||
let chunk_mesh = generate_chunk_mesh(chunk);
|
let chunk_mesh = generate_chunk_mesh(chunk);
|
||||||
@@ -84,16 +80,14 @@ pub fn prepare_chunk_mesh_with_collider(
|
|||||||
chunk_offset: IVec2,
|
chunk_offset: IVec2,
|
||||||
chunk_index: usize,
|
chunk_index: usize,
|
||||||
map_size: UVec2,
|
map_size: UVec2,
|
||||||
) -> (Mesh, Mesh, Collider, Vec3, usize)
|
) -> (Mesh, Mesh, Collider, Vec3, usize) {
|
||||||
{
|
|
||||||
let (chunk_mesh, water_mesh, (col_verts, col_indicies), pos, index) =
|
let (chunk_mesh, water_mesh, (col_verts, col_indicies), pos, index) =
|
||||||
prepare_chunk_mesh(chunk, sealevel, chunk_offset, chunk_index, map_size);
|
prepare_chunk_mesh(chunk, sealevel, chunk_offset, chunk_index, map_size);
|
||||||
let collider: Collider;
|
let collider: Collider;
|
||||||
{
|
{
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _collider_span = info_span!("Create Collider Trimesh").entered();
|
let _collider_span = info_span!("Create Collider Trimesh").entered();
|
||||||
collider = Collider::trimesh_with_flags(col_verts, col_indicies, TriMeshFlags::DELETE_DUPLICATE_TRIANGLES)
|
collider = Collider::trimesh_with_flags(col_verts, col_indicies, TriMeshFlags::DELETE_DUPLICATE_TRIANGLES);
|
||||||
.expect("Failed to generate chunk collision mesh");
|
|
||||||
}
|
}
|
||||||
return (chunk_mesh, water_mesh, collider, pos, index);
|
return (chunk_mesh, water_mesh, collider, pos, index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::{gizmos::gizmos, prelude::*};
|
||||||
use shared::states::GameplayState;
|
use shared::states::GameplayState;
|
||||||
use shared::{resources::TileUnderCursor, sets::GameplaySet};
|
use shared::{resources::TileUnderCursor, sets::GameplaySet};
|
||||||
use world_generation::{
|
use world_generation::{
|
||||||
@@ -11,10 +11,8 @@ use crate::camera_system::components::{PhosCamera, PhosOrbitCamera};
|
|||||||
|
|
||||||
pub struct DebugPlugin;
|
pub struct DebugPlugin;
|
||||||
|
|
||||||
impl Plugin for DebugPlugin
|
impl Plugin for DebugPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.insert_state(DebugState::Base);
|
app.insert_state(DebugState::Base);
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
@@ -47,11 +45,10 @@ impl Plugin for DebugPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct Shape(pub Polyline3d);
|
struct Shape(pub Polyline3d<7>);
|
||||||
|
|
||||||
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum DebugState
|
pub enum DebugState {
|
||||||
{
|
|
||||||
Base,
|
Base,
|
||||||
None,
|
None,
|
||||||
Verbose,
|
Verbose,
|
||||||
@@ -61,21 +58,22 @@ fn regenerate_map(
|
|||||||
mut generator_state: ResMut<NextState<GeneratorState>>,
|
mut generator_state: ResMut<NextState<GeneratorState>>,
|
||||||
mut gameplay_state: ResMut<NextState<GameplayState>>,
|
mut gameplay_state: ResMut<NextState<GameplayState>>,
|
||||||
input: Res<ButtonInput<KeyCode>>,
|
input: Res<ButtonInput<KeyCode>>,
|
||||||
)
|
) {
|
||||||
{
|
if input.just_pressed(KeyCode::KeyR) {
|
||||||
if input.just_pressed(KeyCode::KeyR)
|
|
||||||
{
|
|
||||||
generator_state.set(GeneratorState::Regenerate);
|
generator_state.set(GeneratorState::Regenerate);
|
||||||
gameplay_state.set(GameplayState::PlaceHQ);
|
gameplay_state.set(GameplayState::PlaceHQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_tile_heights(map: Res<Map>, mut gizmos: Gizmos, shape: Res<Shape>, tile_under_cursor: Res<TileUnderCursor>)
|
fn show_tile_heights(map: Res<Map>, mut gizmos: Gizmos, shape: Res<Shape>, tile_under_cursor: Res<TileUnderCursor>) {
|
||||||
{
|
if let Some(contact) = tile_under_cursor.0 {
|
||||||
if let Some(contact) = tile_under_cursor.0
|
|
||||||
{
|
|
||||||
let height = map.sample_height(&contact.tile);
|
let height = map.sample_height(&contact.tile);
|
||||||
gizmos.primitive_3d(&shape.0, contact.tile.to_world(height + 0.01), Color::WHITE);
|
gizmos.primitive_3d(
|
||||||
|
&shape.0,
|
||||||
|
contact.tile.to_world(height + 0.01),
|
||||||
|
Quat::IDENTITY,
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
|
||||||
gizmos.line(contact.point, contact.point + Vec3::X, LinearRgba::RED);
|
gizmos.line(contact.point, contact.point + Vec3::X, LinearRgba::RED);
|
||||||
gizmos.line(contact.point, contact.point + Vec3::Y, LinearRgba::GREEN);
|
gizmos.line(contact.point, contact.point + Vec3::Y, LinearRgba::GREEN);
|
||||||
@@ -85,11 +83,8 @@ fn show_tile_heights(map: Res<Map>, mut gizmos: Gizmos, shape: Res<Shape>, tile_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn show_water_corners(pos: Vec3, gizmos: &mut Gizmos) {
|
||||||
fn show_water_corners(pos: Vec3, gizmos: &mut Gizmos)
|
for i in 0..WATER_HEX_CORNERS.len() {
|
||||||
{
|
|
||||||
for i in 0..WATER_HEX_CORNERS.len()
|
|
||||||
{
|
|
||||||
let p = pos + WATER_HEX_CORNERS[i];
|
let p = pos + WATER_HEX_CORNERS[i];
|
||||||
let p2 = pos + WATER_HEX_CORNERS[(i + 1) % WATER_HEX_CORNERS.len()];
|
let p2 = pos + WATER_HEX_CORNERS[(i + 1) % WATER_HEX_CORNERS.len()];
|
||||||
|
|
||||||
@@ -97,15 +92,14 @@ fn show_water_corners(pos: Vec3, gizmos: &mut Gizmos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn camera_debug(cam_query: Single<(&PhosCamera, &PhosOrbitCamera)>, mut gizmos: Gizmos)
|
fn camera_debug(mut cam_query: Query<(&PhosCamera, &PhosOrbitCamera)>, mut gizmos: Gizmos) {
|
||||||
{
|
let (config, orbit) = cam_query.single();
|
||||||
let (_config, orbit) = cam_query.into_inner();
|
|
||||||
|
|
||||||
gizmos.sphere(orbit.target, 0.3, LinearRgba::RED);
|
gizmos.sphere(orbit.target, Quat::IDENTITY, 0.3, LinearRgba::RED);
|
||||||
let cam_proxy = orbit.target - (orbit.forward * 10.0);
|
let cam_proxy = orbit.target - (orbit.forward * 10.0);
|
||||||
gizmos.ray(orbit.target, orbit.forward * 10.0, LinearRgba::rgb(1.0, 0.0, 1.0));
|
gizmos.ray(orbit.target, orbit.forward * 10.0, LinearRgba::rgb(1.0, 0.0, 1.0));
|
||||||
|
|
||||||
gizmos.circle(cam_proxy, 0.3, LinearRgba::rgb(1.0, 1.0, 0.0));
|
gizmos.circle(cam_proxy, Dir3::Y, 0.3, LinearRgba::rgb(1.0, 1.0, 0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verbose_data() {}
|
fn verbose_data() {}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use bevy::asset::RenderAssetUsages;
|
use bevy::{prelude::*, render::render_asset::RenderAssetUsages};
|
||||||
use bevy::prelude::*;
|
use bevy_inspector_egui::bevy_egui::EguiContexts;
|
||||||
use bevy_inspector_egui::bevy_egui::{EguiContexts, EguiTextureHandle};
|
|
||||||
use bevy_inspector_egui::egui::{self};
|
use bevy_inspector_egui::egui::{self};
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use world_generation::biome_asset::BiomeAsset;
|
use world_generation::biome_asset::BiomeAsset;
|
||||||
@@ -11,10 +10,8 @@ use world_generation::{map::map_utils::render_map, prelude::Map, states::Generat
|
|||||||
|
|
||||||
pub struct EditorPlugin;
|
pub struct EditorPlugin;
|
||||||
|
|
||||||
impl Plugin for EditorPlugin
|
impl Plugin for EditorPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.init_resource::<UIState>();
|
app.init_resource::<UIState>();
|
||||||
|
|
||||||
app.add_systems(PostUpdate, prepare_image.run_if(in_state(GeneratorState::SpawnMap)));
|
app.add_systems(PostUpdate, prepare_image.run_if(in_state(GeneratorState::SpawnMap)));
|
||||||
@@ -28,8 +25,7 @@ impl Plugin for EditorPlugin
|
|||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct MapImage(pub Handle<Image>);
|
struct MapImage(pub Handle<Image>);
|
||||||
|
|
||||||
pub fn prepare_image(mut images: ResMut<Assets<Image>>, heightmap: Res<Map>, mut commands: Commands)
|
pub fn prepare_image(mut images: ResMut<Assets<Image>>, heightmap: Res<Map>, mut commands: Commands) {
|
||||||
{
|
|
||||||
let image = render_map(&heightmap, 0.1);
|
let image = render_map(&heightmap, 0.1);
|
||||||
let handle = images.add(Image::from_dynamic(image.into(), true, RenderAssetUsages::RENDER_WORLD));
|
let handle = images.add(Image::from_dynamic(image.into(), true, RenderAssetUsages::RENDER_WORLD));
|
||||||
|
|
||||||
@@ -37,17 +33,14 @@ pub fn prepare_image(mut images: ResMut<Assets<Image>>, heightmap: Res<Map>, mut
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct UIState
|
struct UIState {
|
||||||
{
|
|
||||||
pub is_open: bool,
|
pub is_open: bool,
|
||||||
pub target_map_type: MapDisplayType,
|
pub target_map_type: MapDisplayType,
|
||||||
pub cur_map_type: MapDisplayType,
|
pub cur_map_type: MapDisplayType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UIState
|
impl Default for UIState {
|
||||||
{
|
fn default() -> Self {
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
is_open: true,
|
is_open: true,
|
||||||
target_map_type: default(),
|
target_map_type: default(),
|
||||||
@@ -57,8 +50,7 @@ impl Default for UIState
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
|
||||||
enum MapDisplayType
|
enum MapDisplayType {
|
||||||
{
|
|
||||||
#[default]
|
#[default]
|
||||||
HeightMap,
|
HeightMap,
|
||||||
Biomes,
|
Biomes,
|
||||||
@@ -69,23 +61,19 @@ enum MapDisplayType
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn asset_reloaded(
|
fn asset_reloaded(
|
||||||
mut asset_events: MessageReader<AssetEvent<BiomeAsset>>,
|
mut asset_events: EventReader<AssetEvent<BiomeAsset>>,
|
||||||
biomes: Res<Assets<BiomeAsset>>,
|
mut biomes: ResMut<Assets<BiomeAsset>>,
|
||||||
biome_painter: Res<BiomePainterAsset>,
|
biome_painter: Res<BiomePainterAsset>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
let mut rebuild = false;
|
let mut rebuild = false;
|
||||||
for event in asset_events.read()
|
for event in asset_events.read() {
|
||||||
{
|
match event {
|
||||||
match event
|
AssetEvent::Modified {..}=> rebuild = true,
|
||||||
{
|
|
||||||
AssetEvent::Modified { .. } => rebuild = true,
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rebuild
|
if rebuild {
|
||||||
{
|
|
||||||
let painter = biome_painter.build(&biomes);
|
let painter = biome_painter.build(&biomes);
|
||||||
commands.insert_resource(painter);
|
commands.insert_resource(painter);
|
||||||
}
|
}
|
||||||
@@ -97,11 +85,11 @@ fn render_map_ui(
|
|||||||
biome_map: Res<BiomeMap>,
|
biome_map: Res<BiomeMap>,
|
||||||
mut contexts: EguiContexts,
|
mut contexts: EguiContexts,
|
||||||
mut state: ResMut<UIState>,
|
mut state: ResMut<UIState>,
|
||||||
)
|
) {
|
||||||
{
|
let id = contexts.add_image(image.0.clone_weak());
|
||||||
let id = contexts.add_image(EguiTextureHandle::Weak(image.0.id()));
|
|
||||||
let mut map_type = state.target_map_type;
|
let mut map_type = state.target_map_type;
|
||||||
let ctx = contexts.ctx_mut().expect("Failed to get egui context");
|
let ctx = contexts.ctx_mut();
|
||||||
egui::Window::new("Map").open(&mut state.is_open).show(ctx, |ui| {
|
egui::Window::new("Map").open(&mut state.is_open).show(ctx, |ui| {
|
||||||
ui.label("Map Test");
|
ui.label("Map Test");
|
||||||
egui::ComboBox::from_label("Display Type")
|
egui::ComboBox::from_label("Display Type")
|
||||||
@@ -132,8 +120,7 @@ fn render_map_ui(
|
|||||||
[512.0, 512.0],
|
[512.0, 512.0],
|
||||||
)));
|
)));
|
||||||
|
|
||||||
if ui.button("Save Image").clicked()
|
if ui.button("Save Image").clicked() {
|
||||||
{
|
|
||||||
let img = get_map_image(&heightmap, &biome_map, map_type);
|
let img = get_map_image(&heightmap, &biome_map, map_type);
|
||||||
_ = img.save(format!("{:?}.png", map_type));
|
_ = img.save(format!("{:?}.png", map_type));
|
||||||
}
|
}
|
||||||
@@ -148,28 +135,22 @@ fn update_map_render(
|
|||||||
heightmap: Res<Map>,
|
heightmap: Res<Map>,
|
||||||
biome_map: Res<BiomeMap>,
|
biome_map: Res<BiomeMap>,
|
||||||
image: Res<MapImage>,
|
image: Res<MapImage>,
|
||||||
)
|
) {
|
||||||
{
|
if state.cur_map_type == state.target_map_type {
|
||||||
if state.cur_map_type == state.target_map_type
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = get_map_image(&heightmap, &biome_map, state.target_map_type);
|
let result = get_map_image(&heightmap, &biome_map, state.target_map_type);
|
||||||
images
|
images.insert(
|
||||||
.insert(
|
|
||||||
image.0.id(),
|
image.0.id(),
|
||||||
Image::from_dynamic(result.into(), true, RenderAssetUsages::RENDER_WORLD),
|
Image::from_dynamic(result.into(), true, RenderAssetUsages::RENDER_WORLD),
|
||||||
)
|
);
|
||||||
.expect("Failed to update map image");
|
|
||||||
|
|
||||||
state.cur_map_type = state.target_map_type;
|
state.cur_map_type = state.target_map_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_map_image(heightmap: &Map, biome_map: &BiomeMap, map_type: MapDisplayType) -> ImageBuffer<Rgba<u8>, Vec<u8>>
|
fn get_map_image(heightmap: &Map, biome_map: &BiomeMap, map_type: MapDisplayType) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
{
|
return match map_type {
|
||||||
return match map_type
|
|
||||||
{
|
|
||||||
MapDisplayType::HeightMap => render_map(&heightmap, 0.1),
|
MapDisplayType::HeightMap => render_map(&heightmap, 0.1),
|
||||||
MapDisplayType::Biomes => render_biome_map(&heightmap, &biome_map),
|
MapDisplayType::Biomes => render_biome_map(&heightmap, &biome_map),
|
||||||
MapDisplayType::BiomeNoise => render_biome_noise_map(&biome_map, Vec3::ONE),
|
MapDisplayType::BiomeNoise => render_biome_noise_map(&biome_map, Vec3::ONE),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pub mod chunk_utils;
|
pub mod chunk_utils;
|
||||||
pub mod debug_plugin;
|
pub mod debug_plugin;
|
||||||
#[cfg(feature = "editor")]
|
|
||||||
pub mod editor_plugin;
|
pub mod editor_plugin;
|
||||||
pub mod tile_selection_plugin;
|
pub mod tile_selection_plugin;
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
use bevy::{prelude::*, window::PrimaryWindow};
|
use bevy::{prelude::*, window::PrimaryWindow};
|
||||||
use bevy_rapier3d::{plugin::ReadRapierContext, prelude::QueryFilter};
|
use bevy_rapier3d::{plugin::RapierContext, prelude::QueryFilter};
|
||||||
use hex::prelude::*;
|
|
||||||
use shared::{
|
use shared::{
|
||||||
resources::{TileContact, TileUnderCursor},
|
resources::{TileContact, TileUnderCursor},
|
||||||
tags::MainCamera,
|
tags::MainCamera,
|
||||||
};
|
};
|
||||||
use world_generation::{prelude::Map, states::GeneratorState};
|
use world_generation::{hex_utils::HexCoord, prelude::Map, states::GeneratorState};
|
||||||
|
|
||||||
pub struct TileSelectionPlugin;
|
pub struct TileSelectionPlugin;
|
||||||
|
|
||||||
impl Plugin for TileSelectionPlugin
|
impl Plugin for TileSelectionPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.init_resource::<TileUnderCursor>();
|
app.init_resource::<TileUnderCursor>();
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
@@ -22,29 +18,28 @@ impl Plugin for TileSelectionPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_tile_under_cursor(
|
fn update_tile_under_cursor(
|
||||||
cam_query: Single<(&GlobalTransform, &Camera), With<MainCamera>>,
|
cam_query: Query<(&GlobalTransform, &Camera), With<MainCamera>>,
|
||||||
window: Single<&Window, With<PrimaryWindow>>,
|
window: Query<&Window, With<PrimaryWindow>>,
|
||||||
rapier: ReadRapierContext,
|
rapier_context: Res<RapierContext>,
|
||||||
map: Res<Map>,
|
map: Res<Map>,
|
||||||
mut tile_under_cursor: ResMut<TileUnderCursor>,
|
mut tile_under_cursor: ResMut<TileUnderCursor>,
|
||||||
)
|
) {
|
||||||
{
|
let win_r = window.get_single();
|
||||||
let (cam_transform, camera) = cam_query.into_inner();
|
if win_r.is_err() {
|
||||||
let Some(cursor_pos) = window.cursor_position()
|
return;
|
||||||
else
|
}
|
||||||
{
|
let win = win_r.unwrap();
|
||||||
|
|
||||||
|
let (cam_transform, camera) = cam_query.single();
|
||||||
|
let Some(cursor_pos) = win.cursor_position() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos)
|
let Some(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos) else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ctx = rapier.single().expect("Failed to get rapier read context");
|
let collision = rapier_context.cast_ray(
|
||||||
|
|
||||||
let collision = ctx.cast_ray(
|
|
||||||
cam_ray.origin,
|
cam_ray.origin,
|
||||||
cam_ray.direction.into(),
|
cam_ray.direction.into(),
|
||||||
500.,
|
500.,
|
||||||
@@ -52,13 +47,11 @@ fn update_tile_under_cursor(
|
|||||||
QueryFilter::only_fixed(),
|
QueryFilter::only_fixed(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some((_e, dist)) = collision
|
if let Some((_e, dist)) = collision {
|
||||||
{
|
|
||||||
let contact_point = cam_ray.get_point(dist);
|
let contact_point = cam_ray.get_point(dist);
|
||||||
let contact_coord = HexCoord::from_world_pos(contact_point);
|
let contact_coord = HexCoord::from_world_pos(contact_point);
|
||||||
//todo: handle correct tile detection when contacting a tile from the side
|
//todo: handle correct tile detection when contacting a tile from the side
|
||||||
if !map.is_in_bounds(&contact_coord)
|
if !map.is_in_bounds(&contact_coord) {
|
||||||
{
|
|
||||||
tile_under_cursor.0 = None;
|
tile_under_cursor.0 = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -68,9 +61,7 @@ fn update_tile_under_cursor(
|
|||||||
contact_point,
|
contact_point,
|
||||||
contact_coord.to_world(surface),
|
contact_coord.to_world(surface),
|
||||||
));
|
));
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
tile_under_cursor.0 = None;
|
tile_under_cursor.0 = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.18.0"
|
bevy = "0.14.2"
|
||||||
world_generation = { path = "../../engine/world_generation" }
|
world_generation = { path = "../../engine/world_generation" }
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
asset_loader = { path = "../../engine/asset_loader" }
|
asset_loader = { path = "../../engine/asset_loader" }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.120"
|
||||||
ron = "0.12.0"
|
ron = "0.8.1"
|
||||||
bevy_asset_loader = { version = "0.25.0", features = [
|
bevy_asset_loader = { version = "0.21.0", features = [
|
||||||
"standard_dynamic_assets",
|
"standard_dynamic_assets",
|
||||||
"3d",
|
"3d",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.18.0"
|
bevy = "0.14.2"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
world_generation = { path = "../../engine/world_generation" }
|
world_generation = { path = "../../engine/world_generation" }
|
||||||
hex = { path = "../../engine/hex" }
|
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::prefab_defination::RotationAnimation;
|
|
||||||
|
|
||||||
pub struct SimpleAnimationPlugin;
|
|
||||||
|
|
||||||
impl Plugin for SimpleAnimationPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_systems(Update, rotate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rotate(mut query: Query<(&mut Transform, &RotationAnimation)>, time: Res<Time>) {
|
|
||||||
for (mut transform, rot) in query.iter_mut() {
|
|
||||||
let cur_rot = transform.rotation;
|
|
||||||
transform.rotation = cur_rot * Quat::from_axis_angle(rot.axis, rot.speed.to_radians() * time.delta_secs());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use bevy::reflect::Reflect;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Default, Reflect, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct BuildingIdentifier(pub usize);
|
pub struct BuildingIdentifier(pub usize);
|
||||||
|
|
||||||
impl From<i32> for BuildingIdentifier {
|
impl From<i32> for BuildingIdentifier {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
use bevy::{ecs::system::EntityCommands, prelude::*};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::prefab_defination::AnimationComponent;
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct ComponentDefination
|
|
||||||
{
|
|
||||||
pub path: String,
|
|
||||||
pub animations: Vec<AnimationComponent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentDefination
|
|
||||||
{
|
|
||||||
pub fn apply(&self, commands: &mut EntityCommands)
|
|
||||||
{
|
|
||||||
for c in &self.animations {
|
|
||||||
c.apply(commands);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
use hex::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Reflect)]
|
|
||||||
pub struct CoordsCollection
|
|
||||||
{
|
|
||||||
points: Vec<IVec2>,
|
|
||||||
origin: IVec2,
|
|
||||||
translation: IVec2,
|
|
||||||
rotation: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CoordsCollection
|
|
||||||
{
|
|
||||||
pub fn from_hex(coords: Vec<HexCoord>) -> Self
|
|
||||||
{
|
|
||||||
CoordsCollection {
|
|
||||||
points: coords.iter().map(|c| c.hex.xy()).collect(),
|
|
||||||
..default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_points(points: Vec<IVec2>) -> Self
|
|
||||||
{
|
|
||||||
CoordsCollection { points, ..default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_translation(mut self, translation: &HexCoord) -> Self
|
|
||||||
{
|
|
||||||
self.translation = translation.hex.xy();
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_translation_vec(mut self, translation: IVec2) -> Self
|
|
||||||
{
|
|
||||||
self.translation = translation;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_origin(mut self, orign: &HexCoord) -> Self
|
|
||||||
{
|
|
||||||
self.origin = orign.hex.xy();
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_rotation(mut self, rotation: i32) -> Self
|
|
||||||
{
|
|
||||||
self.rotation = rotation;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_coords(&self) -> Vec<HexCoord>
|
|
||||||
{
|
|
||||||
let center = HexCoord::from_axial(self.origin);
|
|
||||||
return self
|
|
||||||
.points
|
|
||||||
.iter()
|
|
||||||
.map(|p| HexCoord::from_axial(p + self.origin).rotate_around(¢er, self.rotation))
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Vec<HexCoord>> for CoordsCollection
|
|
||||||
{
|
|
||||||
fn into(self) -> Vec<HexCoord>
|
|
||||||
{
|
|
||||||
self.get_coords()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ impl Plugin for DespawnPuglin {
|
|||||||
|
|
||||||
fn despawn_at(mut commands: Commands, time: Res<Time>, entities: Query<(Entity, &DespawnAt), Without<DespawnAfter>>) {
|
fn despawn_at(mut commands: Commands, time: Res<Time>, entities: Query<(Entity, &DespawnAt), Without<DespawnAfter>>) {
|
||||||
for (entity, at) in entities.iter() {
|
for (entity, at) in entities.iter() {
|
||||||
let d = at.0 - time.elapsed_secs();
|
let d = at.0 - time.elapsed_seconds();
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
.insert(DespawnAfter(Timer::from_seconds(d, TimerMode::Once)));
|
.insert(DespawnAfter(Timer::from_seconds(d, TimerMode::Once)));
|
||||||
@@ -34,7 +34,7 @@ fn despawn_at(mut commands: Commands, time: Res<Time>, entities: Query<(Entity,
|
|||||||
fn despawn_after(mut commands: Commands, mut entities: Query<(&mut DespawnAfter, Entity)>, time: Res<Time>) {
|
fn despawn_after(mut commands: Commands, mut entities: Query<(&mut DespawnAfter, Entity)>, time: Res<Time>) {
|
||||||
for (mut after, entity) in &mut entities.iter_mut() {
|
for (mut after, entity) in &mut entities.iter_mut() {
|
||||||
after.0.tick(time.delta());
|
after.0.tick(time.delta());
|
||||||
if after.0.is_finished() {
|
if after.0.finished() {
|
||||||
commands.entity(entity).despawn();
|
commands.entity(entity).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hex::prelude::*;
|
use world_generation::hex_utils::*;
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Event)]
|
||||||
pub enum TileModifiedEvent
|
pub enum TileModifiedEvent {
|
||||||
{
|
|
||||||
HeightChanged(HexCoord, f32),
|
HeightChanged(HexCoord, f32),
|
||||||
TypeChanged(HexCoord, usize),
|
TypeChanged(HexCoord, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Event)]
|
||||||
pub struct ChunkModifiedEvent
|
pub struct ChunkModifiedEvent {
|
||||||
{
|
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
use bevy::reflect::Reflect;
|
use bevy::prelude::Resource;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use world_generation::hex_utils::HexCoord;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct ResourceIdentifier
|
pub struct ResourceIdentifier {
|
||||||
{
|
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub qty: u32,
|
pub qty: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
|
||||||
pub struct UnitIdentifier(u32);
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
|
||||||
pub struct TileIdentifier(u32);
|
|
||||||
|
|||||||
@@ -1,40 +1,19 @@
|
|||||||
use bevy::reflect::Reflect;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod animation_plugin;
|
|
||||||
pub mod building;
|
pub mod building;
|
||||||
pub mod component_defination;
|
|
||||||
pub mod coords;
|
|
||||||
pub mod despawn;
|
pub mod despawn;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod identifiers;
|
pub mod identifiers;
|
||||||
pub mod prefab_defination;
|
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
pub mod sets;
|
pub mod sets;
|
||||||
pub mod states;
|
pub mod states;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum Tier
|
pub enum Tier {
|
||||||
{
|
|
||||||
Zero,
|
Zero,
|
||||||
One,
|
One,
|
||||||
Two,
|
Two,
|
||||||
Three,
|
Three,
|
||||||
Superior,
|
Superior,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
|
||||||
pub enum StatusEffect
|
|
||||||
{
|
|
||||||
UnitRange(f32),
|
|
||||||
UnitAttack(f32),
|
|
||||||
UnitHealth(f32),
|
|
||||||
StructureRange(f32),
|
|
||||||
StructureAttack(f32),
|
|
||||||
StructureHealth(f32),
|
|
||||||
BuildSpeedMulti(f32),
|
|
||||||
BuildCostMulti(f32),
|
|
||||||
ConsumptionMulti(f32),
|
|
||||||
ProductionMulti(f32),
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
use bevy::{
|
|
||||||
ecs::{relationship::RelatedSpawnerCommands, system::EntityCommands},
|
|
||||||
gltf::{Gltf, GltfMesh},
|
|
||||||
math::{Quat, Vec3},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct PrefabDefination
|
|
||||||
{
|
|
||||||
pub path: String,
|
|
||||||
pub pos: Vec3,
|
|
||||||
pub rot: Vec3,
|
|
||||||
pub children: Option<Vec<PrefabDefination>>,
|
|
||||||
pub animations: Option<Vec<AnimationComponent>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrefabDefination
|
|
||||||
{
|
|
||||||
pub fn spawn_recursive(
|
|
||||||
&self,
|
|
||||||
gltf: &Gltf,
|
|
||||||
commands: &mut RelatedSpawnerCommands<ChildOf>,
|
|
||||||
meshes: &Assets<GltfMesh>,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
let mesh_handle = &gltf.named_meshes[&self.path.clone().into_boxed_str()];
|
|
||||||
if let Some(gltf_mesh) = meshes.get(mesh_handle.id()) {
|
|
||||||
if let Some(primitive) = gltf_mesh.primitives.first() {
|
|
||||||
let mesh = primitive.mesh.clone();
|
|
||||||
let mat = primitive
|
|
||||||
.material
|
|
||||||
.clone()
|
|
||||||
.expect(format!("Mesh '{}' does not have a meterial", primitive.name.as_str()).as_str());
|
|
||||||
let mut entity = commands.spawn((
|
|
||||||
Mesh3d(mesh),
|
|
||||||
MeshMaterial3d(mat),
|
|
||||||
Transform::from_translation(self.pos).with_rotation(Quat::from_euler(
|
|
||||||
bevy::math::EulerRot::XYZ,
|
|
||||||
self.rot.x,
|
|
||||||
self.rot.y,
|
|
||||||
self.rot.z,
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
if let Some(children) = &self.children {
|
|
||||||
entity.with_children(|b| {
|
|
||||||
for child in children {
|
|
||||||
child.spawn_recursive(gltf, b, meshes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait UnpackGltfMesh
|
|
||||||
{
|
|
||||||
fn unpack(&self) -> (Handle<Mesh>, Handle<StandardMaterial>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnpackGltfMesh for GltfMesh
|
|
||||||
{
|
|
||||||
fn unpack(&self) -> (Handle<Mesh>, Handle<StandardMaterial>)
|
|
||||||
{
|
|
||||||
let p = &self.primitives[0];
|
|
||||||
let mut mat: Handle<StandardMaterial> = default();
|
|
||||||
if let Some(mesh_material) = &p.material {
|
|
||||||
mat = mesh_material.clone();
|
|
||||||
}
|
|
||||||
return (p.mesh.clone(), mat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub enum AnimationComponent
|
|
||||||
{
|
|
||||||
Rotation(RotationAnimation),
|
|
||||||
Slider,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Component, Clone, Copy)]
|
|
||||||
pub struct RotationAnimation
|
|
||||||
{
|
|
||||||
pub axis: Vec3,
|
|
||||||
pub speed: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnimationComponent
|
|
||||||
{
|
|
||||||
pub fn apply(&self, commands: &mut EntityCommands)
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
AnimationComponent::Rotation(comp) => {
|
|
||||||
commands.insert(comp.clone());
|
|
||||||
}
|
|
||||||
AnimationComponent::Slider => todo!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,18 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hex::prelude::*;
|
use world_generation::hex_utils::HexCoord;
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub struct TileUnderCursor(pub Option<TileContact>);
|
pub struct TileUnderCursor(pub Option<TileContact>);
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct TileContact
|
pub struct TileContact {
|
||||||
{
|
|
||||||
pub tile: HexCoord,
|
pub tile: HexCoord,
|
||||||
pub point: Vec3,
|
pub point: Vec3,
|
||||||
pub surface: Vec3,
|
pub surface: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileContact
|
impl TileContact {
|
||||||
{
|
pub fn new(tile: HexCoord, contact: Vec3, surface: Vec3) -> Self {
|
||||||
pub fn new(tile: HexCoord, contact: Vec3, surface: Vec3) -> Self
|
|
||||||
{
|
|
||||||
return Self {
|
return Self {
|
||||||
tile,
|
tile,
|
||||||
point: contact,
|
point: contact,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum MenuState
|
pub enum MenuState {
|
||||||
{
|
|
||||||
Loading,
|
Loading,
|
||||||
Startup,
|
Startup,
|
||||||
MainMenu,
|
MainMenu,
|
||||||
@@ -11,16 +10,14 @@ pub enum MenuState
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum GameplayState
|
pub enum GameplayState {
|
||||||
{
|
|
||||||
Waiting,
|
Waiting,
|
||||||
PlaceHQ,
|
PlaceHQ,
|
||||||
Playing,
|
Playing,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum AssetLoadState
|
pub enum AssetLoadState {
|
||||||
{
|
|
||||||
Loading,
|
Loading,
|
||||||
FinalizeAssets,
|
FinalizeAssets,
|
||||||
LoadComplete,
|
LoadComplete,
|
||||||
|
|||||||
@@ -4,22 +4,21 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.18.0"
|
bevy = "0.14.2"
|
||||||
world_generation = { path = "../../engine/world_generation" }
|
world_generation = { path = "../../engine/world_generation" }
|
||||||
hex = { path = "../../engine/hex" }
|
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
bevy_rapier3d = "0.33.0"
|
bevy_rapier3d = "0.27.0"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
asset_loader = { path = "../../engine/asset_loader" }
|
asset_loader = { path = "../../engine/asset_loader" }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.120"
|
||||||
ron = "0.12.0"
|
ron = "0.8.1"
|
||||||
bevy_asset_loader = { version = "0.25.0", features = [
|
bevy_asset_loader = { version = "0.21.0", features = [
|
||||||
"standard_dynamic_assets",
|
"standard_dynamic_assets",
|
||||||
"3d",
|
"3d",
|
||||||
] }
|
] }
|
||||||
quadtree_rs = "0.1.3"
|
quadtree_rs = "0.1.3"
|
||||||
pathfinding = "4.14.0"
|
pathfinding = "4.11.0"
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "4.3.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tracing = ["bevy/trace_tracy"]
|
tracing = ["bevy/trace_tracy"]
|
||||||
|
|||||||
@@ -20,7 +20,13 @@ impl UnitAsset {
|
|||||||
pub fn spawn(&self, transform: Transform) -> CommandQueue {
|
pub fn spawn(&self, transform: Transform) -> CommandQueue {
|
||||||
let mut commands = CommandQueue::default();
|
let mut commands = CommandQueue::default();
|
||||||
|
|
||||||
let bundle = (transform, Unit);
|
let bundle = (
|
||||||
|
PbrBundle {
|
||||||
|
transform: transform,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Unit,
|
||||||
|
);
|
||||||
let domain = self.domain.clone();
|
let domain = self.domain.clone();
|
||||||
commands.push(move |world: &mut World| {
|
commands.push(move |world: &mut World| {
|
||||||
let mut e = world.spawn(bundle);
|
let mut e = world.spawn(bundle);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use bevy::{ecs::world::CommandQueue, prelude::*, tasks::Task};
|
use bevy::{ecs::world::CommandQueue, prelude::*, tasks::Task};
|
||||||
use hex::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use world_generation::hex_utils::HexCoord;
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub struct Unit;
|
pub struct Unit;
|
||||||
@@ -13,8 +13,7 @@ pub struct LandUnit;
|
|||||||
pub struct NavalUnit;
|
pub struct NavalUnit;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
pub enum UnitDomain
|
pub enum UnitDomain {
|
||||||
{
|
|
||||||
Land,
|
Land,
|
||||||
Air,
|
Air,
|
||||||
Naval,
|
Naval,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
pub mod assets;
|
pub mod assets;
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod nav_data;
|
pub mod nav_data;
|
||||||
@@ -8,7 +9,6 @@ pub mod units_plugin;
|
|||||||
pub mod units_spacial_set;
|
pub mod units_spacial_set;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum UnitType
|
pub enum UnitType {
|
||||||
{
|
|
||||||
Basic,
|
Basic,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hex::prelude::*;
|
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use world_generation::prelude::Map;
|
use world_generation::{hex_utils::HexCoord, prelude::Map};
|
||||||
|
|
||||||
#[derive(Clone, Resource)]
|
#[derive(Clone, Resource)]
|
||||||
pub struct NavData
|
pub struct NavData {
|
||||||
{
|
|
||||||
pub tiles: Vec<NavTile>,
|
pub tiles: Vec<NavTile>,
|
||||||
pub map_height: usize,
|
pub map_height: usize,
|
||||||
pub map_width: usize,
|
pub map_width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavData
|
impl NavData {
|
||||||
{
|
pub fn get_neighbors(&self, coord: &HexCoord) -> Vec<(HexCoord, OrderedFloat<f32>)> {
|
||||||
pub fn get_neighbors(&self, coord: &HexCoord) -> Vec<(HexCoord, OrderedFloat<f32>)>
|
|
||||||
{
|
|
||||||
let mut neighbors = Vec::with_capacity(6);
|
let mut neighbors = Vec::with_capacity(6);
|
||||||
let cur_height = self.get_height(coord);
|
let cur_height = self.get_height(coord);
|
||||||
for i in 0..6
|
for i in 0..6 {
|
||||||
{
|
|
||||||
let n = coord.get_neighbor(i);
|
let n = coord.get_neighbor(i);
|
||||||
if !self.is_in_bounds(&n)
|
if !self.is_in_bounds(&n) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let n_height = self.get_height(&n);
|
let n_height = self.get_height(&n);
|
||||||
@@ -29,33 +23,27 @@ impl NavData
|
|||||||
}
|
}
|
||||||
return neighbors;
|
return neighbors;
|
||||||
}
|
}
|
||||||
pub fn get(&self, coord: &HexCoord) -> &NavTile
|
pub fn get(&self, coord: &HexCoord) -> &NavTile {
|
||||||
{
|
|
||||||
return &self.tiles[coord.to_index(self.map_width)];
|
return &self.tiles[coord.to_index(self.map_width)];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_height(&self, coord: &HexCoord) -> f32
|
pub fn get_height(&self, coord: &HexCoord) -> f32 {
|
||||||
{
|
|
||||||
return self.tiles[coord.to_index(self.map_width)].height;
|
return self.tiles[coord.to_index(self.map_width)].height;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool
|
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool {
|
||||||
{
|
|
||||||
return pos.is_in_bounds(self.map_height, self.map_width);
|
return pos.is_in_bounds(self.map_height, self.map_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(map: &Map) -> NavData
|
pub fn build(map: &Map) -> NavData {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _path_span = info_span!("Build Nav Data").entered();
|
let _path_span = info_span!("Build Nav Data").entered();
|
||||||
let mut tiles = Vec::with_capacity(map.get_tile_count());
|
let mut tiles = Vec::with_capacity(map.get_tile_count());
|
||||||
let h = map.get_tile_height();
|
let h = map.get_tile_height();
|
||||||
let w = map.get_tile_width();
|
let w = map.get_tile_width();
|
||||||
for y in 0..h
|
for y in 0..h {
|
||||||
{
|
for x in 0..w {
|
||||||
for x in 0..w
|
let coord = HexCoord::from_grid_pos(x, y);
|
||||||
{
|
|
||||||
let coord = HexCoord::from_offset_pos(x, y);
|
|
||||||
let height = map.sample_height(&coord);
|
let height = map.sample_height(&coord);
|
||||||
let tile = NavTile {
|
let tile = NavTile {
|
||||||
coord,
|
coord,
|
||||||
@@ -73,17 +61,14 @@ impl NavData
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, map: &Map)
|
pub fn update(&mut self, map: &Map) {
|
||||||
{
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _path_span = info_span!("Update Nav Data").entered();
|
let _path_span = info_span!("Update Nav Data").entered();
|
||||||
let h = map.get_tile_height();
|
let h = map.get_tile_height();
|
||||||
let w = map.get_tile_width();
|
let w = map.get_tile_width();
|
||||||
for y in 0..h
|
for y in 0..h {
|
||||||
{
|
for x in 0..w {
|
||||||
for x in 0..w
|
let coord = HexCoord::from_grid_pos(x, y);
|
||||||
{
|
|
||||||
let coord = HexCoord::from_offset_pos(x, y);
|
|
||||||
let height = map.sample_height(&coord);
|
let height = map.sample_height(&coord);
|
||||||
let tile = NavTile {
|
let tile = NavTile {
|
||||||
coord,
|
coord,
|
||||||
@@ -94,8 +79,7 @@ impl NavData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update_tile(&mut self, coord: &HexCoord, height: f32, move_cost: f32)
|
pub fn update_tile(&mut self, coord: &HexCoord, height: f32, move_cost: f32) {
|
||||||
{
|
|
||||||
let tile = &mut self.tiles[coord.to_index(self.map_width)];
|
let tile = &mut self.tiles[coord.to_index(self.map_width)];
|
||||||
tile.move_cost = move_cost;
|
tile.move_cost = move_cost;
|
||||||
tile.height = height;
|
tile.height = height;
|
||||||
@@ -103,17 +87,14 @@ impl NavData
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NavTile
|
pub struct NavTile {
|
||||||
{
|
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
pub move_cost: f32,
|
pub move_cost: f32,
|
||||||
pub coord: HexCoord,
|
pub coord: HexCoord,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavTile
|
impl NavTile {
|
||||||
{
|
pub fn calculate_heuristic(&self, to: &HexCoord) -> OrderedFloat<f32> {
|
||||||
pub fn calculate_heuristic(&self, to: &HexCoord) -> OrderedFloat<f32>
|
|
||||||
{
|
|
||||||
return (self.coord.distance(to) as f32).into();
|
return (self.coord.distance(to) as f32).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ fn spawn_test_unit(
|
|||||||
if let Some(contact) = tile_under_cursor.0 {
|
if let Some(contact) = tile_under_cursor.0 {
|
||||||
info!("Spawning Test Unit");
|
info!("Spawning Test Unit");
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
(Transform::from_translation(contact.surface), Mesh3d(unit.0.clone())),
|
PbrBundle {
|
||||||
|
transform: Transform::from_translation(contact.surface),
|
||||||
|
mesh: unit.0.clone(),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
Unit,
|
Unit,
|
||||||
LandUnit,
|
LandUnit,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{ecs::world::CommandQueue, prelude::*, tasks::AsyncComputeTaskPool, utils::futures};
|
||||||
ecs::world::CommandQueue,
|
|
||||||
prelude::*,
|
|
||||||
tasks::{futures, AsyncComputeTaskPool},
|
|
||||||
};
|
|
||||||
use hex::prelude::*;
|
|
||||||
use pathfinding::prelude::astar;
|
use pathfinding::prelude::astar;
|
||||||
use shared::{events::TileModifiedEvent, resources::TileUnderCursor, sets::GameplaySet};
|
use shared::{events::TileModifiedEvent, resources::TileUnderCursor, sets::GameplaySet};
|
||||||
use world_generation::{prelude::Map, states::GeneratorState};
|
use world_generation::{hex_utils::HexCoord, prelude::Map, states::GeneratorState};
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use crate::units_debug_plugin::UnitsDebugPlugin;
|
use crate::units_debug_plugin::UnitsDebugPlugin;
|
||||||
@@ -21,10 +16,8 @@ use crate::{
|
|||||||
|
|
||||||
pub struct UnitsPlugin;
|
pub struct UnitsPlugin;
|
||||||
|
|
||||||
impl Plugin for UnitsPlugin
|
impl Plugin for UnitsPlugin {
|
||||||
{
|
fn build(&self, app: &mut App) {
|
||||||
fn build(&self, app: &mut App)
|
|
||||||
{
|
|
||||||
app.init_resource::<PathBatchId>();
|
app.init_resource::<PathBatchId>();
|
||||||
app.add_plugins(UnitAssetPlugin);
|
app.add_plugins(UnitAssetPlugin);
|
||||||
|
|
||||||
@@ -43,20 +36,15 @@ impl Plugin for UnitsPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_navdata(mut commands: Commands, map: Res<Map>)
|
fn build_navdata(mut commands: Commands, map: Res<Map>) {
|
||||||
{
|
|
||||||
let nav_data = NavData::build(&map);
|
let nav_data = NavData::build(&map);
|
||||||
commands.insert_resource(nav_data);
|
commands.insert_resource(nav_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_navdata(mut tile_updates: MessageReader<TileModifiedEvent>, mut nav_data: ResMut<NavData>)
|
fn update_navdata(mut tile_updates: EventReader<TileModifiedEvent>, mut nav_data: ResMut<NavData>) {
|
||||||
{
|
for event in tile_updates.read() {
|
||||||
for event in tile_updates.read()
|
match event {
|
||||||
{
|
TileModifiedEvent::HeightChanged(coord, new_height) => {
|
||||||
match event
|
|
||||||
{
|
|
||||||
TileModifiedEvent::HeightChanged(coord, new_height) =>
|
|
||||||
{
|
|
||||||
nav_data.update_tile(coord, *new_height, 1.0);
|
nav_data.update_tile(coord, *new_height, 1.0);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -64,7 +52,6 @@ fn update_navdata(mut tile_updates: MessageReader<TileModifiedEvent>, mut nav_da
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn units_control(tile_under_cursor: Res<TileUnderCursor>) {}
|
fn units_control(tile_under_cursor: Res<TileUnderCursor>) {}
|
||||||
|
|
||||||
fn move_unit(
|
fn move_unit(
|
||||||
@@ -72,27 +59,22 @@ fn move_unit(
|
|||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
map: Res<Map>,
|
map: Res<Map>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
)
|
) {
|
||||||
{
|
for (mut t, mut path, entity) in units.iter_mut() {
|
||||||
for (mut t, mut path, entity) in units.iter_mut()
|
if path.1 >= path.0.len() {
|
||||||
{
|
|
||||||
if path.1 >= path.0.len()
|
|
||||||
{
|
|
||||||
commands.entity(entity).remove::<Path>();
|
commands.entity(entity).remove::<Path>();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let p = path.0[path.1];
|
let p = path.0[path.1];
|
||||||
let d = p - t.translation;
|
let d = p - t.translation;
|
||||||
if d.length() < 0.1
|
if d.length() < 0.1 {
|
||||||
{
|
|
||||||
path.1 += 1;
|
path.1 += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let vel = d.normalize() * 10.0 * time.delta_secs();
|
let vel = d.normalize() * 10.0 * time.delta_seconds();
|
||||||
t.translation += vel;
|
t.translation += vel;
|
||||||
let coord = HexCoord::from_world_pos(t.translation);
|
let coord = HexCoord::from_world_pos(t.translation);
|
||||||
if map.is_in_bounds(&coord)
|
if map.is_in_bounds(&coord) {
|
||||||
{
|
|
||||||
t.translation.y = map.sample_height(&coord);
|
t.translation.y = map.sample_height(&coord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,28 +86,22 @@ fn dispatch_path_requests(
|
|||||||
nav_data: Res<NavData>,
|
nav_data: Res<NavData>,
|
||||||
mut batch_id: ResMut<PathBatchId>,
|
mut batch_id: ResMut<PathBatchId>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
)
|
) {
|
||||||
{
|
if units.is_empty() {
|
||||||
if units.is_empty()
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut groups: HashMap<HexCoord, Vec<PathRequest>> = HashMap::new();
|
let mut groups: HashMap<HexCoord, Vec<PathRequest>> = HashMap::new();
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _group_span = info_span!("Grouping").entered();
|
let _group_span = info_span!("Grouping").entered();
|
||||||
for (transform, target, entity) in units.iter()
|
for (transform, target, entity) in units.iter() {
|
||||||
{
|
|
||||||
let req = PathRequest {
|
let req = PathRequest {
|
||||||
entity,
|
entity,
|
||||||
from: HexCoord::from_world_pos(transform.translation),
|
from: HexCoord::from_world_pos(transform.translation),
|
||||||
};
|
};
|
||||||
if let Some(group) = groups.get_mut(&target.0)
|
if let Some(group) = groups.get_mut(&target.0) {
|
||||||
{
|
|
||||||
group.push(req);
|
group.push(req);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
groups.insert(target.0, vec![req]);
|
groups.insert(target.0, vec![req]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,13 +109,11 @@ fn dispatch_path_requests(
|
|||||||
drop(_group_span);
|
drop(_group_span);
|
||||||
|
|
||||||
let pool = AsyncComputeTaskPool::get();
|
let pool = AsyncComputeTaskPool::get();
|
||||||
for (target, units) in groups
|
for (target, units) in groups {
|
||||||
{
|
|
||||||
let id = batch_id.0;
|
let id = batch_id.0;
|
||||||
batch_id.0 += 1;
|
batch_id.0 += 1;
|
||||||
|
|
||||||
for req in &units
|
for req in &units {
|
||||||
{
|
|
||||||
commands
|
commands
|
||||||
.entity(req.entity)
|
.entity(req.entity)
|
||||||
.insert(PathTaskPending(id))
|
.insert(PathTaskPending(id))
|
||||||
@@ -158,21 +132,17 @@ fn dispatch_path_requests(
|
|||||||
let batch_task = pool.spawn(async move {
|
let batch_task = pool.spawn(async move {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
let mut queue = CommandQueue::default();
|
let mut queue = CommandQueue::default();
|
||||||
for entitiy_req in req.entities
|
for entitiy_req in req.entities {
|
||||||
{
|
|
||||||
let dst = req.destination[i];
|
let dst = req.destination[i];
|
||||||
i += 1;
|
i += 1;
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _path_span = info_span!("Path Finding").entered();
|
let _path_span = info_span!("Path Finding").entered();
|
||||||
if let Some(path) = calculate_path(&entitiy_req.from, &dst, &local_nav_data)
|
if let Some(path) = calculate_path(&entitiy_req.from, &dst, &local_nav_data) {
|
||||||
{
|
|
||||||
queue.push(move |world: &mut World| {
|
queue.push(move |world: &mut World| {
|
||||||
let mut unit_e = world.entity_mut(entitiy_req.entity);
|
let mut unit_e = world.entity_mut(entitiy_req.entity);
|
||||||
|
|
||||||
if let Some(pending_task) = unit_e.get::<PathTaskPending>()
|
if let Some(pending_task) = unit_e.get::<PathTaskPending>() {
|
||||||
{
|
if pending_task.0 == id {
|
||||||
if pending_task.0 == id
|
|
||||||
{
|
|
||||||
unit_e.insert(path);
|
unit_e.insert(path);
|
||||||
unit_e.remove::<PathTaskPending>();
|
unit_e.remove::<PathTaskPending>();
|
||||||
}
|
}
|
||||||
@@ -180,8 +150,7 @@ fn dispatch_path_requests(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if queue.is_empty()
|
if queue.is_empty() {
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
return Some(queue);
|
return Some(queue);
|
||||||
@@ -190,36 +159,26 @@ fn dispatch_path_requests(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_end_points(coord: &HexCoord, count: usize, map: &Map) -> Vec<HexCoord>
|
fn get_end_points(coord: &HexCoord, count: usize, map: &Map) -> Vec<HexCoord> {
|
||||||
{
|
|
||||||
let mut result = Vec::with_capacity(count);
|
let mut result = Vec::with_capacity(count);
|
||||||
if count == 1
|
if count == 1 {
|
||||||
{
|
|
||||||
return vec![*coord];
|
return vec![*coord];
|
||||||
}
|
}
|
||||||
result.push(*coord);
|
result.push(*coord);
|
||||||
let mut r = 1;
|
let mut r = 1;
|
||||||
while result.len() < count
|
while result.len() < count {
|
||||||
{
|
|
||||||
let tiles = HexCoord::select_ring(coord, r);
|
let tiles = HexCoord::select_ring(coord, r);
|
||||||
let needed = count - result.len();
|
let needed = count - result.len();
|
||||||
if needed >= tiles.len()
|
if needed >= tiles.len() {
|
||||||
{
|
for t in tiles {
|
||||||
for t in tiles
|
if map.is_in_bounds(&t) {
|
||||||
{
|
|
||||||
if map.is_in_bounds(&t)
|
|
||||||
{
|
|
||||||
result.push(t);
|
result.push(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
for i in 0..needed {
|
||||||
{
|
|
||||||
for i in 0..needed
|
|
||||||
{
|
|
||||||
let t = tiles[i];
|
let t = tiles[i];
|
||||||
if map.is_in_bounds(&t)
|
if map.is_in_bounds(&t) {
|
||||||
{
|
|
||||||
result.push(t);
|
result.push(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,14 +189,10 @@ fn get_end_points(coord: &HexCoord, count: usize, map: &Map) -> Vec<HexCoord>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_path_task(mut tasks: Query<(&mut PathTask, Entity)>, mut commands: Commands)
|
fn resolve_path_task(mut tasks: Query<(&mut PathTask, Entity)>, mut commands: Commands) {
|
||||||
{
|
for (mut task, entity) in tasks.iter_mut() {
|
||||||
for (mut task, entity) in tasks.iter_mut()
|
if let Some(c) = futures::check_ready(&mut task.0) {
|
||||||
{
|
if let Some(mut queue) = c {
|
||||||
if let Some(c) = futures::check_ready(&mut task.0)
|
|
||||||
{
|
|
||||||
if let Some(mut queue) = c
|
|
||||||
{
|
|
||||||
commands.append(&mut queue);
|
commands.append(&mut queue);
|
||||||
}
|
}
|
||||||
commands.entity(entity).despawn();
|
commands.entity(entity).despawn();
|
||||||
@@ -245,38 +200,32 @@ fn resolve_path_task(mut tasks: Query<(&mut PathTask, Entity)>, mut commands: Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_path(from: &HexCoord, to: &HexCoord, nav: &NavData) -> Option<Path>
|
fn calculate_path(from: &HexCoord, to: &HexCoord, nav: &NavData) -> Option<Path> {
|
||||||
{
|
|
||||||
let path = astar(
|
let path = astar(
|
||||||
from,
|
from,
|
||||||
|n| nav.get_neighbors(n),
|
|n| nav.get_neighbors(n),
|
||||||
|n| nav.get(n).calculate_heuristic(to),
|
|n| nav.get(n).calculate_heuristic(to),
|
||||||
|n| n == to,
|
|n| n == to,
|
||||||
);
|
);
|
||||||
if let Some((nodes, _cost)) = path
|
if let Some((nodes, _cost)) = path {
|
||||||
{
|
|
||||||
let result: Vec<_> = nodes.iter().map(|f| f.to_world(nav.get_height(f))).collect();
|
let result: Vec<_> = nodes.iter().map(|f| f.to_world(nav.get_height(f))).collect();
|
||||||
return Some(Path(result, 1));
|
return Some(Path(result, 1));
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PathRequest
|
struct PathRequest {
|
||||||
{
|
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub from: HexCoord,
|
pub from: HexCoord,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BatchPathRequest
|
struct BatchPathRequest {
|
||||||
{
|
|
||||||
pub entities: Vec<PathRequest>,
|
pub entities: Vec<PathRequest>,
|
||||||
pub destination: Vec<HexCoord>,
|
pub destination: Vec<HexCoord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatchPathRequest
|
impl BatchPathRequest {
|
||||||
{
|
pub fn new(entities: Vec<PathRequest>, dst: Vec<HexCoord>) -> Self {
|
||||||
pub fn new(entities: Vec<PathRequest>, dst: Vec<HexCoord>) -> Self
|
|
||||||
{
|
|
||||||
return Self {
|
return Self {
|
||||||
destination: dst,
|
destination: dst,
|
||||||
entities,
|
entities,
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
max_width = 120
|
max_width = 120
|
||||||
brace_style = "AlwaysNextLine"
|
brace_style = "AlwaysNextLine"
|
||||||
|
control_brace_style = "AlwaysNextLine"
|
||||||
Reference in New Issue
Block a user