From 45801205dc68161c0c1babfead7cc791c0bf9f75 Mon Sep 17 00:00:00 2001 From: crumblingstatue Date: Sat, 15 Apr 2023 19:26:36 +0200 Subject: [PATCH] Implement texture atlas building --- Cargo.lock | 379 ++++++++++++++++++++++- Cargo.toml | 3 + res/graphics/{light2.png => light/1.png} | Bin res/graphics/tiles.png | Bin 10308 -> 0 bytes res/graphics/tiles/dirt.png | Bin 0 -> 6262 bytes res/graphics/tiles/stone.png | Bin 0 -> 7191 bytes res/graphics/tiles/unknown.png | Bin 0 -> 355 bytes src/app.rs | 2 +- src/game.rs | 17 +- src/main.rs | 1 + src/math.rs | 21 ++ src/res.rs | 10 +- src/texture_atlas.rs | 102 ++++++ src/tiles.rs | 75 ++--- src/world.rs | 2 +- src/worldgen.rs | 6 +- tiles.dat | Bin 112 -> 92 bytes 17 files changed, 558 insertions(+), 60 deletions(-) rename res/graphics/{light2.png => light/1.png} (100%) delete mode 100644 res/graphics/tiles.png create mode 100644 res/graphics/tiles/dirt.png create mode 100644 res/graphics/tiles/stone.png create mode 100644 res/graphics/tiles/unknown.png create mode 100644 src/texture_atlas.rs diff --git a/Cargo.lock b/Cargo.lock index 43f883b..e7d6abc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.8.3" @@ -146,6 +152,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -164,6 +176,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -246,6 +264,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.0" @@ -261,6 +285,49 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.15" @@ -270,6 +337,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "derivative" version = "2.2.0" @@ -354,6 +427,12 @@ dependencies = [ "sfml", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "emath" version = "0.21.0" @@ -415,6 +494,22 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -424,6 +519,29 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -457,6 +575,12 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "futures-task" version = "0.3.28" @@ -520,8 +644,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -585,6 +721,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -616,6 +761,15 @@ dependencies = [ "spin", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.1" @@ -628,6 +782,25 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -653,7 +826,7 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.1", "libc", "windows-sys 0.48.0", ] @@ -664,7 +837,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", "windows-sys 0.48.0", @@ -679,6 +852,15 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -688,6 +870,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.141" @@ -752,6 +940,7 @@ dependencies = [ "fnv", "gamedebug_core", "hecs", + "image", "log", "num-traits", "rand", @@ -761,6 +950,8 @@ dependencies = [ "serde", "sfml", "sfml-xt", + "texture_packer", + "walkdir", "worldgen", "zstd", ] @@ -771,12 +962,60 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + [[package]] name = "nohash-hasher" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -786,6 +1025,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -877,6 +1126,26 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -895,6 +1164,18 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "2.7.0" @@ -926,6 +1207,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.26" @@ -971,6 +1261,28 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1077,6 +1389,15 @@ dependencies = [ "serde", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1134,6 +1455,12 @@ dependencies = [ "sfml", ] +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "slab" version = "0.4.8" @@ -1164,6 +1491,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "strsim" @@ -1215,6 +1545,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "texture_packer" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28afa0d0b184ff1ba02bf420a4d096e880f4566ded90198cbc4b7f32ba17fa9b" +dependencies = [ + "image", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -1235,6 +1574,17 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "toml" version = "0.7.3" @@ -1305,6 +1655,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1387,6 +1747,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "widestring" version = "1.0.2" @@ -1609,3 +1975,12 @@ dependencies = [ "libc", "pkg-config", ] + +[[package]] +name = "zune-inflate" +version = "0.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index acdbd5b..4aed34a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ env_logger = "0.10.0" zstd = "0.12.3" clap = { version = "4.2.2", features = ["derive"] } directories = "5.0.0" +texture_packer = "0.25.0" +walkdir = "2.3.3" +image = "0.24.6" [dependencies.s2dc] #git = "https://github.com/crumblingstatue/s2dc.git" diff --git a/res/graphics/light2.png b/res/graphics/light/1.png similarity index 100% rename from res/graphics/light2.png rename to res/graphics/light/1.png diff --git a/res/graphics/tiles.png b/res/graphics/tiles.png deleted file mode 100644 index e5f9e0ab5182a5a2d9bd82afb09b5f8d68888ce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10308 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�ZEx!SsX6}KY{@FcmX{LWDwoQv<+^6M z^6&lMHgyZ*rxpK;4}5R-`!}fdvfwz!e+C8y`Gs;ze*`bvxoqk3t<%h^uBD#bmp^l! z7W22-^Y@~%*5155@A6B%`!$bm@3g<49tm#FFPHraYR>l^?^U_IFMZy#FBkjR8oow2 zNLU{GdQ~R9W)( zwr9h!dC<-^`yCi^ER7~+fn|Nj2(aQg0T z5BM1lTo0{%{MG!^vDf)er0@T@ee?UY)LEO?WM4}?dH3g>Qrpy?iDp%=LW=L|YhIr6 z`L5;deW~wl_inHG+#7%L+Pay`f8Mw#fAZy)Wz!b-AFvi?iL#gCR6F^$>~yyG`Pb{G z{#pC`$u`x`9@kbrG3mdfueyBcvZ=GS$xdb4+T-IHXWg4)`*QK}T+gaY?neHmk6-fM zxtbz%a;fU;sLkI~?)MosEmph!mV2dAz3Lv}8}_-T2dc#d_MPEms{i?H_WOGkrGK~o zzZ(Dl&UyXI-|OFh|NQs<@4xzgcQG)0h`bu)weRDj*_pZ4=YHnv-rgs_{kCrMmo>{T zZ~PjU^~xswef6&wmiJ|!Ui!T2iF*FuQorTJUX!kU>$zCG_ui)a+vc2_b^5XD)@wT8 z*8Has-hgM5j=w&!R{eHK&(GUY=X3RUzEe(qZ@FC6^AxNB9@WRzQ0_T*($^^|uWoK% zGIi4NX))g?TDi%|ReNbhK6_H985wNUb+1tV*=8N4nv)0r9}X*8^J&kbdD?&eFS^eN z>Q+y?vbeb3T8bg>x8N`Dxj|Vc?^Wqd_x5`o`0M^Q-IvPmzr5SOd-v(Oy-#=DH9B_U zY1P@E@8(2#dFe)8Qw2BOJ&kmWzwKT6bzbVsU{D+C+N67KdDUlUOsbTcxopYQCyDx+ zk~610dHHE&v1gw0_j1qus=udQiThOH`|iElGApD0iI<~-y^7){8M!^%nzCC}SNZjq zIr4w6AD+Iq`M=~8{vEZ6COkEE|6lZf|8#FY1H%FL)&IY}o&O~M&uaN6A3seg>tIO%g32X2c`tM$*lv8 zWg5E4$;r)I#`WdR+NW=iFP{CsR9iAPWp1@w*0+f{zb`J&k9B_*b$R;}lm0LBa@MA- zKKXXzvgwnoRIk7FsJX3sQ%`m4v@5qh?@%wDd3pQ1?fRyB|LpE$_>h??@ng?C z91I7p-~99FkJ9gb%a^3Y#>*`J{Vn;;{Y^SA7cc!j?e*J9sc}YG=kMN)&D);+Ea=j$ zX@--oMO`aBxBl;|{3mDk|15k~>Gxijf4c7T-^EecH*f0($8O56_B*Xvee-jY-px}N z=e*CebgF-CTkUnNROTo%|d0slvL7=}onN)=5wU-G4HN+qGiP+g|I| zKDAN(y>0p0lXFtCm7klw)MA*VqOV{7@lHPDJ~NhjhEXsY0;3@?8UmvsFd71*AwbU% zcwVHigMoqlKWHrgJ?*8TUH|@ndoTfHzQcw8*Y6pCSfh9}1V%$(Gz3ONU^E0qLjWTL ZY*?;cvUr_q%5)4Q<>~6@vd$@?2>_#m=pq0B diff --git a/res/graphics/tiles/dirt.png b/res/graphics/tiles/dirt.png new file mode 100644 index 0000000000000000000000000000000000000000..7795ff1c06e850e7a9a8ccc0978d75f03d7cef39 GIT binary patch literal 6262 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}hpIv%N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsJTlI0_wB;MnuK%g!;vM_8F(%tYmLb`5EHB{6JXK-47wzU*t=zc>lZ-o7z3` zo^wR{LeC$srw8uoF}&CzpLpdE#~Y?r$M5-1Z`=qd)7+BRC?VWsDzxfoO~H(tPXFG%boj+^(=}4y6z{)q1_s8KOlRi+PiJSC zZx|RV=G0EK^*HPxb2Q%GWnvb4w@2ZLfVpoLDfMmTa$O;i)fy)2qxZK-jn}iQd+~#) zf)B|XUp;uVW_9yw1&+Fg9lI`TfpCYgkGakx-bXWi zy`S&#mq|?ZRJ2o{XYiuwNPo=q*t*9tMK{hHE4!H)`^zYbdBy6K8){2yeKaZb2{b+R z{dr87MSfw}>e6fb)aw&hELf{kJolMKP{a`-7mf*m?yR8^9hX;4Oj2_G?a5z%|)D>6eON+NuHtdjF{ z^%7I^lT!66atjzhz{b9!ATc>RwL~E)H9a%WR_Xoj{Yna%DYi=CroINg16w1-Ypui3%0DIeEoa6}C!XbFK1^!3Zj%k|2Q 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 A_W%F@ literal 0 HcmV?d00001 diff --git a/res/graphics/tiles/stone.png b/res/graphics/tiles/stone.png new file mode 100644 index 0000000000000000000000000000000000000000..f8aa68e10d07e99ea05aabac4deef63fe7a97f43 GIT binary patch literal 7191 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 GameState { + pub(crate) fn new(world_name: String, path: PathBuf, res: &Res) -> GameState { let mut spawn_point = WorldPos::SURFACE_CENTER; spawn_point.y -= 1104; + let mut tile_db = TileDb::load_or_default(); + tile_db.update_rects(&res.atlas.rects); Self { camera_offset: spawn_point, world: World::new(spawn_point, &world_name, path), @@ -129,7 +132,7 @@ impl GameState { worldgen: Worldgen::from_seed(0), ambient_light: 0, light_sources: Vec::new(), - tile_db: TileDb::load_or_default(), + tile_db, } } } diff --git a/src/main.rs b/src/main.rs index 33f4ce7..c417982 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod math; mod player; mod res; mod stringfmt; +mod texture_atlas; mod tiles; mod world; mod worldgen; diff --git a/src/math.rs b/src/math.rs index 6ff2a10..622e55b 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,5 +1,8 @@ +use std::fmt::Debug; + use egui_inspect::derive::Inspect; use num_traits::{Num, Signed}; +use serde::{Deserialize, Serialize}; use crate::world::{TPosSc, TilePos}; @@ -81,6 +84,24 @@ pub fn smoothwave + PartialOrd + Copy>(input: T, max: T) -> T } } +#[derive(Serialize, Deserialize, Debug, Inspect, Default, Clone, Copy)] +pub struct IntRect { + pub x: i32, + pub y: i32, + pub w: i32, + pub h: i32, +} +impl IntRect { + pub(crate) fn to_sf(&self) -> sfml::graphics::Rect { + sfml::graphics::Rect:: { + left: self.x, + top: self.y, + width: self.w, + height: self.h, + } + } +} + #[test] fn test_smooth_wave() { assert_eq!(smoothwave(0, 100), 0); diff --git a/src/res.rs b/src/res.rs index 3d725a5..be8a15f 100644 --- a/src/res.rs +++ b/src/res.rs @@ -1,9 +1,10 @@ -use sfml::{audio::Music, graphics::Texture, SfBox}; +use sfml::audio::Music; + +use crate::texture_atlas::AtlasBundle; #[derive(Debug)] pub struct Res { - pub tile_atlas: SfBox, - pub light_texture: SfBox, + pub atlas: AtlasBundle, pub surf_music: Music<'static>, pub und_music: Music<'static>, } @@ -11,8 +12,7 @@ pub struct Res { impl Res { pub fn load() -> anyhow::Result { Ok(Self { - tile_atlas: Texture::from_file("res/graphics/tiles.png")?, - light_texture: Texture::from_file("res/graphics/light2.png")?, + atlas: AtlasBundle::new()?, surf_music: Music::from_file("res/music/music.ogg").unwrap(), und_music: Music::from_file("res/music/cave2.ogg").unwrap(), }) diff --git a/src/texture_atlas.rs b/src/texture_atlas.rs new file mode 100644 index 0000000..a4535c6 --- /dev/null +++ b/src/texture_atlas.rs @@ -0,0 +1,102 @@ +use std::{collections::HashMap, path::Path}; + +use crate::math::IntRect; +use sfml::{graphics::Texture, SfBox}; +use texture_packer::{texture::Texture as _, TexturePacker, TexturePackerConfig}; + +pub type RectMap = HashMap; + +#[derive(Debug)] +pub struct AtlasBundle { + pub tex: SfBox, + // Key could be `tiles/dirt` for example, derived from folder+filename without extension + pub rects: RectMap, +} + +impl AtlasBundle { + pub fn new() -> anyhow::Result { + let cfg = TexturePackerConfig { + max_width: 4096, + max_height: 4096, + allow_rotation: false, + border_padding: 0, + texture_padding: 1, + texture_extrusion: 0, + trim: true, + texture_outlines: false, + }; + let mut packer = TexturePacker::new_skyline(cfg); + walk_graphics(|path| { + let img = image::open(path).unwrap(); + let key = path_img_key(path); + packer.pack_own(key, img).unwrap(); + dbg!(path); + }); + let mut rects = HashMap::new(); + let mut tex = Texture::new().unwrap(); + if !tex.create(packer.width(), packer.height()) { + panic!("Failed to create texture"); + } + let pixbuf = make_pix_buf(&packer); + unsafe { + tex.update_from_pixels(&pixbuf, packer.width(), packer.height(), 0, 0); + } + for (k, frame) in packer.get_frames() { + rects.insert( + k.clone(), + IntRect { + x: frame.frame.x as i32, + y: frame.frame.y as i32, + w: frame.frame.w as i32, + h: frame.frame.h as i32, + }, + ); + } + Ok(AtlasBundle { tex, rects }) + } +} + +fn make_pix_buf(packer: &TexturePacker) -> Vec { + let (w, h) = (packer.width(), packer.height()); + let px_size = 4; + let mut vec = vec![0; w as usize * h as usize * px_size as usize]; + dbg!(w, h); + for y in 0..h { + for x in 0..w { + let idx = ((y * w + x) * px_size) as usize; + if let Some(px) = packer.get(x, y) { + vec[idx..idx + px_size as usize].copy_from_slice(&px.0); + } + } + } + vec +} + +fn path_img_key(path: &Path) -> String { + let mut rev_iter = path.components().rev(); + let fname = rev_iter.next().unwrap(); + let folder = rev_iter.next().unwrap(); + let fname: &Path = fname.as_ref(); + let folder: &Path = folder.as_ref(); + folder + .join(fname.file_stem().unwrap()) + .display() + .to_string() +} + +#[test] +fn test_path_img_key() { + assert_eq!( + &path_img_key("/home/person/res/graphics/tiles/foo.png".as_ref()), + "tiles/foo" + ); +} + +fn walk_graphics(mut f: impl FnMut(&Path)) { + for en in walkdir::WalkDir::new("res/graphics") { + let en = en.unwrap(); + if en.file_type().is_file() { + f(en.path()); + } + } +} diff --git a/src/tiles.rs b/src/tiles.rs index af5101f..dcb25de 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -4,11 +4,11 @@ use std::ops::Index; use egui_inspect::derive::Inspect; use serde::{Deserialize, Serialize}; -use sfml::graphics::IntRect; use crate::{ graphics::{ScreenSc, ScreenVec}, - math::TILE_SIZE, + math::{IntRect, TILE_SIZE}, + texture_atlas::RectMap, world::TileId, }; @@ -17,9 +17,11 @@ pub struct TileDef { pub bb: Option, /// Whether the tile emits light, and the light source offset pub light: Option, - pub atlas_offset: AtlasOffset, /// Platform behavior: Horizontally passable, vertically passable upwards pub platform: bool, + #[serde(default)] + pub graphic_name: String, + pub tex_rect: IntRect, } const DEFAULT_TILE_BB: TileBb = TileBb { @@ -44,9 +46,18 @@ pub struct TileDb { impl Default for TileDb { fn default() -> Self { + let unknown = TileDef { + bb: None, + light: Some(ScreenVec { + x: TILE_SIZE as ScreenSc / 2, + y: TILE_SIZE as ScreenSc / 2, + }), + platform: false, + graphic_name: String::from("tiles/unknown"), + tex_rect: IntRect::default(), + }; Self { - // Add empty/air tile - db: vec![EMPTY], + db: vec![EMPTY, unknown], } } } @@ -56,52 +67,26 @@ const EMPTY: TileDef = TileDef { light: None, // Rendering empty tile is actually special cased, and no rendering is done. // But just in case, put the offset to UNKNOWN - atlas_offset: UNKNOWN_ATLAS_OFF, + tex_rect: IntRect { + x: 0, + y: 0, + w: 0, + h: 0, + }, platform: false, + graphic_name: String::new(), }; impl Index for TileDb { type Output = TileDef; fn index(&self, index: TileId) -> &Self::Output { - self.db.get(index as usize).unwrap_or(&UNKNOWN_TILE) + self.db.get(index as usize).unwrap_or_else(|| { + &self.db[1] // Unknown tile def is stored at index 1 + }) } } -#[derive(Debug, Inspect, Serialize, Deserialize)] -pub struct AtlasOffset { - pub x: u16, - pub y: u16, -} -impl AtlasOffset { - pub(crate) fn to_sf_rect(&self) -> IntRect { - IntRect { - left: self.x as i32, - top: self.y as i32, - width: TILE_SIZE as i32, - height: TILE_SIZE as i32, - } - } -} - -impl Default for AtlasOffset { - fn default() -> Self { - UNKNOWN_ATLAS_OFF - } -} - -const UNKNOWN_ATLAS_OFF: AtlasOffset = AtlasOffset { x: 320, y: 0 }; - -static UNKNOWN_TILE: TileDef = TileDef { - bb: None, - light: Some(ScreenVec { - x: TILE_SIZE as ScreenSc / 2, - y: TILE_SIZE as ScreenSc / 2, - }), - atlas_offset: UNKNOWN_ATLAS_OFF, - platform: false, -}; - const PATH: &str = "tiles.dat"; impl TileDb { @@ -129,4 +114,12 @@ impl TileDb { Err(e) => log::warn!("Failed to save tile db: {e}"), } } + + pub(crate) fn update_rects(&mut self, rects: &RectMap) { + for def in &mut self.db { + if !def.graphic_name.is_empty() { + def.tex_rect = rects[&def.graphic_name]; + } + } + } } diff --git a/src/world.rs b/src/world.rs index 2fe90ed..f7546c7 100644 --- a/src/world.rs +++ b/src/world.rs @@ -78,7 +78,7 @@ impl World { chk.at_mut(local) } pub fn save(&self) { - let result = std::fs::create_dir(&self.path); + let result = std::fs::create_dir_all(&self.path); log::info!("{result:?}"); self.save_meta(); self.player.save(&self.path); diff --git a/src/worldgen.rs b/src/worldgen.rs index 1ccb862..97ad339 100644 --- a/src/worldgen.rs +++ b/src/worldgen.rs @@ -34,7 +34,7 @@ impl Worldgen { .add( Tile::new(Tl { bg: 9, - mid: 2, + mid: 3, fg: 6, }) .when(constraint!(nm.clone(), < -0.8)), @@ -43,7 +43,7 @@ impl Worldgen { .add( Tile::new(Tl { bg: 9, - mid: 2, + mid: 3, fg: 0, }) .when(constraint!(nm.clone(), < -0.1)), @@ -52,7 +52,7 @@ impl Worldgen { .add( Tile::new(Tl { bg: 7, - mid: 1, + mid: 2, fg: 0, }) .when(constraint!(nm, < 0.45)), diff --git a/tiles.dat b/tiles.dat index daf63fba58a309c5607544c25696facb3aae252e..883db8180abd1d6d3575ad7208a930cff33d2d1c 100644 GIT binary patch literal 92 zcmbO@W$J+ghZam>U|?XFdSH@(z@fDznK`M&`lWfW&xBw T0HGnqfz5>(#t71yz;GA