From 2dfd3b13d1655622e13bb3727d98658a40454510 Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Thu, 20 Aug 2020 20:34:59 +0200 Subject: [PATCH] (See sharp/lod-history) LOD, shadows, greedy meshing, new lighting, perf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Pretty much a Veloren fork at this point. Here's a high level overview of the changes (will be added to CHANGELOG just before merge). At a high level this MR incorporates roughly two groups of changes. The first group consists of new game features: more flexible map sizes, level of detail terrain, shadow maps, and a new lighting engine. This is "feature work" that (mostly) only adds new things to Veloren, and mostly shouldn't affect old stuff. The second big group of changes are those addressing the fallout from all the new features. These include performance fixes of various sorts: the addition of multiple graphics options and optimization of the cheap ones to avoid work, switching all voxel models to use some variant of greedy meshing, switching over much of our CPU-side vector math to exploit SIMD instructions (coinciding with a fork of `vek`), and a rewrite of how the UI handles text rendering (coinciding with updates to our fork of `conrod`). Making Veloren's hardcoded colors appear correct under the new lighting engine also required considerably changes (TODO: Fill in this section when it's complete). The second category of changes often heavily touches code owned by other people, including frequently modified code "owned" by a handful of people, so I recommend that this code be reviewed particularly carefully. --- At a high level (each will be described in more detail below): - The world map has been refactored. - The world size is no longer hardcoded (@zesterer). - The map generation code was made generic to allow using it outside of the `world` crate (@zesterer). - On world creation, we now compute *horizon maps* (@zesterer). - The way we pass the world from the server to the client has been updated (@xMAC94x). - Artifacts related to image rotation were fixed (@imbris). - Multiflow rivers were enabled (@zesterer). - In the process of making changes related to the world map, various incidental fixes and optimizations were required. - The new *level of detail* feature was added (@zesterer wrote part of this and has checked out the rest). - A new LOD terrain rendering step was added to the pipeline. - The LOD terrain quality was made configurable via a graphics setting. - Horizon maps were used to cast shadows from LOD chunks on both LOD and non-LOD terrain. - A "voxelization" effect was incorporated into rendered LOD terrain to make it blend better into the world. - In the process of making changes related to LOD, various incidental fixes and optimizations were required. - Veloren's lighting has been completely overhauled (@zesterer has already checked most of this out). - A semi-accurate index of refraction was assigned to our materials. - A new, more realistic, physically based approach to lighting was used using the *Ashikhmin Shirley* BRDF. - We emulate *atmospheric scattering* using equations designed for measuring solar panel light exposure. - We attempt to compute *realistic light attenuation* in water using its real material properties. - In the process of making changes related to LOD, various incidental fixes and optimizations were required. - Point and directional lights now cast realistic shadows, using *shadow mapping.* (@imbris, @zesterer, @Treeco, @YuriMomo) - Point light shadow maps were added to the rendering pipeline, using geometry shaders and *seamless cube maps*. - Directional light shadows were added to the rendering pipeline, using LISPSM together with disabling *depth clamping*. - "Shadow-only" chunks and NPCs were added to prevent shadows from models behind you from disappearing. - In the process of making changes related to shadow maps, various incidental fixes and optimizations were required. The addition of shadow maps, LOD terrain, and the new lighting all led to significant performance degradation, on top of other changes happening in master. Therefore, a large number of performance improvements were also needed: - The graphics options were made much more flexible and configurable, and shaders were optimize. - New options were provided for how to render lights and shadows (@Pfauenauge, @zesterer). - Graphic setting storage and configuration were overhauled to make adding new features easier (@Pfauenauge, @imbris). - Shaders were rewritten to utilize GLSL's preprocessor to avoid overhead (@zesterer, @YuriMomo). - In the process of making changes related to providing additional rendering options, various incidental fixes and optimizations were required. - Voxel model creation was switched to use *greedy meshing.* - A new voxel meshing method, greedy meshing, was added (@imbris). - Uses of the older meshing methods were migrated to use greedy meshing (@imbris, @jshipsey, @Pfauenauge). - New restrictions were added to terrain, figure, and sprites to future proof them for further optimizations (@jshipsey, @Pfauenauge, @zesterer). - Most positions are now relative to either chunk or player position for better precision (@imbris, @zesterer, @scottc). - In the process of making changes related to greedy meshing, various incidental fixes and optimizations were required. - Animation and terrain math were switched to use SIMD where possible. - Fixes were made to vek to make its SIMD feature usable for us (@zesterer, @imbris). - The interface and types used in bone animation were changed in various ways (@jshipsey, @Snowram, @Pfauenauge). - Redundant code generation for body animation is now partly taken care of by a macro (@jshipsey, @Snowram, @Pfauenauge). - Animation code was modified to to use vek's SIMD representation where possible (@jshipsey, @Snowram, @Pfauenauge). - Terrain meshing code and shadow map math were also modified to use vek's SIMD representation (@imbris). - SIMD instruction generation was enabled (@YuriMomo, @jshipsey, @Snowram, @imbris, @Angelonfira, @xMAC94x). - In the process of making changes related to greedy meshing, various incidental fixes and optimizations were required. - The way we cache glyphs was completely refactored, fixed, and optimized. - Our fork of `conrod` was optimized in various ways (@imbris). - Our fork of `conrod` now exposes whether a widget was updated during the current frame (@imbris). - Our use of the glyph cache was rewritten for correctness (@imbris). - A *text cache* was introduced that lets us skip remeshing glyphs that have not changed (@imbris). - Various changes were made to reduce pressure on the glyph cache, with more planned (@imbris, @Pfauenauge). - In the process of making changes related to the glyph cache, various incidental fixes and optimizations were required. - Colors were changed to keep Veloren's look consistent with master. - Some older tree models were brought back (@Pfauenauge). - TODO(@Sharp): All hardcoded colors were extracted and made hotloadable. - TODO(@Treeco, @Pfauenauge, @imbris, @jshipsey): Hardcoded colors were fixed to conform to Veloren's style. - TODO(@Treeco, @Pfauenauge, @imbris, @jshipsey): Color models were fixed to conform to Veloren's style. A detailed description of the involved changes follows. --- - The world size is no longer hardcoded. All functions dependent on world size now take a `WorldSizeLg`, which holds the base 2 logarithm of each actual world dimension and is guaranteed to maintain certain properties (outlined in `common/src/terrain/map.rs`). Additionally, many utility functions that utilize the world size were moved into `common` as well (mostly `common/src/terrain/mod.rs`). Finally, the world map format was updated in order to store its size explicitly, with a migration path from the old format that should work whenever the old formatted map was a square (practically always). See `world/src/sim/mod.rs` for these changes. - The map generation code was made generic to allow using it outside of the `world` crate. The parts of the map generating code that do not need to query the world were moved over to `common/src/terrain/map.rs`, allowing them to be used from the client without creating a dependency on `world`. The rest of it was turned into helper functions in `world/src/sim/map.rs`, which can be passed as closures to the generic map generation code to complete its construction. This also means that colors are now passed in separately to the map generation function. See for more details. - On world creation, we now compute *horizon maps*. See the function in `world/src/sim/util.rs`. Given a height map and a plane intersecting that height map, our horizon maps allow us to encode enough information to reconstruct shadows for each point on the height map using only the *horizon angle* (the angle at which the sun starts to become visible). As Veloren's sun only covers one plane, this is sufficient for encoding sun shadows for LOD terrain, by encoding two angles per chunk (one for each 90 degrees the sun covers). We can also use this for the moon, if we want, since the moon follows the same path. Additionally, we store the *height* of the furthest occluder, to try to make the shadows volumetric; so this means 4 bytes in total for each chunk. Support for horizon maps has been merged into the map functionality in common as well. - The way we pass the world from the server to the client has been updated. Rather than passing the prerendered map, we instead pass three maps with values for each chunk; one with the color information, a second with altitude information, and a third with horizon map information. We then reconstruct the map on the client, together with some additional information we send from the server (like the sea level and maximum height). See `common/src/msg/server.rs` for a detailed description of the format of `WorldMapMsg`, and `server/src/libr.rs` and `client/src/lib.rs` for details of the map construction and parsing. - Artifacts related to image rotation were fixed. See the commit message for commit SHA `cf74d55f2e3d2ae7d25fd68d5c73b01a6afde86e` for a detailed explanation. This involved changes to shaders, the addition of a new type of graphic (also reflected in the graphic cache) that allows specifying a border color (which automatically makes the associated texture immutable), and some related fixes. I reproduce the first two paragraphs of the MR description as well: ``` Fix map image artifacts and remove unneeded allocations. Specifically, we address three concerns (the image stretching during rotation, artifacts around the image due to clamping to the nearest border color when the image is drawn to a larger space than the image itself takes up, and potential artifacts around a rotated image which accidentally ended up in an atlas and didn't have enough extra space to guarantee the rotation would work). ``` - Multiflow rivers were enabled. This does not really need to be part of this MR, and would be easy to revert, but since it seemed to provide a nice improvement it's currently packaged with it. We already computed multiple outflows from each chunk for erosion purposes long before this MR. However, we never modified river rendering to be able to handle this case (just a single downhill river flow is complex enough!) so this was not exposed when deciding which chunks were rivers. Now that - In the process of making changes related to the world map, various incidental fixes and optimizations were required. Some examples of fixes include making sure terrain is never lowered to below sea level (to make the shadow maps report correct values), fixing map altitudes and colors to understand things like cliffs and "block level" coloring (that doesn'te xist on the column level), and fixing a crashbug when rendering images for the UI where source pixels are strongly rectangular. Some examples of related performance fixes include avoiding allocating a fresh vector for all the maps (i.e. copying it over to change the format from `[u32; n]` to `DynamicImage` and then copying again to convert to `RgbaImage`), and instead using the `gfx::memory::slice` function to accomplish the same thing. These sorts of changes are spread all arond the code. This includes the additon of a new scene, `voxygen/src/scene/lod.rs`, a new pipeline `voxygen/src/render/pipeline/lod_terrain.rs`, and new shaders `assets/vxygen/shaders/lod-terrain-vert.glsl` and `assets/vxygen/shaders/lod-terrain-frag.glsl`, as well as associated changes to the renderer in `voxygen/src/render/renderer.rs`. The main idea behind our initial approach to LOD was to take the world data we now get from the server (altitude, color, and horizon mapping). - Some previously computed values were turned into shader uniforms for better prediction on weak processors. (@zesterer) - Calls to power or trig functions were removed or replaced with multiplications, where possible. - After some deliberation - To properly handle sprite "waving" for nearby sprites, We explicitly designed the greedy meshing system with figures and sprites in mind. In both cases, we want to be able to *efficiently* pack many different models into the same texture, especially in cases where we know we will either not be removing any of the grouped-together from the models from the texture, or will remove all of them at once (so they can be packed into some specific subtexture). For sprites, since we know every model in advance and never intend to deallocate them, we currently pack them all as efficiently as possible into one giant tetxure atlas. However, in the future we might opt to pack them slightly less efficiently in exchange for shrinking the sprite vertex size. For figures, we pack all the textures for each *model* into the same atlas. is a global texture atlas used for every sprite, and for figures which is why we have the ability to mesh multiple models to the same texture area (using the simple texture atlas allocator) without requiring intermediate vector allocations. This is accomplished by delaying the time when we actually write the color and light data to the texture until *after* all the model vertices have been meshed; then, we can just allocate the whole color/light array at once, making the atlas we use an exact fit. In computer science-y terms, we accomplish this delay by, after we perform the initial greedy meshing (without texture information), not continuing to create the texture data, but instead constructing a *continuation*--that is, a function that, when called, will execute the rest of the computation. We push this continuation (which in Rust terms is a `FnOnce` closure that takes the `ColLightsInfo` that it is supposed to write to as context) onto a onto a vector resizing. To allow for suspended writes to texture data, Rust pointed out to me that the continuation that would eventually write the color and light data to the texture atlas (the one that is shared by all models sharing the same greedy mesher) would have to *own* whatever data it mshed. Because we often generate the model data to mesh as a temporary in `voxygen/src/load.rs`, the - Matrix multiplications in the shader were reduced for figure data (@zesterer). - Vertex "waves" for fluid data were removed. - Terrain "bending" near edges was removed. - Scaling was fixed to make sure empty space was not introduced in a space previously occupied by a block. It was also changed to take ownership of its voxel data, rather than sharing it, to let it be used with meshing. - Rust's nightly version was bumped in order to use the `array_map` function, which lets us reuse more code between the simple map and `FigureModelCache`. - PositionedGlyph::standalone. --- I tried to cite sources in many cases[^realtime],[^lloyd],[^lispsm],[^pbrt],[^greedy],[^tjunctions] where I needed features from elsewhere but I am particularly grateful for the following resources, esepcially where they have accompanying source code. I linked all of them that are accessible to the public (those that are not were obtained through legal means). [^realtime]: Eisemann, Elmar, Michael Schwarz, Ulf Assarsson, Michael Wimmer. Real-Time Shadows. A K Peters/CRC Press (T&F), 20160419. [^lloyd]: Lloyd,B. 2007. [Logarithmic perspective shadow maps](http://gamma.cs.unc.edu/papers/documents/dissertations/lloyd07.pdf). PhD thesis, University of North Carolina. [^lispsm]: Wimmer, M., Scherzer, D., and Purgathofer, W. 2004. [Light space perspective shadow maps](http://gamma.cs.unc.edu/papers/documents/dissertations/lloyd07.pdf). In Proceedings of Eurographics Symposium on Rendering 2004, pp. 143– 152. [^pbrt]: Pharr, Matt, et al. [http://gamma.cs.unc.edu/papers/documents/dissertations/lloyd07.pdf](Physically Based Rendering: From Theory to Implementation). Third edition, Morgan Kaufmann Publishers/Elsevier, 2017. [^greedy]: mikolalysenko. “Meshing in a Minecraft Game.” 0 FPS, 30 June 2012, . [^tjunctions]: blackflux. “Meshing in Voxel Engines – Part 1.” Blackflux.Com, 23 Feb. 2014, . I am also especially grateful to Khronos, Wikiepdia, and stackoverflow for answering many of my specific questions while writing the MR. --- Squashed commit of the following: commit 300505e7305a2fdac722a808ee8538323f215f39 Author: Joshua Yanovski Date: Thu Aug 20 18:46:25 2020 +0200 Fixing cargo doc and typo in CHANGELOG. commit ec0aeb18e8499d7d84ef818331b4b65c3d76cea6 Author: Joshua Yanovski Date: Thu Aug 20 15:38:50 2020 +0200 Hopefully final commit for the LOD branch. commit 5e8ea0b1eaac02903a02feb2eb038d195de4872f Author: Joshua Yanovski Date: Thu Aug 20 10:14:26 2020 +0200 Falling back to power as stopgap. commit e44a1cbf46504ee9931a01bdc9033e145500d557 Author: Joshua Yanovski Date: Thu Aug 20 09:25:41 2020 +0200 Address imbris feedback. Temporarily disables shiny water, lowers max VD. These restrictions will be lifted soon after merging. commit 561e25778a108ac3712f5ec2ce3a51234c4430d3 Author: Joshua Yanovski Date: Thu Aug 20 08:31:13 2020 +0200 Tweaking shaders a bit. commit 7d19259078ce0dd7b3742ad7d0cb3fab3ece3fdc Author: Joshua Yanovski Date: Thu Aug 20 07:59:43 2020 +0200 Fix view example as well. commit 051cd4934e0fad2c0b15db4380a4189fa318679f Author: Joshua Yanovski Date: Thu Aug 20 07:29:06 2020 +0200 Fix meshing benchmark. commit c95e07db3b4dcca285678985e65e31b927cb1c61 Author: Joshua Yanovski Date: Thu Aug 20 05:46:22 2020 +0200 Address MR feedback, fix scene clouds. commit 1bfb816cabdb3ffc1e507a009c2d98696b4b573a Author: Joshua Yanovski Date: Thu Aug 20 04:39:36 2020 +0200 Incorporating Pfau's figure color changes. New eyes and new humanoid colors. commit 3f9b89a3ac7b3356b1dba0d1a8f6541357f81469 Merge: e2f5162e4 62c53963a Author: Joshua Yanovski Date: Thu Aug 20 04:29:41 2020 +0200 Merge remote-tracking branch 'origin/sharp/small-fixes' into sharp/small-fixes commit e2f5162e4f96f4124aa43488f7245d341b3dcfd4 Author: Joshua Yanovski Date: Thu Aug 20 04:28:38 2020 +0200 World colors are all hotloadable. They live in assets/world/style/colors.ron. Only a small handful of hardcoed colors remain in World; they are either part of the map, or difficult to disentangle from the rest of the computation. Comments are made where appropriate. commit 62c53963abe1975009d34a8f9515a355bef24f31 Author: Marcel Märtens Date: Wed Aug 19 15:59:00 2020 +0200 replace pretty_env_logger with tracing commit 5b1625f99d9586fe80e2232f583ec5af9f953099 Merge: d71003acd 4942b5b39 Author: Joshua Yanovski Date: Wed Aug 19 05:15:56 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit d71003acdabeff970b3928e97c26af6847b5b78e Author: Joshua Yanovski Date: Wed Aug 19 05:14:34 2020 +0200 Hotloading colors, part 1: colors in common. Currently, this just entails humanoid colors. There are only three colors not handled; the light emitter colors in common/src/comp/inventory/item/tool.rs. These don't seem important enough to me to warrant making hotloadable, at least not right now, but if it's needed later we can always add them to the file. commit 63b5e0e553eb2ea49276f192a6fc7dd65254270d Merge: c32b337a4 6d2c4b9c1 Author: Joshua Yanovski Date: Mon Aug 17 13:05:37 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit c32b337a46e10d9de473d178a94a3ccd61c39bb3 Author: Joshua Yanovski Date: Mon Aug 17 05:52:04 2020 +0200 Fixing LOD grid, for real. commit a166ae0360395387e09fb35a1f84210c2ce5ec24 Author: Joshua Yanovski Date: Mon Aug 17 05:28:05 2020 +0200 Addressing imbris's initial feedback. Fixes two minor bugs: explosion particles were no longer spawning randomly, and LOD grids were not perfectly even. commit 4cbad004f44060994252dd3d38647a14a589712f Author: Joshua Yanovski Date: Sun Aug 16 19:27:58 2020 +0200 Bumping nightly per request. commit 548680276aac77c25d43d16b5622f847d474dbef Merge: acc098604 8f8b20c91 Author: Joshua Yanovski Date: Sun Aug 16 19:26:06 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit acc0986040589a3492f88a740bc3c3fc693b26d3 Author: Joshua Yanovski Date: Sat Aug 15 22:28:32 2020 +0200 Lower resolution due to lying drivers. commit d3b878de2a52c358d2944a6bbd0555dad7fbdb10 Author: Joshua Yanovski Date: Sat Aug 15 22:15:38 2020 +0200 Fix issues msh encountered with Intel 4600. commit 10245e0c1b0cb6fae10d86409435364edc6102ef Author: Joshua Yanovski Date: Sat Aug 15 21:15:02 2020 +0200 Merge more models into one mesh than we did previously. commit 3155c31e663c52ae5c3a53d5fb5665892a1a498a Merge: 7204cc8a7 3c199280e Author: Joshua Yanovski Date: Thu Aug 13 22:35:22 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 7204cc8a7a4f74a30306bd205d9834fee4bb944f Author: Joshua Yanovski Date: Thu Aug 13 22:34:43 2020 +0200 Fix not yet done NPC animations. This forces them all to be the idle animation if not specified. This fixes issues where you'd have giant NPCs in water. commit bc83360f2a08918f19d417b5f772e1ff554dba08 Author: Joshua Yanovski Date: Thu Aug 13 19:36:37 2020 +0200 Try to fix some bugs: - Z fighting with LOD terrain and water. - Audio SFX not playing. commit 1fd104aa603bf3781b6526a5cada46aeca3049dd Merge: 862df3c99 7c2c392a3 Author: Joshua Yanovski Date: Thu Aug 13 12:02:31 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 862df3c9976c4da9bd7cfd784f1a85973127968a Merge: 0a4218ed9 75c1d4401 Author: Joshua Yanovski Date: Thu Aug 13 05:52:56 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 0a4218ed9d541a2b34c133351bea38a99ddf4ea7 Author: Joshua Yanovski Date: Wed Aug 12 22:27:14 2020 +0200 Fix particle depth. commit f51dfdeb442d0dd5243dd2f344fa4be295bd0875 Merge: c6251a956 5e6dc0471 Author: Joshua Yanovski Date: Wed Aug 12 20:19:04 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit c6251a956ad376400dca5c23420cf8d213dc8fdf Author: Joshua Yanovski Date: Wed Aug 12 20:15:46 2020 +0200 Cache figures more intelligently. Cache figures for longer, and don't cache character states for the player except where they actually affect the rendered model. commit 0ed801d5404982c6fb63b1eaa4567d908b294c9d Merge: c11b9bdf0 eea64f78f Author: Joshua Yanovski Date: Wed Aug 12 16:32:24 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit c11b9bdf0a53bf9884d2f5a48fec9b01d582df1a Author: Joshua Yanovski Date: Wed Aug 12 11:47:15 2020 +0200 Remove unneeded Clippy annotation. commit 16aa9ef40af56d69289d00aafb3deadfb8bd4f35 Author: Joshua Yanovski Date: Sat Aug 8 00:53:02 2020 +0200 Fix hotloading and Clippy. commit 3dc973e0be5b758da1e9805eb764ad401374cd0c Author: Joshua Yanovski Date: Fri Aug 7 23:50:27 2020 +0200 Major speedups with SIMD. commit fba64a7d93d5a96077ce87287bbce6ab9b7fbcae Merge: 76429d00e d1e10b178 Author: Joshua Yanovski Date: Fri Aug 7 13:23:19 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 76429d00eea00d212fbd672a84ee91e75b19b938 Author: Joshua Yanovski Date: Fri Aug 7 13:23:10 2020 +0200 Add clippy.toml. commit c79f512f84dbb83cb82b7954db68ff241dfd8e41 Author: Joshua Yanovski Date: Fri Aug 7 11:55:20 2020 +0200 Fix all clippy issues, clean up Rust code. commit 6f90e010b3fbefb53b0d632e819931350015b6b8 Merge: 77a8c7c26 5929cfa5c Author: Joshua Yanovski Date: Fri Aug 7 06:47:30 2020 +0200 Merge remote-tracking branch 'origin/sharp/small-fixes' into sharp/small-fixes commit 77a8c7c267d3f44a1a62bd6b2274359973c5e4d4 Merge: b44e44232 44febaabd Author: Joshua Yanovski Date: Fri Aug 7 06:47:10 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 5929cfa5c713aa392110bbb62407f76caf53c3df Author: jshipsey Date: Thu Aug 6 20:47:27 2020 -0400 fixed in-hand arrow bug commit b44e442325d828f1cd564d66908f89d09e80474b Author: Joshua Yanovski Date: Thu Aug 6 13:40:35 2020 +0200 Miscellaneous performance improvements. commit be37acf287c1360d8085862526ac3365ffd1d768 Merge: 125d7fc6c c11876547 Author: Joshua Yanovski Date: Mon Aug 3 05:49:27 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit 125d7fc6c4dcd8c8c5f27b8268a4d64f409f3644 Author: Joshua Yanovski Date: Mon Aug 3 04:55:31 2020 +0200 Abstract over simd vs. repr_c vectors. Also some minor improvements to Event size. commit d4d4956e9252e1241ce110b2aa85076c6b1e2a23 Merge: 5f3b7294a aced5f979 Author: Joshua Yanovski Date: Sun Aug 2 20:56:54 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit 5f3b7294af1f8533edc2620b58863c248e2b07af Author: Joshua Yanovski Date: Sun Aug 2 20:43:52 2020 +0200 Fix formatting issues I missed before. commit a428a3ebba70dcab63dd0f8cd983120c90617271 Author: Joshua Yanovski Date: Sun Aug 2 20:41:51 2020 +0200 Fix clippy warnings, part 1. There aer still a bunch of type too complex and function takes too many arguments warnings that I'll fix later (or ignore, since in the one case I did fix a function takes too many arguments warning I think it made the code *less* readable). commit ba54307540ed8a937ac08209730284fc653af85b Author: Joshua Yanovski Date: Thu Jul 30 13:22:42 2020 +0200 Fix light animations so they are removed when the light turns off. commit 7e0f4bcbf0f4717145d8beac40d52e3acebbe2aa Author: Joshua Yanovski Date: Wed Jul 29 21:10:20 2020 +0200 Fix crash in edge case for pixel art. commit 56da06f7a351e2b949e9b014a90b974d511a0924 Merge: cf74d55f2 9f53a4a19 Author: Joshua Yanovski Date: Wed Jul 29 18:56:52 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit cf74d55f2e3d2ae7d25fd68d5c73b01a6afde86e Author: Joshua Yanovski Date: Wed Jul 29 18:29:52 2020 +0200 Fix map image artifacts and remove unneeded allocations. Specifically, we address three concerns (the image stretching during rotation, artifacts around the image due to clamping to the nearest border color when the image is drawn to a larger space than the image itself takes up, and potential artifacts around a rotated image which accidentally ended up in an atlas and didn't have enough extra space to guarantee the rotation would work). The first concern was addressed by fixing the dimensions of the map images drawn from the UI (so that we always use a square source rectangle, rather than a rectangular one according to the dimensions of the map). We also fixed the way rotation was done in the fragment shader for north-facing sources to make it properly handle aspect ratio (this was already done for north-facing targets). Together, these fix rendering issues peculiar to rectangular maps. The second and third concerns were jointly addressed by adding an optional border color to every 2D image drawn by the UI. This turns out not to waste extra space even though we hold a full f32 color (to avoid an extra dependency on gfx's PackedColor), since voxel images already take up more space than Optiion<[f32; 4]> requires. This is then implemented automatically using the "border color" wrapping method in the attached sampler. Since this is implemented in graphics hardware, it only works (at least naively) if the actual image bounds match the texture bounds. Therefore, we altered the way the graphics cache stores images with a border color to guarantee that they are always in their own texture, whose size exactly matches their extent. Since the easiest currently exposed way to set a border color is to do so for an immutable texture, we went a bit further and added a new "immutable" texture storage type used for these cases; currently, it is always and automatically used only when there is a specified border color, but in theory there's no reason we couldn't provide immutable-only images that use the default wrapping mdoe (though clamp to border is admittedly not a great default). To fix the maps case specifically, we set the border color to a translucent version of the ocean border color. This may need tweaking going forward, which shouldn't be hard. As part of this process, we had to modify graphics replacement to make sure immutable images are *removed* when invalidated, rather than just having a validity flag unset (this is normally done by the UI to try to reuse allocations in place if images are updated in benign ways, since the texture atlases used for Ui do not support deallocation; currently this is only used for item images, so there should be no overlap with immutable image replacement, so this was purely precautionary). Since we were already touching the relevant code, we also updated the image dependency to a newer version that provides more ways to avoid allocations, and made a few other changes that should hopefully eliminate redundant most of the intermediate buffer allocations we were performing for what should be zero-cost conversions. This may slightly improve performance in some cases. commit ad18ce939940a8c697270f6e9b94db9942fd8295 Author: Joshua Yanovski Date: Tue Jul 28 13:21:09 2020 +0200 Fix continent scale hack. commit 36b1cb074f5b195aebd7dbbc3da7f0246a1a18ec Author: Joshua Yanovski Date: Tue Jul 28 12:11:40 2020 +0200 Enable loading different sized maps without a recompile. We may want to tweak the effects of the continent_scale_hack. commit 13b6d4d534cc4814b7cb3294ca41bbfea0a6b186 Author: Joshua Yanovski Date: Tue Jul 28 10:55:48 2020 +0200 Removing WORLD_SIZE, part 1. Erased almost every instance of WORLD_SIZE and replaced it with a local power of two, map_size_lg (which respects certain invariants; see common/src/terrain/map.rs for more details about MapSizeLg). This also means we can avoid a dependency on the world crate from client, as desired. Now that the rest of the code is not expecting a fixed WORLD_SIZE, the next step is to arrange for maps to store their world size, and to use that world size as a basis prior to loading the map (as well, probably, as prior to configuring some of the noise functions). commit 30b1d2c6428230a9eaa0d749cdcbbd6f1cbccd78 Merge: 7d56ba31b 1377b369f Author: Joshua Yanovski Date: Mon Jul 27 13:16:58 2020 +0200 Merge remote-tracking branch 'origin/sharp/small-fixes' into sharp/small-fixes commit 7d56ba31b445441461b28a07fc495d7d4f047c17 Merge: 2101113b4 598f14b25 Author: Joshua Yanovski Date: Mon Jul 27 13:16:27 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit 1377b369f6db2258e910d467a5740f31789e3ded Author: Monty Marz Date: Sun Jul 19 23:25:38 2020 +0200 more saturated pumpkins commit ae8d50527f93bb0616c2ad46ce4dacb63bc37c6d Author: Monty Marz Date: Sat Jul 18 20:29:56 2020 +0200 acacia models commit 2101113b467e691de787392d7f20f1745f5637bd Author: Joshua Yanovski Date: Sat Jul 18 18:55:25 2020 +0200 Higher detail LOD. commit add2cfae04b4385fa5590e11e2bd5229d9dee0aa Author: Joshua Yanovski Date: Thu Jul 16 01:57:39 2020 +0200 Revert some irrelevant stuff. commit 2e2ab3dc1eaa59a4aef7f8d34a53d8aae4c8553a Author: Joshua Yanovski Date: Wed Jul 15 13:30:49 2020 +0200 Fixing various things about shadows. * Correcting optimal LISPSM parameter. * Figure shadows are cast when they're not visible. * Chunk shadows stay cast until you look away. * Seamless cubemaps for point lights. * Etc. commit 6c31e6b56217274285b597af297036691ea5d897 Author: Joshua Yanovski Date: Sun Jul 12 19:50:26 2020 +0200 Fix shadow creation. commit 6332cbe006115ae205597529cd8bbccd146c2cca Merge: be438657c 930e0028b Author: Joshua Yanovski Date: Sun Jul 12 18:47:00 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit be438657c33d7b5bfb0a9582c3ed3fd366637323 Author: Joshua Yanovski Date: Sun Jul 12 18:28:08 2020 +0200 Tweaks to shadows. Added shadow map resolution configuration, added seamless cubemaps, documented all existing rendering options, and fixed a few Clippy errors. commit 23b4058906013c7d2a40c286e20e32c5fbd897ed Author: Joshua Yanovski Date: Wed Jul 8 10:11:19 2020 +0200 Fix moon, use nonlinear noise for terrain. Note that the latter has a bit of performance cost. commit 7fbe5cbfbb9dc29607957b8e62f432a4deed193d Author: Joshua Yanovski Date: Wed Jul 8 02:23:02 2020 +0200 Address lies about max texture size. commit bcfc62b5e13a1cdd83f57535fde4694720bebfd9 Merge: 75e3626a7 18a08e8fe Author: Joshua Yanovski Date: Wed Jul 8 02:22:08 2020 +0200 Merge remote-tracking branch 'origin/sharp/small-fixes' into sharp/small-fixes commit 75e3626a785919f43fdcf2127c2b10e3e4df2f9f Author: Joshua Yanovski Date: Wed Jul 8 02:21:52 2020 +0200 OpenGL 3.3 minimum. commit 18a08e8fe2739f02af99a5d2cb4e7c38c49e858b Author: Monty Marz Date: Tue Jul 7 23:57:52 2020 +0200 settings localization commit 90c5d1ca3620092bc3ae10b2211489eb1cf6f5e8 Author: Joshua Yanovski Date: Tue Jul 7 21:11:48 2020 +0200 Lower near distance. commit 0e66f02b25aaddaa2dfddbbc89cd67de54a9a7b4 Author: Joshua Yanovski Date: Tue Jul 7 20:09:01 2020 +0200 All sprites sway in the wind now. commit db1401a6910bf42dcf502462c90038752ff5fbdb Merge: 69e508d8c e8b4b29d7 Author: Joshua Yanovski Date: Tue Jul 7 19:34:17 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 69e508d8c94d8973033817ca86f357e466fc7c4d Author: Joshua Yanovski Date: Tue Jul 7 18:41:37 2020 +0200 Make it easy to switch to SIMD for math. commit ffe0f5928c7a7e87d00cb5426b3b1d831d7e02fd Author: Joshua Yanovski Date: Sat Jul 4 21:21:12 2020 +0200 Fix some issues with underwater rendering. commit bfda6da42f38fd02c31ee92c81ab785a3e50c2a0 Author: Joshua Yanovski Date: Sat Jul 4 19:17:59 2020 +0200 Fix some minor display issues. commit 0ed752e087968cf901301884aaeae698e32ef8a5 Merge: ccc6a06a8 518edcb85 Author: Joshua Yanovski Date: Sat Jul 4 18:14:21 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit ccc6a06a8d4504d6b9f7af7905a414d2ee06ab76 Author: Joshua Yanovski Date: Sat Jul 4 18:04:34 2020 +0200 Some minor changes. commit 4e020246702889269efe1a191788992164c508d6 Merge: 50a64d927 e05c9267a Author: Joshua Yanovski Date: Sat Jul 4 16:17:40 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit 50a64d927e6c4f4b0e4688e4cbfca694bc3f922a Author: Joshua Yanovski Date: Sat Jul 4 13:07:03 2020 +0200 Fix far plane. commit 7dd06da34cae8f9ea3b6c889fe965181f3fd3949 Author: Joshua Yanovski Date: Thu Jul 2 22:25:35 2020 +0200 Add shadows.glsl. commit 618a18c998778bf871b905a74657440fcc384c80 Author: Joshua Yanovski Date: Thu Jul 2 22:10:22 2020 +0200 Adding shadows, greedy meshing, and more. commit eaea83fe6a5cb1cb0ba8beef888183c718258496 Merge: 267018495 2f89b863e Author: Joshua Yanovski Date: Thu May 21 22:47:07 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 2670184954a13e4a9e7a4e35ba79aac0c5fac2f7 Author: Joshua Yanovski Date: Thu May 21 21:20:01 2020 +0200 Make civsim and sites deterministic. For anything in worldgen where you use a HashMap, *please* think carefully about which hasher you are going to use! This is especially true if (for some reason) you are depending on hashmap iteration order remaining stable for some aspect of worldgen. commit f8376fd5dc72b4a9c2f51a6b4570d59c8b8e9343 Merge: 654f7e049 cdee191dd Author: Joshua Yanovski Date: Thu May 21 17:53:57 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 654f7e049258d9da27c09608bbe6a46ffa8787e5 Author: Joshua Yanovski Date: Wed May 20 21:22:30 2020 +0200 Correct backface culling. commit 560501df05ca725409b0f2e4eb31bdfdc15fd0c7 Author: Joshua Yanovski Date: Tue May 19 17:22:06 2020 +0200 Greedy messhing for shadows. commit a4d87e1875ca543436e6bbbeb20348facc5a52d7 Author: Joshua Yanovski Date: Sun May 17 05:59:00 2020 +0200 Shadow maps work for lantern. commit 243d0837b8a3b08172c9a3c348d964d7ddc2a0a8 Merge: 04382dc28 71dd520cd Author: Joshua Yanovski Date: Fri May 15 14:53:13 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 04382dc28632b6c66e8821f6870c0daaa1c1901a Author: Joshua Yanovski Date: Fri May 15 14:22:17 2020 +0200 WIP: better graphics config, better LOD, shadow maps. commit 22ddbad3eb32bfa70f0932176022208fe67ded81 Author: Joshua Yanovski Date: Sat May 2 18:54:09 2020 +0200 Minor shader fixes. commit 746a10e8d01ac235f994b0cde78fa48998602a1f Merge: 0f4a0e763 40ab94673 Author: Joshua Yanovski Date: Sat May 2 04:02:09 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 0f4a0e763db3afbdc4fb0558611f38326ca87151 Author: Joshua Yanovski Date: Fri May 1 23:03:24 2020 +0200 Switch back to pop-in terrain. commit dd74fa7e4a53667b38811d7aec58d7f6a68889bb Author: Joshua Yanovski Date: Fri May 1 22:58:55 2020 +0200 LOD shading closer to voxel shading. commit ef67bd58ba0bdaf622b078892d768263b6cba268 Author: Joshua Yanovski Date: Tue Apr 28 20:49:03 2020 +0200 Experimental underwater lighting. commit 2c5ad9d07605e80d5fd5679dd3a5d70c605b86a9 Merge: 748279835 303967a6f Author: Joshua Yanovski Date: Mon Apr 27 22:35:24 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 7482798354eda18c8207950080fc24d443a369de Author: Joshua Yanovski Date: Mon Apr 27 21:59:55 2020 +0200 Replace discard in figure-frag. commit d83b4ae69be4912f69bf7835f509c68a1c7770a4 Author: Joshua Yanovski Date: Mon Apr 27 18:45:57 2020 +0200 Fix sprite lighting, HDR from focus_pos. commit 0594238004f116274905fa0ed7c2b6c417ed1d29 Author: Joshua Yanovski Date: Mon Apr 27 18:14:10 2020 +0200 Proper HDR from point lights. commit 48c93d2b41ce5743103d18e76cc228f5ac766492 Author: Joshua Yanovski Date: Mon Apr 27 14:01:43 2020 +0200 Brighter ambiance, darker LOD shadows. commit e0452e895ccfa8a43f368cae3b8b8aedc24dad93 Author: Joshua Yanovski Date: Mon Apr 27 13:13:23 2020 +0200 More proper HDR. commit 4c6da3ed16cfeebb455e40da5f557b4af9a499d7 Author: Joshua Yanovski Date: Mon Apr 27 00:13:10 2020 +0200 Trying LOD noise. commit 682a3d74c85df5503ffe1fc1cc891bce789df1ad Author: Joshua Yanovski Date: Sun Apr 26 23:11:08 2020 +0200 Fix LOD heights in towns. commit cc39e5734e8b18f9077bf42e66337c1319dd6b6e Author: Joshua Yanovski Date: Sun Apr 26 21:01:23 2020 +0200 More LOD fixes. commit 8116b21c2e51c836e77894e309ac273caf2917b3 Author: Joshua Yanovski Date: Sat Apr 25 23:54:43 2020 +0200 I like this coloring. commit bc2560ea90b36f4b46190005344232d993043ac3 Merge: 14effdd5d e690efe71 Author: Joshua Yanovski Date: Sat Apr 25 23:48:33 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit 14effdd5db8747fcf3eb34f03b46b7792e92d1c5 Author: Joshua Yanovski Date: Sat Apr 25 22:24:35 2020 +0200 Re-saturate. commit 48a643955d4435b78179acba0beb4a979905cc31 Author: Joshua Yanovski Date: Sat Apr 25 22:23:57 2020 +0200 Various fixes. commit f7b497a0c25f4ad4682c71dd1ed6f608052feb9b Author: Joshua Yanovski Date: Sat Apr 25 03:22:49 2020 +0200 Render figures again. commit 44e4aad48deba8266b9f8bdfd3db096b381f5327 Merge: e6f0a5a98 9ec319a18 Author: Joshua Yanovski Date: Sat Apr 25 02:01:04 2020 +0200 Merge remote-tracking branch 'origin/master' into sharp/small-fixes commit e6f0a5a981a82533ba188fff0a54ce98577bb152 Author: Joshua Yanovski Date: Fri Apr 24 16:12:20 2020 +0200 Add atmospheric scattering. commit f2953087f691a8edbc001cb98ebf5059fc6f8ac0 Author: Joshua Yanovski Date: Thu Apr 23 00:01:20 2020 +0200 Fix shadowing for specular reflections. commit ddd4a67a9799b8d08c7dc0c2bec90621c2bed0e3 Author: Joshua Yanovski Date: Wed Apr 22 22:56:12 2020 +0200 HDR fixes. commit 1015e60deef3c447381996277df7496707a63bd0 Author: Joshua Yanovski Date: Tue Apr 21 18:25:19 2020 +0200 More lighting changes. commit 80c264abd111fb237e57843efcb5c071d4f84613 Author: Joshua Yanovski Date: Mon Apr 13 00:29:59 2020 +0200 Lighting experiments. commit 8414987e589dd5102bad89762a3adaccc0bfe957 Author: Joshua Yanovski Date: Thu Apr 9 02:38:40 2020 +0200 WIP -- lighting changes and soft shadows. commit 9cd2b3fb0d6b49776c6dfe463e4169637250b11a Merge: c7ea687eb 8b149ad11 Author: Joshua Yanovski Date: Sat Apr 4 02:33:29 2020 +0200 Merge branch 'sharp/new-lighting' into sharp/small-fixes commit c7ea687ebbc5aace1b455f134a5bf49ce2c7434a Merge: 476441531 22f3319b4 Author: Joshua Yanovski Date: Sat Apr 4 02:33:02 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes commit 8b149ad11ad4bb75018fcf9519baec27d1c95951 Author: Joshua Yanovski Date: Sat Apr 4 02:32:39 2020 +0200 Trying out a new lighting model. commit b0ac9f36f755dd06f7c17c46150568064790864f Author: Joshua Yanovski Date: Fri Apr 3 07:56:11 2020 +0200 Use bicubic interpolation for terrain. commit f6fc9307a121514b614bc50ef8eb055953e7da8a Merge: 33140a295 22f3319b4 Author: Joshua Yanovski Date: Fri Apr 3 05:01:41 2020 +0200 Merge remote-tracking branch 'origin/master' into zesterer/lod commit 4764415312aae6e9ac2d00e86e84577cb958f1ab Merge: ed2d0111d 13388ee6a Author: Joshua Yanovski Date: Fri Apr 3 04:54:48 2020 +0200 Merge branch 'sharp/map-colors' into sharp/small-fixes commit 13388ee6a42943f3f79a9cb488346cef18e272fe Author: Joshua Yanovski Date: Thu Apr 2 20:30:08 2020 +0200 Various fixes (to coloring and to soft shadows). commit fbd084a94a067082a87cd6d28b82054709bc9265 Merge: 5a089863b 4fdf6896a Author: Joshua Yanovski Date: Thu Apr 2 18:50:38 2020 +0200 Merge branch 'master' of gitlab.com:veloren/veloren into sharp/map-colors commit ed2d0111d994262ae836d84d1fe5a45e4de72a0b Author: Joshua Yanovski Date: Thu Apr 2 06:49:27 2020 +0200 Combining colors and LOD. commit 88342640c6b835114d01c797b89fdede3b0a2108 Merge: 33140a295 5a089863b Author: Joshua Yanovski Date: Thu Apr 2 04:49:20 2020 +0200 Merge branch 'sharp/map-colors' into sharp/small-fixes commit 33140a2951b8212725f42c758b277aaec4d888f7 Merge: 4c65a5aed f34d4b379 Author: Joshua Yanovski Date: Thu Apr 2 04:36:21 2020 +0200 Merge remote-tracking branch 'origin/master' into zesterer/lod commit 5a089863beb01d4794bbe3580ada47b278715ea2 Author: Joshua Yanovski Date: Thu Apr 2 03:17:49 2020 +0200 Making maps brighter. This is probably not the right way to do it, but oh well! commit 32b2c99109dd486aa922886081068f9c550c83f2 Author: Joshua Yanovski Date: Thu Apr 2 02:46:36 2020 +0200 Horizon mapping and "layered" map generation. Horizon mapping is a method of shadow mapping specific to height maps. It can handle any angle between 0 and 90 degrees from the ground, as long as know the horizontal direction in advance, by remembering only a single angle (the "horizon angle" of the shadow map). More is explained in common/src/msg/server.rs. We also remember the approximate height of the largest occluder, to try to be able to generate soft shadows and create a vertical position where the shadows can't go higher. Additionally, map generation has been reworked. Instead of computing everything from explicit samples, we pass in sampling functions that return exactly what the map generator needs. This allows us to cleanly separate the way we sample things like altitudes and colors from the map generation process. We exploit this to generate maps *partially* on the server (with colors and rivers, but not shading). We can then send the partially completed map to the client, which can combine it with shadow information to generate the final map. This is useful for two reasons: first, it makes sure the client can apply shadow information by itself, and second, it lets us pass the unshaded map for use with level of detail functionality. For similar reasons, river generation is split out into its own layer, but for now we opt to still generate rivers on the server (since the river wire format is more complicated to compress and may require some extra work to make sure we have enough precision to draw rivers well enough for LoD). Finally, the mostly ad-hoc lighting we were performing has been (mostly) replaced with explicit Phong reflection shading (including specular highlights). Regularizing this seems useful and helps clarify the "meaning" of the various light intensities, and helps us keep a more physically plausible basis. However, its interaction with soft shadows is still imperfect, and it's not yet clear to me what we need to do to turn this into something useful for LoD. commit f8926a5737ddc51f3d585c651a64c43677aae0f4 Merge: a1aee931e 875ae6ced Author: Joshua Yanovski Date: Fri Mar 13 13:32:42 2020 +0100 Merge remote-tracking branch 'origin/master' into sharp/map-colors commit 4c65a5aed353b119aea65a2aaeb94549b67beb42 Author: Treeco <5021038-Treeco@users.noreply.gitlab.com> Date: Mon Feb 24 16:48:05 2020 +0000 Made LOD setting slider exponential commit 2fa7b2d20d7233dc8bfd64f9f7f54617575248f1 Author: Joshua Barretto Date: Mon Feb 24 17:49:53 2020 +0000 Added mist to LoD commit aab059a450b5f635777129ff82cc15b662965c3c Author: Joshua Barretto Date: Mon Feb 24 15:14:06 2020 +0000 Added LoD slider commit 779c36b538121c5ade3633ae5cb67bb14c8c3877 Author: Joshua Barretto Date: Mon Feb 24 12:54:55 2020 +0000 Reduced cost of vertex pushing commit 9fea150473906b166365b738ebcea07c697daf3d Author: Joshua Barretto Date: Mon Feb 24 12:38:53 2020 +0000 Fixed maths, improved LoD resolution commit 5481df38fea5bf183ff376a3337179cfaa5233dc Author: Joshua Barretto Date: Mon Feb 24 11:22:50 2020 +0000 Dynamically relocate LoD vertices to enhance details commit a3e36a50ababd615da7db1b26158c7906a5def01 Author: Joshua Barretto Date: Sun Feb 23 18:13:51 2020 +0000 Simpler terrain spiral rendering commit 255f450ae9ac8763db4bede075fb409161ed57cc Author: Joshua Barretto Date: Sun Feb 23 16:53:17 2020 +0000 Better LoD precision commit 3d027aebe812a5b8658a4eb8123dc9f61b3776d2 Author: Joshua Barretto Date: Sun Feb 23 16:04:03 2020 +0000 Better falloff commit be775c9484b457b2c0b1a494aec03392d0c70e76 Author: Joshua Barretto Date: Sun Feb 23 15:30:45 2020 +0000 Applied good ideas from experimental branch commit 58587b68545a23c5c04ab4574a4b94b3bc982246 Author: Joshua Barretto Date: Fri Feb 21 16:15:13 2020 +0000 Minor fixes to LoD merging commit 7b42aebd709c14df2db766aad61d9280ad24d84d Author: Joshua Barretto Date: Fri Feb 21 15:04:44 2020 +0000 Capped LoD dragging commit 8aafc559f87124e1ea5ca6e3ddc2aa0c242d793c Author: Joshua Barretto Date: Fri Feb 21 14:54:37 2020 +0000 Better blending between LoD and terrain border commit edd3455d5161792d87ddc8eadc0ecbad5532b284 Author: Joshua Barretto Date: Fri Feb 21 14:40:19 2020 +0000 Fixed LoD z depth, added sea level offset commit b9b06744620114dd5556e73f64fa93c145503a7c Author: Joshua Barretto Date: Fri Feb 21 14:27:43 2020 +0000 Better LoD smoothing commit a1aee931e790431560cd2d953ad61d9497072afd Author: Joshua Yanovski Date: Fri Feb 21 14:52:17 2020 +0100 Adding shadows. commit 2400786c13dd891c131ed86d48d05df516a8a778 Author: Joshua Barretto Date: Fri Feb 21 13:48:40 2020 +0000 Use world map as LoD source commit dbf650f504a4c25fbbc2096ac3616c736bf52d23 Author: Joshua Barretto Date: Mon Jan 20 00:48:14 2020 +0000 Better clouds at distance commit 5e6f81b86cdb9730b9b056877b19257075fd5fa8 Author: Joshua Barretto Date: Sun Jan 19 23:59:02 2020 +0000 sync commit 745e7540ddb000cc645f612767b337c2ddc3f7c0 Author: Joshua Barretto Date: Fri Nov 22 12:40:48 2019 +0000 Improved cloud falloff mist, faster noise sampling commit f6a200d0cb866196ba57697466755f9e0c7ea5d8 Author: Joshua Barretto Date: Fri Nov 22 10:09:00 2019 +0000 Improved long-range depth precision, removed unnecessary LoD polygons commit 63d1b2bb2292898d59fb4f4e502201103dfeb86f Author: Joshua Barretto Date: Thu Nov 21 20:57:46 2019 +0000 Working LoD shader commit f13d98ee3e58f881e8b978861a67663b59ed91ec Author: Joshua Barretto Date: Thu Nov 21 11:03:40 2019 +0000 LoD first attempt (stack overflow issue) --- CHANGELOG.md | 14 + Cargo.lock | 136 +- Cargo.toml | 6 +- assets/common/npc_names.json | 28 + assets/voxygen/i18n/de_DE.ron | 9 + assets/voxygen/i18n/en.ron | 12 + assets/voxygen/shaders/figure-frag.glsl | 201 +- assets/voxygen/shaders/figure-vert.glsl | 119 +- assets/voxygen/shaders/fluid-frag/cheap.glsl | 196 +- assets/voxygen/shaders/fluid-frag/shiny.glsl | 253 +- assets/voxygen/shaders/fluid-vert.glsl | 79 +- .../voxygen/shaders/include/cloud/none.glsl | 2 - .../shaders/include/cloud/regular.glsl | 33 +- assets/voxygen/shaders/include/constants.glsl | 73 + assets/voxygen/shaders/include/globals.glsl | 4 + assets/voxygen/shaders/include/light.glsl | 177 +- assets/voxygen/shaders/include/lod.glsl | 289 + assets/voxygen/shaders/include/random.glsl | 2 + assets/voxygen/shaders/include/shadows.glsl | 218 + assets/voxygen/shaders/include/sky.glsl | 684 ++- assets/voxygen/shaders/include/srgb.glsl | 615 ++- .../shaders/light-shadows-directed-frag.glsl | 48 + .../shaders/light-shadows-directed-vert.glsl | 63 + .../shaders/light-shadows-figure-vert.glsl | 74 + .../voxygen/shaders/light-shadows-frag.glsl | 49 + .../voxygen/shaders/light-shadows-geom.glsl | 277 + .../voxygen/shaders/light-shadows-vert.glsl | 55 + assets/voxygen/shaders/lod-terrain-frag.glsl | 632 +++ assets/voxygen/shaders/lod-terrain-vert.glsl | 104 + assets/voxygen/shaders/particle-frag.glsl | 78 +- assets/voxygen/shaders/particle-vert.glsl | 20 +- .../voxygen/shaders/player-shadow-frag.glsl | 34 +- assets/voxygen/shaders/postprocess-frag.glsl | 146 +- assets/voxygen/shaders/postprocess-vert.glsl | 18 +- assets/voxygen/shaders/skybox-frag.glsl | 47 +- assets/voxygen/shaders/skybox-vert.glsl | 34 +- assets/voxygen/shaders/sprite-frag.glsl | 193 +- assets/voxygen/shaders/sprite-vert.glsl | 242 +- assets/voxygen/shaders/terrain-frag.glsl | 370 +- assets/voxygen/shaders/terrain-vert.glsl | 165 +- assets/voxygen/shaders/ui-vert.glsl | 29 +- .../figure/eyes/general/female_blind-0.vox | 2 +- .../figure/eyes/general/male_blind-0.vox | 2 +- .../voxygen/voxel/humanoid_color_manifest.ron | 285 + assets/voxygen/voxel/sprite/pumpkin/1.vox | 2 +- assets/voxygen/voxel/sprite/pumpkin/2.vox | 2 +- assets/voxygen/voxel/sprite/pumpkin/3.vox | 2 +- assets/voxygen/voxel/sprite/pumpkin/4.vox | 2 +- assets/voxygen/voxel/sprite/pumpkin/5.vox | 2 +- assets/voxygen/voxel/sprite/pumpkin/6.vox | 2 +- assets/voxygen/voxel/sprite/pumpkin/7.vox | 2 +- assets/world/style/colors.ron | 152 + assets/world/tree/acacia/1.vox | 4 +- assets/world/tree/acacia/2.vox | 4 +- assets/world/tree/acacia/3.vox | 4 +- assets/world/tree/acacia/4.vox | 4 +- assets/world/tree/acacia/5.vox | 4 +- assets/world/tree/acacia_3/1.vox | 3 + assets/world/tree/acacia_3/2.vox | 3 + assets/world/tree/acacia_3/3.vox | 3 + assets/world/tree/acacia_3/4.vox | 3 + assets/world/tree/acacia_3/5.vox | 3 + assets/world/tree/acacia_savannah/1.vox | 3 - assets/world/tree/acacia_savannah/2.vox | 3 - assets/world/tree/acacia_savannah/3.vox | 3 - assets/world/tree/acacia_savannah/4.vox | 3 - assets/world/tree/acacia_savannah/5.vox | 3 - client/Cargo.toml | 6 +- client/src/lib.rs | 297 +- common/Cargo.toml | 6 +- common/src/assets/watch.rs | 10 +- common/src/comp/body.rs | 68 +- common/src/comp/body/humanoid.rs | 495 +- common/src/comp/character_state.rs | 9 +- common/src/lib.rs | 3 + common/src/msg/server.rs | 123 +- common/src/terrain/block.rs | 4 +- common/src/terrain/map.rs | 713 +++ common/src/terrain/mod.rs | 158 +- common/src/terrain/structure.rs | 43 +- common/src/typed.rs | 243 + common/src/util/color.rs | 13 +- common/src/vol.rs | 2 +- common/src/volumes/scaled.rs | 30 +- network/src/message.rs | 40 +- server/Cargo.toml | 2 +- server/src/chunk_generator.rs | 19 +- server/src/cmd.rs | 2 +- server/src/events/entity_creation.rs | 2 +- server/src/events/entity_manipulation.rs | 8 + server/src/events/inventory_manip.rs | 2 + server/src/lib.rs | 80 +- server/src/state_ext.rs | 2 - server/src/sys/terrain.rs | 5 +- server/src/test_world.rs | 32 +- voxygen/Cargo.toml | 10 +- voxygen/benches/meshing_benchmark.rs | 7 +- voxygen/clippy.toml | 2 + voxygen/examples/character_renderer.rs | 12 +- voxygen/src/anim/Cargo.toml | 2 +- voxygen/src/anim/src/biped_large/alpha.rs | 92 +- voxygen/src/anim/src/biped_large/idle.rs | 75 +- voxygen/src/anim/src/biped_large/jump.rs | 74 +- voxygen/src/anim/src/biped_large/mod.rs | 136 +- voxygen/src/anim/src/biped_large/run.rs | 76 +- voxygen/src/anim/src/biped_large/wield.rs | 105 +- voxygen/src/anim/src/bird_medium/feed.rs | 35 +- voxygen/src/anim/src/bird_medium/fly.rs | 35 +- voxygen/src/anim/src/bird_medium/idle.rs | 34 +- voxygen/src/anim/src/bird_medium/mod.rs | 79 +- voxygen/src/anim/src/bird_medium/run.rs | 35 +- voxygen/src/anim/src/bird_small/idle.rs | 18 +- voxygen/src/anim/src/bird_small/jump.rs | 18 +- voxygen/src/anim/src/bird_small/mod.rs | 75 +- voxygen/src/anim/src/bird_small/run.rs | 18 +- voxygen/src/anim/src/character/alpha.rs | 348 +- voxygen/src/anim/src/character/beta.rs | 63 +- voxygen/src/anim/src/character/block.rs | 105 +- voxygen/src/anim/src/character/blockidle.rs | 182 +- voxygen/src/anim/src/character/charge.rs | 105 +- voxygen/src/anim/src/character/climb.rs | 163 +- voxygen/src/anim/src/character/dance.rs | 94 +- voxygen/src/anim/src/character/dash.rs | 145 +- voxygen/src/anim/src/character/equip.rs | 152 +- voxygen/src/anim/src/character/glidewield.rs | 177 +- voxygen/src/anim/src/character/gliding.rs | 68 +- voxygen/src/anim/src/character/idle.rs | 88 +- voxygen/src/anim/src/character/jump.rs | 108 +- voxygen/src/anim/src/character/leapmelee.rs | 55 +- voxygen/src/anim/src/character/mod.rs | 161 +- voxygen/src/anim/src/character/roll.rs | 98 +- voxygen/src/anim/src/character/run.rs | 96 +- voxygen/src/anim/src/character/shoot.rs | 113 +- voxygen/src/anim/src/character/sit.rs | 81 +- voxygen/src/anim/src/character/sneak.rs | 147 +- voxygen/src/anim/src/character/spin.rs | 75 +- voxygen/src/anim/src/character/spinmelee.rs | 74 +- voxygen/src/anim/src/character/stand.rs | 82 +- voxygen/src/anim/src/character/swim.rs | 87 +- voxygen/src/anim/src/character/swimwield.rs | 248 +- voxygen/src/anim/src/character/wield.rs | 241 +- voxygen/src/anim/src/critter/idle.rs | 23 +- voxygen/src/anim/src/critter/jump.rs | 22 +- voxygen/src/anim/src/critter/mod.rs | 71 +- voxygen/src/anim/src/critter/run.rs | 24 +- voxygen/src/anim/src/dragon/fly.rs | 66 +- voxygen/src/anim/src/dragon/idle.rs | 68 +- voxygen/src/anim/src/dragon/mod.rs | 144 +- voxygen/src/anim/src/dragon/run.rs | 68 +- voxygen/src/anim/src/fish_medium/idle.rs | 26 +- voxygen/src/anim/src/fish_medium/jump.rs | 26 +- voxygen/src/anim/src/fish_medium/mod.rs | 88 +- voxygen/src/anim/src/fish_medium/run.rs | 26 +- voxygen/src/anim/src/fish_small/idle.rs | 10 +- voxygen/src/anim/src/fish_small/jump.rs | 10 +- voxygen/src/anim/src/fish_small/mod.rs | 65 +- voxygen/src/anim/src/fish_small/run.rs | 10 +- voxygen/src/anim/src/fixture/mod.rs | 50 +- voxygen/src/anim/src/golem/idle.rs | 51 +- voxygen/src/anim/src/golem/jump.rs | 46 +- voxygen/src/anim/src/golem/mod.rs | 107 +- voxygen/src/anim/src/golem/run.rs | 52 +- voxygen/src/anim/src/lib.rs | 152 +- voxygen/src/anim/src/object/mod.rs | 53 +- voxygen/src/anim/src/quadruped_low/alpha.rs | 47 +- voxygen/src/anim/src/quadruped_low/idle.rs | 48 +- voxygen/src/anim/src/quadruped_low/jump.rs | 46 +- voxygen/src/anim/src/quadruped_low/mod.rs | 101 +- voxygen/src/anim/src/quadruped_low/run.rs | 58 +- .../src/anim/src/quadruped_medium/alpha.rs | 66 +- voxygen/src/anim/src/quadruped_medium/idle.rs | 66 +- voxygen/src/anim/src/quadruped_medium/jump.rs | 66 +- voxygen/src/anim/src/quadruped_medium/mod.rs | 147 +- voxygen/src/anim/src/quadruped_medium/run.rs | 131 +- voxygen/src/anim/src/quadruped_small/feed.rs | 40 +- voxygen/src/anim/src/quadruped_small/idle.rs | 35 +- voxygen/src/anim/src/quadruped_small/jump.rs | 34 +- voxygen/src/anim/src/quadruped_small/mod.rs | 78 +- voxygen/src/anim/src/quadruped_small/run.rs | 56 +- voxygen/src/anim/src/vek.rs | 8 + voxygen/src/audio/mod.rs | 10 +- .../src/audio/sfx/event_mapper/combat/mod.rs | 3 +- .../audio/sfx/event_mapper/movement/mod.rs | 3 +- voxygen/src/audio/sfx/mod.rs | 6 +- voxygen/src/hud/item_imgs.rs | 2 +- voxygen/src/hud/map.rs | 23 +- voxygen/src/hud/minimap.rs | 6 +- voxygen/src/hud/mod.rs | 117 +- voxygen/src/hud/overhead.rs | 29 +- voxygen/src/hud/overitem.rs | 8 +- voxygen/src/hud/settings_window.rs | 257 +- voxygen/src/lib.rs | 4 +- voxygen/src/menu/char_selection/mod.rs | 10 +- voxygen/src/menu/main/ui.rs | 7 +- voxygen/src/mesh/greedy.rs | 636 +++ voxygen/src/mesh/mod.rs | 23 +- voxygen/src/mesh/segment.rs | 476 +- voxygen/src/mesh/terrain.rs | 384 +- voxygen/src/mesh/vol.rs | 224 - voxygen/src/render/consts.rs | 11 +- voxygen/src/render/error.rs | 1 + voxygen/src/render/instances.rs | 2 +- voxygen/src/render/mesh.rs | 40 +- voxygen/src/render/mod.rs | 198 +- voxygen/src/render/model.rs | 13 +- voxygen/src/render/pipelines/figure.rs | 116 +- voxygen/src/render/pipelines/fluid.rs | 27 +- voxygen/src/render/pipelines/lod_terrain.rs | 117 + voxygen/src/render/pipelines/mod.rs | 79 +- voxygen/src/render/pipelines/particle.rs | 26 +- voxygen/src/render/pipelines/shadow.rs | 90 + voxygen/src/render/pipelines/skybox.rs | 7 +- voxygen/src/render/pipelines/sprite.rs | 99 +- voxygen/src/render/pipelines/terrain.rs | 90 +- voxygen/src/render/renderer.rs | 1326 ++++- voxygen/src/render/texture.rs | 74 +- voxygen/src/run.rs | 10 +- voxygen/src/scene/camera.rs | 20 +- voxygen/src/scene/figure/cache.rs | 887 +-- voxygen/src/scene/figure/load.rs | 931 ++-- voxygen/src/scene/figure/mod.rs | 2253 ++++---- voxygen/src/scene/lod.rs | 97 + voxygen/src/scene/math.rs | 390 ++ voxygen/src/scene/mod.rs | 713 ++- voxygen/src/scene/particle.rs | 96 +- voxygen/src/scene/simple.rs | 219 +- voxygen/src/scene/terrain.rs | 4839 ++++++++--------- voxygen/src/session.rs | 69 +- voxygen/src/settings.rs | 13 +- voxygen/src/ui/cache.rs | 48 +- voxygen/src/ui/graphic/mod.rs | 260 +- voxygen/src/ui/graphic/pixel_art.rs | 4 +- voxygen/src/ui/graphic/renderer.rs | 2 +- voxygen/src/ui/img_ids.rs | 2 +- voxygen/src/ui/mod.rs | 422 +- voxygen/src/ui/widgets/ingame.rs | 6 +- voxygen/src/window.rs | 6 +- world/Cargo.toml | 6 +- world/examples/settlement_viewer.rs | 8 +- world/examples/view.rs | 6 +- world/examples/water.rs | 209 +- world/src/block/mod.rs | 158 +- world/src/block/natural.rs | 4 +- world/src/civ/mod.rs | 24 +- world/src/column/mod.rs | 215 +- world/src/config.rs | 2 +- world/src/index.rs | 97 +- world/src/layer/mod.rs | 24 +- world/src/lib.rs | 80 +- world/src/sim/erosion.rs | 236 +- world/src/sim/map.rs | 588 +- world/src/sim/mod.rs | 1491 ++--- world/src/sim/util.rs | 251 +- world/src/site/castle/mod.rs | 19 +- world/src/site/dungeon/mod.rs | 36 +- world/src/site/mod.rs | 17 +- .../settlement/building/archetype/house.rs | 144 +- .../settlement/building/archetype/keep.rs | 71 +- .../site/settlement/building/archetype/mod.rs | 10 +- world/src/site/settlement/building/mod.rs | 10 +- world/src/site/settlement/mod.rs | 150 +- 261 files changed, 23131 insertions(+), 11885 deletions(-) create mode 100644 assets/voxygen/shaders/include/constants.glsl create mode 100644 assets/voxygen/shaders/include/lod.glsl create mode 100644 assets/voxygen/shaders/include/shadows.glsl create mode 100644 assets/voxygen/shaders/light-shadows-directed-frag.glsl create mode 100644 assets/voxygen/shaders/light-shadows-directed-vert.glsl create mode 100644 assets/voxygen/shaders/light-shadows-figure-vert.glsl create mode 100644 assets/voxygen/shaders/light-shadows-frag.glsl create mode 100644 assets/voxygen/shaders/light-shadows-geom.glsl create mode 100644 assets/voxygen/shaders/light-shadows-vert.glsl create mode 100644 assets/voxygen/shaders/lod-terrain-frag.glsl create mode 100644 assets/voxygen/shaders/lod-terrain-vert.glsl create mode 100644 assets/voxygen/voxel/humanoid_color_manifest.ron create mode 100644 assets/world/style/colors.ron create mode 100644 assets/world/tree/acacia_3/1.vox create mode 100644 assets/world/tree/acacia_3/2.vox create mode 100644 assets/world/tree/acacia_3/3.vox create mode 100644 assets/world/tree/acacia_3/4.vox create mode 100644 assets/world/tree/acacia_3/5.vox delete mode 100644 assets/world/tree/acacia_savannah/1.vox delete mode 100644 assets/world/tree/acacia_savannah/2.vox delete mode 100644 assets/world/tree/acacia_savannah/3.vox delete mode 100644 assets/world/tree/acacia_savannah/4.vox delete mode 100644 assets/world/tree/acacia_savannah/5.vox create mode 100644 common/src/terrain/map.rs create mode 100644 common/src/typed.rs create mode 100644 voxygen/clippy.toml create mode 100644 voxygen/src/anim/src/vek.rs create mode 100644 voxygen/src/mesh/greedy.rs delete mode 100644 voxygen/src/mesh/vol.rs create mode 100644 voxygen/src/render/pipelines/lod_terrain.rs create mode 100644 voxygen/src/render/pipelines/shadow.rs create mode 100644 voxygen/src/scene/lod.rs create mode 100644 voxygen/src/scene/math.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index fd43ba4018..55245f8670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- New level of detail feature, letting you see all the world's terrain at any view distance. +- Point and directional lights now cast realistic shadows, using shadow mapping. + ### Changed - Fixed a bug where leaving the Settings menu by pressing "N" in single player kept the game paused +- The world map has been refactored to support arbitrary sizes and compute horizon maps. +- Veloren's lighting has been completely overhauled. +- The graphics options were made much more flexible and configurable. +- Many shader optimizations. +- Voxel model creation was switched to use greedy meshing, improving performance. +- Animation and terrain math were switched to use SIMD where possible, improving performance. +- The way we cache glyphs was refactored, fixed, and optimized. +- Colors for models and figures were adjusted to account for the saturation hack. ### Removed +- MSAA has been removed due to incompatibility with greedy meshing. +- Removed a saturation hack that led to colors being improperly displayed. + ## [0.7.0] - 2020-08-15 ### Added diff --git a/Cargo.lock b/Cargo.lock index 85274aa25a..259543ab2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,6 +381,12 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +[[package]] +name = "bytemuck" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7a1029718df60331e557c9e83a55523c955e5dd2a7bfeffad6bbd50b538ae9" + [[package]] name = "byteorder" version = "0.5.3" @@ -581,7 +587,7 @@ dependencies = [ [[package]] name = "conrod_core" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" +source = "git+https://gitlab.com/veloren/conrod.git#09d4675c4549b0fa5b6a87eacf6e1851876a80b8" dependencies = [ "conrod_derive", "copypasta", @@ -596,7 +602,7 @@ dependencies = [ [[package]] name = "conrod_derive" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" +source = "git+https://gitlab.com/veloren/conrod.git#09d4675c4549b0fa5b6a87eacf6e1851876a80b8" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", @@ -606,7 +612,7 @@ dependencies = [ [[package]] name = "conrod_winit" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" +source = "git+https://gitlab.com/veloren/conrod.git#09d4675c4549b0fa5b6a87eacf6e1851876a80b8" [[package]] name = "const-random" @@ -1045,9 +1051,9 @@ checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "deflate" -version = "0.7.20" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", "byteorder 1.3.4", @@ -1224,30 +1230,18 @@ name = "euc" version = "0.5.1" source = "git+https://github.com/zesterer/euc.git#c9a7c17a03d45fce00caeeca09afa1e1558cd183" dependencies = [ - "vek", + "vek 0.11.2", ] [[package]] name = "euclid" -version = "0.19.9" +version = "0.20.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "596b99621b9477e7a5f94d2d8dd13a9c5c302ac358b822c67a42b6f1054450e1" +checksum = "555d51b9a929edb14183fad621e2d5736fc8760707a24246047288d4c142b6bd" dependencies = [ - "euclid_macros", "num-traits", ] -[[package]] -name = "euclid_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "failure" version = "0.1.8" @@ -1878,8 +1872,9 @@ dependencies = [ [[package]] name = "guillotiere" -version = "0.4.2" -source = "git+https://github.com/Imberflur/guillotiere#42c298f5bcf0f95f1a004360d05e25ca3711e9ed" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47065d052e2f000066c4ffbea7051e55bff5c1532c400fc1e269492b2474ccc1" dependencies = [ "euclid", "svg_fmt", @@ -2075,13 +2070,14 @@ dependencies = [ [[package]] name = "image" -version = "0.22.5" +version = "0.23.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed2ada878397b045454ac7cfb011d73132c59f31a955d230bd1f1c2e68eb4a" +checksum = "543904170510c1b5fb65140485d84de4a57fddb2ed685481e9020ce3d2c9f64c" dependencies = [ + "bytemuck", "byteorder 1.3.4", "num-iter", - "num-rational", + "num-rational 0.3.0", "num-traits", "png", ] @@ -2095,15 +2091,6 @@ dependencies = [ "autocfg 1.0.0", ] -[[package]] -name = "inflate" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" -dependencies = [ - "adler32", -] - [[package]] name = "inotify" version = "0.8.3" @@ -2471,6 +2458,15 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + [[package]] name = "mio" version = "0.6.22" @@ -2686,7 +2682,7 @@ dependencies = [ "num-complex", "num-integer", "num-iter", - "num-rational", + "num-rational 0.2.4", "num-traits", ] @@ -2744,6 +2740,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.12" @@ -3091,14 +3098,14 @@ dependencies = [ [[package]] name = "png" -version = "0.15.3" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef859a23054bbfee7811284275ae522f0434a3c8e7f4b74bd4a35ae7e1c4a283" +checksum = "dfe7f9f1c730833200b134370e1d5098964231af8450bce9b78ee3ab5278b970" dependencies = [ "bitflags", "crc32fast", "deflate", - "inflate", + "miniz_oxide", ] [[package]] @@ -3553,9 +3560,9 @@ dependencies = [ [[package]] name = "roots" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c67c712ab62be58b24ab8960e2b95dd4ee00aac115c76f2709657821fe376d" +checksum = "84348444bd7ad45729d0c49a4240d7cdc11c9d512c06c5ad1835c1ad4acda6db" [[package]] name = "route-recognizer" @@ -3778,6 +3785,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "sha1" version = "0.6.0" @@ -4043,9 +4061,9 @@ checksum = "da5b4a0c9f3c7c8e891e445a7c776627e208e8bba23ab680798066dd283e6a15" [[package]] name = "svg_fmt" -version = "0.2.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e5f95e89d737f30cd1f98a9af9a85c2a1cc162cfedfba5a0c54cf92d7206fc" +checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" [[package]] name = "syn" @@ -4440,7 +4458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa14b9f5cd7d513bab5accebe8f403df8b1ac22302cac549a6ac99c0a007c84a" dependencies = [ "num-traits", - "vek", + "vek 0.11.2", ] [[package]] @@ -4608,6 +4626,19 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "vek" +version = "0.12.0" +source = "git+https://gitlab.com/veloren/vek.git?branch=fix_intrinsics#237a78528b505f34f6dde5dc77db3b642388fe4a" +dependencies = [ + "approx", + "num-integer", + "num-traits", + "rustc_version", + "serde", + "static_assertions", +] + [[package]] name = "veloren-chat-cli" version = "0.7.0" @@ -4629,11 +4660,13 @@ dependencies = [ "futures-util", "hashbrown", "image", + "num 0.2.1", "num_cpus", + "rayon", "specs", "tracing", "uvth 3.1.1", - "vek", + "vek 0.12.0", "veloren-common", "veloren_network", ] @@ -4658,14 +4691,16 @@ dependencies = [ "rand 0.7.3", "rayon", "ron", + "roots", "serde", "serde_json", + "serde_repr", "slab", "specs", "specs-idvs", "sum_type", "tracing", - "vek", + "vek 0.12.0", ] [[package]] @@ -4696,7 +4731,7 @@ dependencies = [ "tiny_http", "tracing", "uvth 3.1.1", - "vek", + "vek 0.12.0", "veloren-common", "veloren-world", "veloren_network", @@ -4733,6 +4768,7 @@ dependencies = [ "failure", "gfx", "gfx_device_gl", + "gfx_gl", "gilrs", "git2", "glsl-include", @@ -4757,7 +4793,7 @@ dependencies = [ "tracing-subscriber", "treeculler", "uvth 3.1.1", - "vek", + "vek 0.12.0", "veloren-client", "veloren-common", "veloren-server", @@ -4776,7 +4812,7 @@ dependencies = [ "libloading 0.6.2", "notify", "tracing", - "vek", + "vek 0.12.0", "veloren-common", ] @@ -4787,6 +4823,7 @@ dependencies = [ "arr_macro", "bincode", "bitvec", + "criterion", "fxhash", "hashbrown", "image", @@ -4801,11 +4838,10 @@ dependencies = [ "rand_chacha 0.2.2", "rayon", "ron", - "roots", "serde", "tracing", "tracing-subscriber", - "vek", + "vek 0.12.0", "veloren-common", ] diff --git a/Cargo.toml b/Cargo.toml index e73d2cd0a8..505bbcb4af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,9 @@ opt-level = 2 [profile.no_overflow.package."veloren-world"] opt-level = 3 +[profile.no_overflow.package."veloren-voxygen-anim"] +opt-level = 3 + # this profile is used by developers if dev doesn't has enough debug information, the name must != debug, as debug is used by dev because.... [profile.debuginfo] inherits= 'dev' @@ -74,6 +77,7 @@ debug = false inherits = 'release' debug = 1 -# cpal conflict fix isn't released yet [patch.crates-io] +# cpal conflict fix isn't released yet winit = { git = "https://github.com/Imberflur/winit.git", branch = "macos-test" } +vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics" } diff --git a/assets/common/npc_names.json b/assets/common/npc_names.json index 1491b3f0b6..d24db1222a 100644 --- a/assets/common/npc_names.json +++ b/assets/common/npc_names.json @@ -598,6 +598,34 @@ } } }, + "object": { + "body": { + "keyword": "object", + "names": [] + }, + "species": null + }, + "fish_small": { + "body": { + "keyword": "fish_small", + "names": [] + }, + "species": null + }, + "fish_medium": { + "body": { + "keyword": "fish_medium", + "names": [] + }, + "species": null + }, + "bird_small": { + "body": { + "keyword": "bird_small", + "names": [] + }, + "species": null + }, "quadruped_low": { "body": { "keyword": "quadruped_low", diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE.ron index bf96a3e9b5..04e6bffe0f 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE.ron @@ -303,6 +303,15 @@ magischen Gegenstände ergattern?"#, "hud.settings.bit_depth": "Bittiefe", "hud.settings.refresh_rate": "Bildwiederholrate", "hud.settings.fullscreen": "Vollbild", + "hud.settings.lighting_rendering_mode": "Beleuchtung", + "hud.settings.lighting_rendering_mode.ashikhmin": "Typ A", + "hud.settings.lighting_rendering_mode.blinnphong": "Typ B", + "hud.settings.lighting_rendering_mode.lambertian": "Typ L", + "hud.settings.shadow_rendering_mode": "Schatten", + "hud.settings.shadow_rendering_mode.none": "Keine Schatten", + "hud.settings.shadow_rendering_mode.cheap": "Gering", + "hud.settings.shadow_rendering_mode.map": "Voll", + "hud.settings.lod_detail": "LoD Detail", "hud.settings.save_window_size": "Größe speichern", "hud.settings.music_volume": "Musik Lautstärke", diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 6eb69f292f..a499419b08 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -304,6 +304,18 @@ magically infused items?"#, "hud.settings.refresh_rate": "Refresh Rate", "hud.settings.fullscreen": "Fullscreen", "hud.settings.save_window_size": "Save window size", + "hud.settings.lighting_rendering_mode": "Lighting Rendering Mode", + "hud.settings.lighting_rendering_mode.ashikhmin": "Type A", + "hud.settings.lighting_rendering_mode.blinnphong": "Type B", + "hud.settings.lighting_rendering_mode.lambertian": "Type L", + "hud.settings.shadow_rendering_mode": "Shadow Rendering Mode", + "hud.settings.shadow_rendering_mode.none": "None", + "hud.settings.shadow_rendering_mode.cheap": "Cheap", + "hud.settings.shadow_rendering_mode.map": "Map", + "hud.settings.shadow_rendering_mode.map.resolution": "Resolution", + "hud.settings.lod_detail": "LoD Detail", + "hud.settings.save_window_size": "Save window size", + "hud.settings.music_volume": "Music Volume", "hud.settings.sound_effect_volume": "Sound Effects Volume", diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index 26d85fea88..315807549a 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -1,16 +1,56 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #include in vec3 f_pos; -in vec3 f_col; -in float f_ao; +// in float dummy; +// in vec3 f_col; +// in float f_ao; +// flat in uint f_pos_norm; flat in vec3 f_norm; +/*centroid */in vec2 f_uv_pos; +// in float f_alt; +// in vec4 f_shadow; +// in vec3 light_pos[2]; + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// in vec4 sun_pos; +// #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// const vec4 sun_pos = vec4(0.0); +// #endif + +uniform sampler2D t_col_light; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; layout (std140) uniform u_locals { mat4 model_mat; vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; // bit 0 - is player // bit 1-31 - unused int flags; @@ -18,6 +58,7 @@ uniform u_locals { struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -27,41 +68,155 @@ uniform u_bones { #include #include -#include +#include out vec4 tgt_color; void main() { - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - float point_shadow = shadow_at(f_pos, f_norm); - diffuse_light *= point_shadow; - ambient_light *= point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; + // vec2 texSize = textureSize(t_col_light, 0); + // vec4 col_light = texture(t_col_light, (f_uv_pos + 0.5) / texSize); + // vec3 f_col = col_light.rgb; + // float f_ao = col_light.a; - float ao = pow(f_ao, 0.5) * 0.85 + 0.15; + // vec4 f_col_light = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)); + // vec3 f_col = f_col_light.rgb; + // float f_ao = f_col_light.a; - ambient_light *= ao; - diffuse_light *= ao; + // vec2 f_uv_pos = f_uv_pos + atlas_offs.xy; + vec4 f_col_light = texelFetch(t_col_light, ivec2(f_uv_pos)/* + uv_delta*//* - f_norm * 0.00001*/, 0); + // vec4 f_col_light = texelFetch(t_col_light, ivec2(int(f_uv_pos.x), int(f_uv_pos.y)/* + uv_delta*//* - f_norm * 0.00001*/), 0); + vec3 f_col = /*linear_to_srgb*//*srgb_to_linear*/(f_col_light.rgb); + // vec3 f_col = vec3(1.0); + // vec2 texSize = textureSize(t_col_light, 0); + float f_ao = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; - vec3 surf_color = illuminate(srgb_to_linear(model_col.rgb * f_col), light, diffuse_light, ambient_light); + // vec3 my_chunk_pos = (vec3((uvec3(f_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; + // tgt_color = vec4(hash(floor(vec4(my_chunk_pos.x, 0, 0, 0))), hash(floor(vec4(0, my_chunk_pos.y, 0, 1))), hash(floor(vec4(0, 0, my_chunk_pos.z, 2))), 1.0); + // float f_ao = 0; + // tgt_color = vec4(vec3(f_ao), 1.0); + // tgt_color = vec4(f_col, 1.0); + // return; + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); + + // vec4 light_pos[2]; +//#if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +//#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +//#endif + + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + vec3 view_dir = -cam_to_frag; + + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ + // float sun_light = get_sun_brightness(sun_dir); + // float moon_light = get_moon_brightness(moon_dir); + /* float sun_shade_frac = horizon_at(f_pos, sun_dir); + float moon_shade_frac = horizon_at(f_pos, moon_dir); */ +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float f_alt = alt_at(f_pos.xy); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;// horizon_at2(f_shadow, f_alt, f_pos, moon_dir); + // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). + // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5); + // NOTE: current assumption is that moon and sun shouldn't be out at the sae time. + // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow + // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally). + // float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac; + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); + + vec3 surf_color = /*srgb_to_linear*/(model_col.rgb * f_col); + float alpha = 1.0; + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // vec3 light_frac = /*vec3(1.0);*//*vec3(max(dot(f_norm, -sun_dir) * 0.5 + 0.5, 0.0));*/light_reflection_factor(f_norm, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); + // vec3 point_light = light_at(f_pos, f_norm); + // vec3 light, diffuse_light, ambient_light; + //get_sun_diffuse(f_norm, time_of_day.x, view_dir, k_a * point_shadow * (shade_frac * 0.5 + light_frac * 0.5), k_d * point_shadow * shade_frac, k_s * point_shadow * shade_frac, alpha, emitted_light, reflected_light); + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light); + // reflected_light *= point_shadow * shade_frac; + // emitted_light *= point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= point_shadow * shade_frac; + // reflected_light *= point_shadow; + // emitted_light *= point_shadow; + // max_light *= point_shadow; + + max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + + float ao = f_ao * sqrt(f_ao);//0.25 + f_ao * 0.75; ///*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + + reflected_light *= ao; + emitted_light *= ao; + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + // get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, surf_color * f_light * point_shadow, 0.5 * surf_color * f_light * point_shadow, 0.5 * surf_color * f_light * point_shadow, 2.0, emitted_light, reflected_light); + + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // diffuse_light *= point_shadow; + // ambient_light *= point_shadow; + // vec3 point_light = light_at(f_pos, f_norm); + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + // vec3 surf_color = illuminate(srgb_to_linear(model_col.rgb * f_col), light, diffuse_light, ambient_light); + surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light); + +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif - if ((flags & 1) == 1 && int(cam_mode) == 1) { - float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; + // if ((flags & 1) == 1 && int(cam_mode) == 1) { + // float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; - float opacity = clamp(distance / distance_divider, 0, 1); + // float opacity = clamp(distance / distance_divider, 0, 1); - if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { - discard; - } - } + // // if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { + // // discard; + // // return; + // // } + // } tgt_color = vec4(color, 1.0); } diff --git a/assets/voxygen/shaders/figure-vert.glsl b/assets/voxygen/shaders/figure-vert.glsl index e39718670b..43a9f5decc 100644 --- a/assets/voxygen/shaders/figure-vert.glsl +++ b/assets/voxygen/shaders/figure-vert.glsl @@ -1,16 +1,34 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include +#include in uint v_pos_norm; -in vec3 v_norm; -in uint v_col; -in uint v_ao_bone; +in uint v_atlas_pos; + +// in vec3 v_norm; +/* in uint v_col; +// out vec3 light_pos[2]; +in uint v_ao_bone; */ layout (std140) uniform u_locals { mat4 model_mat; vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; // bit 0 - is player // bit 1-31 - unused int flags; @@ -18,6 +36,7 @@ uniform u_locals { struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -26,38 +45,96 @@ uniform u_bones { BoneData bones[16]; }; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + out vec3 f_pos; -out vec3 f_col; -out float f_ao; +// flat out uint f_pos_norm; flat out vec3 f_norm; +// float dummy; +/*centroid */out vec2 f_uv_pos; +// out vec3 f_col; +// out float f_ao; +// out float f_alt; +// out vec4 f_shadow; + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// out vec4 sun_pos; +// #endif void main() { // Pre-calculate bone matrix - uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; - mat4 combined_mat = model_mat * bones[bone_idx].bone_mat; + /* uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; */ + uint bone_idx = (v_pos_norm >> 27) & 0xFu; + BoneData bone_data = bones[bone_idx]; + mat4 bone_mat = bone_data.bone_mat; + mat4 combined_mat = /*model_mat * */bone_mat; vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; + // vec4 bone_pos = bones[bone_idx].bone_mat * vec4(pos, 1); + f_pos = ( - combined_mat * - vec4(pos, 1)).xyz; + combined_mat * + vec4(pos, 1.0) + ).xyz + (model_pos - focus_off.xyz); - f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + /* f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); */ - f_col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0; + f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(2, 17)) & uvec2(0x7FFFu, 0x7FFFu)); - f_ao = float(v_ao_bone & 0x3u) / 4.0; + // f_col = srgb_to_linear(vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0); + // f_col = vec3(1.0); + + // f_ao = float(v_ao_bone & 0x3u) / 4.0; + // f_ao = 1.0; + /* for (uint i = 0u; i < light_shadow_count.z; ++i) { + light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + } */ // First 3 normals are negative, next 3 are positive - vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); - vec3 norm = normals[(v_pos_norm >> 29) & 0x7u]; + // uint normal_idx = ((v_atlas_pos & 3u) << 1u) | (v_pos_norm >> 31u); + // const vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); + // vec3 norm = normals[normal_idx]; + uint axis_idx = v_atlas_pos & 3u; - // Calculate normal here rather than for each pixel in the fragment shader - f_norm = normalize(( - combined_mat * - vec4(norm, 0.0) - ).xyz); + vec3 norm = bone_data.normals_mat[axis_idx].xyz; + // norm = normalize(norm); + // vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1); - gl_Position = all_mat * vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // // Calculate normal here rather than for each pixel in the fragment shader + // f_norm = normalize(( + // combined_mat * + // vec4(norm, 0) + // ).xyz); + f_norm = mix(-norm, norm, v_pos_norm >> 31u); + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// // vec4 sun_pos = vec4(0.0); +// #endif + + // f_pos_norm = v_pos_norm; + + // Also precalculate shadow texture and estimated terrain altitude. + // f_alt = alt_at(f_pos.xy); + // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + + gl_Position = all_mat/*shadowMats[0].shadowMatrices*/ * vec4(f_pos, 1); + // gl_Position.z = -gl_Position.z / 100.0 / gl_Position.w; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = gl_Position.z / 100.0; + // gl_Position.z = -gl_Position.z; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl index e56796d1d5..1b876d678d 100644 --- a/assets/voxygen/shaders/fluid-frag/cheap.glsl +++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl @@ -1,17 +1,47 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #include #include in vec3 f_pos; flat in uint f_pos_norm; -in vec3 f_col; -in float f_light; +// in vec3 f_col; +// in float f_light; +// in vec3 light_pos[2]; + +// struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +// }; +// +// layout (std140) +// uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +// }; layout (std140) uniform u_locals { vec3 model_offs; - float load_time; + float load_time; + ivec4 atlas_offs; }; uniform sampler2D t_waves; @@ -20,37 +50,149 @@ out vec4 tgt_color; #include #include +#include void main() { - // First 3 normals are negative, next 3 are positive - vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ); + // tgt_color = vec4(1.0 - MU_WATER, 1.0); + // return; + // First 3 normals are negative, next 3 are positive + vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); - // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. - uint norm_axis = (f_pos_norm >> 30) & 0x3u; - // Increase array access by 3 to access positive values - uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; - // Use an array to avoid conditional branching - vec3 f_norm = normals[norm_axis + norm_dir]; + // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. + uint norm_axis = (f_pos_norm >> 30) & 0x3u; + // Increase array access by 3 to access positive values + uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; + // Use an array to avoid conditional branching + vec3 f_norm = normals[norm_axis + norm_dir]; - vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 light_pos[2]; +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +// #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +// #endif - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); - float point_shadow = shadow_at(f_pos,f_norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; - vec3 surf_color = srgb_to_linear(vec3(0.4, 0.7, 2.0)) * light * diffuse_light * ambient_light; + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + vec3 view_dir = -cam_to_frag; + // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); + /*const */vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;//srgb_to_linear(vec3(0.2, 0.5, 1.0)); + // /*const */vec3 water_color = srgb_to_linear(vec3(0.0, 0.25, 0.5)); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.25, true, clouds); + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float f_alt = alt_at(f_pos.xy); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif - float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag); +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir); + // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); + // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir); + // float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac; - vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 1.0 / (1.0 + diffuse_light)), passthrough); + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); - tgt_color = mix(color, vec4(fog_color, 0.0), 0.0); + float fluid_alt = f_pos.z;//max(ceil(f_pos.z), floor(f_alt));// f_alt;//max(f_alt - f_pos.z, 0.0); + + const float alpha = 0.255/* / 4.0 / sqrt(2.0)*/; + const float n2 = 1.3325; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < fluid_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + // Water is transparent so both normals are valid. + vec3 cam_norm = faceforward(f_norm, f_norm, cam_to_frag); + + vec3 mu = MU_WATER; + // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. + vec3 cam_attenuation = vec3(1.0);//compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); + + // NOTE: Assumes normal is vertical. + vec3 sun_view_dir = cam_pos.z <= fluid_alt ? /*refract(view_dir, -f_norm, 1.0 / n2)*//*reflect(view_dir, -f_norm)*/-view_dir : view_dir;//vec3(view_dir.xy, -view_dir.z) : view_dir; + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // float point_shadow = shadow_at(f_pos, f_norm); + // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec3 emitted_light, reflected_light; + // vec3 light, diffuse_light, ambient_light; + // Squared to account for prior saturation. + // float f_light = 1.0;// pow(f_light, 1.5); + // float vert_light = f_light; + // vec3 light_frac = /*vec3(1.0);*/light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); + + // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x*//*-cam_to_frag*/sun_view_dir/*view_dir*/, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, /*vec3(0.0)*/k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + // reflected_light *= f_light * point_shadow * shade_frac; + // emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= f_light * point_shadow * shade_frac; + // reflected_light *= f_light * point_shadow; + // emitted_light *= f_light * point_shadow; + // max_light *= f_light * point_shadow; + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light, point_shadow; + // vec3 point_light = light_at(f_pos, f_norm); + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + // vec3 surf_color = srgb_to_linear(vec3(0.4, 0.7, 2.0)) * light * diffuse_light * ambient_light; + + // lights_at(f_pos, f_norm, cam_to_frag, k_a * f_light * point_shadow, k_d * f_light * point_shadow, k_s * f_light * point_shadow, alpha, emitted_light, reflected_light); + /*vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + + max_light += lights_at(f_pos, /*f_norm*/cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + // vec3 diffuse_light_point = vec3(0.0); + // max_light += lights_at(f_pos, f_norm, view_dir, k_a, vec3(1.0), k_s, alpha, emitted_light, diffuse_light_point); + + // float reflected_light_point = length(reflected_light);///*length*/(diffuse_light_point.r) + f_light * point_shadow; + // float reflected_light_point = dot(reflected_light, reflected_light) * 0.5;///*length*/(diffuse_light_point.r) + f_light * point_shadow; + // vec3 dump_light = vec3(0.0); + // vec3 specular_light_point = vec3(0.0); + // lights_at(f_pos, f_norm, view_dir, vec3(0.0), vec3(0.0), /*vec3(1.0)*/k_s, alpha, dump_light, specular_light_point); + // diffuse_light_point -= specular_light_point; + + // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow; + // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point; + + float passthrough = /*pow(*/dot(cam_norm, -cam_to_frag/*view_dir*/)/*, 0.5)*/; + float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); + + vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light); + // vec4 color = vec4(surf_color, passthrough * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam))); + vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*//*(f_light * point_shadow + point_light)*//*4.0 * reflected_light_point*/min_refl/* * 0.25*/)), passthrough); + +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + vec4 clouds; + vec3 fog_color = get_sky_color(cam_to_frag, time_of_day.x, cam_pos.xyz, f_pos, 0.25, false, clouds); + vec4 final_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec4 final_color = color; +#endif + tgt_color = final_color; } diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index f6692cbeb9..90402d2065 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -1,17 +1,49 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + +// https://www.shadertoy.com/view/XdsyWf + #include #include in vec3 f_pos; flat in uint f_pos_norm; -in vec3 f_col; -in float f_light; +// in vec3 f_col; +// in float f_light; +// in vec3 light_pos[2]; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; layout (std140) uniform u_locals { - vec3 model_offs; + vec3 model_offs; float load_time; + ivec4 atlas_offs; }; uniform sampler2D t_waves; @@ -20,6 +52,7 @@ out vec4 tgt_color; #include #include +#include vec3 warp_normal(vec3 norm, vec3 pos, float time) { return normalize(norm @@ -68,7 +101,20 @@ void main() { // Use an array to avoid conditional branching vec3 f_norm = normals[norm_axis + norm_dir]; + // vec4 light_pos[2]; +//#if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +//#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +//#endif + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + vec3 view_dir = -cam_to_frag; float frag_dist = length(f_pos - cam_pos.xyz); vec3 b_norm; @@ -81,9 +127,10 @@ void main() { } vec3 c_norm = cross(f_norm, b_norm); - float wave00 = wave_height(f_pos); - float wave10 = wave_height(f_pos + vec3(0.1, 0, 0)); - float wave01 = wave_height(f_pos + vec3(0, 0.1, 0)); + vec3 wave_pos = f_pos + focus_off.xyz; + float wave00 = wave_height(wave_pos); + float wave10 = wave_height(wave_pos + vec3(0.1, 0, 0)); + float wave01 = wave_height(wave_pos + vec3(0, 0.1, 0)); float slope = abs(wave00 - wave10) * abs(wave00 - wave01); vec3 nmap = vec3( @@ -95,32 +142,190 @@ void main() { nmap = mix(f_norm, normalize(nmap), min(1.0 / pow(frag_dist, 0.75), 1)); vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y; + // vec3 norm = f_norm; - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); - float point_shadow = shadow_at(f_pos, norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, norm); - light += point_light; - diffuse_light += point_light; + vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER; +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float f_alt = alt_at(f_pos.xy); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.25, true, clouds); + float fluid_alt = max(ceil(f_pos.z), floor(f_alt));// f_alt;//max(f_alt - f_pos.z, 0.0); + const float alpha = 0.255/*/ / 4.0*//* / 4.0 / sqrt(2.0)*/; + const float n2 = 1.3325; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < fluid_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); - vec3 reflect_ray_dir = reflect(cam_to_frag, norm); + // Water is transparent so both normals are valid. + vec3 cam_norm = faceforward(norm, norm, cam_to_frag); + vec4 _clouds; + vec3 reflect_ray_dir = reflect(cam_to_frag/*-view_dir*/, norm); + vec3 refract_ray_dir = refract(cam_to_frag/*-view_dir*/, norm, 1.0 / n2); + vec3 sun_view_dir = view_dir;///*sign(cam_pos.z - fluid_alt) * view_dir;*/cam_pos.z <= fluid_alt ? -view_dir : view_dir; + // vec3 sun_view_dir = cam_pos.z <= fluid_alt ? -view_dir : view_dir; + vec3 beam_view_dir = reflect_ray_dir;//cam_pos.z <= fluid_alt ? -refract_ray_dir : reflect_ray_dir; + /* vec4 reflect_ray_dir4 = view_mat * vec4(reflect_ray_dir, 1.0); + reflect_ray_dir = normalize(vec3(reflect_ray_dir4) / reflect_ray_dir4.w); */ + // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // Squared to account for prior saturation. + float f_light = 1.0;// pow(f_light, 1.5); + vec3 reflect_color = get_sky_color(/*reflect_ray_dir*/beam_view_dir, time_of_day.x, f_pos, vec3(-100000), 0.25, true, _clouds) * f_light; + // /*const */vec3 water_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)); + // /*const */vec3 water_color = srgb_to_linear(vec3(0.8, 0.9, 1.0)); + // NOTE: Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. + // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water + // /*const */vec3 water_attenuation = MU_WATER;// vec3(0.8, 0.05, 0.01); + // /*const */vec3 water_color = vec3(0.2, 0.95, 0.99); + + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;// horizon_at2(f_shadow, f_alt, f_pos, moon_dir); + // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); + // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir); + // float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac; + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); + + // Hack to determine water depth: color goes down with distance through water, so + // we assume water color absorption from this point a to some other point b is the distance + // along the the ray from a to b where it intersects with the surface plane; if it doesn't, + // then the whole segment from a to b is considered underwater. + // TODO: Consider doing for point lights. + // vec3 cam_surface_dir = faceforward(vec3(0.0, 0.0, 1.0), cam_to_frag, vec3(0.0, 0.0, 1.0)); + + // vec3 water_intersection_surface_camera = vec3(cam_pos); + // bool _water_intersects_surface_camera = IntersectRayPlane(f_pos, view_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), cam_surface_dir, water_intersection_surface_camera); + // // Should work because we set it up so that if IntersectRayPlane returns false for camera, its default intersection point is cam_pos. + // float water_depth_to_camera = length(water_intersection_surface_camera - f_pos); + + // vec3 water_intersection_surface_light = f_pos; + // bool _light_intersects_surface_water = IntersectRayPlane(f_pos, sun_dir.z <= 0.0 ? sun_dir : moon_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), vec3(0.0, 0.0, 1.0), water_intersection_surface_light); + // // Should work because we set it up so that if IntersectRayPlane returns false for light, its default intersection point is f_pos-- + // // i.e. if a light ray can't hit the water, it shouldn't contribute to coloring at all. + // float water_depth_to_light = length(water_intersection_surface_light - f_pos); + + // // For ambient color, we just take the distance to the surface out of laziness. + // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); + + // // Color goes down with distance... + // // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law. + // vec3 water_color_direct = exp(-MU_WATER);//exp(-MU_WATER);//vec3(1.0); + // vec3 water_color_direct = exp(-water_attenuation * (water_depth_to_light + water_depth_to_camera)); + // vec3 water_color_ambient = exp(-water_attenuation * (water_depth_to_vertical + water_depth_to_camera)); + vec3 mu = MU_WATER; + // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. + vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); + // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); + // For ambient color, we just take the distance to the surface out of laziness. + // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law. + // float water_depth_to_vertical = max(fluid_alt - cam_pos.z/*f_light*/, 0.0); + // vec3 ambient_attenuation = exp(-mu * water_depth_to_vertical); + + // For ambient reflection, we just take the water + + vec3 k_a = vec3(1.0); + // Oxygen is light blue. + vec3 k_d = vec3(/*vec3(0.2, 0.9, 0.99)*/1.0); + vec3 k_s = vec3(R_s);//2.0 * reflect_color; + + vec3 emitted_light, reflected_light; + // vec3 light, diffuse_light, ambient_light; + // vec3 light_frac = /*vec3(1.0);*/light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); + // 0 = 100% reflection, 1 = translucent water + float passthrough = /*pow(*/dot(faceforward(norm, norm, cam_to_frag/*view_dir*/), -cam_to_frag/*view_dir*/)/*, 0.5)*/; + + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, norm, /*time_of_day.x*/sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + // reflected_light *= /*water_color_direct * */reflect_color * f_light * point_shadow * shade_frac; + // emitted_light *= /*water_color_direct*//*ambient_attenuation * */f_light * point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= f_light * point_shadow * shade_frac; + // reflected_light *= /*water_color_direct * */reflect_color * f_light * point_shadow; + // emitted_light *= /*water_color_direct*//*ambient_attenuation * */f_light * point_shadow; + // max_light *= f_light * point_shadow; + + // vec3 diffuse_light_point = vec3(0.0); + // max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, vec3(1.0), /*vec3(0.0)*/k_s, alpha, emitted_light, diffuse_light_point); + + // vec3 dump_light = vec3(0.0); + // vec3 specular_light_point = vec3(0.0); + // lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, vec3(0.0), vec3(0.0), /*vec3(1.0)*/k_s, alpha, dump_light, specular_light_point); + // diffuse_light_point -= specular_light_point; + // max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, /*k_d*/vec3(0.0), /*vec3(0.0)*/k_s, alpha, emitted_light, /*diffuse_light*/reflected_light); + + max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, /*k_d*//*vec3(0.0)*/k_d, /*vec3(0.0)*/k_s, alpha, f_norm, 1.0, emitted_light, /*diffuse_light*/reflected_light); + + float reflected_light_point = length(reflected_light);///*length*/(diffuse_light_point.r) + f_light * point_shadow; + // TODO: See if we can be smarter about this using point light distances. + // reflected_light += k_d * (diffuse_light_point/* + f_light * point_shadow * shade_frac*/) + /*water_color_ambient*/specular_light_point; + + /* vec3 point_light = light_at(f_pos, norm); + emitted_light += point_light; + reflected_light += point_light; */ + + // get_sun_diffuse(norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light * point_shadow; + // vec3 point_light = light_at(f_pos, norm); + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)) * light * diffuse_light * ambient_light; + vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * reflected_light/* * log(1.0 - MU_WATER)*/); + + // passthrough = pow(passthrough, 1.0 / (1.0 + water_depth_to_camera)); + /* surf_color = cam_attenuation.g < 0.5 ? + vec3(1.0, 0.0, 0.0) : + vec3(0.0, 1.0, 1.0) + ; */ + // passthrough = passthrough * length(cam_attenuation); + + // vec3 reflect_ray_dir = reflect(cam_to_frag, norm); // Hack to prevent the reflection ray dipping below the horizon and creating weird blue spots in the water - reflect_ray_dir.z = max(reflect_ray_dir.z, 0.01); + // reflect_ray_dir.z = max(reflect_ray_dir.z, 0.01); - vec4 _clouds; - vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.25, false, _clouds) * f_light; + // vec4 _clouds; + // vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.25, false, _clouds) * f_light; // Tint - reflect_color = reflect_color * 0.5 * (diffuse_light + ambient_light); + // reflect_color = mix(reflect_color, surf_color, 0.6); + + // vec4 color = mix(vec4(reflect_color * 2.0, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*/(f_light * point_shadow + point_light) * 0.25)), passthrough); + // vec4 color = mix(vec4(reflect_color * 2.0, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*/(/*f_light * point_shadow*/f_light * point_shadow + reflected_light_point/* + point_light*//*reflected_light*/) * 0.25)), passthrough); + // vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 0.0), passthrough); + //vec4 color = vec4(surf_color, 1.0); + // vec4 color = mix(vec4(reflect_color, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point/* + point_light*//*reflected_light*/))), passthrough); + + // float log_cam = log(min(cam_attenuation.r, min(cam_attenuation.g, cam_attenuation.b))); + float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); + vec4 color = vec4(surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam))); + // vec4 color = vec4(surf_color, mix(1.0, 1.0 / (1.0 + /*0.25 * *//*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point)), passthrough)); + // vec4 color = vec4(surf_color, mix(1.0, length(cam_attenuation), passthrough)); + + /* reflect_color = reflect_color * 0.5 * (diffuse_light + ambient_light); // 0 = 100% reflection, 1 = translucent water float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag); - vec4 color = mix(vec4(reflect_color, 1.0), vec4(vec3(0), 1.0 / (1.0 + diffuse_light * 0.25)), passthrough); + vec4 color = mix(vec4(reflect_color, 1.0), vec4(vec3(0), 1.0 / (1.0 + diffuse_light * 0.25)), passthrough); */ - tgt_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + vec4 clouds; + vec3 fog_color = get_sky_color(cam_to_frag/*-view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.25, false, clouds); + vec4 final_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec4 final_color = color; +#endif + tgt_color = final_color; } diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl index 52907b1ef6..6802ab11b9 100644 --- a/assets/voxygen/shaders/fluid-vert.glsl +++ b/assets/voxygen/shaders/fluid-vert.glsl @@ -1,46 +1,83 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include #include +#include in uint v_pos_norm; -in uint v_col_light; +// in uint v_col_light; layout (std140) uniform u_locals { vec3 model_offs; - float load_time; + float load_time; + ivec4 atlas_offs; }; +// struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +// }; +// +// layout (std140) +// uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +// }; + out vec3 f_pos; flat out uint f_pos_norm; -flat out vec3 f_norm; -out vec3 f_col; -out float f_light; +// out vec3 f_col; +// out float f_light; +// out vec3 light_pos[2]; -const float EXTRA_NEG_Z = 65536.0; +const float EXTRA_NEG_Z = 65536.0/*65536.1*/; void main() { - f_pos = vec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0x1FFFFu)) - vec3(0, 0, EXTRA_NEG_Z) + model_offs; - f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); - f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + f_pos = vec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0x1FFFFu)) - vec3(0, 0, EXTRA_NEG_Z) + model_offs - focus_off.xyz; + // f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); + // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); - // Small waves - f_pos.xy += 0.01; // Avoid z-fighting - f_pos.z -= 0.1 + 0.1 * (sin(tick.x * 2.0 + f_pos.x * 2.0 + f_pos.y * 2.0) + 1.0) * 0.5; + // Small waves + // f_pos.xy += 0.01; // Avoid z-fighting + // f_pos.x += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 1.0))); + // f_pos.y += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 2.0))); +#if (FLUID_MODE == FLUID_MODE_SHINY) + // f_pos.z -= 0.1 + 0.1 * (sin(tick.x/* / 60.0*/* 2.0 + f_pos.x * 2.0 + f_pos.y * 2.0) + 1.0) * 0.5; +#endif - f_col = vec3( - float((v_col_light >> 8) & 0xFFu), - float((v_col_light >> 16) & 0xFFu), - float((v_col_light >> 24) & 0xFFu) + /* f_col = vec3( + float((v_col_light >> 8) & 0xFFu), + float((v_col_light >> 16) & 0xFFu), + float((v_col_light >> 24) & 0xFFu) ) / 255.0; - f_light = float(v_col_light & 0xFFu) / 255.0; + f_light = float(v_col_light & 0xFFu) / 255.0; */ + /* for (uint i = 0u; i < light_shadow_count.z; ++i) { + light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + } */ - f_pos_norm = v_pos_norm; + f_pos_norm = v_pos_norm; gl_Position = - all_mat * - vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + all_mat * + vec4(f_pos, 1); + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/include/cloud/none.glsl b/assets/voxygen/shaders/include/cloud/none.glsl index a20fb82326..917fd5e60c 100644 --- a/assets/voxygen/shaders/include/cloud/none.glsl +++ b/assets/voxygen/shaders/include/cloud/none.glsl @@ -1,5 +1,3 @@ -uniform sampler2D t_noise; - vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, float quality) { return vec4(0.0); } diff --git a/assets/voxygen/shaders/include/cloud/regular.glsl b/assets/voxygen/shaders/include/cloud/regular.glsl index a40c700e01..6e9ef6d947 100644 --- a/assets/voxygen/shaders/include/cloud/regular.glsl +++ b/assets/voxygen/shaders/include/cloud/regular.glsl @@ -1,8 +1,5 @@ -uniform sampler2D t_noise; +#include -const float CLOUD_AVG_HEIGHT = 1025.0; -const float CLOUD_HEIGHT_MIN = CLOUD_AVG_HEIGHT - 60.0; -const float CLOUD_HEIGHT_MAX = CLOUD_AVG_HEIGHT + 60.0; const float CLOUD_THRESHOLD = 0.27; const float CLOUD_SCALE = 5.0; const float CLOUD_DENSITY = 100.0; @@ -11,7 +8,15 @@ float vsum(vec3 v) { return v.x + v.y + v.z; } +vec3 get_cloud_heights() { + float CLOUD_AVG_HEIGHT = /*1025.0*/view_distance.z + 0.7 * view_distance.w; + float CLOUD_HEIGHT_MIN = CLOUD_AVG_HEIGHT - 60.0; + float CLOUD_HEIGHT_MAX = CLOUD_AVG_HEIGHT + 60.0; + return vec3(CLOUD_AVG_HEIGHT, CLOUD_HEIGHT_MIN, CLOUD_HEIGHT_MAX); +} + vec2 cloud_at(vec3 pos) { + vec3 max_heights = get_cloud_heights(); vec2 scaled_pos = pos.xy / CLOUD_SCALE; float tick_offs = 0.0 @@ -29,10 +34,10 @@ vec2 cloud_at(vec3 pos) { + texture(t_noise, scaled_pos * 0.02 + tick_offs + time_of_day.x * 0.0002).x * 0.15 ) * value; - float density = max((value - CLOUD_THRESHOLD) - abs(pos.z - CLOUD_AVG_HEIGHT) / 200.0, 0.0) * CLOUD_DENSITY; + float density = max((value - CLOUD_THRESHOLD) - abs(pos.z - max_heights.x) / 200.0, 0.0) * CLOUD_DENSITY; - const float SHADE_GRADIENT = 1.5 / (CLOUD_AVG_HEIGHT - CLOUD_HEIGHT_MIN); - float shade = (pos.z - CLOUD_AVG_HEIGHT) / (CLOUD_HEIGHT_MAX - CLOUD_HEIGHT_MIN) * 5.0 + 0.3; + float SHADE_GRADIENT = 1.5 / (max_heights.x - max_heights.y); + float shade = ((pos.z - max_heights.x) / (max_heights.z - max_heights.y)) * 5.0 + 0.3; return vec2(shade, density / (1.0 + vsum(abs(pos - cam_pos.xyz)) / 5000)); } @@ -40,9 +45,11 @@ vec2 cloud_at(vec3 pos) { vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, float quality) { const int ITERS = 12; const float INCR = 1.0 / ITERS; + origin = origin + focus_off.xyz; - float mind = (CLOUD_HEIGHT_MIN - origin.z) / dir.z; - float maxd = (CLOUD_HEIGHT_MAX - origin.z) / dir.z; + vec3 max_heights = get_cloud_heights(); + float mind = (max_heights.y - origin.z) / dir.z; + float maxd = (max_heights.z - origin.z) / dir.z; float start = max(min(mind, maxd), 0.0); float delta = min(abs(mind - maxd), max_dist); @@ -57,11 +64,11 @@ vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, f dist += fuzz * 0.01 * min(pow(dist * 0.005, 2.0), 1.0); vec3 pos = origin + dir * min(dist, max_dist); - vec2 sample = cloud_at(pos); + vec2 sample_ = cloud_at(pos); - float integral = sample.y * INCR; + float integral = sample_.y * INCR; passthrough *= 1.0 - integral; - cloud_shade = mix(cloud_shade, sample.x, passthrough * integral); + cloud_shade = mix(cloud_shade, sample_.x, passthrough * integral); dist += INCR * delta; if (passthrough < 0.1) { @@ -70,7 +77,7 @@ vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, f } } - float total_density = 1.0 - passthrough / (1.0 + delta * 0.0001); + float total_density = 1.0 - passthrough / (1.0 + pow(max_dist, 0.5) * 0.0001 + max((0.015 - dir.z) * 0.0001, 0.0) * max_dist); total_density = max(total_density - 1.0 / pow(max_dist, 0.25), 0.0); // Hack diff --git a/assets/voxygen/shaders/include/constants.glsl b/assets/voxygen/shaders/include/constants.glsl new file mode 100644 index 0000000000..d1f793af15 --- /dev/null +++ b/assets/voxygen/shaders/include/constants.glsl @@ -0,0 +1,73 @@ +/* NOTE: When included, this file will contain values for the automatically defined settings specified below. */ + +/* TODO: Add the ability to control the tendency to do stuff in the vertex vs. fragment shader. + * Currently this flag is ignored and always set to prefer fragment, but this tradeoff is not correct on all + * machines in all cases (mine, for instance). */ +#define VOXYGEN_COMPUTATION_PREERENCE_FRAGMENT 0 +#define VOXYGEN_COMPUTATION_PREERENCE_VERTEX 1 + +#define FLUID_MODE_CHEAP 0 +#define FLUID_MODE_SHINY 1 + +#define CLOUD_MODE_NONE 0 +#define CLOUD_MODE_REGULAR 1 + +#define LIGHTING_ALGORITHM_LAMBERTIAN 0 +#define LIGHTING_ALGORITHM_BLINN_PHONG 1 +#define LIGHTING_ALGORITHM_ASHIKHMIN 2 + +#define SHADOW_MODE_NONE 0 +#define SHADOW_MODE_CHEAP 1 +#define SHADOW_MODE_MAP 2 + +/* Unlike the other flags (for now anyway), these are bitmask values */ +#define LIGHTING_TYPE_REFLECTION 0x01 +#define LIGHTING_TYPE_TRANSMISSION 0x02 + +/* Currently ignored, but ideally shoud be helpful for determining light transport properties. */ +#define LIGHTING_REFLECTION_KIND_DIFFUSE 0 +#define LIGHTING_REFLECTION_KIND_GLOSSY 1 +#define LIGHTING_REFLECTION_KIND_SPECULAR 2 + +#define LIGHTING_TRANSPORT_MODE_IMPORTANCE 0 +/* Radiance mode is currently used as a proxy for "attenuation and medium materials + * matter," but we may make it more granular. */ +#define LIGHTING_TRANSPORT_MODE_RADIANCE 1 + +#define LIGHTING_DISTRIBUTION_SCHEME_MICROFACET 0 +#define LIGHTING_DISTRIBUTION_SCHEME_VOXEL 1 + +#define LIGHTING_DISTRIBUTION_BECKMANN 0 +#define LIGHTING_DISTRIBUTION_TROWBRIDGE 1 + +/* Constants expected to be defined automatically by configuration: */ + +/* +#define VOXYGEN_COMPUTATION_PREERENCE +#define FLUID_MODE +#define CLOUD_MODE +#define LIGHTING_ALGORITHM +#define SHADOW_MODE +*/ + +/* Constants expected to be defined by any shader that needs to perform lighting calculations + * (but whose values may take automatically defined constants into account): */ + +/* +// At least one of LIGHTING_TYPE_REFLECTION or LIGHTING_TYPE_TRANSMISSION should be set. +#define LIGHTING_TYPE +#define LIGHTING_REFLECTION_KIND +#define LIGHTING_TRANSPORT_MODE +#define LIGHTING_DISTRIBUTION_SCHEME +#define LIGHTING_DISTRIBUTION +*/ + +/* Constants that *may* be defined by any shader. + * (and whose values may take automatically defined constants into account): */ + +/* +// When sets, shadow maps are used to cast shadows. +#define HAS_SHADOW_MAPS +// When set, "full" LOD terrain informatino is available (e.g. terrain colors). +#define HAS_LOD_FULL_INFO +*/ diff --git a/assets/voxygen/shaders/include/globals.glsl b/assets/voxygen/shaders/include/globals.glsl index 895492a23a..b1ceefe8bd 100644 --- a/assets/voxygen/shaders/include/globals.glsl +++ b/assets/voxygen/shaders/include/globals.glsl @@ -4,12 +4,16 @@ uniform u_globals { mat4 proj_mat; mat4 all_mat; vec4 cam_pos; + vec4 focus_off; vec4 focus_pos; vec4 view_distance; vec4 time_of_day; + vec4 sun_dir; + vec4 moon_dir; vec4 tick; vec4 screen_res; uvec4 light_shadow_count; + vec4 shadow_proj_factors; uvec4 medium; ivec4 select_pos; vec4 gamma; diff --git a/assets/voxygen/shaders/include/light.glsl b/assets/voxygen/shaders/include/light.glsl index ee0920ccc6..295abc2c0b 100644 --- a/assets/voxygen/shaders/include/light.glsl +++ b/assets/voxygen/shaders/include/light.glsl @@ -1,11 +1,15 @@ +#include +#include + struct Light { vec4 light_pos; vec4 light_col; + // mat4 light_proj; }; layout (std140) uniform u_lights { - Light lights[32]; + Light lights[31]; }; struct Shadow { @@ -17,21 +21,45 @@ uniform u_shadows { Shadow shadows[24]; }; -#include - -vec3 illuminate(vec3 color, vec3 light, vec3 diffuse, vec3 ambience) { - float avg_col = (color.r + color.g + color.b) / 3.0; - return ((color - avg_col) * light + (diffuse + ambience) * avg_col) * (diffuse + ambience); -} - float attenuation_strength(vec3 rpos) { // This is not how light attenuation works at all, but it produces visually pleasing and mechanically useful properties float d2 = rpos.x * rpos.x + rpos.y * rpos.y + rpos.z * rpos.z; return max(2.0 / pow(d2 + 10, 0.35) - pow(d2 / 50000.0, 0.8), 0.0); } +// // Compute attenuation due to light passing through a substance that fills an area below a horizontal plane +// // (e.g. in most cases, water below the water surface depth). +// // +// // wpos is the position of the point being hit. +// // ray_dir is the reversed direction of the ray (going "out" of the point being hit). +// // surface_alt is the estimated altitude of the horizontal surface separating the substance from air. +// // defaultpos is the position to use in computing the distance along material at this point if there was a failure. +// // +// // Ideally, defaultpos is set so we can avoid branching on error. +// float compute_attenuation_beam(vec3 wpos, vec3 ray_dir, float surface_alt, vec3 defaultpos, float attenuation_depth) { +// vec3 water_intersection_surface_camera = vec3(cam_pos); +// bool _water_intersects_surface_camera = IntersectRayPlane(f_pos, view_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), cam_surface_dir, water_intersection_surface_camera); +// // Should work because we set it up so that if IntersectRayPlane returns false for camera, its default intersection point is cam_pos. +// float water_depth_to_camera = length(water_intersection_surface_camera - f_pos); +// +// vec3 water_intersection_surface_light = f_pos; +// bool _light_intersects_surface_water = IntersectRayPlane(f_pos, sun_dir.z <= 0.0 ? sun_dir : moon_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), vec3(0.0, 0.0, 1.0), water_intersection_surface_light); +// // Should work because we set it up so that if IntersectRayPlane returns false for light, its default intersection point is f_pos-- +// // i.e. if a light ray can't hit the water, it shouldn't contribute to coloring at all. +// float water_depth_to_light = length(water_intersection_surface_light - f_pos); +// +// // For ambient color, we just take the distance to the surface out of laziness. +// float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); +// +// // Color goes down with distance... +// // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law. +// vec3 water_color_direct = exp(-water_attenuation * (water_depth_to_light + water_depth_to_camera)); +// vec3 water_color_ambient = exp(-water_attenuation * (water_depth_to_vertical + water_depth_to_camera)); +// +// } + vec3 light_at(vec3 wpos, vec3 wnorm) { - const float LIGHT_AMBIENCE = 0.025; + const float LIGHT_AMBIANCE = 0.025; vec3 light = vec3(0); @@ -40,7 +68,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { // Only access the array once Light L = lights[i]; - vec3 light_pos = L.light_pos.xyz; + vec3 light_pos = L.light_pos.xyz - focus_off.xyz; // Pre-calculate difference between light and fragment vec3 difference = light_pos - wpos; @@ -50,7 +78,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { // Multiply the vec3 only once vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a); - light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIENCE); + light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIANCE); } return light; } @@ -58,12 +86,15 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { float shadow_at(vec3 wpos, vec3 wnorm) { float shadow = 1.0; +#if (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_MAP) + return shadow; +#elif (SHADOW_MODE == SHADOW_MODE_CHEAP) for (uint i = 0u; i < light_shadow_count.y; i ++) { // Only access the array once Shadow S = shadows[i]; - vec3 shadow_pos = S.shadow_pos_radius.xyz; + vec3 shadow_pos = S.shadow_pos_radius.xyz - focus_off.xyz; float radius = S.shadow_pos_radius.w; vec3 diff = shadow_pos - wpos; @@ -72,8 +103,130 @@ float shadow_at(vec3 wpos, vec3 wnorm) { } float shade = max(pow(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, 0.25) / pow(radius * radius * 0.5, 0.25), 0.5); + // float shade = max(pow(dot(diff, diff) / (radius * radius * 0.5), 0.25), 0.5); + // float shade = dot(diff, diff) / (radius * radius * 0.5); shadow = min(shadow, shade); } + // NOTE: Squared to compenate for prior saturation. return min(shadow, 1.0); + // return min(shadow * shadow, 1.0); +#endif +} + +// Returns computed maximum intensity. +// +// mu is the attenuation coefficient for any substance on a horizontal plane. +// cam_attenuation is the total light attenuation due to the substance for beams between the point and the camera. +// surface_alt is the altitude of the attenuating surface. +float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, vec3 cam_attenuation, float surface_alt, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting, inout vec3 emitted_light, inout vec3 reflected_light/*, out float shadow*/) { + // return 0.0; + // shadow = 0.0; + // vec3 ambient_light = vec3(0.0); + vec3 directed_light = vec3(0.0); + vec3 max_light = vec3(0.0); + + const float LIGHT_AMBIANCE = 0.0;//0.015625; + + for (uint i = 0u; i < /*light_shadow_count.x*//*0u*/light_shadow_count.x/*32u*/; i ++) { + + // Only access the array once + Light L = lights[i]; + + vec3 light_pos = L.light_pos.xyz - focus_off.xyz; + + // Pre-calculate difference between light and fragment + vec3 difference = light_pos - wpos; + float distance_2 = dot(difference, difference); + + // float strength = attenuation_strength(difference);// pow(attenuation_strength(difference), 0.6); + // // NOTE: This normalizes strength to 1.0 at the center of the point source. + // float strength = 1.0 / (1.0 + distance_2); + float strength = 1.0 / distance_2; + + // Multiply the vec3 only once + const float PI = 3.1415926535897932384626433832795; + const float PI_2 = 2 * PI; + float square_factor = /*2.0 * PI_2 * *//*2.0 * */L.light_col.a; + vec3 color = /*srgb_to_linear*/L.light_col.rgb; + + // // Only access the array once + // Shadow S = shadows[i]; + + // vec3 shadow_pos = S.shadow_pos_radius.xyz; + // float radius = S.shadow_pos_radius.w; + + // vec3 diff = shadow_pos - wpos; + // if (diff.z >= 0.0) { + // diff.z = -sign(diff.z) * diff.z * 0.1; + // } + + // float shade = max(pow(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, 0.25) / pow(radius * radius * 0.5, 0.25), /*0.5*/0.0); + + // shadow = min(shadow, shade); + + // Compute reflectance. + float light_distance = sqrt(distance_2); + vec3 light_dir = -difference / light_distance; // normalize(-difference); + // light_dir = faceforward(light_dir, wnorm, light_dir); + bool is_direct = dot(-light_dir, wnorm) > 0.0; + // reflected_light += color * (distance_2 == 0.0 ? vec3(1.0) : light_reflection_factor(wnorm, cam_to_frag, light_dir, k_d, k_s, alpha)); + vec3 direct_light_dir = is_direct ? light_dir : -light_dir; + // vec3 direct_norm_dir = is_direct ? wnorm : -wnorm; + // Compute attenuation due to fluid. + // Default is light_pos, so we take the whole segment length for this beam if it never intersects the surface, unlesss the beam itself + // is above the surface, in which case we take zero (wpos). + color *= cam_attenuation * compute_attenuation_point(wpos, -direct_light_dir, mu, surface_alt, light_pos.z < surface_alt ? light_pos : wpos); + +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + is_direct = true; +#endif + vec3 direct_light = PI * color * strength * square_factor * light_reflection_factor(/*direct_norm_dir*/wnorm, /*cam_to_frag*/view_dir, direct_light_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting); + float computed_shadow = ShadowCalculationPoint(i, -difference, wnorm, wpos/*, light_distance*/); + // directed_light += is_direct ? max(computed_shadow, /*LIGHT_AMBIANCE*/0.0) * direct_light * square_factor : vec3(0.0); + directed_light += is_direct ? mix(LIGHT_AMBIANCE, 1.0, computed_shadow) * direct_light * square_factor : vec3(0.0); + // directed_light += (is_direct ? 1.0 : LIGHT_AMBIANCE) * max(computed_shadow, /*LIGHT_AMBIANCE*/0.0) * direct_light * square_factor;// : vec3(0.0); + // directed_light += mix(LIGHT_AMBIANCE, 1.0, computed_shadow) * direct_light * square_factor; + // ambient_light += is_direct ? vec3(0.0) : vec3(0.0); // direct_light * square_factor * LIGHT_AMBIANCE; + // ambient_light += is_direct ? direct_light * (1.0 - square_factor * LIGHT_AMBIANCE) : vec3(0.0); + + vec3 cam_light_diff = light_pos - focus_pos.xyz; + float cam_distance_2 = dot(cam_light_diff, cam_light_diff);// + 0.0001; + float cam_strength = 1.0 / (/*4.0 * *//*PI * *//*1.0 + */cam_distance_2); + + // vec3 cam_pos_diff = cam_to_frag.xyz - wpos; + // float pos_distance_2 = dot(cam_pos_diff, cam_pos_diff);// + 0.0001; + + // float cam_distance = sqrt(cam_distance_2); + // float distance = sqrt(distance_2); + float both_strength = cam_distance_2 == 0.0 ? distance_2 == 0.0 ? 0.0 : strength/* * strength*//*1.0*/ : distance_2 == 0.0 ? cam_strength/* * cam_strength*//*1.0*/ : + // 1.0 / (cam_distance * distance); + // sqrt(cam_strength * strength); + cam_strength + strength; + // (cam_strength * strength); + // max(cam_strength, strength); + // mix(cam_strength, strength, distance_2 / (cam_distance_2 + distance_2)); + // mix(cam_strength, strength, cam_distance_2 / (cam_distance_2 + distance_2)); + // max(cam_strength, strength);//mix(cam_strength, strength, clamp(distance_2 / /*pos_distance_2*/cam_distance_2, 0.0, 1.0)); + // float both_strength = mix(cam_strength, strength, cam_distance_2 / sqrt(cam_distance_2 + distance_2)); + max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*//*max(both_strength, 1.0) * *//*cam_strength*/computed_shadow * both_strength * square_factor * square_factor * PI * color; + // max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*/max(cam_strength, 1.0/*, strength*//*1.0*/) * square_factor * square_factor * PI * color; + // light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIANCE); + // Compute emiittance. + // float ambient_sides = clamp(mix(0.15, 0.0, abs(dot(wnorm, light_dir)) * 10000.0), 0.0, 0.15); + // float ambient_sides = 0.0;// max(dot(wnorm, light_dir) - 0.15, 0.15); + // // float ambient_sides = 0.0; + // ambient_light += color * (ambient_sides + LIGHT_AMBIANCE); + } + + // shadow = shadow_at(wpos, wnorm); + // float shadow = shadow_at(wpos, wnorm); + reflected_light += directed_light; + // emitted_light += k_a * ambient_light/* * shadow*/;// min(shadow, 1.0); + return /*rel_luminance(ambient_light + directed_light)*/rel_luminance(max_light);//ambient_light; +} + +// Same as lights_at, but with no assumed attenuation due to fluid. +float lights_at(vec3 wpos, vec3 wnorm, vec3 view_dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, inout vec3 emitted_light, inout vec3 reflected_light) { + return lights_at(wpos, wnorm, view_dir, vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, wnorm, 1.0, emitted_light, reflected_light); } diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl new file mode 100644 index 0000000000..2cb34b406c --- /dev/null +++ b/assets/voxygen/shaders/include/lod.glsl @@ -0,0 +1,289 @@ +#include +#include +#include + +uniform sampler2D t_alt; +uniform sampler2D t_horizon; + +const float MIN_SHADOW = 0.33; + +vec2 pos_to_uv(sampler2D sampler, vec2 pos) { + // Want: (pixel + 0.5) / W + vec2 texSize = textureSize(sampler, 0); + vec2 uv_pos = (focus_off.xy + pos + 16) / (32.0 * texSize); + return vec2(uv_pos.x, /*1.0 - */uv_pos.y); +} + +vec2 pos_to_tex(vec2 pos) { + // Want: (pixel + 0.5) + vec2 uv_pos = (focus_off.xy + pos + 16) / 32.0; + return vec2(uv_pos.x, uv_pos.y); +} + +// textureBicubic from https://stackoverflow.com/a/42179924 +vec4 cubic(float v) { + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) * (1.0/6.0); +} + +// NOTE: We assume the sampled coordinates are already in "texture pixels". +vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { + vec2 texSize = textureSize(sampler, 0); + vec2 invTexSize = 1.0 / texSize; + /* texCoords.y = texSize.y - texCoords.y; */ + + texCoords = texCoords/* * texSize */ - 0.5; + + + vec2 fxy = fract(texCoords); + texCoords -= fxy; + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy; + // vec4 c = texCoords.xxyy + vec2 (-1, +1).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s; + + offset *= invTexSize.xxyy; + /* // Correct for map rotaton. + offset.zw = 1.0 - offset.zw; */ + + vec4 sample0 = texture(sampler, offset.xz); + vec4 sample1 = texture(sampler, offset.yz); + vec4 sample2 = texture(sampler, offset.xw); + vec4 sample3 = texture(sampler, offset.yw); + // vec4 sample0 = texelFetch(sampler, offset.xz, 0); + // vec4 sample1 = texelFetch(sampler, offset.yz, 0); + // vec4 sample2 = texelFetch(sampler, offset.xw, 0); + // vec4 sample3 = texelFetch(sampler, offset.yw, 0); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix( + mix(sample3, sample2, sx), mix(sample1, sample0, sx) + , sy); +} + +float alt_at(vec2 pos) { + return (/*round*/(texture/*textureBicubic*/(t_alt, pos_to_uv(t_alt, pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); + //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; + + // return 0.0 + // + pow(texture(t_noise, pos * 0.00005).x * 1.4, 3.0) * 1000.0 + // + texture(t_noise, pos * 0.001).x * 100.0 + // + texture(t_noise, pos * 0.003).x * 30.0; +} + +float alt_at_real(vec2 pos) { + // Basic idea: only really need the real altitude for an accurate water height estimation, so if we are in the cheap shader take a shortcut. +// #if (FLUID_MODE == FLUID_MODE_CHEAP) +// return alt_at(pos); +// #elif (FLUID_MODE == FLUID_MODE_SHINY) + return (/*round*/(textureBicubic(t_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); +// #endif + //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; + + // return 0.0 + // + pow(texture(t_noise, pos * 0.00005).x * 1.4, 3.0) * 1000.0 + // + texture(t_noise, pos * 0.001).x * 100.0 + // + texture(t_noise, pos * 0.003).x * 30.0; +} + + +float horizon_at2(vec4 f_horizons, float alt, vec3 pos, /*float time_of_day*/vec4 light_dir) { + // vec3 sun_dir = get_sun_dir(time_of_day); + const float PI_2 = 3.1415926535897932384626433832795 / 2.0; + const float MIN_LIGHT = 0.0;//0.115/*0.0*/; + + // return 1.0; +/* + + let shade_frac = horizon_map + .and_then(|(angles, heights)| { + chunk_idx + .and_then(|chunk_idx| angles.get(chunk_idx)) + .map(|&e| (e as f64, heights)) + }) + .and_then(|(e, heights)| { + chunk_idx + .and_then(|chunk_idx| heights.get(chunk_idx)) + .map(|&f| (e, f as f64)) + }) + .map(|(angle, height)| { + let w = 0.1; + if angle != 0.0 && light_direction.x != 0.0 { + let deltax = height / angle; + let lighty = (light_direction.y / light_direction.x * deltax).abs(); + let deltay = lighty - height; + let s = (deltay / deltax / w).min(1.0).max(0.0); + // Smoothstep + s * s * (3.0 - 2.0 * s) + } else { + 1.0 + } + }) + .unwrap_or(1.0); +*/ + // vec2 f_horizon; + /* if (light_dir.z >= 0) { + return 0.0; + } */ + /* if (light_dir.x >= 0) { + f_horizon = f_horizons.rg; + // f_horizon = f_horizons.ba; + } else { + f_horizon = f_horizons.ba; + // f_horizon = f_horizons.rg; + } + return 1.0; */ + /* bvec2 f_mode = lessThan(vec2(light_dir.x), vec2(1.0)); + f_horizon = mix(f_horizons.ba, f_horizons.rg, f_mode); */ + // f_horizon = mix(f_horizons.rg, f_horizons.ba, clamp(light_dir.x * 10000.0, 0.0, 1.0)); + vec2 f_horizon = mix(f_horizons.rg, f_horizons.ba, bvec2(light_dir.x < 0.0)); + // vec2 f_horizon = mix(f_horizons.ba, f_horizons.rg, clamp(light_dir.x * 10000.0, 0.0, 1.0)); + // f_horizon = mix(f_horizons.ba, f_horizons.rg, bvec2(lessThan(light_dir.xx, vec2(0.0)))); + /* if (f_horizon.x <= 0) { + return 1.0; + } */ + float angle = tan(f_horizon.x * PI_2); + /* if (angle <= 0.0001) { + return 1.0; + } */ + float height = f_horizon.y * /*1300.0*//*1278.7266845703125*/view_distance.w + view_distance.z; + const float w = 0.1; + float deltah = height - alt - focus_off.z; + //if (deltah < 0.0001/* || angle < 0.0001 || abs(light_dir.x) < 0.0001*/) { + // return 1.0; + /*} else */{ + float lighta = /*max*/(-light_dir.z/*, 0.0*/) / max(abs(light_dir.x), 0.0001); + // NOTE: Ideally, deltah <= 0.0 is a sign we have an oblique horizon angle. + float deltax = deltah / max(angle, 0.0001)/*angle*/; + float lighty = lighta * deltax; + float deltay = lighty - deltah + max(pos.z - alt, 0.0); + // NOTE: the "real" deltah should always be >= 0, so we know we're only handling the 0 case with max. + float s = mix(max(min(max(deltay, 0.0) / max(deltax, 0.0001) / w, 1.0), 0.0), 1.0, deltah <= 0); + return max(/*0.2 + 0.8 * */(s * s * (3.0 - 2.0 * s)), MIN_LIGHT); + /* if (lighta >= angle) { + return 1.0; + } else { + return MIN_LIGHT; + } */ + // float deltah = height - alt; + // float deltah = max(height - alt, 0.0); + // float lighty = abs(sun_dir.z / sun_dir.x * deltax); + // float lighty = abs(sun_dir.z / sun_dir.x * deltax); + // float deltay = lighty - /*pos.z*//*deltah*/(deltah + max(pos.z - alt, 0.0))/*deltah*/; + // float s = max(min(max(deltay, 0.0) / deltax / w, 1.0), 0.0); + // Smoothstep + // return max(/*0.2 + 0.8 * */(s * s * (3.0 - 2.0 * s)), MIN_LIGHT); + } +} + +// float horizon_at(vec3 pos, /*float time_of_day*/vec3 light_dir) { +// vec4 f_horizons = textureBicubic(t_horizon, pos_to_tex(pos.xy)); +// // f_horizons.xyz = /*linear_to_srgb*/(f_horizons.xyz); +// float alt = alt_at_real(pos.xy); +// return horizon_at2(f_horizons, alt, pos, light_dir); +// } + +vec2 splay(vec2 pos) { + // const float SPLAY_MULT = 1048576.0; + float len_2 = dot(pos, pos); + float len_pow = len_2 * sqrt(len_2); + // float len_pow = pow(len/* * SQRT_2*//* * 0.5*/, 3.0); + // vec2 splayed = pos * pow(len * 0.5, 3.0) * SPLAY_MULT; + const float SQRT_2 = sqrt(2.0) / 2.0; + // /const float CBRT_2 = cbrt(2.0) / 2.0; + // vec2 splayed = pos * (view_distance.x * SQRT_2 + pow(len * 0.5, 3.0) * (SPLAY_MULT - view_distance.x)); + vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(t_alt, 0) * 32.0/* - view_distance.x*/)); + return splayed; + + // Radial: pos.x = r - view_distance.x from focus_pos, pos.y = θ from cam_pos to focus_pos on xy plane. + // const float PI_2 = 3.1415926535897932384626433832795; + // float squared = pos.x * pos.x; + // // // vec2 splayed2 = pos * vec2(squared * (SPLAY_MULT - view_distance.x), PI); + // vec2 splayed2 = pos * vec2(squared * (textureSize(t_alt, 0).x * 32.0 - view_distance.x), PI); + // float r = splayed2.x + view_distance.x; + // vec2 theta = vec2(cos(splayed2.y), sin(splayed2.y)); + // return r * theta; + // // mat2 rot_mat = mat2(vec2(theta.x, -theta.y), theta.yx); + // // return r * /*normalize(normalize(focus_pos.xy - cam_pos.xy) + theta);*/rot_mat * normalize(focus_pos.xy - cam_pos.xy); + // return splayed; +} + +vec3 lod_norm(vec2 f_pos/*vec3 pos*/, vec4 square) { + // const float SAMPLE_W = 32; + + // vec2 f_pos = pos.xy; + // float altx0 = alt_at_real(f_pos + vec2(-1.0, 0) * SAMPLE_W); + // float altx1 = alt_at_real(f_pos + vec2(1.0, 0) * SAMPLE_W); + // float alty0 = alt_at_real(f_pos + vec2(0, -1.0) * SAMPLE_W); + // float alty1 = alt_at_real(f_pos + vec2(0, 1.0) * SAMPLE_W); + float altx0 = alt_at(vec2(square.x, f_pos.y)); + float altx1 = alt_at(vec2(square.z, f_pos.y)); + float alty0 = alt_at(vec2(f_pos.x, square.y)); + float alty1 = alt_at(vec2(f_pos.x, square.w)); + float slope = abs(altx1 - altx0) + abs(alty0 - alty1); + + // vec3 norm = normalize(cross( + // vec3(/*2.0 * SAMPLE_W*/square.z - square.x, 0.0, altx1 - altx0), + // vec3(0.0, /*2.0 * SAMPLE_W*/square.w - square.y, alty1 - alty0) + // )); + vec3 norm = normalize(vec3( + (altx0 - altx1) / (square.z - square.x), + (alty0 - alty1) / (square.w - square.y), + 1.0 + //(abs(square.w - square.y) + abs(square.z - square.x)) / (slope + 0.00001) // Avoid NaN + )); + /* vec3 norm = normalize(vec3( + (altx0 - altx1) / (2.0 * SAMPLE_W), + (alty0 - alty1) / (2.0 * SAMPLE_W), + (2.0 * SAMPLE_W) / (slope + 0.00001) // Avoid NaN + )); */ + + return faceforward(norm, vec3(0.0, 0.0, -1.0)/*pos - cam_pos.xyz*/, norm); +} + +vec3 lod_norm(vec2 f_pos/*vec3 pos*/) { + const float SAMPLE_W = 32; + + return lod_norm(f_pos, vec4(f_pos - vec2(SAMPLE_W), f_pos + vec2(SAMPLE_W))); +} + + +vec3 lod_pos(vec2 pos, vec2 focus_pos) { + // Remove spiking by "pushing" vertices towards local optima + vec2 delta = splay(pos); + vec2 hpos = focus_pos + delta; + vec2 nhpos = hpos; + // vec2 lod_shift = splay(abs(pos) - 1.0 / view_distance.y); + float shift = 15.0;// min(lod_shift.x, lod_shift.y) * 0.5; + for (int i = 0; i < 3; i ++) { + // vec4 square = focus_pos.xy + vec4(splay(pos - vec2(1.0, 1.0), splay(pos + vec2(1.0, 1.0)))); + nhpos -= lod_norm(hpos).xy * shift; + } + hpos = hpos + normalize(nhpos - hpos + 0.001) * min(length(nhpos - hpos), 32); + + return vec3(hpos, alt_at_real(hpos)); +} + +#ifdef HAS_LOD_FULL_INFO +uniform sampler2D t_map; + +vec3 lod_col(vec2 pos) { + //return vec3(0, 0.5, 0); + // return /*linear_to_srgb*/vec3(alt_at(pos), textureBicubic(t_map, pos_to_tex(pos)).gb); + return /*linear_to_srgb*/(textureBicubic(t_map, pos_to_tex(pos)).rgb) + ;//+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; + //+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; +} +#endif diff --git a/assets/voxygen/shaders/include/random.glsl b/assets/voxygen/shaders/include/random.glsl index 7d217d858e..7f297866f2 100644 --- a/assets/voxygen/shaders/include/random.glsl +++ b/assets/voxygen/shaders/include/random.glsl @@ -1,3 +1,5 @@ +uniform sampler2D t_noise; + float hash(vec4 p) { p = fract(p * 0.3183099 + 0.1) - fract(p + 23.22121); p *= 17.0; diff --git a/assets/voxygen/shaders/include/shadows.glsl b/assets/voxygen/shaders/include/shadows.glsl new file mode 100644 index 0000000000..e5968a0a2a --- /dev/null +++ b/assets/voxygen/shaders/include/shadows.glsl @@ -0,0 +1,218 @@ +#ifdef HAS_SHADOW_MAPS + + #if (SHADOW_MODE == SHADOW_MODE_MAP) +struct ShadowLocals { + mat4 shadowMatrices; + mat4 texture_mat; +}; + +layout (std140) +uniform u_light_shadows { + ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +}; + +uniform sampler2DShadow t_directed_shadow_maps; +// uniform sampler2DArrayShadow t_directed_shadow_maps; + +// uniform samplerCubeArrayShadow t_shadow_maps; +// uniform samplerCubeArray t_shadow_maps; +uniform samplerCubeShadow t_point_shadow_maps; +// uniform samplerCube t_shadow_maps; + +// uniform sampler2DArray t_directed_shadow_maps; + +float VectorToDepth (vec3 Vec) +{ + // return length(Vec) / screen_res.w; + vec3 AbsVec = abs(Vec); + float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z)); + // float LocalZcomp = length(Vec); + + // Replace f and n with the far and near plane values you used when + // you drew your cube map. + // const float f = 2048.0; + // const float n = 1.0; + + // float NormZComp = (screen_res.w+screen_res.z) / (screen_res.w-screen_res.z) - (2*screen_res.w*screen_res.z)/(screen_res.w-screen_res.z)/LocalZcomp; + // float NormZComp = 1.0 - shadow_proj_factors.y / shadow_proj_factors.x / LocalZcomp; + float NormZComp = shadow_proj_factors.x - shadow_proj_factors.y / LocalZcomp; + // NormZComp = -1000.0 / (NormZComp + 10000.0); + return (NormZComp + 1.0) * 0.5; + + // float NormZComp = length(LocalZcomp); + // NormZComp = -NormZComp / screen_res.w; + // // return (NormZComp + 1.0) * 0.5; + // return NormZComp; +} + +const vec3 sampleOffsetDirections[20] = vec3[] +( + vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), + vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), + vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), + vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), + vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) + // vec3(0, 0, 0) +); + +float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos) +{ + if (lightIndex != 0u) { + return 1.0; + }; + + { + float currentDepth = VectorToDepth(fragToLight);// + bias; + + float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); + /* if (visibility == 1.0 || visibility == 0.0) { + return visibility; + } */ + /* if (visibility >= 0.75) { + return 1.0; + } + if (visibility <= 0.25) { + return 0.0; + } */ + /* if (visibility < 1.0) { + return 0.0; + } */ + // return visibility; + /* if (visibility == 1.0) { + return visibility; + } */ + return visibility; + // return visibility == 1.0 ? 1.0 : 0.0; + } + + // float shadow = 0.0; + // float bias = 0.0;//0.003;//-0.003;//-0.005;//0.001;//-1.0;//-0.001;//0.001;//0.003;//-0.05;//-0.1;//0.0;//0.1 + // float viewDistance = length(cam_pos.xyz - fragPos); + // vec3 firstDelta = vec3(0.0);///*min(viewDistance, 5.0) * *//**normalize(cam_pos - fragPos)*/fragNorm * 0.5; + // fragToLight += firstDelta; + // // viewDistance -= length(firstDelta); + // fragPos -= firstDelta; + + // int samples = 20; + // // float lightDistance = length(fragToLight); + // // float diskRadius = 0.00001; + // // float diskRadius = 1.0; + // // float diskRadius = 0.05; + // float diskRadius = 5.0 / screen_res.w;// (1.0 + (/*viewDistance*/viewDistance / screen_res.w)) / 25.0; + // // float diskRadius = lightDistance; + // for(int i = 0; i < samples; ++i) + // { + // float currentDepth = VectorToDepth(fragToLight + sampleOffsetDirections[i] * diskRadius) + bias; + // // float closestDepth = texture(depthMap, fragToLight).r; + // // closestDepth *= far_plane; // Undo mapping [0;1] + // /* if(currentDepth - bias > closestDepth) + // shadow += 1.0;*/ + // float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth)/*, -2.5*/); + // shadow += visibility; + // // float closestDepth = texture(t_shadow_maps, vec3(fragToLight)/*, -2.5*/).r; + // // shadow += closestDepth > currentDepth ? 1.0 : 0.0; + // } + // shadow /= float(samples); + // // shadow = shadow * shadow * (3.0 - 2.0 * shadow); + + // // use the light to fragment vector to sample from the depth map + // // float bias = 0.0;///*0.05*/0.01;//0.05;// 0.05; + // // float closestDepth = texture(t_shadow_maps, /*vec4*/vec3(fragToLight/*, (lightIndex + 1)*//* * 6*/)/*, 0.0*//*, 0.0*//*, bias*/).r; + // // // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, lightIndex), bias); + // // // // it is currently in linear range between [0,1]. Re-transform back to original value + // // closestDepth = (closestDepth + 0.0) * screen_res.w; // far plane + // // // // now test for shadows + // // // // float shadow = /*currentDepth*/(screen_res.w - bias) > closestDepth ? 1.0 : 0.0; + // // float shadow = currentDepth - bias < closestDepth ? 1.0 : 0.0; + // // float visibility = textureProj(t_shadow_maps, vec4(fragToLight, lightIndex), bias); + // // float visibility = texture(t_shadow_maps, vec4(fragToLight, lightIndex + 1), -(currentDepth/* + screen_res.z*/) / screen_res.w);// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); + // // currentDepth += bias; + // // currentDepth = -1000.0 / (currentDepth + 10000.0); + // // currentDepth /= screen_res.w; + // // float currentDepth = VectorToDepth(fragToLight) + bias; + + // // float visibility = texture(t_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); + // // return visibility == 1.0 ? 1.0 : 0.0; + // return shadow; +} + +float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_pos, vec3 fragPos) +{ + float bias = 0.000;//0.0005;//-0.0001;// 0.05 / (2.0 * view_distance.x); + float diskRadius = 0.01; + const vec3 sampleOffsetDirections[20] = vec3[] + ( + vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), + vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), + vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), + vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), + vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) + // vec3(0, 0, 0) + ); + /* if (lightIndex >= light_shadow_count.z) { + return 1.0; + } */ + // vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz; + // sun_pos.z += sun_pos.w * bias; + ShadowLocals sun_shadow = shadowMats[0]; + vec4 sun_pos = sun_shadow.texture_mat * vec4(fragPos, 1.0); + // sun_pos.z -= sun_pos.w * bias; + float visibility = textureProj(t_directed_shadow_maps, sun_pos); + /* float visibilityLeft = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, -diskRadius, 0.0), 1.0)); + float visibilityRight = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, diskRadius, 0.0), 1.0)); */ + // float nearVisibility = textureProj(t_directed_shadow_maps + vec3(0.001, sun_pos)); + // float visibility = textureProj(t_directed_shadow_maps, vec4(fragPos.xy, /*lightIndex, */fragPos.z + bias, sun_pos.w)); + // return visibility; + // return min(visibility, min(visibilityLeft, visibilityRight)); + // return mix(visibility, 0.0, sun_pos.z < -1.0); + // return mix(mix(0.0, 1.0, visibility == 1.0), 1.0, sign(sun_pos.w) * sun_pos.z > /*1.0*/abs(sun_pos.w)); + // return (visibility - 0.5) * (visibility - 0.5) * 2.0 * sign(visibility - 0.5) + 0.5;// visibility > 0.75 ? visibility : 0.0;// visibility > 0.9 ? 1.0 : 0.0; + return visibility; + // return visibility == 1.0 ? 1.0 : 0.0; + // return abs(fragPos.y - round(fragPos.y)) <= 0.1 || abs(fragPos.x - round(fragPos.x)) <= 0.1 ? ( visibility == 1.0 ? 1.0 : 0.0) : visibility; + /* if (visibility == 1.0) { + return 1.0; + } */ + // return visibility; + /* if (fragPos.z > 1.0) { + return 1.0; + } */ + // vec3 snapToZ = abs(fragPos - vec3(ivec3(fragPos))); // fract(abs(fragPos)); + // // snapToZ = min(snapToZ, 1.0 - snapToZ); + // const float EDGE_DIST = 0.01; + // snapToZ = mix(vec3(0.0), vec3(1.0), lessThanEqual(snapToZ, vec3(EDGE_DIST))); + // // float snapToZDist = dot(snapToZ, snapToZ); + // if (visibility <= 0.75 && /*fract(abs(fragPos.xy)), vec2(0.1)))*/ /*snapToZDist <= 0.25*//*all(lessThan(snapToZ, vec3(0.1)))(*/ + // snapToZ.x + snapToZ.y + snapToZ.z >= 2.0) { + // return 0.0; + // } + // int samples = 20; + // float shadow = 0.0; + // // float bias = 0.0001; + // // float viewDistance = length(cam_pos.xyz - fragPos); + // // float diskRadius = 0.2 * (1.0 + (viewDistance / screen_res.w)) / 25.0; + // // float diskRadius = 0.0003;//0.005;// / (2.0 * view_distance.x);//(1.0 + (viewDistance / screen_res.w)) / 25.0; + // fragPos = sun_pos.xyz / sun_pos.w; + // for(int i = 0; i < samples; ++i) + // { + // vec3 currentDepth = fragPos + vec3(sampleOffsetDirections[i].xyz) * diskRadius + bias; + // visibility = texture(t_directed_shadow_maps, currentDepth);//vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/); + // // visibility = texture(t_directed_shadow_maps, vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/); + // shadow += visibility; + // // mix(visibility, 1.0, visibility >= 0.5); + // } + // shadow /= float(samples); + // return shadow; +} + #elif (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_CHEAP) +float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos) +{ + return 1.0; +} + #endif +#else +float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos) +{ + return 1.0; +} +#endif diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index fe12499086..07bbdd67a4 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -1,215 +1,589 @@ #include +#include #include +#include +#include +// Information about an approximately directional light, like the sun or moon. +struct DirectionalLight { + // vec3 dir; + float shadow; + // vec3 color; + // float brightness; +}; const float PI = 3.141592; const vec3 SKY_DAY_TOP = vec3(0.1, 0.5, 0.9); const vec3 SKY_DAY_MID = vec3(0.02, 0.28, 0.8); const vec3 SKY_DAY_BOT = vec3(0.1, 0.2, 0.3); -const vec3 DAY_LIGHT = vec3(1.2, 1.0, 1.0); +const vec3 DAY_LIGHT = vec3(1.9, 1.75, 0.9);//vec3(1.5, 1.4, 1.0); const vec3 SUN_HALO_DAY = vec3(0.35, 0.35, 0.0); const vec3 SKY_DUSK_TOP = vec3(0.06, 0.1, 0.20); const vec3 SKY_DUSK_MID = vec3(0.35, 0.1, 0.15); const vec3 SKY_DUSK_BOT = vec3(0.0, 0.1, 0.23); -const vec3 DUSK_LIGHT = vec3(3.0, 1.5, 0.3); +const vec3 DUSK_LIGHT = vec3(9.0, 1.5, 0.15); const vec3 SUN_HALO_DUSK = vec3(1.2, 0.15, 0.0); const vec3 SKY_NIGHT_TOP = vec3(0.001, 0.001, 0.0025); const vec3 SKY_NIGHT_MID = vec3(0.001, 0.005, 0.02); const vec3 SKY_NIGHT_BOT = vec3(0.002, 0.004, 0.004); -const vec3 NIGHT_LIGHT = vec3(0.002, 0.01, 0.03); +const vec3 NIGHT_LIGHT = vec3(0.002, 0.02, 0.02); +// const vec3 NIGHT_LIGHT = vec3(0.0, 0.0, 0.0); + +// Linear RGB, scattering coefficients for atmosphere at roughly R, G, B wavelengths. +// +// See https://en.wikipedia.org/wiki/Diffuse_sky_radiation +const vec3 MU_SCATTER = vec3(0.05, 0.10, 0.23) * 1.5; + +const float SUN_COLOR_FACTOR = 5.0;//6.0;// * 1.5;//1.8; const float UNDERWATER_MIST_DIST = 100.0; -vec3 get_sun_dir(float time_of_day) { - const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); +const float PERSISTENT_AMBIANCE = 1.0 / 32.0;// 1.0 / 80; // 1.0 / 512; // 0.00125 // 0.1;// 0.025; // 0.1; - float sun_angle_rad = time_of_day * TIME_FACTOR; - return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); +//vec3 get_sun_dir(float time_of_day) { +// const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); +// +// float sun_angle_rad = time_of_day * TIME_FACTOR; +// // return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); +// return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); +//} +// +//vec3 get_moon_dir(float time_of_day) { +// const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); +// +// float moon_angle_rad = time_of_day * TIME_FACTOR; +// // -cos((60+60*4)/360*2*pi)-0.5 = 0 +// // -cos((60+60*5)/360*2*pi)-0.5 = -0.5 +// // -cos((60+60*6)/360*2*pi)-0.5 = 0 +// // +// // i.e. moon out from (60*5)/360*24 = 20:00 to (60*7/360*24) = 28:00 = 04:00. +// // +// // Then sun out from 04:00 to 20:00. +// return normalize(-vec3(sin(moon_angle_rad), 0.0, cos(moon_angle_rad) - 0.5)); +//} + +float get_sun_brightness(/*vec3 sun_dir*/) { + return max(-sun_dir.z + 0.6, 0.0) * 0.9; } -vec3 get_moon_dir(float time_of_day) { - const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); - - float moon_angle_rad = time_of_day * TIME_FACTOR; - return normalize(-vec3(sin(moon_angle_rad), 0.0, cos(moon_angle_rad) - 0.5)); +float get_moon_brightness(/*vec3 moon_dir*/) { + return max(-moon_dir.z + 0.6, 0.0) * 0.4; } -const float PERSISTENT_AMBIANCE = 0.1; - -float get_sun_brightness(vec3 sun_dir) { - return max(-sun_dir.z + 0.6, 0.0) * 0.9; +vec3 get_sun_color(/*vec3 sun_dir*/) { + return mix( + mix( + DUSK_LIGHT, + NIGHT_LIGHT, + max(sun_dir.z, 0) + ), + DAY_LIGHT, + max(-sun_dir.z, 0) + ); } -float get_moon_brightness(vec3 moon_dir) { - return max(-moon_dir.z + 0.6, 0.0) * 0.07; +vec3 get_moon_color(/*vec3 moon_dir*/) { + return vec3(0.05, 0.05, 0.6); } -vec3 get_sun_color(vec3 sun_dir) { - return mix( - mix( - DUSK_LIGHT, - NIGHT_LIGHT, - max(sun_dir.z, 0) - ), - DAY_LIGHT, - max(-sun_dir.z, 0) - ); +DirectionalLight get_sun_info(vec4 _dir, float shade_frac/*, vec4 light_pos[2]*/, /*vec4 sun_pos*/vec3 f_pos) { + float shadow = shade_frac; +#ifdef HAS_SHADOW_MAPS +#if (SHADOW_MODE == SHADOW_MODE_MAP) + if (sun_dir.z < /*0.6*/0.0) { + /* ShadowLocals sun_shadow = shadowMats[0]; + vec4 sun_pos = sun_shadow.texture_mat * vec4(f_pos, 1.0); */ +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +// #endif + shadow = min(shadow, ShadowCalculationDirected(/*sun_pos, *//*0u*/f_pos)); + } +#endif +#endif + return DirectionalLight(/*dir, */shadow/*, get_sun_color(dir), get_sun_brightness(dir)*/); } -vec3 get_moon_color(vec3 moon_dir) { - return vec3(0.05, 0.05, 0.6); +DirectionalLight get_moon_info(vec4 _dir, float shade_frac/*, vec4 light_pos[2]*/) { + float shadow = shade_frac; +// #ifdef HAS_SHADOW_MAPS +// shadow = min(shade_frac, ShadowCalculationDirected(light_pos, 1u)); +// #endif + return DirectionalLight(/*dir, */shadow/*, get_moon_color(dir), get_moon_brightness(dir)*/); } -void get_sun_diffuse(vec3 norm, float time_of_day, out vec3 light, out vec3 diffuse_light, out vec3 ambient_light, float diffusion) { - const float SUN_AMBIANCE = 0.1; +// // Calculates extra emission and reflectance (due to sunlight / moonlight). +// // +// // reflectence = k_a * i_a + i_a,persistent +// // emittence = Σ { m ∈ lights } i_m * shadow_m * get_light_reflected(light_m) +// // +// // Note that any shadowing to be done that would block the sun and moon, aside from heightmap shadowing (that will be +// // implemented sooon), should be implicitly provided via k_a, k_d, and k_s. For instance, shadowing via ambient occlusion. +// // +// // Also note that the emitted light calculation is kind of lame... we probabbly need something a bit nicer if we ever want to do +// // anything interesting here. +// // void get_sun_diffuse(vec3 norm, float time_of_day, out vec3 light, out vec3 diffuse_light, out vec3 ambient_light, float diffusion +// void get_sun_diffuse(vec3 norm, float time_of_day, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, out vec3 emitted_light, out vec3 reflected_light) { +// const float SUN_AMBIANCE = 0.1 / 2.0;// 0.1 / 3.0; +// +// vec3 sun_dir = get_sun_dir(time_of_day); +// vec3 moon_dir = get_moon_dir(time_of_day); +// +// float sun_light = get_sun_brightness(sun_dir); +// float moon_light = get_moon_brightness(moon_dir); +// +// vec3 sun_color = get_sun_color(sun_dir); +// vec3 moon_color = get_moon_color(moon_dir); +// +// vec3 sun_chroma = sun_color * sun_light; +// vec3 moon_chroma = moon_color * moon_light; +// +// /* float NLsun = max(dot(-norm, sun_dir), 0); +// float NLmoon = max(dot(-norm, moon_dir), 0); +// vec3 E = -dir; */ +// +// // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). +// // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). +// float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-norm, sun_dir)) * mix(0.0, 1.0, abs(sun_dir.z) * 10000.0) * 10000.0), 0.0, 0.5); +// // float ambient_sides = 0.5 - 0.5 * abs(dot(-norm, sun_dir)); +// +// emitted_light = k_a * (ambient_sides + vec3(SUN_AMBIANCE * sun_light + moon_light)) + PERSISTENT_AMBIANCE; +// // TODO: Add shadows. +// reflected_light = +// sun_chroma * light_reflection_factor(norm, dir, sun_dir, k_d, k_s, alpha) + +// moon_chroma * 1.0 * /*4.0 * */light_reflection_factor(norm, dir, moon_dir, k_d, k_s, alpha); +// +// /* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; +// diffuse_light = +// sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + +// moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + +// PERSISTENT_AMBIANCE; +// ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); */ +// } - vec3 sun_dir = get_sun_dir(time_of_day); - vec3 moon_dir = get_moon_dir(time_of_day); +// Returns computed maximum intensity. +// +// wpos is the position of this fragment. +// mu is the attenuation coefficient for any substance on a horizontal plane. +// cam_attenuation is the total light attenuation due to the substance for beams between the point and the camera. +// surface_alt is the altitude of the attenuating surface. +float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, vec3 norm, vec3 dir, vec3 wpos, vec3 mu, vec3 cam_attenuation, float surface_alt, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting, out vec3 emitted_light, out vec3 reflected_light) { + const float MIN_SHADOW = 0.15; + const vec3 SUN_AMBIANCE = MU_SCATTER;//0.23;/* / 1.8*/;// 0.1 / 3.0; + const vec3 MOON_AMBIANCE = MU_SCATTER;//0.23;//0.1; - float sun_light = get_sun_brightness(sun_dir); - float moon_light = get_moon_brightness(moon_dir); + /* vec3 sun_dir = sun_info.dir; + vec3 moon_dir = moon_info.dir; */ + vec3 sun_dir = sun_dir.xyz; + vec3 moon_dir = moon_dir.xyz; - vec3 sun_color = get_sun_color(sun_dir); - vec3 moon_color = get_moon_color(moon_dir); + float sun_light = get_sun_brightness(/*sun_dir*/);//sun_info.brightness;; + float moon_light = get_moon_brightness(/*moon_dir*/);//moon_info.brightness; - vec3 sun_chroma = sun_color * sun_light; - vec3 moon_chroma = moon_color * moon_light; + vec3 sun_color = get_sun_color(/*sun_dir*/) * SUN_COLOR_FACTOR;//sun_info.color * SUN_COLOR_FACTOR; + vec3 moon_color = get_moon_color(/*moon_dir*/);//moon_info.color; - light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; - diffuse_light = - sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + - moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + - PERSISTENT_AMBIANCE; - ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); + // If the sun is facing the wrong way, we currently just want zero light, hence default point is wpos. + vec3 sun_attenuation = compute_attenuation(wpos, -sun_dir, mu, surface_alt, wpos); + vec3 moon_attenuation = compute_attenuation(wpos, -moon_dir, mu, surface_alt, wpos); + + vec3 sun_chroma = sun_color * sun_light * cam_attenuation * sun_attenuation; + vec3 moon_chroma = moon_color * moon_light * cam_attenuation * moon_attenuation; + +// #ifdef HAS_SHADOW_MAPS +// float sun_shadow = ShadowCalculationDirected(light_pos, 0u); +// float moon_shadow = ShadowCalculationDirected(light_pos, 1u); +// #else +// float sun_shadow = 1.0; +// float moon_shadow = 1.0; +// #endif + float sun_shadow = sun_info.shadow; + float moon_shadow = moon_info.shadow; + + // https://en.m.wikipedia.org/wiki/Diffuse_sky_radiation + // + // HdRd radiation should come in at angle normal to us. + // const float H_d = 0.23; + // + // Let β be the angle from horizontal + // (for objects exposed to the sky, where positive when sloping towards south and negative when sloping towards north): + // + // sin β = (north ⋅ norm) / |north||norm| + // = dot(vec3(0, 1, 0), norm) + // + // cos β = sqrt(1.0 - dot(vec3(0, 1, 0), norm)) + // + // Let h be the hour angle (180/0.0 at midnight, 90/1.0 at dawn, 0/0.0 at noon, -90/-1.0 at dusk, -180 at midnight/0.0): + // cos h = (midnight ⋅ -light_dir) / |midnight||-light_dir| + // = (noon ⋅ light_dir) / |noon||light_dir| + // = dot(vec3(0, 0, 1), light_dir) + // + // Let φ be the latitude at this point. 0 at equator, -90 at south pole / 90 at north pole. + // + // Let δ be the solar declination (angular distance of the sun's rays north [or south[] + // of the equator), i.e. the angle made by the line joining the centers of the sun and Earth with its projection on the + // equatorial plane. Caused by axial tilt, and 0 at equinoxes. Normally varies between -23.45 and 23.45 degrees. + // + // Let α (the solar altitude / altitud3 angle) be the vertical angle between the projection of the sun's rays on the + // horizontal plane and the direction of the sun's rays (passing through a point). + // + // Let Θ_z be the vertical angle between sun's rays and a line perpendicular to the horizontal plane through a point, + // i.e. + // + // Θ_z = (π/2) - α + // + // i.e. cos Θ_z = sin α and + // cos α = sin Θ_z + // + // Let γ_s be the horizontal angle measured from north to the horizontal projection of the sun's rays (positive when + // measured westwise). + // + // cos Θ_z = cos φ cos h cos δ + sin φ sin δ + // cos γ_s = sec α (cos φ sin δ - cos δ sin φ cos h) + // = (1 / √(1 - cos² Θ_z)) (cos φ sin δ - cos δ sin φ cos h) + // sin γ_s = sec α cos δ sin h + // = (1 / cos α) cos δ sin h + // = (1 / sin Θ_z) cos δ sin h + // = (1 / √(1 - cos² Θ_z)) cos δ sin h + // + // R_b = (sin(δ)sin(φ - β) + cos(δ)cos(h)cos(φ - β))/(sin(δ)sin(φ) + cos(δ)cos(h)cos(φ)) + // + // Assuming we are on the equator (i.e. φ = 0), and there is no axial tilt or we are at an equinox (i.e. δ = 0): + // + // cos Θ_z = 1 * cos h * 1 + 0 * 0 = cos h + // cos γ_s = (1 / √(1 - cos² h)) (1 * 0 - 1 * 0 * cos h) + // = (1 / √(1 - cos² h)) * 0 + // = 0 + // sin γ_s = (1 / √(1 - cos² h)) * sin h + // = sin h / sin h + // = 1 + // + // R_b = (0 * sin(0 - β) + 1 * cos(h) * cos(0 - β))/(0 * 0 + 1 * cos(h) * 1) + // = (cos(h)cos(-β)) / cos(H) + // = cos(-β), the angle from horizontal. + // + // NOTE: cos(-β) = cos(β). + // float cos_sun = dot(norm, /*-sun_dir*/vec3(0, 0, 1)); + // float cos_moon = dot(norm, -moon_dir); + // + // Let ζ = diffuse reflectance of surrounding ground for solar radiation, then we have + // + // R_d = (1 + cos β) / 2 + // R_r = ζ (1 - cos β) / 2 + // + // H_t = H_b R_b + H_d R_d + (H_b + H_d) R_r + float sin_beta = dot(vec3(0, 1, 0), norm); + float R_b = sqrt(1.0 - sin_beta * sin_beta); + // Rough estimate of diffuse reflectance of rest of ground. + // NOTE: zeta should be close to 0.7 with snow cover, 0.2 normally? Maybe? + vec3 zeta = max(vec3(0.2), k_d * (1.0 - k_s));//vec3(0.2);// k_d * (1.0 - k_s); + float R_d = (1 + R_b) * 0.5; + vec3 R_r = zeta * (1.0 - R_b) * 0.5; + // + // We can break this down into: + // H_t_b = H_b * (R_b + R_r) = light_intensity * (R_b + R_r) + // H_t_r = H_d * (R_d + R_r) = light_intensity * (R_d + R_r) + vec3 R_t_b = R_b + R_r; + vec3 R_t_r = R_d + R_r; + + // vec3 half_vec = normalize(-norm + dir); + vec3 light_frac = R_t_b * (sun_chroma * SUN_AMBIANCE + moon_chroma * MOON_AMBIANCE) * light_reflection_factor(norm, /*norm*//*dir*/dir, /*-norm*/-/*dir*/norm, /*k_d*/k_d/* * (1.0 - k_s)*/, /*k_s*/vec3(0.0), alpha, voxel_norm, voxel_lighting); + // vec3 light_frac = /*vec3(1.0)*//*H_d * */ + // SUN_AMBIANCE * /*sun_light*/sun_chroma * light_reflection_factor(norm, dir, /*vec3(0, 0, -1.0)*/-norm, vec3((1.0 + cos_sun) * 0.5), vec3(k_s * (1.0 - cos_sun) * 0.5), alpha) + + // MOON_AMBIANCE * /*sun_light*/moon_chroma * light_reflection_factor(norm, dir, /*vec3(0, 0, -1.0)*/-norm, vec3((1.0 + cos_moon) * 0.5), vec3(k_s * (1.0 - cos_moon) * 0.5), alpha); + /* float NLsun = max(dot(-norm, sun_dir), 0); + float NLmoon = max(dot(-norm, moon_dir), 0); + vec3 E = -dir; */ + + // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). + // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // float ambient_sides = 0.0; + // float ambient_sides = 0.5 - 0.5 * min(abs(dot(-norm, sun_dir)), abs(dot(-norm, moon_dir))); + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-norm, sun_dir)) * mix(0.0, 1.0, abs(sun_dir.z) * 10000.0) * 10000.0), 0.0, 0.5); + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-norm, sun_dir)) * mix(0.0, 1.0, abs(sun_dir.z) * 10000.0) * 10000.0), 0.0, 0.5); + + emitted_light = light_frac + k_a * PERSISTENT_AMBIANCE * MU_SCATTER; + // emitted_light = k_a * light_frac * (/*ambient_sides + */SUN_AMBIANCE * /*sun_light*/sun_chroma + /*vec3(moon_light)*/MOON_AMBIANCE * moon_chroma) + PERSISTENT_AMBIANCE; + + reflected_light = R_t_r * ( + (1.0 - SUN_AMBIANCE) * sun_chroma * sun_shadow * (light_reflection_factor(norm, dir, sun_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting) /*+ + light_reflection_factor(norm, dir, normalize(sun_dir + vec3(0.0, 0.1, 0.0)), k_d, k_s, alpha) + + light_reflection_factor(norm, dir, normalize(sun_dir - vec3(0.0, 0.1, 0.0)), k_d, k_s, alpha)*/) + + (1.0 - MOON_AMBIANCE) * moon_chroma * moon_shadow * 1.0 * /*4.0 * */light_reflection_factor(norm, dir, moon_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting) + ); + + /* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; + diffuse_light = + sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + + moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + + PERSISTENT_AMBIANCE; + ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); */ + return rel_luminance(emitted_light + reflected_light);//rel_luminance(emitted_light + reflected_light);//sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; +} + +float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, vec3 norm, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting, out vec3 emitted_light, out vec3 reflected_light) { + return get_sun_diffuse2(sun_info, moon_info, norm, dir, vec3(0.0), vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, voxel_norm, voxel_lighting, emitted_light, reflected_light); +} + +float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, vec3 norm, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, out vec3 emitted_light, out vec3 reflected_light) { + return get_sun_diffuse2(sun_info, moon_info, norm, dir, vec3(0.0), vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, norm, 1.0, emitted_light, reflected_light); } // This has been extracted into a function to allow quick exit when detecting a star. float is_star_at(vec3 dir) { - float star_scale = 80.0; + float star_scale = 80.0; - // Star positions - vec3 pos = (floor(dir * star_scale) - 0.5) / star_scale; + // Star positions + vec3 pos = (floor(dir * star_scale) - 0.5) / star_scale; - // Noisy offsets - pos += (3.0 / star_scale) * (1.0 + hash(pos.yxzz) * 0.85); + // Noisy offsets + pos += (3.0 / star_scale) * (1.0 + hash(pos.yxzz) * 0.85); - // Find distance to fragment - float dist = length(pos - dir); + // Find distance to fragment + float dist = length(pos - dir); - // Star threshold - if (dist < 0.0015) { - return 1.0; - } + // Star threshold + if (dist < 0.0015) { + return 1.0; + } - return 0.0; + return 0.0; +} + +vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_stars, float refractionIndex, out vec4 clouds) { +#if (CLOUD_MODE == CLOUD_MODE_NONE) + const bool has_clouds = false; +#elif (CLOUD_MODE == CLOUD_MODE_REGULAR) + const bool has_clouds = true; +#endif + + if (with_stars || has_clouds) { + // Sky color + /* vec3 sun_dir = get_sun_dir(time_of_day); + vec3 moon_dir = get_moon_dir(time_of_day); */ + vec3 sun_dir = sun_dir.xyz; + vec3 moon_dir = moon_dir.xyz; + + // sun_dir = sun_dir.z <= 0 ? refract(sun_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : sun_dir; + // moon_dir = moon_dir.z <= 0 ? refract(moon_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : moon_dir; + + // Add white dots for stars. Note these flicker and jump due to FXAA + float star = 0.0; + if (with_stars) { + vec3 star_dir = normalize(sun_dir * dir.z + cross(sun_dir, vec3(0, 1, 0)) * dir.x + vec3(0, 1, 0) * dir.y); + star = is_star_at(star_dir); + } + + // Sun + const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0; + + vec3 sun_halo_color = mix( + SUN_HALO_DUSK, + SUN_HALO_DAY, + max(-sun_dir.z, 0) + ); + + vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * sun_halo_color; + vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.001, 0.0), 3000.0) * SUN_SURF_COLOR * SUN_COLOR_FACTOR; + vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1); + + // Moon + const vec3 MOON_SURF_COLOR = vec3(0.7, 1.0, 1.5) * 500.0; + const vec3 MOON_HALO_COLOR = vec3(0.015, 0.015, 0.05); + + vec3 moon_halo = pow(max(dot(dir, -moon_dir) + 0.1, 0.0), 8.0) * MOON_HALO_COLOR; + vec3 moon_surf = pow(max(dot(dir, -moon_dir) - 0.001, 0.0), 3000.0) * MOON_SURF_COLOR; + vec3 moon_light = clamp(moon_halo + moon_surf, vec3(0), vec3(max(dir.z * 3.0, 0))); + + // Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1 + + vec3 sky_top = mix( + mix( + SKY_DUSK_TOP + star / (1.0 + moon_surf * 100.0), + SKY_NIGHT_TOP + star / (1.0 + moon_surf * 100.0), + max(pow(sun_dir.z, 0.2), 0) + ), + SKY_DAY_TOP, + max(-sun_dir.z, 0) + ); + + vec3 sky_mid = mix( + mix( SKY_DUSK_MID, + SKY_NIGHT_MID, + max(pow(sun_dir.z, 0.2), 0) + ), + SKY_DAY_MID, + max(-sun_dir.z, 0) + ); + + vec3 sky_bot = mix( + mix( + SKY_DUSK_BOT, + SKY_NIGHT_BOT, + max(pow(sun_dir.z, 0.2), 0) + ), + SKY_DAY_BOT, + max(-sun_dir.z, 0) + ); + + vec3 sky_color = mix( + mix( + sky_mid, + sky_bot, + pow(max(-dir.z, 0), 0.4) + ), + sky_top, + max(dir.z, 0) + ); + + // Approximate distance to fragment + float f_dist = distance(origin, f_pos); + + // Clouds + #if (CLOUD_MODE == CLOUD_MODE_NONE) + clouds = vec4(0.0); + #elif (CLOUD_MODE == CLOUD_MODE_REGULAR) + clouds = get_cloud_color(dir, origin, time_of_day, f_dist, quality); + clouds.rgb *= get_sun_brightness(/*sun_dir*/) * (sun_halo * 1.5 + get_sun_color(/*sun_dir*/)) + get_moon_brightness(/*moon_dir*/) * (moon_halo * 80.0 + get_moon_color(/*moon_dir*/) + 0.25); + #endif + + if (f_dist > 5000.0) { + sky_color += sun_light + moon_light; + } + return mix(sky_color, clouds.rgb, clouds.a); + } else { + clouds = vec4(0.0); + return vec3(0.0); + } } vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_stars, out vec4 clouds) { - // Sky color - vec3 sun_dir = get_sun_dir(time_of_day); - vec3 moon_dir = get_moon_dir(time_of_day); - - // Add white dots for stars. Note these flicker and jump due to FXAA - float star = 0.0; - if (with_stars) { - vec3 star_dir = normalize(sun_dir * dir.z + cross(sun_dir, vec3(0, 1, 0)) * dir.x + vec3(0, 1, 0) * dir.y); - star = is_star_at(star_dir); - } - - // Sun - const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0; - - vec3 sun_halo_color = mix( - SUN_HALO_DUSK, - SUN_HALO_DAY, - max(-sun_dir.z, 0) - ); - - vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * sun_halo_color; - vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.001, 0.0), 3000.0) * SUN_SURF_COLOR; - vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1); - - // Moon - const vec3 MOON_SURF_COLOR = vec3(0.7, 1.0, 1.5) * 500.0; - const vec3 MOON_HALO_COLOR = vec3(0.015, 0.015, 0.05); - - vec3 moon_halo = pow(max(dot(dir, -moon_dir) + 0.1, 0.0), 8.0) * MOON_HALO_COLOR; - vec3 moon_surf = pow(max(dot(dir, -moon_dir) - 0.001, 0.0), 3000.0) * MOON_SURF_COLOR; - vec3 moon_light = clamp(moon_halo + moon_surf, vec3(0), vec3(max(dir.z * 3.0, 0))); - - // Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1 - - vec3 sky_top = mix( - mix( - SKY_DUSK_TOP + star / (1.0 + moon_surf * 100.0), - SKY_NIGHT_TOP + star / (1.0 + moon_surf * 100.0), - max(pow(sun_dir.z, 0.2), 0) - ), - SKY_DAY_TOP, - max(-sun_dir.z, 0) - ); - - vec3 sky_mid = mix( - mix( - SKY_DUSK_MID, - SKY_NIGHT_MID, - max(pow(sun_dir.z, 0.2), 0) - ), - SKY_DAY_MID, - max(-sun_dir.z, 0) - ); - - vec3 sky_bot = mix( - mix( - SKY_DUSK_BOT, - SKY_NIGHT_BOT, - max(pow(sun_dir.z, 0.2), 0) - ), - SKY_DAY_BOT, - max(-sun_dir.z, 0) - ); - - vec3 sky_color = mix( - mix( - sky_mid, - sky_bot, - pow(max(-dir.z, 0), 0.4) - ), - sky_top, - max(dir.z, 0) - ); - - // Approximate distance to fragment - float f_dist = distance(origin, f_pos); - - // Clouds - clouds = get_cloud_color(dir, origin, time_of_day, f_dist, quality); - clouds.rgb *= get_sun_brightness(sun_dir) * (sun_halo * 1.5 + get_sun_color(sun_dir)) + get_moon_brightness(moon_dir) * (moon_halo * 80.0 + get_moon_color(moon_dir) + 0.25); - - if (f_dist > 5000.0) { - sky_color += sun_light + moon_light; - } - - return mix(sky_color, clouds.rgb, clouds.a); + return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_stars, 1.0, clouds); } float fog(vec3 f_pos, vec3 focus_pos, uint medium) { - float fog_radius = view_distance.x; - float mist_radius = 10000000.0; + return max(1.0 - 5000.0 / (1.0 + distance(f_pos.xy, focus_pos.xy)), 0.0); - float min_fog = 0.5; - float max_fog = 1.0; + float fog_radius = view_distance.x; + float mist_radius = 10000000.0; - if (medium == 1u) { - mist_radius = UNDERWATER_MIST_DIST; - min_fog = 0.0; - } + float min_fog = 0.5; + float max_fog = 1.0; - float fog = distance(f_pos.xy, focus_pos.xy) / fog_radius; - float mist = distance(f_pos, focus_pos) / mist_radius; + if (medium == 1u) { + mist_radius = UNDERWATER_MIST_DIST; + min_fog = 0.0; + } - return pow(clamp((max(fog, mist) - min_fog) / (max_fog - min_fog), 0.0, 1.0), 1.7); + float fog = distance(f_pos.xy, focus_pos.xy) / fog_radius; + float mist = distance(f_pos, focus_pos) / mist_radius; + + return pow(clamp((max(fog, mist) - min_fog) / (max_fog - min_fog), 0.0, 1.0), 1.7); +} + +/* vec3 illuminate(vec3 color, vec3 light, vec3 diffuse, vec3 ambience) { + float avg_col = (color.r + color.g + color.b) / 3.0; + return ((color - avg_col) * light + (diffuse + ambience) * avg_col) * (diffuse + ambience); +} */ +vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted, vec3 reflected) { + return emitted + reflected; + const float NIGHT_EXPOSURE = 10.0; + const float DUSK_EXPOSURE = 2.0;//0.8; + const float DAY_EXPOSURE = 1.0;//0.7; + +#if (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_ASHIKHMIN) + const float DAY_SATURATION = 1.1; +#else + const float DAY_SATURATION = 1.0; +#endif + const float DUSK_SATURATION = 0.6; + const float NIGHT_SATURATION = 0.1; + + const float gamma = /*0.5*//*1.*0*/1.0;//1.0; + /* float light = length(emitted + reflected); + float color = srgb_to_linear(emitted + reflected); + float avg_col = (color.r + color.g + color.b) / 3.0; + return ((color - avg_col) * light + reflected * avg_col) * (emitted + reflected); */ + // float max_intensity = vec3(1.0); + vec3 color = emitted + reflected; + float lum = rel_luminance(color); + // float lum_sky = lum - max_light; + + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ + float sky_light = rel_luminance( + get_sun_color(/*sun_dir*/) * get_sun_brightness(/*sun_dir*/) * SUN_COLOR_FACTOR + + get_moon_color(/*moon_dir*/) * get_moon_brightness(/*moon_dir*/)); + + // Tone mapped value. + // vec3 T = /*color*//*lum*/color;//normalize(color) * lum / (1.0 + lum); + // float alpha = 0.5;//2.0; + float alpha = mix( + mix( + DUSK_EXPOSURE, + NIGHT_EXPOSURE, + max(sun_dir.z, 0) + ), + DAY_EXPOSURE, + max(-sun_dir.z, 0) + ); + vec3 now_light = moon_dir.z < 0 ? moon_dir.xyz : sun_dir.xyz; + float cos_view_light = dot(-now_light, view_dir); + // alpha *= exp(1.0 - cos_view_light); + // sky_light *= 1.0 - log(1.0 + view_dir.z); + float alph = sky_light > 0.0 && max_light > 0.0 ? mix(1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light / (0.0 + sky_light)), 1.0, clamp(max_light - sky_light, 0.0, 1.0)) : 1.0; + alpha = alpha * min(alph, 1.0);//((max_light > 0.0 && max_light > sky_light /* && sky_light > 0.0*/) ? /*1.0*/1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light - (0.0 + sky_light)) : 1.0); + // alpha = alpha * min(1.0, (max_light == 0.0 ? 1.0 : (1.0 + abs(lum_sky)) / /*(1.0 + max_light)*/max_light)); + + vec3 col_adjusted = lum == 0.0 ? vec3(0.0) : color / lum; + + // float L = lum == 0.0 ? 0.0 : log(lum); + + + // // float B = T; + // // float B = L + log(alpha); + // float B = lum; + + // float D = L - B; + + // float o = 0.0;//log(PERSISTENT_AMBIANCE); + // float scale = /*-alpha*/-alpha;//1.0; + + // float B_ = (B - o) * scale; + + // // float T = lum; + // float O = exp(B_ + D); + + float T = 1.0 - exp(-alpha * lum);//lum / (1.0 + lum); + // float T = lum; + + // Heuristic desaturation + // const float s = 0.8; + float s = mix( + mix( + DUSK_SATURATION, + NIGHT_SATURATION, + max(sun_dir.z, 0) + ), + DAY_SATURATION, + max(-sun_dir.z, 0) + ); + // s = max(s, (max_light) / (1.0 + s)); + s = max(s, max_light / (1.0 + max_light)); + + vec3 c = pow(col_adjusted, vec3(s)) * T; + // vec3 c = col_adjusted * T; + // vec3 c = sqrt(col_adjusted) * T; + // vec3 c = /*col_adjusted * */col_adjusted * T; + + // return color; + return c; + // float sum_col = color.r + color.g + color.b; + // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); } diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index a56a97b384..9db36c1cb1 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -1,3 +1,24 @@ +// Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. +// See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water +const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); + +// // NOTE: Automatic in v4.0 +// float +// mip_map_level(in vec2 texture_coordinate) +// { +// // The OpenGL Graphics System: A Specification 4.2 +// // - chapter 3.9.11, equation 3.21 +// +// +// vec2 dx_vtc = dFdx(texture_coordinate); +// vec2 dy_vtc = dFdy(texture_coordinate); +// float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); +// +// +// //return max(0.0, 0.5 * log2(delta_max_sqr) - 1.0); // == log2(sqrt(delta_max_sqr)); +// return 0.5 * log2(delta_max_sqr); // == log2(sqrt(delta_max_sqr)); +// } + //https://gamedev.stackexchange.com/questions/92015/optimized-linear-to-srgb-glsl vec3 srgb_to_linear(vec3 srgb) { bvec3 cutoff = lessThan(srgb, vec3(0.04045)); @@ -7,10 +28,592 @@ vec3 srgb_to_linear(vec3 srgb) { return mix(higher, lower, cutoff); } -vec3 linear_to_srgb(vec3 linear) { - bvec3 cutoff = lessThan(linear, vec3(0.0031308)); - vec3 higher = vec3(1.055) * pow(linear, vec3(1.0 / 2.4)) - vec3(0.055); - vec3 lower = linear * vec3(12.92); - - return mix(higher, lower, cutoff); +vec3 linear_to_srgb(vec3 col) { + // bvec3 cutoff = lessThan(col, vec3(0.0060)); + // return mix(11.500726 * col, , cutoff); + vec3 s1 = vec3(sqrt(col.r), sqrt(col.g), sqrt(col.b)); + vec3 s2 = vec3(sqrt(s1.r), sqrt(s1.g), sqrt(s1.b)); + vec3 s3 = vec3(sqrt(s2.r), sqrt(s2.g), sqrt(s2.b)); + return vec3( + mix(11.500726 * col.r, (0.585122381 * s1.r + 0.783140355 * s2.r - 0.368262736 * s3.r), clamp((col.r - 0.0060) * 10000.0, 0.0, 1.0)), + mix(11.500726 * col.g, (0.585122381 * s1.g + 0.783140355 * s2.g - 0.368262736 * s3.g), clamp((col.g - 0.0060) * 10000.0, 0.0, 1.0)), + mix(11.500726 * col.b, (0.585122381 * s1.b + 0.783140355 * s2.b - 0.368262736 * s3.b), clamp((col.b - 0.0060) * 10000.0, 0.0, 1.0)) + ); } + +float pow5(float x) { + float x2 = x * x; + return x2 * x2 * x; +} + +vec4 pow5(vec4 x) { + vec4 x2 = x * x; + return x2 * x2 * x; +} + +// Fresnel angle for perfectly specular dialectric materials. + +// Schlick approximation +vec3 schlick_fresnel(vec3 Rs, float cosTheta) { + // auto pow5 = [](Float v) { return (v * v) * (v * v) * v; }; + // return Rs + pow5(1 - cosTheta) * (Spectrum(1.) - Rs); + return Rs + pow5(1.0 - cosTheta) * (1.0 - Rs); +} + +// Beckmann Distribution +float BeckmannDistribution_D(float NdotH, float alpha) { + const float PI = 3.1415926535897932384626433832795; + float NdotH2 = NdotH * NdotH; + float NdotH2m2 = NdotH2 * alpha * alpha; + float k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + return mix(k_spec, 0.0, NdotH == 0.0); +} + +// Voxel Distribution +float BeckmannDistribution_D_Voxel(vec3 wh, vec3 voxel_norm, float alpha) { + vec3 sides = sign(voxel_norm); + // vec3 cos_sides_i = /*sides * */sides * norm; + // vec3 cos_sides_o = max(sides * view_dir, 0.0); + + vec3 NdotH = wh * sides;//max(wh * sides, 0.0);/*cos_sides_i*///max(sides * wh, 0.0); + + const float PI = 3.1415926535897932384626433832795; + vec3 NdotH2 = NdotH * NdotH; + vec3 NdotH2m2 = NdotH2 * alpha * alpha; + vec3 k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + return dot(mix(k_spec, /*cos_sides_o*/vec3(0.0), equal(NdotH, vec3(0.0))), /*cos_sides_i*/abs(voxel_norm)); + // // const float PI = 3.1415926535897932384626433832795; + // const vec3 normals[6] = vec3[](vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1)); + + // float voxel_norm = 0.0; + // for (int i = 0; i < 6; i ++) { + // // Light reflecting off the half-angle can shine on up to three sides. + // // So, the idea here is to figure out the ratio of visibility of each of these + // // three sides such that their sum adds to 1, then computing a Beckmann Distribution for each side times + // // the this ratio. + // // + // // The ratio of these normals in each direction should be the sum of their cosines with the light over π, + // // I think. + // // + // // cos (wh, theta) + // // + // // - one normal + // // + // // The ratio of each of the three exposed sides should just be the slope. + // vec3 side = normals[i]; + // float side_share = max(dot(norm, side), 0.0); + // float NdotH = max(dot(wh, side), 0.0); + // voxel_norm += side_share * BeckmannDistribution_D(NdotH, alpha); + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // } + + // /* float NdotH = dot(wh, norm); + // float NdotH2 = NdotH * NdotH; + // float NdotH2m2 = NdotH2 * alpha * alpha; + + // float k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + // return mix(k_spec, 0.0, NdotH == 0.0); */ + // return voxel_norm; +} + +float TrowbridgeReitzDistribution_D_Voxel(vec3 wh, vec3 voxel_norm, float alpha) { + vec3 sides = sign(voxel_norm); + // vec3 cos_sides_i = /*sides * */sides * norm; + // vec3 cos_sides_o = max(sides * view_dir, 0.0); + + vec3 NdotH = wh * sides;//max(wh * sides, 0.0);/*cos_sides_i*///max(sides * wh, 0.0); + + const float PI = 3.1415926535897932384626433832795; + vec3 NdotH2 = NdotH * NdotH; + // vec3 m2 = alpha * alpha; + // vec3 NdotH2m2 = NdotH2 * m2; + vec3 NdotH2m2 = NdotH2 * alpha * alpha; + // vec3 Tan2Theta = (1 - NdotH2) / NdotH2; + // vec3 e = (NdotH2 / m2 + (1 - NdotH2) / m2) * Tan2Theta; + // vec3 e = 1 / m2 * (1 - NdotH2) / NdotH2; + vec3 e = (1 - NdotH2) / NdotH2m2; + vec3 k_spec = 1.0 / (PI * NdotH2m2 * NdotH2 * (1 + e) * (1 + e)); + // vec3 k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + return dot(mix(k_spec, /*cos_sides_o*/vec3(0.0), equal(NdotH, vec3(0.0))), /*cos_sides_i*/abs(voxel_norm)); +} + +float BeckmannDistribution_Lambda(vec3 norm, vec3 dir, float alpha) { + float CosTheta = /*max(dot(norm, dir), 0.0);*/dot(norm, dir); + /* if (CosTheta == 0.0) { + return 0.0; + } + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + float TanTheta = SinTheta / CosTheta; + float absTanTheta = abs(TanTheta); */ + // vec3 w = normalize(dir - dot(dir, norm) * (norm)); + // float CosTheta = w.z; + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + float TanTheta = SinTheta / CosTheta; + float absTanTheta = abs(TanTheta); + /* if (isinf(absTanTheta)) { + return 0.0; + } */ + /* float CosPhi = mix(clamp(projDirNorm.x / sinTheta, -1.0, 1.0), 0.0, sinTheta == 0.0); + float SinPhi = mix(clamp(projDirNorm.y / sinTheta, -1.0, 1.0), 0.0, sinTheta == 0.0); + float alpha = sqrt(CosPhi * CosPhi * alphax * alphax + SinPhi * SinPhi * alphay * alphay); */ + // Float absTanTheta = std::abs(TanTheta(w)); + // if (std::isinf(absTanTheta)) return 0.; + // <> + // Float alpha = std::sqrt(Cos2Phi(w) * alphax * alphax + + // Sin2Phi(w) * alphay * alphay); + float a = 1.0 / (alpha * absTanTheta); + /* if (a >= 1.6) { + return 0.0; + } + + return (1.0 - 1.259 * a + 0.396 * a * a) / (3.535 * a + 2.181 * a * a); */ + + return mix(max(0.0, (1.0 - 1.259 * a + 0.396 * a * a) / (3.535 * a + 2.181 * a * a)), 0.0, isinf(absTanTheta) || a >= 1.6); + // Float a = 1 / (alpha * absTanTheta); + // if (a >= 1.6f) + // return 0; + // return (1 - 1.259f * a + 0.396f * a * a) / + // (3.535f * a + 2.181f * a * a); + // return 1 / (1 + Lambda(wo) + Lambda(wi)); +} + +float BeckmannDistribution_G(vec3 norm, vec3 dir, vec3 light_dir, float alpha) { + // return 1 / (1 + Lambda(wo) + Lambda(wi)); + return 1.0 / (1.0 + BeckmannDistribution_Lambda(norm, dir, alpha) + BeckmannDistribution_Lambda(norm, -light_dir, alpha)); +} + +// Fresnel blending +// +// http://www.pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models.html#fragment-MicrofacetDistributionPublicMethods-2 +// and +// http://www.pbr-book.org/3ed-2018/Reflection_Models/Fresnel_Incidence_Effects.html +vec3 FresnelBlend_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_s, float alpha) { + const float PI = 3.1415926535897932384626433832795; + alpha = alpha * sqrt(2.0); + float cos_wi = /*max(*/dot(-light_dir, norm)/*, 0.0)*/; + float cos_wo = /*max(*/dot(dir, norm)/*, 0.0)*/; + + vec3 diffuse = (28.0 / (23.0 * PI)) * R_d * + (1.0 - R_s) * + (1.0 - pow5(1.0 - 0.5 * abs(cos_wi))) * + (1.0 - pow5(1.0 - 0.5 * abs(cos_wo))); + /* Spectrum diffuse = (28.f/(23.f*Pi)) * Rd * + (Spectrum(1.f) - Rs) * + (1 - pow5(1 - .5f * AbsCosTheta(wi))) * + (1 - pow5(1 - .5f * AbsCosTheta(wo))); */ + // Vector3f wh = wi + wo; + vec3 wh = -light_dir + dir; +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + bool is_blocked = cos_wi == 0.0 || cos_wo == 0.0; +#else + bool is_blocked = cos_wi <= 0.0 || cos_wo <= 0.0; +#endif + if (is_blocked) { + return vec3(/*diffuse*/0.0); + } + // if (cos_wo < 0.0) { + // return /*vec3(0.0)*/diffuse; + // } + /* if (cos_wi == 0.0 || cos_wo == 0.0) { + return vec3(0.0); + } */ + /* if (wh.x == 0 && wh.y == 0 && wh.z == 0) { + return vec3(0.0); + // return Spectrum(0); + } */ + wh = normalize(wh);//mix(normalize(wh), vec3(0.0), equal(light_dir, dir)); + float dot_wi_wh = dot(-light_dir, wh); + vec3 specular = BeckmannDistribution_D(dot(wh, norm), alpha) / + (4 * abs(dot_wi_wh) * + max(abs(cos_wi), abs(cos_wo))) * + schlick_fresnel(R_s, dot_wi_wh); + // Spectrum specular = distribution->D(wh) / + // (4 * AbsDot(wi, wh) * + // std::max(AbsCosTheta(wi), AbsCosTheta(wo))) * + // SchlickFresnel(Dot(wi, wh)); + return mix(/*diffuse*//* + specular*/diffuse + specular, vec3(0.0), bvec3(all(equal(light_dir, dir)))); +} + +// Fresnel blending +// +// http://www.pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models.html#fragment-MicrofacetDistributionPublicMethods-2 +// and +// http://www.pbr-book.org/3ed-2018/Reflection_Models/Fresnel_Incidence_Effects.html +vec3 FresnelBlend_Voxel_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_s, float alpha, vec3 voxel_norm, float dist) { + const float PI = 3.1415926535897932384626433832795; + alpha = alpha * sqrt(2.0); + float cos_wi = /*max(*/dot(-light_dir, norm)/*, 0.0)*/; + float cos_wo = /*max(*/dot(dir, norm)/*, 0.0)*/; + +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + vec4 AbsNdotL = abs(vec4(light_dir, cos_wi)); + vec4 AbsNdotV = abs(vec4(dir, cos_wo)); +#else + vec3 sides = sign(voxel_norm); + vec4 AbsNdotL = vec4(max(-light_dir * sides, 0.0), abs(cos_wi)); + vec4 AbsNdotV = vec4(max(dir * sides, 0.0), abs(cos_wo)); +#endif + + // float R_r = 1.0 - R_s; + // float R_r = 1.0 - schlick_fresnel(R_s, cos_wi); + // // Rs + pow5(1.0 - cosTheta) * (1.0 - Rs) + // vec4 R_r = 1.0 - (R_s + (1.0 - R_s) * schlick_fresnel(R_s, cos_wi)); + // mat4 R_r = 1.0 - (vec4(R_s, 0.0) + vec4(1.0 - R_s, 0.0) * pow5(1.0 - AbsNdotL)); + // vec4 AbsNdotL5 = pow5(1.0 - AbsNdotL); + // vec4 R_s4 = vec4(R_s, 0.0); + // mat4 R_r = + // // mat4(1.0 - (R_s.r + (1.0 - R_s.r) * AbsNdotL5), + // // 1.0 - (R_s.g + (1.0 - R_s.g) * AbsNdotL5), + // // 1.0 - (R_s.b + (1.0 - R_s.b) * AbsNdotL5), + // // vec4(0.0) + // // ); + // mat4(1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.x), + // 1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.y), + // 1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.z), + // 1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.w) + // ); + // * ) (R1.0 - R_s.r) 1.0 - (vec4(R_s, 0.0) + vec4(1.0 - R_s, 0.0) * pow5(1.0 - AbsNdotL)); + + vec4 diffuse_factor = + // vec4(abs(vec4(-light_dir * sides, cos_wi))) + (1.0 - pow5(1.0 - 0.5 * AbsNdotL)) * + // (1.0 - pow5(1.0 - 0.5 * abs(vec4(-light_dir * sides, cos_wi)))) * + // (1.0 - pow5(1.0 - 0.5 * abs(vec4(dir * sides, cos_wo)))) + (1.0 - pow5(1.0 - 0.5 * AbsNdotV)) + // vec4(1.0) + ; + /* vec4 diffuse_factor = + (1.0 - pow5(1.0 - 0.5 * max(vec4(-light_dir * sides, abs(cos_wi)), 0.0))) * + (1.0 - pow5(1.0 - 0.5 * max(vec4(dir * sides, abs(cos_wo)), 0.0))); */ + + vec3 diffuse = (28.0 / (23.0 * PI))/*(1.0 / PI)*/ * R_d * + (1.0 - R_s) * + //vec3( + dot(diffuse_factor, /*R_r * */vec4(abs(norm) * (1.0 - dist), dist)) + //) + ; + + vec3 wh = -light_dir + dir; +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + bool is_blocked = cos_wi == 0.0 || cos_wo == 0.0; +#else + bool is_blocked = cos_wi <= 0.0 || cos_wo <= 0.0; +#endif + if (is_blocked) { + return vec3(/*diffuse*/0.0); + } + wh = normalize(wh);//mix(normalize(wh), vec3(0.0), equal(light_dir, dir)); + float dot_wi_wh = dot(-light_dir, wh); + // float distr = TrowbridgeReitzDistribution_D_Voxel(wh, voxel_norm, alpha); + float distr = BeckmannDistribution_D_Voxel(wh, voxel_norm, alpha); + // float distr = BeckmannDistribution_D(dot(wh, norm), alpha); + vec3 specular = distr / + (4 * abs(dot_wi_wh) * + max(abs(cos_wi), abs(cos_wo))) * + schlick_fresnel(R_s, dot_wi_wh); + return mix(/*diffuse*//* + specular*/diffuse + specular, vec3(0.0), bvec3(all(equal(light_dir, dir)))); +} + +// Phong reflection. +// +// Note: norm, dir, light_dir must all be normalizd. +vec3 light_reflection_factor2(vec3 norm, vec3 dir, vec3 light_dir, vec3 k_d, vec3 k_s, float alpha) { + // TODO: These are supposed to be the differential changes in the point location p, in tangent space. + // That is, assuming we can parameterize a 2D surface by some function p : R² → R³, mapping from + // points in a plane to 3D points on the surface, we can define + // ∂p(u,v)/∂u and ∂p(u,v)/∂v representing the changes in the pont location as we move along these + // coordinates. + // + // Then we can define the normal at a point, n(u,v) = ∂p(u,v)/∂u × ∂p(u,v)/∂v. + // + // Additionally, we can define the change in *normals* at each point using the + // Weingarten equations (see http://www.pbr-book.org/3ed-2018/Shapes/Spheres.html): + // + // ∂n/∂u = (fF - eG) / (EG - F²) ∂p/∂u + (eF - fE) / (EG - F²) ∂p/∂v + // ∂n/∂v = (gF - fG) / (EG - F²) ∂p/∂u + (fF - gE) / (EG - F²) ∂p/∂v + // + // where + // + // E = |∂p/∂u ⋅ ∂p/∂u| + // F = ∂p/∂u ⋅ ∂p/∂u + // G = |∂p/∂v ⋅ ∂p/∂v| + // + // and + // + // e = n ⋅ ∂²p/∂u² + // f = n ⋅ ∂²p/(∂u∂v) + // g = n ⋅ ∂²p/∂v² + // + // For planes (see http://www.pbr-book.org/3ed-2018/Shapes/Triangle_Meshes.html) we have + // e = f = g = 0 (since the plane has no curvature of any sort) so we get: + // + // ∂n/∂u = (0, 0, 0) + // ∂n/∂v = (0, 0, 0) + // + // To find ∂p/∂u and ∂p/∂v, we first write p and u parametrically: + // p(u, v) = p0 + u ∂p/∂u + v ∂p/∂v + // + // ( u₀ - u₂ v₀ - v₂ + // u₁ - u₂ v₁ - v₂ ) + // + // Basis: plane norm = norm = (0, 0, 1), x vector = any orthgonal vector on the plane. + // vec3 w_i = + // vec3 w_i = vec3(view_mat * vec4(-light_dir, 1.0)); + // vec3 w_o = vec3(view_mat * vec4(light_dir, 1.0)); + float g = 1.0;// BeckmannDistribution_G(norm, dir, light_dir, alpha); + return FresnelBlend_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s * g, alpha); + // const float PI = 3.141592; + // alpha = alpha * sqrt(2.0); + // float ndotL = /*max*/(dot(norm, -light_dir)/*, 0.0*/); + + // //if (ndotL > 0.0/* && dot(s_norm, -light_dir) > 0.0*/) { + // vec3 H = normalize(-light_dir + dir); + + // float NdotH = dot(norm, H); + // float NdotH2 = NdotH * NdotH; + // float NdotH2m2 = NdotH2 * alpha * alpha; + // float k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + // return mix(k_s * k_spec, vec3(0.0), bvec3(ndotL <= 0.0 || NdotH == 0.0)); + // // + // // (k_d * (L ⋅ N) + k_s * (R ⋅ V)^α) + // // return k_d * ndotL + mix(k_s * pow(max(dot(norm, H), 0.0), alpha * 4.0), vec3(0.0), bvec3(ndotL == 0.0)); + // // } + // // return vec3(0.0); +} + +vec3 light_reflection_factor(vec3 norm, vec3 dir, vec3 light_dir, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting) { +#if (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_LAMBERTIAN) + const float PI = 3.141592; + #if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + vec4 AbsNdotL = abs(vec4(light_dir, dot(norm, light_dir))); + #else + vec3 sides = sign(voxel_norm); + vec4 AbsNdotL = max(vec4(-light_dir * sides, dot(norm, -light_dir)), 0.0); + #endif + float diffuse = dot(AbsNdotL, vec4(abs(voxel_norm) * (1.0 - voxel_lighting), voxel_lighting)); + #elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + float diffuse = abs(dot(norm, light_dir)); + #else + float diffuse = max(dot(norm, -light_dir), 0.0); + #endif + #endif + return k_d / PI * diffuse; +#elif (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_BLINN_PHONG) + const float PI = 3.141592; + alpha = alpha * sqrt(2.0); + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + float ndotL = abs(dot(norm, light_dir)); + #else + float ndotL = max(dot(norm, -light_dir), 0.0); + #endif + + if (ndotL > 0.0) { + #if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + vec4 AbsNdotL = abs(vec4(light_dir, ndotL)); + #else + vec3 sides = sign(voxel_norm); + vec4 AbsNdotL = max(vec4(-light_dir * sides, ndotL), 0.0); + #endif + float diffuse = dot(AbsNdotL, vec4(abs(voxel_norm) * (1.0 - voxel_lighting), voxel_lighting)); + #elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) + float diffuse = ndotL; + #endif + vec3 H = normalize(-light_dir + dir); + + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + float NdotH = abs(dot(norm, H)); + #else + float NdotH = max(dot(norm, H), 0.0); + #endif + return (1.0 - k_s) / PI * k_d * diffuse + k_s * pow(NdotH, alpha/* * 4.0*/); + } + + return vec3(0.0); +#elif (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_ASHIKHMIN) + #if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) + return FresnelBlend_Voxel_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha, voxel_norm, voxel_lighting); + #elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) + //if (voxel_lighting < 1.0) { + return FresnelBlend_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha); + //} else { + // return FresnelBlend_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha); + //} + #endif +#endif +} + +float rel_luminance(vec3 rgb) +{ + // https://en.wikipedia.org/wiki/Relative_luminance + const vec3 W = vec3(0.2126, 0.7152, 0.0722); + return dot(rgb, W); +} + +// From https://discourse.vvvv.org/t/infinite-ray-intersects-with-infinite-plane/10537 +// out of laziness. +bool IntersectRayPlane(vec3 rayOrigin, vec3 rayDirection, vec3 posOnPlane, vec3 planeNormal, inout vec3 intersectionPoint) +{ + float rDotn = dot(rayDirection, planeNormal); + + //parallel to plane or pointing away from plane? + if (rDotn < 0.0000001 ) + return false; + + float s = dot(planeNormal, (posOnPlane - rayOrigin)) / rDotn; + + intersectionPoint = rayOrigin + s * rayDirection; + + return true; +} + +// Compute uniform attenuation due to beam passing through a substance that fills an area below a horizontal plane +// (e.g. in most cases, water below the water surface depth) using the simplest form of the Beer-Lambert law +// (https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law): +// +// I(z) = I₀ e^(-μz) +// +// We compute this value, except for the initial intensity which may be multiplied out later. +// +// wpos is the position of the point being hit. +// ray_dir is the reversed direction of the ray (going "out" of the point being hit). +// mu is the attenuation coefficient for R, G, and B wavelenghts. +// surface_alt is the estimated altitude of the horizontal surface separating the substance from air. +// defaultpos is the position to use in computing the distance along material at this point if there was a failure. +// +// Ideally, defaultpos is set so we can avoid branching on error. +vec3 compute_attenuation(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) { +#if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE) + return vec3(1.0); +#elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + return vec3(1.0); + #else + // return vec3(1.0); + /*if (mu == vec3(0.0)) { + return vec3(1.0); + }*//* else { + return vec3(0.0); + }*/ + // return vec3(0.0); + // vec3 surface_dir = /*surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0)*/vec3(0.0, 0.0, sign(surface_alt - wpos.z)); + ray_dir = faceforward(ray_dir, vec3(0.0, 0.0, -1.0), ray_dir); + vec3 surface_dir = surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0); + // vec3 surface_dir = faceforward(vec3(0.0, 0.0, 1.0), ray_dir, vec3(0.0, 0.0, 1.0)); + bool _intersects_surface = IntersectRayPlane(wpos, ray_dir, vec3(0.0, 0.0, surface_alt), surface_dir, defaultpos); + float depth = length(defaultpos - wpos); + return exp(-mu * depth); + #endif +#endif +} + +// vec3 compute_attenuation2(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) { +// #if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE) +// return vec3(1.0); +// #elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE) +// // return vec3(1.0); +// /*if (mu == vec3(0.0)) { +// return vec3(1.0); +// }*//* else { +// return vec3(0.0); +// }*/ +// // return vec3(0.0); +// // vec3 surface_dir = /*surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0)*/vec3(0.0, 0.0, sign(surface_alt - wpos.z)); +// vec3 surface_dir = surface_alt < wpos.z ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); +// // vec3 surface_dir = faceforward(vec3(0.0, 0.0, 1.0), ray_dir, vec3(0.0, 0.0, 1.0)); +// bool _intersects_surface = IntersectRayPlane(wpos, ray_dir, vec3(0.0, 0.0, surface_alt), surface_dir, defaultpos); +// float depth = length(defaultpos - wpos); +// return exp(-mu * depth); +// #endif +// } + +// Same as compute_attenuation but since both point are known, set a maximum to make sure we don't exceed the length +// from the default point. +vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) { +#if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE) + return vec3(1.0); +#elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE) + // return vec3(1.0); + /*if (mu == vec3(0.0)) { + return vec3(1.0); + }*//* else { + return vec3(0.0); + }*/ + // return vec3(0.0); + vec3 surface_dir = /*surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0)*/vec3(0.0, 0.0, sign(wpos.z - surface_alt)); + // vec3 surface_dir = surface_alt < wpos.z ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + // vec3 surface_dir = faceforward(vec3(0.0, 0.0, 1.0), ray_dir, vec3(0.0, 0.0, 1.0)); + float max_length = dot(defaultpos - wpos, defaultpos - wpos); + bool _intersects_surface = IntersectRayPlane(wpos, ray_dir, vec3(0.0, 0.0, surface_alt), surface_dir, defaultpos); + float depth2 = min(max_length, dot(defaultpos - wpos, defaultpos - wpos)); + return exp(-mu * sqrt(depth2)); +#endif +} + +//#ifdef HAS_SHADOW_MAPS +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +//uniform sampler2DShadow t_directed_shadow_maps; +//// uniform sampler2DArrayShadow t_directed_shadow_maps; +// +//float ShadowCalculationDirected(in vec4 /*light_pos[2]*/sun_pos, uint lightIndex) +//{ +// float bias = 0.0;//-0.0001;// 0.05 / (2.0 * view_distance.x); +// // const vec3 sampleOffsetDirections[20] = vec3[] +// // ( +// // vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), +// // vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), +// // vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), +// // vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), +// // vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) +// // // vec3(0, 0, 0) +// // ); +// /* if (lightIndex >= light_shadow_count.z) { +// return 1.0; +// } */ +// // vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz; +// float visibility = textureProj(t_directed_shadow_maps, sun_pos); +// // float visibility = textureProj(t_directed_shadow_maps, vec4(fragPos.xy, /*lightIndex, */fragPos.z + bias, sun_pos.w)); +// return visibility; +// // return mix(visibility, 0.0, sun_pos.z < -1.0); +// // return mix(mix(0.0, 1.0, visibility == 1.0), 1.0, sign(sun_pos.w) * sun_pos.z > /*1.0*/abs(sun_pos.w)); +// // return visibility == 1.0 ? 1.0 : 0.0; +// /* if (visibility == 1.0) { +// return 1.0; +// } */ +// // return visibility; +// /* if (fragPos.z > 1.0) { +// return 1.0; +// } */ +// // if (visibility <= 0.75) { +// // return 0.0; +// // } +// // int samples = 20; +// // float shadow = 0.0; +// // // float bias = 0.0001; +// // float viewDistance = length(cam_pos.xyz - fragPos); +// // // float diskRadius = 0.2 * (1.0 + (viewDistance / screen_res.w)) / 25.0; +// // float diskRadius = 0.0008;//0.005;// / (2.0 * view_distance.x);//(1.0 + (viewDistance / screen_res.w)) / 25.0; +// // for(int i = 0; i < samples; ++i) +// // { +// // vec3 currentDepth = fragPos + vec3(sampleOffsetDirections[i].xyz) * diskRadius + bias; +// // visibility = texture(t_directed_shadow_maps, vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/); +// // shadow += mix(visibility, 1.0, visibility >= 0.5); +// // } +// // shadow /= float(samples); +// // return shadow; +//} +// #elif (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_CHEAP) +//float ShadowCalculationDirected(in vec4 light_pos[2], uint lightIndex) +//{ +// return 1.0; +//} +// #endif +//#else +//float ShadowCalculationDirected(in vec4 light_pos[2], uint lightIndex) +//{ +// return 1.0; +//} +//#endif diff --git a/assets/voxygen/shaders/light-shadows-directed-frag.glsl b/assets/voxygen/shaders/light-shadows-directed-frag.glsl new file mode 100644 index 0000000000..e201b6ed5e --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-directed-frag.glsl @@ -0,0 +1,48 @@ +// NOTE: We currently do nothing, and just rely on the default shader behavior. +// +// However, in the future we might apply some depth transforms here. + +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +// // Currently, we only need globals for the far plane. +// #include +// // Currently, we only need lights for the light position +// #include + +// in vec3 FragPos; // FragPos from GS (output per emitvertex) +// flat in int FragLayer; + +void main() +{ + // Only need to do anything with point lights, since sun and moon should already have nonlinear + // distance. + /*if (FragLayer > 0) */{ + // get distance between fragment and light source + // float lightDistance = length(FragPos - lights[FragLayer & 31].light_pos.xyz); + + // // // map to [0;1] range by dividing by far_plane + // lightDistance = lightDistance / screen_res.w;//FragPos.w;//screen_res.w; + + // // // write this as modified depth + // // // lightDistance = -1000.0 / (lightDistance + 10000.0); + // // // lightDistance /= screen_res.w; + // gl_FragDepth = lightDistance;// / /*FragPos.w;*/screen_res.w;//-1000.0 / (lightDistance + 1000.0);//lightDistance + } +} diff --git a/assets/voxygen/shaders/light-shadows-directed-vert.glsl b/assets/voxygen/shaders/light-shadows-directed-vert.glsl new file mode 100644 index 0000000000..ad50fe5ad4 --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-directed-vert.glsl @@ -0,0 +1,63 @@ +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + +// Currently, we only need globals for focus_off. +#include +// For shadow locals. +#include + +/* Accurate packed shadow maps for many lights at once! + * + * Ideally, we would just write to a bitmask... + * + * */ + +in uint v_pos_norm; +// in uint v_col_light; +// in vec4 v_pos; + +// Light projection matrices. +layout (std140) +uniform u_locals { + vec3 model_offs; + float load_time; + ivec4 atlas_offs; +}; + +// out vec4 shadowMapCoord; + +const int EXTRA_NEG_Z = 32768; + +void main() { +#if (SHADOW_MODE == SHADOW_MODE_MAP) + vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; + // f_pos = v_pos; + // vec3 f_pos = f_chunk_pos + model_offs; + + // gl_Position = v_pos + vec4(model_offs, 0.0); + gl_Position = /*all_mat * */shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); + // gl_Position.z = -gl_Position.z; + // gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); + // shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex; + // vec4(v_pos, 0.0, 1.0); +#endif +} diff --git a/assets/voxygen/shaders/light-shadows-figure-vert.glsl b/assets/voxygen/shaders/light-shadows-figure-vert.glsl new file mode 100644 index 0000000000..b7d9d1a21f --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-figure-vert.glsl @@ -0,0 +1,74 @@ +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + +// Currently, we only need globals for focus_off. +#include +// For shadow locals. +#include + +/* Accurate packed shadow maps for many lights at once! + * + * Ideally, we would just write to a bitmask... + * + * */ + +in uint v_pos_norm; +in uint v_atlas_pos; +// in uint v_col_light; +// in vec4 v_pos; + +layout (std140) +uniform u_locals { + mat4 model_mat; + vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; + // bit 0 - is player + // bit 1-31 - unused + int flags; +}; + +struct BoneData { + mat4 bone_mat; + mat4 normals_mat; +}; + +layout (std140) +uniform u_bones { + // Warning: might not actually be 16 elements long. Don't index out of bounds! + BoneData bones[16]; +}; + +// out vec4 shadowMapCoord; + +void main() { +#if (SHADOW_MODE == SHADOW_MODE_MAP) + uint bone_idx = (v_pos_norm >> 27) & 0xFu; + vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; + + vec3 f_pos = ( + bones[bone_idx].bone_mat * + vec4(pos, 1.0) + ).xyz + (model_pos - focus_off.xyz/* + vec3(0.0, 0.0, 0.0001)*/); + + gl_Position = shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos, 1.0); +#endif +} diff --git a/assets/voxygen/shaders/light-shadows-frag.glsl b/assets/voxygen/shaders/light-shadows-frag.glsl new file mode 100644 index 0000000000..ee3f84070b --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-frag.glsl @@ -0,0 +1,49 @@ +// NOTE: We currently do nothing, and just rely on the default shader behavior. +// +// However, in the future we might apply some depth transforms here. + +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +// Currently, we only need globals for the far plane. +#include +// // Currently, we only need lights for the light position +// #include + +// in vec3 FragPos; // FragPos from GS (output per emitvertex) +// flat in int FragLayer; + +void main() +{ + // Only need to do anything with point lights, since sun and moon should already have nonlinear + // distance. + ///*if (FragLayer > 0) */{ + // // get distance between fragment and light source + // float lightDistance = length(FragPos); + // // float lightDistance = length(FragPos - lights[((/*FragLayer*/1 - 1) & 31)].light_pos.xyz); + + // // // map to [0;1] range by dividing by far_plane + // lightDistance = lightDistance / screen_res.w;//FragPos.w;//screen_res.w; + + // // // write this as modified depth + // // // lightDistance = -1000.0 / (lightDistance + 10000.0); + // // // lightDistance /= screen_res.w; + // gl_FragDepth = lightDistance;// / /*FragPos.w;*/screen_res.w;//-1000.0 / (lightDistance + 1000.0);//lightDistance + //} +} diff --git a/assets/voxygen/shaders/light-shadows-geom.glsl b/assets/voxygen/shaders/light-shadows-geom.glsl new file mode 100644 index 0000000000..78e0b0896e --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-geom.glsl @@ -0,0 +1,277 @@ +// Adapted from https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows + +// NOTE: We only technically need this for cube map arrays and geometry shader +// instancing. +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + +// Currently, we only need globals for the max light count (light_shadow_count.x) +// and the far plane (scene_res.z). +#include +#include +// // Currently, we only need lights for the light position +// #include + +/* struct Light { + vec4 light_pos; + vec4 light_col; + // mat4 light_proj; +}; + +layout (std140) +uniform u_lights { + Light lights[31]; +}; */ + +// Since our output primitive is a triangle strip, we have to render three vertices +// each. +#define VERTICES_PER_FACE 3 + +// Since we render our depth texture to a cube map, we need to render each face +// six times. If we used other shadow mapping methods with fewer outputs, this would +// shrink considerably. +#define FACES_PER_POINT_LIGHT 6 + +// If MAX_VERTEX_UNIFORM_COMPONENTS_ARB = 512 on many platforms, and we want a mat4 +// for each of 6 directions for each light, 20 is close to the maximum allowable +// size. We could add a final matrix for the directional light of the sun or moon +// to bring us to 126 matrices, which is just 2 off. +// +// To improve this limit, we could do many things, such as: +// - choose an implementation that isn't cube maps (e.g. tetrahedrons or curves; +// if there were an easy way to sample from tetrahedrons, we'd be at 32 * 4 = 128 +// exactly, leaving no room for a solar body, though). +// - Do more work in the geometry shader (e.g. just have a single projection +// matrix per light, and derive the different-facing components; or there may be +// other ways of greatly simplifying this). The tradeoff would be losing performance +// here. +// - Use ARB_instanced_arrays and switch lights with indexing, instead of a uniform +// buffer. This would probably work fine (and ARB_instanced_arrays is supported on +// pretty much every platform), but AFAIK it's possible that instanced arrays are +// slower than uniform arraay access on many platforms. +// - Don't try to do everything in one call (break this out into multiple passes). +// +// Actually, according to what I'm reading, MAX_GEOM_UNIFORM_COMPONENTS = 1024, and +// gl_MaxGeometryUniformComponents = 1024. +// +// Also, this only applies to uniforms defined *outside* of uniform blocks, of which +// there can be up to 12 (14 in OpenGL 4.3, which we definitely can't support). +// GL_MAX_UNIFORM_BLOCK_SIZE has a minimum of 16384, which *easily* exceeds our usage +// constraints. So this part might not matter. +// +// Other restrictions are easy to satisfy: +// +// gl_MaxGeometryVaryingComponents has a minimum of 64 and is the maximum number of +// varying components; I think this is the number of out components per vertex, which +// is technically 0, but would be 4 if we wrote FragPos. But it might also +// be the *total* number of varying components, in which case if we wrote FragPos +// it would be 4 * 20 * 6 * 3 = 1440, which would blow it out of the water. However, +// I kind of doubt this interpretation because writing FragPos for each of 18 vertices, +// as the original shader did, already yields 4 * 18 = 72, and it seems unlikely that +// the original example exceeds OpenGL limits. +// +// gl_MaxGeometryOutputComponents has a minimum of 128 and is the maximum number of +// components allowed in out variables; we easily fall under this since we actually +// have 0 of these. However, if we were to write FragPos for each vertex, it *might* +// cause us to exceed this limit, depending on whether it refers to the total output +// component count *including* varying components, or not. See the previous +// discussion; since 72 < 128 it's more plausible that this interpretation might be +// correct, but hopefully it's not. +// +// gl_MaxGeometryInputComponents has a minimum of 64 and we easily fall under that +// limit (I'm actually not sure we even have any user-defined input components?). +// +// gl_MaxGeometryTextureImageUnits = 16 and we have no texture image units (or maybe +// 1, the one we bound?). This might come into play if we were to have attached +// cubemaps instead of a single cubemap array, in which case it would limit us to +// 16 lights *regardless* of any of the fixes mentioned above (i.e., we'd just have +// to split up draw calls, I think). +// +// --- +// +// However, there is another limit to consider: GL_MAX_GEOMETRY_OUTPUT_VERTICES. Its +// minimum is 256, and 20 * 6 * 3 = 360, which exceeds that. This introduces a new +// limit of at most 14 point lights. +// +// Another, related limit is GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS. This counts +// every component output ("component" is usually a 4-byte field of a vector, but maybe +// this would improve with something like half-floats?), and has a minimum (as of +// OpenGL 3.3) of 1024. Since even builtin outputs gl_Layer count against this total, +// this means we issue 5 components per vertex, and 14 * 6 * 3 * 5 = 1260 > 1024. +// +// Ultimately, we find our maximum output limit of 11, ≤ 1024/5/3/6. +// +// If we choose to reserve a slot for a non-point light (and/or other uniforms), it +// is just 10, or half what we got from VERTICES_PER_FACE (we could also round down to +// 8 as a power of 2, if we had to). +// +// Unlike the input limits, whwich we can get around with "clever" solutions, it seems +// likely that the only real way to defeat the vertex limits is to use instancing of +// some sort (be it geometry shader or otherwise). This would restrict us to OpenGL +// 4.0 or above. +// +// A further consideration (were we to switch to OpenGL 4.1-supported features, but +// actually it is often supported on 3.3 hardware with ARB_viewport_array--whereas +// geometry shader instancing is *not* supported on any 3.3 hardware, so would actually +// require us to upgrade) would be setting gl_ViewportIndex. The main reason to consider +// this is that it allows specifying a separate scissor rectangle per viewport. This +// introduces two new constraints. Firstly, it adds an extra component to each vertex +// (lowering our maximum to 9 faces per light ≤ 1024/6/3/6, or 8 if we want to support a +// directional light). +// +// Secondly, a new constant (MAX_VIEWPORTS) is introduced, which would restrict the +// total number of active viewports; the minimum value for this is 16. While this may +// not seem all that relevant since our current hard limit is 11, the difference is that +// this limit would apply *across* instanced calls (since it may be a "global" +// restriction, tied to the OpenGL context; this means it couldn't even be a multiple +// frame buffer thing, as there is usually one per window). This would also tie in +// with gl_MaxGeometryTextureImageUnits, I guess. +// +// -- +// +// I just realized tht using cube map arrays at all bumps our required OpenGL +// version to 4.0, so let's just do instancing... +// +// The instancing limit on MAX_GEOMETRY_SHADER_INVOCATIONS has a minimum of 32, which +// would be sufficient to run through all 32 lights with a different cube map and +// completely removes any imits on ight count. +// +// This should instantly bring us below all relevant limits in all cases considered +// except for the two that would require 16. Unfortunately, 32 is also the *maximum* +// number of point lights, which is much higher than the usual value, and the instance +// count has to be a constant. If we were to instead geometry-shader-instance each +// *face*, we'd get a maximum light count of 56 ≤ 1024/6/3, which is not as elegant +// but is easily higher than 32. So, let's try using that instead. +// +// It is *possible* that using instancing on the *vertex* shader with the (dynamically +// uniform) total number of instances set to the actual number of point lights, would +// improve performance, since it would give us a 1:1 vertex input:output ratio, which +// might be optimized in hardware. +// +// It also seems plausible that constructing a separate geometry shader with values +// from 1 to 32 would be worthwhile, but that seems a little extreme. +// +// --- +// +// Since wgpu doesn't support geometry shaders anyway, it seems likely that we'll have +// to do the multiple draw calls, anyway... I don't think gl_Layer can be set from +// outside a geometry shader. But in wgpu, such a thing is much cheaper, anyway. +#define MAX_POINT_LIGHTS 31 + +// We use geometry shader instancing to construct each face separately. +#define MAX_LAYER_VERTICES_PER_FACE (MAX_POINT_LIGHTS * VERTICES_PER_FACE) + +#define MAX_LAYER_FACES (MAX_POINT_LIGHTS * FACES_PER_POINT_LIGHT) + +layout (triangles/*, invocations = 6*/) in; + +layout (triangle_strip, max_vertices = /*MAX_LAYER_VERTICES_PER_FACE*//*96*/18) out; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + +// NOTE: We choose not to output FragPos currently to save on space limitations +// (see extensive documentation above). However, as these limitations have been +// relaxed (unless the total of all our varying output components can't exceed +// 128, which would mean FragPos would sum to 4 * 3 * 32 = 384; this could be +// remedied only by setting MAX_POINT_LIGHTS to ), we might enable it again soon. +// +// out vec3 FragPos; // FragPos from GS (output per emitvertex) +// flat out int FragLayer; // Current layer + +// const vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); + +void main() { + // return; + // NOTE: Assuming that light_shadow_count.x < MAX_POINT_LIGHTS. We could min + // it, but that might make this less optimized, and I'd like to keep this loop as + // optimized as is reasonably possible. + // int face = gl_InvocationID; + + // Part 1: emit directed lights. + /* if (face <= light_shadow_count.z) { + // Directed light. + for(int i = 0; i < VERTICES_PER_FACE; ++i) // for each triangle vertex + { + // NOTE: See above, we don't make FragPos a uniform. + FragPos = gl_in[i].gl_Position; + FragLayer = 0; // 0 is the directed light layer. + // vec4 FragPos = gl_in[i].gl_Position; + gl_Layer = i; // built-in variable that specifies to which face we render. + gl_Position = shadowMats[i].shadowMatrices * FragPos; + EmitVertex(); + } + EndPrimitive(); + } */ + + // Part 2: emit point lights. + /* if (light_shadow_count.x == 1) { + return; + } */ +#if (SHADOW_MODE == SHADOW_MODE_MAP) + for (uint layer = 1u; layer <= min(light_shadow_count.x, 1u); ++layer) + { + int layer_base = int(layer) * FACES_PER_POINT_LIGHT; + // We use instancing here in order to increase the number of emitted vertices. + // int face = gl_InvocationID; + for(int face = 0; face < FACES_PER_POINT_LIGHT; ++face) + { + // int layer_face = layer * FACES_PER_POINT_LIGHT + face; + // int layer_face = layer * FACES_PER_POINT_LIGHT + face; + // for(int i = VERTICES_PER_FACE - 1; i >= 0; --i) // for each triangle vertex + for(int i = 0; i < VERTICES_PER_FACE; ++i) // for each triangle vertex + { + // NOTE: See above, we don't make FragPos a uniform. + vec3 fragPos = gl_in[i].gl_Position.xyz; + // FragPos = fragPos - (lights[((/*FragLayer*/layer - 1u) & 31u)].light_pos.xyz - focus_off.xyz); + // FragLayer = layer; + // float lightDistance = length(FragPos - lights[((layer - 1) & 31)].light_pos.xyz); + // lightDistance /= screen_res.w; + + // vec4 FragPos = gl_in[i].gl_Position; + // NOTE: Our normals map to the same thing as cube map normals, *except* that their normal direction is + // swapped; we can fix this by doing normal ^ 0x1u. However, we also want to cull back faces, not front + // faces, so we only care about the shadow cast by the *back* of the triangle, which means we ^ 0x1u + // again and cancel it out. + // int face = int(((floatBitsToUint(gl_Position.w) >> 29) & 0x7u) ^ 0x1u); + int layer_face = layer_base + face; + gl_Layer = face;//layer_face; // built-in variable that specifies to which face we render. + gl_Position = shadowMats[layer_face].shadowMatrices * vec4(fragPos, 1.0); + // gl_Position.z = -((gl_Position.z + screen_res.z) / (screen_res.w - screen_res.z)) * lightDistance; + // gl_Position.z = gl_Position.z / screen_res.w; + // gl_Position.z = gl_Position.z / gl_Position.w; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // lightDistance = -(lightDistance + screen_res.z) / (screen_res.w - screen_res.z); + // gl_Position.z = lightDistance; + EmitVertex(); + } + EndPrimitive(); + } + } +#endif +} diff --git a/assets/voxygen/shaders/light-shadows-vert.glsl b/assets/voxygen/shaders/light-shadows-vert.glsl new file mode 100644 index 0000000000..4ba3c77f9d --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-vert.glsl @@ -0,0 +1,55 @@ +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +// Currently, we only need globals for focus_off. +#include + +/* Accurate packed shadow maps for many lights at once! + * + * Ideally, we would just write to a bitmask... + * + * */ + +in uint v_pos_norm; +// in uint v_col_light; +// in vec4 v_pos; + +// Light projection matrices. +layout (std140) +uniform u_locals { + vec3 model_offs; + float load_time; + ivec4 atlas_offs; +}; + +// out vec4 shadowMapCoord; + +const int EXTRA_NEG_Z = 32768; + +void main() { + vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; + // f_pos = v_pos; + // vec3 f_pos = f_chunk_pos + model_offs; + + // gl_Position = v_pos + vec4(model_offs, 0.0); + gl_Position = /*all_mat * */vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); + // shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex; + // vec4(v_pos, 0.0, 1.0); +} diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl new file mode 100644 index 0000000000..8ae0f0ab2c --- /dev/null +++ b/assets/voxygen/shaders/lod-terrain-frag.glsl @@ -0,0 +1,632 @@ +#version 330 core + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +// #define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_VOXEL +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_LOD_FULL_INFO + +#include +#include +#include + +in vec3 f_pos; +in vec3 f_norm; +// in vec2 v_pos_orig; +// in vec4 f_shadow; +// in vec4 f_square; + +out vec4 tgt_color; + +/// const vec4 sun_pos = vec4(0); +// const vec4 light_pos[2] = vec4[](vec4(0), vec4(0)/*, vec3(00), vec3(0), vec3(0), vec3(0)*/); + +#include + +void main() { + // tgt_color = vec4(vec3(1.0), 1.0); + // return; + // vec3 f_pos = lod_pos(f_pos.xy); + // vec3 f_col = lod_col(f_pos.xy); + + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + + float my_alt = /*f_pos.z;*/alt_at_real(f_pos.xy); + // vec3 f_pos = vec3(f_pos.xy, max(my_alt, f_pos.z)); + /* gl_Position = + proj_mat * + view_mat * + vec4(f_pos, 1); + gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); */ + vec3 my_pos = vec3(f_pos.xy, my_alt); + vec3 my_norm = lod_norm(f_pos.xy/*, f_square*/); + + float which_norm = dot(my_norm, normalize(cam_pos.xyz - my_pos)); + // which_norm = 0.5 + which_norm * 0.5; + + // which_norm = pow(max(0.0, which_norm), /*0.03125*/1 / 8.0);// * 0.5; + // smoothstep + which_norm = which_norm * which_norm * (3 - 2 * abs(which_norm)); + + // which_norm = mix(0.0, 1.0, which_norm > 0.0); + // vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); + vec3 f_norm = mix(faceforward(f_norm, cam_pos.xyz - f_pos, -f_norm), my_norm, which_norm); + vec3 f_pos = mix(f_pos, my_pos, which_norm); + // vec3 fract_pos = fract(f_pos); + /* if (length(f_pos - cam_pos.xyz) <= view_distance.x + 32.0) { + vec4 new_f_pos; + float depth = 10000000.0; + vec4 old_coord = all_mat * vec4(f_pos.xyz, 1.0); + for (int i = 0; i < 6; i ++) { + // vec4 square = focus_pos.xy + vec4(splay(pos - vec2(1.0, 1.0), splay(pos + vec2(1.0, 1.0)))); + vec3 my_f_norm = normals[i]; + vec3 my_f_tan = normals[(i + 2) % 6]; + vec3 my_f_bitan = normals[(i + 4) % 6]; + mat4 foo = mat4(vec4(my_f_tan, 0), vec4(my_f_bitan, 0), vec4(my_f_norm, 0), vec4(0, 0, 0, 1)); + mat4 invfoo = foo * inverse(foo * all_mat); + vec4 my_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0); + vec4 my_f_proj = all_mat * my_f_pos; + if (my_f_proj.z <= depth) { + new_f_pos = my_f_pos; + f_norm = my_f_norm; + depth = my_f_proj.z; + } + } + // f_pos = new_f_pos.xyz; + } */ + + // Test for distance to all 6 sides of the enclosing cube. + // if (/*any(lessThan(fract(f_pos.xy), 0.01))*/fract_pos.x <= 0.1) { + // f_norm = faceforward(vec3(-1, 0, 0), f_norm, vec3(1, 0, 0)); + // f_tan = vec3(0, 1, 0); + // } else if (fract_pos.y <= 0.1) { + // f_norm = faceforward(vec3(0, -1, 0), f_norm, vec3(0, 1, 0)); + // f_tan = vec3(0, 0, 1); + // } else { + // f_norm = faceforward(vec3(0, 0, -1), f_norm, vec3(0, 0, 1)); + // f_tan = vec3(1, 0, 0); + // } + // vec3 f_bitan = cross(f_norm, f_tan); + + // mat4 foo = mat4(vec4(f_tan, 0), vec4(f_bitan, 0), vec4(f_norm, 0), vec4(0, 0, 0, 1)); + // mat4 invfoo = foo * inverse(foo * all_mat); + // vec3 old_coord = all_mat * vec4(f_pos.xyz, 1.0); + // vec4 new_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0); + vec3 f_col = lod_col(f_pos.xy); + // tgt_color = vec4(f_col, 1.0); + // return; + // vec3 f_col = srgb_to_linear(vec3(1.0)); + // vec3 f_norm = faceforward(f_norm, cam_pos.xyz - f_pos, -f_norm); + // vec3 f_up = faceforward(cam_pos.xyz - f_pos, vec3(0.0, 0.0, -1.0), cam_pos.xyz - f_pos); + // vec3 f_norm = faceforward(f_norm, /*vec3(cam_pos.xyz - f_pos.xyz)*/vec3(0.0, 0.0, -1.0), f_norm); + + // const vec3 normals[3] = vec3[](vec3(1,0,0), vec3(0,1,0), vec3(0,0,1));//, vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1)); + // const mat3 side_norms = vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1); + // mat3 sides = mat3( + // /*vec3(1, 0, 0), + // vec3(0, 1, 0), + // vec3(0, 0, 1)*/ + // vec3(1, 0, 0), + // // faceforward(vec3(1, 0, 0), -f_norm, vec3(1, 0, 0)), + // vec3(0, 1, 0), + // // faceforward(vec3(0, 1, 0), -f_norm, vec3(0, 1, 0)), + // vec3(0, 0, 1) + // // faceforward(vec3(0, 0, 1), -f_norm, vec3(0, 0, 1)) + // ); + + // This vector is shorthand for a diagonal matrix, which works because: + // (1) our voxel normal vectors are exactly the basis vectors in worldspace; + // (2) only 3 of them can be in the direction of the actual normal anyway. + // (NOTE: This normal should always be pointing up, so implicitly sides.z = 1.0). + // vec3 sides = sign(f_norm); + // // NOTE: Should really be sides * f_norm, i.e. abs(f_norm), but voxel_norm would then re-multiply by sides so it cancels out. + // vec3 cos_sides_i = sides * f_norm; + // vec3 cos_sides_o = sides * view_dir; + // // vec3 side_factor_i = cos_sides_i; + // // vec3 side_factor_i = f_norm; + // // vec3 side_factor_i = cos_sides_o; + // vec3 side_factor_i = 1.0 - pow(1.0 - 0.5 * cos_sides_i, vec3(5)); + // // vec3 side_factor_i = /*abs*/sign(f_norm) * cos_sides_i;//max(cos_sides_i, 0.0);// 1.0 - pow(1.0 - 0.5 * cos_sides_i, vec3(5.0)); // max(sides * f_norm, vec3(0.0));// + // // vec3 side_factor_i = /*abs*/sign(f_norm) * cos_sides_i;//max(cos_sides_i, 0.0);// 1.0 - pow(1.0 - 0.5 * cos_sides_i, vec3(5.0)); // max(sides * f_norm, vec3(0.0));// + // // vec3 side_factor_o = max(cos_sides_o, 0.0);// 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, 0.0), vec3(5)); + // vec3 side_factor_o = 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, 0.0), vec3(5)); + // // vec3 side_factor_o = max(cos_sides_o, 0.0);// 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, vec3(0.0)), vec3(5.0));//max(sides * view_dir/* * sign(cos_sides_i) */, vec3(0.0)); + // // vec3 side_factor_o = max(sides * view_dir/* * cos_sides_o*/, 0.0);// 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, vec3(0.0)), vec3(5.0));//max(sides * view_dir/* * sign(cos_sides_i) */, vec3(0.0)); + // // NOTE: side = transpose(sides), so we avoid the extra operatin. + // // We multply the vector by the matrix from the *left*, so each normal gets multiplied by the corresponding factor. + // // vec3 voxel_norm = normalize(/*sides * *//*sqrt(1.0 - cos_sides_i * cos_sides_i)*/(side_factor_i * side_factor_o)); + // vec3 voxel_norm = normalize(/*sides * *//*sqrt(1.0 - cos_sides_i * cos_sides_i)*/((28.0 / (23.0 * PI)) * side_factor_i * side_factor_o * sides)); + // vec3 voxel_norm = normalize(sign(f_norm) * sqrt(abs(f_norm)) * max(sign(f_norm) * view_dir, 0.0)); + float f_ao = 1.0;//1.0;//sqrt(dot(cos_sides_i, cos_sides_i) / 3.0); + // float f_ao = 0.2; + // sqrt(dot(sqrt(1.0 - cos_sides_i * cos_sides_i)), 1.0 - cos_sides_o/* * cos_sides_o*/);// length(sqrt(1.0 - cos_sides_o * cos_sides_o) / cos_sides_i * cos_sides_o); + // f_ao = f_ao * f_ao; + + // /* vec3 voxel_norm = vec3(0.0); + // for (int i = 0; i < 3; i ++) { + // // Light reflecting off the half-angle can shine on up to three sides. + // // So, the idea here is to figure out the ratio of visibility of each of these + // // three sides such that their sum adds to 1, then computing a Beckmann Distribution for each side times + // // the this ratio. + // // + // // The ratio of these normals in each direction should be the sum of their cosines with the light over π, + // // I think. + // // + // // cos (wh, theta) + // // + // // - one normal + // // + // // The ratio of each of the three exposed sides should just be the slope. + // vec3 side = normals[i]; + // side = faceforward(side, -f_norm, side); + // float cos_wi = max(dot(f_norm, side), 0.0); + // float cos_wo = max(dot(view_dir, side), 0.0); + // float share = cos_wi * cos_wo; + // // float share = (1.0 - pow5(1.0 - 0.5 * cos_wi)) * (1.0 - pow5(1.0 - 0.5 * cos_wo)); + // voxel_norm += share * side; + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // } + // voxel_norm = normalize(voxel_norm); */ + + float dist_lerp = 0.0;//clamp(pow(max(distance(focus_pos.xy, f_pos.xy) - view_distance.x, 0.0) / 4096.0, 2.0), 0, 1); + // dist_lerp = 0.0; + // voxel_norm = normalize(mix(voxel_norm, f_norm, /*pow(dist_lerp, 1.0)*/dist_lerp)); + + // IDEA: + // We can represent three faces as sign(voxel_norm). + vec3 sides = sign(f_norm); + // There are three relevant vectors: normal, tangent, and bitangent. + // We say normal is the z component, tangent the x component, bitangent the y. + // A blocking side is in the reverse direction of each. + // So -sides is the *direction* of the next block. + // Now, we want to multiply this by the *distance* to the nearest integer in that direction. + // If sides.x is -1, the direction is 1, so the distance is 1.0 - fract(f_pos.x) and the delta is 1.0 - fract(f_pos.x). + // If sides.x is 1, the direction is -1, so the distance is fract(f_pos.x) and the delta is -fract(f_pos.x) = 1.0 + fract(-f_pos.x). + // If sides.x is 0, the direction is 0, so the distance is 0.0 and the delta is 0.0 = 0.0 + fract(0.0 * f_pos.x). + // (we ignore f_pos < 0 for the time being). + // Then this is 1.0 + sides.x * fract(-sides.x * f_pos.x); + // We repeat this for y. + // + // We treat z as the dependent variable. + // IF voxel_norm.x > 0.0, z should increase by voxel_norm.z / voxel_norm.x * delta_sides.x in the x direction; + // IF voxel_norm.y > 0.0, z should increase by voxel_norm.z / voxel_norm.y * delta_sides.y in the y direction; + // IF voxel_norm.x = 0.0, z should not increase in the x direction; + // IF voxel_norm.y = 0.0, z should not increase in the y direction; + // we assume that ¬(voxel_norm.z = 0). + // + // Now observe that we can rephrase this as saying, given a desired change in z (to get to the next integer), how far must + // we travel along x and y? + // + // TODO: Handle negative numbers. + // vec3 delta_sides = mix(-fract(f_pos), 1.0 - fract(f_pos), lessThan(sides, vec3(0.0))); + vec3 delta_sides = mix(fract(f_pos) - 1.0, fract(f_pos), lessThan(sides, vec3(0.0))); + /* vec3 delta_sides = + mix( + mix(-fract(f_pos), 1.0 - fract(f_pos), lessThan(sides, vec3(0.0))), + mix(-(f_pos - ceil(f_pos)), 1.0 - (f_pos - ceil(f_pos)), lessThan(sides, vec3(0.0))), + lessThan(f_pos, vec3(0.0)) + ); */ + /* vec3 delta_sides = + mix( + mix(1.0 - fract(f_pos), -fract(f_pos), lessThan(sides, vec3(0.0))), + mix(1.0 - (f_pos - ceil(f_pos)), -(f_pos - ceil(f_pos)), lessThan(sides, vec3(0.0))), + lessThan(f_pos, vec3(0.0)) + ); */ + // vec3 delta_sides = mix(1.0 - fract(f_pos), -fract(f_pos), lessThan(sides, vec3(0.0))); + // vec3 delta_sides = 1.0 + sides * fract(-sides * f_pos); + // delta_sides = -sign(delta_sides) * (1.0 - abs(delta_sides)); + // Three faces: xy, xz, and yz. + // TODO: Handle zero slopes (for xz and yz). + vec2 corner_xy = min(abs(f_norm.xy / f_norm.z * delta_sides.z), 1.0); + vec2 corner_yz = min(abs(f_norm.yz / f_norm.x * delta_sides.x), 1.0); + vec2 corner_xz = min(abs(f_norm.xz / f_norm.y * delta_sides.y), 1.0); + // vec3 corner_delta = vec3(voxel_norm.xy / voxel_norm.z * delta_sides.z, delta_sides.z); + // Now we just compute an (upper bounded) distance to the corner in each direction. + // vec3 corner_distance = min(abs(corner_delta), 1.0); + // Now, if both sides hit something, lerp to 0.25. If one side hits something, lerp to 0.75. And if no sides hit something, + // lerp to 1.0 (TODO: incorporate the corner properly). + // Bilinear interpolation on each plane: + float ao_xy = dot(vec2(corner_xy.x, 1.0 - corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.25 : 0.5 : corner_xy.y < 1.00 ? 0.5 : 0.75, corner_xy.x < 1.00 ? 0.75 : 1.00), vec2(corner_xy.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(corner_xy.y, 1.0 - corner_xy.y)); + float ao_yz = dot(vec2(corner_yz.x, 1.0 - corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.25 : 0.5 : corner_yz.y < 1.00 ? 0.5 : 0.75, corner_yz.x < 1.00 ? 0.75 : 1.00), vec2(corner_yz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(corner_yz.y, 1.0 - corner_yz.y)); + float ao_xz = dot(vec2(corner_xz.x, 1.0 - corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.25 : 0.5 : corner_xz.y < 1.00 ? 0.5 : 0.75, corner_xz.x < 1.00 ? 0.75 : 1.00), vec2(corner_xz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(corner_xz.y, 1.0 - corner_xz.y)); + /* float ao_xy = dot(vec2(1.0 - corner_xy.x, corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.25 : 0.5 : corner_xy.y < 1.00 ? 0.5 : 0.75, corner_xy.x < 1.00 ? 0.75 : 1.00), vec2(corner_xy.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(1.0 - corner_xy.y, corner_xy.y)); + float ao_yz = dot(vec2(1.0 - corner_yz.x, corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.25 : 0.5 : corner_yz.y < 1.00 ? 0.5 : 0.75, corner_yz.x < 1.00 ? 0.75 : 1.00), vec2(corner_yz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(1.0 - corner_yz.y, corner_yz.y)); + float ao_xz = dot(vec2(1.0 - corner_xz.x, corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.25 : 0.5 : corner_xz.y < 1.00 ? 0.5 : 0.75, corner_xz.x < 1.00 ? 0.75 : 1.00), vec2(corner_xz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(1.0 - corner_xz.y, corner_xz.y)); */ + // Now, if both sides hit something, lerp to 0.0. If one side hits something, lerp to 0.4. And if no sides hit something, + // lerp to 1.0. + // Bilinear interpolation on each plane: + // float ao_xy = dot(vec2(1.0 - corner_xy.x, corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.0 : 0.25 : corner_xy.y < 1.00 ? 0.25 : 1.0, corner_xy.x < 1.00 ? 0.25 : 1.0), vec2(corner_xy.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_xy.y, corner_xy.y)); + // float ao_yz = dot(vec2(1.0 - corner_yz.x, corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.0 : 0.25 : corner_yz.y < 1.00 ? 0.25 : 1.0, corner_yz.x < 1.00 ? 0.25 : 1.0), vec2(corner_yz.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_yz.y, corner_yz.y)); + // float ao_xz = dot(vec2(1.0 - corner_xz.x, corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.0 : 0.25 : corner_xz.y < 1.00 ? 0.25 : 1.0, corner_xz.x < 1.00 ? 0.25 : 1.0), vec2(corner_xz.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_xz.y, corner_xz.y)); + // Now, multiply each component by the face "share" which is just the absolute value of its normal for that plane... + // vec3 f_ao_vec = mix(abs(vec3(ao_yz, ao_xz, ao_xy)), vec3(1.0), bvec3(f_norm.yz == vec2(0.0), f_norm.xz == vec2(0.0), f_norm.xy == vec2(0.0))); + // vec3 f_ao_vec = mix(abs(vec3(ao_yz, ao_xz, ao_xy)), vec3(1.0), bvec3(length(f_norm.yz) <= 0.0, length(f_norm.xz) <= 0.0, length(f_norm.xy) <= 0.0)); + // vec3 f_ao_vec = mix(abs(vec3(ao_yz, ao_xz, ao_xy)), vec3(1.0), bvec3(abs(f_norm.x) <= 0.0, abs(f_norm.y) <= 0.0, abs(f_norm.z) <= 0.0)); + vec3 f_ao_vec = mix(/*abs(voxel_norm)*/vec3(1.0, 1.0, 1.0), /*abs(voxel_norm) * */vec3(ao_yz, ao_xz, ao_xy), /*abs(voxel_norm)*/vec3(length(f_norm.yz), length(f_norm.xz), length(f_norm.xy))/*vec3(1.0)*//*sign(max(view_dir * sides, 0.0))*/); + float f_orig_len = length(cam_pos.xyz - f_pos); + vec3 f_orig_view_dir = normalize(cam_pos.xyz - f_pos); + // f_ao_vec *= sign(max(f_orig_view_dir * sides, 0.0)); + // Projecting view onto face: + // bool IntersectRayPlane(vec3 rayOrigin, vec3 rayDirection, vec3 posOnPlane, vec3 planeNormal, inout vec3 intersectionPoint) + // { + // float rDotn = dot(rayDirection, planeNormal); + // + // //parallel to plane or pointing away from plane? + // if (rDotn < 0.0000001 ) + // return false; + // + // float s = dot(planeNormal, (posOnPlane - rayOrigin)) / rDotn; + // + // intersectionPoint = rayOrigin + s * rayDirection; + // + // return true; + // } + + bvec3 hit_yz_xz_xy; + vec3 dist_yz_xz_xy; + /* { + // vec3 rDotn = -f_orig_view_dir * -sides; + // vec3 rDotn = f_orig_view_dir * sides; + // hit_yz_xz_xy = greaterThanEqual(rDotn, vec3(0.000001)); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / rDotn; + // dist_yz_xz_xy = abs(s * -f_orig_view_dir); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / (-f_orig_view_dir * -sides); + // vec3 s = (f_pos + delta_sides - cam_pos.xyz) / -f_orig_view_dir; + // dist_yz_xz_xy = abs(s); + hit_yz_xz_xy = greaterThanEqual(f_orig_view_dir * sides, vec3(0.000001)); + dist_yz_xz_xy = abs((f_pos + delta_sides - cam_pos.xyz) / -f_orig_view_dir); + } */ + { + // vec3 rDotn = -f_orig_view_dir * -sides; + // vec3 rDotn = f_orig_view_dir * sides; + // hit_yz_xz_xy = greaterThanEqual(rDotn, vec3(0.000001)); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / rDotn; + // dist_yz_xz_xy = abs(s * -f_orig_view_dir); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / (-f_orig_view_dir * -sides); + // vec3 s = (f_pos + delta_sides - cam_pos.xyz) / -f_orig_view_dir; + // dist_yz_xz_xy = abs(s); + hit_yz_xz_xy = greaterThanEqual(f_orig_view_dir * sides, vec3(0.000001)); + dist_yz_xz_xy = abs((f_pos + delta_sides - cam_pos.xyz) / f_orig_view_dir); + } + + // vec3 xy_point = f_pos, xz_point = f_pos, yz_point = f_pos; + // bool hit_xy = (/*ao_yz < 1.0 || ao_xz < 1.0*//*min(f_ao_vec.x, f_ao_vec.y)*//*f_ao_vec.z < 1.0*/true/*min(corner_xz.y, corner_yz.y) < 1.0*//*min(corner_xy.x, corner_xy.y) < 1.0*/) && IntersectRayPlane(cam_pos.xyz, -f_orig_view_dir, vec3(f_pos.x, f_pos.y, f_pos.z + delta_sides.z/* - sides.z*/), vec3(0.0, 0.0, -sides.z), xy_point); + // bool hit_xz = (/*ao_xy < 1.0 || ao_yz < 1.0*//*min(f_ao_vec.x, f_ao_vec.z)*//*f_ao_vec.y < 1.0*/true/*min(corner_xy.y, corner_yz.x) < 1.0*//*min(corner_xz.x, corner_xz.y) < 1.0*/) && IntersectRayPlane(cam_pos.xyz, -f_orig_view_dir, vec3(f_pos.x, f_pos.y + delta_sides.y/* - sides.y*/, f_pos.z), vec3(0.0, -sides.y, 0.0), xz_point); + // bool hit_yz = (/*ao_xy < 1.0 || ao_xz < 1.0*//*min(f_ao_vec.y, f_ao_vec.z) < 1.0*//*f_ao_vec.x < 1.0*/true/*true*//*min(corner_xy.x, corner_xz.x) < 1.0*//*min(corner_yz.x, corner_yz.y) < 1.0*/) && IntersectRayPlane(cam_pos.xyz, -f_orig_view_dir, vec3(f_pos.x + delta_sides.x/* - sides.x*/, f_pos.y, f_pos.z), vec3(-sides.x, 0.0, 0.0), yz_point); + // float xy_dist = distance(cam_pos.xyz, xy_point), xz_dist = distance(cam_pos.xyz, xz_point), yz_dist = distance(cam_pos.xyz, yz_point); + bool hit_xy = hit_yz_xz_xy.z, hit_xz = hit_yz_xz_xy.y, hit_yz = hit_yz_xz_xy.x; + float xy_dist = dist_yz_xz_xy.z, xz_dist = dist_yz_xz_xy.y, yz_dist = dist_yz_xz_xy.x; + // hit_xy = hit_xy && distance(f_pos.xy + delta_sides.xy, xy_point.xy) <= 1.0; + // hit_xz = hit_xz && distance(f_pos.xz + delta_sides.xz, xz_point.xz) <= 1.0; + // hit_yz = hit_yz && distance(f_pos.yz + delta_sides.yz, yz_point.yz) <= 1.0; + vec3 voxel_norm = + hit_yz ? + hit_xz ? + yz_dist < xz_dist ? + hit_xy ? yz_dist < xy_dist ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + hit_xy ? xz_dist < xy_dist ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + hit_xy ? yz_dist < xy_dist ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + hit_xz ? + hit_xy ? xz_dist < xy_dist ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + hit_xy ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); + // vec3 f_ao_view = max(vec3(dot(f_orig_view_dir.yz, sides.yz), dot(f_orig_view_dir.xz, sides.xz), dot(f_orig_view_dir.xy, sides.xy)), 0.0); + // delta_sides *= sqrt(1.0 - f_ao_view * f_ao_view); + // delta_sides *= 1.0 - mix(view_dir / f_ao_view, vec3(0.0), equal(f_ao_view, vec3(0.0)));// sqrt(1.0 - f_ao_view * f_ao_view); + // delta_sides *= 1.0 - /*sign*/(max(vec3(dot(f_orig_view_dir.yz, sides.yz), dot(f_orig_view_dir.xz, sides.xz), dot(f_orig_view_dir.xy, sides.xy)), 0.0)); + // f_ao = length(f_ao_vec); + // f_ao = dot(f_ao_vec, vec3(1.0)) / 3.0; + // f_ao = 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = pow(f_ao_vec.x * f_ao_vec.y * f_ao_vec.z * 3.0, 1.0 / 2.0); // 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = pow(f_ao_vec.x * f_ao_vec.y * f_ao_vec.z, 1.0 / 3.0); // 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = f_ao_vec.x * f_ao_vec.y * f_ao_vec.z + (1.0 - f_ao_vec.x) * (1.0 - f_ao_vec.y) * (1.0 - f_ao_vec.z); + // f_ao = sqrt((f_ao_vec.x + f_ao_vec.y + f_ao_vec.z) / 3.0); // 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = sqrt(dot(f_ao_vec, abs(voxel_norm))); + // f_ao = 3.0 / (1.0 / f_ao_vec.x + 1.0 / f_ao_vec.y + 1.0 / f_ao_vec.z); + // f_ao = min(ao_yz, min(ao_xz, ao_xy)); + // f_ao = max(f_ao_vec.x, max(f_ao_vec.y, f_ao_vec.z)); + // f_ao = min(f_ao_vec.x, min(f_ao_vec.y, f_ao_vec.z)); + // f_ao = sqrt(dot(f_ao_vec * abs(voxel_norm), sqrt(1.0 - delta_sides * delta_sides)) / 3.0); + // f_ao = dot(f_ao_vec, sqrt(1.0 - delta_sides * delta_sides)); + // f_ao = dot(f_ao_vec, 1.0 - abs(delta_sides)); + // f_ao = + // f_ao_vec.x < 1.0 ? + // f_ao_vec.y < 1.0 ? + // abs(delta_sides.x) < abs(delta_sides.y) ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? f_ao_vec.x : f_ao_vec.z : f_ao_vec.x : + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? f_ao_vec.y : f_ao_vec.z : f_ao_vec.y : + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? f_ao_vec.x : f_ao_vec.z : f_ao_vec.x : + // f_ao_vec.y < 1.0 ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? f_ao_vec.y : f_ao_vec.z : f_ao_vec.y : + // f_ao_vec.z; + // f_ao = abs(delta_sides.x) < abs(delta_sides.y) ? abs(delta_sides.x) < abs(delta_sides.z) ? f_ao_vec.x : f_ao_vec.z : + // abs(delta_sides.y) < abs(delta_sides.z) ? f_ao_vec.y : f_ao_vec.z; + // f_ao = abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.y) * f_ao_vec.y ? abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.z) * f_ao_vec.z ? f_ao_vec.x : f_ao_vec.z : + // abs(delta_sides.y) * f_ao_vec.y < abs(delta_sides.z) * f_ao_vec.z ? f_ao_vec.y : f_ao_vec.z; + // f_ao = dot(abs(voxel_norm), abs(voxel_norm) * f_ao_vec)/* / 3.0*/; + // f_ao = sqrt(dot(abs(voxel_norm), f_ao_vec) / 3.0); + // f_ao = /*abs(sides)*/max(sign(1.0 + f_orig_view_dir * sides), 0.0) * f_ao); + // f_ao = mix(f_ao, 1.0, dist_lerp); + + // vec3 voxel_norm = f_norm; + // vec3 voxel_norm = + // f_ao_vec.x < 1.0 ? + // f_ao_vec.y < 1.0 ? + // abs(delta_sides.x) < abs(delta_sides.y) ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // f_ao_vec.y < 1.0 ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // f_ao_vec.z < 1.0 ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); + + // vec3 voxel_norm = + // /*f_ao_vec.x < 1.0*/true ? + // /*f_ao_vec.y < 1.0*/true ? + // abs(delta_sides.x) < abs(delta_sides.y) ? + // /*f_ao_vec.z < 1.0 */true ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // /*f_ao_vec.z < 1.0*/true ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // /*f_ao_vec.z < 1.0*/true ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // /*f_ao_vec.y < 1.0*/true ? + // /*f_ao_vec.z < 1.0*/true ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // vec3(0.0, 0.0, sides.z); + /* vec3 voxel_norm = + f_ao_vec.x < 1.0 ? + f_ao_vec.y < 1.0 ? + abs(delta_sides.x) < abs(delta_sides.y) ? + f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + f_ao_vec.y < 1.0 ? + f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + f_ao_vec.z < 1.0 ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); */ + // vec3 voxel_norm = + // f_ao_vec.x < 1.0 ? + // f_ao_vec.y < 1.0 ? + // abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.y) * f_ao_vec.y ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.z) * f_ao_vec.z ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) * f_ao_vec.y < abs(delta_sides.z) * f_ao_vec.z ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.z) * f_ao_vec.z ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // f_ao_vec.y < 1.0 ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) * f_ao_vec.y < abs(delta_sides.z) * f_ao_vec.z ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // f_ao_vec.z < 1.0 ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); + // vec3 voxel_norm = vec3(0.0); + // voxel_norm = mix(voxel_norm, f_norm, dist_lerp); + + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ + // voxel_norm = vec3(0.0); + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z); + // float shadow_alt = f_pos.z; +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float shadow_alt = f_pos.z; +#endif + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); + // float sun_shade_frac = 1.0; +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir); + + // Magic stop-gap code without any physical justification. + vec3 lerpy_norm; + if (my_norm.z/*f_norm.z*/ > 0.99999) { + lerpy_norm = vec3(0, 0, 1); + } else { + vec3 side_norm = normalize(vec3(my_norm.xy, 0)); + // lerpy_norm = f_norm; + float mix_factor = clamp(abs(dot(f_orig_view_dir, side_norm)), 0, 1); + lerpy_norm = mix( + mix(my_norm, side_norm, clamp(dot(side_norm, my_norm) + 0.5, 0, 1)), + my_norm, + mix_factor + ); + } + const float DIST = 0.07; + voxel_norm = normalize(mix(voxel_norm, lerpy_norm, clamp(my_norm.z * my_norm.z - (1.0 - DIST), 0, 1) / DIST)); + + f_pos.xyz += abs(voxel_norm) * delta_sides; + voxel_norm = voxel_norm == vec3(0.0) ? f_norm : voxel_norm; + + vec3 hash_pos = f_pos + focus_off.xyz; + const float A = 0.055; + const float W_INV = 1 / (1 + A); + const float W_2 = W_INV * W_INV;//pow(W_INV, 2.4); + const float NOISE_FACTOR = 0.02;//pow(0.02, 1.2); + float noise = hash(vec4(floor(hash_pos * 3.0 - voxel_norm * 0.5), 0));//0.005/* - 0.01*/; + vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR); + // noise_delta = noise_delta * noise_delta * W_2 - f_col; + // lum = W ⋅ col + // lum + noise = W ⋅ (col + delta) + // W ⋅ col + noise = W ⋅ col + W ⋅ delta + // noise = W ⋅ delta + // delta = noise / W + // vec3 col = (f_col + noise_delta); + // vec3 col = noise_delta * noise_delta * W_2; + + f_col = noise_delta * noise_delta * W_2; + // f_col = /*srgb_to_linear*/(f_col + hash(vec4(floor(hash_pos * 3.0 - voxel_norm * 0.5), 0)) * 0.01/* - 0.01*/); // Small-scale noise + + // f_ao = 1.0; + // f_ao = dot(f_ao_vec, sqrt(1.0 - delta_sides * delta_sides)); + + f_ao = dot(f_ao_vec, abs(voxel_norm)); + // f_ao = sqrt(dot(f_ao_vec * abs(voxel_norm), sqrt(1.0 - delta_sides * delta_sides)) / 3.0); + + // vec3 ao_pos2 = min(fract(f_pos), 1.0 - fract(f_pos)); + // f_ao = sqrt(dot(ao_pos2, ao_pos2)); + // // f_ao = dot(abs(voxel_norm), f_ao_vec); + // // voxel_norm = f_norm; + + // Note: because voxels, we reduce the normal for reflections to just its z component, dpendng on distance to camera. + // Idea: the closer we are to facing top-down, the more the norm should tend towards up-z. + // vec3 l_norm; // = vec3(0.0, 0.0, 1.0); + // vec3 l_norm = normalize(vec3(f_norm.x / max(abs(f_norm.x), 0.001), f_norm.y / max(abs(f_norm.y), 0.001), f_norm.z / max(abs(f_norm.z), 0.001))); + // vec3 l_factor = 1.0 / (1.0 + max(abs(/*f_pos - cam_pos.xyz*//*-vec3(vert_pos4) / vert_pos4.w*/vec3(f_pos.xy, 0.0) - vec3(/*cam_pos*/focus_pos.xy, cam_to_frag)) - vec3(view_distance.x, view_distance.x, 0.0), 0.0) / vec3(32.0 * 2.0, 32.0 * 2.0, 1.0)); + // l_factor.z = + // vec4 focus_pos4 = view_mat * vec4(focus_pos.xyz, 1.0); + // vec3 focus_dir = normalize(-vec3(focus_pos4) / focus_pos4.w); + + // float l_factor = 1.0 - pow(clamp(0.5 + 0.5 * dot(/*-view_dir*/-cam_to_frag, l_norm), 0.0, 1.0), 2.0);//1.0 / (1.0 + 0.5 * pow(max(distance(/*focus_pos.xy*/vec3(focus_pos.xy, /*vert_pos4.z / vert_pos4.w*/f_pos.z), vec3(f_pos.xy, f_pos.z))/* - view_distance.x*/ - 32.0, 0.0) / (32.0 * 1.0), /*0.5*/1.0)); + // l_factor = 1.0; + // l_norm = normalize(mix(l_norm, f_norm, l_factor)); + // l_norm = f_norm; + + /* l_norm = normalize(vec3( + mix(l_norm.x, f_norm.x, clamp(pow(f_norm.x * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.y * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.z * 0.5, 64), 0, 1)) + )); */ + // f_norm = mix(l_norm, f_norm, min(1.0 / max(cam_to_frag, 0.001), 1.0)); + /* vec3 l_norm = normalize(vec3( + mix(-1.0, 1.0, clamp(pow(f_norm.x * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.y * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.z * 0.5, 64), 0, 1)) + )); */ + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + vec3 view_dir = -cam_to_frag; + // vec3 view_dir = normalize(f_pos - cam_pos.xyz); + + + // vec3 sun_dir = get_sun_dir(time_of_day.x); + // vec3 moon_dir = get_moon_dir(time_of_day.x); + // // float sun_light = get_sun_brightness(sun_dir); + // // float moon_light = get_moon_brightness(moon_dir); + // // float my_alt = f_pos.z;//alt_at_real(f_pos.xy); + // // vec3 f_norm = my_norm; + // // vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + // // float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z); + // // float my_alt = alt_at(f_pos.xy); + // float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); + // float moon_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir); + // // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); + // // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir); + // // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). + // // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5); + // // NOTE: current assumption is that moon and sun shouldn't be out at the sae time. + // // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow + // // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally). + // // float shade_frac = sun_shade_frac + moon_shade_frac; + // // float brightness_denominator = (ambient_sides + vec3(SUN_AMBIANCE * sun_light + moon_light); + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, moon_shade_frac/*, light_pos*/); + + float alpha = 1.0;//0.1;//0.2;///1.0;//sqrt(2.0); + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float cam_alt = alt_at(cam_pos.xy); + float fluid_alt = medium.x == 1u ? max(cam_alt + 1, floor(shadow_alt)) : view_distance.w; + float R_s = (f_pos.z < my_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 emitted_light, reflected_light; + + vec3 mu = medium.x == 1u/* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0); + // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. + vec3 cam_attenuation = compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos); + // Use f_norm here for better shadows. + // vec3 light_frac = light_reflection_factor(f_norm/*l_norm*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(/*1.0*/R_s), alpha); + + // vec3 light, diffuse_light, ambient_light; + // get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, (0.25 * shade_frac + 0.25 * light_frac) * f_col, 0.5 * shade_frac * f_col, 0.5 * shade_frac * /*vec3(1.0)*/f_col, 2.0, emitted_light, reflected_light); + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, voxel_norm/*l_norm*/, view_dir, f_pos, vec3(0.0), cam_attenuation, fluid_alt, vec3(1.0)/* * (0.5 * light_frac + vec3(0.5 * shade_frac))*/, vec3(1.0), /*0.5 * shade_frac * *//*vec3(1.0)*//*f_col*/vec3(R_s), alpha, voxel_norm, dist_lerp/*max(distance(focus_pos.xy, f_pos.xyz) - view_distance.x, 0.0) / 1000 < 1.0*/, emitted_light, reflected_light); + // emitted_light = vec3(1.0); + // emitted_light *= max(shade_frac, MIN_SHADOW); + // reflected_light *= shade_frac; + // max_light *= shade_frac; + // reflected_light = vec3(0.0); + + // dot(diffuse_factor, /*R_r * */vec4(abs(norm) * (1.0 - dist), dist)) + + // corner_xy = mix(all(lessThan(corner_xy, 1.0)) ? vec2(0.0) : 0.4 * (), 1.0 + // + // TODO: Handle similar logic for z. + + // So we repeat this for all three sides to find the "next" position on each side. + // vec3 delta_sides = 1.0 + sides * fract(-sides * f_pos); + // Now, we + // Now, all we have to do is find out whether (again, assuming f_pos is positive) next_sides represents a new integer. + // We currently just treat this as "new floor != old floor". + + // So to find the position at the nearest voxel, we just subtract voxel_norm * fract(sides * ) from f_pos.z. + // Then to find out whether we meet a new "block" in 1 voxel, we just + // on the "other" side can be found (according to my temporary theory) as the cross product + // vec3 norm = normalize(cross( + // vec3(/*2.0 * SAMPLE_W*/square.z - square.x, 0.0, altx1 - altx0), + // vec3(0.0, /*2.0 * SAMPLE_W*/square.w - square.y, alty1 - alty0) + // )); + // vec3 norm = normalize(vec3( + // (altx0 - altx1) / (square.z - square.x), + // (alty0 - alty1) / (square.w - square.y), + // 1.0 + // //(abs(square.w - square.y) + abs(square.z - square.x)) / (slope + 0.00001) // Avoid NaN + // )); + // + // If a side coordinate is 0, then it counts as no AO; + // otherwise, it counts as fractional AO. So what we need is to know whether the fractional AO to the next block in that direction pushes us to a new integer. + // + // vec3 ao_pos_z = floor(f_pos + f_norm); + // vec3 ao_pos_z = corner_distance; + // vec3 ao_pos = 0.5 - clamp(min(fract(abs(f_pos)), 1.0 - fract(abs(f_pos))), 0.0, 0.5); + // + // f_ao = /*sqrt*/1.0 - 2.0 * sqrt(dot(ao_pos, ao_pos) / 2.0); + // f_ao = /*sqrt*/1.0 - (dot(ao_pos, ao_pos)/* / 2.0*/); + // f_ao = /*sqrt*/1.0 - 2.0 * (dot(ao_pos, ao_pos)/* / 2.0*/); + // f_ao = /*sqrt*/1.0 - 2.0 * sqrt(dot(ao_pos, ao_pos) / 2.0); + float ao = f_ao;// /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1; + emitted_light *= ao; + reflected_light *= ao; + + // emitted_light += 0.5 * vec3(SUN_AMBIANCE * sun_shade_frac * sun_light + moon_shade_frac * moon_light) * f_col * (ambient_sides + 1.0); + + // Ambient lighting attempt: vertical light. + // reflected_light += /*0.0125*/0.15 * 0.25 * _col * light_reflection_factor(f_norm, cam_to_frag, vec3(0, 0, -1.0), 0.5 * f_col, 0.5 * f_col, 2.0); + // emitted_light += /*0.0125*/0.25 * f_col * ; + // vec3 light, diffuse_light, ambient_light; + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + // f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise + vec3 surf_color = /*illuminate(emitted_light, reflected_light)*/illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light); + + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + vec4 clouds; + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 1.0, /*true*/false, clouds); + vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif + + // float mist_factor = max(1 - (f_pos.z + (texture(t_noise, f_pos.xy * 0.0005 + time_of_day.x * 0.0003).x - 0.5) * 128.0) / 400.0, 0.0); + // //float mist_factor = f_norm.z * 2.0; + // color = mix(color, vec3(1.0) * /*diffuse_light*/reflected_light, clamp(mist_factor * 0.00005 * distance(f_pos.xy, focus_pos.xy), 0, 0.3)); + // color = surf_color; + + tgt_color = vec4(color, 1.0); +} diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl new file mode 100644 index 0000000000..6ef2dd7bdf --- /dev/null +++ b/assets/voxygen/shaders/lod-terrain-vert.glsl @@ -0,0 +1,104 @@ +#version 330 core + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_VOXEL + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#include +#include +#include + +in vec2 v_pos; + +layout (std140) +uniform u_locals { + vec4 nul; +}; + +out vec3 f_pos; +out vec3 f_norm; +// out vec2 v_pos_orig; +// out vec4 f_square; +// out vec4 f_shadow; +// out float f_light; + +void main() { + // Find distances between vertices. + f_pos = lod_pos(v_pos, focus_pos.xy); + vec2 dims = vec2(1.0 / view_distance.y); + vec4 f_square = focus_pos.xyxy + vec4(splay(v_pos - dims), splay(v_pos + dims)); + f_norm = lod_norm(f_pos.xy, f_square); + // v_pos_orig = v_pos; + + // f_pos = lod_pos(focus_pos.xy + splay(v_pos) * /*1000000.0*/(1 << 20), square); + + // f_norm = lod_norm(f_pos.xy); + + // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + + f_pos.z -= 1.0 / pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0); + + // f_pos.z -= 100.0 * pow(1.0 + 0.01 / view_distance.x, -pow(distance(focus_pos.xy, f_pos.xy), 2.0)); + // f_pos.z = mix(-f_pos.z, f_pos.z, view_distance.x <= distance(focus_pos.xy, f_pos.xy) + 32.0); + + // bool faces_fluid = false;// bool((f_pos_norm >> 28) & 0x1u); + // // TODO: Measure real water surface altitude here. + // float surfaceAlt = mix(view_distance.z, /*floor*/(min(f_pos.z, floor(alt_at_real(cam_pos.xy)))), medium.x); + // // float surfaceAlt = mix(view_distance.z, floor(max(cam_pos.z, alt_at_real(cam_pos.xy))), medium.x); + // // float surfaceAlt = min(floor(f_pos.z), floor(alt_at_real(cam_pos.xy))); // faces_fluid ? max(ceil(f_pos.z), floor(f_alt)) : floor(f_alt); + + // f_pos.z -= max(sign(view_distance.x - distance(focus_pos.xy, f_pos.xy)), 0.0) * (32.0 * view_distance.z / 255 + 32.0 * max(0.0, f_pos.z - cam_pos.z)); + // f_pos.z -= 0.1 + max(view_distance.x - distance(focus_pos.xy, f_pos.xy), 0.0) * (1.0 + max(1.0, ceil(f_pos.z - focus_pos.z))); + + // vec3 wRayinitial = f_pos; // cam_pos.z < f_pos.z ? f_pos : cam_pos.xyz; + // vec3 wRayfinal = cam_pos.xyz; // cam_pos.z < f_pos.z ? cam_pos.xyz : f_pos; + // wRayfinal = dot(wRayfinal - wRayinitial, focus_pos.xyz - cam_pos.xyz) < 0.0 ? wRayfinal : wRayinitial; + // vec3 wRayNormal = /*surfaceAlt < wRayinitial.z ? vec3(0.0, 0.0, -1.0) : */vec3(0.0, 0.0, 1.0); + // float n_camera = mix(1.0, 1.3325, medium.x); + // float n_vertex = faces_fluid ? 1.3325 : 1.0; + // float n1 = n_vertex; // cam_pos.z < f_pos.z ? n_vertex : n_camera; + // float n2 = n_camera; // cam_pos.z < f_pos.z ? n_camera : n_vertex; + + // float wRayLength0 = length(wRayfinal - wRayinitial); + // vec3 wRayDir = (wRayfinal - wRayinitial) / wRayLength0; + // vec3 wPoint = wRayfinal; + // bool wIntersectsSurface = IntersectRayPlane(wRayinitial, wRayDir, vec3(0.0, 0.0, surfaceAlt), -wRayNormal, wPoint); + // float wRayLength = length(wPoint - wRayinitial); + // wPoint = wRayLength < wRayLength0 ? wPoint : wRayfinal; + // wRayLength = min(wRayLength, wRayLength0); // min(max_length, dot(wRayfinal - wpos, defaultpos - wpos)); + + // // vec3 wRayDir2 = (wRayfinal - wRayinitial) / wRayLength; + + // vec3 wRayDir3 = (dot(wRayDir, wRayNormal) < 0.0 && surfaceAlt < wRayinitial.z && wIntersectsSurface/* && medium.x == 1u*/) ? refract(wRayDir, wRayNormal, n2 / n1) : wRayDir; + // // wPoint -= wRayDir3 * wRayLength * n2 / n1; + + // vec3 newRay = (dot(wRayDir3, focus_pos.xyz - cam_pos.xyz) < 0.0 && /*dot(wRayDir, wRayNormal) > 0.0 && *//*surfaceAlt < wRayinitial.z && */wIntersectsSurface && medium.x == 1u) ? wPoint - wRayDir3 * wRayLength * n2 / n1/*wPoint - wRayDir3 * wRayLength * n2 / n1*/ : f_pos;// - (wRayfinal - wPoint) * n2 / n1; // wPoint + n2 * (wRayfinal - wPoint) - n2 / n1 * wRayLength * wRayDir3; + + // newRay.z -= max(view_distance.x - distance(focus_pos.xy, f_pos.xy), 0.0) * (1.0 + max(0.0, f_pos.z - focus_pos.z)); + + + // f_light = 1.0; + + gl_Position = + /* proj_mat * + view_mat * */ + all_mat * + vec4(f_pos/*newRay*/, 1); + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z * gl_Position.w; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); +} diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl index ebbc8e1786..4dfbd6dbca 100644 --- a/assets/voxygen/shaders/particle-frag.glsl +++ b/assets/voxygen/shaders/particle-frag.glsl @@ -1,38 +1,86 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #include in vec3 f_pos; flat in vec3 f_norm; in vec3 f_col; -in float f_ao; -in float f_light; out vec4 tgt_color; #include #include +#include const float FADE_DIST = 32.0; void main() { - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - float point_shadow = shadow_at(f_pos, f_norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; - float ao = pow(f_ao, 0.5) * 0.85 + 0.15; - ambient_light *= ao; - diffuse_light *= ao; - vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + vec3 view_dir = -cam_to_frag; +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float f_alt = alt_at(f_pos.xy); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0; +#endif + float moon_shade_frac = 1.0; + + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac); + + vec3 surf_color = f_col; + float alpha = 1.0; + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // To account for prior saturation. + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + + max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + + surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light); + +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag, time_of_day.x, cam_pos.xyz, f_pos, 0.5, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif tgt_color = vec4(color, 0.3); } diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 14a597d429..082fc4e453 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -1,11 +1,23 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include #include #include in vec3 v_pos; -in uint v_col; +// in uint v_col; in uint v_norm_ao; in vec3 inst_pos; in float inst_time; @@ -163,7 +175,7 @@ void main() { ); } - f_pos = inst_pos + (v_pos * attr.scale * SCALE + attr.offs); + f_pos = (inst_pos - focus_off.xyz) + (v_pos * attr.scale * SCALE + attr.offs); // First 3 normals are negative, next 3 are positive vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); @@ -175,12 +187,8 @@ void main() { f_col = //srgb_to_linear(col) * srgb_to_linear(attr.col); - f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; - - f_light = 1.0; gl_Position = all_mat * vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/player-shadow-frag.glsl b/assets/voxygen/shaders/player-shadow-frag.glsl index 1aba8bc4fa..693bc30409 100644 --- a/assets/voxygen/shaders/player-shadow-frag.glsl +++ b/assets/voxygen/shaders/player-shadow-frag.glsl @@ -1,20 +1,38 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include in vec3 f_pos; in vec3 f_col; flat in vec3 f_norm; +in float f_ao; +// in float f_alt; +// in vec4 f_shadow; layout (std140) uniform u_locals { mat4 model_mat; vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; int flags; }; struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -29,17 +47,17 @@ uniform u_bones { out vec4 tgt_color; void main() { - float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; + // float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; - float opacity = clamp(distance / distance_divider, 0, 1); + // float opacity = clamp(distance / distance_divider, 0, 1); - if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { - discard; - } + // if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { + // discard; + // } - if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > shadow_dithering) { - discard; - } + // if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > shadow_dithering) { + // discard; + // } tgt_color = vec4(0.0,0.0,0.0, 1.0); } diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index 1a3c12cbbe..cb179bfec3 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -1,8 +1,25 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include // Note: The sampler uniform is declared here because it differs for MSAA #include +#include in vec2 f_pos; @@ -29,15 +46,138 @@ vec3 hsv2rgb(vec3 c) { return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } +vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted, vec3 reflected) { + const float NIGHT_EXPOSURE = 10.0; + const float DUSK_EXPOSURE = 2.0;//0.8; + const float DAY_EXPOSURE = 1.0;//0.7; + + const float DAY_SATURATION = 1.0; + const float DUSK_SATURATION = 0.6; + const float NIGHT_SATURATION = 0.1; + + const float gamma = /*0.5*//*1.*0*/1.0;//1.0; + /* float light = length(emitted + reflected); + float color = srgb_to_linear(emitted + reflected); + float avg_col = (color.r + color.g + color.b) / 3.0; + return ((color - avg_col) * light + reflected * avg_col) * (emitted + reflected); */ + // float max_intensity = vec3(1.0); + vec3 color = emitted + reflected; + float lum = rel_luminance(color); + // float lum_sky = lum - max_light; + + // vec3 sun_dir = get_sun_dir(time_of_day.x); + // vec3 moon_dir = get_moon_dir(time_of_day.x); + // float sky_light = rel_luminance( + // get_sun_color(sun_dir) * get_sun_brightness(sun_dir) * SUN_COLOR_FACTOR + + // get_moon_color(moon_dir) * get_moon_brightness(moon_dir)); + float sky_light = lum; + + // Tone mapped value. + // vec3 T = /*color*//*lum*/color;//normalize(color) * lum / (1.0 + lum); + // float alpha = 0.5;//2.0; + // float alpha = mix( + // mix( + // DUSK_EXPOSURE, + // NIGHT_EXPOSURE, + // max(sun_dir.z, 0) + // ), + // DAY_EXPOSURE, + // max(-sun_dir.z, 0) + // ); + float alpha = 1.0;//log(1.0 - lum) / lum; + // vec3 now_light = moon_dir.z < 0 ? moon_dir : sun_dir; + // float cos_view_light = dot(-now_light, view_dir); + // alpha *= exp(1.0 - cos_view_light); + // sky_light *= 1.0 - log(1.0 + view_dir.z); + float alph = sky_light > 0.0 && max_light > 0.0 ? mix(1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light / (0.0 + sky_light)), 1.0, clamp(max_light - sky_light, 0.0, 1.0)) : 1.0; + alpha = alpha * alph;// min(alph, 1.0);//((max_light > 0.0 && max_light > sky_light /* && sky_light > 0.0*/) ? /*1.0*/1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light - (0.0 + sky_light)) : 1.0); + // alpha = alpha * min(1.0, (max_light == 0.0 ? 1.0 : (1.0 + abs(lum_sky)) / /*(1.0 + max_light)*/max_light)); + + vec3 col_adjusted = lum == 0.0 ? vec3(0.0) : color / lum; + + // float L = lum == 0.0 ? 0.0 : log(lum); + + + // // float B = T; + // // float B = L + log(alpha); + // float B = lum; + + // float D = L - B; + + // float o = 0.0;//log(PERSISTENT_AMBIANCE); + // float scale = /*-alpha*/-alpha;//1.0; + + // float B_ = (B - o) * scale; + + // // float T = lum; + // float O = exp(B_ + D); + + float T = 1.0 - exp(-alpha * lum);//lum / (1.0 + lum); + // float T = lum; + + // Heuristic desaturation + // const float s = 0.8; + float s = 1.0; + // float s = mix( + // mix( + // DUSK_SATURATION, + // NIGHT_SATURATION, + // max(sun_dir.z, 0) + // ), + // DAY_SATURATION, + // max(-sun_dir.z, 0) + // ); + // s = max(s, (max_light) / (1.0 + s)); + // s = max(s, max_light / (1.0 + max_light)); + // s = max_light / (1.0 + max_light); + + vec3 c = pow(col_adjusted, vec3(s)) * T; + // vec3 c = col_adjusted * T; + // vec3 c = sqrt(col_adjusted) * T; + // vec3 c = /*col_adjusted * */col_adjusted * T; + + return c; + // float sum_col = color.r + color.g + color.b; + // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); +} + void main() { vec2 uv = (f_pos + 1.0) * 0.5; - if (medium.x == 1u) { + /* if (medium.x == 1u) { uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); - } + } */ + + vec2 c_uv = vec2(0.5);//uv;//vec2(0.5);//uv; + vec2 delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*//*0.5 - */min(uv, 1.0 - uv);//min(uv * (1.0 - uv), 0.25) * 2.0; + // delta = /*sqrt(2.0) / 2.0 - */sqrt(vec2(dot(delta, delta))); + // delta = 0.5 - vec2(min(delta.x, delta.y)); + delta = vec2(0.25);//vec2(dot(/*0.5 - */delta, /*0.5 - */delta));//vec2(min(delta.x, delta.y));//sqrt(2.0) * (0.5 - vec2(min(delta.x, delta.y))); + // delta = vec2(sqrt(dot(delta, delta))); + // vec2 delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*/1.0 - vec2(sqrt(dot(uv, 1.0 - uv)));//min(uv * (1.0 - uv), 0.25) * 2.0; + // float delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*/1.0 - (dot(uv - 0.5, uv - 0.5));//0.01;//25; + // vec2 delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*/sqrt(uv * (1.0 - uv));//min(uv * (1.0 - uv), 0.25) * 2.0; + + // float bright_color0 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(0.0, 0.0), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color1 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(delta.x, delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color2 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(delta.x, -delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color3 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(-delta.x, delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color4 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(-delta.x, -delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + + // float bright_color0 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(0.0, 0.0), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color1 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(delta, delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color2 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(delta, -delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color3 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(-delta, delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color4 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(-delta, -delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + + // float bright_color = max(bright_color0, max(bright_color1, max(bright_color2, max(bright_color3, bright_color4))));// / 2.0;// / 5.0; + + // float bright_color = (bright_color0 + bright_color1 + bright_color2 + bright_color3 + bright_color4) / 5.0; vec4 aa_color = aa_apply(src_color, uv * screen_res.xy, screen_res.xy); + // aa_color.rgb = illuminate(1.0 - 1.0 / (1.0 + bright_color), normalize(cam_pos.xyz - focus_pos.xyz), /*vec3 max_light, */vec3(0.0), aa_color.rgb); + //vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a); //hsva_color.y *= 1.45; //hsva_color.z *= 0.85; @@ -46,9 +186,11 @@ void main() { vec4 final_color = pow(aa_color, gamma); +#if (FLUID_MODE == FLUID_MODE_CHEAP) if (medium.x == 1u) { final_color *= vec4(0.2, 0.2, 0.8, 1.0); } +#endif tgt_color = vec4(final_color.rgb, 1); } diff --git a/assets/voxygen/shaders/postprocess-vert.glsl b/assets/voxygen/shaders/postprocess-vert.glsl index eef08e40d4..d88985486b 100644 --- a/assets/voxygen/shaders/postprocess-vert.glsl +++ b/assets/voxygen/shaders/postprocess-vert.glsl @@ -1,5 +1,21 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include in vec2 v_pos; @@ -14,5 +30,5 @@ out vec2 f_pos; void main() { f_pos = v_pos; - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } diff --git a/assets/voxygen/shaders/skybox-frag.glsl b/assets/voxygen/shaders/skybox-frag.glsl index d537a1f9eb..a39977036d 100644 --- a/assets/voxygen/shaders/skybox-frag.glsl +++ b/assets/voxygen/shaders/skybox-frag.glsl @@ -1,7 +1,24 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_TRANSMISSION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include #include +#include in vec3 f_pos; @@ -13,14 +30,34 @@ uniform u_locals { out vec4 tgt_color; void main() { + // tgt_color = vec4(MU_SCATTER, 1.0); + // return; vec4 _clouds; + + vec3 cam_dir = normalize(f_pos - cam_pos.xyz); + + float cam_alt = alt_at(cam_pos.xy); + // float f_alt = alt_at(f_pos.xy); + float fluid_alt = medium.x == 1u ? floor(cam_alt + 1) : view_distance.w; + // float fluid_alt = max(f_pos.z + 1, floor(f_alt)); + vec3 mu = medium.x == 1u /* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0); + // vec3 sun_attenuation = compute_attenuation(wpos, -sun_dir, mu, surface_alt, wpos); + vec3 cam_attenuation = compute_attenuation(cam_pos.xyz, -cam_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*//*f_pos*//*vec3(f_pos.xy, fluid_alt)*/cam_pos.xyz); + // vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); + // vec3 cam_attenuation = vec3(1.0); + + + /* vec3 world_pos = cam_pos.xyz + cam_dir * 500000.0; + tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, world_pos, 1.0, true, _clouds), 1.0); */ float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); float dist = 100000.0; - if (medium.x == 1u) { - dist = UNDERWATER_MIST_DIST; - } - vec3 wpos = cam_pos.xyz + normalize(f_pos) * dist; - tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, true, _clouds), 1.0); + float refractionIndex = medium.x == 1u ? 1.0 / 1.3325 : 1.0; + /* if (medium.x == 1u) { + dist = UNDERWATER_MIST_DIST; + } */ + vec3 wpos = cam_pos.xyz + /*normalize(f_pos)*/cam_dir * dist; + + tgt_color = vec4(cam_attenuation * get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, true, refractionIndex, _clouds), 1.0); } diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl index 8cfe045eda..86759a29fe 100644 --- a/assets/voxygen/shaders/skybox-vert.glsl +++ b/assets/voxygen/shaders/skybox-vert.glsl @@ -1,5 +1,21 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_TRANSMISSION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include in vec3 v_pos; @@ -12,12 +28,22 @@ uniform u_locals { out vec3 f_pos; void main() { + /* vec3 v_pos = v_pos; + v_pos.y = -v_pos.y; */ f_pos = v_pos; // TODO: Make this position-independent to avoid rounding error jittering gl_Position = - proj_mat * - view_mat * - vec4(v_pos * 100000.0 + cam_pos.xyz, 1); - gl_Position.z = 0.0; + /* proj_mat * + view_mat * */ + all_mat * + /* proj_mat * + view_mat * */ + vec4(/*100000 * */v_pos + cam_pos.xyz, 1); + // vec4(v_pos * (100000.0/* + 0.5*/) + cam_pos.xyz, 1); + // gl_Position = vec4(gl_Position.xy, sign(gl_Position.z) * gl_Position.w, gl_Position.w); + gl_Position.z = gl_Position.w; + // gl_Position.z = gl_Position.w - 0.000001;//0.0; + // gl_Position.z = 1.0; + // gl_Position.z = -1.0; } diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl index 4b983e5ae7..fd97f688ac 100644 --- a/assets/voxygen/shaders/sprite-frag.glsl +++ b/assets/voxygen/shaders/sprite-frag.glsl @@ -1,38 +1,199 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #include in vec3 f_pos; flat in vec3 f_norm; -in vec3 f_col; -in float f_ao; -in float f_light; +flat in float f_light; +// flat in vec3 f_pos_norm; +in vec2 f_uv_pos; +// flat in uint f_atlas_pos; +// in vec3 f_col; +// in float f_ao; +// in float f_light; +// in vec4 light_pos[2]; + +uniform sampler2D t_col_light; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; out vec4 tgt_color; #include #include +#include const float FADE_DIST = 32.0; void main() { - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - float point_shadow = shadow_at(f_pos, f_norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; - float ao = pow(f_ao, 0.5) * 0.85 + 0.15; - ambient_light *= ao; - diffuse_light *= ao; - vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + /* if (f_uv_pos.x < 757) { + discard; + } */ + // vec2 f_uv_pos = vec2(768,1) + 0.5; + // vec2 f_uv_pos = vec2(760, 380);// + 0.5; + // vec2 f_uv_pos = vec2((uvec2(f_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)) + 0.5; + /* if (f_uv_pos.x < 757) { + discard; + } */ + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); + // vec4 f_col_light = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)/* + uv_delta*//* - f_norm * 0.00001*/); + // vec4 f_col_light = textureGrad(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0), vec2(0.5), vec2(0.5)); + vec4 f_col_light = texelFetch(t_col_light, ivec2(f_uv_pos)/* + uv_delta*//* - f_norm * 0.00001*/, 0); + vec3 f_col = /*linear_to_srgb*//*srgb_to_linear*/(f_col_light.rgb); + // vec3 f_col = vec3(1.0); + // vec2 texSize = textureSize(t_col_light, 0); + // float f_ao = f_col_light.a; + // float f_ao = f_col_light.a + length(vec2(dFdx(f_col_light.a), dFdy(f_col_light.a))); + float f_ao = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_ao = 1.0; + // float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + + // vec3 my_chunk_pos = f_pos_norm; + // tgt_color = vec4(hash(floor(vec4(my_chunk_pos.x, 0, 0, 0))), hash(floor(vec4(0, my_chunk_pos.y, 0, 1))), hash(floor(vec4(0, 0, my_chunk_pos.z, 2))), 1.0); + // tgt_color = vec4(f_uv_pos / texSize, 0.0, 1.0); + // tgt_color = vec4(f_col.rgb, 1.0); + // return; + // vec4 light_pos[2]; +//#if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +//#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +//#endif + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + vec3 view_dir = -cam_to_frag; + + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ + // float sun_light = get_sun_brightness(sun_dir); + // float moon_light = get_moon_brightness(moon_dir); + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float f_alt = alt_at(f_pos.xy); + // float f_alt = f_pos.z; +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); + // float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir); + // float sun_shade_frac = horizon_at(f_pos, sun_dir); + // float moon_shade_frac = horizon_at(f_pos, moon_dir); + // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). + // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5); + // NOTE: current assumption is that moon and sun shouldn't be out at the sae time. + // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow + // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally). + // float shade_frac = sun_shade_frac + moon_shade_frac; + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); + + vec3 surf_color = /*srgb_to_linear*//*linear_to_srgb*/(f_col); + float alpha = 1.0; + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // To account for prior saturation. + // float vert_light = pow(f_light, 1.5); + // vec3 light_frac = light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); + /* light_frac += light_reflection_factor(f_norm, view_dir, vec3(1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(-1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, -1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, 1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); */ + + // vec3 light, diffuse_light, ambient_light; + // vec3 emitted_light, reflected_light; + // float point_shadow = shadow_at(f_pos,f_norm); + // vec3 point_light = light_at(f_pos, f_norm); + // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)); + // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x, *//*cam_to_frag*/view_dir, k_a * f_light/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light); + // reflected_light *= /*vert_light * */point_shadow * shade_frac; + // emitted_light *= /*vert_light * */point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= /*vert_light * */point_shadow * shade_frac; + // emitted_light *= point_shadow; + // reflected_light *= point_shadow; + // max_light *= point_shadow; + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // float point_shadow = shadow_at(f_pos, f_norm); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light * point_shadow; + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + + max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + + // float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + float ao = f_ao; + emitted_light *= ao; + reflected_light *= ao; + + surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light); + // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif + // tgt_color = vec4(color, 1.0); tgt_color = vec4(color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (sprite_render_distance - FADE_DIST)) / FADE_DIST, 0, 1)); } diff --git a/assets/voxygen/shaders/sprite-vert.glsl b/assets/voxygen/shaders/sprite-vert.glsl index 91cc75e6d2..e8c4c33270 100644 --- a/assets/voxygen/shaders/sprite-vert.glsl +++ b/assets/voxygen/shaders/sprite-vert.glsl @@ -1,62 +1,236 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include #include +#include in vec3 v_pos; -in uint v_col; +in uint v_atlas_pos; +// in uint v_col; in uint v_norm_ao; +in uint inst_pos_ori; in vec4 inst_mat0; in vec4 inst_mat1; in vec4 inst_mat2; in vec4 inst_mat3; -in vec3 inst_col; +// in vec3 inst_col; in float inst_wind_sway; +struct SpriteLocals { + mat4 mat; + vec4 wind_sway; + vec4 offs; +}; + +layout (std140) +uniform u_locals { + mat4 mat; + vec4 wind_sway; + vec4 offs; + // SpriteLocals sprites[8]; +}; + +// struct Instance { +// mat4 inst_mat; +// vec3 inst_col; +// float inst_wind_sway; +// }; +// +// layout (std140) +// uniform u_ibuf { +// Instance sprite_instances[/*MAX_LAYER_FACES*/512]; +// }; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + +layout (std140) +uniform u_terrain_locals { + vec3 model_offs; + float load_time; + ivec4 atlas_offs; +}; + out vec3 f_pos; flat out vec3 f_norm; -out vec3 f_col; -out float f_ao; -out float f_light; +flat out float f_light; +// flat out vec3 f_pos_norm; +// out vec3 f_col; +// out float f_ao; +out vec2 f_uv_pos; +// flat out uint f_atlas_pos; +// out vec3 light_pos[2]; +// out float f_light; const float SCALE = 1.0 / 11.0; +const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2; + +const int EXTRA_NEG_Z = 32768; void main() { - mat4 inst_mat; - inst_mat[0] = inst_mat0; - inst_mat[1] = inst_mat1; - inst_mat[2] = inst_mat2; - inst_mat[3] = inst_mat3; + // vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + // uint inst_ori = (inst_pos_ori >> 29) & 0x7u; + // SpriteLocals locals = sprites[inst_ori]; + // SpriteLocals locals = sprites; + // mat4 inst_mat = locals.mat; + // float inst_wind_sway = locals.wind_sway.w; - vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; + // mat4 inst_mat = mat4(vec4(1, 0, 0, 0), vec4(0, 1, 0, 0), vec4(0, 0, 1, 0), vec4(5.5, 5.5, 0, 1)); + // float inst_wind_sway = 0.0; + mat4 inst_mat; + inst_mat[0] = inst_mat0; + inst_mat[1] = inst_mat1; + inst_mat[2] = inst_mat2; + inst_mat[3] = inst_mat3; + /* Instance instances = sprite_instances[gl_InstanceID & 1023]; + mat4 inst_mat = instances.inst_mat; + vec3 inst_col = instances.inst_col; + float inst_wind_sway = instances.inst_wind_sway; */ + vec3 inst_offs = model_offs - focus_off.xyz; + // mat3 inst_mat; + // inst_mat[0] = inst_mat0.xyz; + // inst_mat[1] = inst_mat1.xyz; + // inst_mat[2] = inst_mat2.xyz; + // /* Instance instances = sprite_instances[gl_InstanceID & 1023]; + // mat4 inst_mat = instances.inst_mat; + // vec3 inst_col = instances.inst_col; + // float inst_wind_sway = instances.inst_wind_sway; */ + // float inst_wind_sway = wind_sway.w; + // vec3 inst_offs = model_offs - focus_off.xyz; - f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz; - f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + // vec3 sprite_pos = floor(inst_mat3.xyz * SCALE) + inst_offs; - // Wind waving - f_pos += inst_wind_sway * vec3( - sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), - sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), - 0.0 - ) * pow(abs(v_pos.z) * SCALE, 1.3) * 0.2; + // f_pos_norm = v_pos; - // First 3 normals are negative, next 3 are positive - vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); - f_norm = (inst_mat * vec4(normals[(v_norm_ao >> 0) & 0x7u], 0)).xyz; + // vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; + // vec3 sprite_pos = floor((inst_mat * vec4(0, 0, 0, 1)).xyz * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; + // vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(0, 0, 0, 1)).xyz - /* wind_sway.xyz * */offs.xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) - inst_offs; + // vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; - vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0; - f_col = srgb_to_linear(col) * srgb_to_linear(inst_col); - f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; + // vec3 v_pos = vec3(gl_VertexID * 32, gl_VertexID % 32, 1.0); + // f_pos = v_pos + (model_offs - focus_off.xyz); - // Select glowing - if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) { - f_col *= 8.0; - } + // vec3 v_pos = /*inst_mat*//*locals.*/wind_sway.xyz * v_pos; + vec3 v_pos_ = /*inst_mat*//*locals.*//*sprites[0].*/wind_sway.xyz * v_pos; + // vec3 v_pos = (/*inst_mat*/locals.mat * vec4(v_pos, 1)).xyz + vec3(0.5, 0.5, 0.0); + // f_pos = v_pos * SCALE + (inst_chunk_pos + model_offs - focus_off.xyz); - f_light = 1.0; + // vec3 v_pos_ = (inst_mat * vec4(v_pos/* * SCALE*/, 1)).xyz; + // vec3 v_pos = (inst_mat * vec4(v_pos, 1)).xyz; + // f_pos = v_pos + (model_offs - focus_off.xyz); - gl_Position = - all_mat * - vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + f_pos = (inst_mat * vec4(v_pos_, 1.0)).xyz * SCALE + inst_offs; + // f_pos = (inst_mat * v_pos_) * SCALE + sprite_pos; + + // f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz + (model_offs - focus_off.xyz); + // f_pos = v_pos_ + (inst_chunk_pos + model_offs - focus_off.xyz + vec3(0.5, 0.5, 0.0)); + // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); + + // Wind waving + /* const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1); + const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1); + const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR; + const float xy_bias = sin(tick.x * 0.25); + const float z_bias = xy_bias * t_scale; + mat3 shear = mat4( + vec3(x_scale , 0.0, 0.0, 0.0), + vec3(0.0, y_scale, 0.0, 0.0), + vec3(0.0, 0.0, z_bias, 0.0), + vec3(0.0, 0.0, (1.0 / z_bias), 0.0) + ); */ + // const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1); + // const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1); + // const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR; + // const float xy_bias = sin(tick.x * 0.25); + // const float z_bias = xy_bias * t_scale; + // vec3 rotate = inst_wind_sway * vec3( + // sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), + // sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), + // 0.0 + // ) * pow(abs(v_pos_.z/* + sprites[0].offs.z*/)/* * SCALE*/, 1.3) * /*0.2;*/SCALE_FACTOR; + // + // mat3 shear = mat4( + // vec3(x_scale * , 0.0, 0.0, 0.0), + // vec3(0.0, y_scale, 0.0, 0.0), + // vec3(0.0, 0.0, z_bias, 0.0), + // vec3(0.0, 0.0, (1.0 / z_bias), 0.0) + // ); + /*if (wind_sway.w >= 0.4) */{ + f_pos += /*inst_wind_sway*/wind_sway.w * vec3( + sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), + sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), + 0.0 + ) * pow(abs(v_pos_.z/* + sprites[0].offs.z*/)/* * SCALE*/, 1.3) * /*0.2;*/SCALE_FACTOR; + } + + // First 3 normals are negative, next 3 are positive + // vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); + // uint norm_idx = (v_norm_ao >> 0) & 0x7u; + // f_norm = (inst_mat * vec4(normals[], 0)).xyz; + + // TODO: Consider adding a second, already-normalized (i.e. unscaled) matrix. + // vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz); + // vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u]); + + // vec3 norm = bone_data.normals_mat[axis_idx].xyz; + // norm = normalize(norm); + // norm = norm / SCALE_FACTOR / locals.wind_sway.xyz; + // norm = norm / (norm.x + norm.y + norm.z); + // vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1); + + // // Calculate normal here rather than for each pixel in the fragment shader + // f_norm = normalize(( + // combined_mat * + // vec4(norm, 0) + // ).xyz); + + vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz); + f_norm = mix(-norm, norm, v_norm_ao & 1u); + + /* vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0; + f_col = srgb_to_linear(col) * srgb_to_linear(inst_col); + f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; */ + f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));/* + 0.5*/; + // f_atlas_pos = v_atlas_pos; + /* for (uint i = 0u; i < light_shadow_count.z; ++i) { + light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + } */ + + // // Select glowing + // if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) { + // f_col *= 4.0; + // } + // f_light = 1.0; + // if (select_pos.w > 0) */{ + vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; + f_light = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 1.0 / PERSISTENT_AMBIANCE : 1.0; + // } + + gl_Position = + all_mat * + vec4(f_pos, 1); + // gl_Position.z = -gl_Position.z; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl index 037357d0af..efbc5d4125 100644 --- a/assets/voxygen/shaders/terrain-frag.glsl +++ b/assets/voxygen/shaders/terrain-frag.glsl @@ -1,69 +1,373 @@ #version 330 core +// #extension GL_ARB_texture_storage : require + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS #include #include in vec3 f_pos; -in vec3 f_chunk_pos; +// in float f_ao; +// in vec3 f_chunk_pos; +// #ifdef FLUID_MODE_SHINY flat in uint f_pos_norm; -in vec3 f_col; -in float f_light; -in float f_ao; +// #else +// const uint f_pos_norm = 0u; +// #endif +// in float f_alt; +// in vec4 f_shadow; +// in vec3 f_col; +// in float f_light; +/*centroid */in vec2 f_uv_pos; +// in vec3 light_pos[2]; +// const vec3 light_pos[6] = vec3[](vec3(0), vec3(0), vec3(00), vec3(0), vec3(0), vec3(0)); + +/* #if (SHADOW_MODE == SHADOW_MODE_MAP) +in vec4 sun_pos; +#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +const vec4 sun_pos = vec4(0.0); +#endif */ + +uniform sampler2D t_col_light; layout (std140) uniform u_locals { vec3 model_offs; float load_time; + ivec4 atlas_offs; }; out vec4 tgt_color; #include #include - -float vmax(vec3 v) { - return max(v.x, max(v.y, v.z)); -} +#include void main() { + // discard; + // vec4 f_col_light = textureGrad(t_col_light, f_uv_pos / texSize, 0.25, 0.25); + // vec4 f_col_light = texture(t_col_light, (f_uv_pos) / texSize); + // First 3 normals are negative, next 3 are positive - vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); + const vec3 normals[8] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1), vec3(0,0,0), vec3(0,0,0)); + + // uint norm_index = (f_pos_norm >> 29) & 0x7u; + // vec2 uv_delta = (norm_index & 0u) == 0u ? vec2(-1.0) : vec2(0); + + vec2 f_uv_pos = f_uv_pos + atlas_offs.xy; + // vec4 f_col_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0)));//(f_uv_pos/* + 0.5*/) / texSize); + // float f_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0))).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + vec4 f_col_light = texelFetch(t_col_light, ivec2(f_uv_pos)/* + uv_delta*//* - f_norm * 0.00001*/, 0); + // float f_light = f_col_light.a; + // vec4 f_col_light = texelFetch(t_col_light, ivec2(int(f_uv_pos.x), int(f_uv_pos.y)/* + uv_delta*//* - f_norm * 0.00001*/), 0); + vec3 f_col = /*linear_to_srgb*//*srgb_to_linear*/(f_col_light.rgb); + // vec3 f_col = vec3(1.0); + float f_light = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // vec2 texSize = textureSize(t_col_light, 0); + // float f_light = texture(t_col_light, f_uv_pos/* + vec2(atlas_offs.xy)*/).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_light = textureProj(t_col_light, vec3(f_uv_pos/* + vec2(atlas_offs.xy)*/, texSize.x)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_light = textureProjLod(t_col_light, vec3(f_uv_pos/* + vec2(atlas_offs.xy)*/, texSize.x), 0).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_light = textureGrad(t_col_light, (f_uv_pos + 0.5) / texSize, vec2(0.1, 0.0), vec2(0.0, 0.1)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // f_light = sqrt(f_light); + // f_light = sqrt(f_light); + // f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // vec3 f_col = light_col.rgb;//vec4(1.0, 0.0, 0.0, 1.0); + + // float f_ao = 1.0; + + // vec3 my_chunk_pos = vec3(ivec3((uvec3(f_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu))); + // tgt_color = vec4(hash(floor(vec4(my_chunk_pos.x, 0, 0, 0))), hash(floor(vec4(0, my_chunk_pos.y, 0, 1))), hash(floor(vec4(0, 0, my_chunk_pos.z, 2))), 1.0); + // tgt_color.rgb *= f_light; + // tgt_color = vec4(vec3(f_light), 1.0); + // tgt_color = vec4(f_col, 1.0); + // return; + // vec4 light_pos[2]; + // vec4 light_col = vec4( + // hash(floor(vec4(f_pos.x, 0, 0, 0))), + // hash(floor(vec4(0, f_pos.y, 0, 1))), + // hash(floor(vec4(0, 0, f_pos.z, 2))), + // 1.0 + // ); + // vec3 f_col = light_col.rgb;//vec4(1.0, 0.0, 0.0, 1.0); + // tgt_color = vec4(f_col, 1.0); + // tgt_color = vec4(light_shadow_count.x <= 31u ? f_col : vec3(0.0), 1.0); + // tgt_color = vec4(0.0, 0.0, 0.0, 1.0); + // float sum = 0.0; + // for (uint i = 0u; i < /* 6 * */light_shadow_count.x; i ++) { + // // uint i = 1u; + // Light L = lights[i/* / 6*/]; + + // /* vec4 light_col = vec4( + // hash(vec4(1.0, 0.0, 0.0, i)), + // hash(vec4(1.0, 1.0, 0.0, i)), + // hash(vec4(1.0, 0.0, 1.0, i)), + // 1.0 + // ); */ + // vec3 light_col = vec3(1.0);//L.light_col.rgb; + // float light_strength = L.light_col.a / 255.0; + // // float light_strength = 1.0 / light_shadow_count.x; + + // vec3 light_pos = L.light_pos.xyz; + + // // Pre-calculate difference between light and fragment + // vec3 fragToLight = f_pos - light_pos; + + // // vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u]; + + // // use the light to fragment vector to sample from the depth map + // float bias = 0.0;//0.05;//0.05; + // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, i)/*, 0.0*//*, bias*/).r; + // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, lightIndex), bias); + // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, i + 1)/*, bias*/).r; + // float currentDepth = VectorToDepth(fragToLight) + bias; + // float closestDepth = texture(t_shadow_maps, vec3(fragToLight)/*, -2.5*/).r; + // + // // float visibility = texture(t_shadow_maps, vec4(fragToLight, i + 1), -(length(fragToLight) - bias)/* / screen_res.w*/); + // // it is currently in linear range between [0,1]. Re-transform back to original value + // // closestDepth *= screen_res.w; // far plane + // // now test for shadows + // // float shadow = /*currentDepth*/(screen_res.w - bias) > closestDepth ? 1.0 : 0.0; + // // float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + + // // tgt_color += light_col * vec4(vec3(/*closestDepth*/visibility/* + bias*//* / screen_res.w */) * 1.0 / light_shadow_count.x, 0.0); + // // tgt_color.rgb += light_col * vec3(closestDepth + 0.05 / screen_res.w) * 1.0 /*/ light_shadow_count.x*/ * light_strength; + // tgt_color.rgb += light_col * vec3(closestDepth) * 1.0 / screen_res.w /*/ light_shadow_count.x*/ * light_strength; + // sum += light_strength; + // } // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. - uint norm_axis = (f_pos_norm >> 30) & 0x3u; - // Increase array access by 3 to access positive values - uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; + // uint norm_axis = (f_pos_norm >> 30) & 0x3u; + // // Increase array access by 3 to access positive values + // uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; // Use an array to avoid conditional branching + // uint norm_index = (f_pos_norm >> 29) & 0x7u; + // vec3 f_norm = normals[norm_index]; vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u]; + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); - float ao = pow(f_ao, 0.5) * 0.9 + 0.1; + // /* if (light_shadow_count.x == 1) { + // tgt_color.rgb = vec3(0.0); + // } */ + // if (sum > 0.0) { + // tgt_color.rgb /= sum; + // } + // return; + // Whether this face is facing fluid or not. + bool faces_fluid = bool((f_pos_norm >> 28) & 0x1u); - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - float point_shadow = shadow_at(f_pos, f_norm); - diffuse_light *= point_shadow; - ambient_light *= point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - ambient_light *= f_light * ao; - diffuse_light *= f_light * ao; - diffuse_light += point_light * ao; + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + vec3 view_dir = -cam_to_frag; + // vec3 view_dir = normalize(f_pos - cam_pos.xyz); - vec3 col = f_col + hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0)) * 0.02; // Small-scale noise + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ - // Select glowing - if (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.01)) { - if (vmax(abs(mod(f_pos - f_norm * 0.5, 1.0) - 0.5)) > 0.45) { - col *= 0.5; - } - } +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float f_alt = alt_at(f_pos.xy); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif - vec3 surf_color = illuminate(srgb_to_linear(col), light, diffuse_light, ambient_light); + float alpha = 1.0;//0.0001;//1.0; + // TODO: Possibly angle with water surface into account? Since we can basically assume it's horizontal. + const float n2 = 1.5;//1.01; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + // float faces_fluid = faces_fluid && f_pos.z <= floor(f_alt); + float fluid_alt = max(f_pos.z + 1, floor(f_alt)); + float R_s = /*(f_pos.z < f_alt)*/faces_fluid /*&& f_pos.z <= fluid_alt*/ ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + // vec3 surf_color = /*srgb_to_linear*/(f_col); + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + // float sun_light = get_sun_brightness(sun_dir); + // float moon_light = get_moon_brightness(moon_dir); + /* float sun_shade_frac = horizon_at(f_pos, sun_dir); + float moon_shade_frac = horizon_at(f_pos, moon_dir); */ + // float f_alt = alt_at(f_pos.xy); + // vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir); + // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). + // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5); + // NOTE: current assumption is that moon and sun shouldn't be out at the sae time. + // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow + // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally). + // float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac; + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); + + float max_light = 0.0; + + // After shadows are computed, we use a refracted sun and moon direction. + // sun_dir = faces_fluid && sun_shade_frac > 0.0 ? refract(sun_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), 1.0 / 1.3325) : sun_dir; + // moon_dir = faces_fluid && moon_shade_frac > 0.0 ? refract(moon_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), 1.0 / 1.3325) : moon_dir; + + // Compute attenuation due to water from the camera. + vec3 mu = faces_fluid/* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0); + // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. + vec3 cam_attenuation = + medium.x == 1u ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos) + : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz); + + // Computing light attenuation from water. + vec3 emitted_light, reflected_light; + // To account for prior saturation + /*float */f_light = faces_fluid ? 1.0 : f_light * sqrt(f_light); + + emitted_light = vec3(1.0); + reflected_light = vec3(1.0); + float f_select = (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.5)) ? 1.0 / PERSISTENT_AMBIANCE : 1.0; + max_light += get_sun_diffuse2(/*time_of_day.x, */sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a * f_select/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + + // emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW); + // reflected_light *= f_light * point_shadow * shade_frac; + // max_light *= f_light * point_shadow * shade_frac; + emitted_light *= f_light; + reflected_light *= f_light; + max_light *= f_light; + + max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + + // float f_ao = 1.0; + + // float ao = /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1; + // emitted_light *= ao; + // reflected_light *= ao; + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + + // float point_shadow = shadow_at(f_pos, f_norm); + // vec3 point_light = light_at(f_pos, f_norm); + // vec3 light, diffuse_light, ambient_light; + + // get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, k_a * f_light, k_d * f_light, k_s * f_light, alpha, emitted_light, reflected_light); + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // float point_shadow = shadow_at(f_pos, f_norm); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light * point_shadow; + // vec3 point_light = light_at(f_pos, f_norm); + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + // reflected_light += light_reflection_factor(norm, cam_to_frag, , vec3 k_d, vec3 k_s, float alpha) { + + // light_reflection_factorplight_reflection_factor + + // vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light); + vec3 f_chunk_pos = f_pos - (model_offs - focus_off.xyz); + float noise = hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0));//0.005/* - 0.01*/; + +//vec3 srgb_to_linear(vec3 srgb) { +// bvec3 cutoff = lessThan(srgb, vec3(0.04045)); +// vec3 higher = pow((srgb + vec3(0.055))/vec3(1.055), vec3(2.4)); +// vec3 lower = srgb/vec3(12.92); +// +// return mix(higher, lower, cutoff); +//} +// +//vec3 linear_to_srgb(vec3 col) { +// // bvec3 cutoff = lessThan(col, vec3(0.0060)); +// // return mix(11.500726 * col, , cutoff); +// vec3 s1 = vec3(sqrt(col.r), sqrt(col.g), sqrt(col.b)); +// vec3 s2 = vec3(sqrt(s1.r), sqrt(s1.g), sqrt(s1.b)); +// vec3 s3 = vec3(sqrt(s2.r), sqrt(s2.g), sqrt(s2.b)); +// return vec3( +// mix(11.500726 * col.r, (0.585122381 * s1.r + 0.783140355 * s2.r - 0.368262736 * s3.r), clamp((col.r - 0.0060) * 10000.0, 0.0, 1.0)), +// mix(11.500726 * col.g, (0.585122381 * s1.g + 0.783140355 * s2.g - 0.368262736 * s3.g), clamp((col.g - 0.0060) * 10000.0, 0.0, 1.0)), +// mix(11.500726 * col.b, (0.585122381 * s1.b + 0.783140355 * s2.b - 0.368262736 * s3.b), clamp((col.b - 0.0060) * 10000.0, 0.0, 1.0)) +// ); +// +// 11.500726 +//} + // vec3 noise_delta = vec3(noise * 0.005); + // vec3 noise_delta = noise * 0.02 * (1.0 - vec3(0.2126, 0.7152, 0.0722)); + // vec3 noise_delta = noise * 0.002 / vec3(0.2126, 0.7152, 0.0722); + // vec3 noise_delta = sqrt(f_col) + noise; + /* vec3 noise_delta = f_col + noise * 0.02; + noise_delta *= noise_delta; + noise_delta -= f_col; */ + // vec3 noise_delta = (1.0 - f_col) * 0.02 * noise * noise; + // + // a = 0.055 + // + // 1 / (1 + a) = 1 / (1 + 0.055) ~ 0.947867299 + // + // l2s = x^(1/2.4) * (1 / (1 + a)) - a + c + // s2l = (l + a)^2.4 * (1 / (1 + a))^2.4 + // = ((x^(1/2.4) * (1 / (1 + a)) - a + c) + a)^2.4 * (1 / (1 + a))^2.4 + // = (x^(1/2.4) * (1 / (1 + a)) + c)^2.4 * (1 / (1 + a))^2.4 + // + // ~ (x^(1/2) * 1 / (1 + a) + c)^2 * (1 / (1 + a))^2 + // + // = ((x + a)^2.4 * (1 / (1 + a))^2.4 + c)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // = (((x + a)^2.4 + c * (1 + a)^2.4) * (1 / (1 + a))^2.4)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // = ((x + a)^2.4 + c * (1 + a)^2.4)^(1/2.4) * ((1 / (1 + a))^2.4)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // = ((x + a)^2.4 + c * (1 + a)^2.4)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // + // = ((x + a)^2 + c * (1 + a)^2)^(1/2) * (1 / (1 + a))^(1/2) + // = (x^2 + a^2 + 2xa + c + ca^2 + 2ac)^(1/2) * (1 / (1 + a))^(1/2) + // + const float A = 0.055; + const float W_INV = 1 / (1 + A); + const float W_2 = W_INV * W_INV;//pow(W_INV, 2.4); + const float NOISE_FACTOR = 0.02;//pow(0.02, 1.2); + vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR); + // noise_delta = noise_delta * noise_delta * W_2 - f_col; + // lum = W ⋅ col + // lum + noise = W ⋅ (col + delta) + // W ⋅ col + noise = W ⋅ col + W ⋅ delta + // noise = W ⋅ delta + // delta = noise / W + // vec3 col = (f_col + noise_delta); + vec3 col = noise_delta * noise_delta * W_2; + // vec3 col = srgb_to_linear(linear_to_srgb(f_col) + noise * 0.02); + // vec3 col = /*srgb_to_linear*/(f_col + noise); // Small-scale noise + // vec3 col = /*srgb_to_linear*/(f_col + hash(vec4(floor(f_pos * 3.0 - f_norm * 0.5), 0)) * 0.01); // Small-scale noise + vec3 surf_color = illuminate(max_light, view_dir, col * emitted_light, col * reflected_light); + +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 1.0, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 1.0, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif tgt_color = vec4(color, 1.0); } diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl index 0b4664b24d..e3d9615350 100644 --- a/assets/voxygen/shaders/terrain-vert.glsl +++ b/assets/voxygen/shaders/terrain-vert.glsl @@ -1,42 +1,171 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +// #define HAS_SHADOW_MAPS + #include #include +#include +#include + in uint v_pos_norm; -in uint v_col_light; +// in uint v_col_light; +in uint v_atlas_pos; layout (std140) uniform u_locals { vec3 model_offs; float load_time; + ivec4 atlas_offs; }; -out vec3 f_pos; -out vec3 f_chunk_pos; -flat out uint f_pos_norm; -out vec3 f_col; -out float f_light; -out float f_ao; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; -const int EXTRA_NEG_Z = 65536; +out vec3 f_pos; +// #ifdef FLUID_MODE_SHINY +flat out uint f_pos_norm; + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// out vec4 sun_pos; +// #endif + +// #endif +// out float f_alt; +// out vec4 f_shadow; +// out vec3 f_col; +// out vec3 f_chunk_pos; +// out float f_ao; +/*centroid */out vec2 f_uv_pos; +// out vec3 light_pos[2]; +// out float f_light; + +// uniform sampler2DRect t_col_light; + +const int EXTRA_NEG_Z = 32768; void main() { - f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0x1FFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); - f_pos = f_chunk_pos + model_offs; + // over it (if this vertex to see if it intersects. + // f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + f_pos = f_chunk_pos + model_offs - focus_off.xyz; - f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); - f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + // f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); + // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); - f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // vec3 light_col = vec3( + // hash(floor(vec4(f_chunk_pos.x, 0, 0, 0))), + // hash(floor(vec4(0, f_chunk_pos.y, 0, 1))), + // hash(floor(vec4(0, 0, f_chunk_pos.z, 2))) + // ); - f_light = float(v_col_light & 0x3Fu) / 64.0; - f_ao = float((v_col_light >> 6u) & 3u) / 4.0; + // f_col = light_col;// f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // f_light = 1.0;//float(v_col_light & 0x3Fu) / 64.0; + // f_ao = 1.0;//float((v_col_light >> 6u) & 3u) / 4.0; + // f_col = f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // f_light = float(v_col_light & 0x3Fu) / 64.0; + // f_ao = float((v_col_light >> 6u) & 3u) / 4.0; + // for (uint i = 0u; i < 1u/*light_shadow_count.z*/; ++i) { + // light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + // } + // vec2 texSize = textureSize(t_col_light, 0); + f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)); + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// // vec4 sun_pos = vec4(0.0); +// #endif + +// #ifdef FLUID_MODE_SHINY f_pos_norm = v_pos_norm; +// #endif + // Also precalculate shadow texture and estimated terrain altitude. + // f_alt = alt_at(f_pos.xy); + // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + + // IDEA: Cast a ray from the vertex to the camera (if this vertex is above the camera) or from the camera to the vertex (if this + // vertex is below the camera) to see where it intersects the plane of water. All of this only applies if either the terrain + // vertex is in water, or the camera is in water. + // + // If an intersection is found, refract the ray across the barrier using the correct ratio of indices of refraction (1 / N_WATER + // if the vertex is above the camera [ray is going from air to water], N_WATER if the camera is above the vertex + // [ray is going from water to air]). + // + // In order to make sure that terrain and other objects below such an interface are properly renered, we then "un-refract" by + // reversing the refracted vector, and multiplying that by the distance from the object from which we cast the ray to the + // intersectng point, in order to make the object appear to the viewer where it should after refraction. + // bool faces_fluid = bool((f_pos_norm >> 28) & 0x1u); + // // TODO: Measure real water surface altitude here. + // float surfaceAlt = faces_fluid ? max(ceil(f_pos.z), floor(f_alt)) : /*floor(f_alt);*/mix(view_distance.z, min(f_alt, floor(alt_at_real(cam_pos.xy))), medium.x); + + // vec3 wRayinitial = f_pos; // cam_pos.z < f_pos.z ? f_pos : cam_pos.xyz; + // vec3 wRayfinal = cam_pos.xyz; // cam_pos.z < f_pos.z ? cam_pos.xyz : f_pos; + // vec3 wRayNormal = surfaceAlt < wRayinitial.z ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + // float n_camera = mix(1.0, 1.3325, medium.x); + // float n_vertex = faces_fluid ? 1.3325 : 1.0; + // float n1 = n_vertex; // cam_pos.z < f_pos.z ? n_vertex : n_camera; + // float n2 = n_camera; // cam_pos.z < f_pos.z ? n_camera : n_vertex; + + // float wRayLength0 = length(wRayfinal - wRayinitial); + // vec3 wRayDir = (wRayfinal - wRayinitial) / wRayLength0; + // vec3 wPoint = wRayfinal; + // bool wIntersectsSurface = IntersectRayPlane(wRayinitial, wRayDir, vec3(0.0, 0.0, surfaceAlt), -wRayNormal, wPoint); + // float wRayLength = length(wPoint - wRayinitial); + // wPoint = wRayLength < wRayLength0 ? wPoint : wRayfinal; + // wRayLength = min(wRayLength, wRayLength0); // min(max_length, dot(wRayfinal - wpos, defaultpos - wpos)); + + // // vec3 wRayDir2 = (wRayfinal - wRayinitial) / wRayLength; + + // vec3 wRayDir3 = (dot(wRayDir, wRayNormal) < 0.0 && wIntersectsSurface) ? refract(wRayDir, wRayNormal, n2 / n1) : wRayDir; + // // wPoint -= wRayDir3 * wRayLength * n2 / n1; + // vec3 newRay = dot(wRayDir, wRayNormal) < 0.0 && wIntersectsSurface ? wPoint - wRayDir3 * wRayLength * n2 / n1 : f_pos;// - (wRayfinal - wPoint) * n2 / n1; // wPoint + n2 * (wRayfinal - wPoint) - n2 / n1 * wRayLength * wRayDir3; + +#ifdef HAS_SHADOW_MAPS gl_Position = - all_mat * - vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + /*all_mat*/shadowMats[0].shadowMatrices/*texture_mat*/ * + vec4(f_pos/*newRay*/, 1); + gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); +#else + gl_Position = all_mat * vec4(f_pos/*newRay*/, 1); +#endif + // gl_Position.y /= gl_Position.w; + // gl_Position.w = 1.0; + // gl_Position.z = -gl_Position.z; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z *gl_Position.w; + // gl_Position.z = gl_Position.z / 100.0; + // gl_Position.z = gl_Position.z / 10000.0; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/ui-vert.glsl b/assets/voxygen/shaders/ui-vert.glsl index 57d7230d6b..0a95e0a591 100644 --- a/assets/voxygen/shaders/ui-vert.glsl +++ b/assets/voxygen/shaders/ui-vert.glsl @@ -22,30 +22,39 @@ out vec4 f_color; void main() { f_color = v_color; + // vec2 v_pos = vec2(-1.0,1.0) * v_pos; + /* f_uv = vec2(1.0,1.0) * v_uv; */ + // vec2 v_uv = vec2(1.0,-1.0) * v_uv; + if (w_pos.w == 1.0) { f_uv = v_uv; - // Fixed scale In-game element - vec4 projected_pos = proj_mat * view_mat * vec4(w_pos.xyz, 1.0); - gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos, 0.0, 1.0); + // Fixed scale In-game element + vec4 projected_pos = /*proj_mat * view_mat*/all_mat * vec4(w_pos.xyz - focus_off.xyz, 1.0); + gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos/* * projected_pos.w*/, -1.0, /*projected_pos.w*/1.0); } else if (v_mode == uint(3)) { // HACK: North facing source rectangle. + gl_Position = vec4(v_pos, -1.0, 1.0); vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); + // TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3). + vec2 aspect_ratio = textureSize(u_tex, 0).yx; mat2 look_at = mat2(look_at_dir.y, look_at_dir.x, -look_at_dir.x, look_at_dir.y); - f_uv = v_center + look_at * (v_uv - v_center); - gl_Position = vec4(v_pos, 0.0, 1.0); + vec2 v_centered = (v_uv - v_center) / aspect_ratio; + vec2 v_rotated = look_at * v_centered; + f_uv = aspect_ratio * v_rotated + v_center; } else if (v_mode == uint(5)) { // HACK: North facing target rectangle. f_uv = v_uv; - float aspect_ratio = screen_res.x / screen_res.y; vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); + // TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3). + vec2 aspect_ratio = screen_res.yx; mat2 look_at = mat2(look_at_dir.y, -look_at_dir.x, look_at_dir.x, look_at_dir.y); - vec2 v_len = v_pos - v_center; - vec2 v_proj = look_at * vec2(v_len.x, v_len.y / aspect_ratio); - gl_Position = vec4(v_center + vec2(v_proj.x, v_proj.y * aspect_ratio), 0.0, 1.0); + vec2 v_centered = (v_pos - v_center) / aspect_ratio; + vec2 v_rotated = look_at * v_centered; + gl_Position = vec4(aspect_ratio * v_rotated + v_center, -1.0, 1.0); } else { // Interface element f_uv = v_uv; - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } f_mode = v_mode; } diff --git a/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox b/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox index 36a6062875..b982e9968d 100644 --- a/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox +++ b/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4ebef30fe578b65cb6821ca082d8a94a4bfb761466e7418c80cf5b3f8c1950c +oid sha256:686aecb4bd5f03a9957b9293439b0ace457c0ee6b39cf59134c177be7ca5dca2 size 1152 diff --git a/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox b/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox index 1fb83d3dd6..596a8b9c5f 100644 --- a/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox +++ b/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:322447258c4759eb6ddebc4eb778f5755e0a0a7bf8cbe380da991c56a85ec375 +oid sha256:e247a6519fdd7225b032dbd9c5a9aaa5529644b8dd67c47149e38972fb8f74c3 size 1144 diff --git a/assets/voxygen/voxel/humanoid_color_manifest.ron b/assets/voxygen/voxel/humanoid_color_manifest.ron new file mode 100644 index 0000000000..0ca7742fcb --- /dev/null +++ b/assets/voxygen/voxel/humanoid_color_manifest.ron @@ -0,0 +1,285 @@ +#![enable(unwrap_newtypes)] + +( + // NOTE: You can't change the legnths of these arrays without updating num_hair_colors() in + // common/src/comp/body/humanoid.rs. That's because this is a hack; we should really use enum + // variants for hair colors like we do all the other stuff. Once we fix that, this will no + // longer be something you need to worry about. + hair_colors: ( + Danari: [ + (198, 169, 113), // Philosopher's Grey + //(245, 232, 175), // Cream Blonde + //(228, 208, 147), // Gold Blonde + //(228, 223, 141), // Platinum Blonde + (176, 106, 41), // Summer Blonde + (107, 76, 51), // Oak + //(203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (107, 32, 60), // Grape Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + //(146, 32, 32), // Autumn Red + (20, 19, 17), // Black + ], + Dwarf: [ + (210, 204, 130), // Platinum Blonde + (220, 199, 119), // Cream Blonde + (212, 156, 73), // Gold Blonde + (176, 106, 41), // Summer Blonde + (216, 146, 114), // Matte Pink + (107, 76, 51), // Oak + (203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (191, 228, 254), // Ice NobleBlue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (146, 166, 172), // Matte Green + (0, 139, 58), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + Elf: [ + (66, 83, 113), // Mysterious Blue + (13, 76, 41), // Rainforest Green + (245, 232, 175), // Cream Blonde + (212, 156, 73), // Gold Blonde + (228, 223, 141), // Platinum Blonde + (176, 106, 41), // Summer Blonde + (107, 76, 51), // Oak + (203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (103, 191, 254), // Ice Blue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (80, 156, 211), // Candy Pink + (216, 146, 114), // Matte Pink + (146, 166, 172), // Matte Green + (84, 139, 107), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + Human: [ + (210, 204, 130), // Platinum Blonde + (220, 199, 119), // Cream Blonde + (212, 156, 73), // Gold Blonde + (176, 106, 41), // Summer Blonde + (216, 146, 114), // Matte Pink + (203, 200, 98), // Light + (107, 76, 51), // Oak + (64, 32, 18), // Skin7 + (86, 72, 81), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (114, 137, 211), // Ice Blue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (80, 156, 211), // Candy Pink + (146, 166, 172), // Matte Green + (84, 139, 107), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + Orc: [ + (66, 66, 59), // Wise Grey + //(107, 76, 51), // Oak + //(203, 154, 98), // Light + (64, 32, 18), // Skin7 + (54, 30, 26), // Dark Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (66, 83, 113), // Mysterious Blue + (20, 19, 17), // Black + ], + Undead: [ + //(245, 232, 175), // Cream Blonde + (228, 208, 147), // Gold Blonde + //(228, 223, 141), // Platinum Blonde + (176, 106, 41), // Summer Blonde + (107, 76, 51), // Oak + (203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (111, 54, 117), // Punky Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (103, 191, 254), // Ice Blue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (66, 66, 59), // Decayed Grey + //(80, 156, 211), // Candy Pink + (216, 146, 114), // Matte Pink + (0, 131, 122), // Rotten Green + (146, 166, 172), // Matte Green + (84, 139, 107), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + ), + eye_colors_light: ( + VigorousBlack: (71, 59, 49), + NobleBlue: (75, 158, 191), + CuriousGreen: (110, 167, 113), + LoyalBrown: (73, 42, 36), + ViciousRed: (169,0 ,47), + PumpkinOrange: (220, 156, 19), + GhastlyYellow: (221, 225, 31), + MagicPurple: (137, 4, 177), + ToxicGreen: (1, 223, 1), + ExoticPurple: (95, 32, 111), + SulfurYellow: (235, 198, 94), + AmberOrange: (137, 46, 1), + PineGreen: (0, 78, 56), + CornflowerBlue: (18, 66, 90), + ), + eye_colors_dark: ( + VigorousBlack: (32, 32, 32), + NobleBlue: (62, 130, 159), + CuriousGreen: (81, 124, 84), + LoyalBrown: (54, 30, 26), + ViciousRed: (119, 0, 33), + PumpkinOrange: (209, 145, 18), + GhastlyYellow: (205, 212, 29), + MagicPurple: (110, 3, 143), + ToxicGreen: (1, 185, 1), + ExoticPurple: (69, 23, 80), + SulfurYellow: (209, 176, 84), + AmberOrange: (112, 40, 1), + PineGreen: (0, 54, 38), + CornflowerBlue: (13, 47, 64), + ), + eye_white: (255, 255, 255), + skin_colors_plain: ( + Skin1: (228, 183, 160), + Skin2: (226, 181, 158), + Skin3: (223, 179, 157), + Skin4: (221, 177, 155), + Skin5: (218, 176, 154), + Skin6: (216, 174, 152), + Skin7: (213, 172, 151), + Skin8: (211, 170, 149), + Skin9: (198, 159, 140), + Skin10: (180, 144, 127), + Skin11: (163, 130, 114), + Skin12: (135, 103, 90), + Skin13: (120, 92, 80), + Skin14: (105, 80, 70), + Skin15: (90, 69, 60), + Skin16: (75, 57, 50), + Skin17: (60, 46, 40), + Skin18: (45, 34, 30), + Iron: (135, 113, 95), + Steel: (108, 94, 86), + DanariOne: (43, 166, 224), + DanariTwo: (40, 155, 210), + DanariThree: (37, 143, 195), + DanariFour: (34, 132, 181), + ElfOne: (118, 84, 157), + ElfTwo: (99, 114, 161), + // ElfThree: (230, 188, 198), + OrcOne: (61, 130, 42), + OrcTwo: (82, 117, 36), + OrcThree: (71, 94, 42), + OrcFour: (97, 54, 29), + UndeadOne: (178, 178, 178), + UndeadTwo: (162, 157, 150), + UndeadThree: (145, 135, 121), + ), + skin_colors_light: ( + Skin1: (233, 190, 166), + Skin2: (232, 188, 164), + Skin3: (229, 186, 163), + Skin4: (227, 184, 161), + Skin5: (224, 183, 159), + Skin6: (222, 181, 157), + Skin7: (220, 178, 156), + Skin8: (218, 176, 154), + Skin9: (205, 165, 145), + Skin10: (187, 149, 131), + Skin11: (169, 134, 118), + Skin12: (135, 103, 90), + Skin13: (120, 92, 80), + Skin14: (105, 80, 70), + Skin15: (90, 69, 60), + Skin16: (75, 57, 50), + Skin17: (60, 46, 40), + Skin18: (45, 34, 30), + Iron: (144, 125, 106), + Steel: (120, 107, 99), + DanariOne: (44, 172, 230), + DanariTwo: (41, 161, 217), + DanariThree: (38, 148, 202), + DanariFour: (35, 136, 188), + ElfOne: (122, 87, 163), + ElfTwo: (102, 118, 167), + //ElfThree: (242, 199, 209), + OrcOne: (83, 165, 56), + OrcTwo: (92, 132, 46), + OrcThree: (84, 110, 54), + OrcFour: (97, 54, 29), + UndeadOne: (185, 185, 185), + UndeadTwo: (168, 163, 155), + UndeadThree: (150, 139, 125), + ), + skin_colors_dark: ( + Skin1: (222, 176, 154), + Skin2: (220, 174, 153), + Skin3: (217, 172, 152), + Skin4: (214, 171, 150), + Skin5: (211, 170, 149), + Skin6: (209, 168, 147), + Skin7: (206, 166, 146), + Skin8: (204, 164, 144), + Skin9: (191, 154, 136), + Skin10: (173, 139, 123), + Skin11: (157, 126, 110), + Skin12: (132, 103, 82), + Skin13: (107, 82, 72), + Skin14: (92, 70, 62), + Skin15: (77, 59, 51), + Skin16: (61, 47, 41), + Skin17: (48, 37, 32), + Skin18: (33, 25, 22), + Iron: (124, 99, 82), + Steel: (96, 81, 72), + DanariOne: (43, 166, 224), + DanariTwo: (40, 155, 210), + DanariThree: (37, 143, 195), + DanariFour: (34, 132, 181), + ElfOne: (114, 81, 152), + ElfTwo: (96, 110, 155), + //ElfThree: (217, 178, 187), + OrcOne: (55, 114, 36), + OrcTwo: (70, 104, 29), + OrcThree: (60, 83, 32), + OrcFour: (84, 47, 25), + UndeadOne: (172, 172, 172), + UndeadTwo: (156, 152, 145), + UndeadThree: (128, 119, 107), + ), +) diff --git a/assets/voxygen/voxel/sprite/pumpkin/1.vox b/assets/voxygen/voxel/sprite/pumpkin/1.vox index e04c596c6d..862306e4e3 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/1.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0580c2e316710f88f1bb7a20462e64ad44aa6a47b3b58069d1d6b1015bdec06 +oid sha256:049cec7bd63a666ee7ce53cb4b515890204be79368b81726622e03f5e5d85e9e size 2568 diff --git a/assets/voxygen/voxel/sprite/pumpkin/2.vox b/assets/voxygen/voxel/sprite/pumpkin/2.vox index 2cb8808644..80c74e5046 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/2.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/2.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2616a9c1e18ec29006d0fce787957cb9c19aab3a25b2b360f0dd490515ad57d3 +oid sha256:956f9160ccf98ef2a07f9840ec0d74727179181cb47ce2c9bf28751c21624664 size 2612 diff --git a/assets/voxygen/voxel/sprite/pumpkin/3.vox b/assets/voxygen/voxel/sprite/pumpkin/3.vox index ce49f8bbed..69bf0537ea 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/3.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/3.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f338dd3b94bb36a7f36e5272e847e83bfa9317daf3f7b8535e73ea39842ef1b6 +oid sha256:730f8184875a8589d450bdebb875c874dc57423b42ebc0ab1440f99117885510 size 2580 diff --git a/assets/voxygen/voxel/sprite/pumpkin/4.vox b/assets/voxygen/voxel/sprite/pumpkin/4.vox index ebe4c384a3..cd625d2969 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/4.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/4.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e65e3d6c3bda693b7ef21a86aac13f7a4fa87a09a1b731e58b13369e75751d7 +oid sha256:a81c85e279dd1f291a20261a4043bf4b533c99ea801851052855daf707651fdb size 2592 diff --git a/assets/voxygen/voxel/sprite/pumpkin/5.vox b/assets/voxygen/voxel/sprite/pumpkin/5.vox index 1ebaedd451..a05f24b96d 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/5.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/5.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d96e0c99bd97e8a16d74f6419d6c5ea4740b5fbf82f7d61835f68dfb75debc87 +oid sha256:9803e6c26173ad1b72e269752041f6e14076bbefaa7686d162d10fe57c58cf47 size 2616 diff --git a/assets/voxygen/voxel/sprite/pumpkin/6.vox b/assets/voxygen/voxel/sprite/pumpkin/6.vox index 6e2e3bf480..e35f75668b 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/6.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/6.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0abc7fd43fbb5cfbe6ff4a341a94d40f09693164a0d35c480f297ff1bae5eae +oid sha256:1415d7c7594b9e772a0ccedc25996e24dd3eadb599e9727e713258c80811f67f size 4668 diff --git a/assets/voxygen/voxel/sprite/pumpkin/7.vox b/assets/voxygen/voxel/sprite/pumpkin/7.vox index 3842695cce..d750d82d6d 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/7.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/7.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f965826e3c5d890d32cdc1de331ae60e6d35e672cc6f44a4dd407268f9e66ab +oid sha256:ec16bd5be9598f7b085b01737711877720560f15780afadff20da1fb6b129647 size 4740 diff --git a/assets/world/style/colors.ron b/assets/world/style/colors.ron new file mode 100644 index 0000000000..bc03c21078 --- /dev/null +++ b/assets/world/style/colors.ron @@ -0,0 +1,152 @@ +#![enable(unwrap_newtypes)] +#![enable(implicit_some)] + +// NOTE: Many of these colors are not used directly, but are modified in various ways (e.g. via +// lerping). So don't be too frustrated if a color change seems to have a different effect in +// different places; just follow the trends. +( + block: ( + pyramid: (203, 170, 146), + + // These are all ranges from low to high. + structure_blocks: ( + None: None, + // Samples the surface color. + Grass: None, + // Water blocks ignore color information, and even if they didn't would not be lerped. + Water: None, + GreenSludge: None, + // Leaves all actually get interpolated. + TemperateLeaves: (start: (0, 132, 94), end: (142, 181, 0)), + PineLeaves: (start: (0, 60, 50), end: (30, 100, 10)), + PalmLeavesInner: (start: (61, 166, 43), end: (29, 130, 32)), + PalmLeavesOuter: (start: (62, 171, 38), end: (45, 171, 65)), + Acacia: (start: (15, 126, 50), end: (30, 180, 10)), + Liana: (start: (0, 125, 107), end: (0, 155, 129)), + Mangrove: (start: (32, 56, 22), end: (57, 69, 27)), + ) + + // Water blocks ignore color now so this isn't used, but just in case this color was worth + // remembering here it is. + // green_sludge: (30.0, 126.0, 23.0) + ), + column: ( + cold_grass: (0.0, 0.5, 0.25), + warm_grass: (0.4, 0.8, 0.0), + dark_grass: (0.15, 0.4, 0.1), + wet_grass: (0.1, 0.8, 0.2), + cold_stone: (0.57, 0.67, 0.8), + hot_stone: (0.07, 0.07, 0.06), + warm_stone: (0.77, 0.77, 0.64), + beach_sand: (0.8, 0.75, 0.5), + desert_sand: (0.7, 0.4, 0.25), + snow: (0.8, 0.85, 1.0), + + stone_col: (195, 187, 201), + + dirt_low: (0.075, 0.07, 0.3), + dirt_high: (0.75, 0.55, 0.1), + + snow_high: (0.01, 0.3, 0.0), + warm_stone_high: (0.3, 0.12, 0.2), + + grass_high: (0.15, 0.2, 0.15), + tropical_high: (0.87, 0.62, 0.56), + ), + // NOTE: I think (but am not sure) that this is the color of stuff below the bottom-most + // ground. I'm not sure how easy it is to see. + deep_stone_color: (125, 120, 130), + layer: ( + bridge: (80, 80, 100), + stalagtite: (200, 200, 200), + ), + site: ( + castle: (), + dungeon: ( + stone: (150, 150, 175), + ), + settlement: ( + building: ( + archetype: ( + keep: ( + brick_base: (80, 80, 80), + floor_base: (80, 60, 10), + pole: (90, 70, 50), + flag: ( + Evil: (80, 10, 130), + Good: (200, 80, 40), + ), + stone: ( + Evil: (65, 60, 55), + Good: (100, 100, 110), + ), + ), + house: ( + foundation: (100, 100, 100), + floor: (100, 75, 50), + roof: ( + Roof1: (0x99, 0x5E, 0x54), + Roof2: (0x43, 0x63, 0x64), + Roof3: (0x76, 0x6D, 0x68), + Roof4: (0x7B, 0x41, 0x61), + Roof5: (0x52, 0x20, 0x20), + Roof6: (0x1A, 0x4A, 0x59), + Roof7: (0xCC, 0x76, 0x4E), + // (0x1D, 0x4D, 0x45), + // (0xB3, 0x7D, 0x60), + // (0xAC, 0x5D, 0x26), + // (0x32, 0x46, 0x6B), + // (0x2B, 0x19, 0x0F), + // (0x93, 0x78, 0x51), + // (0x92, 0x57, 0x24), + // (0x4A, 0x4E, 0x4E), + // (0x2F, 0x32, 0x47), + // (0x8F, 0x35, 0x43), + // (0x6D, 0x1E, 0x3A), + // (0x6D, 0xA7, 0x80), + // (0x4F, 0xA0, 0x95), + // (0xE2, 0xB9, 0x99), + // (0x7A, 0x30, 0x22), + // (0x4A, 0x06, 0x08), + // (0x8E, 0xB4, 0x57), + ), + wall: ( + Wall1: (200, 180, 150), + Wall2: (0xB8, 0xB4, 0xA4), + Wall3: (0x76, 0x6D, 0x68), + Wall4: (0xF3, 0xC9, 0x8F), + Wall5: (0xD3, 0xB7, 0x99), + Wall6: (0xE1, 0xAB, 0x91), + Wall7: (0x82, 0x57, 0x4C), + Wall8: (0xB9, 0x96, 0x77), + Wall9: (0xAE, 0x8D, 0x9C), + ), + support: ( + Support1: (60, 45, 30), + Support2: (0x65, 0x55, 0x56), + Support3: (0x53, 0x33, 0x13), + Support4: (0x58, 0x42, 0x33), + ), + ), + ), + ), + plot_town_path: (100, 95, 65), + + plot_field_dirt: (80, 55, 35), + plot_field_mound: (70, 80, 30), + + wall_low: (130, 100, 0), + wall_high :(90, 70, 50), + + tower_color: (50, 50, 50), + + // NOTE: Ideally these would be part of a make_case_elim, but we can't use it beacuse + // it doesn't support struct variants yet. + plot_dirt: (90, 70, 50), + plot_grass: (100, 200, 0), + plot_water: (100, 150, 250), + plot_town: (150, 110, 60), + // TODO: Add field furrow stuff. + ), + ), +) diff --git a/assets/world/tree/acacia/1.vox b/assets/world/tree/acacia/1.vox index 310198c54f..1e34877f5c 100644 --- a/assets/world/tree/acacia/1.vox +++ b/assets/world/tree/acacia/1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e1fee1ea15e891aa424ea8054f5ea5bb02f1e6991c771d4191f0e655aba109b -size 22600 +oid sha256:e1b58989ac01175fba006ec70815959a8cdc7fe3035c43f3464a06afd5f65f98 +size 8360 diff --git a/assets/world/tree/acacia/2.vox b/assets/world/tree/acacia/2.vox index 08cb8d7e49..b7719a4cec 100644 --- a/assets/world/tree/acacia/2.vox +++ b/assets/world/tree/acacia/2.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:976e990871ab219ca3023eaaad8d7e92a5ed4823555b819dabce6c60581a922f -size 2160 +oid sha256:5b17758334983bbd97eef5a66b64c1828bee6df480b745ed2e47e046c047f10e +size 1420 diff --git a/assets/world/tree/acacia/3.vox b/assets/world/tree/acacia/3.vox index cb06009f12..d0c14bc134 100644 --- a/assets/world/tree/acacia/3.vox +++ b/assets/world/tree/acacia/3.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d9bb5949defcad8260a0293a6e42429440dfff4833edfa9a04caf06ef5895e3 -size 3068 +oid sha256:adc156f886d8fddc6c456ec85c21c0561aee7fa5f51a1a9b074b704e3c0b92ad +size 1688 diff --git a/assets/world/tree/acacia/4.vox b/assets/world/tree/acacia/4.vox index 30b2e0efde..ebeb422216 100644 --- a/assets/world/tree/acacia/4.vox +++ b/assets/world/tree/acacia/4.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2071db98db659494ee497e7ed728c3cbf838fb7f02594b5255417e5e13e182ac -size 17712 +oid sha256:ece352f8ce6034fd20efca429b46426a66a7a2e637a6ec82b8e718af14ceb547 +size 8088 diff --git a/assets/world/tree/acacia/5.vox b/assets/world/tree/acacia/5.vox index 9cf8015236..77c24923f8 100644 --- a/assets/world/tree/acacia/5.vox +++ b/assets/world/tree/acacia/5.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fc04f31dd7fb85219500d6145233663e6f1a040aed8b4acc84585e626c10b82 -size 33720 +oid sha256:c030cffb4f8f5f908a70046bef904daa4e065d78f718ec22579a05139fba4103 +size 8564 diff --git a/assets/world/tree/acacia_3/1.vox b/assets/world/tree/acacia_3/1.vox new file mode 100644 index 0000000000..310198c54f --- /dev/null +++ b/assets/world/tree/acacia_3/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e1fee1ea15e891aa424ea8054f5ea5bb02f1e6991c771d4191f0e655aba109b +size 22600 diff --git a/assets/world/tree/acacia_3/2.vox b/assets/world/tree/acacia_3/2.vox new file mode 100644 index 0000000000..08cb8d7e49 --- /dev/null +++ b/assets/world/tree/acacia_3/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:976e990871ab219ca3023eaaad8d7e92a5ed4823555b819dabce6c60581a922f +size 2160 diff --git a/assets/world/tree/acacia_3/3.vox b/assets/world/tree/acacia_3/3.vox new file mode 100644 index 0000000000..cb06009f12 --- /dev/null +++ b/assets/world/tree/acacia_3/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d9bb5949defcad8260a0293a6e42429440dfff4833edfa9a04caf06ef5895e3 +size 3068 diff --git a/assets/world/tree/acacia_3/4.vox b/assets/world/tree/acacia_3/4.vox new file mode 100644 index 0000000000..30b2e0efde --- /dev/null +++ b/assets/world/tree/acacia_3/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2071db98db659494ee497e7ed728c3cbf838fb7f02594b5255417e5e13e182ac +size 17712 diff --git a/assets/world/tree/acacia_3/5.vox b/assets/world/tree/acacia_3/5.vox new file mode 100644 index 0000000000..9cf8015236 --- /dev/null +++ b/assets/world/tree/acacia_3/5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fc04f31dd7fb85219500d6145233663e6f1a040aed8b4acc84585e626c10b82 +size 33720 diff --git a/assets/world/tree/acacia_savannah/1.vox b/assets/world/tree/acacia_savannah/1.vox deleted file mode 100644 index 1e34877f5c..0000000000 --- a/assets/world/tree/acacia_savannah/1.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e1b58989ac01175fba006ec70815959a8cdc7fe3035c43f3464a06afd5f65f98 -size 8360 diff --git a/assets/world/tree/acacia_savannah/2.vox b/assets/world/tree/acacia_savannah/2.vox deleted file mode 100644 index b7719a4cec..0000000000 --- a/assets/world/tree/acacia_savannah/2.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b17758334983bbd97eef5a66b64c1828bee6df480b745ed2e47e046c047f10e -size 1420 diff --git a/assets/world/tree/acacia_savannah/3.vox b/assets/world/tree/acacia_savannah/3.vox deleted file mode 100644 index d0c14bc134..0000000000 --- a/assets/world/tree/acacia_savannah/3.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:adc156f886d8fddc6c456ec85c21c0561aee7fa5f51a1a9b074b704e3c0b92ad -size 1688 diff --git a/assets/world/tree/acacia_savannah/4.vox b/assets/world/tree/acacia_savannah/4.vox deleted file mode 100644 index ebeb422216..0000000000 --- a/assets/world/tree/acacia_savannah/4.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ece352f8ce6034fd20efca429b46426a66a7a2e637a6ec82b8e718af14ceb547 -size 8088 diff --git a/assets/world/tree/acacia_savannah/5.vox b/assets/world/tree/acacia_savannah/5.vox deleted file mode 100644 index 77c24923f8..0000000000 --- a/assets/world/tree/acacia_savannah/5.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c030cffb4f8f5f908a70046bef904daa4e065d78f718ec22579a05139fba4103 -size 8564 diff --git a/client/Cargo.toml b/client/Cargo.toml index a78c32154e..70733d28dd 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -13,10 +13,12 @@ uvth = "3.1.1" futures-util = "0.3" futures-executor = "0.3" futures-timer = "2.0" -image = { version = "0.22.5", default-features = false, features = ["png"] } +image = { version = "0.23.8", default-features = false, features = ["png"] } +num = "0.2.0" num_cpus = "1.10.1" tracing = { version = "0.1", default-features = false } +rayon = "^1.3.0" specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5" } -vek = { version = "0.11.0", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] } authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" } diff --git a/client/src/lib.rs b/client/src/lib.rs index fe71cde3e5..1c5e28bf6c 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -29,7 +29,7 @@ use common::{ recipe::RecipeBook, state::State, sync::{Uid, UidAllocator, WorldSyncExt}, - terrain::{block::Block, TerrainChunk, TerrainChunkSize}, + terrain::{block::Block, neighbors, TerrainChunk, TerrainChunkSize}, vol::RectVolSize, }; use futures_executor::block_on; @@ -40,6 +40,8 @@ use image::DynamicImage; use network::{ Network, Participant, Pid, ProtocolAddr, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED, }; +use num::traits::FloatConst; +use rayon::prelude::*; use std::{ collections::VecDeque, net::SocketAddr, @@ -74,7 +76,28 @@ pub struct Client { client_state: ClientState, thread_pool: ThreadPool, pub server_info: ServerInfo, - pub world_map: (Arc, Vec2), + /// Just the "base" layer for LOD; currently includes colors and nothing + /// else. In the future we'll add more layers, like shadows, rivers, and + /// probably foliage, cities, roads, and other structures. + pub lod_base: Vec, + /// The "height" layer for LOD; currently includes only land altitudes, but + /// in the future should also water depth, and probably other + /// information as well. + pub lod_alt: Vec, + /// The "shadow" layer for LOD. Includes east and west horizon angles and + /// an approximate max occluder height, which we use to try to + /// approximate soft and volumetric shadows. + pub lod_horizon: Vec, + /// A fully rendered map image for use with the map and minimap; note that + /// this can be constructed dynamically by combining the layers of world + /// map data (e.g. with shadow map data or river data), but at present + /// we opt not to do this. + /// + /// The second element of the tuple is the world size (as a 2D grid, + /// in chunks), and the third element holds the minimum height for any land + /// chunk (i.e. the sea level) in its x coordinate, and the maximum land + /// height above this height (i.e. the max height) in its y coordinate. + pub world_map: (Arc, Vec2, Vec2), pub player_list: HashMap, pub character_list: CharacterList, pub active_character_id: Option, @@ -130,84 +153,217 @@ impl Client { // We reduce the thread count by 1 to keep rendering smooth thread_pool.set_num_threads((num_cpus::get() - 1).max(1)); - let (network, f) = Network::new(Pid::new()); - thread_pool.execute(f); + let (network, scheduler) = Network::new(Pid::new()); + thread_pool.execute(scheduler); let participant = block_on(network.connect(ProtocolAddr::Tcp(addr.into())))?; let mut stream = block_on(participant.open(10, PROMISES_ORDERED | PROMISES_CONSISTENCY))?; // Wait for initial sync - let (state, entity, server_info, world_map, recipe_book, max_group_size) = block_on( - async { - loop { - match stream.recv().await? { - ServerMsg::InitialSync { - entity_package, - server_info, - time_of_day, - max_group_size, - world_map: (map_size, world_map), - recipe_book, - } => { - // TODO: Display that versions don't match in Voxygen - if server_info.git_hash != *common::util::GIT_HASH { - warn!( - "Server is running {}[{}], you are running {}[{}], versions \ - might be incompatible!", - server_info.git_hash, - server_info.git_date, - common::util::GIT_HASH.to_string(), - common::util::GIT_DATE.to_string(), + let ( + state, + entity, + server_info, + lod_base, + lod_alt, + lod_horizon, + world_map, + recipe_book, + max_group_size, + ) = block_on(async { + loop { + match stream.recv().await? { + ServerMsg::InitialSync { + entity_package, + server_info, + time_of_day, + max_group_size, + world_map, + recipe_book, + } => { + // TODO: Display that versions don't match in Voxygen + if server_info.git_hash != *common::util::GIT_HASH { + warn!( + "Server is running {}[{}], you are running {}[{}], versions might \ + be incompatible!", + server_info.git_hash, + server_info.git_date, + common::util::GIT_HASH.to_string(), + common::util::GIT_DATE.to_string(), + ); + } + + debug!("Auth Server: {:?}", server_info.auth_provider); + + // Initialize `State` + let mut state = State::default(); + // Client-only components + state + .ecs_mut() + .register::>(); + + let entity = state.ecs_mut().apply_entity_package(entity_package); + *state.ecs_mut().write_resource() = time_of_day; + + let map_size_lg = common::terrain::MapSizeLg::new(world_map.dimensions_lg) + .map_err(|_| { + Error::Other(format!( + "Server sent bad world map dimensions: {:?}", + world_map.dimensions_lg, + )) + })?; + let map_size = map_size_lg.chunks(); + let max_height = world_map.max_height; + let sea_level = world_map.sea_level; + let rgba = world_map.rgba; + let alt = world_map.alt; + let expected_size = + (u32::from(map_size.x) * u32::from(map_size.y)) as usize; + if rgba.len() != expected_size { + return Err(Error::Other("Server sent a bad world map image".into())); + } + if alt.len() != expected_size { + return Err(Error::Other("Server sent a bad altitude map.".into())); + } + let [west, east] = world_map.horizons; + let scale_angle = + |a: u8| (a as f32 / 255.0 * ::FRAC_PI_2()).tan(); + let scale_height = |h: u8| h as f32 / 255.0 * max_height; + let scale_height_big = |h: u32| (h >> 3) as f32 / 8191.0 * max_height; + + debug!("Preparing image..."); + let unzip_horizons = |(angles, heights): &(Vec<_>, Vec<_>)| { + ( + angles.iter().copied().map(scale_angle).collect::>(), + heights + .iter() + .copied() + .map(scale_height) + .collect::>(), + ) + }; + let horizons = [unzip_horizons(&west), unzip_horizons(&east)]; + + // Redraw map (with shadows this time). + let mut world_map = vec![0u32; rgba.len()]; + let mut map_config = common::terrain::map::MapConfig::orthographic( + map_size_lg, + core::ops::RangeInclusive::new(0.0, max_height), + ); + map_config.horizons = Some(&horizons); + let rescale_height = |h: f32| h / max_height; + let bounds_check = |pos: Vec2| { + pos.reduce_partial_min() >= 0 + && pos.x < map_size.x as i32 + && pos.y < map_size.y as i32 + }; + map_config.generate( + |pos| { + let (rgba, alt, downhill_wpos) = if bounds_check(pos) { + let posi = + pos.y as usize * map_size.x as usize + pos.x as usize; + let [r, g, b, a] = rgba[posi].to_le_bytes(); + let alti = alt[posi]; + // Compute downhill. + let downhill = { + let mut best = -1; + let mut besth = alti; + for nposi in neighbors(map_size_lg, posi) { + let nbh = alt[nposi]; + if nbh < besth { + besth = nbh; + best = nposi as isize; + } + } + best + }; + let downhill_wpos = if downhill < 0 { + None + } else { + Some( + Vec2::new( + (downhill as usize % map_size.x as usize) as i32, + (downhill as usize / map_size.x as usize) as i32, + ) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ) + }; + (Rgba::new(r, g, b, a), alti, downhill_wpos) + } else { + (Rgba::zero(), 0, None) + }; + let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let downhill_wpos = downhill_wpos.unwrap_or( + wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32), ); - } - - debug!("Auth Server: {:?}", server_info.auth_provider); - - // Initialize `State` - let mut state = State::default(); - // Client-only components - state - .ecs_mut() - .register::>(); - - let entity = state.ecs_mut().apply_entity_package(entity_package); - *state.ecs_mut().write_resource() = time_of_day; - - assert_eq!(world_map.len(), (map_size.x * map_size.y) as usize); - let mut world_map_raw = - vec![0u8; 4 * world_map.len()/*map_size.x * map_size.y*/]; - LittleEndian::write_u32_into(&world_map, &mut world_map_raw); - debug!("Preparing image..."); - let world_map = Arc::new( + let alt = rescale_height(scale_height_big(alt)); + common::terrain::map::MapSample { + rgb: Rgb::from(rgba), + alt: f64::from(alt), + downhill_wpos, + connections: None, + } + }, + |wpos| { + let pos = + wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + rescale_height(if bounds_check(pos) { + let posi = + pos.y as usize * map_size.x as usize + pos.x as usize; + scale_height_big(alt[posi]) + } else { + 0.0 + }) + }, + |pos, (r, g, b, a)| { + world_map[pos.y * map_size.x as usize + pos.x] = + u32::from_le_bytes([r, g, b, a]); + }, + ); + let make_raw = |rgba| -> Result<_, Error> { + let mut raw = vec![0u8; 4 * world_map.len()]; + LittleEndian::write_u32_into(rgba, &mut raw); + Ok(Arc::new( image::DynamicImage::ImageRgba8({ // Should not fail if the dimensions are correct. - let world_map = - image::ImageBuffer::from_raw(map_size.x, map_size.y, world_map_raw); - world_map.ok_or_else(|| Error::Other("Server sent a bad world map image".into()))? + let map = + image::ImageBuffer::from_raw(u32::from(map_size.x), u32::from(map_size.y), raw); + map.ok_or_else(|| Error::Other("Server sent a bad world map image".into()))? }) - // Flip the image, since Voxygen uses an orientation where rotation from - // positive x axis to positive y axis is counterclockwise around the z axis. - .flipv(), - ); - debug!("Done preparing image..."); + // Flip the image, since Voxygen uses an orientation where rotation from + // positive x axis to positive y axis is counterclockwise around the z axis. + .flipv(), + )) + }; + let lod_base = rgba; + let lod_alt = alt; + let world_map = make_raw(&world_map)?; + let horizons = (west.0, west.1, east.0, east.1) + .into_par_iter() + .map(|(wa, wh, ea, eh)| u32::from_le_bytes([wa, wh, ea, eh])) + .collect::>(); + let lod_horizon = horizons; + let map_bounds = Vec2::new(sea_level, max_height); + debug!("Done preparing image..."); - break Ok(( - state, - entity, - server_info, - (world_map, map_size), - recipe_book, - max_group_size, - )); - }, - ServerMsg::TooManyPlayers => break Err(Error::TooManyPlayers), - err => { - warn!("whoops, server mad {:?}, ignoring", err); - }, - } + break Ok(( + state, + entity, + server_info, + lod_base, + lod_alt, + lod_horizon, + (world_map, map_size, map_bounds), + recipe_book, + max_group_size, + )); + }, + ServerMsg::TooManyPlayers => break Err(Error::TooManyPlayers), + err => { + warn!("whoops, server mad {:?}, ignoring", err); + }, } - }, - )?; + } + })?; stream.send(ClientMsg::Ping)?; @@ -222,6 +378,9 @@ impl Client { thread_pool, server_info, world_map, + lod_base, + lod_alt, + lod_horizon, player_list: HashMap::new(), character_list: CharacterList::default(), active_character_id: None, diff --git a/common/Cargo.toml b/common/Cargo.toml index 9dd2828065..d9467ee3dc 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -11,12 +11,14 @@ no-assets = [] arraygen = "0.1.13" specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" } +roots = "0.0.6" specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" } -vek = { version = "0.11.0", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } dot_vox = "4.0" -image = { version = "0.22.5", default-features = false, features = ["png"] } +image = { version = "0.23.8", default-features = false, features = ["png"] } serde = { version = "1.0.110", features = ["derive"] } serde_json = "1.0.50" +serde_repr = "0.1.6" ron = { version = "0.6", default-features = false } tracing = { version = "0.1", default-features = false } rand = "0.7" diff --git a/common/src/assets/watch.rs b/common/src/assets/watch.rs index f425c106f1..c5e8739f85 100644 --- a/common/src/assets/watch.rs +++ b/common/src/assets/watch.rs @@ -170,5 +170,13 @@ impl ReloadIndicator { } // Returns true if the watched file was changed - pub fn reloaded(&self) -> bool { self.reloaded.swap(false, Ordering::Acquire) } + pub fn reloaded(&self) -> bool { + // Optimize for the common case by performing an initial relaxed read, avoiding + // the atomic write. + if self.reloaded.load(Ordering::Relaxed) { + self.reloaded.swap(false, Ordering::Acquire) + } else { + false + } + } } diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index f0458c4b17..6661bcc766 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -14,6 +14,7 @@ pub mod quadruped_small; use crate::{ assets::{self, Asset}, + make_case_elim, npc::NpcKind, }; use serde::{Deserialize, Serialize}; @@ -21,23 +22,26 @@ use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; use std::{fs::File, io::BufReader}; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Body { - Humanoid(humanoid::Body) = 0, - QuadrupedSmall(quadruped_small::Body) = 1, - QuadrupedMedium(quadruped_medium::Body) = 2, - BirdMedium(bird_medium::Body) = 3, - FishMedium(fish_medium::Body) = 4, - Dragon(dragon::Body) = 5, - BirdSmall(bird_small::Body) = 6, - FishSmall(fish_small::Body) = 7, - BipedLarge(biped_large::Body) = 8, - Object(object::Body) = 9, - Golem(golem::Body) = 10, - Critter(critter::Body) = 11, - QuadrupedLow(quadruped_low::Body) = 12, -} +make_case_elim!( + body, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Body { + Humanoid(body: humanoid::Body) = 0, + QuadrupedSmall(body: quadruped_small::Body) = 1, + QuadrupedMedium(body: quadruped_medium::Body) = 2, + BirdMedium(body: bird_medium::Body) = 3, + FishMedium(body: fish_medium::Body) = 4, + Dragon(body: dragon::Body) = 5, + BirdSmall(body: bird_small::Body) = 6, + FishSmall(body: fish_small::Body) = 7, + BipedLarge(body: biped_large::Body)= 8, + Object(body: object::Body) = 9, + Golem(body: golem::Body) = 10, + Critter(body: critter::Body) = 11, + QuadrupedLow(body: quadruped_low::Body) = 12, + } +); /// Data representing data generic to the body together with per-species data. /// @@ -60,10 +64,14 @@ pub struct AllBodies { pub quadruped_small: BodyData>, pub quadruped_medium: BodyData>, pub bird_medium: BodyData>, + pub fish_medium: BodyData, + pub dragon: BodyData>, + pub bird_small: BodyData, + pub fish_small: BodyData, pub biped_large: BodyData>, + pub object: BodyData, pub golem: BodyData>, pub critter: BodyData>, - pub dragon: BodyData>, pub quadruped_low: BodyData>, } @@ -87,6 +95,30 @@ impl core::ops::Index for AllBodies core::ops::Index<&'a Body> for AllBodies { + type Output = BodyMeta; + + #[inline] + fn index(&self, index: &Body) -> &Self::Output { + match index { + Body::Humanoid(_) => &self.humanoid.body, + Body::QuadrupedSmall(_) => &self.quadruped_small.body, + Body::QuadrupedMedium(_) => &self.quadruped_medium.body, + Body::BirdMedium(_) => &self.bird_medium.body, + Body::FishMedium(_) => &self.fish_medium.body, + Body::Dragon(_) => &self.dragon.body, + Body::BirdSmall(_) => &self.bird_small.body, + Body::FishSmall(_) => &self.fish_small.body, + Body::BipedLarge(_) => &self.biped_large.body, + Body::Object(_) => &self.object.body, + Body::Golem(_) => &self.golem.body, + Body::Critter(_) => &self.critter.body, + Body::QuadrupedLow(_) => &self.quadruped_low.body, + } + } +} + impl< BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>, SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>, diff --git a/common/src/comp/body/humanoid.rs b/common/src/comp/body/humanoid.rs index ff1276da72..59873b44f7 100644 --- a/common/src/comp/body/humanoid.rs +++ b/common/src/comp/body/humanoid.rs @@ -1,6 +1,7 @@ +use crate::make_case_elim; use rand::{seq::SliceRandom, thread_rng, Rng}; use serde::{Deserialize, Serialize}; -use vek::Rgb; +use serde_repr::{Deserialize_repr, Serialize_repr}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Body { @@ -58,16 +59,19 @@ impl From for super::Body { fn from(body: Body) -> Self { super::Body::Humanoid(body) } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Species { - Danari = 0, - Dwarf = 1, - Elf = 2, - Human = 3, - Orc = 4, - Undead = 5, -} +make_case_elim!( + species, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Species { + Danari = 0, + Dwarf = 1, + Elf = 2, + Human = 3, + Orc = 4, + Undead = 5, + } +); /// Data representing per-species generic data. /// @@ -114,142 +118,6 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() } } -// Hair Colors -pub const DANARI_HAIR_COLORS: [(u8, u8, u8); 12] = [ - (198, 169, 113), // Philosopher's Grey - //(245, 232, 175), // Cream Blonde - //(228, 208, 147), // Gold Blonde - //(228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - //(203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (107, 32, 60), // Grape Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - //(146, 32, 32), // Autumn Red - (20, 19, 17), // Black -]; -pub const DWARF_HAIR_COLORS: [(u8, u8, u8); 21] = [ - (245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - (228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice NobleBlue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (174, 148, 161), // Matte Pink - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; -pub const ELF_HAIR_COLORS: [(u8, u8, u8); 24] = [ - (66, 83, 113), // Mysterious Blue - (13, 76, 41), // Rainforest Green - (245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - (228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice Blue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (224, 182, 184), // Candy Pink - (174, 148, 161), // Matte Pink - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; -pub const HUMAN_HAIR_COLORS: [(u8, u8, u8); 22] = [ - (245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - (228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice Blue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (224, 182, 184), // Candy Pink - (174, 148, 161), // Matte Pink - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; -pub const ORC_HAIR_COLORS: [(u8, u8, u8); 11] = [ - (66, 66, 59), // Wise Grey - //(107, 76, 51), // Oak Skin4 - //(203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (54, 30, 26), // Dark Skin7 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (66, 83, 113), // Mysterious Blue - (20, 19, 17), // Black -]; -pub const UNDEAD_HAIR_COLORS: [(u8, u8, u8); 22] = [ - //(245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - //(228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (111, 54, 117), // Punky Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice Blue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (66, 66, 59), // Decayed Grey - //(224, 182, 184), // Candy Pink - (174, 148, 161), // Matte Pink - (0, 131, 122), // Rotten Green - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; - // Skin colors pub const DANARI_SKIN_COLORS: [Skin; 4] = [ Skin::DanariOne, @@ -352,17 +220,6 @@ pub const UNDEAD_EYE_COLORS: [EyeColor; 5] = [ ]; impl Species { - fn hair_colors(self) -> &'static [(u8, u8, u8)] { - match self { - Species::Danari => &DANARI_HAIR_COLORS, - Species::Dwarf => &DWARF_HAIR_COLORS, - Species::Elf => &ELF_HAIR_COLORS, - Species::Human => &HUMAN_HAIR_COLORS, - Species::Orc => &ORC_HAIR_COLORS, - Species::Undead => &UNDEAD_HAIR_COLORS, - } - } - fn skin_colors(self) -> &'static [Skin] { match self { Species::Danari => &DANARI_SKIN_COLORS, @@ -385,16 +242,22 @@ impl Species { } } - pub fn hair_color(self, val: u8) -> Rgb { - self.hair_colors() - .get(val as usize) - .copied() - .unwrap_or((0, 0, 0)) - .into() + /// FIXME: This is a hack! The only reason we need to do this is because + /// hair colors are currently just indices into an array, not enum + /// variants. Once we have proper variants for hair colors, we won't + /// need to do this anymore, since we will use locally defined arrays to + /// represent per-species stuff (or have some other solution for validity). + pub fn num_hair_colors(self) -> u8 { + match self { + Species::Danari => 12, + Species::Dwarf => 21, + Species::Elf => 24, + Species::Human => 22, + Species::Orc => 11, + Species::Undead => 22, + } } - pub fn num_hair_colors(self) -> u8 { self.hair_colors().len() as u8 } - pub fn skin_color(self, val: u8) -> Skin { self.skin_colors() .get(val as usize) @@ -484,238 +347,78 @@ impl Species { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum BodyType { - Female = 0, - Male = 1, -} +make_case_elim!( + body_type, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum BodyType { + Female = 0, + Male = 1, + } +); + pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male]; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum EyeColor { - VigorousBlack = 0, - NobleBlue = 1, - CuriousGreen = 2, - LoyalBrown = 3, - ViciousRed = 4, - PumpkinOrange = 5, - GhastlyYellow = 6, - MagicPurple = 7, - ToxicGreen = 8, - ExoticPurple = 9, - SulfurYellow = 10, - AmberOrange = 11, - PineGreen = 12, - CornflowerBlue = 13, -} -impl EyeColor { - pub fn light_rgb(self) -> Rgb { - match self { - EyeColor::VigorousBlack => Rgb::new(71, 59, 49), - EyeColor::NobleBlue => Rgb::new(75, 158, 191), - EyeColor::CuriousGreen => Rgb::new(110, 167, 113), - EyeColor::LoyalBrown => Rgb::new(73, 42, 36), - EyeColor::ViciousRed => Rgb::new(182, 0, 0), - EyeColor::PumpkinOrange => Rgb::new(220, 156, 19), - EyeColor::GhastlyYellow => Rgb::new(221, 225, 31), - EyeColor::MagicPurple => Rgb::new(137, 4, 177), - EyeColor::ToxicGreen => Rgb::new(1, 223, 1), - EyeColor::ExoticPurple => Rgb::new(95, 32, 111), - EyeColor::SulfurYellow => Rgb::new(235, 198, 94), - EyeColor::AmberOrange => Rgb::new(137, 46, 1), - EyeColor::PineGreen => Rgb::new(0, 78, 56), - EyeColor::CornflowerBlue => Rgb::new(18, 66, 90), - } +make_case_elim!( + eye_color, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] + #[repr(u32)] + pub enum EyeColor { + VigorousBlack = 0, + NobleBlue = 1, + CuriousGreen = 2, + LoyalBrown = 3, + ViciousRed = 4, + PumpkinOrange = 5, + GhastlyYellow = 6, + MagicPurple = 7, + ToxicGreen = 8, + ExoticPurple = 9, + SulfurYellow = 10, + AmberOrange = 11, + PineGreen = 12, + CornflowerBlue = 13, } +); - pub fn dark_rgb(self) -> Rgb { - match self { - EyeColor::VigorousBlack => Rgb::new(32, 32, 32), - EyeColor::NobleBlue => Rgb::new(62, 130, 159), - EyeColor::CuriousGreen => Rgb::new(81, 124, 84), - EyeColor::LoyalBrown => Rgb::new(54, 30, 26), - EyeColor::ViciousRed => Rgb::new(148, 0, 0), - EyeColor::PumpkinOrange => Rgb::new(209, 145, 18), - EyeColor::GhastlyYellow => Rgb::new(205, 212, 29), - EyeColor::MagicPurple => Rgb::new(110, 3, 143), - EyeColor::ToxicGreen => Rgb::new(1, 185, 1), - EyeColor::ExoticPurple => Rgb::new(69, 23, 80), - EyeColor::SulfurYellow => Rgb::new(209, 176, 84), - EyeColor::AmberOrange => Rgb::new(112, 40, 1), - EyeColor::PineGreen => Rgb::new(0, 54, 38), - EyeColor::CornflowerBlue => Rgb::new(13, 47, 64), - } +make_case_elim!( + skin, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] + #[repr(u32)] + pub enum Skin { + Skin1 = 0, + Skin2 = 1, + Skin3 = 2, + Skin4 = 3, + Skin5 = 4, + Skin6 = 5, + Iron = 6, + Steel = 7, + DanariOne = 8, + DanariTwo = 9, + DanariThree = 10, + DanariFour = 11, + ElfOne = 12, + ElfTwo = 13, + //ElfThree = 14, + OrcOne = 14, + OrcTwo = 15, + OrcThree = 16, + UndeadOne = 17, + UndeadTwo = 18, + UndeadThree = 19, + Skin7 = 20, + Skin8 = 21, + Skin9 = 22, + Skin10 = 23, + Skin11 = 24, + Skin12 = 25, + Skin13 = 26, + Skin14 = 27, + Skin15 = 28, + Skin16 = 29, + Skin17 = 30, + Skin18 = 31, + OrcFour = 32, } - - pub fn white_rgb(self) -> Rgb { Rgb::new(255, 255, 255) } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Accessory { - Nothing = 0, - Some = 1, -} -pub const ALL_ACCESSORIES: [Accessory; 2] = [Accessory::Nothing, Accessory::Some]; - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Skin { - Skin1 = 0, - Skin2 = 1, - Skin3 = 2, - Skin4 = 3, - Skin5 = 4, - Skin6 = 5, - Iron = 6, - Steel = 7, - DanariOne = 8, - DanariTwo = 9, - DanariThree = 10, - DanariFour = 11, - ElfOne = 12, - ElfTwo = 13, - //ElfThree = 14, - OrcOne = 14, - OrcTwo = 15, - OrcThree = 16, - UndeadOne = 17, - UndeadTwo = 18, - UndeadThree = 19, - Skin7 = 20, - Skin8 = 21, - Skin9 = 22, - Skin10 = 23, - Skin11 = 24, - Skin12 = 25, - Skin13 = 26, - Skin14 = 27, - Skin15 = 28, - Skin16 = 29, - Skin17 = 30, - Skin18 = 31, - OrcFour = 32, -} -impl Skin { - pub fn rgb(self) -> Rgb { - let color = match self { - Self::Skin1 => (255, 229, 200), - Self::Skin2 => (255, 218, 190), - Self::Skin3 => (255, 206, 180), - Self::Skin4 => (255, 195, 170), - Self::Skin5 => (240, 184, 160), - Self::Skin6 => (225, 172, 150), - Self::Skin7 => (210, 161, 140), - Self::Skin8 => (195, 149, 130), - Self::Skin9 => (180, 138, 120), - Self::Skin10 => (165, 126, 110), - Self::Skin11 => (150, 114, 100), - Self::Skin12 => (135, 103, 90), - Self::Skin13 => (120, 92, 80), - Self::Skin14 => (105, 80, 70), - Self::Skin15 => (90, 69, 60), - Self::Skin16 => (75, 57, 50), - Self::Skin17 => (60, 46, 40), - Self::Skin18 => (45, 34, 30), - Self::Iron => (135, 113, 95), - Self::Steel => (108, 94, 86), - Self::DanariOne => (104, 168, 196), - Self::DanariTwo => (30, 149, 201), - Self::DanariThree => (57, 120, 148), - Self::DanariFour => (40, 85, 105), - Self::ElfOne => (178, 164, 186), - Self::ElfTwo => (132, 139, 161), - //Self::ElfThree => (230, 188, 198), - Self::OrcOne => (61, 130, 42), - Self::OrcTwo => (82, 117, 36), - Self::OrcThree => (71, 94, 42), - Self::OrcFour => (97, 54, 29), - Self::UndeadOne => (240, 243, 239), - Self::UndeadTwo => (178, 178, 178), - Self::UndeadThree => (145, 135, 121), - }; - Rgb::from(color) - } - - pub fn light_rgb(self) -> Rgb { - let color = match self { - Self::Skin1 => (255, 229, 200), - Self::Skin2 => (255, 218, 190), - Self::Skin3 => (255, 206, 180), - Self::Skin4 => (255, 195, 170), - Self::Skin5 => (240, 184, 160), - Self::Skin6 => (225, 172, 150), - Self::Skin7 => (210, 161, 140), - Self::Skin8 => (195, 149, 130), - Self::Skin9 => (180, 138, 120), - Self::Skin10 => (165, 126, 110), - Self::Skin11 => (150, 114, 100), - Self::Skin12 => (135, 103, 90), - Self::Skin13 => (120, 92, 80), - Self::Skin14 => (105, 80, 70), - Self::Skin15 => (90, 69, 60), - Self::Skin16 => (75, 57, 50), - Self::Skin17 => (60, 46, 40), - Self::Skin18 => (45, 34, 30), - Self::Iron => (144, 125, 106), - Self::Steel => (120, 107, 99), - Self::DanariOne => (116, 176, 208), - Self::DanariTwo => (42, 158, 206), - Self::DanariThree => (70, 133, 160), - Self::DanariFour => (53, 96, 116), - Self::ElfOne => (190, 176, 199), //178, 164, 186 - Self::ElfTwo => (137, 144, 167), - //Self::ElfThree => (242, 199, 209), - Self::OrcOne => (83, 165, 56), - Self::OrcTwo => (92, 132, 46), - Self::OrcThree => (84, 110, 54), - Self::OrcFour => (97, 54, 29), - Self::UndeadOne => (254, 252, 251), - Self::UndeadTwo => (190, 192, 191), - Self::UndeadThree => (160, 151, 134), - }; - Rgb::from(color) - } - - pub fn dark_rgb(self) -> Rgb { - let color = match self { - Self::Skin1 => (242, 217, 189), - Self::Skin2 => (242, 207, 189), - Self::Skin3 => (242, 197, 172), - Self::Skin4 => (242, 186, 162), - Self::Skin5 => (212, 173, 150), - Self::Skin6 => (212, 163, 142), - Self::Skin7 => (196, 151, 132), - Self::Skin8 => (181, 139, 121), - Self::Skin9 => (168, 129, 113), - Self::Skin10 => (153, 117, 103), - Self::Skin11 => (138, 105, 92), - Self::Skin12 => (122, 93, 82), - Self::Skin13 => (107, 82, 72), - Self::Skin14 => (92, 70, 62), - Self::Skin15 => (77, 59, 51), - Self::Skin16 => (61, 47, 41), - Self::Skin17 => (48, 37, 32), - Self::Skin18 => (33, 25, 22), - Self::Iron => (124, 99, 82), - Self::Steel => (96, 81, 72), - Self::DanariOne => (92, 155, 183), - Self::DanariTwo => (25, 142, 192), - Self::DanariThree => (52, 115, 143), - Self::DanariFour => (34, 80, 99), - Self::ElfOne => (170, 155, 175), //170, 157, 179 - Self::ElfTwo => (126, 132, 153), - //Self::ElfThree => (217, 178, 187), - Self::OrcOne => (55, 114, 36), - Self::OrcTwo => (70, 104, 29), - Self::OrcThree => (60, 83, 32), - Self::OrcFour => (84, 47, 25), - Self::UndeadOne => (229, 231, 230), - Self::UndeadTwo => (165, 166, 164), - Self::UndeadThree => (130, 122, 106), - }; - Rgb::from(color) - } -} +); diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 7f7eef651d..26b490a7f4 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -82,7 +82,8 @@ impl CharacterState { | CharacterState::BasicBlock | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) - | CharacterState::ChargedRanged(_)) + | CharacterState::ChargedRanged(_) + ) } pub fn is_attack(&self) -> bool { @@ -93,7 +94,8 @@ impl CharacterState { | CharacterState::TripleStrike(_) | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) - | CharacterState::ChargedRanged(_)) + | CharacterState::ChargedRanged(_) + ) } pub fn is_aimed(&self) -> bool { @@ -104,7 +106,8 @@ impl CharacterState { | CharacterState::TripleStrike(_) | CharacterState::BasicBlock | CharacterState::LeapMelee(_) - | CharacterState::ChargedRanged(_)) + | CharacterState::ChargedRanged(_) + ) } pub fn is_block(&self) -> bool { matches!(self, CharacterState::BasicBlock) } diff --git a/common/src/lib.rs b/common/src/lib.rs index f5bf9ef71a..dacdedd767 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,6 +3,8 @@ #![type_length_limit = "1664759"] #![feature( arbitrary_enum_discriminant, + const_checked_int_methods, + fundamental, option_unwrap_none, bool_to_option, label_break_value, @@ -37,6 +39,7 @@ pub mod store; pub mod sync; pub mod sys; pub mod terrain; +pub mod typed; pub mod util; pub mod vol; pub mod volumes; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index d6d55e46e8..6b8904d892 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -48,6 +48,127 @@ pub struct CharacterInfo { pub level: u32, } +#[derive(Debug, Clone, Serialize, Deserialize)] +/// World map information. Note that currently, we always send the whole thing +/// in one go, but the structure aims to try to provide information as locally +/// as possible, so that in the future we can split up large maps into multiple +/// WorldMapMsg fragments. +/// +/// TODO: Update message format to make fragmentable, allowing us to send more +/// information without running into bandwidth issues. +/// +/// TODO: Add information for rivers (currently, we just prerender them on the +/// server, but this is not a great solution for LoD. The map rendering code is +/// already set up to be able to take advantage of the river rendering being +/// split out, but the format is a little complicated for space reasons and it +/// may take some tweaking to get right, so we avoid sending it for now). +/// +/// TODO: measure explicit compression schemes that might save space, e.g. +/// repeating the "small angles" optimization that works well on more detailed +/// shadow maps intended for height maps. +pub struct WorldMapMsg { + /// Log base 2 of world map dimensions (width × height) in chunks. + /// + /// NOTE: Invariant: chunk count fits in a u16. + pub dimensions_lg: Vec2, + /// Sea level (used to provide a base altitude). + pub sea_level: f32, + /// Max height (used to scale altitudes). + pub max_height: f32, + /// RGB+A; the alpha channel is currently unused, but will be used in the + /// future. Entries are in the usual chunk order. + pub rgba: Vec, + /// Altitudes: bits 2 to 0 are unused, then bits 15 to 3 are used for + /// altitude. The remainder are currently unused, but we have plans to + /// use 7 bits for water depth (using an integer f7 encoding), and we + /// will find other uses for the remaining 12 bits. + pub alt: Vec, + /// Horizon mapping. This is a variant of shadow mapping that is + /// specifically designed for height maps; it takes advantage of their + /// regular structure (e.g. no holes) to compress all information needed + /// to decide when to cast a sharp shadow into a single nagle, the "horizon + /// angle." This is the smallest angle with the ground at which light can + /// pass through any occluders to reach the chunk, in some chosen + /// horizontal direction. This would not be sufficient for a more + /// complicated 3D structure, but it works for height maps since: + /// + /// 1. they have no gaps, so as soon as light can shine through it will + /// always be able to do so, and + /// 2. we only care about lighting from the top, and only from the east and + /// west (since at a large scale like this we mostly just want to + /// handle variable sunlight; moonlight would present more challenges + /// but we currently have no plans to try to cast accurate shadows in + /// moonlight). + /// + /// Our chosen format is two pairs of vectors, + /// with the first pair representing west-facing light (casting shadows on + /// the left side) and the second representing east-facing light + /// (casting shadows on the east side). + /// + /// The pair of vectors consists of (with each vector in the usual chunk + /// order): + /// + /// * Horizon angle pointing east (1 byte, scaled so 1 unit = 255° / 360). + /// We might consider switching to tangent if that represents the + /// information we care about better. + /// * Approximate (floor) height of maximal occluder. We currently use this + /// to try to deliver some approximation of soft shadows, which isn't that + /// big a deal on the world map but is probably needed in order to ensure + /// smooth transitions between chunks in LoD view. Additionally, when we + /// start using the shadow information to do local lighting on the world + /// map, we'll want a quick way to test where we can go out of shadoow at + /// arbitrary heights (since the player and other entities cajn find + /// themselves far from the ground at times). While this is only an + /// approximation to a proper distance map, hopefully it will give us + /// something that feels reasonable enough for Veloren's style. + /// + /// NOTE: On compression. + /// + /// Horizon mapping has a lot of advantages for height maps (simple, easy to + /// understand, doesn't require any fancy math or approximation beyond + /// precision loss), though it loses a few of them by having to store + /// distance to occluder as well. However, just storing tons + /// and tons of regular shadow maps (153 for a full day cycle, stored at + /// irregular intervals) combined with clever explicit compression and + /// avoiding recording sharp local shadows (preferring retracing for + /// these), yielded a compression rate of under 3 bits per column! Since + /// we likely want to avoid per-column shadows for worlds of the sizes we + /// want, we'd still need to store *some* extra information to create + /// soft shadows, but it would still be nice to try to drive down our + /// size as much as possible given how compressible shadows of height + /// maps seem to be in practice. Therefore, we try to take advantage of the + /// way existing compression algorithms tend to work to see if we can + /// achieve significant gains without doing a lot of custom work. + /// + /// Specifically, since our rays are cast east/west, we expect that for each + /// row, the horizon angles in each direction should be sequences of + /// monotonically increasing values (as chunks approach a tall + /// occluder), followed by sequences of no shadow, repeated + /// until the end of the map. Monotonic sequences and same-byte sequences + /// are usually easy to compress and existing algorithms are more likely + /// to be able to deal with them than jumbled data. If we were to keep + /// both directions in the same vector, off-the-shelf compression would + /// probably be less effective. + /// + /// For related reasons, rather than storing distances as in a standard + /// distance map (which would lead to monotonically *decreaing* values + /// as we approached the occluder from a given direction), we store the + /// estimated *occluder height.* The idea here is that we replace the + /// monotonic sequences with constant sequences, which are extremely + /// straightforward to compress and mostly handled automatically by anything + /// that does run-length encoding (i.e. most off-the-shelf compression + /// algorithms). + /// + /// We still need to benchmark this properly, as there's no guarantee our + /// current compression algorithms will actually work well on this data + /// in practice. It's possible that some other permutation (e.g. more + /// bits reserved for "distance to occluder" in exchange for an even + /// more predictible sequence) would end up compressing better than storing + /// angles, or that we don't need as much precision as we currently have + /// (256 possible angles). + pub horizons: [(Vec, Vec); 2], +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub enum InviteAnswer { Accepted, @@ -68,7 +189,7 @@ pub enum ServerMsg { server_info: ServerInfo, time_of_day: state::TimeOfDay, max_group_size: u32, - world_map: (Vec2, Vec), + world_map: WorldMapMsg, recipe_book: RecipeBook, }, /// An error occurred while loading character data diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 72717bd0fb..19e5499dbd 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -453,10 +453,10 @@ pub struct Block { } impl Block { - pub fn new(kind: BlockKind, color: Rgb) -> Self { + pub const fn new(kind: BlockKind, color: Rgb) -> Self { Self { kind, - color: color.into_array(), + color: [color.r, color.g, color.b], } } diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs new file mode 100644 index 0000000000..e8977fb1bb --- /dev/null +++ b/common/src/terrain/map.rs @@ -0,0 +1,713 @@ +use super::{ + neighbors, quadratic_nearest_point, river_spline_coeffs, uniform_idx_as_vec2, + vec2_as_uniform_idx, TerrainChunkSize, NEIGHBOR_DELTA, TERRAIN_CHUNK_BLOCKS_LG, +}; +use crate::vol::RectVolSize; +use core::{f32, f64, iter, ops::RangeInclusive}; +use vek::*; + +/// Base two logarithm of the maximum size of the precomputed world, in meters, +/// along the x (E/W) and y (N/S) dimensions. +/// +/// NOTE: Each dimension is guaranteed to be a power of 2, so the logarithm is +/// exact. This is so that it is possible (at least in theory) for compiler or +/// runtime optimizations exploiting this are possible. For example, division +/// by the chunk size can turn into a bit shift. +/// +/// NOTE: As an invariant, this value is at least [TERRAIN_CHUNK_BLOCKS_LG]. +/// +/// NOTE: As an invariant, `(1 << [MAX_WORLD_BLOCKS_LG])` fits in an i32. +/// +/// TODO: Add static assertions for the above invariants. +/// +/// Currently, we define the maximum to be 19 (corresponding to 2^19 m) for both +/// directions. This value was derived by backwards reasoning from the following +/// conservative estimate of the maximum landmass area (using an approximation +/// of 1024 blocks / km instead of 1000 blocks / km, which will result in an +/// estimate that is strictly lower than the real landmass): +/// +/// Max area (km²) +/// ≌ (2^19 blocks * 1 km / 1024 blocks)^2 +/// = 2^((19 - 10) * 2) km² +/// = 2^18 km² +/// = 262,144 km² +/// +/// which is roughly the same area as the entire United Kingdom, and twice the +/// horizontal extent of Dwarf Fortress's largest map. Besides the comparison +/// to other games without infinite or near-infinite maps (like Dwarf Fortress), +/// there are other reasons to choose this as a good maximum size: +/// +/// * It is large enough to include geological features of fairly realistic +/// scale. It may be hard to do justice to truly enormous features like the +/// Amazon River, and natural temperature variation not related to altitude +/// would probably not produce climate extremes on an Earth-like planet, but +/// it can comfortably fit enormous river basins, Everest-scale mountains, +/// large islands and inland lakes, vast forests and deserts, and so on. +/// +/// * It is large enough that making it from one side of the map to another will +/// take a *very* long time. We show this with two examples. In each +/// example, travel is either purely horizontal or purely vertical (to +/// minimize distance traveled) across the whole map, and we assume there are +/// no obstacles or slopes. +/// +/// In example 1, a human is walking at the (real-time) speed of the fastest +/// marathon runners (around 6 blocks / real-time s). We assume the human can +/// maintain this pace indefinitely without stopping. Then crossing the map +/// will take about: +/// +/// 2^19 blocks * 1 real-time s / 6 blocks * 1 real-time min / 60 real-time s +/// * 1 real-time hr / 60 real-time min * 1 real-time days / 24 hr = 2^19 / 6 / +/// 60 / 60 / 24 real-time days ≌ 1 real-time day. +/// +/// That's right--it will take a full day of *real* time to cross the map at +/// an apparent speed of 6 m / s. Moreover, since in-game time passes at a +/// rate of 1 in-game min / 1 in-game s, this would also take *60 days* of +/// in-game time. +/// +/// Still though, this is the rate of an ordinary human. And besides that, if +/// we instead had a marathon runner traveling at 6 m / in-game s, it would +/// take just 1 day of in-game time for the runner to cross the map, or a mere +/// 0.4 hr of real time. To show that this rate of travel is unrealistic (and +/// perhaps make an eventual argument for a slower real-time to in-game time +/// conversion rate), our second example will consist of a high-speed train +/// running at 300 km / real-time h (the fastest real-world high speed train +/// averages under 270 k m / h, with 300 km / h as the designed top speed). +/// For a train traveling at this apparent speed (in real time), crossing the +/// map would take: +/// +/// 2^19 blocks * 1 km / 1000 blocks * 1 real-time hr / 300 km +/// = 2^19 / 1000 / 300 real-time hr +/// ≌ 1.75 real-time hr +/// +/// = 2^19 / 1000 / 300 real-time hr * 60 in-game hr / real-time hr +/// * 1 in-game days / 24 in-game hr +/// = 2^19 / 1000 / 300 * 60 / 24 in-game days +/// ≌ 4.37 in-game days +/// +/// In other words, something faster in real-time than any existing high-speed +/// train would be over 4 times slower (in real-time) than our hypothetical +/// top marathon runner running at 6 m / s in in-game speed. This suggests +/// that the gap between in-game time and real-time is probably much too large +/// for most purposes; however, what it definitely shows is that even +/// extremely fast in-game transport across the world will not trivialize its +/// size. +/// +/// It follows that cities or towns of realistic scale, player housing, +/// fields, and so on, will all fit comfortably on a map of this size, while +/// at the same time still being reachable by non-warping, in-game mechanisms +/// (such as high-speed transit). It also provides plenty of room for mounts +/// of varying speeds, which can help ensure that players don't feel cramped or +/// deliberately slowed down by their own speed. +/// +/// * It is small enough that it is (barely) plausible that we could still +/// generate maps for a world of this size using detailed and realistic +/// erosion algorithms. At 1/4 of this map size along each dimension, +/// generation currently takes around 5 hours on a good computer, and one +/// could imagine (since the bottleneck step appears to be roughly O(n)) that +/// with a smart implementation generation times of under a week could be +/// achievable. +/// +/// * The map extends further than the resolution of human eyesight under +/// Earthlike conditions, even from tall mountains across clear landscapes. +/// According to one calculation, even from Mt. Everest in the absence of +/// cloud cover, you could only see for about 339 km before the Earth's +/// horizon prevented you from seeing further, and other sources suggest that +/// in practice the limit is closer to 160 km under realistic conditions. This +/// implies that making the map much larger in a realistic way would require +/// incorporating curvature, and also implies that any features that cannot +/// fit on the map would not (under realistic atmospheric conditions) be fully +/// visible from any point on Earth. Therefore, even if we cannot represent +/// features larger than this accurately, nothing should be amiss from a +/// visual perspective, so this should not significantly impact the player +/// experience. +pub const MAX_WORLD_BLOCKS_LG: Vec2 = Vec2 { x: 19, y: 19 }; + +/// Base two logarithm of a world size, in chunks, per dimension +/// (each dimension must be a power of 2, so the logarithm is exact). +/// +/// NOTE: As an invariant, each dimension must be between 0 and +/// `[MAX_WORLD_BLOCKS_LG] - [TERRAIN_CHUNK_BLOCKS_LG]`. +/// +/// NOTE: As an invariant, `(1 << ([DEFAULT_WORLD_CHUNKS_LG] + +/// [TERRAIN_CHUNK_BLOCKS_LG]))` fits in an i32 (derived from the invariant +/// on [MAX_WORLD_BLOCKS_LG]). +/// +/// NOTE: As an invariant, each dimension (in chunks) must fit in a u16. +/// +/// NOTE: As an invariant, the product of dimensions (in chunks) must fit in a +/// usize. +/// +/// These invariants are all checked on construction of a `MapSizeLg`. +#[derive(Clone, Copy, Debug)] +pub struct MapSizeLg(Vec2); + +impl MapSizeLg { + // FIXME: We cannot use is_some() here because it is not currently marked as a + // `const fn`. Since being able to use conditionals in constant expressions has + // not technically been stabilized yet, Clippy probably doesn't check for this + // case yet. When it can, or when is_some() is stabilized as a `const fn`, + // we should deal with this. + #[allow(clippy::redundant_pattern_matching)] + /// Construct a new `MapSizeLg`, returning an error if the needed invariants + /// do not hold and the vector otherwise. + /// + /// TODO: In the future, we may use unsafe code to assert to the compiler + /// that these invariants indeed hold, safely opening up optimizations + /// that might not otherwise be available at runtime. + #[inline(always)] + pub const fn new(map_size_lg: Vec2) -> Result { + // Assertion on dimensions: must be between + // 0 and ([MAX_WORLD_BLOCKS_LG] - [TERRAIN_CHUNK_BLOCKS_LG]) + let is_le_max = map_size_lg.x <= MAX_WORLD_BLOCKS_LG.x - TERRAIN_CHUNK_BLOCKS_LG + && map_size_lg.y <= MAX_WORLD_BLOCKS_LG.y - TERRAIN_CHUNK_BLOCKS_LG; + // Assertion on dimensions: chunks must fit in a u16. + let chunks_in_range = + /* 1u16.checked_shl(map_size_lg.x).is_some() && + 1u16.checked_shl(map_size_lg.y).is_some(); */ + map_size_lg.x <= 16 && + map_size_lg.y <= 16; + if is_le_max && chunks_in_range { + // Assertion on dimensions: blocks must fit in a i32. + let blocks_in_range = + /* 1i32.checked_shl(map_size_lg.x + TERRAIN_CHUNK_BLOCKS_LG).is_some() && + 1i32.checked_shl(map_size_lg.y + TERRAIN_CHUNK_BLOCKS_LG).is_some(); */ + map_size_lg.x + TERRAIN_CHUNK_BLOCKS_LG < 32 && + map_size_lg.y + TERRAIN_CHUNK_BLOCKS_LG < 32; + // Assertion on dimensions: product of dimensions must fit in a usize. + let chunks_product_in_range = + matches!(1usize.checked_shl(map_size_lg.x + map_size_lg.y), Some(_)); + if blocks_in_range && chunks_product_in_range { + // Cleared all invariants. + Ok(MapSizeLg(map_size_lg)) + } else { + Err(()) + } + } else { + Err(()) + } + } + + #[inline(always)] + /// Acquire the `MapSizeLg`'s inner vector. + pub const fn vec(self) -> Vec2 { self.0 } + + #[inline(always)] + /// Get the size of this map in chunks. + pub const fn chunks(self) -> Vec2 { Vec2::new(1 << self.0.x, 1 << self.0.y) } + + /// Get the size of an array of the correct size to hold all chunks. + pub const fn chunks_len(self) -> usize { 1 << (self.0.x + self.0.y) } +} + +impl From for Vec2 { + #[inline(always)] + fn from(size: MapSizeLg) -> Self { size.vec() } +} + +pub struct MapConfig<'a> { + /// Base two logarithm of the chunk dimensions of the base map. + /// Has no default; set explicitly during initial orthographic projection. + pub map_size_lg: MapSizeLg, + /// Dimensions of the window being written to. + /// + /// Defaults to `1 << [MapConfig::map_size_lg]`. + pub dimensions: Vec2, + /// x, y, and z of top left of map. + /// + /// Default x and y are 0.0; no reasonable default for z, so set during + /// initial orthographic projection. + pub focus: Vec3, + /// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain + /// makes smaller differences in altitude appear larger. + /// + /// No reasonable default for z; set during initial orthographic projection. + pub gain: f32, + /// `fov` is used for shading purposes and refers to how much impact a + /// change in the z direction has on the perceived slope relative to the + /// same change in x and y. + /// + /// It is stored as cos θ in the range (0, 1\] where θ is the FOV + /// "half-angle" used for perspective projection. At 1.0, we treat it + /// as the limit value for θ = 90 degrees, and use an orthographic + /// projection. + /// + /// Defaults to 1.0. + /// + /// FIXME: This is a hack that tries to incorrectly implement a variant of + /// perspective projection (which generates ∂P/∂x and ∂P/∂y for screen + /// coordinate P by using the hyperbolic function \[assuming frustum of + /// \[l, r, b, t, n, f\], rh coordinates, and output from -1 to 1 in + /// s/t, 0 to 1 in r, and NDC is left-handed \[so visible z ranges from + /// -n to -f\]\]): + /// + /// P.s(x, y, z) = -1 + 2(-n/z x - l) / ( r - l) + /// P.t(x, y, z) = -1 + 2(-n/z y - b) / ( t - b) + /// P.r(x, y, z) = 0 + -f(-n/z - 1) / ( f - n) + /// + /// Then arbitrarily using W_e_x = (r - l) as the width of the projected + /// image, we have W_e_x = 2 n_e tan θ ⇒ tan Θ = (r - l) / (2n_e), for a + /// perspective projection + /// + /// (where θ is the half-angle of the FOV). + /// + /// Taking the limit as θ → 90, we show that this degenrates to an + /// orthogonal projection: + /// + /// lim{n → ∞}(-f(-n / z - 1) / (f - n)) = -(z - -n) / (f - n). + /// + /// (Proof not currently included, but has been formalized for the P.r case + /// in Coq-tactic notation; the proof can be added on request, but is + /// large and probably not well-suited to Rust documentation). + /// + /// For this reason, we feel free to store `fov` as cos θ in the range (0, + /// 1\]. + /// + /// However, `fov` does not actually work properly yet, so for now we just + /// treat it as a visual gimmick. + pub fov: f64, + /// Scale is like gain, but for x and y rather than z. + /// + /// Defaults to (1 << world_size_lg).x / dimensions.x (NOTE: fractional, not + /// integer, division!). + pub scale: f64, + /// Vector that indicates which direction light is coming from, if shading + /// is turned on. + /// + /// Right-handed coordinate system: light is going left, down, and + /// "backwards" (i.e. on the map, where we translate the y coordinate on + /// the world map to z in the coordinate system, the light comes from -y + /// on the map and points towards +y on the map). In a right + /// handed coordinate system, the "camera" points towards -z, so positive z + /// is backwards "into" the camera. + /// + /// "In world space the x-axis will be pointing east, the y-axis up and the + /// z-axis will be pointing south" + /// + /// Defaults to (-0.8, -1.0, 0.3). + pub light_direction: Vec3, + /// If Some, uses the provided horizon map. + /// + /// Defaults to None. + pub horizons: Option<&'a [(Vec, Vec); 2]>, + /// If true, only the basement (bedrock) is used for altitude; otherwise, + /// the surface is used. + /// + /// Defaults to false. + pub is_basement: bool, + /// If true, water is rendered; otherwise, the surface without water is + /// rendered, even if it is underwater. + /// + /// Defaults to true. + pub is_water: bool, + /// If true, 3D lighting and shading are turned on. Otherwise, a plain + /// altitude map is used. + /// + /// Defaults to true. + pub is_shaded: bool, + /// If true, the red component of the image is also used for temperature + /// (redder is hotter). Defaults to false. + pub is_temperature: bool, + /// If true, the blue component of the image is also used for humidity + /// (bluer is wetter). + /// + /// Defaults to false. + pub is_humidity: bool, + /// Record debug information. + /// + /// Defaults to false. + pub is_debug: bool, +} + +pub const QUADRANTS: usize = 4; + +pub struct MapDebug { + pub quads: [[u32; QUADRANTS]; QUADRANTS], + pub rivers: u32, + pub lakes: u32, + pub oceans: u32, +} + +/// Connection kind (per edge). Currently just supports rivers, but may be +/// extended to support paths or at least one other kind of connection. +#[derive(Clone, Copy, Debug)] +pub enum ConnectionKind { + /// Connection forms a visible river. + River, +} + +/// Map connection (per edge). +#[derive(Clone, Copy, Debug)] +pub struct Connection { + /// The kind of connection this is (e.g. river or path). + pub kind: ConnectionKind, + /// Assumed to be the "b" part of a 2d quadratic function. + pub spline_derivative: Vec2, + /// Width of the connection. + pub width: f32, +} + +/// Per-chunk data the map needs to be able to sample in order to correctly +/// render. +#[derive(Clone, Debug)] +pub struct MapSample { + /// the base RGB color for a particular map pixel using the current settings + /// (i.e. the color *without* lighting). + pub rgb: Rgb, + /// Surface altitude information + /// (correctly reflecting settings like is_basement and is_water) + pub alt: f64, + /// Downhill chunk (may not be meaningful on ocean tiles, or at least edge + /// tiles) + pub downhill_wpos: Vec2, + /// Connection information about any connections to/from this chunk (e.g. + /// rivers). + /// + /// Connections at each index correspond to the same index in + /// NEIGHBOR_DELTA. + pub connections: Option<[Option; 8]>, +} + +impl<'a> MapConfig<'a> { + /// Constructs the configuration settings for an orthographic projection of + /// a map from the top down, rendering (by default) the complete map to + /// an image such that the chunk:pixel ratio is 1:1. + /// + /// Takes two arguments: the base two logarithm of the horizontal map extent + /// (in chunks), and the z bounds of the projection. + pub fn orthographic(map_size_lg: MapSizeLg, z_bounds: RangeInclusive) -> Self { + assert!(z_bounds.start() <= z_bounds.end()); + // NOTE: Safe cast since map_size_lg is restricted by the prior assert. + let dimensions = map_size_lg.chunks().map(usize::from); + Self { + map_size_lg, + dimensions, + focus: Vec3::new(0.0, 0.0, f64::from(*z_bounds.start())), + gain: z_bounds.end() - z_bounds.start(), + fov: 1.0, + scale: 1.0, + light_direction: Vec3::new(-1.2, -1.0, 0.8), + horizons: None, + + is_basement: false, + is_water: true, + is_shaded: true, + is_temperature: false, + is_humidity: false, + is_debug: false, + } + } + + /// Get the base 2 logarithm of the underlying map size. + pub fn map_size_lg(&self) -> MapSizeLg { self.map_size_lg } + + /// Generates a map image using the specified settings. Note that it will + /// write from left to write from (0, 0) to dimensions - 1, inclusive, + /// with 4 1-byte color components provided as (r, g, b, a). It is up + /// to the caller to provide a function that translates this information + /// into the correct format for a buffer and writes to it. + /// + /// sample_pos is a function that, given a chunk position, returns enough + /// information about the chunk to attempt to render it on the map. + /// When in doubt, try using `MapConfig::sample_pos` for this. + /// + /// sample_wpos is a simple function that, given a *column* position, + /// returns the approximate altitude at that column. When in doubt, try + /// using `MapConfig::sample_wpos` for this. + #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 + #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 + #[allow(clippy::many_single_char_names)] + pub fn generate( + &self, + sample_pos: impl Fn(Vec2) -> MapSample, + sample_wpos: impl Fn(Vec2) -> f32, + mut write_pixel: impl FnMut(Vec2, (u8, u8, u8, u8)), + ) -> MapDebug { + let MapConfig { + map_size_lg, + dimensions, + focus, + gain, + fov, + scale, + light_direction, + horizons, + + is_shaded, + // is_debug, + .. + } = *self; + + let light_direction = Vec3::new( + light_direction.x, + light_direction.y, + 0.0, // we currently ignore light_direction.z. + ); + let light_shadow_dir = if light_direction.x >= 0.0 { 0 } else { 1 }; + let horizon_map = horizons.map(|horizons| &horizons[light_shadow_dir]); + let light = light_direction.normalized(); + let /*mut */quads = [[0u32; QUADRANTS]; QUADRANTS]; + let /*mut */rivers = 0u32; + let /*mut */lakes = 0u32; + let /*mut */oceans = 0u32; + + let focus_rect = Vec2::from(focus); + + let chunk_size = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); + + /* // NOTE: Asserting this to enable LLVM optimizations. Ideally we should come up + // with a principled way to do this (especially one with no runtime + // cost). + assert!( + map_size_lg + .vec() + .cmple(&(MAX_WORLD_BLOCKS_LG - TERRAIN_CHUNK_BLOCKS_LG)) + .reduce_and() + ); */ + let world_size = map_size_lg.chunks(); + + (0..dimensions.y * dimensions.x).for_each(|chunk_idx| { + let i = chunk_idx % dimensions.x as usize; + let j = chunk_idx / dimensions.x as usize; + + let wposf = focus_rect + Vec2::new(i as f64, j as f64) * scale; + let pos = wposf.map(|e: f64| e as i32); + let wposf = wposf * chunk_size; + + let chunk_idx = if pos.reduce_partial_min() >= 0 + && pos.x < world_size.x as i32 + && pos.y < world_size.y as i32 + { + Some(vec2_as_uniform_idx(map_size_lg, pos)) + } else { + None + }; + + let MapSample { + rgb, + alt, + downhill_wpos, + .. + } = sample_pos(pos); + + let alt = alt as f32; + let wposi = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let mut rgb = rgb.map(|e| e as f64 / 255.0); + + // Material properties: + // + // For each material in the scene, + // k_s = (RGB) specular reflection constant + let mut k_s = Rgb::new(1.0, 1.0, 1.0); + // k_d = (RGB) diffuse reflection constant + let mut k_d = rgb; + // k_a = (RGB) ambient reflection constant + let mut k_a = rgb; + // α = (per-material) shininess constant + let mut alpha = 4.0; // 4.0; + + // Compute connections + let mut has_river = false; + // NOTE: consider replacing neighbors with local_cells, since it is more + // accurate (though I'm not sure if it can matter for these + // purposes). + chunk_idx + .into_iter() + .flat_map(|chunk_idx| { + neighbors(map_size_lg, chunk_idx).chain(iter::once(chunk_idx)) + }) + .for_each(|neighbor_posi| { + let neighbor_pos = uniform_idx_as_vec2(map_size_lg, neighbor_posi); + let neighbor_wpos = neighbor_pos.map(|e| e as f64) * chunk_size; + let MapSample { connections, .. } = sample_pos(neighbor_pos); + NEIGHBOR_DELTA + .iter() + .zip(connections.iter().flatten()) + .for_each(|(&delta, connection)| { + let connection = if let Some(connection) = connection { + connection + } else { + return; + }; + let downhill_wpos = neighbor_wpos + + Vec2::from(delta).map(|e: i32| e as f64) * chunk_size; + let coeffs = river_spline_coeffs( + neighbor_wpos, + connection.spline_derivative, + downhill_wpos, + ); + let (_t, _pt, dist) = if let Some((t, pt, dist)) = + quadratic_nearest_point(&coeffs, wposf) + { + (t, pt, dist) + } else { + let ndist = wposf.distance_squared(neighbor_wpos); + let ddist = wposf.distance_squared(downhill_wpos); + if ndist <= ddist { + (0.0, neighbor_wpos, ndist) + } else { + (1.0, downhill_wpos, ddist) + } + }; + let connection_dist = + (dist.sqrt() - (connection.width as f64 * 0.5).max(1.0)).max(0.0); + if connection_dist == 0.0 { + match connection.kind { + ConnectionKind::River => { + has_river = true; + }, + } + } + }); + }); + + // Color in connectins. + let water_color_factor = 2.0; + let g_water = 32.0 * water_color_factor; + let b_water = 64.0 * water_color_factor; + if has_river { + let water_rgb = Rgb::new(0, ((g_water) * 1.0) as u8, ((b_water) * 1.0) as u8) + .map(|e| e as f64 / 255.0); + rgb = water_rgb; + k_s = Rgb::new(1.0, 1.0, 1.0); + k_d = water_rgb; + k_a = water_rgb; + alpha = 0.255; + } + + let downhill_alt = sample_wpos(downhill_wpos); + let cross_pos = wposi + + ((downhill_wpos - wposi) + .map(|e| e as f32) + .rotated_z(f32::consts::FRAC_PI_2) + .map(|e| e as i32)); + let cross_alt = sample_wpos(cross_pos); + // TODO: Fix use of fov to match proper perspective projection, as described in + // the doc comment. + // Pointing downhill, forward + // (index--note that (0,0,1) is backward right-handed) + let forward_vec = Vec3::new( + (downhill_wpos.x - wposi.x) as f64, + ((downhill_alt - alt) * gain) as f64 * fov, + (downhill_wpos.y - wposi.y) as f64, + ); + // Pointing 90 degrees left (in horizontal xy) of downhill, up + // (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed) + let up_vec = Vec3::new( + (cross_pos.x - wposi.x) as f64, + ((cross_alt - alt) * gain) as f64 * fov, + (cross_pos.y - wposi.y) as f64, + ); + // let surface_normal = Vec3::new(fov* (f.y * u.z - f.z * u.y), -(f.x * u.z - + // f.z * u.x), fov* (f.x * u.y - f.y * u.x)).normalized(); + // Then cross points "to the right" (upwards) on a right-handed coordinate + // system. (right-handed coordinate system means (0, 0, 1.0) is + // "forward" into the screen). + let surface_normal = forward_vec.cross(up_vec).normalized(); + + // TODO: Figure out if we can reimplement debugging. + /* if is_debug { + let quad = + |x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1); + if river_kind.is_none() || humidity != 0.0 { + quads[quad(humidity)][quad(temperature)] += 1; + } + match river_kind { + Some(RiverKind::River { .. }) => { + rivers += 1; + }, + Some(RiverKind::Lake { .. }) => { + lakes += 1; + }, + Some(RiverKind::Ocean { .. }) => { + oceans += 1; + }, + None => {}, + } + } */ + + let shade_frac = horizon_map + .and_then(|(angles, heights)| { + chunk_idx + .and_then(|chunk_idx| angles.get(chunk_idx)) + .map(|&e| (e as f64, heights)) + }) + .and_then(|(e, heights)| { + chunk_idx + .and_then(|chunk_idx| heights.get(chunk_idx)) + .map(|&f| (e, f as f64)) + }) + .map(|(angle, height)| { + let w = 0.1; + let height = (height - f64::from(alt * gain)).max(0.0); + if angle != 0.0 && light_direction.x != 0.0 && height != 0.0 { + let deltax = height / angle; + let lighty = (light_direction.y / light_direction.x * deltax).abs(); + let deltay = lighty - height; + let s = (deltay / deltax / w).min(1.0).max(0.0); + // Smoothstep + s * s * (3.0 - 2.0 * s) + } else { + 1.0 + } + }) + .unwrap_or(1.0); + + let rgb = if is_shaded { + // Phong reflection model with shadows: + // + // I_p = k_a i_a + shadow * Σ {m ∈ lights} (k_d (L_m ⋅ N) i_m,d + k_s (R_m ⋅ + // V)^α i_m,s) + // + // where for the whole scene, + // i_a = (RGB) intensity of ambient lighting component + let i_a = Rgb::new(0.1, 0.1, 0.1); + // V = direction pointing towards the viewer (e.g. virtual camera). + let v = Vec3::new(0.0, 0.0, -1.0).normalized(); + + // for each light m, + // i_m,d = (RGB) intensity of diffuse component of light source m + let i_m_d = Rgb::new(1.0, 1.0, 1.0); + // i_m,s = (RGB) intensity of specular component of light source m + let i_m_s = Rgb::new(0.45, 0.45, 0.45); + + // for each light m and point p, + // L_m = (normalized) direction vector from point on surface to light source m + let l_m = light; + // N = (normalized) normal at this point on the surface, + let n = surface_normal; + // R_m = (normalized) direction a perfectly reflected ray of light from m would + // take from point p = 2(L_m ⋅ N)N - L_m + let r_m = (-l_m).reflected(n); // 2 * (l_m.dot(n)) * n - l_m; + // + // and for each point p in the scene, + // shadow = computed shadow factor at point p + // FIXME: Should really just be shade_frac, but with only ambient light we lose + // all local lighting detail... some sort of global illumination (e.g. + // radiosity) is of course the "right" solution, but maybe we can find + // something cheaper? + let shadow = 0.2 + 0.8 * shade_frac; + + let lambertian = l_m.dot(n).max(0.0); + let spec_angle = r_m.dot(v).max(0.0); + + let ambient = k_a * i_a; + let diffuse = k_d * lambertian * i_m_d; + let specular = k_s * spec_angle.powf(alpha) * i_m_s; + (ambient + shadow * (diffuse + specular)).map(|e| e.min(1.0)) + } else { + rgb + } + .map(|e| (e * 255.0) as u8); + + let rgba = (rgb.r, rgb.g, rgb.b, 255); + write_pixel(Vec2::new(i, j), rgba); + }); + + MapDebug { + quads, + rivers, + lakes, + oceans, + } + } +} diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 55e7b56013..ada6b7ab85 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -1,14 +1,17 @@ pub mod biome; pub mod block; pub mod chonk; +pub mod map; pub mod structure; // Reexports pub use self::{ biome::BiomeKind, block::{Block, BlockKind}, + map::MapSizeLg, structure::Structure, }; +use roots::find_roots_cubic; use serde::{Deserialize, Serialize}; use crate::{vol::RectVolSize, volumes::vol_grid_2d::VolGrid2d}; @@ -19,8 +22,24 @@ use vek::*; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TerrainChunkSize; +/// Base two logarithm of the number of blocks along either horizontal axis of +/// a chunk. +/// +/// NOTE: (1 << CHUNK_SIZE_LG) is guaranteed to fit in a u32. +/// +/// NOTE: A lot of code assumes that the two dimensions are equal, so we make it +/// explicit here. +/// +/// NOTE: It is highly unlikely that a value greater than 5 will work, as many +/// frontend optimizations rely on being able to pack chunk horizontal +/// dimensions into 5 bits each. +pub const TERRAIN_CHUNK_BLOCKS_LG: u32 = 5; + impl RectVolSize for TerrainChunkSize { - const RECT_SIZE: Vec2 = Vec2 { x: 32, y: 32 }; + const RECT_SIZE: Vec2 = Vec2 { + x: (1 << TERRAIN_CHUNK_BLOCKS_LG), + y: (1 << TERRAIN_CHUNK_BLOCKS_LG), + }; } // TerrainChunkMeta @@ -50,3 +69,140 @@ impl TerrainChunkMeta { pub type TerrainChunk = chonk::Chonk; pub type TerrainGrid = VolGrid2d; + +// Terrain helper functions used across multiple crates. + +/// Computes the position Vec2 of a SimChunk from an index, where the index was +/// generated by uniform_noise. +/// +/// NOTE: Dimensions obey constraints on [map::MapConfig::map_size_lg]. +#[inline(always)] +pub fn uniform_idx_as_vec2(map_size_lg: MapSizeLg, idx: usize) -> Vec2 { + let x_mask = (1 << map_size_lg.vec().x) - 1; + Vec2::new((idx & x_mask) as i32, (idx >> map_size_lg.vec().x) as i32) +} + +/// Computes the index of a Vec2 of a SimChunk from a position, where the index +/// is generated by uniform_noise. NOTE: Both components of idx should be +/// in-bounds! +#[inline(always)] +pub fn vec2_as_uniform_idx(map_size_lg: MapSizeLg, idx: Vec2) -> usize { + ((idx.y as usize) << map_size_lg.vec().x) | idx.x as usize +} + +// NOTE: want to keep this such that the chunk index is in ascending order! +pub const NEIGHBOR_DELTA: [(i32, i32); 8] = [ + (-1, -1), + (0, -1), + (1, -1), + (-1, 0), + (1, 0), + (-1, 1), + (0, 1), + (1, 1), +]; + +/// Iterate through all cells adjacent to a chunk. +#[inline(always)] +pub fn neighbors(map_size_lg: MapSizeLg, posi: usize) -> impl Clone + Iterator { + let pos = uniform_idx_as_vec2(map_size_lg, posi); + let world_size = map_size_lg.chunks(); + NEIGHBOR_DELTA + .iter() + .map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y)) + .filter(move |pos| { + pos.x >= 0 && pos.y >= 0 && pos.x < world_size.x as i32 && pos.y < world_size.y as i32 + }) + .map(move |pos| vec2_as_uniform_idx(map_size_lg, pos)) +} + +pub fn river_spline_coeffs( + // _sim: &WorldSim, + chunk_pos: Vec2, + spline_derivative: Vec2, + downhill_pos: Vec2, +) -> Vec3> { + let dxy = downhill_pos - chunk_pos; + // Since all splines have been precomputed, we don't have to do that much work + // to evaluate the spline. The spline is just ax^2 + bx + c = 0, where + // + // a = dxy - chunk.river.spline_derivative + // b = chunk.river.spline_derivative + // c = chunk_pos + let spline_derivative = spline_derivative.map(|e| e as f64); + Vec3::new(dxy - spline_derivative, spline_derivative, chunk_pos) +} + +/// Find the nearest point from a quadratic spline to this point (in terms of t, +/// the "distance along the curve" by which our spline is parameterized). Note +/// that if t < 0.0 or t >= 1.0, we probably shouldn't be considered "on the +/// curve"... hopefully this works out okay and gives us what we want (a +/// river that extends outwards tangent to a quadratic curve, with width +/// configured by distance along the line). +#[allow(clippy::let_and_return)] // TODO: Pending review in #587 +#[allow(clippy::many_single_char_names)] +pub fn quadratic_nearest_point( + spline: &Vec3>, + point: Vec2, +) -> Option<(f64, Vec2, f64)> { + let a = spline.z.x; + let b = spline.y.x; + let c = spline.x.x; + let d = point.x; + let e = spline.z.y; + let f = spline.y.y; + let g = spline.x.y; + let h = point.y; + // This is equivalent to solving the following cubic equation (derivation is a + // bit annoying): + // + // A = 2(c^2 + g^2) + // B = 3(b * c + g * f) + // C = ((a - d) * 2 * c + b^2 + (e - h) * 2 * g + f^2) + // D = ((a - d) * b + (e - h) * f) + // + // Ax³ + Bx² + Cx + D = 0 + // + // Once solved, this yield up to three possible values for t (reflecting minimal + // and maximal values). We should choose the minimal such real value with t + // between 0.0 and 1.0. If we fall outside those bounds, then we are + // outside the spline and return None. + let a_ = (c * c + g * g) * 2.0; + let b_ = (b * c + g * f) * 3.0; + let a_d = a - d; + let e_h = e - h; + let c_ = a_d * c * 2.0 + b * b + e_h * g * 2.0 + f * f; + let d_ = a_d * b + e_h * f; + let roots = find_roots_cubic(a_, b_, c_, d_); + let roots = roots.as_ref(); + + let min_root = roots + .iter() + .copied() + .filter_map(|root| { + let river_point = spline.x * root * root + spline.y * root + spline.z; + let river_zero = spline.z; + let river_one = spline.x + spline.y + spline.z; + if root > 0.0 && root < 1.0 { + Some((root, river_point)) + } else if river_point.distance_squared(river_zero) < 0.5 { + Some((root, /*river_point*/ river_zero)) + } else if river_point.distance_squared(river_one) < 0.5 { + Some((root, /*river_point*/ river_one)) + } else { + None + } + }) + .map(|(root, river_point)| { + let river_distance = river_point.distance_squared(point); + (root, river_point, river_distance) + }) + // In the (unlikely?) case that distances are equal, prefer the earliest point along the + // river. + .min_by(|&(ap, _, a), &(bp, _, b)| { + (a, ap < 0.0 || ap > 1.0, ap) + .partial_cmp(&(b, bp < 0.0 || bp > 1.0, bp)) + .unwrap() + }); + min_root +} diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 25501a4ac2..abe9168f4f 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -1,6 +1,7 @@ use super::BlockKind; use crate::{ assets::{self, Asset}, + make_case_elim, vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol}, volumes::dyna::{Dyna, DynaError}, }; @@ -9,25 +10,29 @@ use serde::Deserialize; use std::{fs::File, io::BufReader, sync::Arc}; use vek::*; -#[derive(Copy, Clone, PartialEq)] -pub enum StructureBlock { - None, - Grass, - TemperateLeaves, - PineLeaves, - Acacia, - Mangrove, - PalmLeavesInner, - PalmLeavesOuter, - Water, - GreenSludge, - Fruit, - Coconut, - Chest, - Hollow, - Liana, - Normal(Rgb), -} +make_case_elim!( + structure_block, + #[derive(Copy, Clone, PartialEq)] + #[repr(u32)] + pub enum StructureBlock { + None = 0, + Grass = 1, + TemperateLeaves = 2, + PineLeaves = 3, + Acacia = 4, + Mangrove = 5, + PalmLeavesInner = 6, + PalmLeavesOuter = 7, + Water = 8, + GreenSludge = 9, + Fruit = 10, + Coconut = 11, + Chest = 12, + Hollow = 13, + Liana = 14, + Normal(color: Rgb) = 15, + } +); impl Vox for StructureBlock { fn empty() -> Self { StructureBlock::None } diff --git a/common/src/typed.rs b/common/src/typed.rs new file mode 100644 index 0000000000..1dbf98f069 --- /dev/null +++ b/common/src/typed.rs @@ -0,0 +1,243 @@ +use core::marker::PhantomData; + +pub trait SubContext { + fn sub_context(self) -> Context; +} + +impl SubContext for Context { + fn sub_context(self) -> Context { self } +} + +impl SubContext for (Head, Tail) { + fn sub_context(self) -> Tail { self.1 } +} + +pub trait Typed { + fn reduce(self, context: Context) -> (Type, S); +} + +pub struct Pure(pub T); + +impl, T, S> Typed, S> for T { + fn reduce(self, context: Context) -> (Pure, S) { (Pure(self), context.sub_context()) } +} + +/// A lazy pattern match reified as a Rust type. +/// +/// `expr` is the expression being matched on, generally of some enum type `Ty`. +/// +/// `case` represents the pattern match--it will generally be a structure with +/// one field per constructor in `Ty`. The field should contain enough +/// information to run the match arm for that constructor, given the information +/// contained in the constructor arguments. +/// +/// `ty` represents the return type of the match expression. It does not carry +/// any runtime-relevant information, but is needed in order to simplify our +/// trait definitions. +/// +/// The intent is that you should not construct this structure directly, nor +/// should you define or construct the `Cases` structure directly. Instead, to +/// use this you are expected to wrap your enum declaration in a call to +/// [make_case_elim!], as follows: +/// +/// ``` +/// # #![feature(arbitrary_enum_discriminant)] +/// # #[macro_use] extern crate veloren_common; +/// +/// veloren_common::make_case_elim!( +/// my_type_module, +/// #[repr(u32)] +/// #[derive(Clone,Copy)] +/// pub enum MyType { +/// Constr1 = 0, +/// Constr2(arg : u8) = 1, +/// /* ..., */ +/// } +/// ); +/// ``` +/// +/// This macro automatically does a few things. First, it creates the `enum` +/// type `MyType` in the current scope, as expected. Second, it creates a +/// module named `my_type_module` in the current scope, into which it dumps a +/// few things. In this case: +/// +/// ``` +/// # #![feature(arbitrary_enum_discriminant)] +/// # #[macro_use] extern crate veloren_common; +/// +/// #[repr(u32)] +/// #[derive(Clone, Copy)] +/// pub enum MyType { +/// Constr1 = 0, +/// Constr2(u8) = 1, +/// /* ..., */ +/// } +/// +/// # #[allow(non_snake_case)] +/// # #[allow(dead_code)] +/// mod my_type_module { +/// use ::serde::{Deserialize, Serialize}; +/// +/// /// The number of variants in this enum. +/// pub const NUM_VARIANTS: usize = 2; +/// +/// /// An array of all the variant indices (in theory, this can be used by this or other +/// /// macros in order to easily build up things like uniform random samplers). +/// pub const ALL_INDICES: [u32; NUM_VARIANTS] = [0, 1]; +/// +/// /// A convenience trait used to store a different type for each constructor in this +/// /// pattern. +/// pub trait PackedElim { +/// type Constr1; +/// type Constr2; +/// } +/// +/// /// The actual *cases.* If you think of pattern match arms as being closures that accept +/// /// the cconstructor types as arguments, you can think of this structure as somehow +/// /// representing just the data *owned* by the closure. This is also what you will +/// /// generally store in your ron file--it has a field for each constructor of your enum, +/// /// with the types of all the fields specified by the implementation of [PackedElim] for +/// /// the [Elim] argument. Each field has the same name as the constructor it represents. +/// #[derive(Serialize, Deserialize)] +/// pub struct Cases { +/// pub Constr1: Elim::Constr1, +/// pub Constr2: Elim::Constr2, +/// } +/// +/// /// Finally, because it represents by an overwhelming margin the most common usecase, we +/// /// predefine a particular pattern matching strategy--"pure"--where every arm holds data of +/// /// the exact same type, T. +/// impl PackedElim for veloren_common::typed::Pure { +/// type Constr1 = T; +/// type Constr2 = T; +/// } +/// +/// /// Because PureCases is so convenient, we have an alias for it. Thus, in order to +/// /// represent a pattern match on an argument that returns a constant of type (u8,u8,u8) for +/// /// each arm, you'd use the type `PureCases<(u8, u8, u8)>`. +/// pub type PureCases = Cases>; +/// } +/// ``` +/// +/// Finally, a useful implementation of the [Typed] trait completes this story, +/// providing a way to evaluate this lazy math statement within Rust. +/// Unfortunately, [Typed] is quite complicated, and this story is still being +/// fully evaluated, so showing teh type may not be that elucidating. +/// Instead, we'll just present the method you can use most easily to pattern +/// match using the PureCases pattern we mentioned earlier: +/// +/// pub fn elim_case_pure<'a, Type>(&'a self, cases: &'a $mod::PureCases) +/// -> &'a Type +/// +/// If self is expression of your defined enum type, and match data defined by +/// PureCases, this evaluates the pattern match on self and returns the matched +/// case. +/// +/// To see how this is used in more detail, check out +/// `common/src/body/humanoid.rs`; it is also used extensively in the world +/// repository. +/// +/// --- +/// +/// Limitations: +/// +/// Unfortunately, due to restrictions on procedural macros, we currently always +/// require the types defined to #[repr(inttype)] as you can see above. There +/// are also some other current limitations that we hopefully will be able to +/// lift at some point; struct variants are not yet supported, and neither +/// attributes on fields. +#[fundamental] +pub struct ElimCase { + pub expr: Expr, + pub cases: Cases, + pub ty: PhantomData, +} + +#[macro_export] +macro_rules! as_item { + ($i:item) => { + $i + }; +} + +#[macro_export] +macro_rules! make_case_elim { + ($mod:ident, $( #[$ty_attr:meta] )* $vis:vis enum $ty:ident { + $( $constr:ident $( ( $( $arg_name:ident : $arg_ty:ty ),* ) )? = $index:expr ),* $(,)? + }) => { + $crate::as_item! { + $( #[$ty_attr] )* + $vis enum $ty { + $( $constr $( ($( $arg_ty, )*) )? = $index, )* + } + } + + #[allow(non_snake_case)] + #[allow(dead_code)] + $vis mod $mod { + use ::serde::{Deserialize, Serialize}; + + pub const NUM_VARIANTS: usize = 0 $( + { let _ = $index; 1 } )*; + + pub const ALL_INDICES: [u32; NUM_VARIANTS] = [ $( $index, )* ]; + + pub trait PackedElim { + $( type $constr; )* + } + + #[derive(Serialize, Deserialize)] + pub struct Cases { + $( pub $constr : Elim::$constr, )* + } + + impl PackedElim for $crate::typed::Pure { + $( type $constr = T; )* + } + + pub type PureCases = Cases<$crate::typed::Pure>; + } + + #[allow(unused_parens)] + impl<'a, Elim: $mod::PackedElim, Context, Type, S> + $crate::typed::Typed for $crate::typed::ElimCase<&'a $ty, &'a $mod::Cases, Type> + where + $( &'a Elim::$constr: $crate::typed::Typed<($( ($( &'a $arg_ty, )*), )? Context), Type, S>, )* + { + fn reduce(self, context: Context) -> (Type, S) + { + let Self { expr, cases, .. } = self; + match expr { + $( $ty::$constr $( ($( $arg_name, )*) )? => + <_ as $crate::typed::Typed<_, Type, _>>::reduce( + &cases.$constr, + ($( ($( $arg_name, )*), )? context), + ), + )* + } + } + } + + impl $ty { + pub fn elim_case<'a, Elim: $mod::PackedElim, Context, S, Type>(&'a self, cases: &'a $mod::Cases, context: Context) -> + (Type, S) + where + $crate::typed::ElimCase<&'a $ty, &'a $mod::Cases, Type> : $crate::typed::Typed, + { + use $crate::typed::Typed; + + let case = $crate::typed::ElimCase { + expr: self, + cases, + ty: ::core::marker::PhantomData, + }; + case.reduce(context) + } + + pub fn elim_case_pure<'a, Type>(&'a self, cases: &'a $mod::PureCases) -> &'a Type + { + let ($crate::typed::Pure(expr), ()) = self.elim_case(cases, ()); + expr + } + } + } +} diff --git a/common/src/util/color.rs b/common/src/util/color.rs index 72a2eca0f2..61a166693e 100644 --- a/common/src/util/color.rs +++ b/common/src/util/color.rs @@ -98,13 +98,20 @@ pub fn hsv_to_rgb(hsv: Vec3) -> Rgb { Rgb::new(r + m, g + m, b + m) } +/// Convert linear rgb to CIEXYZ +#[inline(always)] +pub fn rgb_to_xyz(rgb: Rgb) -> Vec3 { + // XYZ + Mat3::new( + 0.4124, 0.3576, 0.1805, 0.2126, 0.7152, 0.0722, 0.0193, 0.1192, 0.9504, + ) * Vec3::from(rgb) +} + /// Convert linear rgb to CIExyY #[inline(always)] pub fn rgb_to_xyy(rgb: Rgb) -> Vec3 { // XYZ - let xyz = Mat3::new( - 0.4124, 0.3576, 0.1805, 0.2126, 0.7152, 0.0722, 0.0193, 0.1192, 0.9504, - ) * Vec3::from(rgb); + let xyz = rgb_to_xyz(rgb); let sum = xyz.sum(); Vec3::new(xyz.x / sum, xyz.y / sum, xyz.y) diff --git a/common/src/vol.rs b/common/src/vol.rs index 210779c8d6..2313eea2e7 100644 --- a/common/src/vol.rs +++ b/common/src/vol.rs @@ -25,7 +25,7 @@ pub trait BaseVol { type Vox: Vox; type Error: Debug; - fn scaled_by(&self, scale: Vec3) -> Scaled + fn scaled_by(self, scale: Vec3) -> Scaled where Self: Sized, { diff --git a/common/src/volumes/scaled.rs b/common/src/volumes/scaled.rs index 44caa0bf51..c435c04096 100644 --- a/common/src/volumes/scaled.rs +++ b/common/src/volumes/scaled.rs @@ -1,28 +1,36 @@ use crate::vol::{BaseVol, ReadVol, SizedVol, Vox}; use vek::*; -pub struct Scaled<'a, V> { - pub inner: &'a V, +pub struct Scaled { + pub inner: V, pub scale: Vec3, } -impl<'a, V: BaseVol> BaseVol for Scaled<'a, V> { +impl BaseVol for Scaled { type Error = V::Error; type Vox = V::Vox; } -impl<'a, V: ReadVol> ReadVol for Scaled<'a, V> { +impl ReadVol for Scaled { #[inline(always)] fn get(&self, pos: Vec3) -> Result<&Self::Vox, Self::Error> { - let ideal_pos = pos.map2(self.scale, |e, scale| e as f32 / scale); - let pos = ideal_pos.map(|e| e.trunc() as i32); + // let ideal_pos = pos.map2(self.scale, |e, scale| (e as f32 + 0.5) / scale); + // let pos = ideal_pos.map(|e| e.trunc() as i32); + let min_pos = pos.map2(self.scale, |e, scale| ((e as f32) / scale).floor() as i32); + let max_pos = pos.map2(self.scale, |e, scale| { + ((e as f32 + 1.0) / scale).ceil() as i32 + }); + let pos = pos.map2(self.scale, |e, scale| { + (((e as f32 + 0.5) / scale) - 0.5).round() as i32 + }); - let ideal_search_size = Vec3::::one() / self.scale; + // let ideal_search_size = Vec3::::one() / self.scale; let range_iter = |i: usize| { std::iter::successors(Some(0), |p| Some(if *p < 0 { -*p } else { -(*p + 1) })) .take_while(move |p| { - ((ideal_pos[i] - ideal_search_size[i] / 2.0).round() as i32 - ..(ideal_pos[i] + ideal_search_size[i] / 2.0).round() as i32) + (min_pos[i]..max_pos[i]) + /* ((ideal_pos[i] - ideal_search_size[i] / 2.0).ceil() as i32 + ..(ideal_pos[i] + ideal_search_size[i] / 2.0).ceil() as i32) */ .contains(&(pos[i] + *p)) }) }; @@ -36,7 +44,7 @@ impl<'a, V: ReadVol> ReadVol for Scaled<'a, V> { } } -impl<'a, V: SizedVol> SizedVol for Scaled<'a, V> { +impl SizedVol for Scaled { #[inline(always)] fn lower_bound(&self) -> Vec3 { self.inner @@ -48,6 +56,6 @@ impl<'a, V: SizedVol> SizedVol for Scaled<'a, V> { fn upper_bound(&self) -> Vec3 { self.inner .upper_bound() - .map2(self.scale, |e, scale| (e as f32 * scale).ceil() as i32 + 1) + .map2(self.scale, |e, scale| (e as f32 * scale).ceil() as i32) } } diff --git a/network/src/message.rs b/network/src/message.rs index 5238f6791a..d5ab754a26 100644 --- a/network/src/message.rs +++ b/network/src/message.rs @@ -99,37 +99,17 @@ pub(crate) fn partial_eq_io_error(first: &io::Error, second: &io::Error) -> bool } pub(crate) fn partial_eq_bincode(first: &bincode::ErrorKind, second: &bincode::ErrorKind) -> bool { + use bincode::ErrorKind::*; match *first { - bincode::ErrorKind::Io(ref f) => match *second { - bincode::ErrorKind::Io(ref s) => partial_eq_io_error(f, s), - _ => false, - }, - bincode::ErrorKind::InvalidUtf8Encoding(f) => match *second { - bincode::ErrorKind::InvalidUtf8Encoding(s) => f == s, - _ => false, - }, - bincode::ErrorKind::InvalidBoolEncoding(f) => match *second { - bincode::ErrorKind::InvalidBoolEncoding(s) => f == s, - _ => false, - }, - bincode::ErrorKind::InvalidCharEncoding => { - matches!(*second, bincode::ErrorKind::InvalidCharEncoding) - }, - bincode::ErrorKind::InvalidTagEncoding(f) => match *second { - bincode::ErrorKind::InvalidTagEncoding(s) => f == s, - _ => false, - }, - bincode::ErrorKind::DeserializeAnyNotSupported => { - matches!(*second, bincode::ErrorKind::DeserializeAnyNotSupported) - }, - bincode::ErrorKind::SizeLimit => matches!(*second, bincode::ErrorKind::SizeLimit), - bincode::ErrorKind::SequenceMustHaveLength => { - matches!(*second, bincode::ErrorKind::SequenceMustHaveLength) - }, - bincode::ErrorKind::Custom(ref f) => match *second { - bincode::ErrorKind::Custom(ref s) => f == s, - _ => false, - }, + Io(ref f) => matches!(*second, Io(ref s) if partial_eq_io_error(f, s)), + InvalidUtf8Encoding(f) => matches!(*second, InvalidUtf8Encoding(s) if f == s), + InvalidBoolEncoding(f) => matches!(*second, InvalidBoolEncoding(s) if f == s), + InvalidCharEncoding => matches!(*second, InvalidCharEncoding), + InvalidTagEncoding(f) => matches!(*second, InvalidTagEncoding(s) if f == s), + DeserializeAnyNotSupported => matches!(*second, DeserializeAnyNotSupported), + SizeLimit => matches!(*second, SizeLimit), + SequenceMustHaveLength => matches!(*second, SequenceMustHaveLength), + Custom(ref f) => matches!(*second, Custom(ref s) if f == s), } } diff --git a/server/Cargo.toml b/server/Cargo.toml index 25fe6ba4db..e0a6493d08 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -17,7 +17,7 @@ specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "spec tracing = "0.1" specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" } -vek = "0.11.0" +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } uvth = "3.1.1" futures-util = "0.3" futures-executor = "0.3" diff --git a/server/src/chunk_generator.rs b/server/src/chunk_generator.rs index 3e769956da..b69d597c12 100644 --- a/server/src/chunk_generator.rs +++ b/server/src/chunk_generator.rs @@ -1,5 +1,5 @@ #[cfg(not(feature = "worldgen"))] -use crate::test_world::World; +use crate::test_world::{IndexOwned, World}; use common::{generation::ChunkSupplement, terrain::TerrainChunk}; use crossbeam::channel; use hashbrown::{hash_map::Entry, HashMap}; @@ -9,11 +9,12 @@ use std::sync::{ Arc, }; use vek::*; -#[cfg(feature = "worldgen")] use world::World; +#[cfg(feature = "worldgen")] +use world::{IndexOwned, World}; type ChunkGenResult = ( Vec2, - Result<(TerrainChunk, ChunkSupplement), EcsEntity>, + Result<(TerrainChunk, ChunkSupplement), Option>, ); pub struct ChunkGenerator { @@ -34,10 +35,11 @@ impl ChunkGenerator { pub fn generate_chunk( &mut self, - entity: EcsEntity, + entity: Option, key: Vec2, thread_pool: &mut uvth::ThreadPool, world: Arc, + index: IndexOwned, ) { let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) { v @@ -48,8 +50,9 @@ impl ChunkGenerator { v.insert(Arc::clone(&cancel)); let chunk_tx = self.chunk_tx.clone(); thread_pool.execute(move || { + let index = index.as_index_ref(); let payload = world - .generate_chunk(key, || cancel.load(Ordering::Relaxed)) + .generate_chunk(index, key, || cancel.load(Ordering::Relaxed)) .map_err(|_| entity); let _ = chunk_tx.send((key, payload)); }); @@ -74,4 +77,10 @@ impl ChunkGenerator { cancel.store(true, Ordering::Relaxed); } } + + pub fn cancel_all(&mut self) { + self.pending_chunks.drain().for_each(|(_, cancel)| { + cancel.store(true, Ordering::Relaxed); + }); + } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7a3da0ba47..2d15bc6921 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1473,7 +1473,7 @@ fn handle_debug_column( let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32); let chunk = sim.get(chunk_pos)?; - let col = sampler.get((wpos, server.world.index()))?; + let col = sampler.get((wpos, server.index.as_index_ref()))?; let downhill = chunk.downhill; let river = &chunk.river; let flux = chunk.flux; diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 5e910452c9..206136aadc 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -119,7 +119,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { .create_object(Pos(pos), comp::object::Body::CampfireLit) .with(LightEmitter { col: Rgb::new(1.0, 0.65, 0.2), - strength: 2.0, + strength: 5.0, flicker: 1.0, animated: true, }) diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index b09f4c9f0d..f03fa99906 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -33,6 +33,14 @@ pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) { /// other players. If the entity that killed it had stats, then give it exp for /// the kill. Experience given is equal to the level of the entity that was /// killed times 10. +// NOTE: Clippy incorrectly warns about a needless collect here because it does not +// understand that the pet count (which is computed during the first iteration over the +// members in range) is actually used by the second iteration over the members in range; +// since we have no way of knowing the pet count before the first loop finishes, we +// definitely need at least two loops. Then (currently) our only options are to store +// the member list in temporary space (e.g. by collecting to a vector), or to repeat +// the loop; but repeating the loop would currently be very inefficient since it has to +// rescan every entity on the server again. #[allow(clippy::needless_collect)] pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSource) { let state = server.state_mut(); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index de201fd6fa..a646b561f9 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -337,6 +337,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .and_then(|ldt| slot::loadout_remove(slot, ldt)), }; + // FIXME: We should really require the drop and write to be atomic! if let (Some(item), Some(pos)) = (item, state.ecs().read_storage::().get(entity)) { @@ -363,6 +364,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let recipe_book = default_recipe_book(); let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok()); + // FIXME: We should really require the drop and write to be atomic! if craft_result.is_some() { let _ = state.ecs().write_storage().insert( entity, diff --git a/server/src/lib.rs b/server/src/lib.rs index 241c9588f8..aef6fe1dbb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -33,7 +33,7 @@ use common::{ cmd::ChatCommand, comp::{self, ChatType}, event::{EventBus, ServerEvent}, - msg::{ClientState, ServerInfo, ServerMsg}, + msg::{server::WorldMapMsg, ClientState, ServerInfo, ServerMsg}, outcome::Outcome, recipe::default_recipe_book, state::{State, TimeOfDay}, @@ -55,15 +55,15 @@ use std::{ time::{Duration, Instant}, }; #[cfg(not(feature = "worldgen"))] -use test_world::{World, WORLD_SIZE}; +use test_world::{IndexOwned, World}; use tracing::{debug, error, info, warn}; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; #[cfg(feature = "worldgen")] use world::{ civ::SiteKind, - sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, WORLD_SIZE}, - World, + sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, + IndexOwned, World, }; #[macro_use] extern crate diesel; @@ -82,7 +82,8 @@ pub struct Tick(u64); pub struct Server { state: State, world: Arc, - map: Vec, + index: IndexOwned, + map: WorldMapMsg, network: Network, @@ -173,7 +174,7 @@ impl Server { state.ecs_mut().insert(AliasValidator::new(banned_words)); #[cfg(feature = "worldgen")] - let world = World::generate(settings.world_seed, WorldOpts { + let (world, index) = World::generate(settings.world_seed, WorldOpts { seed_elements: true, world_file: if let Some(ref opts) = settings.map_file { opts.clone() @@ -184,21 +185,27 @@ impl Server { ..WorldOpts::default() }); #[cfg(feature = "worldgen")] - let map = world.get_map_data(); + let map = world.get_map_data(index.as_index_ref()); #[cfg(not(feature = "worldgen"))] - let world = World::generate(settings.world_seed); + let (world, index) = World::generate(settings.world_seed); #[cfg(not(feature = "worldgen"))] - let map = vec![0]; + let map = WorldMapMsg { + dimensions: Vec2::new(1, 1), + max_height: 1.0, + rgba: vec![0], + horizons: [(vec![0], vec![0]), (vec![0], vec![0])], + }; #[cfg(feature = "worldgen")] let spawn_point = { + let index = index.as_index_ref(); // NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops, // but are needed to be explicit about casting (and to make the compiler stop // complaining) // spawn in the chunk, that is in the middle of the world - let center_chunk: Vec2 = WORLD_SIZE.map(|e| e as i32) / 2; + let center_chunk: Vec2 = world.sim().map_size_lg().chunks().map(i32::from) / 2; // Find a town to spawn in that's close to the centre of the world let spawn_chunk = world @@ -219,11 +226,11 @@ impl Server { // get a z cache for the collumn in which we want to spawn let mut block_sampler = world.sample_blocks(); let z_cache = block_sampler - .get_z_cache(spawn_location, world.index()) + .get_z_cache(spawn_location, index) .expect(&format!("no z_cache found for chunk: {}", spawn_chunk)); // get the minimum and maximum z values at which there could be soild blocks - let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, world.index()); + let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, index); // round range outwards, so no potential air block is missed let min_z = min_z.floor() as i32; let max_z = max_z.ceil() as i32; @@ -239,7 +246,7 @@ impl Server { Vec3::new(spawn_location.x, spawn_location.y, *z), Some(&z_cache), false, - world.index(), + index, ) .map(|b| b.is_air()) .unwrap_or(false) @@ -283,6 +290,7 @@ impl Server { let this = Self { state, world: Arc::new(world), + index, map, network, @@ -489,6 +497,42 @@ impl Server { }, }); + { + // Check for new chunks; cancel and regenerate all chunks if the asset has been + // reloaded. Note that all of these assignments are no-ops, so the + // only work we do here on the fast path is perform a relaxed read on an atomic. + // boolean. + let index = &mut self.index; + let thread_pool = &mut self.thread_pool; + let world = &mut self.world; + let ecs = self.state.ecs_mut(); + + index.reload_colors_if_changed(|index| { + let mut chunk_generator = ecs.write_resource::(); + let client = ecs.read_storage::(); + let mut terrain = ecs.write_resource::(); + + // Cancel all pending chunks. + chunk_generator.cancel_all(); + + if client.is_empty() { + // No cilents, so just clear all terrain. + terrain.clear(); + } else { + // There's at leasat one client, so regenerate all chunks. + terrain.iter().for_each(|(pos, _)| { + chunk_generator.generate_chunk( + None, + pos, + thread_pool, + world.clone(), + index.clone(), + ); + }); + } + }); + } + let end_of_server_tick = Instant::now(); // 8) Update Metrics @@ -700,7 +744,7 @@ impl Server { server_info: self.get_server_info(), time_of_day: *self.state.ecs().read_resource(), max_group_size: self.settings().max_player_group_size, - world_map: (WORLD_SIZE.map(|e| e as u32), self.map.clone()), + world_map: self.map.clone(), recipe_book: (&*default_recipe_book()).clone(), }); @@ -723,7 +767,13 @@ impl Server { self.state .ecs() .write_resource::() - .generate_chunk(entity, key, &mut self.thread_pool, self.world.clone()); + .generate_chunk( + Some(entity), + key, + &mut self.thread_pool, + self.world.clone(), + self.index.clone(), + ); } fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) { diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index e2d8a8c15f..ac5b8e098f 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -206,7 +206,6 @@ impl StateExt for State { } } - #[allow(clippy::map_identity)] // TODO: Pending review in #587 fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { let (body, stats, inventory, loadout) = components; // Make sure physics are accepted. @@ -215,7 +214,6 @@ impl StateExt for State { // Notify clients of a player list update let client_uid = self .read_component_cloned::(entity) - .map(|u| u) .expect("Client doesn't have a Uid!!!"); self.notify_registered_clients(ServerMsg::PlayerListUpdate( diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 739d1988b1..65277a7d9a 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -60,7 +60,7 @@ impl<'a> System<'a> for Sys { 'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() { let (chunk, supplement) = match res { Ok((chunk, supplement)) => (chunk, supplement), - Err(entity) => { + Err(Some(entity)) => { if let Some(client) = clients.get_mut(entity) { client.notify(ServerMsg::TerrainChunkUpdate { key, @@ -69,6 +69,9 @@ impl<'a> System<'a> for Sys { } continue 'insert_terrain_chunks; }, + Err(None) => { + continue 'insert_terrain_chunks; + }, }; // Send the chunk to all nearby players. for (view_distance, pos, client) in (&players, &positions, &mut clients) diff --git a/server/src/test_world.rs b/server/src/test_world.rs index b95fabfb61..4df254bf21 100644 --- a/server/src/test_world.rs +++ b/server/src/test_world.rs @@ -1,23 +1,49 @@ use common::{ generation::{ChunkSupplement, EntityInfo}, - terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, + terrain::{Block, BlockKind, MapSizeLg, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, vol::{ReadVol, RectVolSize, Vox, WriteVol}, }; use rand::{prelude::*, rngs::SmallRng}; use std::time::Duration; use vek::*; -pub const WORLD_SIZE: Vec2 = Vec2 { x: 1, y: 1 }; +const DEFAULT_WORLD_CHUNKS_LG: MapSizeLg = + if let Ok(map_size_lg) = MapSizeLg::new(Vec2 { x: 1, y: 1 }) { + map_size_lg + } else { + panic!("Default world chunk size does not satisfy required invariants."); + }; pub struct World; +#[derive(Clone)] +pub struct IndexOwned; + +#[derive(Clone, Copy)] +pub struct IndexRef<'a>(&'a IndexOwned); + +impl IndexOwned { + pub fn reload_colors_if_changed( + &mut self, + _reload: impl FnOnce(&mut Self) -> R, + ) -> Option { + None + } + + pub fn as_index_ref(&self) -> IndexRef { IndexRef(self) } +} + impl World { - pub fn generate(_seed: u32) -> Self { Self } + pub fn generate(_seed: u32) -> (Self, IndexOwned) { (Self, IndexOwned) } pub fn tick(&self, dt: Duration) {} + #[inline(always)] + pub const fn map_size_lg(&self) -> MapSizeLg { DEFAULT_WORLD_CHUNKS_LG } + pub fn generate_chunk( &self, + _index: IndexRef, chunk_pos: Vec2, _should_continue: impl FnMut() -> bool, ) -> Result<(TerrainChunk, ChunkSupplement), ()> { diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index a5d55b2c5f..459cd9657d 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -9,7 +9,7 @@ default-run = "veloren-voxygen" # autobins = false [features] -gl = ["gfx_device_gl"] +gl = ["gfx_device_gl", "gfx_gl"] singleplayer = ["server"] tweak = ["const-tweaker"] hot-anim = ["anim/use-dyn-lib"] @@ -25,6 +25,7 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } +gfx_gl = { version = "0.6.1", optional = true } old_school_gfx_glutin_ext = "0.24" glutin = "0.24.1" winit = { version = "0.22.2", features = ["serde"] } @@ -37,7 +38,7 @@ specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818 specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" } # Mathematics -vek = { version = "0.11.0", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } # Controller gilrs = { version = "0.7", features = ["serde"] } @@ -49,11 +50,11 @@ server = { package = "veloren-server", path = "../server", optional = true } glsl-include = "0.3.1" failure = "0.1.6" dot_vox = "4.0" -image = { version = "0.22.5", default-features = false, features = ["ico", "png"] } +image = { version = "0.23.8", default-features = false, features = ["ico", "png"] } serde = "1.0" serde_derive = "1.0" ron = { version = "0.6", default-features = false } -guillotiere = { git = "https://github.com/Imberflur/guillotiere" } +guillotiere = "0.5.2" msgbox = { git = "https://github.com/bekker/msgbox-rs.git", default-features = false, rev = "68fe39a", optional = true } directories-next = "1.0.1" num = "0.2" @@ -68,6 +69,7 @@ chrono = "0.4.9" bincode = "1.2" deunicode = "1.0" uvth = "3.1.1" +# vec_map = { version = "0.8.2" } const-tweaker = { version = "0.3.1", optional = true } itertools = "0.9.0" diff --git a/voxygen/benches/meshing_benchmark.rs b/voxygen/benches/meshing_benchmark.rs index 45b7a39403..74085505d2 100644 --- a/voxygen/benches/meshing_benchmark.rs +++ b/voxygen/benches/meshing_benchmark.rs @@ -15,7 +15,7 @@ const GEN_SIZE: i32 = 4; pub fn criterion_benchmark(c: &mut Criterion) { // Generate chunks here to test let mut terrain = TerrainGrid::new().unwrap(); - let world = World::generate(42, sim::WorldOpts { + let (world, index) = World::generate(42, sim::WorldOpts { // NOTE: If this gets too expensive, we can turn it off. // TODO: Consider an option to turn off all erosion as well, or even provide altitude // directly with a closure. @@ -23,10 +23,11 @@ pub fn criterion_benchmark(c: &mut Criterion) { world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()), ..Default::default() }); + let index = index.as_index_ref(); (0..GEN_SIZE) .flat_map(|x| (0..GEN_SIZE).map(move |y| Vec2::new(x, y))) .map(|offset| offset + CENTER) - .map(|pos| (pos, world.generate_chunk(pos, || false).unwrap())) + .map(|pos| (pos, world.generate_chunk(index, pos, || false).unwrap())) .for_each(|(key, chunk)| { terrain.insert(key, Arc::new(chunk.0)); }); @@ -132,7 +133,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { c.bench( "meshing", Benchmark::new(&format!("Terrain mesh {}, {}", x, y), move |b| { - b.iter(|| volume.generate_mesh(black_box(range))) + b.iter(|| volume.generate_mesh(black_box((range, Vec2::new(8192, 8192))))) }) // Lower sample size to save time .sample_size(15), diff --git a/voxygen/clippy.toml b/voxygen/clippy.toml new file mode 100644 index 0000000000..fee00d7951 --- /dev/null +++ b/voxygen/clippy.toml @@ -0,0 +1,2 @@ +# Because 7 is just very low, it corresponds to a 6-argument method. +too-many-arguments-threshold = 10 diff --git a/voxygen/examples/character_renderer.rs b/voxygen/examples/character_renderer.rs index 70cfe2a725..aa02b414f4 100644 --- a/voxygen/examples/character_renderer.rs +++ b/voxygen/examples/character_renderer.rs @@ -22,16 +22,8 @@ fn main() { let (_context, device, factory, color_view, depth_view) = init_headless(context, dim); - let mut renderer = render::Renderer::new( - device, - factory, - color_view, - depth_view, - render::AaMode::SsaaX4, - render::CloudMode::Regular, - render::FluidMode::Shiny, - ) - .unwrap(); + let mut renderer = + render::Renderer::new(device, factory, color_view, depth_view, Default::default()).unwrap(); // Create character let body = comp::humanoid::Body::random(); diff --git a/voxygen/src/anim/Cargo.toml b/voxygen/src/anim/Cargo.toml index 506e6f49b1..45b293d874 100644 --- a/voxygen/src/anim/Cargo.toml +++ b/voxygen/src/anim/Cargo.toml @@ -17,7 +17,7 @@ be-dyn-lib = [] default = ["be-dyn-lib"] [dependencies] -vek = { version = "0.11.2", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } common = { package = "veloren-common", path = "../../../common" } libloading = { version = "0.6.2", optional = true } notify = { version = "5.0.0-pre.2", optional = true } diff --git a/voxygen/src/anim/src/biped_large/alpha.rs b/voxygen/src/anim/src/biped_large/alpha.rs index 7646ed3641..7ed3c6fe8c 100644 --- a/voxygen/src/anim/src/biped_large/alpha.rs +++ b/voxygen/src/anim/src/biped_large/alpha.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -61,150 +63,152 @@ impl Animation for AlphaAnimation { let short = (anim_time as f32 * lab as f32 * 16.0).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(slower * 1.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = Quaternion::rotation_z(slower * 1.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.02; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.0); next.main.scale = Vec3::one() * 1.02; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_l.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_l.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_r.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_r.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_r.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1, ); - next.upper_torso.ori = Quaternion::rotation_z(slower * -1.2) * Quaternion::rotation_x(-0.3); + next.upper_torso.orientation = + Quaternion::rotation_z(slower * -1.2) * Quaternion::rotation_x(-0.3); next.upper_torso.scale = Vec3::one(); - next.control.offset = Vec3::new(7.0, 9.0, -10.0); - next.control.ori = Quaternion::rotation_x(slowersmooth * 0.35) + next.control.position = Vec3::new(7.0, 9.0, -10.0); + next.control.orientation = Quaternion::rotation_x(slowersmooth * 0.35) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(slowersmooth * -0.5 + slower * -0.5); next.control.scale = Vec3::one(); if velocity < 0.5 { - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); + next.lower_torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); - next.jaw.ori = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); + next.jaw.orientation = Quaternion::rotation_z(0.0); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 8.0; } else { - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = + next.lower_torso.orientation = Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); next.lower_torso.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothoril * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_x(0.5 + footrotl * -0.16) + next.shoulder_l.orientation = Quaternion::rotation_x(0.5 + footrotl * -0.16) * Quaternion::rotation_y(0.1) * Quaternion::rotation_z(footrotl * 0.1); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothorir * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_x(0.5 + footrotr * -0.16) + next.shoulder_r.orientation = Quaternion::rotation_x(0.5 + footrotr * -0.16) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(footrotr * -0.1); next.shoulder_r.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); next.torso.scale = Vec3::one() / 8.0; } diff --git a/voxygen/src/anim/src/biped_large/idle.rs b/voxygen/src/anim/src/biped_large/idle.rs index 8e9ca72cbc..dffe3d56ae 100644 --- a/voxygen/src/anim/src/biped_large/idle.rs +++ b/voxygen/src/anim/src/biped_large/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -39,122 +41,123 @@ impl Animation for IdleAnimation { let wave_slow = (anim_time as f32 * 0.8).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + torso * 0.2, ) * 1.02; - next.head.ori = Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); + next.head.orientation = + Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + torso * 0.5, ); - next.upper_torso.ori = Quaternion::rotation_z(0.0); + next.upper_torso.orientation = Quaternion::rotation_z(0.0); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1 + torso * 0.15, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.lower_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.09); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.09); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + torso * 0.0, ); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_z(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-5.0, -7.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-5.0, -7.0, 7.0); + next.main.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.6) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 8.0; next diff --git a/voxygen/src/anim/src/biped_large/jump.rs b/voxygen/src/anim/src/biped_large/jump.rs index 0b7725e4d6..037c2dfbd1 100644 --- a/voxygen/src/anim/src/biped_large/jump.rs +++ b/voxygen/src/anim/src/biped_large/jump.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct JumpAnimation; @@ -26,122 +28,122 @@ impl Animation for JumpAnimation { let wave_slow = (anim_time as f32 * 0.8).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + torso * 0.2, ) * 1.02; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + torso * 0.5, ); - next.upper_torso.ori = Quaternion::rotation_x(-0.3); + next.upper_torso.orientation = Quaternion::rotation_x(-0.3); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1 + torso * 0.15, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); + next.lower_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.09); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.09); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + torso * 0.0, ); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_z(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-5.0, -7.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-5.0, -7.0, 7.0); + next.main.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.6) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.5); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.5); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.5); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.5); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.8); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.8); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.8); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.8); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, -5.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, 5.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 8.0; next diff --git a/voxygen/src/anim/src/biped_large/mod.rs b/voxygen/src/anim/src/biped_large/mod.rs index 572f642a59..e812e0cd8b 100644 --- a/voxygen/src/anim/src/biped_large/mod.rs +++ b/voxygen/src/anim/src/biped_large/mod.rs @@ -10,103 +10,69 @@ pub use self::{ wield::WieldAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct BipedLargeSkeleton { - head: Bone, - jaw: Bone, - upper_torso: Bone, - lower_torso: Bone, - tail: Bone, - main: Bone, - second: Bone, - shoulder_l: Bone, - shoulder_r: Bone, - hand_l: Bone, - hand_r: Bone, - leg_l: Bone, - leg_r: Bone, - foot_l: Bone, - foot_r: Bone, - torso: Bone, - control: Bone, -} - -impl BipedLargeSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct BipedLargeSkeleton { + + head, + + jaw, + + upper_torso, + + lower_torso, + + tail, + + main, + + second, + + shoulder_l, + + shoulder_r, + + hand_l, + + hand_r, + + leg_l, + + leg_r, + + foot_l, + + foot_r, + torso, + control, +}); impl Skeleton for BipedLargeSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 15; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"biped_large_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let jaw_mat = self.jaw.compute_base_matrix(); - let upper_torso_mat = self.upper_torso.compute_base_matrix(); - let lower_torso_mat = self.lower_torso.compute_base_matrix(); - let tail_mat = self.tail.compute_base_matrix(); - let main_mat = self.main.compute_base_matrix(); - let second_mat = self.second.compute_base_matrix(); - let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); - let shoulder_r_mat = self.shoulder_r.compute_base_matrix(); - let hand_l_mat = self.hand_l.compute_base_matrix(); - let hand_r_mat = self.hand_r.compute_base_matrix(); - let leg_l_mat = self.leg_l.compute_base_matrix(); - let leg_r_mat = self.leg_r.compute_base_matrix(); - let torso_mat = self.torso.compute_base_matrix(); - let control_mat = self.control.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let upper_torso = Mat4::::from(self.upper_torso); - ( - [ - FigureBoneData::new(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.head.compute_base_matrix() * jaw_mat, - ), - FigureBoneData::new(torso_mat * upper_torso_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat * tail_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * main_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * second_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_r_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * hand_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * hand_r_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat * leg_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat * leg_r_mat), - FigureBoneData::new(self.foot_l.compute_base_matrix()), - FigureBoneData::new(self.foot_r.compute_base_matrix()), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + let torso_mat = base_mat * Mat4::::from(self.torso); + let upper_torso_mat = torso_mat * upper_torso; + let lower_torso_mat = upper_torso_mat * Mat4::::from(self.lower_torso); + let head_mat = upper_torso_mat * Mat4::::from(self.head); + let control_mat = upper_torso_mat * Mat4::::from(self.control); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.jaw.interpolate(&target.jaw, dt); - self.upper_torso.interpolate(&target.upper_torso, dt); - self.lower_torso.interpolate(&target.lower_torso, dt); - self.tail.interpolate(&target.tail, dt); - self.main.interpolate(&target.main, dt); - self.second.interpolate(&target.second, dt); - self.shoulder_l.interpolate(&target.shoulder_l, dt); - self.shoulder_r.interpolate(&target.shoulder_r, dt); - self.hand_l.interpolate(&target.hand_l, dt); - self.hand_r.interpolate(&target.hand_r, dt); - self.leg_l.interpolate(&target.leg_l, dt); - self.leg_r.interpolate(&target.leg_r, dt); - self.foot_l.interpolate(&target.foot_l, dt); - self.foot_r.interpolate(&target.foot_r, dt); - self.torso.interpolate(&target.torso, dt); - self.control.interpolate(&target.control, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_mat), + make_bone(head_mat * Mat4::::from(self.jaw)), + make_bone(upper_torso_mat), + make_bone(lower_torso_mat), + make_bone(lower_torso_mat * Mat4::::from(self.tail)), + make_bone(control_mat * Mat4::::from(self.main)), + make_bone(control_mat * Mat4::::from(self.second)), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_l)), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_r)), + make_bone(control_mat * Mat4::::from(self.hand_l)), + make_bone(control_mat * Mat4::::from(self.hand_r)), + make_bone(lower_torso_mat * Mat4::::from(self.leg_l)), + make_bone(lower_torso_mat * Mat4::::from(self.leg_r)), + make_bone(base_mat * Mat4::::from(self.foot_l)), + make_bone(base_mat * Mat4::::from(self.foot_r)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/biped_large/run.rs b/voxygen/src/anim/src/biped_large/run.rs index a7929510f9..ede6b30ebb 100644 --- a/voxygen/src/anim/src/biped_large/run.rs +++ b/voxygen/src/anim/src/biped_large/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -57,125 +59,127 @@ impl Animation for RunAnimation { let shortalt = (anim_time as f32 * lab as f32 * 16.0 + PI / 2.0).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = + Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + shortalt * -1.5, ); - next.upper_torso.ori = Quaternion::rotation_z(short * 0.18); + next.upper_torso.orientation = Quaternion::rotation_z(short * 0.18); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); + next.lower_torso.orientation = + Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 * 0.0); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 * 0.0); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_z(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); - next.main.offset = Vec3::new(-5.0, -7.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-5.0, -7.0, 7.0); + next.main.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.6) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothoril * -3.0, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_x(footrotl * -0.36) + next.shoulder_l.orientation = Quaternion::rotation_x(footrotl * -0.36) * Quaternion::rotation_y(0.1) * Quaternion::rotation_z(footrotl * 0.3); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothorir * -3.0, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_x(footrotr * -0.36) + next.shoulder_r.orientation = Quaternion::rotation_x(footrotr * -0.36) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(footrotr * -0.3); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -1.0 + -skeleton_attr.hand.0, skeleton_attr.hand.1 + foothoril * -4.0, skeleton_attr.hand.2 + foothoril * 1.0, ); - next.hand_l.ori = Quaternion::rotation_x(0.15 + (handhoril * -1.2).max(-0.3)) + next.hand_l.orientation = Quaternion::rotation_x(0.15 + (handhoril * -1.2).max(-0.3)) * Quaternion::rotation_y(handhoril * -0.1); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( 1.0 + skeleton_attr.hand.0, skeleton_attr.hand.1 + foothorir * -4.0, skeleton_attr.hand.2 + foothorir * 1.0, ); - next.hand_r.ori = Quaternion::rotation_x(0.15 + (handhorir * -1.2).max(-0.3)) + next.hand_r.orientation = Quaternion::rotation_x(0.15 + (handhorir * -1.2).max(-0.3)) * Quaternion::rotation_y(handhorir * 0.1); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 0.98; - next.leg_l.ori = + next.leg_l.orientation = Quaternion::rotation_z(short * 0.18) * Quaternion::rotation_x(foothoril * 0.3); next.leg_l.scale = Vec3::one() * 0.98; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 0.98; - next.leg_r.ori = + next.leg_r.orientation = Quaternion::rotation_z(short * 0.18) * Quaternion::rotation_x(foothorir * 0.3); next.leg_r.scale = Vec3::one() * 0.98; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1 + foothoril * 8.5, skeleton_attr.foot.2 + ((footvertl * 6.5).max(0.0)), ) / 8.0; - next.foot_l.ori = + next.foot_l.orientation = Quaternion::rotation_x(-0.5 + footrotl * 0.85) * Quaternion::rotation_y(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1 + foothorir * 8.5, skeleton_attr.foot.2 + ((footvertr * 6.5).max(0.0)), ) / 8.0; - next.foot_r.ori = + next.foot_r.orientation = Quaternion::rotation_x(-0.5 + footrotr * 0.85) * Quaternion::rotation_y(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); next.torso.scale = Vec3::one() / 8.0; next } diff --git a/voxygen/src/anim/src/biped_large/wield.rs b/voxygen/src/anim/src/biped_large/wield.rs index 7ab5867229..b26a8322cb 100644 --- a/voxygen/src/anim/src/biped_large/wield.rs +++ b/voxygen/src/anim/src/biped_large/wield.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct WieldAnimation; @@ -69,172 +71,175 @@ impl Animation for WieldAnimation { let shortalt = (anim_time as f32 * lab as f32 * 16.0 + PI / 2.0).sin(); - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.0); next.main.scale = Vec3::one() * 1.02; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_l.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_l.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_r.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_r.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_r.scale = Vec3::one() * 1.02; if velocity < 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + breathe * 0.2, ) * 1.02; - next.head.ori = + next.head.orientation = Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + breathe * 0.5, ); - next.upper_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1 + breathe * 0.15, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.lower_torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); - next.jaw.ori = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); + next.jaw.orientation = Quaternion::rotation_z(0.0); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + breathe * 0.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + breathe * 0.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(test * 0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(test * 0.0); next.torso.scale = Vec3::one() / 8.0; - next.control.offset = Vec3::new(7.0, 9.0, -10.0); - next.control.ori = Quaternion::rotation_x(test * 0.02) + next.control.position = Vec3::new(7.0, 9.0, -10.0); + next.control.orientation = Quaternion::rotation_x(test * 0.02) * Quaternion::rotation_y(test * 0.02) * Quaternion::rotation_z(test * 0.02); next.control.scale = Vec3::one(); } else { - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = + Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + shortalt * -1.5, ); - next.upper_torso.ori = Quaternion::rotation_z(short * 0.18); + next.upper_torso.orientation = Quaternion::rotation_z(short * 0.18); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = + next.lower_torso.orientation = Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); next.lower_torso.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothoril * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_x(0.5 + footrotl * -0.16) + next.shoulder_l.orientation = Quaternion::rotation_x(0.5 + footrotl * -0.16) * Quaternion::rotation_y(0.1) * Quaternion::rotation_z(footrotl * 0.1); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothorir * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_x(0.5 + footrotr * -0.16) + next.shoulder_r.orientation = Quaternion::rotation_x(0.5 + footrotr * -0.16) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(footrotr * -0.1); next.shoulder_r.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); next.torso.scale = Vec3::one() / 8.0; - next.control.offset = Vec3::new(7.0, 9.0, -10.0); - next.control.ori = Quaternion::rotation_x(test * 0.02) + next.control.position = Vec3::new(7.0, 9.0, -10.0); + next.control.orientation = Quaternion::rotation_x(test * 0.02) * Quaternion::rotation_y(test * 0.02) * Quaternion::rotation_z(test * 0.02); next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/bird_medium/feed.rs b/voxygen/src/anim/src/bird_medium/feed.rs index a5ca080177..51db636ca4 100644 --- a/voxygen/src/anim/src/bird_medium/feed.rs +++ b/voxygen/src/anim/src/bird_medium/feed.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::ops::Mul; -use vek::*; pub struct FeedAnimation; @@ -40,54 +42,55 @@ impl Animation for FeedAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0 + 1.0, -2.0 + skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(duck_head_look.x) + next.head.position = + Vec3::new(0.0, skeleton_attr.head.0 + 1.0, -2.0 + skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(duck_head_look.x) * Quaternion::rotation_x(-0.3 / skeleton_attr.feed + wave_slow_cos * 0.03 + wave * 0.1); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0 + skeleton_attr.feed, -1.0 - 5.0 * (skeleton_attr.feed - 1.0) + wave_slow * 0.3 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_x(-0.5 * skeleton_attr.feed) + next.torso.orientation = Quaternion::rotation_x(-0.5 * skeleton_attr.feed) * Quaternion::rotation_y(wave_slow * 0.03); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(wave_slow_cos * 0.03); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(wave_slow_cos * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_y(0.4 - wave_slow * 0.1); + next.wing_l.orientation = Quaternion::rotation_y(0.4 - wave_slow * 0.1); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y(-0.4 + wave_slow * 0.1); + next.wing_r.orientation = Quaternion::rotation_y(-0.4 + wave_slow * 0.1); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_y(0.0); + next.leg_l.orientation = Quaternion::rotation_y(0.0); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_medium/fly.rs b/voxygen/src/anim/src/bird_medium/fly.rs index 23477c3671..8fdc61b7ed 100644 --- a/voxygen/src/anim/src/bird_medium/fly.rs +++ b/voxygen/src/anim/src/bird_medium/fly.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct FlyAnimation; @@ -28,60 +30,61 @@ impl Animation for FlyAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0 + 0.5, skeleton_attr.head.1 + center * 0.5 - 1.0, ); - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); + next.head.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0 + centeroffset * 0.6, center * 0.6 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_y(center * 0.05); + next.torso.orientation = Quaternion::rotation_y(center * 0.05); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + centeroffset * 0.6, ); - next.tail.ori = Quaternion::rotation_x(center * 0.03); + next.tail.orientation = Quaternion::rotation_x(center * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_y((0.57 + footl * 1.2).max(0.0)); + next.wing_l.orientation = Quaternion::rotation_y((0.57 + footl * 1.2).max(0.0)); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y((-0.57 + footr * 1.2).min(0.0)); + next.wing_r.orientation = Quaternion::rotation_y((-0.57 + footr * 1.2).min(0.0)); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_x(-1.3 + footl * 0.06); + next.leg_l.orientation = Quaternion::rotation_x(-1.3 + footl * 0.06); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(-1.3 + footr * 0.06); + next.leg_r.orientation = Quaternion::rotation_x(-1.3 + footr * 0.06); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_medium/idle.rs b/voxygen/src/anim/src/bird_medium/idle.rs index 33b430636b..2b7765121b 100644 --- a/voxygen/src/anim/src/bird_medium/idle.rs +++ b/voxygen/src/anim/src/bird_medium/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::ops::Mul; -use vek::*; pub struct IdleAnimation; @@ -38,53 +40,53 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(duck_head_look.x) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(duck_head_look.x) * Quaternion::rotation_x(-duck_head_look.y.abs() + wave_slow_cos * 0.03); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0, wave_slow * 0.3 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_y(wave_slow * 0.03); + next.torso.orientation = Quaternion::rotation_y(wave_slow * 0.03); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(wave_slow_cos * 0.03); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(wave_slow_cos * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_z(0.0); + next.wing_l.orientation = Quaternion::rotation_z(0.0); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y(0.0); + next.wing_r.orientation = Quaternion::rotation_y(0.0); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_y(0.0); + next.leg_l.orientation = Quaternion::rotation_y(0.0); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_medium/mod.rs b/voxygen/src/anim/src/bird_medium/mod.rs index fddd7dfa07..09096ab094 100644 --- a/voxygen/src/anim/src/bird_medium/mod.rs +++ b/voxygen/src/anim/src/bird_medium/mod.rs @@ -6,69 +6,46 @@ pub mod run; // Reexports pub use self::{feed::FeedAnimation, fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct BirdMediumSkeleton { - head: Bone, - torso: Bone, - tail: Bone, - wing_l: Bone, - wing_r: Bone, - leg_l: Bone, - leg_r: Bone, -} - -impl BirdMediumSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct BirdMediumSkeleton { + + head, + + torso, + + tail, + + wing_l, + + wing_r, + + leg_l, + + leg_r, +}); impl Skeleton for BirdMediumSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 7; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"bird_medium_compute_mats\0"; - fn bone_count(&self) -> usize { 7 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_medium_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); - ( - [ - FigureBoneData::new(torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new(torso_mat), - FigureBoneData::new(torso_mat * self.tail.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.wing_l.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.wing_r.compute_base_matrix()), - FigureBoneData::new(self.leg_l.compute_base_matrix()), - FigureBoneData::new(self.leg_r.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.torso.interpolate(&target.torso, dt); - self.tail.interpolate(&target.tail, dt); - self.wing_l.interpolate(&target.wing_l, dt); - self.wing_r.interpolate(&target.wing_r, dt); - self.leg_l.interpolate(&target.leg_l, dt); - self.leg_r.interpolate(&target.leg_r, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat * Mat4::::from(self.head)), + make_bone(torso_mat), + make_bone(torso_mat * Mat4::::from(self.tail)), + make_bone(torso_mat * Mat4::::from(self.wing_l)), + make_bone(torso_mat * Mat4::::from(self.wing_r)), + make_bone(base_mat * Mat4::::from(self.leg_l)), + make_bone(base_mat * Mat4::::from(self.leg_r)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/bird_medium/run.rs b/voxygen/src/anim/src/bird_medium/run.rs index c86f16a86b..8cfb0699d4 100644 --- a/voxygen/src/anim/src/bird_medium/run.rs +++ b/voxygen/src/anim/src/bird_medium/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -28,60 +30,61 @@ impl Animation for RunAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + center * 0.5, ); - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); + next.head.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0 + centeroffset * 0.6, center * 0.6 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_y(center * 0.05); + next.torso.orientation = Quaternion::rotation_y(center * 0.05); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + centeroffset * 0.6, ); - next.tail.ori = Quaternion::rotation_x(center * 0.03); + next.tail.orientation = Quaternion::rotation_x(center * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_y((footl * 0.35).max(0.0)); + next.wing_l.orientation = Quaternion::rotation_y((footl * 0.35).max(0.0)); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y((footr * 0.35).min(0.0)); + next.wing_r.orientation = Quaternion::rotation_y((footr * 0.35).min(0.0)); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + footl * 1.0, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_x(footl * 0.5); + next.leg_l.orientation = Quaternion::rotation_x(footl * 0.5); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + footr * 1.0, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(footr * 0.5); + next.leg_r.orientation = Quaternion::rotation_x(footr * 0.5); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_small/idle.rs b/voxygen/src/anim/src/bird_small/idle.rs index f25b9a8810..94b540d9e9 100644 --- a/voxygen/src/anim/src/bird_small/idle.rs +++ b/voxygen/src/anim/src/bird_small/idle.rs @@ -1,6 +1,6 @@ use super::{super::Animation, BirdSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct IdleAnimation; @@ -21,20 +21,20 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.wing_l.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_l.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_l.scale = Vec3::one() / 10.88; - next.wing_r.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_r.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_r.scale = Vec3::one() / 10.88; next diff --git a/voxygen/src/anim/src/bird_small/jump.rs b/voxygen/src/anim/src/bird_small/jump.rs index 059480cc4a..671d838d67 100644 --- a/voxygen/src/anim/src/bird_small/jump.rs +++ b/voxygen/src/anim/src/bird_small/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, BirdSmallSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -21,20 +21,20 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.wing_l.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_l.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_l.scale = Vec3::one() / 10.88; - next.wing_r.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_r.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_r.scale = Vec3::one() / 10.88; next diff --git a/voxygen/src/anim/src/bird_small/mod.rs b/voxygen/src/anim/src/bird_small/mod.rs index dde8a69b6f..3684e6d977 100644 --- a/voxygen/src/anim/src/bird_small/mod.rs +++ b/voxygen/src/anim/src/bird_small/mod.rs @@ -5,71 +5,40 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone)] -pub struct BirdSmallSkeleton { - head: Bone, - torso: Bone, - wing_l: Bone, - wing_r: Bone, -} - -impl BirdSmallSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self { - head: Bone::default(), - torso: Bone::default(), - wing_l: Bone::default(), - wing_r: Bone::default(), - } - } -} +skeleton_impls!(struct BirdSmallSkeleton { + + head, + + torso, + + wing_l, + + wing_r, +}); impl Skeleton for BirdSmallSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 4; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"bird_small_compute_mats\0"; - fn bone_count(&self) -> usize { 4 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_small_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); - ( - [ - FigureBoneData::new(self.head.compute_base_matrix() * torso_mat), - FigureBoneData::new(torso_mat), - FigureBoneData::new(self.wing_l.compute_base_matrix() * torso_mat), - FigureBoneData::new(self.wing_r.compute_base_matrix() * torso_mat), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.torso.interpolate(&target.torso, dt); - self.wing_l.interpolate(&target.wing_l, dt); - self.wing_r.interpolate(&target.wing_r, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat * Mat4::::from(self.head)), + make_bone(torso_mat), + make_bone(torso_mat * Mat4::::from(self.wing_l)), + make_bone(torso_mat * Mat4::::from(self.wing_r)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/bird_small/run.rs b/voxygen/src/anim/src/bird_small/run.rs index 167889c0d0..80c06aa5e8 100644 --- a/voxygen/src/anim/src/bird_small/run.rs +++ b/voxygen/src/anim/src/bird_small/run.rs @@ -1,6 +1,6 @@ use super::{super::Animation, BirdSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct RunAnimation; @@ -21,20 +21,20 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.wing_l.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_l.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_l.scale = Vec3::one() / 10.88; - next.wing_r.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_r.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_r.scale = Vec3::one() / 10.88; next diff --git a/voxygen/src/anim/src/character/alpha.rs b/voxygen/src/anim/src/character/alpha.rs index dcf8507717..697c369581 100644 --- a/voxygen/src/anim/src/character/alpha.rs +++ b/voxygen/src/anim/src/character/alpha.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -58,436 +60,446 @@ impl Animation for AlphaAnimation { match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.head.offset = + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.25) + next.head.orientation = Quaternion::rotation_z(slow * -0.25) * Quaternion::rotation_x(0.0 + slow * 0.15) * Quaternion::rotation_y(slow * -0.15); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(slow * 0.4) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(slow * 0.4) * Quaternion::rotation_x(0.0 + slow * -0.2) * Quaternion::rotation_y(slow * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = next.chest.ori * -0.3; + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = next.chest.orientation * -0.3; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = next.chest.ori * -0.45; + next.shorts.position = + Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = next.chest.orientation * -0.45; - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - next.control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + next.control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); + next.control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4) * Quaternion::rotation_y(slow * -1.3) * Quaternion::rotation_z(1.4 + slow * -0.5); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, slow * -3.0 + quick * 3.0 - 4.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slow * 0.6) + next.l_foot.orientation = Quaternion::rotation_x(slow * 0.6) * Quaternion::rotation_y((slow * -0.2).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, slow * 3.0 + quick * -3.0 + 5.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slow * -0.6) + next.r_foot.orientation = Quaternion::rotation_x(slow * -0.6) * Quaternion::rotation_y((slow * 0.2).min(0.0)); next.r_foot.scale = Vec3::one(); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Dagger(_)) => { - next.head.offset = + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.25) + next.head.orientation = Quaternion::rotation_z(slow * -0.25) * Quaternion::rotation_x(0.0 + slow * 0.15) * Quaternion::rotation_y(slow * -0.15); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(slow * 0.4) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(slow * 0.4) * Quaternion::rotation_x(0.0 + slow * -0.2) * Quaternion::rotation_y(slow * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = next.chest.ori * -0.3; + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = next.chest.orientation * -0.3; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = next.chest.ori * -0.45; + next.shorts.position = + Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = next.chest.orientation * -0.45; // TODO: Fix animation - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0); + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0); next.l_hand.scale = Vec3::one() * 1.12; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); - next.l_control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - next.l_control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + next.l_control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); + next.l_control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4) * Quaternion::rotation_y(slow * -1.3) * Quaternion::rotation_z(1.4 + slow * -0.5); next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0); + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0); next.r_hand.scale = Vec3::one() * 1.12; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0); - next.r_control.offset = Vec3::new(8.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(8.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); - // next.r_control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - // next.r_control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + // next.r_control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, + // 2.0); next.r_control.orientation = + // Quaternion::rotation_x(-1.4 + slow * 0.4) // * Quaternion::rotation_y(slow * -1.3) // * Quaternion::rotation_z(1.4 + slow * -0.5); // next.r_control.scale = Vec3::one(); - // next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - // next.r_hand.ori = Quaternion::rotation_x(1.27); + // next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + // next.r_hand.orientation = Quaternion::rotation_x(1.27); // next.r_hand.scale = Vec3::one() * 1.05; - // next.control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - // next.control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + // next.control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); + // next.control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4) // * Quaternion::rotation_y(slow * -1.3) // * Quaternion::rotation_z(1.4 + slow * -0.5); // next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, slow * -3.0 + quick * 3.0 - 4.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slow * 0.6) + next.l_foot.orientation = Quaternion::rotation_x(slow * 0.6) * Quaternion::rotation_y((slow * -0.2).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, slow * 3.0 + quick * -3.0 + 5.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slow * -0.6) + next.r_foot.orientation = Quaternion::rotation_x(slow * -0.6) * Quaternion::rotation_y((slow * 0.2).min(0.0)); next.r_foot.scale = Vec3::one(); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Axe(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0 + slowax * 2.0, 0.0 + skeleton_attr.head.0 + slowax * -2.0, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(slowax * 0.25) + next.head.orientation = Quaternion::rotation_z(slowax * 0.25) * Quaternion::rotation_x(0.0 + slowax * 0.2) * Quaternion::rotation_y(slowax * 0.2); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slowax * 0.2) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(slowax * 0.2) * Quaternion::rotation_x(0.0 + slowax * 0.2) * Quaternion::rotation_y(slowax * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = next.chest.ori * -0.2; + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = next.chest.orientation * -0.2; - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = next.chest.ori * -0.15; + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = next.chest.orientation * -0.15; - next.l_hand.offset = Vec3::new(-4.0, 3.0, 2.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3) + next.l_hand.position = Vec3::new(-4.0, 3.0, 2.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(-2.5, 9.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3) + next.r_hand.position = Vec3::new(-2.5, 9.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-6.0, 10.0, -5.0); - next.main.ori = Quaternion::rotation_x(1.27) + next.main.position = Vec3::new(-6.0, 10.0, -5.0); + next.main.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(-0.3) * Quaternion::rotation_z(-0.8); - next.lantern.ori = Quaternion::rotation_x(slowax * -0.7 + 0.4) + next.lantern.orientation = Quaternion::rotation_x(slowax * -0.7 + 0.4) * Quaternion::rotation_y(slowax * 0.4); - next.control.offset = Vec3::new(0.0, 0.0 + slowax * 8.2, 6.0); - next.control.ori = Quaternion::rotation_x(0.8) + next.control.position = Vec3::new(0.0, 0.0 + slowax * 8.2, 6.0); + next.control.orientation = Quaternion::rotation_x(0.8) * Quaternion::rotation_y(-0.3) * Quaternion::rotation_z(-0.7 + slowax * -1.9); next.control.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = + Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(3.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(3.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.head.offset = + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slower * 0.03) + next.head.orientation = Quaternion::rotation_z(slower * 0.03) * Quaternion::rotation_x(slowersmooth * 0.1) * Quaternion::rotation_y(slower * 0.05 + slowersmooth * 0.06) * Quaternion::rotation_z((slowersmooth * -0.4).max(0.0)); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slower * 0.18 + slowersmooth * 0.15) - * Quaternion::rotation_x(0.0 + slower * 0.18 + slowersmooth * 0.15) - * Quaternion::rotation_y(slower * 0.18 + slowersmooth * 0.15); + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = + Quaternion::rotation_z(slower * 0.18 + slowersmooth * 0.15) + * Quaternion::rotation_x(0.0 + slower * 0.18 + slowersmooth * 0.15) + * Quaternion::rotation_y(slower * 0.18 + slowersmooth * 0.15); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) - * Quaternion::rotation_x(0.0 + slower * -0.1) - * Quaternion::rotation_y(slower * -0.1); + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = + Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) + * Quaternion::rotation_x(0.0 + slower * -0.1) + * Quaternion::rotation_y(slower * -0.1); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) - * Quaternion::rotation_x(0.0 + slower * -0.1) - * Quaternion::rotation_y(slower * -0.1); + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = + Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) + * Quaternion::rotation_x(0.0 + slower * -0.1) + * Quaternion::rotation_y(slower * -0.1); - next.lantern.ori = Quaternion::rotation_x(slower * -0.7 + 0.4) + next.lantern.orientation = Quaternion::rotation_x(slower * -0.7 + 0.4) * Quaternion::rotation_y(slower * 0.4); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; if velocity > 0.5 { - next.l_foot.offset = + next.l_foot.position = Vec3::new(-skeleton_attr.foot.0, foot * -6.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4) + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4) * Quaternion::rotation_z((slower * 0.3).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, foot * 6.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4) + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4) * Quaternion::rotation_z((slower * 0.3).max(0.0)); next.r_foot.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.15); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.15); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.5, skeleton_attr.foot.2 + (slower * 2.5).max(0.0), ); - next.l_foot.ori = Quaternion::rotation_x(slower * -0.2 - 0.2) + next.l_foot.orientation = Quaternion::rotation_x(slower * -0.2 - 0.2) * Quaternion::rotation_z((slower * 1.0).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 3.5 - slower * 2.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slower * 0.1) + next.r_foot.orientation = Quaternion::rotation_x(slower * 0.1) * Quaternion::rotation_z((slower * 0.5).max(0.0)); next.r_foot.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - //next.control.offset = Vec3::new(-4.0, 3.0 + slower * 2.0, 5.0 + slower * - // 5.0); next.control.ori = Quaternion::rotation_x() + //next.control.position = Vec3::new(-4.0, 3.0 + slower * 2.0, 5.0 + slower * + // 5.0); next.control.orientation = Quaternion::rotation_x() // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(1.4); next.control.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(-1.5 + slower * 1.5) + next.control.position = Vec3::new(-8.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(-1.5 + slower * 1.5) * Quaternion::rotation_y(slowersmooth * 0.35 - 0.3) * Quaternion::rotation_z(1.4 + slowersmooth * 0.2); next.control.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Staff(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, 0.0 + skeleton_attr.head.0, /* + decel * 0.8 */ // Had some clipping issues skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(decel * 0.25) + next.head.orientation = Quaternion::rotation_z(decel * 0.25) * Quaternion::rotation_x(0.0 + decel * 0.1) * Quaternion::rotation_y(decel * -0.1); - next.chest.ori = Quaternion::rotation_z(decel * -0.2) + next.chest.orientation = Quaternion::rotation_z(decel * -0.2) * Quaternion::rotation_x(0.0 + decel * -0.2) * Quaternion::rotation_y(decel * 0.2); - next.belt.ori = Quaternion::rotation_z(decel * -0.1) + next.belt.orientation = Quaternion::rotation_z(decel * -0.1) * Quaternion::rotation_x(0.0 + decel * -0.1) * Quaternion::rotation_y(decel * 0.1); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(decel * -0.08) + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = Quaternion::rotation_z(decel * -0.08) * Quaternion::rotation_x(0.0 + decel * -0.08) * Quaternion::rotation_y(decel * 0.08); - next.l_hand.offset = Vec3::new(0.0, 1.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(0.0, 1.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(0.0, 0.0, 10.0); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.0, 0.0, 10.0); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -4.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -4.0); + next.main.orientation = Quaternion::rotation_x(-0.3); - next.control.offset = Vec3::new(-8.0 - slow * 1.0, 3.0 - slow * 5.0, 0.0); - next.control.ori = Quaternion::rotation_x(-1.2) + next.control.position = Vec3::new(-8.0 - slow * 1.0, 3.0 - slow * 5.0, 0.0); + next.control.orientation = Quaternion::rotation_x(-1.2) * Quaternion::rotation_y(slow * 1.5) * Quaternion::rotation_z(1.4 + slow * 0.5); next.control.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Shield(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, 0.0 + skeleton_attr.head.0 + decel * 0.8, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(decel * 0.25) + next.head.orientation = Quaternion::rotation_z(decel * 0.25) * Quaternion::rotation_x(0.0 + decel * 0.1) * Quaternion::rotation_y(decel * -0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(decel * -0.2) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(decel * -0.2) * Quaternion::rotation_x(0.0 + decel * -0.2) * Quaternion::rotation_y(decel * 0.2); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.belt.offset = Vec3::new(0.0, 0.0, 0.0); - next.belt.ori = Quaternion::rotation_z(decel * -0.1) + next.belt.position = Vec3::new(0.0, 0.0, 0.0); + next.belt.orientation = Quaternion::rotation_z(decel * -0.1) * Quaternion::rotation_x(0.0 + decel * -0.1) * Quaternion::rotation_y(decel * 0.1); - next.shorts.offset = Vec3::new(0.0, 0.0, 0.0); - next.belt.ori = Quaternion::rotation_z(decel * -0.08) + next.shorts.position = Vec3::new(0.0, 0.0, 0.0); + next.belt.orientation = Quaternion::rotation_z(decel * -0.08) * Quaternion::rotation_x(0.0 + decel * -0.08) * Quaternion::rotation_y(decel * 0.08); - next.l_control.offset = + next.l_control.position = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.l_control.ori = Quaternion::rotation_z(-0.8) + next.l_control.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.0 + accel_med * -0.8) * Quaternion::rotation_y(0.0 + accel_med * -0.4); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0); + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_z(0.0); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_z(0.0); - next.r_control.offset = Vec3::new(8.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(8.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0); + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0); next.r_hand.scale = Vec3::one() * 1.01; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0); }, Some(ToolKind::Debug(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + decel * 0.8, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_x(0.0); + next.head.orientation = Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(decel * -0.2) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(decel * -0.2) * Quaternion::rotation_x(0.0 + decel * -0.2) * Quaternion::rotation_y(decel * 0.2); - next.l_hand.offset = + next.l_hand.position = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.l_hand.ori = Quaternion::rotation_z(-0.8) + next.l_hand.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(accel_med * -0.8) * Quaternion::rotation_y(accel_med * -0.4); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = + next.r_hand.position = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_z(-0.8) + next.r_hand.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(accel_med * -0.8) * Quaternion::rotation_y(accel_med * -0.4); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.main.ori = Quaternion::rotation_z(-0.8) + next.main.position = + Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); + next.main.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.0 + accel_med * -0.8) * Quaternion::rotation_y(0.0 + accel_med * -0.4); next.main.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, _ => {}, } - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, @@ -495,7 +507,7 @@ impl Animation for AlphaAnimation { next.lantern.scale = Vec3::one() * 0.65; next.l_shoulder.scale = Vec3::one() * 1.1; next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/beta.rs b/voxygen/src/anim/src/character/beta.rs index 720ce56bf3..739d4ca406 100644 --- a/voxygen/src/anim/src/character/beta.rs +++ b/voxygen/src/anim/src/character/beta.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; -use vek::*; pub struct BetaAnimation; @@ -46,84 +48,85 @@ impl Animation for BetaAnimation { ) = active_tool_kind { //INTENTION: SWORD - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.18) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(slow * -0.18) * Quaternion::rotation_x(-0.1 + slow * -0.28) * Quaternion::rotation_y(0.2 + slow * 0.18); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slow * 0.2) + next.chest.position = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(slow * 0.2) * Quaternion::rotation_x(slow * 0.2) * Quaternion::rotation_y(slow * -0.1); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slow * 0.1) + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = Quaternion::rotation_z(slow * 0.1) * Quaternion::rotation_x(slow * 0.1) * Quaternion::rotation_y(slow * -0.04); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(slow * 0.1) + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = Quaternion::rotation_z(slow * 0.1) * Quaternion::rotation_x(slow * 0.1) * Quaternion::rotation_y(slow * -0.05); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3); - next.control.offset = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0); - next.control.ori = Quaternion::rotation_x(-1.4) + next.control.position = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0); + next.control.orientation = Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(slow * 2.0 + 0.7) * Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, footquick * -9.5, skeleton_attr.foot.2, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(footquick * 0.3) * Quaternion::rotation_y(footquick * -0.6); - next.r_foot.offset = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(footquick * -0.3) * Quaternion::rotation_y(footquick * 0.2); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/block.rs b/voxygen/src/anim/src/character/block.rs index b215071cd8..67099c8d58 100644 --- a/voxygen/src/anim/src/character/block.rs +++ b/voxygen/src/anim/src/character/block.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct Input { pub attack: bool, @@ -42,133 +44,135 @@ impl Animation for BlockAnimation { .sin() * 0.15, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, -1.0 + skeleton_attr.head.0, skeleton_attr.head.1 + 19.5 + wave_ultra_slow * 0.2, ); - next.head.ori = Quaternion::rotation_x(-0.25); + next.head.orientation = Quaternion::rotation_x(-0.25); next.head.scale = Vec3::one() * 1.01 * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 5.0 + wave_ultra_slow * 0.2); - next.chest.ori = + next.chest.position = + Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 5.0 + wave_ultra_slow * 0.2); + next.chest.orientation = Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(wave_ultra_slow_cos * 0.01); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 3.0 + wave_ultra_slow * 0.2); - next.belt.ori = + next.belt.position = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 3.0 + wave_ultra_slow * 0.2); + next.belt.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(wave_ultra_slow_cos * 0.008); next.belt.scale = Vec3::one() * 1.01; - next.shorts.offset = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 1.0 + wave_ultra_slow * 0.2); - next.shorts.ori = Quaternion::rotation_x(0.1); + next.shorts.position = + Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 1.0 + wave_ultra_slow * 0.2); + next.shorts.orientation = Quaternion::rotation_x(0.1); next.shorts.scale = Vec3::one(); match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -6.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 0.0, -6.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0, 13.0, 8.0); - next.control.ori = Quaternion::rotation_x(0.2) + next.control.position = Vec3::new(-8.0, 13.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-1.57); next.control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 3.5, 6.5); - next.l_hand.ori = Quaternion::rotation_x(2.07) + next.l_hand.position = Vec3::new(-7.0, 3.5, 6.5); + next.l_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, 3.75); - next.r_hand.ori = Quaternion::rotation_x(2.07) + next.r_hand.position = Vec3::new(7.0, 2.5, 3.75); + next.r_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, 5.5); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, 5.5); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(-0.85); next.main.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( + next.main.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 4.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); }, Some(ToolKind::Shield(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( + next.main.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 4.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); @@ -176,34 +180,35 @@ impl Animation for BlockAnimation { _ => {}, } - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 5.0, 0.0); - next.glider.ori = Quaternion::rotation_y(0.0); + next.glider.position = Vec3::new(0.0, 5.0, 0.0); + next.glider.orientation = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/blockidle.rs b/voxygen/src/anim/src/character/blockidle.rs index a08717f268..43a5ff1fb1 100644 --- a/voxygen/src/anim/src/character/blockidle.rs +++ b/voxygen/src/anim/src/character/blockidle.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct Input { pub attack: bool, @@ -41,94 +43,94 @@ impl Animation for BlockIdleAnimation { .sin() * 0.15, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, -1.0 + skeleton_attr.head.0, skeleton_attr.head.1 + wave_ultra_slow * 0.2, ); - next.head.ori = Quaternion::rotation_x(0.0); + next.head.orientation = Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.01 * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, 0.0, skeleton_attr.chest.1 + wave_ultra_slow * 0.2, ); - next.chest.ori = Quaternion::rotation_y(wave_ultra_slow_cos * 0.01); + next.chest.orientation = Quaternion::rotation_y(wave_ultra_slow_cos * 0.01); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new( + next.belt.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, 0.0, skeleton_attr.belt.1 + wave_ultra_slow * 0.2, ); - next.belt.ori = + next.belt.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(wave_ultra_slow_cos * 0.008); next.belt.scale = Vec3::one() * 1.01; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, 0.0, skeleton_attr.shorts.1 + wave_ultra_slow * 0.2, ); - next.shorts.ori = Quaternion::rotation_x(0.1); + next.shorts.orientation = Quaternion::rotation_x(0.1); next.shorts.scale = Vec3::one(); match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -6.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 0.0, -6.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0, 13.0, 8.0); - next.control.ori = Quaternion::rotation_x(0.2) + next.control.position = Vec3::new(-8.0, 13.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-1.57); next.control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); - next.l_hand.ori = Quaternion::rotation_x(2.07) + next.l_hand.position = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); + next.l_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); - next.r_hand.ori = Quaternion::rotation_x(2.07) + next.r_hand.position = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); + next.r_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(-0.85); next.main.scale = Vec3::one(); @@ -138,44 +140,44 @@ impl Animation for BlockIdleAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.main.scale = Vec3::one(); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); @@ -185,60 +187,60 @@ impl Animation for BlockIdleAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); - next.l_hand.ori = Quaternion::rotation_x(2.07) + next.l_hand.position = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); + next.l_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); - next.r_hand.ori = Quaternion::rotation_x(2.07) + next.r_hand.position = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); + next.r_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(-0.85); next.main.scale = Vec3::one(); @@ -252,44 +254,44 @@ impl Animation for BlockIdleAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.main.scale = Vec3::one(); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(3.0, 7.0, 5.0); - next.r_control.ori = Quaternion::rotation_x(0.5 * PI) + next.r_control.position = Vec3::new(3.0, 7.0, 5.0); + next.r_control.orientation = Quaternion::rotation_x(0.5 * PI) * Quaternion::rotation_y(0.5 * PI) * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); @@ -298,41 +300,43 @@ impl Animation for BlockIdleAnimation { _ => {}, } - next.l_foot.offset = Vec3::new(-3.4, 0.3, skeleton_attr.foot.1 + wave_ultra_slow_cos * 0.1); - next.l_foot.ori = Quaternion::rotation_x(-0.3); + next.l_foot.position = + Vec3::new(-3.4, 0.3, skeleton_attr.foot.1 + wave_ultra_slow_cos * 0.1); + next.l_foot.orientation = Quaternion::rotation_x(-0.3); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(3.4, 1.2, skeleton_attr.foot.1 + wave_ultra_slow * 0.1); - next.r_foot.ori = Quaternion::rotation_x(0.3); + next.r_foot.position = Vec3::new(3.4, 1.2, skeleton_attr.foot.1 + wave_ultra_slow * 0.1); + next.r_foot.orientation = Quaternion::rotation_x(0.3); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one(); + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/charge.rs b/voxygen/src/anim/src/character/charge.rs index 11dd3568bb..5589299236 100644 --- a/voxygen/src/anim/src/character/charge.rs +++ b/voxygen/src/anim/src/character/charge.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct ChargeAnimation; @@ -62,7 +64,7 @@ impl Animation for ChargeAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -74,73 +76,75 @@ impl Animation for ChargeAnimation { 0.0 } * 1.3; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( stop * -2.0, -3.5 + stop * 2.5 + skeleton_attr.head.0, skeleton_attr.head.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(stop * -1.0 + tilt * -2.0) * Quaternion::rotation_y(stop * -0.3); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(stop * 1.2 + stress * stop * 0.02 + tilt * -2.0); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = + Quaternion::rotation_z(stop * 1.2 + stress * stop * 0.02 + tilt * -2.0); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(stop * -0.5 + tilt * 2.0); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(stop * -0.5 + tilt * 2.0); - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(stop * -0.7 + tilt * 4.0); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(stop * -0.7 + tilt * 4.0); match active_tool_kind { //TODO: Inventory Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.0, -2.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.0, -2.0, -5.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(9.0, 1.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(9.2, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(9.2, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new( + next.control.position = Vec3::new( -7.0 + quick * 3.5 * (1.0 / (stopa + 0.1)), 6.0 + quicka * 3.5 * (1.0 / (stopa + 0.1)), 6.0 - stop * 3.0, ); - next.control.ori = + next.control.orientation = Quaternion::rotation_x(stop * -0.2) * Quaternion::rotation_z(stop * 0.2); next.control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(1.0, -2.0 + stop * -1.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(1.0, -2.0 + stop * -1.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(4.9, 1.0, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(4.9, 1.0, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, -1.0, -14.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, -1.0, -14.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.hold.offset = Vec3::new(0.4, -0.3, -5.8); - next.hold.ori = Quaternion::rotation_x(-1.6) + next.hold.position = Vec3::new(0.4, -0.3, -5.8); + next.hold.orientation = Quaternion::rotation_x(-1.6) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.0); next.hold.scale = Vec3::one() * 1.0; - next.control.offset = Vec3::new(-10.0 + stop * 13.0, 6.0 + stop * 4.0, 8.0); - next.control.ori = Quaternion::rotation_x(0.0) + next.control.position = Vec3::new(-10.0 + stop * 13.0, 6.0 + stop * 4.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(stop * -0.4) * Quaternion::rotation_z(stop * -0.6); next.control.scale = Vec3::one(); @@ -149,76 +153,77 @@ impl Animation for ChargeAnimation { } if velocity > 0.2 { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0 - foot * 1.5, skeleton_attr.foot.1 + foote * 2.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(foote * -0.1) + next.l_foot.orientation = Quaternion::rotation_x(foote * -0.1) * Quaternion::rotation_z(0.4) * Quaternion::rotation_y(0.15); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0 + foot * 1.5, skeleton_attr.foot.1 + foote * -1.5, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(0.0) + next.r_foot.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_z(0.4) * Quaternion::rotation_y(0.0); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.5 + stop * -1.3, skeleton_attr.foot.2 + tilt * -4.0 * foot, ); - next.l_foot.ori = Quaternion::rotation_x(stop * -0.2 - 0.2 + stop * stress * 0.02) - * Quaternion::rotation_z(stop * 0.1) - * Quaternion::rotation_y(stop * 0.08); + next.l_foot.orientation = + Quaternion::rotation_x(stop * -0.2 - 0.2 + stop * stress * 0.02) + * Quaternion::rotation_z(stop * 0.1) + * Quaternion::rotation_y(stop * 0.08); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 3.5 + stop * 1.5, skeleton_attr.foot.2 + tilt * 4.0 * foot, ); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(stop * 0.1) * Quaternion::rotation_z(stop * 0.1); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.3); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.3); next.back.scale = Vec3::one() * 1.02; - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/climb.rs b/voxygen/src/anim/src/character/climb.rs index aee0be362b..ec4c311202 100644 --- a/voxygen/src/anim/src/character/climb.rs +++ b/voxygen/src/anim/src/character/climb.rs @@ -1,9 +1,10 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; - pub struct ClimbAnimation; impl Animation for ClimbAnimation { @@ -61,251 +62,259 @@ impl Animation for ClimbAnimation { * 0.15, ); let stagnant = if speed > -0.7 { 1.0 } else { 0.0 }; //sets static position when there is no movement + + next.hold.scale = Vec3::one() * 0.0; + if speed > 0.7 || lateral > 0.1 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -4.0 + skeleton_attr.head.0, skeleton_attr.head.1 + smootha * 0.2, ); - next.head.ori = Quaternion::rotation_z(smooth * 0.1) + next.head.orientation = Quaternion::rotation_z(smooth * 0.1) * Quaternion::rotation_x(0.6) * Quaternion::rotation_y(quick * 0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + smootha * 1.1, ); - next.chest.ori = Quaternion::rotation_z(quick * 0.25) + next.chest.orientation = Quaternion::rotation_z(quick * 0.25) * Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(quick * -0.12); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(quick * 0.0) * Quaternion::rotation_x(0.0); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1); + next.belt.orientation = + Quaternion::rotation_z(quick * 0.0) * Quaternion::rotation_x(0.0); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.2); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.2); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0 + 1.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(quick * 0.0) + next.shorts.orientation = Quaternion::rotation_z(quick * 0.0) * Quaternion::rotation_x(0.1) * Quaternion::rotation_y(quick * 0.10); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, 4.0 + skeleton_attr.hand.1 + quicka * 1.5, 5.0 + skeleton_attr.hand.2 - quick * 4.0, ); - next.l_hand.ori = Quaternion::rotation_x(2.2 + quicka * 0.5); + next.l_hand.orientation = Quaternion::rotation_x(2.2 + quicka * 0.5); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, 5.0 + skeleton_attr.hand.1 - quicka * 1.5, 5.0 + skeleton_attr.hand.2 + quick * 4.0, ); - next.r_hand.ori = Quaternion::rotation_x(2.2 - quicka * 0.5); + next.r_hand.orientation = Quaternion::rotation_x(2.2 - quicka * 0.5); next.r_hand.scale = Vec3::one(); match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 5.0 + skeleton_attr.foot.1, skeleton_attr.foot.2 + quick * 2.5, ); - next.l_foot.ori = Quaternion::rotation_x(0.2 - quicka * 0.5); + next.l_foot.orientation = Quaternion::rotation_x(0.2 - quicka * 0.5); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, skeleton_attr.foot.2 - quick * 2.5, ); - next.r_foot.ori = Quaternion::rotation_x(0.2 + quicka * 0.5); + next.r_foot.orientation = Quaternion::rotation_x(0.2 + quicka * 0.5); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(smootha * 0.15); + next.l_shoulder.orientation = Quaternion::rotation_x(smootha * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(smooth * 0.15); + next.r_shoulder.orientation = Quaternion::rotation_x(smooth * 0.15); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-7.0, -5.0, 18.0); - next.main.ori = + next.main.position = Vec3::new(-7.0, -5.0, 18.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + smootha * 0.25); next.main.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_y(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_y(0.0); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(smooth * -0.3) * Quaternion::rotation_y(smooth * -0.3); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, -0.2 + smooth * -0.08, 0.4) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, -0.2 + smooth * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -1.0 - stagnant + skeleton_attr.head.0, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_x( + next.head.orientation = Quaternion::rotation_x( -0.25 * (1.0 - stagnant) + stagnant * 2.0 * head_look.x.abs(), ) * Quaternion::rotation_z(stagnant * 3.5 * head_look.x.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 1.0 + skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(0.6 * stagnant) + next.chest.position = + Vec3::new(0.0, 1.0 + skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(0.6 * stagnant) * Quaternion::rotation_x((0.2 + drop * 0.05) * (1.0 - stagnant)); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + 0.5, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(0.1 + dropa * 0.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + 0.5, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_x(0.1 + dropa * 0.1); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x( + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x( -0.2 + dropa * 0.1 - 0.15 * (1.0 - stagnant) + stagnant * 0.1, ); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0 + 1.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(0.1 + dropa * 0.12 * (1.0 - stagnant)); + next.shorts.orientation = Quaternion::rotation_x(0.1 + dropa * 0.12 * (1.0 - stagnant)); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, 7.5 + stagnant * -5.0 + skeleton_attr.hand.1, 7.0 + stagnant * -7.0 + skeleton_attr.hand.2 + dropa * -1.0 * (1.0 - stagnant), ); - next.l_hand.ori = Quaternion::rotation_x(2.2 + stagnant * -1.4) + next.l_hand.orientation = Quaternion::rotation_x(2.2 + stagnant * -1.4) * Quaternion::rotation_y((0.3 + dropa * 0.1) * (1.0 - stagnant)); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, 7.5 + stagnant * -2.5 + skeleton_attr.hand.1, 5.0 + skeleton_attr.hand.2 + drop * -1.0 * (1.0 - stagnant), ); - next.r_hand.ori = Quaternion::rotation_x(2.2) + next.r_hand.orientation = Quaternion::rotation_x(2.2) * Quaternion::rotation_y(-0.3 + drop * 0.1 * (1.0 - stagnant)); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 4.0 + stagnant * 3.0 + skeleton_attr.foot.1, 1.0 + skeleton_attr.foot.2 + drop * -2.0 * (1.0 - stagnant), ); - next.l_foot.ori = Quaternion::rotation_x(0.55 + drop * 0.1 * (1.0 - stagnant)); + next.l_foot.orientation = Quaternion::rotation_x(0.55 + drop * 0.1 * (1.0 - stagnant)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + stagnant * 4.0 + skeleton_attr.foot.1, -2.0 + skeleton_attr.foot.2 + smooth * 1.0 * (1.0 - stagnant), ); - next.r_foot.ori = Quaternion::rotation_x(0.2 + smooth * 0.15 * (1.0 - stagnant)); + next.r_foot.orientation = + Quaternion::rotation_x(0.2 + smooth * 0.15 * (1.0 - stagnant)); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-7.0, -5.0, 18.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 18.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_y(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_y(0.0); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, -0.2, 0.4) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.4) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/dance.rs b/voxygen/src/anim/src/character/dance.rs index a79e16ad9d..6abdd7328e 100644 --- a/voxygen/src/anim/src/character/dance.rs +++ b/voxygen/src/anim/src/character/dance.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct DanceAnimation; @@ -54,136 +56,142 @@ impl Animation for DanceAnimation { * 0.15, ); - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(short * -0.6) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(short * -0.6) * Quaternion::rotation_x(0.2 + head_look.y.max(0.0) + shorte.abs() * -0.2); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + shortealt * 1.5, ); - next.chest.ori = Quaternion::rotation_z(short * 0.35) + next.chest.orientation = Quaternion::rotation_z(short * 0.35) * Quaternion::rotation_y(shorte * 0.08) * Quaternion::rotation_x(foot * 0.07); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(shorte * 0.25); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(shorte * 0.25); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.25 + shorte * 0.1 + noisea * 0.1 + noiseb * 0.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = + Quaternion::rotation_x(-0.25 + shorte * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(foot * 0.35); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(foot * 0.35); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( 1.0 - skeleton_attr.hand.0, 2.0 + skeleton_attr.hand.1 + shortealt * -3.0, skeleton_attr.hand.2 + shortealt * -0.75, ); - next.l_hand.ori = Quaternion::rotation_x(1.4 + foot * 0.15) * Quaternion::rotation_y(0.2); + next.l_hand.orientation = + Quaternion::rotation_x(1.4 + foot * 0.15) * Quaternion::rotation_y(0.2); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, 2.0 + skeleton_attr.hand.1 + shortealt * 3.0, skeleton_attr.hand.2 + shortealt * 0.75, ); - next.r_hand.ori = Quaternion::rotation_x(1.4 + foot * -0.15) * Quaternion::rotation_y(-0.2); + next.r_hand.orientation = + Quaternion::rotation_x(1.4 + foot * -0.15) * Quaternion::rotation_y(-0.2); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0 + foot * 0.8, 1.5 + -skeleton_attr.foot.1 + foot * -4.0, skeleton_attr.foot.2 + 2.0, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.3) * Quaternion::rotation_z(short * -0.15); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0 + foot * 0.8, 1.5 + -skeleton_attr.foot.1 + foot * 4.0, skeleton_attr.foot.2 + 2.0, ); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.3) * Quaternion::rotation_z(short * 0.15); + next.r_foot.orientation = + Quaternion::rotation_x(foot * 0.3) * Quaternion::rotation_z(short * 0.15); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(shorte * 0.15); + next.l_shoulder.orientation = Quaternion::rotation_x(shorte * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(shorte * -0.15); + next.r_shoulder.orientation = Quaternion::rotation_x(shorte * -0.15); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(short * -0.2); + next.torso.position = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(short * -0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/dash.rs b/voxygen/src/anim/src/character/dash.rs index 4f6f73e431..8e1cf865c3 100644 --- a/voxygen/src/anim/src/character/dash.rs +++ b/voxygen/src/anim/src/character/dash.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; -use vek::*; pub struct Input { pub attack: bool, @@ -40,84 +42,86 @@ impl Animation for DashAnimation { match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); - next.chest.ori = Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); + next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); + next.chest.orientation = + Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); - next.shorts.offset = Vec3::new(0.0, 3.0, -3.0); - next.shorts.ori = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, 3.0, -3.0); + next.shorts.orientation = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); - next.control.ori = + next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); + next.control.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(1.1 + slow * 0.2); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4 - 0.8); + next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8); - next.r_foot.offset = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4 - 0.8); + next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8); }, Some(ToolKind::Dagger(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); - next.chest.ori = Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); + next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); + next.chest.orientation = + Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); - next.shorts.offset = Vec3::new(0.0, 3.0, -3.0); - next.shorts.ori = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, 3.0, -3.0); + next.shorts.orientation = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); - next.control.ori = + next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); + next.control.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(1.1 + slow * 0.2); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4 - 0.8); + next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8); - next.r_foot.offset = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4 - 0.8); + next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8); }, _ => {}, } @@ -125,68 +129,69 @@ impl Animation for DashAnimation { match second_tool_kind { //TODO: Inventory Some(ToolKind::Dagger(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); - next.chest.ori = Quaternion::rotation_x(0.0); + next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); + next.chest.orientation = Quaternion::rotation_x(0.0); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_x(0.0); - next.shorts.offset = Vec3::new(0.0, 3.0, -3.0); - next.shorts.ori = Quaternion::rotation_x(0.0); + next.shorts.position = Vec3::new(0.0, 3.0, -3.0); + next.shorts.orientation = Quaternion::rotation_x(0.0); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(-8.0, -10.0, 0.0); + next.l_control.position = Vec3::new(-8.0, -10.0, 0.0); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0); + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0); next.l_hand.scale = Vec3::one() * 1.04; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); next.main.scale = Vec3::one(); - next.r_control.offset = Vec3::new(8.0, 10.0, 0.0); + next.r_control.position = Vec3::new(8.0, 10.0, 0.0); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0); + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0); next.r_hand.scale = Vec3::one() * 1.05; - next.second.offset = Vec3::new(0.0, 6.0, -1.0); - next.second.ori = Quaternion::rotation_x(-0.3); + next.second.position = Vec3::new(0.0, 6.0, -1.0); + next.second.orientation = Quaternion::rotation_x(-0.3); next.second.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4 - 0.8); + next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8); - next.r_foot.offset = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4 - 0.8); + next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8); }, _ => {}, } - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; diff --git a/voxygen/src/anim/src/character/equip.rs b/voxygen/src/anim/src/character/equip.rs index df02fc16e5..ff07260341 100644 --- a/voxygen/src/anim/src/character/equip.rs +++ b/voxygen/src/anim/src/character/equip.rs @@ -2,7 +2,7 @@ use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct EquipAnimation; @@ -41,141 +41,150 @@ impl Animation for EquipAnimation { match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(-0.2); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(-0.2); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.3); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -6.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, -6.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = + next.control.position = Vec3::new(-3.0 + equip_slowa * -1.5, -5.0, 12.0 + equip_slow * 1.5); - next.control.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.control.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { - next.l_hand.offset = Vec3::new(-4.0, 3.0, 6.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3) + next.l_hand.position = Vec3::new(-4.0, 3.0, 6.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(-2.5, 9.0, 4.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3) + next.r_hand.position = Vec3::new(-2.5, 9.0, 4.0); + next.r_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-6.0, 10.0, -1.0); - next.main.ori = Quaternion::rotation_x(1.27) + next.main.position = Vec3::new(-6.0, 10.0, -1.0); + next.main.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(-0.3) * Quaternion::rotation_z(-0.8); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.2) * Quaternion::rotation_y(-0.3); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = + Quaternion::rotation_x(0.2) * Quaternion::rotation_y(-0.3); next.control.scale = Vec3::one(); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 5.5, 3.5); - next.l_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.32); + next.l_hand.position = Vec3::new(-7.0, 5.5, 3.5); + next.l_hand.orientation = + Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.32); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(8.0, 7.75, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.22); + next.r_hand.position = Vec3::new(8.0, 7.75, 0.0); + next.r_hand.orientation = + Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.22); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(6.0, 7.0, 0.0); - next.main.ori = Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(6.0, 7.0, 0.0); + next.main.orientation = + Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); - next.control.offset = + next.control.position = Vec3::new(-3.0 + equip_slowa * -1.5, -12.0, 12.0 + equip_slow * 1.5); - next.control.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(1.35 + 2.5); + next.control.orientation = + Quaternion::rotation_x(0.0) * Quaternion::rotation_y(1.35 + 2.5); next.control.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.0, -2.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.0, -2.0, -5.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(9.0, 1.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(11.0, 9.0, 10.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(11.0, 9.0, 10.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(wave_ultra_slow * 0.2) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.2) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(wave_ultra_slow_cos * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Shield(_)) => { - next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.position = Vec3::new(-6.0, 3.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.position = Vec3::new(-6.0, 3.0, -2.0); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-6.0, 4.5, 0.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(-6.0, 4.5, 0.0); + next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(2.0, 1.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(2.0, 1.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(5.9, 4.5, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(5.9, 4.5, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(wave_ultra_slow * 0.2) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.2) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(wave_ultra_slow_cos * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Dagger(_)) => { // TODO: Fix animation - // next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); - // next.l_hand.ori = Quaternion::rotation_x(-0.3); + // next.l_hand.position = Vec3::new(-6.0, 3.5, 0.0); + // next.l_hand.orientation = Quaternion::rotation_x(-0.3); // next.l_hand.scale = Vec3::one() * 1.01; - // next.main.offset = Vec3::new(-6.0, 4.5, 0.0); - // next.main.ori = Quaternion::rotation_x(-0.3); + // next.main.position = Vec3::new(-6.0, 4.5, 0.0); + // next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); - // next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - // next.r_hand.ori = Quaternion::rotation_x(-0.3); + // next.r_hand.position = Vec3::new(-6.0, 3.0, -2.0); + // next.r_hand.orientation = Quaternion::rotation_x(-0.3); // next.r_hand.scale = Vec3::one() * 1.01; }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0); - next.l_hand.ori = Quaternion::rotation_x(1.27 + wave * 0.25) + next.l_hand.position = Vec3::new(-7.0, 4.0, 3.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27 + wave * 0.25) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, -1.25); - next.r_hand.ori = + next.r_hand.position = Vec3::new(7.0, 2.5, -1.25); + next.r_hand.orientation = Quaternion::rotation_x(1.27 + wave * 0.25) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, -2.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, -2.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.27) * Quaternion::rotation_z(wave * -0.25); next.main.scale = Vec3::one(); @@ -194,40 +203,41 @@ impl Animation for EquipAnimation { .sin() * 0.1, ); + next.hold.scale = Vec3::one() * 0.0; if velocity > 0.5 { - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.2); } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + short * 0.2, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(wave_ultra_slow_cos * 0.035 - 0.2); + next.l_foot.orientation = Quaternion::rotation_x(wave_ultra_slow_cos * 0.035 - 0.2); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(wave_ultra_slow * 0.035); + next.r_foot.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.035); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; } next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/glidewield.rs b/voxygen/src/anim/src/character/glidewield.rs index 6dfb6dbfb6..c9e5bc36b1 100644 --- a/voxygen/src/anim/src/character/glidewield.rs +++ b/voxygen/src/anim/src/character/glidewield.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct GlideWieldAnimation; @@ -96,7 +98,7 @@ impl Animation for GlideWieldAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -108,208 +110,215 @@ impl Animation for GlideWieldAnimation { 0.0 } * 1.3; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -2.0 - skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + 15.0, ); - next.l_hand.ori = Quaternion::rotation_x(3.35); + next.l_hand.orientation = Quaternion::rotation_x(3.35); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( 2.0 + skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + 15.0, ); - next.r_hand.ori = Quaternion::rotation_x(3.35); + next.r_hand.orientation = Quaternion::rotation_x(3.35); next.r_hand.scale = Vec3::one(); if speed > 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + short * 0.1, ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) - * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) + * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 2.0 + shortalt * -1.5 - lower, ); - next.chest.ori = Quaternion::rotation_z(short * 0.10 * walkintensity + tilt * -1.0) - * Quaternion::rotation_y(tilt * 2.2) - * Quaternion::rotation_x( - shortalter * 0.035 + wave_stop * speed * -0.1 + (tilt.abs()), - ); + next.chest.orientation = + Quaternion::rotation_z(short * 0.10 * walkintensity + tilt * -1.0) + * Quaternion::rotation_y(tilt * 2.2) + * Quaternion::rotation_x( + shortalter * 0.035 + wave_stop * speed * -0.1 + (tilt.abs()), + ); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5); next.belt.scale = Vec3::one(); - next.glider.ori = Quaternion::rotation_x(0.8); - next.glider.offset = Vec3::new(0.0, -10.0, 15.0); + next.glider.orientation = Quaternion::rotation_x(0.8); + next.glider.position = Vec3::new(0.0, -10.0, 15.0); next.glider.scale = Vec3::one() * 1.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7); next.shorts.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothoril * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertl * -2.7).max(-1.0)) * walkintensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.l_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothorir * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertr * -2.7).max(-1.0)) * walkintensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.r_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); next.r_shoulder.scale = Vec3::one() * 1.1; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.3 + breathe * -0.05, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + slow * 0.3, ); - next.chest.ori = Quaternion::rotation_z(head_look.x * 0.6); + next.chest.orientation = Quaternion::rotation_z(head_look.x * 0.6); next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(head_look.x * -0.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(head_look.x * -0.1); next.belt.scale = Vec3::one() + breathe * -0.03; - next.glider.ori = Quaternion::rotation_x(0.35); - next.glider.offset = Vec3::new(0.0, -9.0, 17.0); + next.glider.orientation = Quaternion::rotation_x(0.35); + next.glider.position = Vec3::new(0.0, -9.0, 17.0); next.glider.scale = Vec3::one() * 1.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(head_look.x * -0.2); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(head_look.x * -0.2); next.shorts.scale = Vec3::one() + breathe * -0.03; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, @@ -318,50 +327,52 @@ impl Animation for GlideWieldAnimation { match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/gliding.rs b/voxygen/src/anim/src/character/gliding.rs index 27d8d3a6f6..738eec1ee4 100644 --- a/voxygen/src/anim/src/character/gliding.rs +++ b/voxygen/src/anim/src/character/gliding.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct GlidingAnimation; @@ -56,7 +58,7 @@ impl Animation for GlidingAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.0001 && m.is_finite()) .reduce_and() @@ -74,77 +76,78 @@ impl Animation for GlidingAnimation { anim_time as f32 }; - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_x(0.35 - slow * 0.10 + head_look.y) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_x(0.35 - slow * 0.10 + head_look.y) * Quaternion::rotation_z(head_look.x + slowa * 0.15); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(slowa * 0.02); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(slowa * 0.02); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slowa * 0.1 + tilt * tiltcancel * 12.0); + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = Quaternion::rotation_z(slowa * 0.1 + tilt * tiltcancel * 12.0); - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(slowa * 0.12 + tilt * tiltcancel * 16.0); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(slowa * 0.12 + tilt * tiltcancel * 16.0); - next.l_hand.offset = Vec3::new(-9.5, -3.0, 10.0); - next.l_hand.ori = Quaternion::rotation_x(-2.7 + slowa * -0.1); + next.l_hand.position = Vec3::new(-9.5, -3.0, 10.0); + next.l_hand.orientation = Quaternion::rotation_x(-2.7 + slowa * -0.1); - next.r_hand.offset = Vec3::new(9.5, -3.0, 10.0); - next.r_hand.ori = Quaternion::rotation_x(-2.7 + slowa * -0.10); + next.r_hand.position = Vec3::new(9.5, -3.0, 10.0); + next.r_hand.orientation = Quaternion::rotation_x(-2.7 + slowa * -0.10); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + slowa * -1.0 + tilt * tiltcancel * -35.0, -1.0 + skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x( + next.l_foot.orientation = Quaternion::rotation_x( (wave_stop * -0.7 - quicka * -0.21 + slow * 0.19) * speed * 0.04, ) * Quaternion::rotation_z(tilt * tiltcancel * 20.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + slowa * 1.0 + tilt * tiltcancel * 35.0, -1.0 + skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x( + next.r_foot.orientation = Quaternion::rotation_x( (wave_stop * -0.8 + quick * -0.25 + slowb * 0.13) * speed * 0.04, ) * Quaternion::rotation_z(tilt * tiltcancel * 20.0); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, -13.0 + slow * 0.10, 8.0); - next.glider.ori = Quaternion::rotation_x(0.8) * Quaternion::rotation_y(slowa * 0.04); + next.glider.position = Vec3::new(0.0, -13.0 + slow * 0.10, 8.0); + next.glider.orientation = + Quaternion::rotation_x(0.8) * Quaternion::rotation_y(slowa * 0.04); next.glider.scale = Vec3::one(); match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } @@ -152,15 +155,16 @@ impl Animation for GlidingAnimation { next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -4.0, 10.0) / 11.0 * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.06 * speed.max(12.0) + slow * 0.04) + next.torso.position = Vec3::new(0.0, -4.0, 10.0) / 11.0 * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.06 * speed.max(12.0) + slow * 0.04) * Quaternion::rotation_y(tilt * tiltcancel * 32.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; diff --git a/voxygen/src/anim/src/character/idle.rs b/voxygen/src/anim/src/character/idle.rs index 7933f4a18a..9b76637dec 100644 --- a/voxygen/src/anim/src/character/idle.rs +++ b/voxygen/src/anim/src/character/idle.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct IdleAnimation; @@ -26,7 +28,7 @@ impl Animation for IdleAnimation { let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI / 2.0).sin(); let head_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1 + wave_ultra_slow * 0.1 + head_abs * -0.5, @@ -34,139 +36,141 @@ impl Animation for IdleAnimation { next.head.scale = Vec3::one() * skeleton_attr.head_scale - head_abs * 0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + wave_ultra_slow * 0.1, ); next.chest.scale = Vec3::one() + head_abs * 0.05; - next.belt.offset = Vec3::new( + next.belt.position = Vec3::new( 0.0, skeleton_attr.belt.0, skeleton_attr.belt.1 + wave_ultra_slow * 0.1, ); - next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.orientation = Quaternion::rotation_x(0.0); next.belt.scale = Vec3::one() - head_abs * 0.05; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1 + wave_ultra_slow * 0.1, ); - next.shorts.ori = Quaternion::rotation_x(0.0); + next.shorts.orientation = Quaternion::rotation_x(0.0); next.shorts.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + wave_ultra_slow_cos * 0.15, skeleton_attr.hand.2 + wave_ultra_slow * 0.5, ); - next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.l_hand.orientation = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 + wave_ultra_slow_cos * 0.15, skeleton_attr.hand.2 + wave_ultra_slow * 0.5 + head_abs * -0.05, ); - next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.r_hand.orientation = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); next.r_hand.scale = Vec3::one() + head_abs * -0.05; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = (Vec3::one() + head_abs * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = (Vec3::one() + head_abs * -0.05) * 1.15; next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); - next.lantern.scale = Vec3::one() * 0.0; + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/jump.rs b/voxygen/src/anim/src/character/jump.rs index b00ab37dfe..1506f87017 100644 --- a/voxygen/src/anim/src/character/jump.rs +++ b/voxygen/src/anim/src/character/jump.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct JumpAnimation; impl Animation for JumpAnimation { @@ -41,7 +43,7 @@ impl Animation for JumpAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -53,163 +55,165 @@ impl Animation for JumpAnimation { 0.0 } * 1.3; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, -1.0 + skeleton_attr.head.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_x(0.25 + slow * 0.04) * Quaternion::rotation_z(tilt * -2.5); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 1.0); - next.chest.ori = Quaternion::rotation_z(tilt * -2.0); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 1.0); + next.chest.orientation = Quaternion::rotation_z(tilt * -2.0); next.chest.scale = Vec3::one() * 1.01; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(tilt * 2.0); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(tilt * 2.0); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_z(0.0); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_z(0.0); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(tilt * 3.0); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(tilt * 3.0); next.shorts.scale = Vec3::one(); if random > 0.5 { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, 1.0 + skeleton_attr.hand.1 + 4.0, 2.0 + skeleton_attr.hand.2 + slow * 1.5, ); - next.l_hand.ori = + next.l_hand.orientation = Quaternion::rotation_x(1.9 + slow * 0.4) * Quaternion::rotation_y(0.2); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 - 3.0, skeleton_attr.hand.2 + slow * 1.5, ); - next.r_hand.ori = + next.r_hand.orientation = Quaternion::rotation_x(-0.5 + slow * -0.4) * Quaternion::rotation_y(-0.2); next.r_hand.scale = Vec3::one(); } else { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 - 3.0, skeleton_attr.hand.2 + slow * 1.5, ); - next.l_hand.ori = + next.l_hand.orientation = Quaternion::rotation_x(-0.5 + slow * -0.4) * Quaternion::rotation_y(0.2); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, 1.0 + skeleton_attr.hand.1 + 4.0, 2.0 + skeleton_attr.hand.2 + slow * 1.5, ); - next.r_hand.ori = + next.r_hand.orientation = Quaternion::rotation_x(1.9 + slow * 0.4) * Quaternion::rotation_y(-0.2); next.r_hand.scale = Vec3::one(); }; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 - 6.0 * switch, 1.0 + skeleton_attr.foot.2 + slow * 1.5, ); - next.l_foot.ori = Quaternion::rotation_x(-1.2 * switch + slow * -0.2 * switch); + next.l_foot.orientation = Quaternion::rotation_x(-1.2 * switch + slow * -0.2 * switch); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + 6.0 * switch, 1.0 + skeleton_attr.foot.2 + slow * 1.5, ); - next.r_foot.ori = Quaternion::rotation_x(1.2 * switch + slow * 0.2 * switch); + next.r_foot.orientation = Quaternion::rotation_x(1.2 * switch + slow * 0.2 * switch); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.4 * switch); + next.l_shoulder.orientation = Quaternion::rotation_x(0.4 * switch); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(-0.4 * switch); + next.r_shoulder.orientation = Quaternion::rotation_x(-0.4 * switch); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(1.0 * switch + slow * 0.3 * switch) + next.lantern.orientation = Quaternion::rotation_x(1.0 * switch + slow * 0.3 * switch) * Quaternion::rotation_y(0.6 * switch + slow * 0.3 * switch); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/leapmelee.rs b/voxygen/src/anim/src/character/leapmelee.rs index 4c9aca309d..dada5b978d 100644 --- a/voxygen/src/anim/src/character/leapmelee.rs +++ b/voxygen/src/anim/src/character/leapmelee.rs @@ -1,7 +1,7 @@ use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; use common::comp::item::{Hands, ToolKind}; /* use std::f32::consts::PI; */ -use vek::*; +use super::super::vek::*; pub struct LeapAnimation; @@ -32,72 +32,75 @@ impl Animation for LeapAnimation { * ((anim_time as f32 * lab as f32 * 4.0).sin()); if let Some(ToolKind::Hammer(_)) = active_tool_kind { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(3.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(3.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + slower * -1.0, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(slower * 0.05) + next.head.orientation = Quaternion::rotation_z(slower * 0.05) * Quaternion::rotation_x((slowersmooth * -0.25 + slower * 0.55).max(-0.2)) * Quaternion::rotation_y(slower * 0.05); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slower * 0.08 + slowersmooth * 0.15) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(slower * 0.08 + slowersmooth * 0.15) * Quaternion::rotation_x(-0.3 + slower * 0.45 + slowersmooth * 0.26) * Quaternion::rotation_y(slower * 0.18 + slowersmooth * 0.15); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0 + slower * -0.7); - next.belt.ori = Quaternion::rotation_z(slower * -0.16 + slowersmooth * -0.12) + next.belt.position = Vec3::new(0.0, 0.0, -2.0 + slower * -0.7); + next.belt.orientation = Quaternion::rotation_z(slower * -0.16 + slowersmooth * -0.12) * Quaternion::rotation_x(0.0 + slower * -0.06) * Quaternion::rotation_y(slower * -0.05); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0 + slower * -0.7); - next.shorts.ori = Quaternion::rotation_z(slower * -0.08 + slowersmooth * -0.08) + next.shorts.position = Vec3::new(0.0, 0.0, -5.0 + slower * -0.7); + next.shorts.orientation = Quaternion::rotation_z(slower * -0.08 + slowersmooth * -0.08) * Quaternion::rotation_x(0.0 + slower * -0.08 + slowersmooth * -0.08) * Quaternion::rotation_y(slower * -0.07); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slower * -0.7 + 0.4) * Quaternion::rotation_y(slower * 0.4); + next.hold.scale = Vec3::one() * 0.0; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, slower * 3.0 + slowersmooth * -6.0 - 2.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slower * -0.2 + slowersmooth * -0.3 - 0.2); + next.l_foot.orientation = + Quaternion::rotation_x(slower * -0.2 + slowersmooth * -0.3 - 0.2); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, slower * 2.0 + slowersmooth * -4.0 - 1.0, -2.0 + skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slower * -0.4 + slowersmooth * -0.6 - 1.0); + next.r_foot.orientation = + Quaternion::rotation_x(slower * -0.4 + slowersmooth * -0.6 - 1.0); next.control.scale = Vec3::one(); - next.control.offset = Vec3::new(-7.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(-0.7 + slower * 1.5) + next.control.position = Vec3::new(-7.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(-0.7 + slower * 1.5) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(1.4 + slowersmooth * -0.4 + slower * 0.2); next.control.scale = Vec3::one(); } - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); @@ -110,8 +113,8 @@ impl Animation for LeapAnimation { (_, _) => Vec3::zero(), }; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next } diff --git a/voxygen/src/anim/src/character/mod.rs b/voxygen/src/anim/src/character/mod.rs index 7f2ef17a49..4709eac25b 100644 --- a/voxygen/src/anim/src/character/mod.rs +++ b/voxygen/src/anim/src/character/mod.rs @@ -36,120 +36,79 @@ pub use self::{ swimwield::SwimWieldAnimation, wield::WieldAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp; -use vek::{Vec3, Vec4}; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct CharacterSkeleton { - head: Bone, - chest: Bone, - belt: Bone, - back: Bone, - shorts: Bone, - l_hand: Bone, - r_hand: Bone, - l_foot: Bone, - r_foot: Bone, - l_shoulder: Bone, - r_shoulder: Bone, - glider: Bone, - main: Bone, - second: Bone, - lantern: Bone, - hold: Bone, - torso: Bone, - control: Bone, - l_control: Bone, - r_control: Bone, -} - -impl CharacterSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct CharacterSkeleton { + + head, + + chest, + + belt, + + back, + + shorts, + + l_hand, + + r_hand, + + l_foot, + + r_foot, + + l_shoulder, + + r_shoulder, + + glider, + + main, + + second, + + lantern, + + hold, + torso, + control, + l_control, + r_control, +}); impl Skeleton for CharacterSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 16; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"character_compute_mats\0"; - fn bone_count(&self) -> usize { 16 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "character_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let chest_mat = self.chest.compute_base_matrix(); - let torso_mat = self.torso.compute_base_matrix(); - let l_hand_mat = self.l_hand.compute_base_matrix(); - let r_hand_mat = self.r_hand.compute_base_matrix(); - let control_mat = self.control.compute_base_matrix(); - let l_control_mat = self.l_control.compute_base_matrix(); - let r_control_mat = self.r_control.compute_base_matrix(); - let main_mat = self.main.compute_base_matrix(); - let second_mat = self.second.compute_base_matrix(); - let shorts_mat = self.shorts.compute_base_matrix(); - let head_mat = self.head.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); + let chest_mat = torso_mat * Mat4::::from(self.chest); + let head_mat = chest_mat * Mat4::::from(self.head); + let shorts_mat = chest_mat * Mat4::::from(self.shorts); + let control_mat = chest_mat * Mat4::::from(self.control); + let l_control_mat = control_mat * Mat4::::from(self.l_control); + let r_control_mat = control_mat * Mat4::::from(self.r_control); - let lantern_final_mat = - torso_mat * chest_mat * shorts_mat * self.lantern.compute_base_matrix(); + let l_hand_mat = Mat4::::from(self.l_hand); + let lantern_mat = Mat4::::from(self.lantern); - ( - [ - FigureBoneData::new(torso_mat * chest_mat * head_mat), - FigureBoneData::new(torso_mat * chest_mat), - FigureBoneData::new(torso_mat * chest_mat * self.belt.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.back.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * shorts_mat), - FigureBoneData::new( - torso_mat * chest_mat * control_mat * l_control_mat * l_hand_mat, - ), - FigureBoneData::new( - torso_mat * chest_mat * control_mat * r_control_mat * r_hand_mat, - ), - FigureBoneData::new(torso_mat * self.l_foot.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.r_foot.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.l_shoulder.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.r_shoulder.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.glider.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * control_mat * l_control_mat * main_mat), - FigureBoneData::new( - torso_mat * chest_mat * control_mat * r_control_mat * second_mat, - ), - FigureBoneData::new(lantern_final_mat), - FigureBoneData::new( - torso_mat - * chest_mat - * control_mat - * l_hand_mat - * self.hold.compute_base_matrix(), - ), - ], - (lantern_final_mat * Vec4::new(0.0, 0.0, 0.0, 1.0)).xyz(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.chest.interpolate(&target.chest, dt); - self.belt.interpolate(&target.belt, dt); - self.back.interpolate(&target.back, dt); - self.shorts.interpolate(&target.shorts, dt); - self.l_hand.interpolate(&target.l_hand, dt); - self.r_hand.interpolate(&target.r_hand, dt); - self.l_foot.interpolate(&target.l_foot, dt); - self.r_foot.interpolate(&target.r_foot, dt); - self.l_shoulder.interpolate(&target.l_shoulder, dt); - self.r_shoulder.interpolate(&target.r_shoulder, dt); - self.glider.interpolate(&target.glider, dt); - self.main.interpolate(&target.main, dt); - self.second.interpolate(&target.second, dt); - self.lantern.interpolate(&target.lantern, dt); - self.hold.interpolate(&target.hold, dt); - self.torso.interpolate(&target.torso, dt); - self.control.interpolate(&target.control, dt); - self.l_control.interpolate(&target.l_control, dt); - self.r_control.interpolate(&target.r_control, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_mat), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.belt)), + make_bone(chest_mat * Mat4::::from(self.back)), + make_bone(shorts_mat), + make_bone(l_control_mat * l_hand_mat), + make_bone(r_control_mat * Mat4::::from(self.r_hand)), + make_bone(torso_mat * Mat4::::from(self.l_foot)), + make_bone(torso_mat * Mat4::::from(self.r_foot)), + make_bone(chest_mat * Mat4::::from(self.l_shoulder)), + make_bone(chest_mat * Mat4::::from(self.r_shoulder)), + make_bone(chest_mat * Mat4::::from(self.glider)), + make_bone(l_control_mat * Mat4::::from(self.main)), + make_bone(r_control_mat * Mat4::::from(self.second)), + make_bone(shorts_mat * lantern_mat), + // FIXME: Should this be l_control_mat? + make_bone(control_mat * l_hand_mat * Mat4::::from(self.hold)), + ]; + // NOTE: lantern_mat.cols.w = lantern_mat * Vec4::unit_w() + (head_mat * lantern_mat.cols.w).xyz() } } diff --git a/voxygen/src/anim/src/character/roll.rs b/voxygen/src/anim/src/character/roll.rs index f5eaf819f3..1db19b1381 100644 --- a/voxygen/src/anim/src/character/roll.rs +++ b/voxygen/src/anim/src/character/roll.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct RollAnimation; @@ -33,7 +35,7 @@ impl Animation for RollAnimation { let spin = anim_time as f32; let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.0001 && m.is_finite()) .reduce_and() @@ -45,141 +47,143 @@ impl Animation for RollAnimation { 0.0 }; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + 3.0, skeleton_attr.head.1 - 1.0, ); - next.head.ori = Quaternion::rotation_x(-0.75); + next.head.orientation = Quaternion::rotation_x(-0.75); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, -9.5 + skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_x(-0.2); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, -9.5 + skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_x(-0.2); next.chest.scale = Vec3::one() * 1.01; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1 + 1.0); - next.belt.ori = Quaternion::rotation_x(0.55); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1 + 1.0); + next.belt.orientation = Quaternion::rotation_x(0.55); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + 4.5, skeleton_attr.shorts.1 + 2.5, ); - next.shorts.ori = Quaternion::rotation_x(0.8); + next.shorts.orientation = Quaternion::rotation_x(0.8); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + 1.0, skeleton_attr.hand.2 + 2.0, ); - next.l_hand.ori = Quaternion::rotation_x(0.6); + next.l_hand.orientation = Quaternion::rotation_x(0.6); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, skeleton_attr.hand.1 + 1.0, skeleton_attr.hand.2 + 2.0, ); - next.r_hand.ori = Quaternion::rotation_x(0.6); + next.r_hand.orientation = Quaternion::rotation_x(0.6); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( 1.0 - skeleton_attr.foot.0, skeleton_attr.foot.1 + 5.5, skeleton_attr.foot.2 - 5.0, ); - next.l_foot.ori = Quaternion::rotation_x(0.9); + next.l_foot.orientation = Quaternion::rotation_x(0.9); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + 5.5, skeleton_attr.foot.2 - 5.0, ); - next.r_foot.ori = Quaternion::rotation_x(0.9); + next.r_foot.orientation = Quaternion::rotation_x(0.9); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + 2.0, skeleton_attr.shoulder.2 + 1.0, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 8.0) / 11.0 * skeleton_attr.scaler; - next.torso.ori = + next.torso.position = Vec3::new(0.0, 0.0, 8.0) / 11.0 * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(spin * -10.0) * Quaternion::rotation_z(tilt * -10.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/run.rs b/voxygen/src/anim/src/character/run.rs index d821576b11..10ad471fd4 100644 --- a/voxygen/src/anim/src/character/run.rs +++ b/voxygen/src/anim/src/character/run.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct RunAnimation; @@ -98,7 +100,7 @@ impl Animation for RunAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -110,147 +112,151 @@ impl Animation for RunAnimation { 0.0 } * 1.3; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + short * 0.1, ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) - * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) + * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 2.0 + shortalt * -1.5 - lower, ); - next.chest.ori = Quaternion::rotation_z(short * 0.18 * walkintensity + tilt * -0.6) + next.chest.orientation = Quaternion::rotation_z(short * 0.18 * walkintensity + tilt * -0.6) * Quaternion::rotation_y(tilt * 1.6) * Quaternion::rotation_x( impact * 0.06 + shortalter * 0.035 + wave_stop * speed * -0.07 + (tilt.abs()), ); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = + Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0 + foothorir * -1.3, 3.0 + skeleton_attr.hand.1 + foothorir * -7.0 * walkintensity, 1.5 + skeleton_attr.hand.2 - foothorir * 5.5 * walkintensity, ); - next.l_hand.ori = Quaternion::rotation_x(0.6 + footrotr * -1.2 * walkintensity) + next.l_hand.orientation = Quaternion::rotation_x(0.6 + footrotr * -1.2 * walkintensity) * Quaternion::rotation_y(footrotr * 0.4 * walkintensity); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0 + foothoril * 1.3, 3.0 + skeleton_attr.hand.1 + foothoril * -6.5 * walkintensity, 1.5 + skeleton_attr.hand.2 - foothoril * 7.0 * walkintensity, ); - next.r_hand.ori = Quaternion::rotation_x(0.6 + footrotl * -1.2 * walkintensity) + next.r_hand.orientation = Quaternion::rotation_x(0.6 + footrotl * -1.2 * walkintensity) * Quaternion::rotation_y(footrotl * -0.4 * walkintensity); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothoril * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertl * -2.7).max(-1.0)) * walkintensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) + next.l_foot.orientation = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) * Quaternion::rotation_y(tilt * 1.8); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothorir * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertr * -2.7).max(-1.0)) * walkintensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) + next.r_foot.orientation = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) * Quaternion::rotation_y(tilt * 1.8); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/shoot.rs b/voxygen/src/anim/src/character/shoot.rs index 9184ab99f5..1fe4dfc856 100644 --- a/voxygen/src/anim/src/character/shoot.rs +++ b/voxygen/src/anim/src/character/shoot.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; -use vek::*; pub struct ShootAnimation; @@ -36,71 +38,73 @@ impl Animation for ShootAnimation { let exp = ((anim_time as f32).powf(0.3 as f32)).min(1.2); - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(exp * -0.4) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(exp * -0.4) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(exp * 0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 - exp * 1.5, skeleton_attr.chest.1, ); - next.chest.ori = Quaternion::rotation_z(0.4 + exp * 1.0) + next.chest.orientation = Quaternion::rotation_z(0.4 + exp * 1.0) * Quaternion::rotation_x(0.0 + exp * 0.2) * Quaternion::rotation_y(exp * -0.08); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + exp * 1.0, skeleton_attr.belt.1); - next.belt.ori = next.chest.ori * -0.1; + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + exp * 1.0, skeleton_attr.belt.1); + next.belt.orientation = next.chest.orientation * -0.1; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + exp * 1.0, skeleton_attr.shorts.1, ); - next.shorts.ori = next.chest.ori * -0.08; + next.shorts.orientation = next.chest.orientation * -0.08; match active_tool_kind { //TODO: Inventory Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.5, 0.5, -4.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.5, 0.5, -4.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(8.0, 4.0, 2.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(8.0, 4.0, 2.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(9.2, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(9.2, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0 - exp * 5.0); - next.control.ori = Quaternion::rotation_x(exp * 1.3) + next.control.position = Vec3::new(-7.0, 6.0, 6.0 - exp * 5.0); + next.control.orientation = Quaternion::rotation_x(exp * 1.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(exp * 1.5); next.control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(1.0 - exp * 2.0, -4.0 - exp * 4.0, -1.0 + exp * 6.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = + Vec3::new(1.0 - exp * 2.0, -4.0 - exp * 4.0, -1.0 + exp * 6.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6 + exp * 0.8) * Quaternion::rotation_z(-0.3 + exp * 0.9); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(4.9, 3.0, -4.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(4.9, 3.0, -4.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.control.offset = Vec3::new(-9.0, 6.0, 8.0); - next.control.ori = Quaternion::rotation_x(exp * 0.4) + next.control.position = Vec3::new(-9.0, 6.0, 8.0); + next.control.orientation = Quaternion::rotation_x(exp * 0.4) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); @@ -108,81 +112,82 @@ impl Animation for ShootAnimation { _ => {}, } if velocity > 0.5 { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0 - foot * 1.0 + exp * -1.0, foote * 0.8 + exp * 1.5, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(exp * 0.5) + next.l_foot.orientation = Quaternion::rotation_x(exp * 0.5) * Quaternion::rotation_z(exp * 0.4) * Quaternion::rotation_y(0.15); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0 + foot * 1.0 + exp * 1.0, foote * -0.8 + exp * -1.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(exp * -0.5) + next.r_foot.orientation = Quaternion::rotation_x(exp * -0.5) * Quaternion::rotation_z(exp * 0.4) * Quaternion::rotation_y(0.0); next.r_foot.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.15); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.15); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.5, skeleton_attr.foot.2 + exp * 2.5, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(exp * -0.2 - 0.2) * Quaternion::rotation_z(exp * 1.0); - next.r_foot.offset = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 3.5 - exp * 2.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(exp * 0.1) * Quaternion::rotation_z(exp * 0.5); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.r_foot.orientation = + Quaternion::rotation_x(exp * 0.1) * Quaternion::rotation_z(exp * 0.5); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.back.offset = Vec3::new(0.0, -2.8, 7.25); - next.back.ori = Quaternion::rotation_x(-0.3); + next.back.position = Vec3::new(0.0, -2.8, 7.25); + next.back.orientation = Quaternion::rotation_x(-0.3); next.back.scale = Vec3::one() * 1.02; - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.position = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.position = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 5.0, 0.0); - next.glider.ori = Quaternion::rotation_y(0.0); + next.glider.position = Vec3::new(0.0, 5.0, 0.0); + next.glider.orientation = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(exp * -0.7 + 0.4) * Quaternion::rotation_y(exp * 0.4); next.lantern.scale = Vec3::one() * 0.65; - next.hold.offset = Vec3::new(17.5, -25.0, -10.5); - next.hold.ori = Quaternion::rotation_x(-1.6) + next.hold.position = Vec3::new(17.5, -25.0, -10.5); + next.hold.orientation = Quaternion::rotation_x(-1.6) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.0); next.hold.scale = Vec3::one() * 0.0; - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/sit.rs b/voxygen/src/anim/src/character/sit.rs index 268e7e8dd7..f7d61a1e08 100644 --- a/voxygen/src/anim/src/character/sit.rs +++ b/voxygen/src/anim/src/character/sit.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SitAnimation; @@ -39,133 +41,136 @@ impl Animation for SitAnimation { .sin() * 0.125, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.1 + stop * -0.8, ); - next.head.ori = Quaternion::rotation_z(head_look.x + slow * 0.2 - slow * 0.1) + next.head.orientation = Quaternion::rotation_z(head_look.x + slow * 0.2 - slow * 0.1) * Quaternion::rotation_x((slowa * -0.1 + slow * 0.1 + head_look.y).abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 + stop * -0.4, skeleton_attr.chest.1 + slow * 0.1 + stop * -0.8, ); - next.chest.ori = Quaternion::rotation_x(stop * 0.15); + next.chest.orientation = Quaternion::rotation_x(stop * 0.15); next.chest.scale = Vec3::one() + slow_abs * 0.05; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + stop * 1.2, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(stop * 0.3); + next.belt.position = + Vec3::new(0.0, skeleton_attr.belt.0 + stop * 1.2, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_x(stop * 0.3); next.belt.scale = (Vec3::one() + slow_abs * 0.05) * 1.02; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + stop * 2.5, skeleton_attr.shorts.1 + stop * 0.6, ); - next.shorts.ori = Quaternion::rotation_x(stop * 0.6); + next.shorts.orientation = Quaternion::rotation_x(stop * 0.6); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + slowa * 0.15, skeleton_attr.hand.2 + slow * 0.7 + stop * -2.0, ); - next.l_hand.ori = Quaternion::rotation_x(slowa * -0.1 + slow * 0.1); + next.l_hand.orientation = Quaternion::rotation_x(slowa * -0.1 + slow * 0.1); next.l_hand.scale = Vec3::one() + slow_abs * -0.05; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 + slowa * 0.15, skeleton_attr.hand.2 + slow * 0.7 + stop * -2.0, ); - next.r_hand.ori = Quaternion::rotation_x(slow * -0.1 + slowa * 0.1); + next.r_hand.orientation = Quaternion::rotation_x(slow * -0.1 + slowa * 0.1); next.r_hand.scale = Vec3::one() + slow_abs * -0.05; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, 3.0 + skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slow * 0.1 + stop * 1.2 + slow * 0.1); + next.l_foot.orientation = Quaternion::rotation_x(slow * 0.1 + stop * 1.2 + slow * 0.1); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, 3.0 + skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slowa * 0.1 + stop * 1.2 + slowa * 0.1); + next.r_foot.orientation = Quaternion::rotation_x(slowa * 0.1 + stop * 1.2 + slowa * 0.1); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = (Vec3::one() + slow_abs * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = (Vec3::one() + slow_abs * -0.05) * 1.15; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, stop * -0.16) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, -0.2, stop * -0.16) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/sneak.rs b/voxygen/src/anim/src/character/sneak.rs index 52563090a1..83b44d82f3 100644 --- a/voxygen/src/anim/src/character/sneak.rs +++ b/voxygen/src/anim/src/character/sneak.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::ToolKind; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SneakAnimation; @@ -74,237 +76,242 @@ impl Animation for SneakAnimation { * 0.1, ); - let ori: Vec2 = Vec2::from(orientation); + let orientation: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(orientation, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() - && ori.angle_between(last_ori).is_finite() + && orientation.angle_between(last_ori).is_finite() { - ori.angle_between(last_ori).min(0.2) - * last_ori.determine_side(Vec2::zero(), ori).signum() + orientation.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), orientation).signum() } else { 0.0 } * 1.3; + next.hold.scale = Vec3::one() * 0.0; + if speed > 0.5 { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( 1.0 - skeleton_attr.hand.0, 4.0 + skeleton_attr.hand.1, 1.0 + skeleton_attr.hand.2, ); - next.l_hand.ori = Quaternion::rotation_x(1.0); + next.l_hand.orientation = Quaternion::rotation_x(1.0); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, -1.0 + skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.r_hand.ori = Quaternion::rotation_x(0.4); + next.r_hand.orientation = Quaternion::rotation_x(0.4); next.r_hand.scale = Vec3::one(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -4.0 + skeleton_attr.head.0, -1.0 + skeleton_attr.head.1 + short * 0.06, ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.06) - * Quaternion::rotation_x(head_look.y + 0.45); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.06) + * Quaternion::rotation_x(head_look.y + 0.45); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, -1.0 + skeleton_attr.chest.1 + shortalt * -0.5, ); - next.chest.ori = Quaternion::rotation_z(0.3 + short * 0.08 + tilt * -0.2) + next.chest.orientation = Quaternion::rotation_z(0.3 + short * 0.08 + tilt * -0.2) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_x(-0.5); next.chest.scale = Vec3::one(); - next.belt.offset = + next.belt.position = Vec3::new(0.0, 0.5 + skeleton_attr.belt.0, 0.7 + skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5) * Quaternion::rotation_x(0.2); next.belt.scale = Vec3::one(); - next.glider.ori = Quaternion::rotation_x(0.0); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.orientation = Quaternion::rotation_x(0.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, 1.0 + skeleton_attr.shorts.0, 1.0 + skeleton_attr.shorts.1, ); - next.shorts.ori = Quaternion::rotation_z(short * 0.16 + tilt * -1.5) + next.shorts.orientation = Quaternion::rotation_z(short * 0.16 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7) * Quaternion::rotation_x(0.3); next.shorts.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * -10.5 * walkintensity - lower * 1.0, 1.0 + skeleton_attr.foot.2 + ((footvertl * -1.7).max(-1.0)) * walkintensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -0.8 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.l_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotl * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * -10.5 * walkintensity - lower * 1.0, 1.0 + skeleton_attr.foot.2 + ((footvertr * -1.7).max(-1.0)) * walkintensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -0.8 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.r_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotr * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); next.r_shoulder.scale = Vec3::one() * 1.1; - next.main.offset = Vec3::new(-7.0, -6.5, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -6.5, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.2 + 0.4) * Quaternion::rotation_y(shorte * 0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -4.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1 + slow * 0.1 + breathe * -0.05, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(0.6 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, -3.0 + skeleton_attr.chest.1 + slow * 0.1, ); - next.chest.ori = Quaternion::rotation_x(-0.7); + next.chest.orientation = Quaternion::rotation_x(-0.7); next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(0.3 + head_look.x * -0.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(0.3 + head_look.x * -0.1); next.belt.scale = Vec3::one() + breathe * -0.03; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( 1.0 - skeleton_attr.hand.0, 5.0 + skeleton_attr.hand.1, 0.0 + skeleton_attr.hand.2, ); - next.l_hand.ori = Quaternion::rotation_x(1.35); + next.l_hand.orientation = Quaternion::rotation_x(1.35); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.r_hand.ori = Quaternion::rotation_x(0.4); + next.r_hand.orientation = Quaternion::rotation_x(0.4); next.r_hand.scale = Vec3::one(); - next.glider.ori = Quaternion::rotation_x(0.35); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.orientation = Quaternion::rotation_x(0.35); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(0.6 + head_look.x * -0.2); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.6 + head_look.x * -0.2); next.shorts.scale = Vec3::one() + breathe * -0.03; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -6.0 + skeleton_attr.foot.1, 1.0 + skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(-0.5); + next.l_foot.orientation = Quaternion::rotation_x(-0.5); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(0.0); + next.r_foot.orientation = Quaternion::rotation_x(0.0); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/spin.rs b/voxygen/src/anim/src/character/spin.rs index 52fa49eef5..8896e71bae 100644 --- a/voxygen/src/anim/src/character/spin.rs +++ b/voxygen/src/anim/src/character/spin.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct Input { pub attack: bool, @@ -43,86 +45,87 @@ impl Animation for SpinAnimation { ) = active_tool_kind { //INTENTION: SWORD - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); - next.control.ori = Quaternion::rotation_x(-1.7) + next.control.position = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); + next.control.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_y(0.2 + spin * -2.0) * Quaternion::rotation_z(1.4 + spin * 0.1); next.control.scale = Vec3::one(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + spin * -0.8, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(spin * -0.25) + next.head.orientation = Quaternion::rotation_z(spin * -0.25) * Quaternion::rotation_x(0.0 + spin * -0.1) * Quaternion::rotation_y(spin * -0.2); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(spin * 0.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(spin * 0.1) * Quaternion::rotation_x(0.0 + spin * 0.1) * Quaternion::rotation_y(decel * -0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = next.chest.ori * -0.1; + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = next.chest.orientation * -0.1; next.belt.scale = Vec3::one(); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.belt.ori = next.chest.ori * -0.08; + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.belt.orientation = next.chest.orientation * -0.08; next.shorts.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z((spin * 7.0).max(0.3)) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z((spin * 7.0).max(0.3)) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.l_foot.offset = Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -1.2); + next.l_foot.position = Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -1.2); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, foot * -1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 1.2); + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, foot * -1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 1.2); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.position = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.position = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 5.0, 0.0); - next.glider.ori = Quaternion::rotation_y(0.0); + next.glider.position = Vec3::new(0.0, 5.0, 0.0); + next.glider.orientation = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(spin * -0.7 + 0.4) * Quaternion::rotation_y(spin * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/spinmelee.rs b/voxygen/src/anim/src/character/spinmelee.rs index 949b549482..b7c06d9fe4 100644 --- a/voxygen/src/anim/src/character/spinmelee.rs +++ b/voxygen/src/anim/src/character/spinmelee.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct SpinMeleeAnimation; @@ -48,111 +50,113 @@ impl Animation for SpinMeleeAnimation { let quick = (anim_time as f32 * lab as f32 * 8.0).sin(); if let Some(ToolKind::Axe(_)) = active_tool_kind { - next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0); - next.l_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0); + next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(PI); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5); - next.r_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.r_hand.position = Vec3::new(0.5, 0.0, -2.5); + next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-0.0, -2.0, -1.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(-0.0, -2.0, -1.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(0.0, 16.0, 3.0); - next.control.ori = Quaternion::rotation_x(-1.4) + next.control.position = Vec3::new(0.0, 16.0, 3.0); + next.control.orientation = Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(1.4); next.control.scale = Vec3::one(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(0.08); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 - 3.0, skeleton_attr.chest.1 - 2.0, ); - next.chest.ori = Quaternion::rotation_z(0.0) + next.chest.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.3); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_z(0.0) + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4) * Quaternion::rotation_y(0.0); next.belt.scale = Vec3::one() * 0.98; - next.shorts.offset = Vec3::new(0.0, 3.0, -2.5); - next.shorts.ori = Quaternion::rotation_z(0.0) + next.shorts.position = Vec3::new(0.0, 3.0, -2.5); + next.shorts.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.7) * Quaternion::rotation_y(0.0); next.shorts.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( -xshift * (anim_time as f32).min(0.6), -yshift * (anim_time as f32).min(0.6), 0.0, ) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(spin * -16.0) + next.torso.orientation = Quaternion::rotation_z(spin * -16.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } if velocity.z.abs() > 0.1 { - next.l_foot.offset = Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); - next.l_foot.ori = Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0); + next.l_foot.position = + Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); + next.l_foot.orientation = Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); - next.r_foot.ori = Quaternion::rotation_x(1.0); + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); + next.r_foot.orientation = Quaternion::rotation_x(1.0); next.r_foot.scale = Vec3::one(); } else if speed < 0.5 { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 2.0 + quick * -6.0, skeleton_attr.foot.2, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2); - next.r_foot.ori = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_y(-0.4); next.r_foot.scale = Vec3::one(); } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 2.0 + quick * -6.0, skeleton_attr.foot.2, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + quick * 6.0, skeleton_attr.foot.2, ); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_z(0.0); next.r_foot.scale = Vec3::one(); }; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_z(0.0) + next.lantern.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.7) * Quaternion::rotation_y(-0.8); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.hold.scale = Vec3::one() * 0.0; + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/stand.rs b/voxygen/src/anim/src/character/stand.rs index bfa97846b3..3362bd4ea2 100644 --- a/voxygen/src/anim/src/character/stand.rs +++ b/voxygen/src/anim/src/character/stand.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct StandAnimation; @@ -37,141 +39,143 @@ impl Animation for StandAnimation { .sin() * 0.15, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.3 + breathe * -0.05, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(impact * -0.02 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + slow * 0.3 + impact * 0.2, ); - next.chest.ori = + next.chest.orientation = Quaternion::rotation_z(head_look.x * 0.6) * Quaternion::rotation_x(impact * 0.04); next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - next.belt.offset = Vec3::new( + next.belt.position = Vec3::new( 0.0, skeleton_attr.belt.0 + impact * 0.005, skeleton_attr.belt.1, ); - next.belt.ori = + next.belt.orientation = Quaternion::rotation_z(head_look.x * -0.1) * Quaternion::rotation_x(impact * -0.03); next.belt.scale = Vec3::one() + breathe * -0.03; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + impact * -0.2, skeleton_attr.shorts.1, ); - next.shorts.ori = + next.shorts.orientation = Quaternion::rotation_z(head_look.x * -0.2) * Quaternion::rotation_x(impact * -0.04); next.shorts.scale = Vec3::one() + breathe * -0.03; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + slow * 0.15 - impact * 0.2, skeleton_attr.hand.2 + slow * 0.5 + impact * -0.1, ); - next.l_hand.ori = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); + next.l_hand.orientation = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 + slow * 0.15 - impact * 0.2, skeleton_attr.hand.2 + slow * 0.5 + impact * -0.1, ); - next.r_hand.ori = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); + next.r_hand.orientation = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 - impact * 0.15, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(impact * 0.02); + next.l_foot.orientation = Quaternion::rotation_x(impact * 0.02); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + impact * 0.15, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(impact * -0.02); + next.r_foot.orientation = Quaternion::rotation_x(impact * -0.02); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - + next.hold.position = Vec3::new(0.4, -0.3, -5.8); + next.hold.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/swim.rs b/voxygen/src/anim/src/character/swim.rs index ccb37b6e25..dfaacc2501 100644 --- a/voxygen/src/anim/src/character/swim.rs +++ b/voxygen/src/anim/src/character/swim.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SwimAnimation; @@ -77,7 +79,7 @@ impl Animation for SwimAnimation { ); let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -91,12 +93,12 @@ impl Animation for SwimAnimation { let abstilt = tilt.abs(); let squash = if abstilt > 0.2 { 0.35 } else { 1.0 }; //condenses the body at strong turns - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 - 1.0 + short * 0.3, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x * 0.3 + short * -0.2 * intensity + tilt * 3.0) * Quaternion::rotation_x( (0.4 * head_look.y * (1.0 / intensity)).abs() @@ -106,132 +108,135 @@ impl Animation for SwimAnimation { ); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, -10.0 + skeleton_attr.chest.1 + short * 0.3 * intensity, ); - next.chest.ori = Quaternion::rotation_z(short * 0.1 * intensity); + next.chest.orientation = Quaternion::rotation_z(short * 0.1 * intensity); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) * Quaternion::rotation_z(short * -0.2 * intensity); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) * Quaternion::rotation_z(short * -0.3 * intensity); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -1.0 - skeleton_attr.hand.0, 1.5 + skeleton_attr.hand.1 - foot * 2.0 * intensity * squash, intensity * 5.0 + skeleton_attr.hand.2 + foot * -5.0 * intensity * squash, ); - next.l_hand.ori = Quaternion::rotation_x(1.5 + foot * -1.2 * intensity * squash) + next.l_hand.orientation = Quaternion::rotation_x(1.5 + foot * -1.2 * intensity * squash) * Quaternion::rotation_y(0.4 + foot * -0.35); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( 1.0 + skeleton_attr.hand.0, 1.5 + skeleton_attr.hand.1 + foot * 2.0 * intensity * squash, intensity * 5.0 + skeleton_attr.hand.2 + foot * 5.0 * intensity * squash, ); - next.r_hand.ori = Quaternion::rotation_x(1.5 + foot * 1.2 * intensity * squash) + next.r_hand.orientation = Quaternion::rotation_x(1.5 + foot * 1.2 * intensity * squash) * Quaternion::rotation_y(-0.4 + foot * -0.35); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * 1.5 * intensity * squash, -10.0 + skeleton_attr.foot.2 + footrotl * 3.0 * intensity * squash, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(-0.8 * squash + footrotl * 0.4 * intensity * squash); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * 1.5 * intensity * squash, -10.0 + skeleton_attr.foot.2 + footrotr * 3.0 * intensity * squash, ); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(-0.8 * squash + footrotr * 0.4 * intensity * squash); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * intensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * intensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * intensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * intensity); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; + let switch = if avg_vel.z > 0.0 && avgspeed < 0.5 { avgtotal.min(0.5) } else { avgtotal }; - next.torso.offset = Vec3::new(0.0, 0.0, 1.0 - avgspeed * 0.05) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x( + next.torso.position = Vec3::new(0.0, 0.0, 1.0 - avgspeed * 0.05) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x( (((1.0 / switch) * PI / 2.0 + avg_vel.z * 0.12).min(1.57) - PI / 2.0) + avgspeed * avg_vel.z * -0.003, ) * Quaternion::rotation_y(tilt * 8.0) diff --git a/voxygen/src/anim/src/character/swimwield.rs b/voxygen/src/anim/src/character/swimwield.rs index 3554ffa5e5..4e0f2f4b72 100644 --- a/voxygen/src/anim/src/character/swimwield.rs +++ b/voxygen/src/anim/src/character/swimwield.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SwimWieldAnimation; @@ -67,90 +69,96 @@ impl Animation for SwimWieldAnimation { let noisea = (anim_time as f32 * 11.0 + PI / 6.0).sin(); let noiseb = (anim_time as f32 * 19.0 + PI / 4.0).sin(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * 1.5 * intensity, -10.0 + skeleton_attr.foot.2 + footrotl * 3.0 * intensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.8 + footrotl * 0.4 * intensity); + next.l_foot.orientation = Quaternion::rotation_x(-0.8 + footrotl * 0.4 * intensity); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * 1.5 * intensity, -10.0 + skeleton_attr.foot.2 + footrotr * 3.0 * intensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.8 + footrotr * 0.4 * intensity); + next.r_foot.orientation = Quaternion::rotation_x(-0.8 + footrotr * 0.4 * intensity); next.r_foot.scale = Vec3::one(); + + next.hold.scale = Vec3::one() * 0.0; + if velocity > 0.01 { - next.torso.offset = Vec3::new(0.0, 0.0, 1.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(velocity * -0.05); + next.torso.position = Vec3::new(0.0, 0.0, 1.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(velocity * -0.05); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x( + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x( (-0.5 + short * 0.3 + noisea * 0.3 + noiseb * 0.3).min(-0.1), ); next.back.scale = Vec3::one() * 1.02; } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0 + slowalt * 0.5, skeleton_attr.chest.0, skeleton_attr.chest.1 + u_slow * 0.5, ); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.chest.ori = + next.chest.orientation = Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.25); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = + Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); next.belt.scale = Vec3::one() * 1.02; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.2); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.2); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.3); } match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -3.0); - next.main.ori = Quaternion::rotation_x(-0.1) + next.main.position = Vec3::new(0.0, 0.0, -3.0); + next.main.orientation = Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.15) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); @@ -160,129 +168,132 @@ impl Animation for SwimWieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - //next.control.ori = Quaternion::rotation_x(slow * 1.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + //next.control.orientation = Quaternion::rotation_x(slow * 1.0); // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { if velocity < 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.5 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(0.35 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.ori = Quaternion::rotation_x(-0.35) + next.chest.orientation = Quaternion::rotation_x(-0.35) * Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.15); - next.belt.offset = + next.belt.position = Vec3::new(0.0, 1.0 + skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(0.15) + next.belt.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.15); - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, 1.0 + skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); - next.control.ori = Quaternion::rotation_x(1.8) + next.shorts.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); + next.control.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } else { - next.control.ori = Quaternion::rotation_x(2.1) + next.control.orientation = Quaternion::rotation_x(2.1) * Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } - next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0); - next.l_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0); + next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5); - next.r_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.r_hand.position = Vec3::new(0.5, 0.0, -2.5); + next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-0.0, -2.0, -1.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(-0.0, -2.0, -1.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-3.0, 11.0, 3.0); + next.control.position = Vec3::new(-3.0, 11.0, 3.0); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = + Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(2.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(2.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.control.offset = Vec3::new(6.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(0.3 + u_slow * 0.15) + next.control.position = Vec3::new(6.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(0.3 + u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.5, 0.5, -4.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.5, 0.5, -4.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(8.0, 4.0, 2.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(8.0, 4.0, 2.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(12.0, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(12.0, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new(-14.0, 1.8, 3.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.2) + next.control.position = Vec3::new(-14.0, 1.8, 3.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_y(-0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); @@ -292,116 +303,117 @@ impl Animation for SwimWieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(2.0, 1.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(2.0, 1.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(5.9, 4.5, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(5.9, 4.5, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.hold.offset = Vec3::new(1.2, -1.0, -5.2); - next.hold.ori = Quaternion::rotation_x(-1.7) + next.hold.position = Vec3::new(1.2, -1.0, -5.2); + next.hold.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.1); next.hold.scale = Vec3::one() * 1.0; - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0); - next.l_hand.ori = Quaternion::rotation_x(1.27) + next.l_hand.position = Vec3::new(-7.0, 4.0, 3.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, -1.25); - next.r_hand.ori = Quaternion::rotation_x(1.27) + next.r_hand.position = Vec3::new(7.0, 2.5, -1.25); + next.r_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, -2.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, -2.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.27) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(0.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Farming(_)) => { if velocity < 0.5 { - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(-0.2 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; } - next.l_hand.offset = Vec3::new(9.0, 1.0, 1.0); - next.l_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(9.0, 1.0, 1.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 11.0); - next.r_hand.ori = Quaternion::rotation_x(1.57) + next.r_hand.position = Vec3::new(9.0, 1.0, 11.0); + next.r_hand.orientation = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(7.5, 7.5, 13.2); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(7.5, 7.5, 13.2); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(3.14) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.1) + next.control.position = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.1) * Quaternion::rotation_y(0.6 + u_slow * 0.1) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/wield.rs b/voxygen/src/anim/src/character/wield.rs index 0251f9bb8f..375ce073b7 100644 --- a/voxygen/src/anim/src/character/wield.rs +++ b/voxygen/src/anim/src/character/wield.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct WieldAnimation; @@ -51,76 +53,79 @@ impl Animation for WieldAnimation { let noiseb = (anim_time as f32 * 19.0 + PI / 4.0).sin(); if velocity > 0.5 { - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x( + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x( (-0.5 + short * 0.3 + noisea * 0.3 + noiseb * 0.3).min(-0.1), ); next.back.scale = Vec3::one() * 1.02; } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0 + slowalt * 0.5, skeleton_attr.chest.0, skeleton_attr.chest.1 + u_slow * 0.5, ); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(u_slowalt * 0.035 - 0.2); + next.l_foot.orientation = Quaternion::rotation_x(u_slowalt * 0.035 - 0.2); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(u_slow * 0.035); + next.r_foot.orientation = Quaternion::rotation_x(u_slow * 0.035); - next.chest.ori = + next.chest.orientation = Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.15); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = + Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); next.belt.scale = Vec3::one() * 1.02; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.2); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.2); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.3); } match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -3.0); - next.main.ori = Quaternion::rotation_x(-0.1) + next.main.position = Vec3::new(0.0, 0.0, -3.0); + next.main.orientation = Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.15) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); @@ -130,129 +135,132 @@ impl Animation for WieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - //next.control.ori = Quaternion::rotation_x(slow * 1.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + //next.control.orientation = Quaternion::rotation_x(slow * 1.0); // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { if velocity < 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.5 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(0.35 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.ori = Quaternion::rotation_x(-0.35) + next.chest.orientation = Quaternion::rotation_x(-0.35) * Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.15); - next.belt.offset = + next.belt.position = Vec3::new(0.0, 1.0 + skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(0.15) + next.belt.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.15); - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, 1.0 + skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); - next.control.ori = Quaternion::rotation_x(1.8) + next.shorts.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); + next.control.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } else { - next.control.ori = Quaternion::rotation_x(2.1) + next.control.orientation = Quaternion::rotation_x(2.1) * Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } - next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0); - next.l_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0); + next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5); - next.r_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.r_hand.position = Vec3::new(0.5, 0.0, -2.5); + next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-0.0, -2.0, -1.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(-0.0, -2.0, -1.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-3.0, 11.0, 3.0); + next.control.position = Vec3::new(-3.0, 11.0, 3.0); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = + Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(2.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(2.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.control.offset = Vec3::new(6.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(0.3 + u_slow * 0.15) + next.control.position = Vec3::new(6.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(0.3 + u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.5, 0.5, -4.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.5, 0.5, -4.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(8.0, 4.0, 2.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(8.0, 4.0, 2.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(12.0, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(12.0, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new(-14.0, 1.8, 3.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.2) + next.control.position = Vec3::new(-14.0, 1.8, 3.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_y(-0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); @@ -262,116 +270,117 @@ impl Animation for WieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(2.0, 1.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(2.0, 1.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(5.9, 4.5, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(5.9, 4.5, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.hold.offset = Vec3::new(1.2, -1.0, -5.2); - next.hold.ori = Quaternion::rotation_x(-1.7) + next.hold.position = Vec3::new(1.2, -1.0, -5.2); + next.hold.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.1); next.hold.scale = Vec3::one() * 1.0; - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0); - next.l_hand.ori = Quaternion::rotation_x(1.27) + next.l_hand.position = Vec3::new(-7.0, 4.0, 3.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, -1.25); - next.r_hand.ori = Quaternion::rotation_x(1.27) + next.r_hand.position = Vec3::new(7.0, 2.5, -1.25); + next.r_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, -2.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, -2.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.27) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(0.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Farming(_)) => { if velocity < 0.5 { - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(-0.2 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; } - next.l_hand.offset = Vec3::new(9.0, 1.0, 1.0); - next.l_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(9.0, 1.0, 1.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 11.0); - next.r_hand.ori = Quaternion::rotation_x(1.57) + next.r_hand.position = Vec3::new(9.0, 1.0, 11.0); + next.r_hand.orientation = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(7.5, 7.5, 13.2); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(7.5, 7.5, 13.2); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(3.14) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.1) + next.control.position = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.1) * Quaternion::rotation_y(0.6 + u_slow * 0.1) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/critter/idle.rs b/voxygen/src/anim/src/critter/idle.rs index 22708a480d..192a89cab0 100644 --- a/voxygen/src/anim/src/critter/idle.rs +++ b/voxygen/src/anim/src/critter/idle.rs @@ -1,7 +1,7 @@ use super::{super::Animation, CritterAttr, CritterSkeleton}; //use std::{f32::consts::PI, ops::Mul}; +use super::super::vek::*; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -37,29 +37,30 @@ impl Animation for IdleAnimation { .sin() * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(rat_head_look.x) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(rat_head_look.x) * Quaternion::rotation_x(rat_head_look.y + wave * 0.03); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + wave * 0.3, ) / 18.0; - next.chest.ori = Quaternion::rotation_y(wave_slow * 0.06); + next.chest.orientation = Quaternion::rotation_y(wave_slow * 0.06); next.chest.scale = Vec3::one() / 18.0; - next.feet_f.offset = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); - next.feet_f.ori = Quaternion::rotation_z(0.0); + next.feet_f.position = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); + next.feet_f.orientation = Quaternion::rotation_z(0.0); next.feet_f.scale = Vec3::one(); - next.feet_b.offset = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); - next.feet_b.ori = Quaternion::rotation_x(0.0); + next.feet_b.position = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); + next.feet_b.orientation = Quaternion::rotation_x(0.0); next.feet_b.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0 + wave * 0.2, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_y(wave_slow * 0.05); + next.tail.position = + Vec3::new(0.0, skeleton_attr.tail.0 + wave * 0.2, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_y(wave_slow * 0.05); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/critter/jump.rs b/voxygen/src/anim/src/critter/jump.rs index bb82953d97..7630744289 100644 --- a/voxygen/src/anim/src/critter/jump.rs +++ b/voxygen/src/anim/src/critter/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, CritterAttr, CritterSkeleton}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -23,24 +23,24 @@ impl Animation for JumpAnimation { let wave = (_anim_time as f32 * 1.0).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(0.8) * Quaternion::rotation_x(0.5); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(0.8) * Quaternion::rotation_x(0.5); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) / 18.0; - next.chest.ori = Quaternion::rotation_y(0.0); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) / 18.0; + next.chest.orientation = Quaternion::rotation_y(0.0); next.chest.scale = Vec3::one() / 18.0; - next.feet_f.offset = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); - next.feet_f.ori = Quaternion::rotation_x(wave * 0.4); + next.feet_f.position = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); + next.feet_f.orientation = Quaternion::rotation_x(wave * 0.4); next.feet_f.scale = Vec3::one(); - next.feet_b.offset = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); - next.feet_b.ori = Quaternion::rotation_x(wave * 0.4); + next.feet_b.position = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); + next.feet_b.orientation = Quaternion::rotation_x(wave * 0.4); next.feet_b.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_y(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_y(0.0); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/critter/mod.rs b/voxygen/src/anim/src/critter/mod.rs index 2fe638803e..5e50de56cc 100644 --- a/voxygen/src/anim/src/critter/mod.rs +++ b/voxygen/src/anim/src/critter/mod.rs @@ -5,18 +5,18 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; + +skeleton_impls!(struct CritterSkeleton { + + head, + + chest, + + feet_f, + + feet_b, + + tail, +}); -#[derive(Clone, Default)] -pub struct CritterSkeleton { - head: Bone, - chest: Bone, - feet_f: Bone, - feet_b: Bone, - tail: Bone, -} pub struct CritterAttr { head: (f32, f32), chest: (f32, f32), @@ -25,51 +25,30 @@ pub struct CritterAttr { tail: (f32, f32), } -impl CritterSkeleton { - pub fn new() -> Self { Self::default() } -} - impl Skeleton for CritterSkeleton { type Attr = CritterAttr; + const BONE_COUNT: usize = 5; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"critter_compute_mats\0"; - fn bone_count(&self) -> usize { 5 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "critter_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let chest_mat = self.chest.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_mat * self.head.compute_base_matrix()), - FigureBoneData::new(chest_mat), - FigureBoneData::new(chest_mat * self.feet_f.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.feet_b.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.tail.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.chest.interpolate(&target.chest, dt); - self.feet_f.interpolate(&target.feet_f, dt); - self.feet_b.interpolate(&target.feet_b, dt); - self.tail.interpolate(&target.tail, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(chest_mat * Mat4::::from(self.head)), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.feet_f)), + make_bone(chest_mat * Mat4::::from(self.feet_b)), + make_bone(chest_mat * Mat4::::from(self.tail)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/critter/run.rs b/voxygen/src/anim/src/critter/run.rs index cb2019a9a0..2f342e28bd 100644 --- a/voxygen/src/anim/src/critter/run.rs +++ b/voxygen/src/anim/src/critter/run.rs @@ -1,7 +1,7 @@ use super::{super::Animation, CritterAttr, CritterSkeleton}; //use std::{f32::consts::PI, ops::Mul}; +use super::super::vek::*; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -26,30 +26,32 @@ impl Animation for RunAnimation { let wavealt = (anim_time as f32 * 8.0 + PI / 2.0).sin(); let wave_slow = (anim_time as f32 * 6.5 + PI).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + wave * 0.03); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + wave * 0.03); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 + wave * 1.0, skeleton_attr.chest.1, ) / 18.0; - next.chest.ori = Quaternion::rotation_x(wave * 0.1); + next.chest.orientation = Quaternion::rotation_x(wave * 0.1); next.chest.scale = Vec3::one() / 18.0; - next.feet_f.offset = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); - next.feet_f.ori = + next.feet_f.position = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); + next.feet_f.orientation = Quaternion::rotation_x(wave * 0.8) * Quaternion::rotation_z(wavealt / 6.0); next.feet_f.scale = Vec3::one(); - next.feet_b.offset = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); - next.feet_b.ori = + next.feet_b.position = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); + next.feet_b.orientation = Quaternion::rotation_x(wavealt * 0.8) * Quaternion::rotation_z(wavealt / 6.0); next.feet_b.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0 + wave * 1.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_y(wave_slow * 0.08); + next.tail.position = + Vec3::new(0.0, skeleton_attr.tail.0 + wave * 1.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_y(wave_slow * 0.08); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/dragon/fly.rs b/voxygen/src/anim/src/dragon/fly.rs index 14379e7a42..4602f19cd5 100644 --- a/voxygen/src/anim/src/dragon/fly.rs +++ b/voxygen/src/anim/src/dragon/fly.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, DragonSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + DragonSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct FlyAnimation; @@ -36,123 +38,123 @@ impl Animation for FlyAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + wave_ultra_slow * 0.20, ); - next.head_upper.ori = + next.head_upper.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(wave_ultra_slow * -0.10); next.head_upper.scale = Vec3::one() * 1.05; - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + wave_ultra_slow * 0.20, ); - next.head_lower.ori = + next.head_lower.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(wave_ultra_slow * -0.10); next.head_lower.scale = Vec3::one() * 1.05; - next.jaw.offset = Vec3::new( + next.jaw.position = Vec3::new( 0.0, skeleton_attr.jaw.0 - wave_ultra_slow_cos * 0.12, skeleton_attr.jaw.1 + wave_slow * 0.2, ); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.03); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.03); next.jaw.scale = Vec3::one() * 1.05; - next.tail_front.offset = Vec3::new( + next.tail_front.position = Vec3::new( 0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1 + centeroffset * 0.6, ); - next.tail_front.ori = Quaternion::rotation_x(center * 0.03); + next.tail_front.orientation = Quaternion::rotation_x(center * 0.03); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = Vec3::new( + next.tail_rear.position = Vec3::new( 0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1 + centeroffset * 0.6, ); - next.tail_rear.ori = Quaternion::rotation_x(center * 0.03); + next.tail_rear.orientation = Quaternion::rotation_x(center * 0.03); next.tail_rear.scale = Vec3::one() * 0.98; - next.chest_front.offset = Vec3::new( + next.chest_front.position = Vec3::new( 0.0, skeleton_attr.chest_front.0, skeleton_attr.chest_front.1, ); - next.chest_front.ori = Quaternion::rotation_y(center * 0.05); + next.chest_front.orientation = Quaternion::rotation_y(center * 0.05); next.chest_front.scale = Vec3::one(); - next.chest_rear.offset = + next.chest_rear.position = Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1); - next.chest_rear.ori = Quaternion::rotation_y(center * 0.05); + next.chest_rear.orientation = Quaternion::rotation_y(center * 0.05); next.chest_rear.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_x(-1.3 + footl * 0.06); + next.foot_fl.orientation = Quaternion::rotation_x(-1.3 + footl * 0.06); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_x(-1.3 + footr * 0.06); + next.foot_fr.orientation = Quaternion::rotation_x(-1.3 + footr * 0.06); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_x(-1.3 + footl * 0.06); + next.foot_bl.orientation = Quaternion::rotation_x(-1.3 + footl * 0.06); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_x(-1.3 + footr * 0.06); + next.foot_br.orientation = Quaternion::rotation_x(-1.3 + footr * 0.06); next.foot_br.scale = Vec3::one(); - next.wing_in_l.offset = Vec3::new( + next.wing_in_l.position = Vec3::new( -skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_l.ori = Quaternion::rotation_y(0.4 + wingl * 0.6); + next.wing_in_l.orientation = Quaternion::rotation_y(0.4 + wingl * 0.6); next.wing_in_l.scale = Vec3::one(); - next.wing_in_r.offset = Vec3::new( + next.wing_in_r.position = Vec3::new( skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_r.ori = Quaternion::rotation_y(-0.4 + wingr * 0.6); + next.wing_in_r.orientation = Quaternion::rotation_y(-0.4 + wingr * 0.6); next.wing_in_r.scale = Vec3::one(); - next.wing_out_l.offset = Vec3::new( + next.wing_out_l.position = Vec3::new( -skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_l.ori = Quaternion::rotation_y((0.35 + wingl * 0.6).max(0.2)); + next.wing_out_l.orientation = Quaternion::rotation_y((0.35 + wingl * 0.6).max(0.2)); next.wing_out_l.scale = Vec3::one(); - next.wing_out_r.offset = Vec3::new( + next.wing_out_r.position = Vec3::new( skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_r.ori = Quaternion::rotation_y((-0.35 + wingr * 0.6).min(-0.2)); + next.wing_out_r.orientation = Quaternion::rotation_y((-0.35 + wingr * 0.6).min(-0.2)); next.wing_out_r.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/dragon/idle.rs b/voxygen/src/anim/src/dragon/idle.rs index b61f87fbc4..bee9419896 100644 --- a/voxygen/src/anim/src/dragon/idle.rs +++ b/voxygen/src/anim/src/dragon/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, DragonSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + DragonSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,113 +40,115 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + ultra_slow * 0.20, ); - next.head_upper.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_upper.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(0.8 * dragon_look.y); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + ultra_slow * 0.20, ); - next.head_lower.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_lower.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(-0.2 + 0.8 * dragon_look.y); next.head_lower.scale = Vec3::one() * 1.05; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(slow * 0.04); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(slow * 0.04); next.jaw.scale = Vec3::one() * 1.05; - next.chest_front.offset = Vec3::new( + next.chest_front.position = Vec3::new( 0.0, skeleton_attr.chest_front.0, skeleton_attr.chest_front.1, ); - next.chest_front.ori = Quaternion::rotation_y(0.0); + next.chest_front.orientation = Quaternion::rotation_y(0.0); next.chest_front.scale = Vec3::one() * 1.05; - next.chest_rear.offset = + next.chest_rear.position = Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1); - next.chest_rear.ori = Quaternion::rotation_y(0.0); + next.chest_rear.orientation = Quaternion::rotation_y(0.0); next.chest_rear.scale = Vec3::one() * 1.05; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_z(slowalt * 0.10) * Quaternion::rotation_x(0.1); + next.tail_front.orientation = + Quaternion::rotation_z(slowalt * 0.10) * Quaternion::rotation_x(0.1); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(0.05); + next.tail_rear.orientation = + Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(0.05); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_x(0.0); + next.foot_fl.orientation = Quaternion::rotation_x(0.0); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_x(0.0); + next.foot_fr.orientation = Quaternion::rotation_x(0.0); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_x(0.0); + next.foot_bl.orientation = Quaternion::rotation_x(0.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_x(0.0); + next.foot_br.orientation = Quaternion::rotation_x(0.0); next.foot_br.scale = Vec3::one(); - next.wing_in_l.offset = Vec3::new( + next.wing_in_l.position = Vec3::new( -skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_l.ori = Quaternion::rotation_y(0.8 + slow * 0.02); + next.wing_in_l.orientation = Quaternion::rotation_y(0.8 + slow * 0.02); next.wing_in_l.scale = Vec3::one(); - next.wing_in_r.offset = Vec3::new( + next.wing_in_r.position = Vec3::new( skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_r.ori = Quaternion::rotation_y(-0.8 - slow * 0.02); + next.wing_in_r.orientation = Quaternion::rotation_y(-0.8 - slow * 0.02); next.wing_in_r.scale = Vec3::one(); - next.wing_out_l.offset = Vec3::new( + next.wing_out_l.position = Vec3::new( -skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_l.ori = Quaternion::rotation_y(-2.0 + slow * 0.02); + next.wing_out_l.orientation = Quaternion::rotation_y(-2.0 + slow * 0.02); next.wing_out_l.scale = Vec3::one(); - next.wing_out_r.offset = Vec3::new( + next.wing_out_r.position = Vec3::new( skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_r.ori = Quaternion::rotation_y(2.0 - slow * 0.02); + next.wing_out_r.orientation = Quaternion::rotation_y(2.0 - slow * 0.02); next.wing_out_r.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/dragon/mod.rs b/voxygen/src/anim/src/dragon/mod.rs index 5a4ec4bbf5..4792a7afdb 100644 --- a/voxygen/src/anim/src/dragon/mod.rs +++ b/voxygen/src/anim/src/dragon/mod.rs @@ -5,112 +5,68 @@ pub mod run; // Reexports pub use self::{fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct DragonSkeleton { - head_upper: Bone, - head_lower: Bone, - jaw: Bone, - chest_front: Bone, - chest_rear: Bone, - tail_front: Bone, - tail_rear: Bone, - wing_in_l: Bone, - wing_in_r: Bone, - wing_out_l: Bone, - wing_out_r: Bone, - foot_fl: Bone, - foot_fr: Bone, - foot_bl: Bone, - foot_br: Bone, -} - -impl DragonSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct DragonSkeleton { + + head_upper, + + head_lower, + + jaw, + + chest_front, + + chest_rear, + + tail_front, + + tail_rear, + + wing_in_l, + + wing_in_r, + + wing_out_l, + + wing_out_r, + + foot_fl, + + foot_fr, + + foot_bl, + + foot_br, +}); impl Skeleton for DragonSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 15; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"dragon_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "dragon_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let head_upper_mat = self.head_upper.compute_base_matrix(); - let head_lower_mat = self.head_lower.compute_base_matrix(); - let chest_front_mat = self.chest_front.compute_base_matrix(); - let chest_rear_mat = self.chest_rear.compute_base_matrix(); - let wing_in_l_mat = self.wing_in_l.compute_base_matrix(); - let wing_in_r_mat = self.wing_in_r.compute_base_matrix(); - let tail_front_mat = self.tail_front.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_front_mat * head_lower_mat * head_upper_mat), - FigureBoneData::new(chest_front_mat * head_lower_mat), - FigureBoneData::new( - chest_front_mat - * head_lower_mat - * head_upper_mat - * self.jaw.compute_base_matrix(), - ), - FigureBoneData::new(chest_front_mat), - FigureBoneData::new(chest_front_mat * self.chest_rear.compute_base_matrix()), - FigureBoneData::new(chest_front_mat * chest_rear_mat * tail_front_mat), - FigureBoneData::new( - chest_front_mat - * chest_rear_mat - * tail_front_mat - * self.tail_rear.compute_base_matrix(), - ), - FigureBoneData::new(chest_front_mat * self.wing_in_l.compute_base_matrix()), - FigureBoneData::new(chest_front_mat * self.wing_in_r.compute_base_matrix()), - FigureBoneData::new( - chest_front_mat * wing_in_l_mat * self.wing_out_l.compute_base_matrix(), - ), - FigureBoneData::new( - chest_front_mat * wing_in_r_mat * self.wing_out_r.compute_base_matrix(), - ), - FigureBoneData::new(chest_front_mat * self.foot_fl.compute_base_matrix()), - FigureBoneData::new(chest_front_mat * self.foot_fr.compute_base_matrix()), - FigureBoneData::new( - chest_front_mat - * self.chest_rear.compute_base_matrix() - * self.foot_bl.compute_base_matrix(), - ), - FigureBoneData::new( - chest_front_mat - * self.chest_rear.compute_base_matrix() - * self.foot_br.compute_base_matrix(), - ), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_front_mat = base_mat * Mat4::::from(self.chest_front); + let chest_rear_mat = chest_front_mat * Mat4::::from(self.chest_rear); + let head_lower_mat = chest_front_mat * Mat4::::from(self.head_lower); + let wing_in_l_mat = chest_front_mat * Mat4::::from(self.wing_in_l); + let wing_in_r_mat = chest_front_mat * Mat4::::from(self.wing_in_r); + let tail_front_mat = chest_rear_mat * Mat4::::from(self.tail_front); + let head_upper_mat = head_lower_mat * Mat4::::from(self.head_upper); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head_upper.interpolate(&target.head_upper, dt); - self.head_lower.interpolate(&target.head_lower, dt); - self.jaw.interpolate(&target.jaw, dt); - self.chest_front.interpolate(&target.chest_front, dt); - self.chest_rear.interpolate(&target.chest_rear, dt); - self.tail_front.interpolate(&target.tail_front, dt); - self.tail_rear.interpolate(&target.tail_rear, dt); - self.wing_in_l.interpolate(&target.wing_in_l, dt); - self.wing_in_r.interpolate(&target.wing_in_r, dt); - self.wing_out_l.interpolate(&target.wing_out_l, dt); - self.wing_out_r.interpolate(&target.wing_out_r, dt); - self.foot_fl.interpolate(&target.foot_fl, dt); - self.foot_fr.interpolate(&target.foot_fr, dt); - self.foot_bl.interpolate(&target.foot_bl, dt); - self.foot_br.interpolate(&target.foot_br, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_upper_mat), + make_bone(head_lower_mat), + make_bone(head_upper_mat * Mat4::::from(self.jaw)), + make_bone(chest_front_mat), + make_bone(chest_rear_mat), + make_bone(tail_front_mat), + make_bone(tail_front_mat * Mat4::::from(self.tail_rear)), + make_bone(wing_in_l_mat), + make_bone(wing_in_r_mat), + make_bone(wing_in_l_mat * Mat4::::from(self.wing_out_l)), + make_bone(wing_in_r_mat * Mat4::::from(self.wing_out_r)), + make_bone(chest_front_mat * Mat4::::from(self.foot_fl)), + make_bone(chest_front_mat * Mat4::::from(self.foot_fr)), + make_bone(chest_rear_mat * Mat4::::from(self.foot_bl)), + make_bone(chest_rear_mat * Mat4::::from(self.foot_br)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/dragon/run.rs b/voxygen/src/anim/src/dragon/run.rs index 49f42dcf12..f2ebd5671d 100644 --- a/voxygen/src/anim/src/dragon/run.rs +++ b/voxygen/src/anim/src/dragon/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, DragonSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + DragonSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -37,7 +39,7 @@ impl Animation for RunAnimation { // let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -68,128 +70,128 @@ impl Animation for RunAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(short * -0.03 - 0.1) + next.head_upper.orientation = Quaternion::rotation_x(short * -0.03 - 0.1) * Quaternion::rotation_z(tilt * -1.2) * Quaternion::rotation_y(tilt * 0.8); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(tilt * -0.8) + next.head_lower.orientation = Quaternion::rotation_z(tilt * -0.8) * Quaternion::rotation_x(short * -0.05) * Quaternion::rotation_y(tilt * 0.3); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new( + next.jaw.position = Vec3::new( 0.0, skeleton_attr.jaw.0 - wave_ultra_slow_cos * 0.12, skeleton_attr.jaw.1 + wave_slow * 0.2, ); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.03); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.03); next.jaw.scale = Vec3::one() * 1.05; - next.tail_front.offset = Vec3::new( + next.tail_front.position = Vec3::new( 0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1 + centeroffset * 0.6, ); - next.tail_front.ori = + next.tail_front.orientation = Quaternion::rotation_x(center * 0.03) * Quaternion::rotation_z(tilt * 1.5); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = Vec3::new( + next.tail_rear.position = Vec3::new( 0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1 + centeroffset * 0.6, ); - next.tail_rear.ori = + next.tail_rear.orientation = Quaternion::rotation_x(center * 0.03) * Quaternion::rotation_z(tilt * 1.5); next.tail_rear.scale = Vec3::one() * 0.98; - next.chest_front.offset = Vec3::new( + next.chest_front.position = Vec3::new( 0.0, skeleton_attr.chest_front.0, skeleton_attr.chest_front.1 + shortalt * 2.5 + x_tilt * 10.0, ); - next.chest_front.ori = Quaternion::rotation_x(short * 0.13 + x_tilt) + next.chest_front.orientation = Quaternion::rotation_x(short * 0.13 + x_tilt) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(tilt * -1.5); next.chest_front.scale = Vec3::one(); - next.chest_rear.offset = Vec3::new( + next.chest_rear.position = Vec3::new( 0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1 + shortalt * 0.2, ); - next.chest_rear.ori = Quaternion::rotation_x(short * 0.1) + next.chest_rear.orientation = Quaternion::rotation_x(short * 0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(tilt * 1.8); next.chest_rear.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + horilf * 2.5, skeleton_attr.feet_f.2 + vertlf * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_fl.ori = Quaternion::rotation_x(horilf * 0.6); + next.foot_fl.orientation = Quaternion::rotation_x(horilf * 0.6); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + horirfoffset * 2.5, skeleton_attr.feet_f.2 + vertrfoffset * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_fr.ori = Quaternion::rotation_x(horirb * 0.6); + next.foot_fr.orientation = Quaternion::rotation_x(horirb * 0.6); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + horilboffset * 3.0, skeleton_attr.feet_b.2 + vertlboffset * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_bl.ori = Quaternion::rotation_x(horilf * 0.55); + next.foot_bl.orientation = Quaternion::rotation_x(horilf * 0.55); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + horirb * 3.0, skeleton_attr.feet_b.2 + vertrb * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_br.ori = Quaternion::rotation_x(horirb * 0.55); + next.foot_br.orientation = Quaternion::rotation_x(horirb * 0.55); next.foot_br.scale = Vec3::one(); - next.wing_in_l.offset = Vec3::new( + next.wing_in_l.position = Vec3::new( -skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_l.ori = Quaternion::rotation_y(0.8 + tilt * 1.0); + next.wing_in_l.orientation = Quaternion::rotation_y(0.8 + tilt * 1.0); next.wing_in_l.scale = Vec3::one(); - next.wing_in_r.offset = Vec3::new( + next.wing_in_r.position = Vec3::new( skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_r.ori = Quaternion::rotation_y(-0.8 + tilt * 1.0); + next.wing_in_r.orientation = Quaternion::rotation_y(-0.8 + tilt * 1.0); next.wing_in_r.scale = Vec3::one(); - next.wing_out_l.offset = Vec3::new( + next.wing_out_l.position = Vec3::new( -skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_l.ori = Quaternion::rotation_y(-2.0 + tilt * 1.0); + next.wing_out_l.orientation = Quaternion::rotation_y(-2.0 + tilt * 1.0); next.wing_out_l.scale = Vec3::one(); - next.wing_out_r.offset = Vec3::new( + next.wing_out_r.position = Vec3::new( skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_r.ori = Quaternion::rotation_y(2.0 + tilt * 1.0); + next.wing_out_r.orientation = Quaternion::rotation_y(2.0 + tilt * 1.0); next.wing_out_r.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/fish_medium/idle.rs b/voxygen/src/anim/src/fish_medium/idle.rs index a61c284abd..150a67826d 100644 --- a/voxygen/src/anim/src/fish_medium/idle.rs +++ b/voxygen/src/anim/src/fish_medium/idle.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct IdleAnimation; @@ -21,28 +21,28 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 4.5, 2.0); - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 4.5, 2.0); + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() * 1.01; - next.rear.offset = Vec3::new(0.0, 3.1, -4.5); - next.rear.ori = Quaternion::rotation_z(0.0); + next.rear.position = Vec3::new(0.0, 3.1, -4.5); + next.rear.orientation = Quaternion::rotation_z(0.0); next.rear.scale = Vec3::one() * 0.98; - next.tail.offset = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() / 11.0; - next.fin_l.offset = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.ori = Quaternion::rotation_y(0.0); + next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.orientation = Quaternion::rotation_y(0.0); next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.offset = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.ori = Quaternion::rotation_y(0.0); + next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.orientation = Quaternion::rotation_y(0.0); next.fin_r.scale = Vec3::one() / 10.5; next } diff --git a/voxygen/src/anim/src/fish_medium/jump.rs b/voxygen/src/anim/src/fish_medium/jump.rs index 2858c2ba23..33e679b0e2 100644 --- a/voxygen/src/anim/src/fish_medium/jump.rs +++ b/voxygen/src/anim/src/fish_medium/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -21,28 +21,28 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 4.5, 2.0); - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 4.5, 2.0); + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() * 1.01; - next.rear.offset = Vec3::new(0.0, 3.1, -4.5); - next.rear.ori = Quaternion::rotation_z(0.0); + next.rear.position = Vec3::new(0.0, 3.1, -4.5); + next.rear.orientation = Quaternion::rotation_z(0.0); next.rear.scale = Vec3::one() * 0.98; - next.tail.offset = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() / 11.0; - next.fin_l.offset = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.ori = Quaternion::rotation_y(0.0); + next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.orientation = Quaternion::rotation_y(0.0); next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.offset = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.ori = Quaternion::rotation_y(0.0); + next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.orientation = Quaternion::rotation_y(0.0); next.fin_r.scale = Vec3::one() / 10.5; next } diff --git a/voxygen/src/anim/src/fish_medium/mod.rs b/voxygen/src/anim/src/fish_medium/mod.rs index 7e3741d4c5..97e663bb97 100644 --- a/voxygen/src/anim/src/fish_medium/mod.rs +++ b/voxygen/src/anim/src/fish_medium/mod.rs @@ -5,79 +5,47 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone)] -pub struct FishMediumSkeleton { - head: Bone, - torso: Bone, - rear: Bone, - tail: Bone, - fin_l: Bone, - fin_r: Bone, -} - -impl FishMediumSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self { - head: Bone::default(), - torso: Bone::default(), - rear: Bone::default(), - tail: Bone::default(), - fin_l: Bone::default(), - fin_r: Bone::default(), - } - } -} +skeleton_impls!(struct FishMediumSkeleton { + + head, + + torso, + + rear, + + tail, + + fin_l, + + fin_r, +}); impl Skeleton for FishMediumSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 6; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fish_medium_compute_mats\0"; - fn bone_count(&self) -> usize { 6 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_medium_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); - let rear_mat = self.rear.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); + let rear_mat = torso_mat * Mat4::::from(self.rear); - ( - [ - FigureBoneData::new(self.head.compute_base_matrix() * torso_mat), - FigureBoneData::new(torso_mat), - FigureBoneData::new(rear_mat * torso_mat), - FigureBoneData::new(self.tail.compute_base_matrix() * rear_mat), - FigureBoneData::new(self.fin_l.compute_base_matrix() * rear_mat), - FigureBoneData::new(self.fin_r.compute_base_matrix() * rear_mat), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.torso.interpolate(&target.torso, dt); - self.rear.interpolate(&target.rear, dt); - self.tail.interpolate(&target.tail, dt); - self.fin_l.interpolate(&target.fin_l, dt); - self.fin_r.interpolate(&target.fin_r, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat * Mat4::::from(self.head)), + make_bone(torso_mat), + make_bone(rear_mat), + make_bone(rear_mat * Mat4::::from(self.tail)), + make_bone(rear_mat * Mat4::::from(self.fin_l)), + make_bone(rear_mat * Mat4::::from(self.fin_r)), + ]; + Vec3::default() } } + pub struct SkeletonAttr; impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { diff --git a/voxygen/src/anim/src/fish_medium/run.rs b/voxygen/src/anim/src/fish_medium/run.rs index 47219f1b31..4d0823abd3 100644 --- a/voxygen/src/anim/src/fish_medium/run.rs +++ b/voxygen/src/anim/src/fish_medium/run.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct RunAnimation; @@ -21,28 +21,28 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 4.5, 2.0); - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 4.5, 2.0); + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() * 1.01; - next.rear.offset = Vec3::new(0.0, 3.1, -4.5); - next.rear.ori = Quaternion::rotation_z(0.0); + next.rear.position = Vec3::new(0.0, 3.1, -4.5); + next.rear.orientation = Quaternion::rotation_z(0.0); next.rear.scale = Vec3::one() * 0.98; - next.tail.offset = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() / 11.0; - next.fin_l.offset = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.ori = Quaternion::rotation_y(0.0); + next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.orientation = Quaternion::rotation_y(0.0); next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.offset = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.ori = Quaternion::rotation_y(0.0); + next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.orientation = Quaternion::rotation_y(0.0); next.fin_r.scale = Vec3::one() / 10.5; next } diff --git a/voxygen/src/anim/src/fish_small/idle.rs b/voxygen/src/anim/src/fish_small/idle.rs index 4262a42db9..841627ec41 100644 --- a/voxygen/src/anim/src/fish_small/idle.rs +++ b/voxygen/src/anim/src/fish_small/idle.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct IdleAnimation; @@ -21,12 +21,12 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.tail.offset = Vec3::new(0.0, 4.5, 2.0); - next.tail.ori = Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, 4.5, 2.0); + next.tail.orientation = Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() * 1.01; next diff --git a/voxygen/src/anim/src/fish_small/jump.rs b/voxygen/src/anim/src/fish_small/jump.rs index 90bca46ade..5af66c2869 100644 --- a/voxygen/src/anim/src/fish_small/jump.rs +++ b/voxygen/src/anim/src/fish_small/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishSmallSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -21,12 +21,12 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.tail.offset = Vec3::new(0.0, 4.5, 2.0); - next.tail.ori = Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, 4.5, 2.0); + next.tail.orientation = Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() * 1.01; next diff --git a/voxygen/src/anim/src/fish_small/mod.rs b/voxygen/src/anim/src/fish_small/mod.rs index 7821d8b446..d3281ff459 100644 --- a/voxygen/src/anim/src/fish_small/mod.rs +++ b/voxygen/src/anim/src/fish_small/mod.rs @@ -5,65 +5,36 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone)] -pub struct FishSmallSkeleton { - torso: Bone, - tail: Bone, -} - -impl FishSmallSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self { - torso: Bone::default(), - tail: Bone::default(), - } - } -} +skeleton_impls!(struct FishSmallSkeleton { + + torso, + + tail, +}); impl Skeleton for FishSmallSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 2; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fish_small_compute_mats\0"; - fn bone_count(&self) -> usize { 2 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_small_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); - ( - [ - FigureBoneData::new(torso_mat), - FigureBoneData::new(self.tail.compute_base_matrix() * torso_mat), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.torso.interpolate(&target.torso, dt); - self.tail.interpolate(&target.tail, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat), + make_bone(torso_mat * Mat4::::from(self.tail)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/fish_small/run.rs b/voxygen/src/anim/src/fish_small/run.rs index 8e93a3ae7c..f19d685e9e 100644 --- a/voxygen/src/anim/src/fish_small/run.rs +++ b/voxygen/src/anim/src/fish_small/run.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct RunAnimation; @@ -21,12 +21,12 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.tail.offset = Vec3::new(0.0, 4.5, 2.0); - next.tail.ori = Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, 4.5, 2.0); + next.tail.orientation = Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() * 1.01; next diff --git a/voxygen/src/anim/src/fixture/mod.rs b/voxygen/src/anim/src/fixture/mod.rs index 40fe3095fc..633b2a5ec6 100644 --- a/voxygen/src/anim/src/fixture/mod.rs +++ b/voxygen/src/anim/src/fixture/mod.rs @@ -1,49 +1,35 @@ -use super::{FigureBoneData, Skeleton}; -use vek::Vec3; +use super::{make_bone, vek::*, FigureBoneData, Skeleton}; -#[derive(Clone)] +#[derive(Clone, Default)] pub struct FixtureSkeleton; pub struct SkeletonAttr; -impl FixtureSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { Self {} } +impl<'a, Factor> Lerp for &'a FixtureSkeleton { + type Output = FixtureSkeleton; + + fn lerp_unclamped_precise(_from: Self, _to: Self, _factor: Factor) -> Self::Output { + FixtureSkeleton + } + + fn lerp_unclamped(_from: Self, _to: Self, _factor: Factor) -> Self::Output { FixtureSkeleton } } impl Skeleton for FixtureSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 1; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fixture_compute_mats\0"; - fn bone_count(&self) -> usize { 1 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "fixture_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - ( - [ - FigureBoneData::new(vek::Mat4::identity()), // <-- This is actually a bone! - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - ], - Vec3::default(), - ) + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + buf[0] = make_bone(base_mat); + Vec3::default() } - - fn interpolate(&mut self, _target: &Self, _dt: f32) {} } diff --git a/voxygen/src/anim/src/golem/idle.rs b/voxygen/src/anim/src/golem/idle.rs index cbdfe7b05e..da453d4629 100644 --- a/voxygen/src/anim/src/golem/idle.rs +++ b/voxygen/src/anim/src/golem/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, GolemSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + GolemSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,88 +40,89 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + torso * 0.2, ) * 1.02; - next.head.ori = Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); + next.head.orientation = + Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + torso * 0.5, ) / 8.0; - next.upper_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one() / 8.0; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0); - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0); + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/golem/jump.rs b/voxygen/src/anim/src/golem/jump.rs index ac294f59dc..277f345ebd 100644 --- a/voxygen/src/anim/src/golem/jump.rs +++ b/voxygen/src/anim/src/golem/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, GolemSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -22,84 +22,84 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1, ) / 8.0; - next.upper_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one() / 8.0; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0); - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0); + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/golem/mod.rs b/voxygen/src/anim/src/golem/mod.rs index 5bd5861d1b..015c3d8750 100644 --- a/voxygen/src/anim/src/golem/mod.rs +++ b/voxygen/src/anim/src/golem/mod.rs @@ -5,86 +5,55 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct GolemSkeleton { - head: Bone, - upper_torso: Bone, - shoulder_l: Bone, - shoulder_r: Bone, - hand_l: Bone, - hand_r: Bone, - leg_l: Bone, - leg_r: Bone, - foot_l: Bone, - foot_r: Bone, - torso: Bone, -} - -impl GolemSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct GolemSkeleton { + + head, + + upper_torso, + + shoulder_l, + + shoulder_r, + + hand_l, + + hand_r, + + leg_l, + + leg_r, + + foot_l, + + foot_r, + torso, +}); impl Skeleton for GolemSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 10; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"golem_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "golem_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let upper_torso_mat = self.upper_torso.compute_base_matrix(); - let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); - let shoulder_r_mat = self.shoulder_r.compute_base_matrix(); - let leg_l_mat = self.leg_l.compute_base_matrix(); - let leg_r_mat = self.leg_r.compute_base_matrix(); - let torso_mat = self.torso.compute_base_matrix(); - let foot_l_mat = self.foot_l.compute_base_matrix(); - let foot_r_mat = self.foot_r.compute_base_matrix(); - ( - [ - FigureBoneData::new(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new(torso_mat * upper_torso_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_r_mat), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.hand_l.compute_base_matrix(), - ), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.hand_r.compute_base_matrix(), - ), - FigureBoneData::new(foot_l_mat * leg_l_mat), - FigureBoneData::new(foot_r_mat * leg_r_mat), - FigureBoneData::new(foot_l_mat), - FigureBoneData::new(foot_r_mat), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); + let foot_l_mat = base_mat * Mat4::::from(self.foot_l); + let foot_r_mat = base_mat * Mat4::::from(self.foot_r); + let upper_torso_mat = torso_mat * Mat4::::from(self.upper_torso); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.upper_torso.interpolate(&target.upper_torso, dt); - self.shoulder_l.interpolate(&target.shoulder_l, dt); - self.shoulder_r.interpolate(&target.shoulder_r, dt); - self.hand_l.interpolate(&target.hand_l, dt); - self.hand_r.interpolate(&target.hand_r, dt); - self.leg_l.interpolate(&target.leg_l, dt); - self.leg_r.interpolate(&target.leg_r, dt); - self.foot_l.interpolate(&target.foot_l, dt); - self.foot_r.interpolate(&target.foot_r, dt); - self.torso.interpolate(&target.torso, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(upper_torso_mat * Mat4::::from(self.head)), + make_bone(upper_torso_mat), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_l)), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_r)), + make_bone(upper_torso_mat * Mat4::::from(self.hand_l)), + make_bone(upper_torso_mat * Mat4::::from(self.hand_r)), + make_bone(foot_l_mat * Mat4::::from(self.leg_l)), + make_bone(foot_r_mat * Mat4::::from(self.leg_r)), + make_bone(foot_l_mat), + make_bone(foot_r_mat), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/golem/run.rs b/voxygen/src/anim/src/golem/run.rs index c5d1c0f6ec..63761bb1a2 100644 --- a/voxygen/src/anim/src/golem/run.rs +++ b/voxygen/src/anim/src/golem/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, GolemSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + GolemSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -42,89 +44,91 @@ impl Animation for RunAnimation { .sqrt()) * ((anim_time as f32 * lab as f32 + PI * 0.4).sin()); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(belt * -0.3) * Quaternion::rotation_x(0.3); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = Quaternion::rotation_z(belt * -0.3) * Quaternion::rotation_x(0.3); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + belt * 1.0, ) / 8.0; - next.upper_torso.ori = Quaternion::rotation_z(belt * 0.40) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = + Quaternion::rotation_z(belt * 0.40) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one() / 8.0; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotl * -0.15); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotr * -0.15); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_l.ori = + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.3 + footrotl * -0.8); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_r.ori = + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.3 + footrotr * -0.8); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * 8.0 + 3.0, skeleton_attr.foot.2 + footvertl * 4.0, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_x(footrotl * 0.7); + next.foot_l.orientation = Quaternion::rotation_x(footrotl * 0.7); next.foot_l.scale = Vec3::one() / 8.0 * 0.98; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * 8.0 + 3.0, skeleton_attr.foot.2 + footvertr * 4.0, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotr * 0.7); + next.foot_r.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotr * 0.7); next.foot_r.scale = Vec3::one() / 8.0 * 0.98; - next.torso.offset = Vec3::new(0.0, 0.0, belt * 0.15); - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, belt * 0.15); + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/lib.rs b/voxygen/src/anim/src/lib.rs index 8fa5542505..e12290e9e9 100644 --- a/voxygen/src/anim/src/lib.rs +++ b/voxygen/src/anim/src/lib.rs @@ -1,7 +1,44 @@ +#![feature(const_generics)] #![feature(or_patterns)] +#![allow(incomplete_features)] #[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); +macro_rules! skeleton_impls { + { struct $Skeleton:ident { $( $(+)? $bone:ident ),* $(,)? } } => { + #[derive(Clone, Default)] + pub struct $Skeleton { + $( + $bone: Bone, + )* + } + + impl<'a, Factor> Lerp for &'a $Skeleton + where + Factor: Copy, + Bone: Lerp + { + type Output = $Skeleton; + + fn lerp_unclamped_precise(from: Self, to: Self, factor: Factor) -> Self::Output { + Self::Output { + $( + $bone: Lerp::lerp_unclamped_precise(from.$bone, to.$bone, factor), + )* + } + } + + fn lerp_unclamped(from: Self, to: Self, factor: Factor) -> Self::Output { + Self::Output { + $( + $bone: Lerp::lerp_unclamped(from.$bone, to.$bone, factor), + )* + } + } + } + } +} + pub mod biped_large; pub mod bird_medium; pub mod bird_small; @@ -17,94 +54,73 @@ pub mod object; pub mod quadruped_low; pub mod quadruped_medium; pub mod quadruped_small; +pub mod vek; #[cfg(feature = "use-dyn-lib")] pub use dyn_lib::init; #[cfg(feature = "use-dyn-lib")] use std::ffi::CStr; -use vek::*; -// TODO: replace with inner type everywhere -pub struct FigureBoneData(pub Mat4); -impl FigureBoneData { - pub fn new(mat: Mat4) -> Self { Self(mat) } +use self::vek::*; - pub fn default() -> Self { Self(Mat4::identity()) } +type MatRaw = [[f32; 4]; 4]; + +pub type FigureBoneData = (MatRaw, MatRaw); + +pub const MAX_BONE_COUNT: usize = 16; + +fn make_bone(mat: Mat4) -> FigureBoneData { + let normal = mat.map_cols(Vec4::normalized); + (mat.into_col_arrays(), normal.into_col_arrays()) } -#[derive(Copy, Clone, Debug)] -pub struct Bone { - pub offset: Vec3, - pub ori: Quaternion, - pub scale: Vec3, -} - -impl Default for Bone { - fn default() -> Self { - Self { - offset: Vec3::zero(), - ori: Quaternion::identity(), - scale: Vec3::broadcast(1.0 / 11.0), - } - } -} - -impl Bone { - pub fn compute_base_matrix(&self) -> Mat4 { - Mat4::::translation_3d(self.offset) - * Mat4::scaling_3d(self.scale) - * Mat4::from(self.ori) - } - - /// Change the current bone to be more like `target`. - fn interpolate(&mut self, target: &Bone, dt: f32) { - // TODO: Make configurable. - let factor = (15.0 * dt).min(1.0); - self.offset += (target.offset - self.offset) * factor; - self.ori = vek::Slerp::slerp(self.ori, target.ori, factor); - self.scale += (target.scale - self.scale) * factor; - } -} +pub type Bone = Transform; pub trait Skeleton: Send + Sync + 'static { type Attr; + const BONE_COUNT: usize; + #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8]; - fn bone_count(&self) -> usize { 16 } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; MAX_BONE_COUNT], + ) -> Vec3; +} - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3); - - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { - #[cfg(not(feature = "use-dyn-lib"))] - { - Self::compute_matrices_inner(self) - } - #[cfg(feature = "use-dyn-lib")] - { - let lock = dyn_lib::LIB.lock().unwrap(); - let lib = &lock.as_ref().unwrap().lib; - - let compute_fn: libloading::Symbol ([FigureBoneData; 16], Vec3)> = - unsafe { lib.get(Self::COMPUTE_FN) }.unwrap_or_else(|e| { - panic!( - "Trying to use: {} but had error: {:?}", - CStr::from_bytes_with_nul(Self::COMPUTE_FN) - .map(CStr::to_str) - .unwrap() - .unwrap(), - e - ) - }); - - compute_fn(self) - } +pub fn compute_matrices( + skeleton: &S, + base_mat: Mat4, + buf: &mut [FigureBoneData; MAX_BONE_COUNT], +) -> Vec3 { + #[cfg(not(feature = "use-dyn-lib"))] + { + S::compute_matrices_inner(skeleton, base_mat, buf) } + #[cfg(feature = "use-dyn-lib")] + { + let lock = dyn_lib::LIB.lock().unwrap(); + let lib = &lock.as_ref().unwrap().lib; - /// Change the current skeleton to be more like `target`. - fn interpolate(&mut self, target: &Self, dt: f32); + let compute_fn: libloading::Symbol< + fn(&S, Mat4, &mut [FigureBoneData; MAX_BONE_COUNT]) -> Vec3, + > = unsafe { lib.get(S::COMPUTE_FN) }.unwrap_or_else(|e| { + panic!( + "Trying to use: {} but had error: {:?}", + CStr::from_bytes_with_nul(S::COMPUTE_FN) + .map(CStr::to_str) + .unwrap() + .unwrap(), + e + ) + }); + + compute_fn(skeleton, base_mat, buf) + } } pub trait Animation { diff --git a/voxygen/src/anim/src/object/mod.rs b/voxygen/src/anim/src/object/mod.rs index 398e6d80be..d0613aa59c 100644 --- a/voxygen/src/anim/src/object/mod.rs +++ b/voxygen/src/anim/src/object/mod.rs @@ -1,49 +1,36 @@ -use super::{FigureBoneData, Skeleton}; -use vek::*; +use super::{make_bone, vek::*, FigureBoneData, Skeleton}; -#[derive(Clone)] +#[derive(Clone, Default)] pub struct ObjectSkeleton; -pub struct SkeletonAttr; -impl ObjectSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { Self {} } +impl<'a, Factor> Lerp for &'a ObjectSkeleton { + type Output = ObjectSkeleton; + + fn lerp_unclamped_precise(_from: Self, _to: Self, _factor: Factor) -> Self::Output { + ObjectSkeleton + } + + fn lerp_unclamped(_from: Self, _to: Self, _factor: Factor) -> Self::Output { ObjectSkeleton } } +pub struct SkeletonAttr; + const SCALE: f32 = 1.0 / 11.0; impl Skeleton for ObjectSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 1; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"object_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "object_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - ( - [ - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - ], - Vec3::default(), - ) + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + buf[0] = make_bone(base_mat * Mat4::scaling_3d(SCALE)); + Vec3::default() } - - fn interpolate(&mut self, _target: &Self, _dt: f32) {} } diff --git a/voxygen/src/anim/src/quadruped_low/alpha.rs b/voxygen/src/anim/src/quadruped_low/alpha.rs index 0e60916168..87eedd579b 100644 --- a/voxygen/src/anim/src/quadruped_low/alpha.rs +++ b/voxygen/src/anim/src/quadruped_low/alpha.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -29,71 +31,72 @@ impl Animation for AlphaAnimation { / (0.001 + 0.9999 * ((anim_time as f32 * 7.0 + PI * 0.0).sin()).powf(2.0 as f32))) .sqrt()) * ((anim_time as f32 * 7.0 + PI * 0.0).sin()); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_z(short * 0.3) * Quaternion::rotation_x(0.0); + next.head_upper.orientation = + Quaternion::rotation_z(short * 0.3) * Quaternion::rotation_x(0.0); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = + next.head_lower.orientation = Quaternion::rotation_z(short * 0.2) * Quaternion::rotation_y(short * -0.4); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.2 + quick * 0.3); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.2 + quick * 0.3); next.jaw.scale = Vec3::one() * 0.98; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(short * -0.07); + next.chest.orientation = Quaternion::rotation_y(short * -0.07); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_x(0.15) + next.tail_front.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_y(short * 0.2) * Quaternion::rotation_z(short * 0.3); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_y(short * 0.5) + next.tail_rear.orientation = Quaternion::rotation_y(short * 0.5) * Quaternion::rotation_x(-0.12) * Quaternion::rotation_z(short * 0.3); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_y(short * 0.12); + next.foot_fl.orientation = Quaternion::rotation_y(short * 0.12); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_y(short * 0.12); + next.foot_fr.orientation = Quaternion::rotation_y(short * 0.12); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_y(short * 0.12); + next.foot_bl.orientation = Quaternion::rotation_y(short * 0.12); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_y(short * 0.12); + next.foot_br.orientation = Quaternion::rotation_y(short * 0.12); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_low/idle.rs b/voxygen/src/anim/src/quadruped_low/idle.rs index 1a7ef52347..091f129c42 100644 --- a/voxygen/src/anim/src/quadruped_low/idle.rs +++ b/voxygen/src/anim/src/quadruped_low/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,74 +40,76 @@ impl Animation for IdleAnimation { * 0.1, ); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + slower * 0.20, ); - next.head_upper.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_upper.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(0.8 * dragon_look.y); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + slower * 0.20, ); - next.head_lower.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_lower.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(0.8 * dragon_look.y); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(slow * 0.04); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(slow * 0.04); next.jaw.scale = Vec3::one() * 0.98; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(slow * 0.03); + next.chest.orientation = Quaternion::rotation_y(slow * 0.03); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(slowalt * 0.12); + next.tail_front.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(slowalt * 0.12); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(-0.12); + next.tail_rear.orientation = + Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(-0.12); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_fl.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_fr.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_bl.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_br.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_low/jump.rs b/voxygen/src/anim/src/quadruped_low/jump.rs index ba851f9a52..6e1266cf8f 100644 --- a/voxygen/src/anim/src/quadruped_low/jump.rs +++ b/voxygen/src/anim/src/quadruped_low/jump.rs @@ -1,5 +1,7 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; -use vek::*; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; pub struct JumpAnimation; @@ -20,66 +22,66 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.0); + next.head_upper.orientation = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.0); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(0.2); + next.head_lower.orientation = Quaternion::rotation_z(0.2); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.3); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.3); next.jaw.scale = Vec3::one() * 0.98; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(0.0); + next.chest.orientation = Quaternion::rotation_y(0.0); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(-0.2); + next.tail_front.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(-0.2); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_z(-0.4) * Quaternion::rotation_x(-0.12); + next.tail_rear.orientation = Quaternion::rotation_z(-0.4) * Quaternion::rotation_x(-0.12); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_z(0.3); + next.foot_fl.orientation = Quaternion::rotation_z(0.3); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_z(0.3); + next.foot_fr.orientation = Quaternion::rotation_z(0.3); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_y(0.0); + next.foot_bl.orientation = Quaternion::rotation_y(0.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_y(0.0); + next.foot_br.orientation = Quaternion::rotation_y(0.0); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_low/mod.rs b/voxygen/src/anim/src/quadruped_low/mod.rs index cc621e6667..d36eba621b 100644 --- a/voxygen/src/anim/src/quadruped_low/mod.rs +++ b/voxygen/src/anim/src/quadruped_low/mod.rs @@ -8,81 +8,54 @@ pub use self::{ alpha::AlphaAnimation, idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct QuadrupedLowSkeleton { - head_upper: Bone, - head_lower: Bone, - jaw: Bone, - chest: Bone, - tail_front: Bone, - tail_rear: Bone, - foot_fl: Bone, - foot_fr: Bone, - foot_bl: Bone, - foot_br: Bone, -} - -impl QuadrupedLowSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct QuadrupedLowSkeleton { + + head_upper, + + head_lower, + + jaw, + + chest, + + tail_front, + + tail_rear, + + foot_fl, + + foot_fr, + + foot_bl, + + foot_br, +}); impl Skeleton for QuadrupedLowSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 10; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"quadruped_low_compute_mats\0"; - fn bone_count(&self) -> usize { 10 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_low_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let head_upper_mat = self.head_upper.compute_base_matrix(); - let head_lower_mat = self.head_lower.compute_base_matrix(); - let chest_mat = self.chest.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_mat * head_lower_mat * head_upper_mat), - FigureBoneData::new(chest_mat * head_lower_mat), - FigureBoneData::new( - chest_mat * head_lower_mat * head_upper_mat * self.jaw.compute_base_matrix(), - ), - FigureBoneData::new(chest_mat), - FigureBoneData::new(chest_mat * self.tail_front.compute_base_matrix()), - FigureBoneData::new( - chest_mat - * self.tail_front.compute_base_matrix() - * self.tail_rear.compute_base_matrix(), - ), - FigureBoneData::new(chest_mat * self.foot_fl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.foot_fr.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.foot_bl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.foot_br.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); + let tail_front = chest_mat * Mat4::::from(self.tail_front); + let head_lower_mat = chest_mat * Mat4::::from(self.head_lower); + let head_upper_mat = head_lower_mat * Mat4::::from(self.head_upper); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head_upper.interpolate(&target.head_upper, dt); - self.head_lower.interpolate(&target.head_lower, dt); - self.jaw.interpolate(&target.jaw, dt); - self.chest.interpolate(&target.chest, dt); - self.tail_front.interpolate(&target.tail_front, dt); - self.tail_rear.interpolate(&target.tail_rear, dt); - self.foot_fl.interpolate(&target.foot_fl, dt); - self.foot_fr.interpolate(&target.foot_fr, dt); - self.foot_bl.interpolate(&target.foot_bl, dt); - self.foot_br.interpolate(&target.foot_br, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_upper_mat), + make_bone(head_lower_mat), + make_bone(head_upper_mat * Mat4::::from(self.jaw)), + make_bone(chest_mat), + make_bone(tail_front), + make_bone(tail_front * Mat4::::from(self.tail_rear)), + make_bone(chest_mat * Mat4::::from(self.foot_fl)), + make_bone(chest_mat * Mat4::::from(self.foot_fr)), + make_bone(chest_mat * Mat4::::from(self.foot_bl)), + make_bone(chest_mat * Mat4::::from(self.foot_br)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/quadruped_low/run.rs b/voxygen/src/anim/src/quadruped_low/run.rs index a71979ee24..54c876e084 100644 --- a/voxygen/src/anim/src/quadruped_low/run.rs +++ b/voxygen/src/anim/src/quadruped_low/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -72,7 +74,7 @@ impl Animation for RunAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -83,94 +85,96 @@ impl Animation for RunAnimation { } else { 0.0 } * 1.3; - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(-skeleton_attr.lean.0 + x_tilt * -1.0) + next.head_upper.orientation = Quaternion::rotation_x(-skeleton_attr.lean.0 + x_tilt * -1.0) * Quaternion::rotation_y(tilt * 0.3) * Quaternion::rotation_z(short * -0.06 + tilt * -1.5); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_y(tilt * 1.0) + next.head_lower.orientation = Quaternion::rotation_y(tilt * 1.0) * Quaternion::rotation_z(short * -0.15 + tilt * -0.8) * Quaternion::rotation_x(x_tilt * 0.4); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(0.0); next.jaw.scale = Vec3::one() * 0.98; - next.tail_front.offset = Vec3::new( + next.tail_front.position = Vec3::new( 0.0, skeleton_attr.tail_front.0 + skeleton_attr.lean.0 * 2.0, skeleton_attr.tail_front.1 + skeleton_attr.lean.0 * 2.0, ); - next.tail_front.ori = + next.tail_front.orientation = Quaternion::rotation_z(shortalt * 0.18 * skeleton_attr.lean.1 + tilt * 1.8) * Quaternion::rotation_y(shortalt * 0.1) * Quaternion::rotation_x(0.06 - skeleton_attr.lean.0 * 1.2 + x_tilt * 0.2); next.tail_front.scale = Vec3::one(); - next.tail_rear.offset = Vec3::new( + next.tail_rear.position = Vec3::new( 0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1 + shortalt * 0.6, ); - next.tail_rear.ori = + next.tail_rear.orientation = Quaternion::rotation_z(shortalt * 0.25 * skeleton_attr.lean.1 + tilt * 1.6) * Quaternion::rotation_y(shortalt * 0.1) * Quaternion::rotation_x(-0.04 + x_tilt * 0.5); next.tail_rear.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_z(short * 0.13 + tilt * -1.9) + next.chest.orientation = Quaternion::rotation_z(short * 0.13 + tilt * -1.9) * Quaternion::rotation_y(short * 0.12 + tilt * 0.7) * Quaternion::rotation_x(x_tilt + skeleton_attr.lean.0); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + foothoril * -2.0, skeleton_attr.feet_f.2 + 1.0 + ((footvertl * -1.8).max(-0.0)), ); - next.foot_fl.ori = Quaternion::rotation_x( + next.foot_fl.orientation = Quaternion::rotation_x( -0.2 + footvertl * -0.45 * skeleton_attr.lean.1 - skeleton_attr.lean.0, ) * Quaternion::rotation_y(tilt * -1.0) * Quaternion::rotation_z(foothoril * 0.4 * skeleton_attr.lean.1 + tilt * -2.0); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + foothorir * -2.0, skeleton_attr.feet_f.2 + 1.0 + ((footvertr * -1.8).max(-0.0)), ); - next.foot_fr.ori = Quaternion::rotation_x( + next.foot_fr.orientation = Quaternion::rotation_x( -0.2 + footvertr * -0.45 * skeleton_attr.lean.1 - skeleton_attr.lean.0, ) * Quaternion::rotation_y(tilt * -1.0) * Quaternion::rotation_z(foothorir * -0.4 * skeleton_attr.lean.1 + tilt * -2.0); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + foothorilb * -1.0, skeleton_attr.feet_b.2 + ((footvertlb * -1.2).max(-0.0)), ); - next.foot_bl.ori = Quaternion::rotation_x(-0.2 + footvertlb * -0.5 - skeleton_attr.lean.0) - * Quaternion::rotation_y(tilt * -1.0) - * Quaternion::rotation_z(foothorilb * 0.4 + tilt * -2.0); + next.foot_bl.orientation = + Quaternion::rotation_x(-0.2 + footvertlb * -0.5 - skeleton_attr.lean.0) + * Quaternion::rotation_y(tilt * -1.0) + * Quaternion::rotation_z(foothorilb * 0.4 + tilt * -2.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + foothorirb * -1.0, skeleton_attr.feet_b.2 + ((footvertrb * -1.2).max(-0.0)), ); - next.foot_br.ori = Quaternion::rotation_x(-0.2 + footvertrb * -0.5 - skeleton_attr.lean.0) - * Quaternion::rotation_y(tilt * -1.0) - * Quaternion::rotation_z(foothorirb * -0.4 + tilt * -2.0); + next.foot_br.orientation = + Quaternion::rotation_x(-0.2 + footvertrb * -0.5 - skeleton_attr.lean.0) + * Quaternion::rotation_y(tilt * -1.0) + * Quaternion::rotation_z(foothorirb * -0.4 + tilt * -2.0); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_medium/alpha.rs b/voxygen/src/anim/src/quadruped_medium/alpha.rs index 80df941140..ae84f5f8ce 100644 --- a/voxygen/src/anim/src/quadruped_medium/alpha.rs +++ b/voxygen/src/anim/src/quadruped_medium/alpha.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -30,114 +32,114 @@ impl Animation for AlphaAnimation { .sqrt()) * ((anim_time as f32 * 4.0 + PI * 0.5).sin()); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = + next.head_upper.orientation = Quaternion::rotation_y(short * -0.2) * Quaternion::rotation_x(0.1 + short * 0.2); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head_lower.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.3 + quick * 0.4); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.3 + quick * 0.4); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0 + short * 2.8, skeleton_attr.torso_front.1 + short * 1.0, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_y(short * -0.1); + next.torso_front.orientation = Quaternion::rotation_y(short * -0.1); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = + next.torso_back.position = Vec3::new(0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1); - next.torso_back.ori = Quaternion::rotation_y(short * -0.1) + next.torso_back.orientation = Quaternion::rotation_y(short * -0.1) * Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(0.0); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(0.0); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fl.ori = + next.leg_fl.orientation = Quaternion::rotation_x(short * -0.1) * Quaternion::rotation_y(short * 0.15); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fr.ori = + next.leg_fr.orientation = Quaternion::rotation_x(short * 0.3) * Quaternion::rotation_y(short * -0.2); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + 1.0, ); - next.leg_bl.ori = + next.leg_bl.orientation = Quaternion::rotation_x(-0.1 + short * -0.2) * Quaternion::rotation_y(short * 0.2); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + 1.0, ); - next.leg_br.ori = + next.leg_br.orientation = Quaternion::rotation_x(-0.1 + short * -0.2) * Quaternion::rotation_y(0.1 + short * 0.2); next.leg_br.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + short * -0.2, ); - next.foot_fl.ori = Quaternion::rotation_x(short * -0.05); + next.foot_fl.orientation = Quaternion::rotation_x(short * -0.05); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = + next.foot_fr.orientation = Quaternion::rotation_x(short * -0.4) * Quaternion::rotation_y(short * 0.15); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + short * -0.8, ); - next.foot_bl.ori = + next.foot_bl.orientation = Quaternion::rotation_x(-0.2 + short * 0.2) * Quaternion::rotation_y(short * 0.15); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = + next.foot_br.orientation = Quaternion::rotation_x(-0.2 + short * 0.2) * Quaternion::rotation_y(short * 0.15); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_medium/idle.rs b/voxygen/src/anim/src/quadruped_medium/idle.rs index b41ebcc636..193bb3aa6e 100644 --- a/voxygen/src/anim/src/quadruped_medium/idle.rs +++ b/voxygen/src/anim/src/quadruped_medium/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -49,121 +51,121 @@ impl Animation for IdleAnimation { * 0.125, ); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + slower * 0.2, ); - next.head_upper.ori = + next.head_upper.orientation = Quaternion::rotation_z(0.3 * look.x) * Quaternion::rotation_x(0.3 * look.y); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + slower * 0.1, ); - next.head_lower.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head_lower.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new( + next.jaw.position = Vec3::new( 0.0, skeleton_attr.jaw.0 - slower * 0.12, skeleton_attr.jaw.1 + slow * 0.2, ); - next.jaw.ori = Quaternion::rotation_x(slow * 0.05); + next.jaw.orientation = Quaternion::rotation_x(slow * 0.05); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0 + slow * 0.2 + tailmove.x) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1 + slower * 0.3, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_y(slow * 0.02); + next.torso_front.orientation = Quaternion::rotation_y(slow * 0.02); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = Vec3::new( + next.torso_back.position = Vec3::new( 0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1 + slower * 0.2, ); - next.torso_back.ori = Quaternion::rotation_y(-slow * 0.005) + next.torso_back.orientation = Quaternion::rotation_y(-slow * 0.005) * Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso_back.scale = Vec3::one() * 0.99; - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(0.0 + slower * 0.03); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(0.0 + slower * 0.03); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2 + slow * -0.15 + slower * -0.15, ); - next.leg_fl.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_fl.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2 + slow * 0.15 + slower * -0.15, ); - next.leg_fr.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_fr.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + slower * -0.3, ); - next.leg_bl.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_bl.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + slower * -0.3, ); - next.leg_br.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_br.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_br.scale = Vec3::one() * 1.02; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slower * -0.2, ); - next.foot_fl.ori = Quaternion::rotation_x(0.0); + next.foot_fl.orientation = Quaternion::rotation_x(0.0); next.foot_fl.scale = Vec3::one() * 0.94; - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slower * -0.2, ); - next.foot_fr.ori = Quaternion::rotation_x(0.0); + next.foot_fr.orientation = Quaternion::rotation_x(0.0); next.foot_fr.scale = Vec3::one() * 0.94; - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slower * -0.2, ); - next.foot_bl.ori = Quaternion::rotation_x(0.0); + next.foot_bl.orientation = Quaternion::rotation_x(0.0); next.foot_bl.scale = Vec3::one() * 0.94; - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slower * -0.2, ); - next.foot_br.ori = Quaternion::rotation_x(0.0); + next.foot_br.orientation = Quaternion::rotation_x(0.0); next.foot_br.scale = Vec3::one() * 0.94; next diff --git a/voxygen/src/anim/src/quadruped_medium/jump.rs b/voxygen/src/anim/src/quadruped_medium/jump.rs index eb8647aecc..5d70d11b3c 100644 --- a/voxygen/src/anim/src/quadruped_medium/jump.rs +++ b/voxygen/src/anim/src/quadruped_medium/jump.rs @@ -1,5 +1,7 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; -use vek::*; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; pub struct JumpAnimation; @@ -20,106 +22,106 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.3); + next.head_upper.orientation = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.3); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(0.2) * Quaternion::rotation_x(0.3); + next.head_lower.orientation = Quaternion::rotation_z(0.2) * Quaternion::rotation_x(0.3); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.4); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.4); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.3); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.3); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_y(0.0); + next.torso_front.orientation = Quaternion::rotation_y(0.0); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = + next.torso_back.position = Vec3::new(0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1); - next.torso_back.ori = Quaternion::rotation_z(-0.3) + next.torso_back.orientation = Quaternion::rotation_z(-0.3) * Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(0.6); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(0.6); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fl.ori = Quaternion::rotation_x(-0.4); + next.leg_fl.orientation = Quaternion::rotation_x(-0.4); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fr.ori = Quaternion::rotation_x(0.4); + next.leg_fr.orientation = Quaternion::rotation_x(0.4); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2, ); - next.leg_bl.ori = Quaternion::rotation_y(0.0); + next.leg_bl.orientation = Quaternion::rotation_y(0.0); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2, ); - next.leg_br.ori = Quaternion::rotation_y(0.0); + next.leg_br.orientation = Quaternion::rotation_y(0.0); next.leg_br.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_x(-0.3); + next.foot_fl.orientation = Quaternion::rotation_x(-0.3); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_x(0.2); + next.foot_fr.orientation = Quaternion::rotation_x(0.2); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_x(0.0); + next.foot_bl.orientation = Quaternion::rotation_x(0.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_x(0.0); + next.foot_br.orientation = Quaternion::rotation_x(0.0); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_medium/mod.rs b/voxygen/src/anim/src/quadruped_medium/mod.rs index 4a1fefe2bf..08e5db484b 100644 --- a/voxygen/src/anim/src/quadruped_medium/mod.rs +++ b/voxygen/src/anim/src/quadruped_medium/mod.rs @@ -8,113 +8,68 @@ pub use self::{ alpha::AlphaAnimation, idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct QuadrupedMediumSkeleton { - head_upper: Bone, - head_lower: Bone, - jaw: Bone, - tail: Bone, - torso_front: Bone, - torso_back: Bone, - ears: Bone, - leg_fl: Bone, - leg_fr: Bone, - leg_bl: Bone, - leg_br: Bone, - foot_fl: Bone, - foot_fr: Bone, - foot_bl: Bone, - foot_br: Bone, -} - -impl QuadrupedMediumSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct QuadrupedMediumSkeleton { + + head_upper, + + head_lower, + + jaw, + + tail, + + torso_front, + + torso_back, + + ears, + + leg_fl, + + leg_fr, + + leg_bl, + + leg_br, + + foot_fl, + + foot_fr, + + foot_bl, + + foot_br, +}); impl Skeleton for QuadrupedMediumSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 15; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"quadruped_medium_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_medium_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let ears_mat = self.ears.compute_base_matrix(); - let head_upper_mat = self.head_upper.compute_base_matrix(); - let head_lower_mat = self.head_lower.compute_base_matrix(); - let torso_front_mat = self.torso_front.compute_base_matrix(); - let torso_back_mat = self.torso_back.compute_base_matrix(); - let leg_fl_mat = self.leg_fl.compute_base_matrix(); - let leg_fr_mat = self.leg_fr.compute_base_matrix(); - let leg_bl_mat = self.leg_bl.compute_base_matrix(); - let leg_br_mat = self.leg_br.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_front_mat = base_mat * Mat4::::from(self.torso_front); + let torso_back_mat = torso_front_mat * Mat4::::from(self.torso_back); + let head_lower_mat = torso_front_mat * Mat4::::from(self.head_lower); + let leg_fl_mat = torso_front_mat * Mat4::::from(self.leg_fl); + let leg_fr_mat = torso_front_mat * Mat4::::from(self.leg_fr); + let leg_bl_mat = torso_back_mat * Mat4::::from(self.leg_bl); + let leg_br_mat = torso_back_mat * Mat4::::from(self.leg_br); + let head_upper_mat = head_lower_mat * Mat4::::from(self.head_upper); - ( - [ - FigureBoneData::new(torso_front_mat * head_lower_mat * head_upper_mat), - FigureBoneData::new(torso_front_mat * head_lower_mat), - FigureBoneData::new( - torso_front_mat - * head_lower_mat - * head_upper_mat - * self.jaw.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat * torso_back_mat * self.tail.compute_base_matrix(), - ), - FigureBoneData::new(torso_front_mat), - FigureBoneData::new(torso_front_mat * torso_back_mat), - FigureBoneData::new(torso_front_mat * head_lower_mat * head_upper_mat * ears_mat), - FigureBoneData::new(torso_front_mat * leg_fl_mat), - FigureBoneData::new(torso_front_mat * leg_fr_mat), - FigureBoneData::new(torso_front_mat * torso_back_mat * leg_bl_mat), - FigureBoneData::new(torso_front_mat * torso_back_mat * leg_br_mat), - FigureBoneData::new( - torso_front_mat * leg_fl_mat * self.foot_fl.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat * leg_fr_mat * self.foot_fr.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat - * torso_back_mat - * leg_bl_mat - * self.foot_bl.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat - * torso_back_mat - * leg_br_mat - * self.foot_br.compute_base_matrix(), - ), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head_upper.interpolate(&target.head_upper, dt); - self.head_lower.interpolate(&target.head_lower, dt); - self.jaw.interpolate(&target.jaw, dt); - self.tail.interpolate(&target.tail, dt); - self.torso_back.interpolate(&target.torso_back, dt); - self.torso_front.interpolate(&target.torso_front, dt); - self.ears.interpolate(&target.ears, dt); - self.leg_fl.interpolate(&target.leg_fl, dt); - self.leg_fr.interpolate(&target.leg_fr, dt); - self.leg_bl.interpolate(&target.leg_bl, dt); - self.leg_br.interpolate(&target.leg_br, dt); - self.foot_fl.interpolate(&target.foot_fl, dt); - self.foot_fr.interpolate(&target.foot_fr, dt); - self.foot_bl.interpolate(&target.foot_bl, dt); - self.foot_br.interpolate(&target.foot_br, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_upper_mat), + make_bone(head_lower_mat), + make_bone(head_upper_mat * Mat4::::from(self.jaw)), + make_bone(torso_back_mat * Mat4::::from(self.tail)), + make_bone(torso_front_mat), + make_bone(torso_back_mat), + make_bone(head_upper_mat * Mat4::::from(self.ears)), + make_bone(leg_fl_mat), + make_bone(leg_fr_mat), + make_bone(leg_bl_mat), + make_bone(leg_br_mat), + make_bone(leg_fl_mat * Mat4::::from(self.foot_fl)), + make_bone(leg_fr_mat * Mat4::::from(self.foot_fr)), + make_bone(leg_bl_mat * Mat4::::from(self.foot_bl)), + make_bone(leg_br_mat * Mat4::::from(self.foot_br)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/quadruped_medium/run.rs b/voxygen/src/anim/src/quadruped_medium/run.rs index 5dadddf37d..8e12e0ee01 100644 --- a/voxygen/src/anim/src/quadruped_medium/run.rs +++ b/voxygen/src/anim/src/quadruped_medium/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -59,7 +61,7 @@ impl Animation for RunAnimation { // let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -73,247 +75,248 @@ impl Animation for RunAnimation { let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); if speed < 8.0 { //Trot - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(short * -0.03 - 0.1 + x_tilt * -0.5) - * Quaternion::rotation_z(tilt * -1.2); + next.head_upper.orientation = + Quaternion::rotation_x(short * -0.03 - 0.1 + x_tilt * -0.5) + * Quaternion::rotation_z(tilt * -1.2); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(tilt * -0.8) + next.head_lower.orientation = Quaternion::rotation_z(tilt * -0.8) * Quaternion::rotation_x(short * -0.05 + x_tilt * -0.5); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(0.0); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(shortalt * 0.3) * Quaternion::rotation_z(tilt * 1.5); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1 + shortalt * 1.0 + x_tilt, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_x(short * 0.03 + x_tilt) + next.torso_front.orientation = Quaternion::rotation_x(short * 0.03 + x_tilt) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(tilt * -1.5); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = Vec3::new( + next.torso_back.position = Vec3::new( 0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1 + shortalt * 0.04 - 0.2, ); - next.torso_back.ori = + next.torso_back.orientation = Quaternion::rotation_x(short * 0.06) * Quaternion::rotation_z(tilt * 1.8); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(shortalt * 0.04 + 0.2); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(shortalt * 0.04 + 0.2); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertaltfslow * -1.4, skeleton_attr.leg_f.2 + 1.0 + footverttaltfslow * -0.3, ); - next.leg_fl.ori = Quaternion::rotation_x(footverttaltfslow * -0.35) + next.leg_fl.orientation = Quaternion::rotation_x(footverttaltfslow * -0.35) * Quaternion::rotation_z(tilt * -0.5); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertalt * -1.4, skeleton_attr.leg_f.2 + 1.0 + footverttalt * -0.3, ); - next.leg_fr.ori = + next.leg_fr.orientation = Quaternion::rotation_x(footverttalt * -0.35) * Quaternion::rotation_z(tilt * -0.5); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvertalt * -1.0, skeleton_attr.leg_b.2 + 1.0 + footverttalt * -0.3, ); - next.leg_bl.ori = + next.leg_bl.orientation = Quaternion::rotation_x(footverttalt * -0.2) * Quaternion::rotation_z(tilt * -1.5); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvertaltfslow * -1.0, skeleton_attr.leg_b.2 + 1.0 + footverttaltfslow * -0.3, ); - next.leg_br.ori = Quaternion::rotation_x(footverttaltfslow * -0.2) + next.leg_br.orientation = Quaternion::rotation_x(footverttaltfslow * -0.2) * Quaternion::rotation_z(tilt * -1.5); next.leg_br.scale = Vec3::one() * 1.02; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvertfslow * -1.0 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fl.ori = + next.foot_fl.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footverttfslow * 0.5); next.foot_fl.scale = Vec3::one() * 0.96; - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvert * -1.0 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fr.ori = + next.foot_fr.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footvertt * 0.5); next.foot_fr.scale = Vec3::one() * 0.96; - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvert * -1.8).max(0.0)), ); - next.foot_bl.ori = Quaternion::rotation_x(footvertt * 0.5 - 0.2); + next.foot_bl.orientation = Quaternion::rotation_x(footvertt * 0.5 - 0.2); next.foot_bl.scale = Vec3::one() * 0.96; - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvertfslow * -0.8).max(-0.0)), ); - next.foot_br.ori = Quaternion::rotation_x(footverttfslow * 0.5 - 0.2); + next.foot_br.orientation = Quaternion::rotation_x(footverttfslow * 0.5 - 0.2); next.foot_br.scale = Vec3::one() * 0.96; } else { let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); //Gallop - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(short * -0.03 - 0.1) + next.head_upper.orientation = Quaternion::rotation_x(short * -0.03 - 0.1) * Quaternion::rotation_z(tilt * -1.2) * Quaternion::rotation_y(tilt * 0.8); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(tilt * -0.8) + next.head_lower.orientation = Quaternion::rotation_z(tilt * -0.8) * Quaternion::rotation_x(short * -0.05) * Quaternion::rotation_y(tilt * 0.3); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(0.0); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(shortalt * 0.3) * Quaternion::rotation_z(tilt * 1.5); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1 + shortalt * 2.5 + x_tilt * 10.0, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_x(short * 0.13 + x_tilt) + next.torso_front.orientation = Quaternion::rotation_x(short * 0.13 + x_tilt) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_z(tilt * -1.5); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = Vec3::new( + next.torso_back.position = Vec3::new( 0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1 + shortalt * 0.2 - 0.2, ); - next.torso_back.ori = Quaternion::rotation_x(short * 0.1) + next.torso_back.orientation = Quaternion::rotation_x(short * 0.1) * Quaternion::rotation_z(tilt * 1.8) * Quaternion::rotation_y(tilt * 0.6); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(shortalt * 0.2 + 0.2); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(shortalt * 0.2 + 0.2); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertaltf * -1.3, skeleton_attr.leg_f.2 + 1.0 + footverttaltf * -1.9, ); - next.leg_fl.ori = Quaternion::rotation_x(footverttaltf * -0.65) + next.leg_fl.orientation = Quaternion::rotation_x(footverttaltf * -0.65) * Quaternion::rotation_z(tilt * -0.5) * Quaternion::rotation_y(tilt * 1.5); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertalt * -1.3, skeleton_attr.leg_f.2 + 1.0 + footverttalt * -1.9, ); - next.leg_fr.ori = Quaternion::rotation_x(footverttalt * -0.65) + next.leg_fr.orientation = Quaternion::rotation_x(footverttalt * -0.65) * Quaternion::rotation_z(tilt * -0.5) * Quaternion::rotation_y(tilt * 1.5); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvert * -1.7, skeleton_attr.leg_b.2 + 1.0 + footvertt * -1.5, ); - next.leg_bl.ori = Quaternion::rotation_x(footvertt * -0.45 - 0.2) + next.leg_bl.orientation = Quaternion::rotation_x(footvertt * -0.45 - 0.2) * Quaternion::rotation_y(tilt * 1.5) * Quaternion::rotation_z(tilt * -1.5); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvertf * -1.7, skeleton_attr.leg_b.2 + 1.0 + footverttf * -1.5, ); - next.leg_br.ori = Quaternion::rotation_x(footverttf * -0.45 - 0.2) + next.leg_br.orientation = Quaternion::rotation_x(footverttf * -0.45 - 0.2) * Quaternion::rotation_y(tilt * 1.5) * Quaternion::rotation_z(tilt * -1.5); next.leg_br.scale = Vec3::one() * 1.02; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvertf * -2.7 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fl.ori = + next.foot_fl.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footverttf * 0.9) * Quaternion::rotation_y(tilt * -1.0); next.foot_fl.scale = Vec3::one() * 0.96; - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvert * -2.7 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fr.ori = + next.foot_fr.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footvertt * 0.9) * Quaternion::rotation_y(tilt * -1.0); next.foot_fr.scale = Vec3::one() * 0.96; - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvert * 1.3).max(0.0)), ); - next.foot_bl.ori = Quaternion::rotation_x(footvertt * -0.9 - 0.2) + next.foot_bl.orientation = Quaternion::rotation_x(footvertt * -0.9 - 0.2) * Quaternion::rotation_y(tilt * -1.0); next.foot_bl.scale = Vec3::one() * 0.96; - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvertf * 1.3).max(-0.0)), ); - next.foot_br.ori = Quaternion::rotation_x(footverttf * -0.9 - 0.2) + next.foot_br.orientation = Quaternion::rotation_x(footverttf * -0.9 - 0.2) * Quaternion::rotation_y(tilt * -1.0); next.foot_br.scale = Vec3::one() * 0.96; } diff --git a/voxygen/src/anim/src/quadruped_small/feed.rs b/voxygen/src/anim/src/quadruped_small/feed.rs index aa4de97cb3..348eb5caf8 100644 --- a/voxygen/src/anim/src/quadruped_small/feed.rs +++ b/voxygen/src/anim/src/quadruped_small/feed.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct FeedAnimation; @@ -39,60 +41,62 @@ impl Animation for FeedAnimation { * 0.5, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0 + 1.5, skeleton_attr.head.1 + slow * 0.2, ); - next.head.ori = Quaternion::rotation_z(head_look.y) + next.head.orientation = Quaternion::rotation_z(head_look.y) * Quaternion::rotation_x(slow * 0.05 + quick * 0.08 - 0.4 * skeleton_attr.feed); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(slow * 0.02, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(slow * 0.02, skeleton_attr.chest.0, skeleton_attr.chest.1) / 11.0 * skeleton_attr.scaler; - next.chest.ori = Quaternion::rotation_x(-0.35 * skeleton_attr.feed) + next.chest.orientation = Quaternion::rotation_x(-0.35 * skeleton_attr.feed) * Quaternion::rotation_y(head_look.y * 0.1); next.chest.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + 0.5, ); - next.leg_fl.ori = Quaternion::rotation_x(slow * 0.01 + 0.25 * skeleton_attr.feed) + next.leg_fl.orientation = Quaternion::rotation_x(slow * 0.01 + 0.25 * skeleton_attr.feed) * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + 0.5, ); - next.leg_fr.ori = Quaternion::rotation_x(slow_alt * 0.01 + 0.25 * skeleton_attr.feed) - * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); + next.leg_fr.orientation = + Quaternion::rotation_x(slow_alt * 0.01 + 0.25 * skeleton_attr.feed) + * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + 1.0, skeleton_attr.feet_b.2 - 1.0, ); - next.leg_bl.ori = Quaternion::rotation_x(slow_alt * 0.01 + 0.15 * skeleton_attr.feed) - * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); + next.leg_bl.orientation = + Quaternion::rotation_x(slow_alt * 0.01 + 0.15 * skeleton_attr.feed) + * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + 1.0, skeleton_attr.feet_b.2 - 1.0, ); - next.leg_br.ori = Quaternion::rotation_x(slow * 0.01 + 0.15 * skeleton_attr.feed) + next.leg_br.orientation = Quaternion::rotation_x(slow * 0.01 + 0.15 * skeleton_attr.feed) * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_br.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(slow * 0.3 + head_look.y * 0.3); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(slow * 0.3 + head_look.y * 0.3); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_small/idle.rs b/voxygen/src/anim/src/quadruped_small/idle.rs index d32c14a128..9209704c2d 100644 --- a/voxygen/src/anim/src/quadruped_small/idle.rs +++ b/voxygen/src/anim/src/quadruped_small/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,55 +40,56 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.2); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.position = + Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.2); + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y + slow_alt * 0.03); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(slow * 0.05, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(slow * 0.05, skeleton_attr.chest.0, skeleton_attr.chest.1) / 11.0 * skeleton_attr.scaler; - next.chest.ori = Quaternion::rotation_y(slow * 0.05); + next.chest.orientation = Quaternion::rotation_y(slow * 0.05); next.chest.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slow * -0.2, ); - next.leg_fl.ori = + next.leg_fl.orientation = Quaternion::rotation_x(slow * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slow * 0.2, ); - next.leg_fr.ori = + next.leg_fr.orientation = Quaternion::rotation_x(slow_alt * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slow * -0.2, ); - next.leg_bl.ori = + next.leg_bl.orientation = Quaternion::rotation_x(slow_alt * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slow * 0.2, ); - next.leg_br.ori = + next.leg_br.orientation = Quaternion::rotation_x(slow * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_br.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(slow * 0.4); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(slow * 0.4); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_small/jump.rs b/voxygen/src/anim/src/quadruped_small/jump.rs index a8395ac958..8315f0bbf9 100644 --- a/voxygen/src/anim/src/quadruped_small/jump.rs +++ b/voxygen/src/anim/src/quadruped_small/jump.rs @@ -1,5 +1,7 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; -use vek::*; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; pub struct JumpAnimation; @@ -20,50 +22,50 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.5); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.5); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(0.0); + next.chest.orientation = Quaternion::rotation_y(0.0); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.leg_fl.ori = Quaternion::rotation_x(0.0); + next.leg_fl.orientation = Quaternion::rotation_x(0.0); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.leg_fr.ori = Quaternion::rotation_x(0.0); + next.leg_fr.orientation = Quaternion::rotation_x(0.0); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.leg_bl.ori = Quaternion::rotation_x(0.0); + next.leg_bl.orientation = Quaternion::rotation_x(0.0); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.leg_br.ori = Quaternion::rotation_x(0.0); + next.leg_br.orientation = Quaternion::rotation_x(0.0); next.leg_br.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(-0.3); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(-0.3); next.tail.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/quadruped_small/mod.rs b/voxygen/src/anim/src/quadruped_small/mod.rs index 0c19fc6ed2..05142e0639 100644 --- a/voxygen/src/anim/src/quadruped_small/mod.rs +++ b/voxygen/src/anim/src/quadruped_small/mod.rs @@ -6,67 +6,45 @@ pub mod run; // Reexports pub use self::{feed::FeedAnimation, idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct QuadrupedSmallSkeleton { - head: Bone, - chest: Bone, - leg_fl: Bone, - leg_fr: Bone, - leg_bl: Bone, - leg_br: Bone, - tail: Bone, -} - -impl QuadrupedSmallSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct QuadrupedSmallSkeleton { + + head, + + chest, + + leg_fl, + + leg_fr, + + leg_bl, + + leg_br, + + tail, +}); impl Skeleton for QuadrupedSmallSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 7; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"quadruped_small_compute_mats\0"; - fn bone_count(&self) -> usize { 7 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_small_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let chest_mat = self.chest.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_mat * self.head.compute_base_matrix()), - FigureBoneData::new(chest_mat), - FigureBoneData::new(chest_mat * self.leg_fl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.leg_fr.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.leg_bl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.leg_br.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.tail.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.chest.interpolate(&target.chest, dt); - self.leg_fl.interpolate(&target.leg_fl, dt); - self.leg_fr.interpolate(&target.leg_fr, dt); - self.leg_bl.interpolate(&target.leg_bl, dt); - self.leg_br.interpolate(&target.leg_br, dt); - self.tail.interpolate(&target.tail, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(chest_mat * Mat4::::from(self.head)), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.leg_fl)), + make_bone(chest_mat * Mat4::::from(self.leg_fr)), + make_bone(chest_mat * Mat4::::from(self.leg_bl)), + make_bone(chest_mat * Mat4::::from(self.leg_br)), + make_bone(chest_mat * Mat4::::from(self.tail)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/quadruped_small/run.rs b/voxygen/src/anim/src/quadruped_small/run.rs index 09d14dedc0..cbf1092460 100644 --- a/voxygen/src/anim/src/quadruped_small/run.rs +++ b/voxygen/src/anim/src/quadruped_small/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -35,7 +37,7 @@ impl Animation for RunAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -48,13 +50,13 @@ impl Animation for RunAnimation { } * 1.3; let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_x(x_tilt * -0.5 + short * -0.2) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_x(x_tilt * -0.5 + short * -0.2) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_z(tilt * -1.2); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 @@ -62,54 +64,58 @@ impl Animation for RunAnimation { + shortalt * 3.0 * skeleton_attr.spring, ) / 11.0 * skeleton_attr.scaler; - next.chest.ori = Quaternion::rotation_x(short * 0.2 * skeleton_attr.spring + x_tilt) - * Quaternion::rotation_y(tilt * 0.8) - * Quaternion::rotation_z(tilt * -1.5); + next.chest.orientation = + Quaternion::rotation_x(short * 0.2 * skeleton_attr.spring + x_tilt) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_z(tilt * -1.5); next.chest.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + footverttf * 3.0 * skeleton_attr.maximize, skeleton_attr.feet_f.2 + ((footvertf * -1.5).max(-1.0)), ); - next.leg_fl.ori = Quaternion::rotation_x(0.2 + skeleton_attr.minimize * footverttf * 0.65) - * Quaternion::rotation_z(tilt * -0.5) - * Quaternion::rotation_y(tilt * 1.5); + next.leg_fl.orientation = + Quaternion::rotation_x(0.2 + skeleton_attr.minimize * footverttf * 0.65) + * Quaternion::rotation_z(tilt * -0.5) + * Quaternion::rotation_y(tilt * 1.5); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + footvertt * 3.0 * skeleton_attr.minimize, skeleton_attr.feet_f.2 + ((footvert * -1.5).max(-1.0)), ); - next.leg_fr.ori = Quaternion::rotation_x(0.2 + skeleton_attr.maximize * footvertt * 0.65) - * Quaternion::rotation_z(tilt * -0.5) - * Quaternion::rotation_y(tilt * 1.5); + next.leg_fr.orientation = + Quaternion::rotation_x(0.2 + skeleton_attr.maximize * footvertt * 0.65) + * Quaternion::rotation_z(tilt * -0.5) + * Quaternion::rotation_y(tilt * 1.5); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + footvertt * -1.4, skeleton_attr.feet_b.2 + ((footvert * 1.5).max(-1.0)), ); - next.leg_bl.ori = Quaternion::rotation_x(-0.25 + skeleton_attr.maximize * footvertt * -0.8) - * Quaternion::rotation_y(tilt * 1.5) - * Quaternion::rotation_z(tilt * -1.5); + next.leg_bl.orientation = + Quaternion::rotation_x(-0.25 + skeleton_attr.maximize * footvertt * -0.8) + * Quaternion::rotation_y(tilt * 1.5) + * Quaternion::rotation_z(tilt * -1.5); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + footverttf * -1.4, skeleton_attr.feet_b.2 + ((footvertf * 1.5).max(-1.0)), ); - next.leg_br.ori = + next.leg_br.orientation = Quaternion::rotation_x(-0.25 + skeleton_attr.maximize * footverttf * -0.8) * Quaternion::rotation_y(tilt * 1.5) * Quaternion::rotation_z(tilt * -1.5); next.leg_br.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(short * 0.2 + x_tilt) + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(short * 0.2 + x_tilt) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_z(tilt * 1.5); next.tail.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/vek.rs b/voxygen/src/anim/src/vek.rs new file mode 100644 index 0000000000..ee9bda3c45 --- /dev/null +++ b/voxygen/src/anim/src/vek.rs @@ -0,0 +1,8 @@ +pub use ::vek::{ + bezier::repr_simd::*, geom::repr_simd::*, mat::repr_simd::column_major::Mat4, ops::*, + quaternion::repr_simd::*, transform::repr_simd::*, transition::*, vec::repr_simd::*, +}; +/* pub use ::vek::{ + bezier::repr_c::*, geom::repr_c::*, mat::repr_c::column_major::Mat4, ops::*, + quaternion::repr_c::*, transform::repr_c::*, transition::*, vec::repr_c::*, +}; */ diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index a8796fe8e8..5dab15fd4f 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -44,22 +44,18 @@ pub struct AudioFrontend { listener: Listener, } -#[allow(clippy::same_item_push)] // TODO: Pending review in #587 impl AudioFrontend { /// Construct with given device - #[allow(clippy::redundant_clone)] // TODO: Pending review in #587 pub fn new(device: String, max_sfx_channels: usize) -> Self { - let mut sfx_channels = Vec::with_capacity(max_sfx_channels); let audio_device = get_device_raw(&device); + let mut sfx_channels = Vec::with_capacity(max_sfx_channels); if let Some(audio_device) = &audio_device { - for _ in 0..max_sfx_channels { - sfx_channels.push(SfxChannel::new(&audio_device)); - } + sfx_channels.resize_with(max_sfx_channels, || SfxChannel::new(&audio_device)); } Self { - device: device.clone(), + device, device_list: list_devices(), audio_device, sound_cache: SoundCache::default(), diff --git a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs index 3caeb17fc4..efc36f6997 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs @@ -53,7 +53,8 @@ impl EventMapper for CombatEventMapper { let sfx_event_bus = ecs.read_resource::>(); let mut sfx_emitter = sfx_event_bus.emitter(); - let cam_pos = camera.dependents().cam_pos; + let focus_off = camera.get_focus_pos().map(f32::trunc); + let cam_pos = camera.dependents().cam_pos + focus_off; for (entity, pos, loadout, character) in ( &ecs.entities(), diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 55bff58c96..f551987ebb 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -50,7 +50,8 @@ impl EventMapper for MovementEventMapper { let sfx_event_bus = ecs.read_resource::>(); let mut sfx_emitter = sfx_event_bus.emitter(); - let cam_pos = camera.dependents().cam_pos; + let focus_off = camera.get_focus_pos().map(f32::trunc); + let cam_pos = camera.dependents().cam_pos + focus_off; for (entity, pos, vel, body, physics, character) in ( &ecs.entities(), diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 8c47867aa1..7439e24e72 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -234,8 +234,10 @@ impl SfxMgr { } let ecs = state.ecs(); + let focus_off = camera.get_focus_pos().map(f32::trunc); + let cam_pos = camera.dependents().cam_pos + focus_off; - audio.set_listener_pos(camera.dependents().cam_pos, camera.dependents().cam_dir); + audio.set_listener_pos(cam_pos, camera.dependents().cam_dir); // TODO: replace; deprecated in favor of outcomes self.event_mapper @@ -247,7 +249,7 @@ impl SfxMgr { for event in events { let position = match event.pos { Some(pos) => pos, - _ => camera.dependents().cam_pos, + _ => cam_pos, }; if let Some(item) = self.triggers.get_trigger(&event.sfx) { diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 48e99b22b9..cd98cee9eb 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -52,7 +52,7 @@ enum ImageSpec { impl ImageSpec { fn create_graphic(&self) -> Graphic { match self { - ImageSpec::Png(specifier) => Graphic::Image(graceful_load_img(&specifier)), + ImageSpec::Png(specifier) => Graphic::Image(graceful_load_img(&specifier), None), ImageSpec::Vox(specifier) => Graphic::Voxel( graceful_load_segment_no_skin(&specifier), Transform { diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index b6871ff88e..ae34f752d0 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -134,7 +134,7 @@ impl<'a> Widget for Map<'a> { .set(state.ids.icon, ui); // Map Title - Text::new(&self.localized_strings.get("hud.map.map_title")) + Text::new(self.localized_strings.get("hud.map.map_title")) .mid_top_with_margin_on(state.ids.frame, 3.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(29)) @@ -142,15 +142,12 @@ impl<'a> Widget for Map<'a> { .set(state.ids.map_title, ui); // Questlog Title - Text::new(&format!( - "{}", - &self.localized_strings.get("hud.map.qlog_title") - )) - .mid_top_with_margin_on(state.ids.qlog_align, 6.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(21)) - .color(TEXT_COLOR) - .set(state.ids.qlog_title, ui); + Text::new(self.localized_strings.get("hud.map.qlog_title")) + .mid_top_with_margin_on(state.ids.qlog_align, 6.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(21)) + .color(TEXT_COLOR) + .set(state.ids.qlog_title, ui); // X-Button if Button::image(self.imgs.close_button) @@ -197,8 +194,10 @@ impl<'a> Widget for Map<'a> { .read_storage::() .get(self.client.entity()) .map_or(Vec3::zero(), |pos| pos.0); - let w_src = worldsize.x / TerrainChunkSize::RECT_SIZE.x as f64 / zoom; - let h_src = worldsize.y / TerrainChunkSize::RECT_SIZE.y as f64 / zoom; + let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .reduce_partial_max()/*.min(f64::MAX)*/; + let w_src = max_zoom / zoom; + let h_src = max_zoom / zoom; let rect_src = position::Rect::from_xy_dim( [ player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64, diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 28d27563e7..502ee619e0 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -134,7 +134,7 @@ impl<'a> Widget for MiniMap<'a> { // somehow if you zoom in too far. Or both. let min_zoom = 1.0; let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .reduce_partial_min()/*.min(f64::MAX)*/; + .reduce_partial_max()/*.min(f64::MAX)*/; // NOTE: Not sure if a button can be clicked while disabled, but we still double // check for both kinds of zoom to make sure that not only was the @@ -190,8 +190,8 @@ impl<'a> Widget for MiniMap<'a> { .map_or(Vec3::zero(), |pos| pos.0); // Get map image source rectangle dimensons. - let w_src = worldsize.x / TerrainChunkSize::RECT_SIZE.x as f64 / zoom; - let h_src = worldsize.y / TerrainChunkSize::RECT_SIZE.y as f64 / zoom; + let w_src = max_zoom / zoom; + let h_src = max_zoom / zoom; // Set map image to be centered around player coordinates. let rect_src = position::Rect::from_xy_dim( diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index bf52c29b1e..202aa2dc2b 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -46,8 +46,11 @@ use spell::Spell; use crate::{ ecs::comp as vcomp, i18n::{i18n_asset_key, LanguageMetadata, VoxygenLocalization}, - render::{AaMode, CloudMode, Consts, FluidMode, Globals, Renderer}, - scene::camera::{self, Camera}, + render::{Consts, Globals, RenderMode, Renderer}, + scene::{ + camera::{self, Camera}, + lod, + }, ui::{fonts::ConrodVoxygenFonts, slot, Graphic, Ingameable, ScaleMode, Ui}, window::{Event as WinEvent, GameInput}, GlobalState, @@ -178,6 +181,7 @@ widget_ids! { time, entity_count, num_chunks, + num_lights, num_figures, num_particles, @@ -245,7 +249,9 @@ pub struct DebugInfo { pub velocity: Option, pub ori: Option, pub num_chunks: u32, + pub num_lights: u32, pub num_visible_chunks: u32, + pub num_shadow_chunks: u32, pub num_figures: u32, pub num_figures_visible: u32, pub num_particles: u32, @@ -268,6 +274,7 @@ pub enum Event { ToggleMouseYInvert(bool), ToggleSmoothPan(bool), AdjustViewDistance(u32), + AdjustLodDetail(u32), AdjustSpriteRenderDistance(u32), AdjustFigureLoDRenderDistance(u32), AdjustMusicVolume(f32), @@ -280,9 +287,6 @@ pub enum Event { AdjustWindowSize([u16; 2]), ToggleParticlesEnabled(bool), ToggleFullscreen, - ChangeAaMode(AaMode), - ChangeCloudMode(CloudMode), - ChangeFluidMode(FluidMode), ChangeResolution([u16; 2]), ChangeBitDepth(Option), ChangeRefreshRate(Option), @@ -305,14 +309,15 @@ pub enum Event { UseSlot(comp::slot::Slot), SwapSlots(comp::slot::Slot, comp::slot::Slot), DropSlot(comp::slot::Slot), - ChangeHotbarState(HotbarState), + ChangeHotbarState(Box), Ability3(bool), Logout, Quit, - ChangeLanguage(LanguageMetadata), + ChangeLanguage(Box), ChangeBinding(GameInput), ResetBindings, ChangeFreeLookBehavior(PressBehavior), + ChangeRenderMode(Box), ChangeAutoWalkBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), CraftRecipe(String), @@ -577,10 +582,17 @@ impl Hud { ui.set_scaling_mode(settings.gameplay.ui_scale); // Generate ids. let ids = Ids::new(ui.id_generator()); + // NOTE: Use a border the same color as the LOD ocean color (but with a + // translucent alpha since UI have transparency and LOD doesn't). + let mut water_color = lod::water_color(); + water_color.a = 0.5; // Load world map let world_map = ( - ui.add_graphic_with_rotations(Graphic::Image(client.world_map.0.clone())), - client.world_map.1, + ui.add_graphic_with_rotations(Graphic::Image( + client.world_map.0.clone(), + Some(water_color), + )), + client.world_map.1.map(u32::from), ); // Load images. let imgs = Imgs::load(&mut ui).expect("Failed to load images!"); @@ -670,7 +682,7 @@ impl Hud { &mut self, client: &Client, global_state: &GlobalState, - debug_info: DebugInfo, + debug_info: &Option, dt: Duration, info: HudInfo, camera: &Camera, @@ -679,10 +691,6 @@ impl Hud { let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets(); // pulse time for pulsating elements self.pulse = self.pulse + dt.as_secs_f32(); - self.velocity = match debug_info.velocity { - Some(velocity) => velocity.0.magnitude(), - None => 0.0, - }; let version = format!( "{}-{}", @@ -776,7 +784,7 @@ impl Hud { } // Max amount the sct font size increases when "flashing" - const FLASH_MAX: f32 = 25.0; + const FLASH_MAX: u32 = 2; // Get player position. let player_pos = client @@ -820,9 +828,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if timer < 0.1 { - (FLASH_MAX * (1.0 - timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -873,9 +881,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if floater.timer < 0.1 { - (FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -955,7 +963,7 @@ impl Hud { + ((exp_change.abs() as f32 / stats.exp.maximum() as f32).min(1.0) * 50.0) as u32 + if timer < 0.1 { - (FLASH_MAX * (1.0 - timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -999,7 +1007,7 @@ impl Hud { .min(1.0) * 50.0) as u32 + if floater.timer < 0.1 { - (FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -1228,9 +1236,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if timer < 0.1 { - (FLASH_MAX * (1.0 - timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -1273,9 +1281,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if floater.timer < 0.1 { - (FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -1383,7 +1391,11 @@ impl Hud { } // Display debug window. - if global_state.settings.gameplay.toggle_debug { + if let Some(debug_info) = debug_info { + self.velocity = match debug_info.velocity { + Some(velocity) => velocity.0.magnitude(), + None => 0.0, + }; // Alpha Version Text::new(&version) .top_left_with_margins_on(ui_widgets.window, 5.0, 5.0) @@ -1489,8 +1501,8 @@ impl Hud { // Number of chunks Text::new(&format!( - "Chunks: {} ({} visible)", - debug_info.num_chunks, debug_info.num_visible_chunks, + "Chunks: {} ({} visible) & {} (shadow)", + debug_info.num_chunks, debug_info.num_visible_chunks, debug_info.num_shadow_chunks, )) .color(TEXT_COLOR) .down_from(self.ids.entity_count, 5.0) @@ -1498,13 +1510,21 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_chunks, ui_widgets); + // Number of lights + Text::new(&format!("Lights: {}", debug_info.num_lights,)) + .color(TEXT_COLOR) + .down_from(self.ids.num_chunks, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.num_lights, ui_widgets); + // Number of figures Text::new(&format!( "Figures: {} ({} visible)", debug_info.num_figures, debug_info.num_figures_visible, )) .color(TEXT_COLOR) - .down_from(self.ids.num_chunks, 5.0) + .down_from(self.ids.num_lights, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_figures, ui_widgets); @@ -1867,6 +1887,9 @@ impl Hud { settings_window::Event::AdjustViewDistance(view_distance) => { events.push(Event::AdjustViewDistance(view_distance)); }, + settings_window::Event::AdjustLodDetail(lod_detail) => { + events.push(Event::AdjustLodDetail(lod_detail)); + }, settings_window::Event::AdjustSpriteRenderDistance(view_distance) => { events.push(Event::AdjustSpriteRenderDistance(view_distance)); }, @@ -1909,14 +1932,8 @@ impl Hud { settings_window::Event::AdjustGamma(new_gamma) => { events.push(Event::ChangeGamma(new_gamma)); }, - settings_window::Event::ChangeAaMode(new_aa_mode) => { - events.push(Event::ChangeAaMode(new_aa_mode)); - }, - settings_window::Event::ChangeCloudMode(new_cloud_mode) => { - events.push(Event::ChangeCloudMode(new_cloud_mode)); - }, - settings_window::Event::ChangeFluidMode(new_fluid_mode) => { - events.push(Event::ChangeFluidMode(new_fluid_mode)); + settings_window::Event::ChangeRenderMode(new_render_mode) => { + events.push(Event::ChangeRenderMode(new_render_mode)); }, settings_window::Event::ChangeResolution(new_resolution) => { events.push(Event::ChangeResolution(new_resolution)); @@ -2142,10 +2159,10 @@ impl Hud { events.push(Event::SwapSlots(a, b)); } else if let (Inventory(i), Hotbar(h)) = (a, b) { self.hotbar.add_inventory_link(h, i.0); - events.push(Event::ChangeHotbarState(self.hotbar.to_owned())); + events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); } else if let (Hotbar(a), Hotbar(b)) = (a, b) { self.hotbar.swap(a, b); - events.push(Event::ChangeHotbarState(self.hotbar.to_owned())); + events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); } }, slot::Event::Dropped(from) => { @@ -2154,7 +2171,7 @@ impl Hud { events.push(Event::DropSlot(from)); } else if let Hotbar(h) = from { self.hotbar.clear_slot(h); - events.push(Event::ChangeHotbarState(self.hotbar.to_owned())); + events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); } }, slot::Event::Used(from) => { @@ -2452,7 +2469,7 @@ impl Hud { &mut self, client: &Client, global_state: &mut GlobalState, - debug_info: DebugInfo, + debug_info: &Option, camera: &Camera, dt: Duration, info: HudInfo, @@ -2461,8 +2478,7 @@ impl Hud { if self.ui.ui.global_input().events().any(|event| { use conrod_core::{event, input}; matches!(event, - //event::Event::Raw(event::Input::Press(input::Button::Keyboard(input::Key::Tab))) - // => true, + /* event::Event::Raw(event::Input::Press(input::Button::Keyboard(input::Key::Tab))) | */ event::Event::Ui(event::Ui::Press( _, event::Press { @@ -2476,6 +2492,11 @@ impl Hud { .handle_event(conrod_core::event::Input::Text("\t".to_string())); } + if !self.show.ui { + // Optimization: skip maintaining UI when it's off. + return vec![]; + } + if let Some(maybe_id) = self.to_focus.take() { self.ui.focus_widget(maybe_id); } @@ -2483,14 +2504,16 @@ impl Hud { let camera::Dependents { view_mat, proj_mat, .. } = camera.dependents(); - self.ui.maintain( - &mut global_state.window.renderer_mut(), - Some(proj_mat * view_mat), - ); + let focus_off = camera.get_focus_pos().map(f32::trunc); // Check if item images need to be reloaded self.item_imgs.reload_if_changed(&mut self.ui); + self.ui.maintain( + &mut global_state.window.renderer_mut(), + Some(proj_mat * view_mat * Mat4::translation_3d(-focus_off)), + ); + events } diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 3a990f5220..78d6161f79 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -109,17 +109,23 @@ impl<'a> Ingameable for Overhead<'a> { // Number of conrod primitives contained in the overhead display. TODO maybe // this could be done automatically? // - 2 Text::new for name - // If HP Info is shown + 6 + // + // If HP Info is shown: // - 1 for level: either Text or Image - // - 4 for HP + mana + fg + bg - // - 1 for HP Text - // If there's a speech bubble + 13 - // - 2 Text::new for speec8 bubble + // - 3 for HP + fg + bg + // - 1 for HP text + // - If there's mana + // - 1 Rect::new for mana + // + // If there's a speech bubble + // - 2 Text::new for speech bubble // - 1 Image::new for icon // - 10 Image::new for speech bubble (9-slice + tail) 2 + if self.bubble.is_some() { 13 } else { 0 } - + if (self.stats.health.current() as f64 / self.stats.health.maximum() as f64) < 1.0 { - 6 + + if f64::from(self.stats.health.current()) / f64::from(self.stats.health.maximum()) + < 1.0 + { + 5 + if self.energy.is_some() { 1 } else { 0 } } else { 0 } @@ -291,10 +297,11 @@ impl<'a> Widget for Overhead<'a> { .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); if dark_mode { - tail.w_h(22.0, 13.0).set(state.ids.speech_bubble_tail, ui) + tail.w_h(22.0, 13.0) } else { - tail.w_h(22.0, 28.0).set(state.ids.speech_bubble_tail, ui) - }; + tail.w_h(22.0, 28.0) + } + .set(state.ids.speech_bubble_tail, ui); let mut text_shadow = Text::new(&bubble_contents) .color(shadow_color) @@ -320,6 +327,8 @@ impl<'a> Widget for Overhead<'a> { Image::new(icon) .w_h(16.0, 16.0) .top_left_with_margin_on(state.ids.speech_bubble_text, -16.0) + // TODO: Figure out whether this should be parented. + // .parent(id) .set(state.ids.speech_bubble_icon, ui); } diff --git a/voxygen/src/hud/overitem.rs b/voxygen/src/hud/overitem.rs index bb4458ecb6..46da9859db 100644 --- a/voxygen/src/hud/overitem.rs +++ b/voxygen/src/hud/overitem.rs @@ -17,7 +17,7 @@ widget_ids! { #[derive(WidgetCommon)] pub struct Overitem<'a> { name: &'a str, - distance: &'a f32, + _distance: &'a f32, fonts: &'a ConrodVoxygenFonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -27,7 +27,7 @@ impl<'a> Overitem<'a> { pub fn new(name: &'a str, distance: &'a f32, fonts: &'a ConrodVoxygenFonts) -> Self { Self { name, - distance, + _distance: distance, fonts, common: widget::CommonBuilder::default(), } @@ -63,8 +63,8 @@ impl<'a> Widget for Overitem<'a> { fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { state, ui, .. } = args; - let font_size = - ((1.0 - (self.distance / common::comp::MAX_PICKUP_RANGE_SQR)) * 30.0) as u32; + let font_size = 30; + // ((1.0 - (self.distance / common::comp::MAX_PICKUP_RANGE_SQR)) * 30.0) as u32; // ItemName Text::new(&self.name) diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 7ece673e69..3d7d53d47a 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ i18n::{list_localizations, LanguageMetadata, VoxygenLocalization}, - render::{AaMode, CloudMode, FluidMode}, + render::{AaMode, CloudMode, FluidMode, LightingMode, RenderMode, ShadowMapMode, ShadowMode}, ui::{fonts::ConrodVoxygenFonts, ImageSlider, ScaleMode, ToggleButton}, window::GameInput, GlobalState, @@ -16,6 +16,7 @@ use conrod_core::{ widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use core::convert::TryFrom; use itertools::Itertools; use std::iter::once; @@ -94,6 +95,9 @@ widget_ids! { vd_slider, vd_text, vd_value, + lod_detail_slider, + lod_detail_text, + lod_detail_value, sprite_dist_slider, sprite_dist_text, sprite_dist_value, @@ -128,6 +132,13 @@ widget_ids! { // fullscreen_button, fullscreen_label, + lighting_mode_text, + lighting_mode_list, + shadow_mode_text, + shadow_mode_list, + shadow_mode_map_resolution_text, + shadow_mode_map_resolution_slider, + shadow_mode_map_resolution_value, save_window_size_button, audio_volume_slider, audio_volume_text, @@ -245,13 +256,12 @@ pub enum Event { AdjustSpriteRenderDistance(u32), AdjustFigureLoDRenderDistance(u32), AdjustFOV(u16), + AdjustLodDetail(u32), AdjustGamma(f32), AdjustWindowSize([u16; 2]), ToggleParticlesEnabled(bool), ToggleFullscreen, - ChangeAaMode(AaMode), - ChangeCloudMode(CloudMode), - ChangeFluidMode(FluidMode), + ChangeRenderMode(Box), ChangeResolution([u16; 2]), ChangeBitDepth(Option), ChangeRefreshRate(Option), @@ -269,7 +279,7 @@ pub enum Event { SctDamageBatch(bool), SpeechBubbleDarkMode(bool), SpeechBubbleIcon(bool), - ChangeLanguage(LanguageMetadata), + ChangeLanguage(Box), ChangeBinding(GameInput), ResetBindings, ChangeFreeLookBehavior(PressBehavior), @@ -1239,7 +1249,9 @@ impl<'a> Widget for SettingsWindow<'a> { .label_font_id(self.fonts.cyri.conrod_id) .set(state.ids.languages_list, ui) { - events.push(Event::ChangeLanguage(language_list[clicked].to_owned())); + events.push(Event::ChangeLanguage(Box::new( + language_list[clicked].to_owned(), + ))); } } @@ -1714,7 +1726,10 @@ impl<'a> Widget for SettingsWindow<'a> { if let Some(new_val) = ImageSlider::discrete( self.global_state.settings.graphics.view_distance, 1, - 65, + // FIXME: Move back to 64 once we support multiple texture atlases, or figure out a + // way to increase the size of the terrain atlas. + 25, + // 65, self.imgs.slider_indicator, self.imgs.slider, ) @@ -1805,9 +1820,47 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.fov_value, ui); + // LoD detail + Text::new(&self.localized_strings.get("hud.settings.lod_detail")) + .down_from(state.ids.fov_slider, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.lod_detail_text, ui); + + if let Some(new_val) = ImageSlider::discrete( + ((self.global_state.settings.graphics.lod_detail as f32 / 100.0).log(5.0) * 10.0) + .round() as i32, + 0, + 20, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .down_from(state.ids.lod_detail_text, 8.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.lod_detail_slider, ui) + { + events.push(Event::AdjustLodDetail( + (5.0f32.powf(new_val as f32 / 10.0) * 100.0) as u32, + )); + } + + Text::new(&format!( + "{}", + self.global_state.settings.graphics.lod_detail + )) + .right_from(state.ids.lod_detail_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.lod_detail_value, ui); + // Gamma Text::new(&self.localized_strings.get("hud.settings.gamma")) - .down_from(state.ids.fov_slider, 10.0) + .down_from(state.ids.lod_detail_slider, 10.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) @@ -1917,6 +1970,8 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.figure_dist_value, ui); + let render_mode = &self.global_state.settings.graphics.render_mode; + // AaMode Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode")) .down_from(state.ids.gamma_slider, 8.0) @@ -1925,27 +1980,26 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.aa_mode_text, ui); + // NOTE: MSAA modes are currently disabled from the UI due to poor + // interaction with greedy meshing, and may eventually be removed. let mode_list = [ AaMode::None, AaMode::Fxaa, - AaMode::MsaaX4, + /* AaMode::MsaaX4, AaMode::MsaaX8, - AaMode::MsaaX16, + AaMode::MsaaX16, */ AaMode::SsaaX4, ]; let mode_label_list = [ - "No AA", - "FXAA", - "MSAA x4", + "No AA", "FXAA", + /* "MSAA x4", "MSAA x8", - "MSAA x16 (experimental)", + "MSAA x16 (experimental)", */ "SSAA x4", ]; // Get which AA mode is currently active - let selected = mode_list - .iter() - .position(|x| *x == self.global_state.settings.graphics.aa_mode); + let selected = mode_list.iter().position(|x| *x == render_mode.aa); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1955,7 +2009,10 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.aa_mode_text, 8.0) .set(state.ids.aa_mode_list, ui) { - events.push(Event::ChangeAaMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + aa: mode_list[clicked], + ..render_mode.clone() + }))); } // CloudMode @@ -1979,9 +2036,7 @@ impl<'a> Widget for SettingsWindow<'a> { ]; // Get which cloud rendering mode is currently active - let selected = mode_list - .iter() - .position(|x| *x == self.global_state.settings.graphics.cloud_mode); + let selected = mode_list.iter().position(|x| *x == render_mode.cloud); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1991,7 +2046,10 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.cloud_mode_text, 8.0) .set(state.ids.cloud_mode_list, ui) { - events.push(Event::ChangeCloudMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + cloud: mode_list[clicked], + ..render_mode.clone() + }))); } // FluidMode @@ -2006,7 +2064,11 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.fluid_mode_text, ui); - let mode_list = [FluidMode::Cheap, FluidMode::Shiny]; + // FIXME: Add shiny water back to the UI once we fix the bug on nVidia cards. + let mode_list = [ + FluidMode::Cheap, + // FluidMode::Shiny + ]; let mode_label_list = [ &self .localized_strings @@ -2017,9 +2079,7 @@ impl<'a> Widget for SettingsWindow<'a> { ]; // Get which fluid rendering mode is currently active - let selected = mode_list - .iter() - .position(|x| *x == self.global_state.settings.graphics.fluid_mode); + let selected = mode_list.iter().position(|x| *x == render_mode.fluid); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -2029,14 +2089,155 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.fluid_mode_text, 8.0) .set(state.ids.fluid_mode_list, ui) { - events.push(Event::ChangeFluidMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + fluid: mode_list[clicked], + ..render_mode.clone() + }))); + } + + // LightingMode + Text::new( + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode"), + ) + .down_from(state.ids.fluid_mode_list, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.lighting_mode_text, ui); + + let mode_list = [ + LightingMode::Ashikhmin, + LightingMode::BlinnPhong, + LightingMode::Lambertian, + ]; + let mode_label_list = [ + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode.ashikhmin"), + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode.blinnphong"), + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode.lambertian"), + ]; + + // Get which lighting rendering mode is currently active + let selected = mode_list.iter().position(|x| *x == render_mode.lighting); + + if let Some(clicked) = DropDownList::new(&mode_label_list, selected) + .w_h(400.0, 22.0) + .color(MENU_BG) + .label_color(TEXT_COLOR) + .label_font_id(self.fonts.cyri.conrod_id) + .down_from(state.ids.lighting_mode_text, 8.0) + .set(state.ids.lighting_mode_list, ui) + { + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + lighting: mode_list[clicked], + ..render_mode.clone() + }))); + } + + // ShadowMode + Text::new( + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode"), + ) + .down_from(state.ids.lighting_mode_list, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.shadow_mode_text, ui); + + let shadow_map_mode = ShadowMapMode::try_from(render_mode.shadow).ok(); + let mode_list = [ + ShadowMode::None, + ShadowMode::Cheap, + ShadowMode::Map(shadow_map_mode.unwrap_or_default()), + ]; + let mode_label_list = [ + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.none"), + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.cheap"), + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.map"), + ]; + + // Get which shadow rendering mode is currently active + let selected = mode_list.iter().position(|x| *x == render_mode.shadow); + + if let Some(clicked) = DropDownList::new(&mode_label_list, selected) + .w_h(400.0, 22.0) + .color(MENU_BG) + .label_color(TEXT_COLOR) + .label_font_id(self.fonts.cyri.conrod_id) + .down_from(state.ids.shadow_mode_text, 8.0) + .set(state.ids.shadow_mode_list, ui) + { + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + shadow: mode_list[clicked], + ..render_mode.clone() + }))); + } + + if let Some(shadow_map_mode) = shadow_map_mode { + // Display the shadow map mode if selected. + Text::new( + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.map.resolution"), + ) + .right_from(state.ids.shadow_mode_list, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.shadow_mode_map_resolution_text, ui); + + if let Some(new_val) = ImageSlider::discrete( + (shadow_map_mode.resolution.log2() * 4.0).round() as i8, + -8, + 8, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .right_from(state.ids.shadow_mode_map_resolution_text, 8.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.shadow_mode_map_resolution_slider, ui) + { + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + shadow: ShadowMode::Map(ShadowMapMode { + resolution: 2.0f32.powf(f32::from(new_val) / 4.0), + }), + ..render_mode.clone() + }))); + } + + // TODO: Consider fixing to avoid allocation (it's probably not a bottleneck but + // there's no reason to allocate for numbers). + Text::new(&format!("{}", shadow_map_mode.resolution)) + .right_from(state.ids.shadow_mode_map_resolution_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.shadow_mode_map_resolution_value, ui); } // Particles Text::new(&self.localized_strings.get("hud.settings.particles")) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .down_from(state.ids.fluid_mode_list, 8.0) + .down_from(state.ids.shadow_mode_list, 8.0) .color(TEXT_COLOR) .set(state.ids.particles_label, ui); diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 9eeeb17318..ff26ee3f13 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] -#![allow(clippy::option_map_unit_fn)] -#![feature(drain_filter, bool_to_option, or_patterns)] +#![allow(clippy::option_map_unit_fn, incomplete_features)] +#![feature(array_map, bool_to_option, const_generics, drain_filter, or_patterns)] #![recursion_limit = "2048"] #[macro_use] diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index ece5cb3ea4..2edbc0f330 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -25,13 +25,15 @@ pub struct CharSelectionState { impl CharSelectionState { /// Create a new `CharSelectionState`. pub fn new(global_state: &mut GlobalState, client: Rc>) -> Self { + let scene = Scene::new( + global_state.window.renderer_mut(), + Some("fixture.selection_bg"), + &*client.borrow(), + ); Self { char_selection_ui: CharSelectionUi::new(global_state), client, - scene: Scene::new( - global_state.window.renderer_mut(), - Some("fixture.selection_bg"), - ), + scene, } } diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index d14a4e3549..dc89969460 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -204,9 +204,10 @@ impl<'a> MainMenuUi { // Load images let imgs = Imgs::load(&mut ui).expect("Failed to load images"); let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load images!"); - let bg_img_id = ui.add_graphic(Graphic::Image(load_expect( - bg_imgs.choose(&mut rng).unwrap(), - ))); + let bg_img_id = ui.add_graphic(Graphic::Image( + load_expect(bg_imgs.choose(&mut rng).unwrap()), + None, + )); //let chosen_tip = *tips.choose(&mut rng).unwrap(); // Load language let voxygen_i18n = load_expect::(&i18n_asset_key( diff --git a/voxygen/src/mesh/greedy.rs b/voxygen/src/mesh/greedy.rs new file mode 100644 index 0000000000..d906ea4d24 --- /dev/null +++ b/voxygen/src/mesh/greedy.rs @@ -0,0 +1,636 @@ +use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline}; +use vek::*; + +type TerrainVertex = ::Vertex; + +type TodoRect = ( + Vec3, + Vec2>, + guillotiere::Rectangle, + Vec3, +); + +pub struct GreedyConfig { + pub data: D, + /// The minimum position to mesh, in the coordinate system used + /// for queries against the volume. + pub draw_delta: Vec3, + /// For each dimension i, for faces drawn in planes *parallel* to i, + /// represents the number of voxels considered along dimenson i in those + /// planes, starting from `draw_delta`. + pub greedy_size: Vec3, + /// For each dimension i, represents the number of planes considered + /// *orthogonal* to dimension i, starting from `draw_delta`. This should + /// usually be the same as greedy_size. + /// + /// An important exception is during chunk rendering (where vertical faces + /// at chunk boundaries would otherwise be rendered twice, and also + /// force us to use more than 5 bits to represent x and y + /// positions--though there may be a clever way around the latter). + /// Thus, for chunk rendering we set the number of *vertical* planes to + /// one less than the chunk size along the x and y dimensions, but keep + /// the number of *horizontal* planes large enough to cover the whole + /// chunk. + pub greedy_size_cross: Vec3, + /// Given a position, return the lighting information for the voxel at that + /// position. + pub get_light: FL, + /// Given a position, return the color information for the voxel at that + /// position. + pub get_color: FC, + /// Given a position, return the opacity information for the voxel at that + /// position. Currently, we don't support real translucent lighting, so the + /// value should either be `false` (for opaque blocks) or `true` + /// (otherwise). + pub get_opacity: FO, + /// Given a position and a normal, should we draw the face between the + /// position and position - normal (i.e. the voxel "below" this vertex)? + /// If so, provide its orientation, together with any other meta + /// information required for the mesh that needs to split up faces. For + /// example, terrain faces currently record a bit indicating whether + /// they are exposed to water or not, so we should not merge faces where + /// one is submerged in water and the other is not, even if they + /// otherwise have the same orientation, dimensions, and are + /// next to each other. + pub should_draw: FS, + /// Create an opauqe quad (used for only display rendering) from its + /// top-left atlas position, the rectangle's dimensions in (2D) atlas + /// space, a world position, the u and v axes of the rectangle in (3D) + /// world space, the normal facing out frmo the rectangle in world + /// space, and meta information common to every voxel in this rectangle. + pub push_quad: FP, +} + +/// A suspended greedy mesh, with enough information to recover color data. +/// +/// The reason this exists is that greedy meshing is split into two parts. +/// First, the meshing itself needs to be performed; secondly, we generate a +/// texture atlas. We do things in this order to avoid having to copy all the +/// old vertices to the correct location. However, when trying to use the same +/// texture atlas for more than one model, this approach runs into the +/// problem that enough model information needs to be remembered to be able to +/// generate the colors after the function returns, so we box up the actual +/// coloring part as a continuation. When called with a final tile size and +/// vector, the continuation will consume the color data and write it to the +/// vector. +pub type SuspendedMesh<'a> = dyn for<'r> FnOnce(&'r mut ColLightInfo) + 'a; + +/// Shared state for a greedy mesh, potentially passed along to multiple models. +/// +/// For an explanation of why we want this, see `SuspendedMesh`. +pub struct GreedyMesh<'a> { + atlas: guillotiere::SimpleAtlasAllocator, + col_lights_size: Vec2, + max_size: guillotiere::Size, + suspended: Vec>>, +} + +impl<'a> GreedyMesh<'a> { + /// Construct a new greedy mesher. + /// + /// Takes as input the maximum allowable size of the texture atlas used to + /// store the light/color data for this mesh. + /// + /// NOTE: It is an error to pass any size > u16::MAX. + /// + /// Even aside from the above limitation, this will not necessarily always + /// be the same as the maximum atlas size supported by the hardware. + /// For instance, since we want to reserve 4 bits for a bone index for + /// figures in their shadow vertex, the atlas parameter for figures has + /// to have at least 2 bits of the normal; thus, it can only take up at + /// most 30 bits total, meaning we are restricted to "only" at most 2^15 + /// × 2^15 atlases even if the hardware supports larger ones. + pub fn new(max_size: guillotiere::Size) -> Self { + let min_max_dim = max_size.width.min(max_size.height); + assert!( + min_max_dim >= 4, + "min_max_dim={:?} >= 4 ({:?}", + min_max_dim, + max_size + ); + // TODO: Collect information to see if we can choose a good value here. + let large_size_threshold = 256.min(min_max_dim / 2 + 1); + let small_size_threshold = 33.min(large_size_threshold / 2 + 1); + let size = guillotiere::Size::new(32, 32).min(max_size); + let atlas = + guillotiere::SimpleAtlasAllocator::with_options(size, &guillotiere::AllocatorOptions { + snap_size: 1, + small_size_threshold, + large_size_threshold, + }); + let col_lights_size = Vec2::new(1u16, 1u16); + Self { + atlas, + col_lights_size, + max_size, + suspended: Vec::new(), + } + } + + /// Perform greedy meshing on a model, separately producing "pure" model + /// data (the opaque mesh, together with atlas positions connecting + /// each rectangle with texture information), and raw light and color + /// data ready to be used as a texture (accessible with `finalize`). + /// Texture data built up within the same greedy mesh will be inserted + /// into the same atlas, which can be used to group texture data for + /// things like figures that are the result of meshing multiple models. + /// + /// Returns an estimate of the bounds of the current meshed model. + /// + /// For more information on the config parameter, see [GreedyConfig]. + pub fn push( + &mut self, + config: GreedyConfig, + ) where + FL: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, + FC: for<'r> FnMut(&'r mut D, Vec3) -> Rgb + 'a, + FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, + FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, + FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), + { + let cont = greedy_mesh( + &mut self.atlas, + &mut self.col_lights_size, + self.max_size, + config, + ); + self.suspended.push(cont); + } + + /// Finalize the mesh, producing texture color data for the whole model. + /// + /// By delaying finalization until the contents of the whole texture atlas + /// are known, we can perform just a single allocation to construct a + /// precisely fitting atlas. This will also let us (in the future) + /// suspend meshing partway through in order to meet frame budget, and + /// potentially use a single staged upload to the GPU. + /// + /// Returns the ColLightsInfo corresponding to the consstructed atlas. + pub fn finalize(self) -> ColLightInfo { + let cur_size = self.col_lights_size; + let col_lights = vec![ + TerrainVertex::make_col_light(254, Rgb::broadcast(254)); + usize::from(cur_size.x) * usize::from(cur_size.y) + ]; + let mut col_lights_info = (col_lights, cur_size); + self.suspended.into_iter().for_each(|cont| { + cont(&mut col_lights_info); + }); + col_lights_info + } + + pub fn max_size(&self) -> guillotiere::Size { self.max_size } +} + +fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FC, FO, FS, FP>( + atlas: &mut guillotiere::SimpleAtlasAllocator, + col_lights_size: &mut Vec2, + max_size: guillotiere::Size, + GreedyConfig { + mut data, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + mut should_draw, + mut push_quad, + }: GreedyConfig, +) -> Box> +where + FL: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, + FC: for<'r> FnMut(&'r mut D, Vec3) -> Rgb + 'a, + FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, + FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, + FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), +{ + // TODO: Collect information to see if we can choose a good value here. + let mut todo_rects = Vec::with_capacity(1024); + + // x (u = y, v = z) + greedy_mesh_cross_section( + Vec3::new(greedy_size.y, greedy_size.z, greedy_size_cross.x), + |pos| { + should_draw( + &mut data, + draw_delta + Vec3::new(pos.z, pos.x, pos.y), + Vec3::unit_x(), + Vec2::new(Vec3::unit_y(), Vec3::unit_z()), + ) + }, + |pos, dim, &(faces_forward, ref meta)| { + let pos = Vec3::new(pos.z, pos.x, pos.y); + let uv = Vec2::new(Vec3::unit_y(), Vec3::unit_z()); + let norm = Vec3::unit_x(); + let atlas_pos = if let Some(atlas_pos) = add_to_atlas( + atlas, + &mut todo_rects, + pos, + uv, + dim, + norm, + faces_forward, + max_size, + col_lights_size, + ) { + atlas_pos + } else { + return; + }; + create_quad_greedy( + pos, + dim, + uv, + norm, + faces_forward, + meta, + atlas_pos, + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + // y (u = z, v = x) + greedy_mesh_cross_section( + Vec3::new(greedy_size.z, greedy_size.x, greedy_size_cross.y), + |pos| { + should_draw( + &mut data, + draw_delta + Vec3::new(pos.y, pos.z, pos.x), + Vec3::unit_y(), + Vec2::new(Vec3::unit_z(), Vec3::unit_x()), + ) + }, + |pos, dim, &(faces_forward, ref meta)| { + let pos = Vec3::new(pos.y, pos.z, pos.x); + let uv = Vec2::new(Vec3::unit_z(), Vec3::unit_x()); + let norm = Vec3::unit_y(); + let atlas_pos = if let Some(atlas_pos) = add_to_atlas( + atlas, + &mut todo_rects, + pos, + uv, + dim, + norm, + faces_forward, + max_size, + col_lights_size, + ) { + atlas_pos + } else { + return; + }; + create_quad_greedy( + pos, + dim, + uv, + norm, + faces_forward, + meta, + atlas_pos, + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + // z (u = x, v = y) + greedy_mesh_cross_section( + Vec3::new(greedy_size.x, greedy_size.y, greedy_size_cross.z), + |pos| { + should_draw( + &mut data, + draw_delta + Vec3::new(pos.x, pos.y, pos.z), + Vec3::unit_z(), + Vec2::new(Vec3::unit_x(), Vec3::unit_y()), + ) + }, + |pos, dim, &(faces_forward, ref meta)| { + let pos = Vec3::new(pos.x, pos.y, pos.z); + let uv = Vec2::new(Vec3::unit_x(), Vec3::unit_y()); + let norm = Vec3::unit_z(); + let atlas_pos = if let Some(atlas_pos) = add_to_atlas( + atlas, + &mut todo_rects, + pos, + uv, + dim, + norm, + faces_forward, + max_size, + col_lights_size, + ) { + atlas_pos + } else { + return; + }; + create_quad_greedy( + pos, + dim, + uv, + norm, + faces_forward, + meta, + atlas_pos, + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + Box::new(move |col_lights_info| { + let mut data = data; + draw_col_lights( + col_lights_info, + &mut data, + todo_rects, + draw_delta, + get_light, + get_color, + get_opacity, + TerrainVertex::make_col_light, + ); + }) +} + +/// Greedy meshing a single cross-section. +// TODO: See if we can speed a lot of this up using SIMD. +fn greedy_mesh_cross_section( + dims: Vec3, + // Should we draw a face here (below this vertex)? If so, provide its meta information. + mut draw_face: impl FnMut(Vec3) -> Option, + // Vertex, width and height, and meta information about the block. + mut push_quads: impl FnMut(Vec3, Vec2, &M), +) { + // mask represents which faces are either set while the other is unset, or unset + // while the other is set. + let mut mask = (0..dims.y * dims.x).map(|_| None).collect::>(); + (0..dims.z + 1).for_each(|d| { + // Compute mask + mask.iter_mut().enumerate().for_each(|(posi, mask)| { + let i = posi % dims.x; + let j = posi / dims.x; + // NOTE: Safe because dims.z actually fits in a u16. + *mask = draw_face(Vec3::new(i as i32, j as i32, d as i32)); + }); + + (0..dims.y).for_each(|j| { + let mut i = 0; + while i < dims.x { + // Compute width (number of set x bits for this row and layer, starting at the + // current minimum column). + if let Some(ori) = &mask[j * dims.x + i] { + let width = 1 + mask[j * dims.x + i + 1..j * dims.x + dims.x] + .iter() + .take_while(move |&mask| mask.as_ref() == Some(ori)) + .count(); + let max_x = i + width; + // Compute height (number of rows having w set x bits for this layer, starting + // at the current minimum column and row). + let height = 1 + + (j + 1..dims.y) + .take_while(|h| { + mask[h * dims.x + i..h * dims.x + max_x] + .iter() + .all(|mask| mask.as_ref() == Some(ori)) + }) + .count(); + let max_y = j + height; + // Add quad. + push_quads(Vec3::new(i, j, d), Vec2::new(width, height), ori); + // Unset mask bits in drawn region, so we don't try to re-draw them. + (j..max_y).for_each(|l| { + mask[l * dims.x + i..l * dims.x + max_x] + .iter_mut() + .for_each(|mask| { + *mask = None; + }); + }); + // Update x value. + i = max_x; + } else { + i += 1; + } + } + }); + }); +} + +fn add_to_atlas( + atlas: &mut guillotiere::SimpleAtlasAllocator, + todo_rects: &mut Vec, + pos: Vec3, + uv: Vec2>, + dim: Vec2, + norm: Vec3, + faces_forward: bool, + max_size: guillotiere::Size, + cur_size: &mut Vec2, +) -> Option { + // TODO: Check this conversion. + let atlas_rect; + loop { + // NOTE: Conversion to i32 is safe because he x, y, and z dimensions for any + // chunk index must fit in at least an i16 (lower for x and y, probably + // lower for z). + let res = atlas.allocate(guillotiere::Size::new(dim.x as i32 + 1, dim.y as i32 + 1)); + if let Some(atlas_rect_) = res { + atlas_rect = atlas_rect_; + break; + } + // Allocation failure. + let current_size = atlas.size(); + if current_size == max_size { + // NOTE: Currently, if we fail to allocate a terrain chunk in the atlas and we + // have already reached the maximum texture size, we choose to just skip the + // geometry and log a warning, rather than panicking or trying to use a fallback + // technique (e.g. a texture array). + // + // FIXME: Either make more robust, or explicitly document that limits on texture + // size need to be respected for terrain data (the OpenGL minimum requirement is + // 1024 × 1024, but in practice almost all computers support 4096 × 4096 or + // higher; see + // https://feedback.wildfiregames.com/report/opengl/feature/GL_MAX_TEXTURE_SIZE). + panic!( + "Could not add texture to atlas using simple allocator (pos={:?}, dim={:?});we \ + could not fit the whole model into a single texture on this machine + (max texture size={:?}, so we are discarding this rectangle.", + pos, dim, max_size + ); + } + // Otherwise, we haven't reached max size yet, so double the size (or reach the + // max texture size) and try again. + let new_size = guillotiere::Size::new( + max_size.width.min(current_size.width.saturating_mul(2)), + max_size.height.min(current_size.height.saturating_mul(2)), + ); + atlas.grow(new_size); + } + // NOTE: Conversion is correct because our initial max size for the atlas was + // a u16 and we never grew the atlas, meaning all valid coordinates within the + // atlas also fit into a u16. + *cur_size = Vec2::new( + cur_size.x.max(atlas_rect.max.x as u16), + cur_size.y.max(atlas_rect.max.y as u16), + ); + + // NOTE: pos can be converted safely from usize to i32 because all legal block + // coordinates in this chunk must fit in an i32 (actually we have the much + // stronger property that this holds across the whole map). + let norm = norm.map(i32::from); + todo_rects.push(( + pos.map(|e| e as i32) + if faces_forward { -norm } else { Vec3::zero() }, + uv, + atlas_rect, + if faces_forward { norm } else { -norm }, + )); + Some(atlas_rect) +} + +/// We deferred actually recording the colors within the rectangles in order to +/// generate a texture of minimal size; we now proceed to create and populate +/// it. +// TODO: Consider using the heavier interface (not the simple one) which seems +// to provide builtin support for what we're doing here. +// +// TODO: See if we can speed this up using SIMD. +fn draw_col_lights( + (col_lights, cur_size): &mut ColLightInfo, + data: &mut D, + todo_rects: Vec, + draw_delta: Vec3, + mut get_light: impl FnMut(&mut D, Vec3) -> f32, + mut get_color: impl FnMut(&mut D, Vec3) -> Rgb, + mut get_opacity: impl FnMut(&mut D, Vec3) -> bool, + mut make_col_light: impl FnMut(u8, Rgb) -> <::Surface as gfx::format::SurfaceTyped>::DataType, +) { + todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| { + // NOTE: Conversions are safe because width, height, and offset must be + // non-negative, and because every allocated coordinate in the atlas must be in + // bounds for the original size, max_texture_size, which fit into a u16. + let width = (rect.max.x - rect.min.x) as u16; + let height = (rect.max.y - rect.min.y) as u16; + let left = rect.min.x as u16; + let top = rect.min.y as u16; + let uv = uv.map(|e| e.map(i32::from)); + let pos = pos + draw_delta; + (0..height).for_each(|v| { + let start = usize::from(cur_size.x) * usize::from(top + v) + usize::from(left); + (0..width) + .zip(&mut col_lights[start..start + usize::from(width)]) + .for_each(|(u, col_light)| { + let pos = pos + uv.x * i32::from(u) + uv.y * i32::from(v); + // TODO: Consider optimizing to take advantage of the fact that this whole + // face should be facing nothing but air (this is not currently true, but + // could be if we used the right AO strategy). + // Each indirect light needs to come in through the direct light. + // Thus, we assign each light a score based on opacity (currently just 0 or + // 1, but it could support transluscent lights in the future). + // Thus, indirect_u_opacity and indirect_v_opacity are multiplied by + // direct_opacity, and indirect_uv_opacity is multiplied by + // the maximum of both of u and v's indirect opacities (since there are + // two choices for how to get to the direct surface). + let pos = pos + + if u + 1 == width { -uv.x } else { Vec3::zero() } + + if v + 1 == height { -uv.y } else { Vec3::zero() }; + let uv = Vec2::new( + if u + 1 == width { -uv.x } else { uv.x }, + if v + 1 == height { -uv.y } else { uv.y }, + ); + + let light_pos = pos + delta; + + // Currently, we assume that direct_opacity is 1 (if it's 0, you can't see + // the face anyway, since it's blocked by the block directly in front of it). + // TODO: If we add non-0/1 opacities, fix this. + // bottom-left block + let direct_u_opacity = get_opacity(data, light_pos - uv.x); + // top-right block + let direct_v_opacity = get_opacity(data, light_pos - uv.y); + + // NOTE: Since we only support 0/1 opacities currently, we asssume + // direct_opacity is 1, and the light value will be zero anyway for objects + // with opacity 0, we only "multiply" by indirect_uv_opacity for now (since + // it's the only one that could be 0 even if its light value is not). + // However, "spiritually" these light values are all being multiplied by + // their opacities. + let darkness = ( + // Light from the bottom-right-front block to this vertex always + // appears on this face, since it's the block this face is facing (so + // it can't be blocked by anything). + get_light(data, light_pos) + + get_light(data, light_pos - uv.x) + + get_light(data, light_pos - uv.y) + + if direct_u_opacity || direct_v_opacity { + get_light(data, light_pos - uv.x - uv.y) + } else { + 0.0 + } + ) / 4.0; + let col = get_color(data, pos); + let light = (darkness * 255.0) as u8; + *col_light = make_col_light(light, col); + }); + }); + }); +} + +/// Precondition: when this function is called, atlas_pos should reflect an +/// actual valid position in a texture atlas (meaning it should fit into a u16). +// TODO: See if we can speed a lot of this up using SIMD. +fn create_quad_greedy( + origin: Vec3, + dim: Vec2, + uv: Vec2>, + norm: Vec3, + faces_forward: bool, + meta: &M, + atlas_pos: guillotiere::Rectangle, + mut push_quad: impl FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), +) { + let origin = origin.map(|e| e as f32); + // NOTE: Conversion to f32 safe by function precondition (u16 can losslessly + // cast to f32, and dim fits in a u16). + let draw_dim = uv.map2(dim.map(|e| e as f32), |e, f| e.map(f32::from) * f); + let dim = Vec2::new(Vec2::new(dim.x as u16, 0), Vec2::new(0, dim.y as u16)); + let (draw_dim, dim, /* uv, */ norm) = if faces_forward { + (draw_dim, dim, norm) + } else { + ( + Vec2::new(draw_dim.y, draw_dim.x), + Vec2::new(dim.y, dim.x), + -norm, + ) + }; + let norm = norm.map(f32::from); + // NOTE: Conversion to u16 safe by function precondition. + let atlas_pos = Vec2::new(atlas_pos.min.x as u16, atlas_pos.min.y as u16); + push_quad(atlas_pos, dim, origin, draw_dim, norm, meta); +} + +pub fn create_quad( + atlas_pos: Vec2, + dim: Vec2>, + origin: Vec3, + draw_dim: Vec2>, + norm: Vec3, + meta: &M, + create_vertex: impl Fn(Vec2, Vec3, Vec3, &M) -> O::Vertex, +) -> Quad { + Quad::new( + create_vertex(atlas_pos, origin, norm, meta), + create_vertex(atlas_pos + dim.x, origin + draw_dim.x, norm, meta), + create_vertex( + atlas_pos + dim.x + dim.y, + origin + draw_dim.x + draw_dim.y, + norm, + meta, + ), + create_vertex(atlas_pos + dim.y, origin + draw_dim.y, norm, meta), + ) +} diff --git a/voxygen/src/mesh/mod.rs b/voxygen/src/mesh/mod.rs index 695c1e617b..657d635e32 100644 --- a/voxygen/src/mesh/mod.rs +++ b/voxygen/src/mesh/mod.rs @@ -1,17 +1,26 @@ +pub mod greedy; pub mod segment; pub mod terrain; -mod vol; use crate::render::{self, Mesh}; -pub trait Meshable<'a, P: render::Pipeline, T: render::Pipeline> { +pub type MeshGen = ( + Mesh<>::Pipeline>, + Mesh<>::TranslucentPipeline>, + Mesh<>::ShadowPipeline>, + >::Result, +); + +/// FIXME: Remove this whole trait at some point. This "abstraction" is never +/// abstracted over, and is organized completely differently from how we +/// actually mesh things nowadays. +pub trait Meshable { type Pipeline: render::Pipeline; type TranslucentPipeline: render::Pipeline; + type ShadowPipeline: render::Pipeline; type Supplement; + type Result; - // Generate meshes - one opaque, one translucent - fn generate_mesh( - &'a self, - supp: Self::Supplement, - ) -> (Mesh, Mesh); + // Generate meshes - one opaque, one translucent, one shadow + fn generate_mesh(self, supp: Self::Supplement) -> MeshGen; } diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index 2909c51f35..62ff06078b 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -1,87 +1,146 @@ use crate::{ - mesh::{vol, Meshable}, - render::{self, FigurePipeline, Mesh, ParticlePipeline, SpritePipeline}, + mesh::{ + greedy::{self, GreedyConfig, GreedyMesh}, + MeshGen, Meshable, + }, + render::{ + self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline, + TerrainPipeline, + }, + scene::math, }; use common::{ figure::Cell, - util::{linear_to_srgb, srgb_to_linear}, vol::{BaseVol, ReadVol, SizedVol, Vox}, }; +use core::ops::Range; use vek::*; -type FigureVertex = ::Vertex; type SpriteVertex = ::Vertex; +type TerrainVertex = ::Vertex; type ParticleVertex = ::Vertex; -impl<'a, V: 'a> Meshable<'a, FigurePipeline, FigurePipeline> for V +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, /* TODO: Use VolIterator instead of manually iterating * &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>, * &'a V: BaseVol, */ { - type Pipeline = FigurePipeline; - type Supplement = (Vec3, Vec3); + type Pipeline = TerrainPipeline; + /// NOTE: The result provides the (roughly) computed bounds for the model, + /// and the vertex range meshed for this model; we return this instead + /// of the full opaque mesh so we can avoid allocating a separate mesh + /// for each bone. + /// + /// Later, we can iterate through the bone array and correctly assign bone + /// ids to all vertices in range for each segment. + /// + /// FIXME: A refactor of the figure cache to not just return an array of + /// models (thus allowing us to knoe the bone index ahead of time) would + /// avoid needing per-bone information at all. + type Result = (math::Aabb, Range); + type ShadowPipeline = ShadowPipeline; + type Supplement = ( + &'b mut GreedyMesh<'a>, + &'b mut Mesh, + Vec3, + Vec3, + ); type TranslucentPipeline = FigurePipeline; - #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, - ) -> (Mesh, Mesh) { - let mut mesh = Mesh::new(); + self, + (greedy, opaque_mesh, offs, scale): Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 15); - let vol_iter = (self.lower_bound().x..self.upper_bound().x) - .map(|i| { - (self.lower_bound().y..self.upper_bound().y).map(move |j| { - (self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k)) - }) - }) - .flatten() - .flatten() - .map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty()))); + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + // NOTE: Figure sizes should be no more than 512 along each axis. + let greedy_size = upper_bound - lower_bound + 1; + assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); + let greedy_size_cross = greedy_size; + let draw_delta = lower_bound; - for (pos, vox) in vol_iter { - if let Some(col) = vox.get_color() { - vol::push_vox_verts( - &mut mesh, - faces_to_make(self, pos, true, |vox| vox.is_empty()), - offs + pos.map(|e| e as f32), - &[[[Rgba::from_opaque(col); 3]; 3]; 3], - |origin, norm, col, light, ao| { - FigureVertex::new( - origin * scale, - norm, - linear_to_srgb(srgb_to_linear(col) * light), - ao, - 0, - ) - }, - &{ - let mut ls = [[[None; 3]; 3]; 3]; - for x in 0..3 { - for y in 0..3 { - for z in 0..3 { - ls[z][y][x] = self - .get(pos + Vec3::new(x as i32, y as i32, z as i32) - 1) - .map(|v| v.is_empty()) - .unwrap_or(true) - .then_some(1.0); - } - } - } - ls - }, - ); + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 } - } + }; + let get_color = |vol: &mut V, pos: Vec3| { + vol.get(pos) + .ok() + .and_then(|vox| vox.get_color()) + .unwrap_or(Rgb::zero()) + }; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy(pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Vox::empty()) + }) + }; + let create_opaque = |atlas_pos, pos, norm| { + TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, 0) + }; - (mesh, Mesh::new()) + let start = opaque_mesh.vertices().len(); + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), + )); + }, + }); + let bounds = math::Aabb { + // NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16. + min: math::Vec3::from((lower_bound.as_::() + offs) * scale), + max: math::Vec3::from((upper_bound.as_::() + offs) * scale), + } + .made_valid(); + let vertex_range = start..opaque_mesh.vertices().len(); + + ( + Mesh::new(), + Mesh::new(), + Mesh::new(), + (bounds, vertex_range), + ) } } -impl<'a, V: 'a> Meshable<'a, SpritePipeline, SpritePipeline> for V +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, /* TODO: Use VolIterator instead of manually iterating @@ -89,66 +148,93 @@ where * &'a V: BaseVol, */ { type Pipeline = SpritePipeline; - type Supplement = (Vec3, Vec3); + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh, bool); type TranslucentPipeline = SpritePipeline; - #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, - ) -> (Mesh, Mesh) { - let mut mesh = Mesh::new(); + self, + (greedy, opaque_mesh, vertical_stripes): Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 16); - let vol_iter = (self.lower_bound().x..self.upper_bound().x) - .map(|i| { - (self.lower_bound().y..self.upper_bound().y).map(move |j| { - (self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k)) - }) - }) - .flatten() - .flatten() - .map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty()))); + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + let greedy_size = upper_bound - lower_bound + 1; + assert!( + greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, + "Sprite size out of bounds: {:?} ≤ (15, 15, 63)", + greedy_size - 1 + ); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); - for (pos, vox) in vol_iter { - if let Some(col) = vox.get_color() { - vol::push_vox_verts( - &mut mesh, - faces_to_make(self, pos, true, |vox| vox.is_empty()), - offs + pos.map(|e| e as f32), - &[[[Rgba::from_opaque(col); 3]; 3]; 3], - |origin, norm, col, light, ao| { - SpriteVertex::new( - origin * scale, - norm, - linear_to_srgb(srgb_to_linear(col) * light), - ao, - ) - }, - &{ - let mut ls = [[[None; 3]; 3]; 3]; - for x in 0..3 { - for y in 0..3 { - for z in 0..3 { - ls[z][y][x] = self - .get(pos + Vec3::new(x as i32, y as i32, z as i32) - 1) - .map(|v| v.is_empty()) - .unwrap_or(true) - .then_some(1.0); - } - } - } - ls - }, - ); + let greedy_size_cross = greedy_size; + let draw_delta = lower_bound; + + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 } - } + }; + let get_color = |vol: &mut V, pos: Vec3| { + vol.get(pos) + .ok() + .and_then(|vox| vox.get_color()) + .unwrap_or(Rgb::zero()) + }; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Vox::empty()) + }) + }; + let create_opaque = + |atlas_pos, pos: Vec3, norm, _meta| SpriteVertex::new(atlas_pos, pos, norm); - (mesh, Mesh::new()) + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), + )); + }, + }); + + (Mesh::new(), Mesh::new(), Mesh::new(), ()) } } -impl<'a, V: 'a> Meshable<'a, ParticlePipeline, ParticlePipeline> for V +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, /* TODO: Use VolIterator instead of manually iterating @@ -156,85 +242,127 @@ where * &'a V: BaseVol, */ { type Pipeline = ParticlePipeline; - type Supplement = (Vec3, Vec3); + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = &'b mut GreedyMesh<'a>; type TranslucentPipeline = ParticlePipeline; - #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, - ) -> (Mesh, Mesh) { - let mut mesh = Mesh::new(); + self, + greedy: Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 16); - let vol_iter = (self.lower_bound().x..self.upper_bound().x) - .map(|i| { - (self.lower_bound().y..self.upper_bound().y).map(move |j| { - (self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k)) - }) - }) - .flatten() - .flatten() - .map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty()))); + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + let greedy_size = upper_bound - lower_bound + 1; + assert!( + greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, + "Particle size out of bounds: {:?} ≤ (15, 15, 63)", + greedy_size - 1 + ); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); - for (pos, vox) in vol_iter { - if let Some(col) = vox.get_color() { - vol::push_vox_verts( - &mut mesh, - faces_to_make(self, pos, true, |vox| vox.is_empty()), - offs + pos.map(|e| e as f32), - &[[[Rgba::from_opaque(col); 3]; 3]; 3], - |origin, norm, col, light, ao| { - ParticleVertex::new( - origin * scale, - norm, - linear_to_srgb(srgb_to_linear(col) * light), - ao, - ) - }, - &{ - let mut ls = [[[None; 3]; 3]; 3]; - for x in 0..3 { - for y in 0..3 { - for z in 0..3 { - ls[z][y][x] = self - .get(pos + Vec3::new(x as i32, y as i32, z as i32) - 1) - .map(|v| v.is_empty()) - .unwrap_or(true) - .then_some(1.0); - } - } - } - ls - }, - ); + let greedy_size_cross = greedy_size; + let draw_delta = lower_bound; + + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 } - } + }; + let get_color = |vol: &mut V, pos: Vec3| { + vol.get(pos) + .ok() + .and_then(|vox| vox.get_color()) + .unwrap_or(Rgb::zero()) + }; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy(pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Vox::empty()) + }) + }; + let create_opaque = |_atlas_pos, pos: Vec3, norm| ParticleVertex::new(pos, norm); - (mesh, Mesh::new()) + let mut opaque_mesh = Mesh::new(); + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), + )); + }, + }); + + (opaque_mesh, Mesh::new(), Mesh::new(), ()) } } -/// Use the 6 voxels/blocks surrounding the one at the specified position -/// to detemine which faces should be drawn -fn faces_to_make( - seg: &V, +fn should_draw_greedy( pos: Vec3, - error_makes_face: bool, - should_add: impl Fn(&V::Vox) -> bool, -) -> [bool; 6] { - let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); - let make_face = |offset| { - seg.get(pos + offset) - .map(|v| should_add(v)) - .unwrap_or(error_makes_face) - }; - [ - make_face(-x), - make_face(x), - make_face(-y), - make_face(y), - make_face(-z), - make_face(z), - ] + delta: Vec3, + _uv: Vec2>, + flat_get: impl Fn(Vec3) -> Cell, +) -> Option<(bool, /* u8 */ ())> { + let from = flat_get(pos - delta); + let to = flat_get(pos); + let from_opaque = !from.is_empty(); + if from_opaque != to.is_empty() { + None + } else { + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. + Some((from_opaque, ())) + } +} + +fn should_draw_greedy_ao( + vertical_stripes: bool, + pos: Vec3, + delta: Vec3, + _uv: Vec2>, + flat_get: impl Fn(Vec3) -> Cell, +) -> Option<(bool, bool)> { + let from = flat_get(pos - delta); + let to = flat_get(pos); + let from_opaque = !from.is_empty(); + if from_opaque != to.is_empty() { + None + } else { + let faces_forward = from_opaque; + let ao = !vertical_stripes || (pos.z & 1) != 0; + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. + Some((faces_forward, ao)) + } } diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index b465565b71..4a7c008550 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -1,10 +1,13 @@ use crate::{ - mesh::{vol, Meshable}, - render::{self, FluidPipeline, Mesh, TerrainPipeline}, + mesh::{ + greedy::{self, GreedyConfig, GreedyMesh}, + MeshGen, Meshable, + }, + render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline}, }; use common::{ terrain::{Block, BlockKind}, - vol::{DefaultVolIterator, ReadVol, RectRasterableVol, Vox}, + vol::{ReadVol, RectRasterableVol, Vox}, volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d}, }; use std::{collections::VecDeque, fmt::Debug}; @@ -13,6 +16,16 @@ use vek::*; type TerrainVertex = ::Vertex; type FluidVertex = ::Vertex; +#[derive(Clone, Copy, PartialEq)] +enum FaceKind { + /// Opaque face that is facing something non-opaque; either + /// water (Opaque(true)) or something else (Opaque(false)). + Opaque(bool), + /// Fluid face that is facing something non-opaque, non-tangible, + /// and non-fluid (most likely air). + Fluid, +} + trait Blendable { fn is_blended(&self) -> bool; } @@ -27,7 +40,7 @@ impl Blendable for BlockKind { } const SUNLIGHT: u8 = 24; -const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; +const _MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; fn calc_light + ReadVol + Debug>( bounds: Aabb, @@ -209,10 +222,12 @@ fn calc_light + ReadVol + Debug>( } impl<'a, V: RectRasterableVol + ReadVol + Debug> - Meshable<'a, TerrainPipeline, FluidPipeline> for VolGrid2d + Meshable for &'a VolGrid2d { type Pipeline = TerrainPipeline; - type Supplement = Aabb; + type Result = (Aabb, ColLightInfo); + type ShadowPipeline = ShadowPipeline; + type Supplement = (Aabb, Vec2); type TranslucentPipeline = FluidPipeline; #[allow(clippy::collapsible_if)] @@ -222,13 +237,14 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> #[allow(clippy::panic_params)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - range: Self::Supplement, - ) -> (Mesh, Mesh) { + self, + (range, max_texture_size): Self::Supplement, + ) -> MeshGen { // Find blocks that should glow - let lit_blocks = - DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) - .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); + // FIXME: Replace with real lit blocks when we actually have blocks that glow. + let lit_blocks = core::iter::empty(); + /* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) + .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */ // Calculate chunk lighting let mut light = calc_light(range, self, lit_blocks); @@ -278,7 +294,7 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> let z = z + 1; match flat.get((x * h * d + y * d + z) as usize).copied() { Some(b) => b, - None => panic!("x {} y {} z {} d {} h {}"), + None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), } } }; @@ -310,247 +326,121 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> } .min(range.size().d - 1); - // // We use multiple meshes and then combine them later such that we can group - // similar z // levels together (better rendering performance) - // let mut opaque_meshes = vec![Mesh::new(); ((z_end + 1 - z_start).clamped(1, - // 60) as usize / 10).max(1)]; + let max_size = + guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y)); + let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1); + // NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5 + // + 14). FIXME: Make this function fallible, since the terrain + // information might be dynamically generated which would make this hard + // to enforce. + assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384); + // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, + // which always fits into a f32. + let max_bounds: Vec3 = greedy_size.as_::(); + // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, + // which always fits into a usize. + let greedy_size = greedy_size.as_::(); + let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z); + let draw_delta = Vec3::new(1, 1, z_start); + + let get_light = |_: &mut (), pos: Vec3| light(pos + range.min); + let get_color = + |_: &mut (), pos: Vec3| flat_get(pos).get_color().unwrap_or(Rgb::zero()); + let get_opacity = |_: &mut (), pos: Vec3| !flat_get(pos).is_opaque(); + let flat_get = |pos| flat_get(pos); + let should_draw = |_: &mut (), pos: Vec3, delta: Vec3, _uv| { + should_draw_greedy(pos, delta, flat_get) + }; + // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. + let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32); + let create_opaque = |atlas_pos, pos, norm, meta| { + TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta) + }; + let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm); + + let mut greedy = GreedyMesh::new(max_size); let mut opaque_mesh = Mesh::new(); let mut fluid_mesh = Mesh::new(); + greedy.push(GreedyConfig { + data: (), + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta { + FaceKind::Opaque(meta) => { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), + )); + }, + FaceKind::Fluid => { + fluid_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + &(), + |atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm), + )); + }, + }, + }); - for x in 1..range.size().w - 1 { - for y in 1..range.size().w - 1 { - let mut blocks = [[[None; 3]; 3]; 3]; - for i in 0..3 { - for j in 0..3 { - for k in 0..3 { - blocks[k][j][i] = Some(flat_get( - Vec3::new(x, y, z_start) + Vec3::new(i as i32, j as i32, k as i32) - - 1, - )); - } - } - } + let min_bounds = mesh_delta; + let bounds = Aabb { + min: min_bounds, + max: max_bounds + min_bounds, + }; + let (col_lights, col_lights_size) = greedy.finalize(); - let mut lights = [[[None; 3]; 3]; 3]; - for i in 0..3 { - for j in 0..3 { - for k in 0..3 { - lights[k][j][i] = if blocks[k][j][i] - .map(|block| block.is_opaque()) - .unwrap_or(false) - { - None - } else { - Some(light( - Vec3::new( - x + range.min.x, - y + range.min.y, - z_start + range.min.z, - ) + Vec3::new(i as i32, j as i32, k as i32) - - 1, - )) - }; - } - } - } - - let get_color = |maybe_block: Option<&Block>, neighbour: bool| { - maybe_block - .filter(|vox| vox.is_opaque() && (!neighbour || vox.is_blended())) - .and_then(|vox| vox.get_color()) - .map(Rgba::from_opaque) - .unwrap_or(Rgba::zero()) - }; - - for z in z_start..z_end + 1 { - let pos = Vec3::new(x, y, z); - let offs = (pos - Vec3::new(1, 1, -range.min.z)).map(|e| e as f32); - - lights[0] = lights[1]; - lights[1] = lights[2]; - blocks[0] = blocks[1]; - blocks[1] = blocks[2]; - - for i in 0..3 { - for j in 0..3 { - let block = Some(flat_get(pos + Vec3::new(i as i32, j as i32, 2) - 1)); - blocks[2][j][i] = block; - } - } - for i in 0..3 { - for j in 0..3 { - lights[2][j][i] = if blocks[2][j][i] - .map(|block| block.is_opaque()) - .unwrap_or(false) - { - None - } else { - Some(light( - pos + range.min + Vec3::new(i as i32, j as i32, 2) - 1, - )) - }; - } - } - - let block = blocks[1][1][1]; - let colors = if block.map_or(false, |vox| vox.is_blended()) { - let mut colors = [[[Rgba::zero(); 3]; 3]; 3]; - for i in 0..3 { - for j in 0..3 { - for k in 0..3 { - colors[i][j][k] = get_color( - blocks[i][j][k].as_ref(), - i != 1 || j != 1 || k != 1, - ) - } - } - } - colors - } else { - [[[get_color(blocks[1][1][1].as_ref(), false); 3]; 3]; 3] - }; - - // let opaque_mesh_index = ((z - z_start) * opaque_meshes.len() as i32 / (z_end - // + 1 - z_start).max(1)) as usize; let selected_opaque_mesh - // = &mut opaque_meshes[opaque_mesh_index]; Create mesh - // polygons - if block.map_or(false, |vox| vox.is_opaque()) { - vol::push_vox_verts( - &mut opaque_mesh, //selected_opaque_mesh, - faces_to_make(&blocks, false, |vox| !vox.is_opaque()), - offs, - &colors, - |pos, norm, col, light, ao| { - //let light = (light.min(ao) * 255.0) as u32; - let light = (light * 255.0) as u32; - let ao = (ao * 255.0) as u32; - let norm = if norm.x != 0.0 { - if norm.x < 0.0 { 0 } else { 1 } - } else if norm.y != 0.0 { - if norm.y < 0.0 { 2 } else { 3 } - } else { - if norm.z < 0.0 { 4 } else { 5 } - }; - TerrainVertex::new(norm, light, ao, pos, col) - }, - &lights, - ); - } else if block.map_or(false, |vox| vox.is_fluid()) { - vol::push_vox_verts( - &mut fluid_mesh, - faces_to_make(&blocks, false, |vox| vox.is_air()), - offs, - &colors, - |pos, norm, col, light, _ao| { - FluidVertex::new(pos, norm, col, light, 0.3) - }, - &lights, - ); - } - } - } - } - - // let opaque_mesh = opaque_meshes - // .into_iter() - // .rev() - // .fold(Mesh::new(), |mut opaque_mesh, m: Mesh| { - // m.verts().chunks_exact(3).rev().for_each(|vs| { - // opaque_mesh.push(vs[0]); - // opaque_mesh.push(vs[1]); - // opaque_mesh.push(vs[2]); - // }); - // opaque_mesh - // }); - - (opaque_mesh, fluid_mesh) + ( + opaque_mesh, + fluid_mesh, + Mesh::new(), + (bounds, (col_lights, col_lights_size)), + ) } } -/// Use the 6 voxels/blocks surrounding the center -/// to detemine which faces should be drawn -/// Unlike the one in segments.rs this uses a provided array of blocks instead -/// of retrieving from a volume -/// blocks[z][y][x] -fn faces_to_make( - blocks: &[[[Option; 3]; 3]; 3], - error_makes_face: bool, - should_add: impl Fn(Block) -> bool, -) -> [bool; 6] { - // Faces to draw - let make_face = |opt_v: Option| opt_v.map(|v| should_add(v)).unwrap_or(error_makes_face); - [ - make_face(blocks[1][1][0]), - make_face(blocks[1][1][2]), - make_face(blocks[1][0][1]), - make_face(blocks[1][2][1]), - make_face(blocks[0][1][1]), - make_face(blocks[2][1][1]), - ] -} - -/* -impl + ReadVol + Debug> Meshable for VolGrid3d { - type Pipeline = TerrainPipeline; - type Supplement = Aabb; - - fn generate_mesh(&self, range: Self::Supplement) -> Mesh { - let mut mesh = Mesh::new(); - - let mut last_chunk_pos = self.pos_key(range.min); - let mut last_chunk = self.get_key(last_chunk_pos); - - let size = range.max - range.min; - for x in 1..size.x - 1 { - for y in 1..size.y - 1 { - for z in 1..size.z - 1 { - let pos = Vec3::new(x, y, z); - - let new_chunk_pos = self.pos_key(range.min + pos); - if last_chunk_pos != new_chunk_pos { - last_chunk = self.get_key(new_chunk_pos); - last_chunk_pos = new_chunk_pos; - } - let offs = pos.map(|e| e as f32 - 1.0); - if let Some(chunk) = last_chunk { - let chunk_pos = Self::chunk_offs(range.min + pos); - if let Some(col) = chunk.get(chunk_pos).ok().and_then(|vox| vox.get_color()) - { - let col = col.map(|e| e as f32 / 255.0); - - vol::push_vox_verts( - &mut mesh, - self, - range.min + pos, - offs, - col, - TerrainVertex::new, - false, - ); - } - } else { - if let Some(col) = self - .get(range.min + pos) - .ok() - .and_then(|vox| vox.get_color()) - { - let col = col.map(|e| e as f32 / 255.0); - - vol::push_vox_verts( - &mut mesh, - self, - range.min + pos, - offs, - col, - TerrainVertex::new, - false, - ); - } - } - } - } +fn should_draw_greedy( + pos: Vec3, + delta: Vec3, + flat_get: impl Fn(Vec3) -> Block, +) -> Option<(bool, FaceKind)> { + let from = flat_get(pos - delta); + let to = flat_get(pos); + let from_opaque = from.is_opaque(); + if from_opaque == to.is_opaque() { + // Check the interface of fluid and non-tangible non-fluids (e.g. air). + let from_fluid = from.is_fluid(); + if from_fluid == to.is_fluid() || from.is_tangible() || to.is_tangible() { + None + } else { + // While fluid is not culled, we still try to keep a consistent orientation as + // we do for land; if going from fluid to non-fluid, + // forwards-facing; otherwise, backwards-facing. + Some((from_fluid, FaceKind::Fluid)) } - mesh + } else { + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. Also, if either from or to is fluid, set the meta accordingly. + Some(( + from_opaque, + FaceKind::Opaque(if from_opaque { + to.is_fluid() + } else { + from.is_fluid() + }), + )) } } -*/ diff --git a/voxygen/src/mesh/vol.rs b/voxygen/src/mesh/vol.rs deleted file mode 100644 index d0ff5af21a..0000000000 --- a/voxygen/src/mesh/vol.rs +++ /dev/null @@ -1,224 +0,0 @@ -use vek::*; - -use crate::render::{ - mesh::{Mesh, Quad}, - Pipeline, -}; - -/// Given volume, position, and cardinal directions, compute each vertex's AO -/// value. `dirs` should be a slice of length 5 so that the sliding window of -/// size 2 over the slice yields each vertex' adjacent positions. -#[allow(unsafe_code)] -fn get_ao_quad( - shift: Vec3, - dirs: &[Vec3], - darknesses: &[[[Option; 3]; 3]; 3], -) -> Vec4<(f32, f32)> { - dirs.windows(2) - .map(|offs| { - let vox_opaque = |pos: Vec3| { - let pos = (pos + 1).map(|e| e as usize); - darknesses[pos.z][pos.y][pos.x].is_none() - }; - - let (s1, s2) = ( - vox_opaque(shift + offs[0]), - vox_opaque(shift + offs[1]), - /* - vol.get(pos + shift + offs[0]) - .map(&is_opaque) - .unwrap_or(false), - vol.get(pos + shift + offs[1]) - .map(&is_opaque) - .unwrap_or(false), - */ - ); - - let mut darkness = 0.0; - let mut total = 0.0f32; - for x in 0..2 { - for y in 0..2 { - let dark_pos = shift + offs[0] * x + offs[1] * y + 1; - if let Some(dark) = - darknesses[dark_pos.z as usize][dark_pos.y as usize][dark_pos.x as usize] - { - darkness += dark; - total += 1.0; - } - } - } - let darkness = darkness / total.max(1.0); - - ( - darkness, - if s1 && s2 { - 0.0 - } else { - let corner = vox_opaque(shift + offs[0] + offs[1]); - // Map both 1 and 2 neighbors to 0.5 occlusion. - if s1 || s2 || corner { 0.4 } else { 1.0 } - }, - ) - }) - .collect::>() -} - -#[allow(unsafe_code)] -fn get_col_quad(dirs: &[Vec3], cols: &[[[Rgba; 3]; 3]; 3]) -> Vec4> { - dirs.windows(2) - .map(|offs| { - let primary_col = Rgb::from(cols[1][1][1]).map(|e: u8| e as f32); - let mut color = Rgb::zero(); - let mut total = 0.0; - for x in 0..2 { - for y in 0..2 { - let col_pos = offs[0] * x + offs[1] * y + 1; - let col = unsafe { - cols.get_unchecked(col_pos.z as usize) - .get_unchecked(col_pos.y as usize) - .get_unchecked(col_pos.x as usize) - }; - if col.a > 0 { - let col = Rgb::new(col.r, col.g, col.b).map(|e| e as f32); - if Vec3::::from(primary_col).distance_squared(Vec3::from(col)) - < (0.025f32 * 256.0).powf(2.0) - { - color += col; - total += 256.0; - } - } - } - } - - color / total - }) - .collect() -} - -// Utility function -fn create_quad, Vec3, Rgb, f32, f32) -> P::Vertex>( - origin: Vec3, - unit_x: Vec3, - unit_y: Vec3, - norm: Vec3, - cols: Vec4>, - darkness_ao: Vec4<(f32, f32)>, - vcons: &F, -) -> Quad

{ - let darkness = darkness_ao.map(|e| e.0); - let ao = darkness_ao.map(|e| e.1); - - let ao_map = ao; - - if ao[0].min(ao[2]) < ao[1].min(ao[3]) { - Quad::new( - vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3]), - vcons(origin, norm, cols[0], darkness[0], ao_map[0]), - vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1]), - vcons( - origin + unit_x + unit_y, - norm, - cols[2], - darkness[2], - ao_map[2], - ), - ) - } else { - Quad::new( - vcons(origin, norm, cols[0], darkness[0], ao_map[0]), - vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1]), - vcons( - origin + unit_x + unit_y, - norm, - cols[2], - darkness[2], - ao_map[2], - ), - vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3]), - ) - } -} - -pub fn push_vox_verts( - mesh: &mut Mesh

, - faces: [bool; 6], - offs: Vec3, - cols: &[[[Rgba; 3]; 3]; 3], - vcons: impl Fn(Vec3, Vec3, Rgb, f32, f32) -> P::Vertex, - darknesses: &[[[Option; 3]; 3]; 3], -) { - let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); - - // -x - if faces[0] { - mesh.push_quad(create_quad( - offs, - Vec3::unit_z(), - Vec3::unit_y(), - -Vec3::unit_x(), - get_col_quad(&[-z, -y, z, y, -z], cols), - get_ao_quad(-Vec3::unit_x(), &[-z, -y, z, y, -z], darknesses), - &vcons, - )); - } - // +x - if faces[1] { - mesh.push_quad(create_quad( - offs + Vec3::unit_x(), - Vec3::unit_y(), - Vec3::unit_z(), - Vec3::unit_x(), - get_col_quad(&[-y, -z, y, z, -y], cols), - get_ao_quad(Vec3::unit_x(), &[-y, -z, y, z, -y], darknesses), - &vcons, - )); - } - // -y - if faces[2] { - mesh.push_quad(create_quad( - offs, - Vec3::unit_x(), - Vec3::unit_z(), - -Vec3::unit_y(), - get_col_quad(&[-x, -z, x, z, -x], cols), - get_ao_quad(-Vec3::unit_y(), &[-x, -z, x, z, -x], darknesses), - &vcons, - )); - } - // +y - if faces[3] { - mesh.push_quad(create_quad( - offs + Vec3::unit_y(), - Vec3::unit_z(), - Vec3::unit_x(), - Vec3::unit_y(), - get_col_quad(&[-z, -x, z, x, -z], cols), - get_ao_quad(Vec3::unit_y(), &[-z, -x, z, x, -z], darknesses), - &vcons, - )); - } - // -z - if faces[4] { - mesh.push_quad(create_quad( - offs, - Vec3::unit_y(), - Vec3::unit_x(), - -Vec3::unit_z(), - get_col_quad(&[-y, -x, y, x, -y], cols), - get_ao_quad(-Vec3::unit_z(), &[-y, -x, y, x, -y], darknesses), - &vcons, - )); - } - // +z - if faces[5] { - mesh.push_quad(create_quad( - offs + Vec3::unit_z(), - Vec3::unit_x(), - Vec3::unit_y(), - Vec3::unit_z(), - get_col_quad(&[-x, -y, x, y, -x], cols), - get_ao_quad(Vec3::unit_z(), &[-x, -y, x, y, -x], darknesses), - &vcons, - )); - } -} diff --git a/voxygen/src/render/consts.rs b/voxygen/src/render/consts.rs index effe76e8c0..0eded13428 100644 --- a/voxygen/src/render/consts.rs +++ b/voxygen/src/render/consts.rs @@ -23,9 +23,14 @@ impl Consts { &mut self, encoder: &mut gfx::Encoder, vals: &[T], + offset: usize, ) -> Result<(), RenderError> { - encoder - .update_buffer(&self.buf, vals, 0) - .map_err(RenderError::UpdateError) + if vals.is_empty() { + Ok(()) + } else { + encoder + .update_buffer(&self.buf, vals, offset) + .map_err(RenderError::UpdateError) + } } } diff --git a/voxygen/src/render/error.rs b/voxygen/src/render/error.rs index a0437c4794..8c9352857d 100644 --- a/voxygen/src/render/error.rs +++ b/voxygen/src/render/error.rs @@ -10,6 +10,7 @@ pub enum RenderError { IncludeError(glsl_include::Error), MappingError(gfx::mapping::Error), CopyError(gfx::CopyError<[u16; 3], usize>), + CustomError(String), } impl From> for RenderError { diff --git a/voxygen/src/render/instances.rs b/voxygen/src/render/instances.rs index c99be74ffa..c53d5ee2c2 100644 --- a/voxygen/src/render/instances.rs +++ b/voxygen/src/render/instances.rs @@ -15,7 +15,7 @@ impl Instances { pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result { Ok(Self { ibuf: factory - .create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::TRANSFER_DST) + .create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::empty()) .map_err(RenderError::BufferCreationError)?, }) } diff --git a/voxygen/src/render/mesh.rs b/voxygen/src/render/mesh.rs index 6b345505bb..4ddbe65843 100644 --- a/voxygen/src/render/mesh.rs +++ b/voxygen/src/render/mesh.rs @@ -1,4 +1,5 @@ use super::Pipeline; +use core::{iter::FromIterator, ops::Range}; /// A `Vec`-based mesh structure used to store mesh data on the CPU. pub struct Mesh { @@ -19,7 +20,7 @@ where impl Mesh

{ /// Create a new `Mesh`. #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { Self { verts: vec![] } } + pub fn new() -> Self { Self { verts: Vec::new() } } /// Clear vertices, allows reusing allocated memory of the underlying Vec. pub fn clear(&mut self) { self.verts.clear(); } @@ -68,6 +69,11 @@ impl Mesh

{ } pub fn iter(&self) -> std::slice::Iter { self.verts.iter() } + + /// NOTE: Panics if vertex_range is out of bounds of vertices. + pub fn iter_mut(&mut self, vertex_range: Range) -> std::slice::IterMut { + self.verts[vertex_range].iter_mut() + } } impl IntoIterator for Mesh

{ @@ -77,6 +83,24 @@ impl IntoIterator for Mesh

{ fn into_iter(self) -> Self::IntoIter { self.verts.into_iter() } } +impl FromIterator> for Mesh

{ + fn from_iter>>(tris: I) -> Self { + tris.into_iter().fold(Self::new(), |mut this, tri| { + this.push_tri(tri); + this + }) + } +} + +impl FromIterator> for Mesh

{ + fn from_iter>>(quads: I) -> Self { + quads.into_iter().fold(Self::new(), |mut this, quad| { + this.push_quad(quad); + this + }) + } +} + /// Represents a triangle stored on the CPU. pub struct Tri { a: P::Vertex, @@ -100,4 +124,18 @@ impl Quad

{ pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex, d: P::Vertex) -> Self { Self { a, b, c, d } } + + pub fn rotated_by(self, n: usize) -> Self + where + P::Vertex: Clone, + { + let verts = [self.a, self.b, self.c, self.d]; + + Self { + a: verts[n % 4].clone(), + b: verts[(1 + n) % 4].clone(), + c: verts[(2 + n) % 4].clone(), + d: verts[(3 + n) % 4].clone(), + } + } } diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 317ba97db6..fce129283f 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -16,24 +16,33 @@ pub use self::{ mesh::{Mesh, Quad, Tri}, model::{DynamicModel, Model}, pipelines::{ - figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals}, + figure::{ + BoneData as FigureBoneData, BoneMeshes, FigureModel, FigurePipeline, + Locals as FigureLocals, + }, fluid::FluidPipeline, + lod_terrain::{Locals as LodTerrainLocals, LodData, LodTerrainPipeline}, particle::{Instance as ParticleInstance, ParticlePipeline}, postprocess::{ create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline, }, + shadow::{Locals as ShadowLocals, ShadowPipeline}, skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline}, - sprite::{Instance as SpriteInstance, SpritePipeline}, + sprite::{Instance as SpriteInstance, Locals as SpriteLocals, SpritePipeline}, terrain::{Locals as TerrainLocals, TerrainPipeline}, ui::{ create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals, Mode as UiMode, UiPipeline, }, - Globals, Light, Shadow, + GlobalModel, Globals, Light, Shadow, + }, + renderer::{ + ColLightFmt, ColLightInfo, LodAltFmt, LodColorFmt, LodTextureFmt, Renderer, + ShadowDepthStencilFmt, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt, }, - renderer::{Renderer, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt}, texture::Texture, }; +pub use gfx::texture::{FilterMethod, WrapMode}; #[cfg(feature = "gl")] use gfx_device_gl as gfx_backend; @@ -55,26 +64,201 @@ pub trait Pipeline { use serde_derive::{Deserialize, Serialize}; /// Anti-aliasing modes -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum AaMode { None, + /// Fast approximate antialiasing. + /// + /// This is a screen-space technique, and therefore works fine with greedy + /// meshing. Fxaa, + /// Multisampling AA, up to 4 samples per pixel. + /// + /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will + /// also struggle in the futrue with deferred shading, so they may be + /// removed in the future. MsaaX4, + /// Multisampling AA, up to 8 samples per pixel. + /// + /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will + /// also struggle in the futrue with deferred shading, so they may be + /// removed in the future. MsaaX8, + /// Multisampling AA, up to 16 samples per pixel. + /// + /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will + /// also struggle in the futrue with deferred shading, so they may be + /// removed in the future. MsaaX16, + /// Super-sampling antialiasing, 4 samples per pixel. + /// + /// Unlike MSAA, SSAA *always* performs 4 samples per pixel, rather than + /// trying to choose importance samples at boundary regions, so it works + /// much better with techniques like deferred rendering and greedy + /// meshing that (without significantly more work) invalidate the + /// GPU's assumptions about importance sampling. SsaaX4, } +impl Default for AaMode { + fn default() -> Self { AaMode::Fxaa } +} + /// Cloud modes -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum CloudMode { + /// No clouds. On computers that can't handle loops well, have performance + /// issues in fragment shaders in general, or just have large + /// resolutions, this can be a *very* impactful performance difference. + /// Part of that is because of inefficiencies in how we implement + /// regular clouds. It is still not all that cheap on low-end machines, due + /// to many calculations being performed that use relatively expensive + /// functions, and at some point I'd like to both optimize the regular + /// sky shader further and create an even cheaper option. None, + /// Volumetric clouds. This option can be *very* expensive on low-end + /// machines, to the point of making the game unusable, for several + /// reasons: + /// + /// - The volumetric clouds use raymarching, which will cause catastrophic + /// performance degradation on GPUs without good support for loops. There + /// is an attempt to minimize the impact of this using a z-range check, + /// but on some low-end GPUs (such as some integraetd graphics cards) this + /// test doesn't appear to be able to be predicted well at shader + /// invocation time. + /// - The cloud computations themselves are fairly involved, further + /// degrading performance. + /// - Although the sky shader is always drawn at the outer edges of the + /// skybox, the clouds themselves are supposed to be positioned much + /// lower, which means the depth check for the skybox incorrectly cuts off + /// clouds in some places. To compensate for these cases (e.g. where + /// terrain is occluded by clouds from above, and the camera is above the + /// clouds), we currently branch to see if we need to render the clouds in + /// *every* fragment shader. For machines that can't optimize the check, + /// this is absurdly expensive, so we should look at alternatives in the + /// future that player better with the GPU. Regular, } +impl Default for CloudMode { + fn default() -> Self { CloudMode::Regular } +} + /// Fluid modes -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum FluidMode { + /// "Cheap" water. This water implements no waves, no reflections, no + /// diffraction, and no light attenuation through water. As a result, + /// it can be much cheaper than shiny reflection. Cheap, + /// "Shiny" water. This water implements waves on the surfaces, some + /// attempt at reflections, and tries to compute accurate light + /// attenuation through water (this is what results in the + /// colors changing as you descend into deep water). + /// + /// Unfortunately, the way the engine is currently set up, calculating + /// accurate attenuation is a bit difficult; we use estimates from + /// horizon maps for the current water altitude, which can both be off + /// by up to (max_altitude / 255) meters, only has per-chunk horizontal + /// resolution, and cannot handle edge cases like horizontal water (e.g. + /// waterfalls) well. We are okay with the latter, and will try to fix + /// the former soon. + /// + /// Another issue is that we don't always know whether light is *blocked*, + /// which causes attenuation to be computed incorrectly; this can be + /// addressed by using shadow maps (at least for terrain). Shiny, } + +impl Default for FluidMode { + fn default() -> Self { FluidMode::Cheap } +} + +/// Lighting modes +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum LightingMode { + /// Ashikhmin-Shirley BRDF lighting model. Attempts to generate a + /// physically plausible (to some extent) lighting distribution. + /// + /// This mdoel may not work as well with purely directional lighting, and is + /// more expensive than the other models. + Ashikhmin, + /// Standard Blinn-Phong shading, combing Lambertian diffuse reflections and + /// specular highlights. + BlinnPhong, + /// Standard Lambertian lighting model, with only diffuse reflections. The + /// cheapest lighting model by a decent margin, but the performance + /// difference between it and Blinn-Phong will probably only be + /// significant on low-end machines that are bottlenecked on fragment + /// shading. + Lambertian, +} + +impl Default for LightingMode { + fn default() -> Self { LightingMode::BlinnPhong } +} + +/// Shadow map settings. +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub struct ShadowMapMode { + /// Multiple of default resolution (default, which is 1.0, is currently + /// the closest higher power of two above the length of the longest + /// diagonal of the screen resolution, but this may change). + pub resolution: f32, +} + +impl Default for ShadowMapMode { + fn default() -> Self { Self { resolution: 1.0 } } +} + +/// Shadow modes +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum ShadowMode { + /// No shadows at all. By far the cheapest option. + None, + /// Point shadows (draw circles under figures, up to a configured maximum; + /// also render LOD shadows using horizon maps). Can be expensive on + /// some machines, probably mostly due to horizon mapping; the point + /// shadows are not rendered too efficiently, but that can probably + /// be addressed later. + Cheap, + /// Shadow map (render the scene from each light source, and also renders + /// LOD shadows using horizon maps). + Map(ShadowMapMode), +} + +impl Default for ShadowMode { + fn default() -> Self { ShadowMode::Cheap } +} + +impl core::convert::TryFrom for ShadowMapMode { + type Error = (); + + /// Get the shadow map details if they exist. + fn try_from(value: ShadowMode) -> Result { + if let ShadowMode::Map(map) = value { + Ok(map) + } else { + Err(()) + } + } +} + +impl ShadowMode { + pub fn is_map(&self) -> bool { matches!(self, Self::Map(_)) } +} + +/// Render modes +#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)] +pub struct RenderMode { + #[serde(default)] + pub aa: AaMode, + #[serde(default)] + pub cloud: CloudMode, + #[serde(default)] + pub fluid: FluidMode, + #[serde(default)] + pub lighting: LightingMode, + #[serde(default)] + pub shadow: ShadowMode, +} diff --git a/voxygen/src/render/model.rs b/voxygen/src/render/model.rs index d994215d1d..119f314feb 100644 --- a/voxygen/src/render/model.rs +++ b/voxygen/src/render/model.rs @@ -22,6 +22,15 @@ impl Model

{ } pub fn vertex_range(&self) -> Range { self.vertex_range.clone() } + + /// Create a model with a slice of a portion of this model to send to the + /// renderer. + pub fn submodel(&self, vertex_range: Range) -> Model

{ + Model { + vbuf: self.vbuf.clone(), + vertex_range, + } + } } /// Represents a mesh on the GPU which can be updated dynamically. @@ -40,10 +49,10 @@ impl DynamicModel

{ /// Create a model with a slice of a portion of this model to send to the /// renderer. - pub fn submodel(&self, range: Range) -> Model

{ + pub fn submodel(&self, vertex_range: Range) -> Model

{ Model { vbuf: self.vbuf.clone(), - vertex_range: range.start as u32..range.end as u32, + vertex_range, } } diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index e0226d0aa0..03e071f667 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -1,36 +1,33 @@ use super::{ - super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + super::{Mesh, Model, Pipeline, TerrainPipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, }; +use crate::mesh::greedy::GreedyMesh; +use core::ops::Range; use gfx::{ self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, - gfx_pipeline_inner, gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + gfx_pipeline_inner, state::ColorMask, }; use vek::*; gfx_defines! { - vertex Vertex { - pos_norm: u32 = "v_pos_norm", - col: u32 = "v_col", - // BBBBBBAA - // B = Bone - // A = AO - ao_bone: u8 = "v_ao_bone", - } - constant Locals { model_mat: [[f32; 4]; 4] = "model_mat", model_col: [f32; 4] = "model_col", + atlas_offs: [i32; 4] = "atlas_offs", + model_pos: [f32; 3] = "model_pos", flags: u32 = "flags", } constant BoneData { bone_mat: [[f32; 4]; 4] = "bone_mat", + normals_mat: [[f32; 4]; 4] = "normals_mat", } pipeline pipe { - vbuf: gfx::VertexBuffer = (), + vbuf: gfx::VertexBuffer<::Vertex> = (), + // abuf: gfx::VertexBuffer<::Vertex> = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", @@ -38,72 +35,91 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Replace))), - } -} - -impl Vertex { - #[allow(clippy::collapsible_if)] - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32, bone_idx: u8) -> Self { - let norm_bits = if norm.x != 0.0 { - if norm.x < 0.0 { 0 } else { 1 } - } else if norm.y != 0.0 { - if norm.y < 0.0 { 2 } else { 3 } - } else { - if norm.z < 0.0 { 4 } else { 5 } - }; - Self { - pos_norm: pos - .map2(Vec3::new(0, 9, 18), |e, shift| { - (((e * 2.0 + 256.0) as u32) & 0x3FF) << shift - }) - .reduce_bitor() - | (norm_bits << 29), - col: col - .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift) - .reduce_bitor(), - ao_bone: (bone_idx << 2) | ((ao * 3.9999) as u8), - } - } - - pub fn with_bone_idx(mut self, bone_idx: u8) -> Self { - self.ao_bone = (self.ao_bone & 0b11) | (bone_idx << 2); - self + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Replace))), } } impl Locals { - pub fn new(model_mat: Mat4, col: Rgba, is_player: bool) -> Self { + pub fn new( + model_mat: anim::vek::Mat4, + col: Rgba, + pos: anim::vek::Vec3, + atlas_offs: Vec2, + is_player: bool, + ) -> Self { let mut flags = 0; flags |= is_player as u32; Self { model_mat: model_mat.into_col_arrays(), model_col: col.into_array(), + model_pos: pos.into_array(), + atlas_offs: Vec4::from(atlas_offs).into_array(), flags, } } } impl Default for Locals { - fn default() -> Self { Self::new(Mat4::identity(), Rgba::broadcast(1.0), false) } + fn default() -> Self { + Self::new( + anim::vek::Mat4::identity(), + Rgba::broadcast(1.0), + anim::vek::Vec3::default(), + Vec2::default(), + false, + ) + } } impl BoneData { - pub fn new(bone_mat: Mat4) -> Self { + pub fn new(bone_mat: anim::vek::Mat4, normals_mat: anim::vek::Mat4) -> Self { Self { bone_mat: bone_mat.into_col_arrays(), + normals_mat: normals_mat.into_col_arrays(), } } +} - pub fn default() -> Self { Self::new(Mat4::identity()) } +impl Default for BoneData { + fn default() -> Self { Self::new(anim::vek::Mat4::identity(), anim::vek::Mat4::identity()) } } pub struct FigurePipeline; impl Pipeline for FigurePipeline { - type Vertex = Vertex; + type Vertex = ::Vertex; } + +pub struct FigureModel { + pub opaque: Model, + /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different + * LOD levels. */ +} + +impl FigureModel { + /// Start a greedy mesh designed for figure bones. + pub fn make_greedy<'a>() -> GreedyMesh<'a> { + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + let max_size = guillotiere::Size::new((1 << 15) - 1, (1 << 15) - 1); + GreedyMesh::new(max_size) + } +} + +pub type BoneMeshes = (Mesh, (anim::vek::Aabb, Range)); diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs index 040a757a07..a871aa3c4a 100644 --- a/voxygen/src/render/pipelines/fluid.rs +++ b/voxygen/src/render/pipelines/fluid.rs @@ -1,19 +1,16 @@ use super::{ super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + shadow, Globals, Light, Shadow, }; use gfx::{ self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, - gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + gfx_vertex_struct_meta, state::ColorMask, }; -use std::ops::Mul; use vek::*; gfx_defines! { vertex Vertex { pos_norm: u32 = "v_pos_norm", - col_light: u32 = "v_col_light", } pipeline pipe { @@ -24,18 +21,28 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + noise: gfx::TextureSampler = "t_noise", waves: gfx::TextureSampler<[f32; 4]> = "t_waves", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_TEST,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_TEST,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } impl Vertex { #[allow(clippy::identity_op)] // TODO: Pending review in #587 #[allow(clippy::into_iter_on_ref)] // TODO: Pending review in #587 - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, light: f32, _opac: f32) -> Self { + pub fn new(pos: Vec3, norm: Vec3) -> Self { let (norm_axis, norm_dir) = norm .as_slice() .into_iter() @@ -52,12 +59,6 @@ impl Vertex { | ((pos.y as u32) & 0x003F) << 6 | (((pos.z + EXTRA_NEG_Z).max(0.0).min((1 << 17) as f32) as u32) & 0x1FFFF) << 12 | (norm_bits & 0x7) << 29, - col_light: 0 - | ((col.r.mul(200.0) as u32) & 0xFF) << 8 - | ((col.g.mul(200.0) as u32) & 0xFF) << 16 - | ((col.b.mul(200.0) as u32) & 0xFF) << 24 - | ((light.mul(255.0) as u32) & 0xFF) << 0, - //| ((opac.mul(0.4) as u32) & 0xFF) << 0, } } } diff --git a/voxygen/src/render/pipelines/lod_terrain.rs b/voxygen/src/render/pipelines/lod_terrain.rs new file mode 100644 index 0000000000..9003525dc5 --- /dev/null +++ b/voxygen/src/render/pipelines/lod_terrain.rs @@ -0,0 +1,117 @@ +use super::{ + super::{ + LodAltFmt, LodColorFmt, LodTextureFmt, Pipeline, Renderer, Texture, TgtColorFmt, + TgtDepthStencilFmt, + }, + Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, texture::SamplerInfo, +}; +use vek::*; + +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + } + + constant Locals { + nul: [f32; 4] = "nul", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + map: gfx::TextureSampler<[f32; 4]> = "t_map", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } +} + +impl Vertex { + pub fn new(pos: Vec2) -> Self { + Self { + pos: pos.into_array(), + } + } +} + +impl Locals { + pub fn default() -> Self { Self { nul: [0.0; 4] } } +} + +pub struct LodTerrainPipeline; + +impl Pipeline for LodTerrainPipeline { + type Vertex = Vertex; +} + +pub struct LodData { + pub map: Texture, + pub alt: Texture, + pub horizon: Texture, + pub tgt_detail: u32, +} + +impl LodData { + pub fn new( + renderer: &mut Renderer, + map_size: Vec2, + lod_base: &[u32], + lod_alt: &[u32], + lod_horizon: &[u32], + tgt_detail: u32, + border_color: gfx::texture::PackedColor, + ) -> Self { + let kind = gfx::texture::Kind::D2(map_size.x, map_size.y, gfx::texture::AaMode::Single); + let info = gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Border, + ); + Self { + map: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_base)], + SamplerInfo { + border: border_color, + ..info + }, + ) + .expect("Failed to generate map texture"), + alt: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_alt)], + SamplerInfo { + border: [0.0, 0.0, 0.0, 0.0].into(), + ..info + }, + ) + .expect("Failed to generate alt texture"), + horizon: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_horizon)], + SamplerInfo { + border: [1.0, 0.0, 1.0, 0.0].into(), + ..info + }, + ) + .expect("Failed to generate horizon texture"), + tgt_detail, + } + } +} diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 442b31be32..db6c64ed8b 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1,30 +1,48 @@ pub mod figure; pub mod fluid; +pub mod lod_terrain; pub mod particle; pub mod postprocess; +pub mod shadow; pub mod skybox; pub mod sprite; pub mod terrain; pub mod ui; +use super::Consts; use crate::scene::camera::CameraMode; use common::terrain::BlockKind; use gfx::{self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta}; use vek::*; +pub const MAX_POINT_LIGHT_COUNT: usize = 31; +pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; +pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; + gfx_defines! { constant Globals { view_mat: [[f32; 4]; 4] = "view_mat", proj_mat: [[f32; 4]; 4] = "proj_mat", all_mat: [[f32; 4]; 4] = "all_mat", cam_pos: [f32; 4] = "cam_pos", + focus_off: [f32; 4] = "focus_off", focus_pos: [f32; 4] = "focus_pos", - // TODO: Fix whatever alignment issue requires these uniforms to be aligned. + /// NOTE: view_distance.x is the horizontal view distance, view_distance.y is the LOD + /// detail, view_distance.z is the + /// minimum height over any land chunk (i.e. the sea level), and view_distance.w is the + /// maximum height over this minimum height. + /// + /// TODO: Fix whatever alignment issue requires these uniforms to be aligned. view_distance: [f32; 4] = "view_distance", time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64. + sun_dir: [f32; 4] = "sun_dir", + moon_dir: [f32; 4] = "moon_dir", tick: [f32; 4] = "tick", + /// x, y represent the resolution of the screen; + /// w, z represent the near and far planes of the shadow map. screen_res: [f32; 4] = "screen_res", light_shadow_count: [u32; 4] = "light_shadow_count", + shadow_proj_factors: [f32; 4] = "shadow_proj_factors", medium: [u32; 4] = "medium", select_pos: [i32; 4] = "select_pos", gamma: [f32; 4] = "gamma", @@ -52,11 +70,15 @@ impl Globals { cam_pos: Vec3, focus_pos: Vec3, view_distance: f32, + tgt_detail: f32, + map_bounds: Vec2, time_of_day: f64, tick: f64, screen_res: Vec2, + shadow_planes: Vec2, light_count: usize, shadow_count: usize, + directed_light_count: usize, medium: BlockKind, select_pos: Option>, gamma: f32, @@ -68,12 +90,32 @@ impl Globals { proj_mat: proj_mat.into_col_arrays(), all_mat: (proj_mat * view_mat).into_col_arrays(), cam_pos: Vec4::from(cam_pos).into_array(), - focus_pos: Vec4::from(focus_pos).into_array(), - view_distance: [view_distance; 4], + focus_off: Vec4::from(focus_pos).map(|e: f32| e.trunc()).into_array(), + focus_pos: Vec4::from(focus_pos).map(|e: f32| e.fract()).into_array(), + view_distance: [view_distance, tgt_detail, map_bounds.x, map_bounds.y], time_of_day: [time_of_day as f32; 4], + sun_dir: Vec4::from_direction(Self::get_sun_dir(time_of_day)).into_array(), + moon_dir: Vec4::from_direction(Self::get_moon_dir(time_of_day)).into_array(), tick: [tick as f32; 4], - screen_res: Vec4::from(screen_res.map(|e| e as f32)).into_array(), - light_shadow_count: [light_count as u32, shadow_count as u32, 0, 0], + // Provide the shadow map far plane as well. + screen_res: [ + screen_res.x as f32, + screen_res.y as f32, + shadow_planes.x, + shadow_planes.y, + ], + light_shadow_count: [ + (light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32, + (shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32, + (directed_light_count % (MAX_DIRECTED_LIGHT_COUNT + 1)) as u32, + 0, + ], + shadow_proj_factors: [ + (shadow_planes.y + shadow_planes.x) / (shadow_planes.y - shadow_planes.x), + (2.0 * shadow_planes.y * shadow_planes.x) / (shadow_planes.y - shadow_planes.x), + 0.0, + 0.0, + ], medium: [if medium.is_fluid() { 1 } else { 0 }; 4], select_pos: select_pos .map(|sp| Vec4::from(sp) + Vec4::unit_w()) @@ -84,6 +126,21 @@ impl Globals { sprite_render_distance, } } + + fn get_angle_rad(time_of_day: f64) -> f32 { + const TIME_FACTOR: f32 = (std::f32::consts::PI * 2.0) / (3600.0 * 24.0); + time_of_day as f32 * TIME_FACTOR + } + + pub fn get_sun_dir(time_of_day: f64) -> Vec3 { + let angle_rad = Self::get_angle_rad(time_of_day); + Vec3::new(angle_rad.sin(), 0.0, angle_rad.cos()) + } + + pub fn get_moon_dir(time_of_day: f64) -> Vec3 { + let angle_rad = Self::get_angle_rad(time_of_day); + -Vec3::new(angle_rad.sin(), 0.0, angle_rad.cos() - 0.5).normalized() + } } impl Default for Globals { @@ -94,9 +151,13 @@ impl Default for Globals { Vec3::zero(), Vec3::zero(), 0.0, + 100.0, + Vec2::new(140.0, 2048.0), 0.0, 0.0, Vec2::new(800, 500), + Vec2::new(1.0, 25.0), + 0, 0, 0, BlockKind::Air, @@ -143,3 +204,11 @@ impl Shadow { impl Default for Shadow { fn default() -> Self { Self::new(Vec3::zero(), 0.0) } } + +// Global scene data spread across several arrays. +pub struct GlobalModel { + pub globals: Consts, + pub lights: Consts, + pub shadows: Consts, + pub shadow_mats: Consts, +} diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 616e3158c4..b2af4921f2 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -1,11 +1,10 @@ use super::{ super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + shadow, Globals, Light, Shadow, }; use gfx::{ self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, - gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + gfx_vertex_struct_meta, state::ColorMask, }; use vek::*; @@ -13,7 +12,7 @@ gfx_defines! { vertex Vertex { pos: [f32; 3] = "v_pos", // ____BBBBBBBBGGGGGGGGRRRRRRRR - col: u32 = "v_col", + // col: u32 = "v_col", // ...AANNN // A = AO // N = Normal @@ -54,16 +53,26 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } impl Vertex { #[allow(clippy::collapsible_if)] - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32) -> Self { + pub fn new(pos: Vec3, norm: Vec3) -> Self { let norm_bits = if norm.x != 0.0 { if norm.x < 0.0 { 0 } else { 1 } } else if norm.y != 0.0 { @@ -74,10 +83,7 @@ impl Vertex { Self { pos: pos.into_array(), - col: col - .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift) - .reduce_bitor(), - norm_ao: norm_bits | (((ao * 3.9999) as u32) << 3), + norm_ao: norm_bits, } } } diff --git a/voxygen/src/render/pipelines/shadow.rs b/voxygen/src/render/pipelines/shadow.rs new file mode 100644 index 0000000000..cee7a2fd1e --- /dev/null +++ b/voxygen/src/render/pipelines/shadow.rs @@ -0,0 +1,90 @@ +use super::{ + super::{ + ColLightFmt, ColLightInfo, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt, + TerrainLocals, Texture, + }, + figure, terrain, Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, +}; +use vek::*; + +gfx_defines! { + constant Locals { + shadow_matrices: [[f32; 4]; 4] = "shadowMatrices", + texture_mats: [[f32; 4]; 4] = "texture_mat", + } + + pipeline pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + } + + pipeline figure_pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + bones: gfx::ConstantBuffer = "u_bones", + globals: gfx::ConstantBuffer = "u_globals", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + } +} + +impl Locals { + pub fn new(shadow_mat: Mat4, texture_mat: Mat4) -> Self { + Self { + shadow_matrices: shadow_mat.into_col_arrays(), + texture_mats: texture_mat.into_col_arrays(), + } + } + + pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) } +} + +pub struct ShadowPipeline; + +impl ShadowPipeline { + pub fn create_col_lights( + renderer: &mut Renderer, + (col_lights, col_lights_size): ColLightInfo, + ) -> Result, RenderError> { + renderer.create_texture_immutable_raw( + gfx::texture::Kind::D2( + col_lights_size.x, + col_lights_size.y, + gfx::texture::AaMode::Single, + ), + gfx::texture::Mipmap::Provided, + &[&col_lights], + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) + } +} + +impl Pipeline for ShadowPipeline { + type Vertex = terrain::Vertex; +} diff --git a/voxygen/src/render/pipelines/skybox.rs b/voxygen/src/render/pipelines/skybox.rs index e6cbd95649..9d2b43be3a 100644 --- a/voxygen/src/render/pipelines/skybox.rs +++ b/voxygen/src/render/pipelines/skybox.rs @@ -5,7 +5,6 @@ use super::{ use gfx::{ self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, gfx_vertex_struct_meta, - state::{Comparison, Stencil, StencilOp}, }; gfx_defines! { @@ -23,10 +22,14 @@ gfx_defines! { locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + noise: gfx::TextureSampler = "t_noise", tgt_color: gfx::RenderTarget = "tgt_color", - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } diff --git a/voxygen/src/render/pipelines/sprite.rs b/voxygen/src/render/pipelines/sprite.rs index 9eb562eb49..9ac8ed369f 100644 --- a/voxygen/src/render/pipelines/sprite.rs +++ b/voxygen/src/render/pipelines/sprite.rs @@ -1,52 +1,97 @@ use super::{ super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + shadow, terrain, Globals, Light, Shadow, }; +use core::fmt; use gfx::{ - self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, - gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask, }; use vek::*; gfx_defines! { vertex Vertex { pos: [f32; 3] = "v_pos", + // Because we try to restrict terrain sprite data to a 128×128 block + // we need an offset into the texture atlas. + atlas_pos: u32 = "v_atlas_pos", // ____BBBBBBBBGGGGGGGGRRRRRRRR - col: u32 = "v_col", + // col: u32 = "v_col", // ...AANNN // A = AO // N = Normal norm_ao: u32 = "v_norm_ao", } - vertex Instance { + constant Locals { + // Each matrix performs rotatation, translation, and scaling, relative to the sprite + // origin, for all sprite instances. The matrix will be in an array indexed by the + // sprite instance's orientation (0 through 7). + mat: [[f32; 4]; 4] = "mat", + wind_sway: [f32; 4] = "wind_sway", + offs: [f32; 4] = "offs", + } + + vertex/*constant*/ Instance { + // Terrain block position and orientation + pos_ori: u32 = "inst_pos_ori", inst_mat0: [f32; 4] = "inst_mat0", inst_mat1: [f32; 4] = "inst_mat1", inst_mat2: [f32; 4] = "inst_mat2", inst_mat3: [f32; 4] = "inst_mat3", - inst_col: [f32; 3] = "inst_col", inst_wind_sway: f32 = "inst_wind_sway", } pipeline pipe { vbuf: gfx::VertexBuffer = (), ibuf: gfx::InstanceBuffer = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", + locals: gfx::ConstantBuffer = "u_locals", + // A sprite instance is a cross between a sprite and a terrain chunk. + terrain_locals: gfx::ConstantBuffer = "u_terrain_locals", globals: gfx::ConstantBuffer = "u_globals", lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } +} + +impl fmt::Display for Vertex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Vertex") + .field("pos", &Vec3::::from(self.pos)) + .field( + "atlas_pos", + &Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF), + ) + .field("norm_ao", &self.norm_ao) + .finish() } } impl Vertex { + // NOTE: Limit to 16 (x) × 16 (y) × 32 (z). #[allow(clippy::collapsible_if)] - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32) -> Self { + pub fn new( + atlas_pos: Vec2, + pos: Vec3, + norm: Vec3, /* , col: Rgb, ao: f32 */ + ) -> Self { let norm_bits = if norm.x != 0.0 { if norm.x < 0.0 { 0 } else { 1 } } else if norm.y != 0.0 { @@ -56,31 +101,53 @@ impl Vertex { }; Self { + // pos_norm: ((pos.x as u32) & 0x003F) + // | ((pos.y as u32) & 0x003F) << 6 + // | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12 + // | if meta { 1 } else { 0 } << 28 + // | (norm_bits & 0x7) << 29, pos: pos.into_array(), - col: col - .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift) - .reduce_bitor(), - norm_ao: norm_bits | (((ao * 3.9999) as u32) << 3), + atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16, + norm_ao: norm_bits, } } } impl Instance { - pub fn new(mat: Mat4, col: Rgb, wind_sway: f32) -> Self { + pub fn new(mat: Mat4, wind_sway: f32, pos: Vec3, ori_bits: u8) -> Self { + const EXTRA_NEG_Z: i32 = 32768; + let mat_arr = mat.into_col_arrays(); Self { + pos_ori: ((pos.x as u32) & 0x003F) + | ((pos.y as u32) & 0x003F) << 6 + | (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12 + | (u32::from(ori_bits) & 0x7) << 29, inst_mat0: mat_arr[0], inst_mat1: mat_arr[1], inst_mat2: mat_arr[2], inst_mat3: mat_arr[3], - inst_col: col.into_array(), inst_wind_sway: wind_sway, } } } impl Default for Instance { - fn default() -> Self { Self::new(Mat4::identity(), Rgb::broadcast(1.0), 0.0) } + fn default() -> Self { Self::new(Mat4::identity(), 0.0, Vec3::zero(), 0) } +} + +impl Default for Locals { + fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) } +} + +impl Locals { + pub fn new(mat: Mat4, scale: Vec3, offs: Vec3, wind_sway: f32) -> Self { + Self { + mat: mat.into_col_arrays(), + wind_sway: [scale.x, scale.y, scale.z, wind_sway], + offs: [offs.x, offs.y, offs.z, 0.0], + } + } } pub struct SpritePipeline; diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index e43cdc99ae..66eaaabf5d 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -1,60 +1,115 @@ use super::{ - super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + super::{ColLightFmt, Pipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, }; use gfx::{ self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, gfx_vertex_struct_meta, - state::{Comparison, Stencil, StencilOp}, }; -use std::ops::Mul; use vek::*; gfx_defines! { vertex Vertex { pos_norm: u32 = "v_pos_norm", - col_light: u32 = "v_col_light", + atlas_pos: u32 = "v_atlas_pos", } constant Locals { model_offs: [f32; 3] = "model_offs", load_time: f32 = "load_time", + atlas_offs: [i32; 4] = "atlas_offs", } pipeline pipe { vbuf: gfx::VertexBuffer = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::RenderTarget = "tgt_color", - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } impl Vertex { #[allow(clippy::identity_op)] // TODO: Pending review in #587 - pub fn new(norm_bits: u32, light: u32, ao: u32, pos: Vec3, col: Rgb) -> Self { - const EXTRA_NEG_Z: f32 = 65536.0; + /// NOTE: meta is true when the terrain vertex is touching water. + pub fn new(atlas_pos: Vec2, pos: Vec3, norm: Vec3, meta: bool) -> Self { + const EXTRA_NEG_Z: f32 = 32768.0; + let norm_bits = if norm.x != 0.0 { + if norm.x < 0.0 { 0 } else { 1 } + } else if norm.y != 0.0 { + if norm.y < 0.0 { 2 } else { 3 } + } else if norm.z < 0.0 { + 4 + } else { + 5 + }; Self { - pos_norm: 0 - | ((pos.x as u32) & 0x003F) << 0 + pos_norm: ((pos.x as u32) & 0x003F) << 0 | ((pos.y as u32) & 0x003F) << 6 - | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 17) as f32) as u32) & 0xFFFFF) << 12 + | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12 + | if meta { 1 } else { 0 } << 28 | (norm_bits & 0x7) << 29, - col_light: 0 - | ((col.r.mul(255.0) as u32) & 0xFF) << 8 - | ((col.g.mul(255.0) as u32) & 0xFF) << 16 - | ((col.b.mul(255.0) as u32) & 0xFF) << 24 - | (ao >> 6) << 6 - | ((light >> 2) & 0x3F) << 0, + atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) << 0 | ((atlas_pos.y as u32) & 0xFFFF) << 16, } } + + pub fn new_figure(atlas_pos: Vec2, pos: Vec3, norm: Vec3, bone_idx: u8) -> Self { + let norm_bits = if norm.x.min(norm.y).min(norm.z) < 0.0 { + 0 + } else { + 1 + }; + let axis_bits = if norm.x != 0.0 { + 0 + } else if norm.y != 0.0 { + 1 + } else { + 2 + }; + Self { + pos_norm: pos + .map2(Vec3::new(0, 9, 18), |e, shift| { + (((e * 2.0 + 256.0) as u32) & 0x1FF) << shift + }) + .reduce_bitor() + | (((bone_idx & 0xF) as u32) << 27) + | (norm_bits << 31), + atlas_pos: ((atlas_pos.x as u32) & 0x7FFF) << 2 + | ((atlas_pos.y as u32) & 0x7FFF) << 17 + | axis_bits & 3, + } + } + + pub fn make_col_light( + light: u8, + col: Rgb, + ) -> <::Surface as gfx::format::SurfaceTyped>::DataType + { + [col.r, col.g, col.b, light] + } + + /// Set the bone_idx for an existing figure vertex. + pub fn set_bone_idx(&mut self, bone_idx: u8) { + self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27); + } } impl Locals { @@ -62,6 +117,7 @@ impl Locals { Self { model_offs: [0.0; 3], load_time: 0.0, + atlas_offs: [0; 4], } } } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 1fa5cc6f2c..41ba19f4ea 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -5,32 +5,38 @@ use super::{ mesh::Mesh, model::{DynamicModel, Model}, pipelines::{ - figure, fluid, particle, postprocess, skybox, sprite, terrain, ui, Globals, Light, Shadow, + figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, ui, + GlobalModel, Globals, }, texture::Texture, - AaMode, CloudMode, FluidMode, Pipeline, RenderError, + AaMode, CloudMode, FilterMethod, FluidMode, LightingMode, Pipeline, RenderError, RenderMode, + ShadowMapMode, ShadowMode, WrapMode, }; use common::assets::{self, watch::ReloadIndicator}; +use core::convert::TryFrom; use gfx::{ self, handle::Sampler, - state::{Comparison, Stencil, StencilOp}, + state::Comparison, traits::{Device, Factory, FactoryExt}, }; use glsl_include::Context as IncludeContext; -use tracing::error; +use tracing::{error, warn}; use vek::*; /// Represents the format of the pre-processed color target. pub type TgtColorFmt = gfx::format::Srgba8; /// Represents the format of the pre-processed depth and stencil target. -pub type TgtDepthStencilFmt = gfx::format::DepthStencil; +pub type TgtDepthStencilFmt = gfx::format::Depth; /// Represents the format of the window's color target. pub type WinColorFmt = gfx::format::Srgba8; /// Represents the format of the window's depth target. pub type WinDepthFmt = gfx::format::Depth; +/// Represents the format of the pre-processed shadow depth target. +pub type ShadowDepthStencilFmt = gfx::format::Depth; + /// A handle to a pre-processed color target. pub type TgtColorView = gfx::handle::RenderTargetView; /// A handle to a pre-processed depth target. @@ -42,12 +48,63 @@ pub type WinColorView = gfx::handle::RenderTargetView; +/// Represents the format of LOD shadows. +pub type LodTextureFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Unorm); + +/// Represents the format of LOD altitudes. +pub type LodAltFmt = (gfx::format::R16_G16, gfx::format::Unorm); + +/// Represents the format of LOD map colors. +pub type LodColorFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); + +/// Represents the format of greedy meshed color-light textures. +pub type ColLightFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); + +/// A handle to a shadow depth target. +pub type ShadowDepthStencilView = + gfx::handle::DepthStencilView; +/// A handle to a shadow depth target as a resource. +pub type ShadowResourceView = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; + /// A handle to a render color target as a resource. pub type TgtColorRes = gfx::handle::ShaderResourceView< gfx_backend::Resources, ::View, >; +/// A handle to a greedy meshed color-light texture as a resorce. +pub type ColLightRes = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; +/// A type representing data that can be converted to an immutable texture map +/// of ColLight data (used for texture atlases created during greedy meshing). +pub type ColLightInfo = ( + Vec<<::Surface as gfx::format::SurfaceTyped>::DataType>, + Vec2, +); + +/// A type that holds shadow map data. Since shadow mapping may not be +/// supported on all platforms, we try to keep it separate. +pub struct ShadowMapRenderer { + // directed_encoder: gfx::Encoder, + // point_encoder: gfx::Encoder, + directed_depth_stencil_view: ShadowDepthStencilView, + directed_res: ShadowResourceView, + directed_sampler: Sampler, + + point_depth_stencil_view: ShadowDepthStencilView, + point_res: ShadowResourceView, + point_sampler: Sampler, + + point_pipeline: GfxPipeline>, + terrain_directed_pipeline: GfxPipeline>, + figure_directed_pipeline: GfxPipeline>, +} + /// A type that encapsulates rendering state. `Renderer` is central to Voxygen's /// rendering subsystem and contains any state necessary to interact with the /// GPU, along with pipeline state objects (PSOs) needed to renderer different @@ -67,6 +124,8 @@ pub struct Renderer { sampler: Sampler, + shadow_map: Option, + skybox_pipeline: GfxPipeline>, figure_pipeline: GfxPipeline>, terrain_pipeline: GfxPipeline>, @@ -74,6 +133,7 @@ pub struct Renderer { sprite_pipeline: GfxPipeline>, particle_pipeline: GfxPipeline>, ui_pipeline: GfxPipeline>, + lod_terrain_pipeline: GfxPipeline>, postprocess_pipeline: GfxPipeline>, player_shadow_pipeline: GfxPipeline>, @@ -81,24 +141,38 @@ pub struct Renderer { noise_tex: Texture<(gfx::format::R8, gfx::format::Unorm)>, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, + mode: RenderMode, } impl Renderer { /// Create a new `Renderer` from a variety of backend-specific components /// and the window targets. pub fn new( - device: gfx_backend::Device, + mut device: gfx_backend::Device, mut factory: gfx_backend::Factory, win_color_view: WinColorView, win_depth_view: WinDepthView, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, + mode: RenderMode, ) -> Result { + // Enable seamless cubemaps globally, where available--they are essentially a + // strict improvement on regular cube maps. + // + // Note that since we only have to enable this once globally, there is no point + // in doing this on rerender. + Self::enable_seamless_cube_maps(&mut device); + + let dims = win_color_view.get_dimensions(); + let mut shader_reload_indicator = ReloadIndicator::new(); + let shadow_views = Self::create_shadow_views( + &mut factory, + (dims.0, dims.1), + &ShadowMapMode::try_from(mode.shadow).unwrap_or_default(), + ) + .map_err(|err| { + warn!("Could not create shadow map views: {:?}", err); + }) + .ok(); let ( skybox_pipeline, @@ -108,27 +182,68 @@ impl Renderer { sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, ) = create_pipelines( &mut factory, - aa_mode, - cloud_mode, - fluid_mode, + &mode, + shadow_views.is_some(), &mut shader_reload_indicator, )?; - let dims = win_color_view.get_dimensions(); let (tgt_color_view, tgt_depth_stencil_view, tgt_color_res) = - Self::create_rt_views(&mut factory, (dims.0, dims.1), aa_mode)?; + Self::create_rt_views(&mut factory, (dims.0, dims.1), &mode)?; + + let shadow_map = if let ( + Some(point_pipeline), + Some(terrain_directed_pipeline), + Some(figure_directed_pipeline), + Some(shadow_views), + ) = ( + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + shadow_views, + ) { + let ( + point_depth_stencil_view, + point_res, + point_sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, + ) = shadow_views; + Some(ShadowMapRenderer { + // point_encoder: factory.create_command_buffer().into(), + // directed_encoder: factory.create_command_buffer().into(), + point_depth_stencil_view, + point_res, + point_sampler, + + directed_depth_stencil_view, + directed_res, + directed_sampler, + + point_pipeline, + terrain_directed_pipeline, + figure_directed_pipeline, + }) + } else { + None + }; let sampler = factory.create_sampler_linear(); let noise_tex = Texture::new( &mut factory, &assets::load_expect("voxygen.texture.noise"), - Some(gfx::texture::FilterMethod::Trilinear), + Some(gfx::texture::FilterMethod::Bilinear), Some(gfx::texture::WrapMode::Tile), + None, )?; Ok(Self { @@ -143,8 +258,11 @@ impl Renderer { tgt_depth_stencil_view, tgt_color_res, + sampler, + shadow_map, + skybox_pipeline, figure_pipeline, terrain_pipeline, @@ -152,6 +270,7 @@ impl Renderer { sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, @@ -159,9 +278,7 @@ impl Renderer { noise_tex, - aa_mode, - cloud_mode, - fluid_mode, + mode, }) } @@ -193,9 +310,9 @@ impl Renderer { (&mut self.win_color_view, &mut self.win_depth_view) } - /// Change the anti-aliasing mode - pub fn set_aa_mode(&mut self, aa_mode: AaMode) -> Result<(), RenderError> { - self.aa_mode = aa_mode; + /// Change the render mode. + pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> { + self.mode = mode; // Recreate render target self.on_resize()?; @@ -206,31 +323,8 @@ impl Renderer { Ok(()) } - /// Change the cloud rendering mode - pub fn set_cloud_mode(&mut self, cloud_mode: CloudMode) -> Result<(), RenderError> { - self.cloud_mode = cloud_mode; - - // Recreate render target - self.on_resize()?; - - // Recreate pipelines with the new cloud mode - self.recreate_pipelines(); - - Ok(()) - } - - /// Change the fluid rendering mode - pub fn set_fluid_mode(&mut self, fluid_mode: FluidMode) -> Result<(), RenderError> { - self.fluid_mode = fluid_mode; - - // Recreate render target - self.on_resize()?; - - // Recreate pipelines with the new fluid mode - self.recreate_pipelines(); - - Ok(()) - } + /// Get the render mode. + pub fn render_mode(&self) -> &RenderMode { &self.mode } /// Resize internal render targets to match window render target dimensions. pub fn on_resize(&mut self) -> Result<(), RenderError> { @@ -239,10 +333,35 @@ impl Renderer { // Avoid panics when creating texture with w,h of 0,0. if dims.0 != 0 && dims.1 != 0 { let (tgt_color_view, tgt_depth_stencil_view, tgt_color_res) = - Self::create_rt_views(&mut self.factory, (dims.0, dims.1), self.aa_mode)?; + Self::create_rt_views(&mut self.factory, (dims.0, dims.1), &self.mode)?; self.tgt_color_res = tgt_color_res; self.tgt_color_view = tgt_color_view; self.tgt_depth_stencil_view = tgt_depth_stencil_view; + if let (Some(shadow_map), ShadowMode::Map(mode)) = + (self.shadow_map.as_mut(), self.mode.shadow) + { + match Self::create_shadow_views(&mut self.factory, (dims.0, dims.1), &mode) { + Ok(( + point_depth_stencil_view, + point_res, + point_sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, + )) => { + shadow_map.point_depth_stencil_view = point_depth_stencil_view; + shadow_map.point_res = point_res; + shadow_map.point_sampler = point_sampler; + + shadow_map.directed_depth_stencil_view = directed_depth_stencil_view; + shadow_map.directed_res = directed_res; + shadow_map.directed_sampler = directed_sampler; + }, + Err(err) => { + warn!("Could not create shadow map views: {:?}", err); + }, + } + } } Ok(()) @@ -251,9 +370,9 @@ impl Renderer { fn create_rt_views( factory: &mut gfx_device_gl::Factory, size: (u16, u16), - aa_mode: AaMode, + mode: &RenderMode, ) -> Result<(TgtColorView, TgtDepthStencilView, TgtColorRes), RenderError> { - let kind = match aa_mode { + let kind = match mode.aa { AaMode::None | AaMode::Fxaa => { gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Single) }, @@ -303,6 +422,142 @@ impl Renderer { Ok((tgt_color_view, tgt_depth_stencil_view, tgt_color_res)) } + /// Create textures and views for shadow maps. + // This is a one-use type and the two halves are not guaranteed to remain identical, so we + // disable the type complexity lint. + #[allow(clippy::type_complexity)] + fn create_shadow_views( + factory: &mut gfx_device_gl::Factory, + size: (u16, u16), + mode: &ShadowMapMode, + ) -> Result< + ( + ShadowDepthStencilView, + ShadowResourceView, + Sampler, + ShadowDepthStencilView, + ShadowResourceView, + Sampler, + ), + RenderError, + > { + // (Attempt to) apply resolution factor to shadow map resolution. + let resolution_factor = mode.resolution.clamped(0.25, 4.0); + + let max_texture_size = Self::max_texture_size_raw(factory); + // Limit to max texture size, rather than erroring. + let size = Vec2::new(size.0, size.1).map(|e| { + let size = f32::from(e) * resolution_factor; + // NOTE: We know 0 <= e since we clamped the resolution factor to be between + // 0.25 and 4.0. + if size <= f32::from(max_texture_size) { + size as u16 + } else { + max_texture_size + } + }); + + let levels = 1; + // Limit to max texture size rather than erroring. + let two_size = size.map(|e| { + u16::checked_next_power_of_two(e) + .filter(|&e| e <= max_texture_size) + .unwrap_or(max_texture_size) + }); + let min_size = size.reduce_min(); + let max_size = size.reduce_max(); + let _min_two_size = two_size.reduce_min(); + let _max_two_size = two_size.reduce_max(); + // For rotated shadow maps, the maximum size of a pixel along any axis is the + // size of a diagonal along that axis. + let diag_size = size.map(f64::from).magnitude(); + let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size; + let (diag_size, _diag_cross_size) = + if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) { + // NOTE: diag_cross_size must be non-negative, since it is the ratio of a + // non-negative and a positive number (if max_size were zero, + // diag_size would be 0 too). And it must be <= diag_size, + // since min_size <= max_size. Therefore, if diag_size fits in a + // u16, so does diag_cross_size. + (diag_size as u16, diag_cross_size as u16) + } else { + // Limit to max texture resolution rather than error. + (max_texture_size as u16, max_texture_size as u16) + }; + let diag_two_size = u16::checked_next_power_of_two(diag_size) + .filter(|&e| e <= max_texture_size) + // Limit to max texture resolution rather than error. + .unwrap_or(max_texture_size); + let depth_stencil_cty = <::Channel as gfx::format::ChannelTyped>::get_channel_type(); + + let point_shadow_tex = factory + .create_texture( + gfx::texture::Kind::Cube(diag_two_size / 4), + levels as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_stencil_cty), + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + + let point_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &point_shadow_tex, + 0, + None, + gfx::texture::DepthStencilFlags::empty(), + )?; + + let point_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &point_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + let directed_shadow_tex = factory + .create_texture( + gfx::texture::Kind::D2(diag_two_size, diag_two_size, gfx::texture::AaMode::Single), + levels as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_stencil_cty), + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + let directed_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &directed_shadow_tex, + 0, + None, + gfx::texture::DepthStencilFlags::empty(), + )?; + let directed_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &directed_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + let mut sampler_info = gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + // Lights should always be assumed to flood areas we can't see. + gfx::texture::WrapMode::Border, + ); + sampler_info.comparison = Some(Comparison::LessEqual); + sampler_info.border = [1.0; 4].into(); + let point_shadow_tex_sampler = factory.create_sampler(sampler_info); + let directed_shadow_tex_sampler = factory.create_sampler(sampler_info); + + Ok(( + point_tgt_shadow_view, + point_tgt_shadow_res, + point_shadow_tex_sampler, + directed_tgt_shadow_view, + directed_tgt_shadow_res, + directed_shadow_tex_sampler, + )) + } + /// Get the resolution of the render target. pub fn get_resolution(&self) -> Vec2 { Vec2::new( @@ -311,14 +566,127 @@ impl Renderer { ) } + /// Get the resolution of the shadow render target. + pub fn get_shadow_resolution(&self) -> (Vec2, Vec2) { + if let Some(shadow_map) = &self.shadow_map { + let point_dims = shadow_map.point_depth_stencil_view.get_dimensions(); + let directed_dims = shadow_map.directed_depth_stencil_view.get_dimensions(); + ( + Vec2::new(point_dims.0, point_dims.1), + Vec2::new(directed_dims.0, directed_dims.1), + ) + } else { + (Vec2::new(1, 1), Vec2::new(1, 1)) + } + } + + /// Queue the clearing of the shadow targets ready for a new frame to be + /// rendered. + pub fn clear_shadows(&mut self) { + if !self.mode.shadow.is_map() { + return; + } + if let Some(shadow_map) = self.shadow_map.as_mut() { + // let point_encoder = &mut shadow_map.point_encoder; + let point_encoder = &mut self.encoder; + point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0); + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view, 1.0); + } + } + + /// NOTE: Supported by Vulkan (by default), DirectX 10+ (it seems--it's hard + /// to find proof of this, but Direct3D 10 apparently does it by + /// default, and 11 definitely does, so I assume it's natively supported + /// by DirectX itself), OpenGL 3.2+, and Metal (done by default). While + /// there may be some GPUs that don't quite support it correctly, the + /// impact is relatively small, so there is no reason not to enable it where + /// available. + #[allow(unsafe_code)] + fn enable_seamless_cube_maps(device: &mut gfx_backend::Device) { + unsafe { + // NOTE: Currently just fail silently rather than complain if the computer is on + // a version lower than 3.2, where seamless cubemaps were introduced. + if !device.get_info().is_version_supported(3, 2) { + return; + } + + // NOTE: Safe because GL_TEXTURE_CUBE_MAP_SEAMLESS is supported by OpenGL 3.2+ + // (see https://www.khronos.org/opengl/wiki/Cubemap_Texture#Seamless_cubemap); + // enabling seamless cube maps should always be safe regardless of the state of + // the OpenGL context, so no further checks are needd. + device.with_gl(|gl| { + gl.Enable(gfx_gl::TEXTURE_CUBE_MAP_SEAMLESS); + }); + } + } + + /// NOTE: Supported by all but a handful of mobile GPUs + /// (see https://github.com/gpuweb/gpuweb/issues/480) + /// so wgpu should support it too. + #[allow(unsafe_code)] + fn set_depth_clamp(device: &mut gfx_backend::Device, depth_clamp: bool) { + unsafe { + // NOTE: Currently just fail silently rather than complain if the computer is on + // a version lower than 3.3, though we probably will complain + // elsewhere regardless, since shadow mapping is an optional feature + // and having depth clamping disabled won't cause undefined + // behavior, just incorrect shadowing from objects behind the viewer. + if !device.get_info().is_version_supported(3, 3) { + return; + } + + // NOTE: Safe because glDepthClamp is (I believe) supported by + // OpenGL 3.3, so we shouldn't have to check for other OpenGL versions which + // may use different extensions. Also, enabling depth clamping should + // essentially always be safe regardless of the state of the OpenGL + // context, so no further checks are needed. + device.with_gl(|gl| { + if depth_clamp { + gl.Enable(gfx_gl::DEPTH_CLAMP); + } else { + gl.Disable(gfx_gl::DEPTH_CLAMP); + } + }); + } + } + /// Queue the clearing of the depth target ready for a new frame to be /// rendered. pub fn clear(&mut self) { self.encoder.clear_depth(&self.tgt_depth_stencil_view, 1.0); - self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0); + // self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0); self.encoder.clear_depth(&self.win_depth_view, 1.0); } + /// Set up shadow rendering. + pub fn start_shadows(&mut self) { + if !self.mode.shadow.is_map() { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + self.encoder.flush(&mut self.device); + Self::set_depth_clamp(&mut self.device, true); + } + } + + /// Perform all queued draw calls for global.shadows. + pub fn flush_shadows(&mut self) { + if !self.mode.shadow.is_map() { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + let point_encoder = &mut self.encoder; + // let point_encoder = &mut shadow_map.point_encoder; + point_encoder.flush(&mut self.device); + // let directed_encoder = &mut shadow_map.directed_encoder; + // directed_encoder.flush(&mut self.device); + // Reset depth clamping. + Self::set_depth_clamp(&mut self.device, false); + } + } + /// Perform all queued draw calls for this frame and clean up discarded /// items. pub fn flush(&mut self) { @@ -335,9 +703,8 @@ impl Renderer { fn recreate_pipelines(&mut self) { match create_pipelines( &mut self.factory, - self.aa_mode, - self.cloud_mode, - self.fluid_mode, + &self.mode, + self.shadow_map.is_some(), &mut self.shader_reload_indicator, ) { Ok(( @@ -348,8 +715,12 @@ impl Renderer { sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, )) => { self.skybox_pipeline = skybox_pipeline; self.figure_pipeline = figure_pipeline; @@ -358,8 +729,24 @@ impl Renderer { self.sprite_pipeline = sprite_pipeline; self.particle_pipeline = particle_pipeline; self.ui_pipeline = ui_pipeline; + self.lod_terrain_pipeline = lod_terrain_pipeline; self.postprocess_pipeline = postprocess_pipeline; self.player_shadow_pipeline = player_shadow_pipeline; + if let ( + Some(point_pipeline), + Some(terrain_directed_pipeline), + Some(figure_directed_pipeline), + Some(shadow_map), + ) = ( + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + self.shadow_map.as_mut(), + ) { + shadow_map.point_pipeline = point_pipeline; + shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; + shadow_map.figure_directed_pipeline = figure_directed_pipeline; + } }, Err(e) => error!(?e, "Could not recreate shaders from assets due to an error",), } @@ -371,7 +758,7 @@ impl Renderer { vals: &[T], ) -> Result, RenderError> { let mut consts = Consts::new(&mut self.factory, vals.len()); - consts.update(&mut self.encoder, vals)?; + consts.update(&mut self.encoder, vals, 0)?; Ok(consts) } @@ -381,7 +768,7 @@ impl Renderer { consts: &mut Consts, vals: &[T], ) -> Result<(), RenderError> { - consts.update(&mut self.encoder, vals) + consts.update(&mut self.encoder, vals, 0) } /// Create a new set of instances with the provided values. @@ -418,16 +805,86 @@ impl Renderer { } /// Return the maximum supported texture size. - pub fn max_texture_size(&self) -> usize { self.factory.get_capabilities().max_texture_size } + pub fn max_texture_size(&self) -> u16 { Self::max_texture_size_raw(&self.factory) } + + /// Return the maximum supported texture size from the factory. + fn max_texture_size_raw(factory: &gfx_backend::Factory) -> u16 { + /// NOTE: OpenGL requirement. + const MAX_TEXTURE_SIZE_MIN: u16 = 1024; + #[cfg(target_os = "macos")] + /// NOTE: Because Macs lie about their max supported texture size. + const MAX_TEXTURE_SIZE_MAX: u16 = 8192; + #[cfg(not(target_os = "macos"))] + /// NOTE: Apparently Macs aren't the only machines that lie. + /// + /// TODO: Find a way to let graphics cards that don't lie do better. + const MAX_TEXTURE_SIZE_MAX: u16 = 8192; + // NOTE: Many APIs for textures require coordinates to fit in u16, which is why + // we perform this conversion. + u16::try_from(factory.get_capabilities().max_texture_size) + .unwrap_or(MAX_TEXTURE_SIZE_MIN) + .min(MAX_TEXTURE_SIZE_MAX) + } + + /// Create a new immutable texture from the provided image. + pub fn create_texture_immutable_raw( + &mut self, + kind: gfx::texture::Kind, + mipmap: gfx::texture::Mipmap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_immutable_raw(&mut self.factory, kind, mipmap, data, sampler_info) + } + + /// Create a new raw texture. + pub fn create_texture_raw( + &mut self, + kind: gfx::texture::Kind, + max_levels: u8, + bind: gfx::memory::Bind, + usage: gfx::memory::Usage, + levels: (u8, u8), + swizzle: gfx::format::Swizzle, + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_raw( + &mut self.device, + &mut self.factory, + kind, + max_levels, + bind, + usage, + levels, + swizzle, + sampler_info, + ) + } /// Create a new texture from the provided image. - pub fn create_texture( + pub fn create_texture( &mut self, image: &image::DynamicImage, - filter_method: Option, - wrap_mode: Option, - ) -> Result { - Texture::new(&mut self.factory, image, filter_method, wrap_mode) + filter_method: Option, + wrap_mode: Option, + border: Option, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new(&mut self.factory, image, filter_method, wrap_mode, border) } /// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the @@ -498,8 +955,9 @@ impl Renderer { pub fn render_skybox( &mut self, model: &Model, - globals: &Consts, + global: &GlobalModel, locals: &Consts, + lod: &lod_terrain::LodData, ) { self.encoder.draw( &gfx::Slice { @@ -513,10 +971,12 @@ impl Renderer { &skybox::pipe::Data { vbuf: model.vbuf.clone(), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -524,13 +984,33 @@ impl Renderer { /// Queue the rendering of the provided figure model in the upcoming frame. pub fn render_figure( &mut self, - model: &Model, - globals: &Consts, + model: &figure::FigureModel, + col_lights: &Texture, + global: &GlobalModel, locals: &Consts, bones: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let model = &model.opaque; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -542,14 +1022,20 @@ impl Renderer { &self.figure_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), bones: bones.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -557,13 +1043,34 @@ impl Renderer { /// Queue the rendering of the player silhouette in the upcoming frame. pub fn render_player_shadow( &mut self, - model: &Model, - globals: &Consts, - locals: &Consts, - bones: &Consts, - lights: &Consts, - shadows: &Consts, + _model: &figure::FigureModel, + _col_lights: &Texture, + _global: &GlobalModel, + _bones: &Consts, + _lod: &lod_terrain::LodData, + _locals: &Consts, ) { + // FIXME: Consider reenabling at some point. + /* let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let model = &model.opaque; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -575,28 +1082,54 @@ impl Renderer { &self.player_shadow_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), bones: bones.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (0, 0)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (0, 0) */), }, - ); + ); */ } /// Queue the rendering of the player model in the upcoming frame. pub fn render_player( &mut self, - model: &Model, - globals: &Consts, + model: &figure::FigureModel, + col_lights: &Texture, + global: &GlobalModel, locals: &Consts, bones: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let model = &model.opaque; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -608,14 +1141,20 @@ impl Renderer { &self.figure_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), bones: bones.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -625,11 +1164,30 @@ impl Renderer { pub fn render_terrain_chunk( &mut self, model: &Model, - globals: &Consts, + col_lights: &Texture, + global: &GlobalModel, locals: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -641,13 +1199,153 @@ impl Renderer { &self.terrain_pipeline.pso, &terrain::pipe::Data { vbuf: model.vbuf.clone(), + // TODO: Consider splitting out texture atlas data into a separate vertex buffer, + // since we don't need it for things like global.shadows. + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of a shadow map from a point light in the upcoming + /// frame. + pub fn render_shadow_point( + &mut self, + model: &Model, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_map() { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + + // let point_encoder = &mut shadow_map.point_encoder; + let point_encoder = &mut self.encoder; + point_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.point_pipeline.pso, + &shadow::pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: terrain_locals.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.point_depth_stencil_view.clone(), + }, + ); + } + + /// Queue the rendering of terrain shadow map from all directional lights in + /// the upcoming frame. + pub fn render_terrain_shadow_directed( + &mut self, + model: &Model, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_map() { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.terrain_directed_pipeline.pso, + &shadow::pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: terrain_locals.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), + }, + ); + } + + /// Queue the rendering of figure shadow map from all directional lights in + /// the upcoming frame. + pub fn render_figure_shadow_directed( + &mut self, + model: &figure::FigureModel, + global: &GlobalModel, + figure_locals: &Consts, + bones: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_map() { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + let model = &model.opaque; + + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.figure_directed_pipeline.pso, + &shadow::figure_pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: figure_locals.buf.clone(), + bones: bones.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), }, ); } @@ -657,12 +1355,30 @@ impl Renderer { pub fn render_fluid_chunk( &mut self, model: &Model, - globals: &Consts, + global: &GlobalModel, locals: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, waves: &Texture, ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -675,13 +1391,18 @@ impl Renderer { &fluid::pipe::Data { vbuf: model.vbuf.clone(), locals: locals.buf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), waves: (waves.srv.clone(), waves.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -691,11 +1412,32 @@ impl Renderer { pub fn render_sprites( &mut self, model: &Model, - globals: &Consts, + col_lights: &Texture, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, instances: &Instances, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -708,12 +1450,56 @@ impl Renderer { &sprite::pipe::Data { vbuf: model.vbuf.clone(), ibuf: instances.ibuf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + terrain_locals: terrain_locals.buf.clone(), + // NOTE: It would be nice if this wasn't needed and we could use a constant buffer + // offset into the sprite data. Hopefully, when we switch to wgpu we can do this, + // as it offers the exact API we want (the equivalent can be done in OpenGL using + // glBindBufferOffset). + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided LoD terrain model in the upcoming + /// frame. + pub fn render_lod_terrain( + &mut self, + model: &Model, + global: &GlobalModel, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.lod_terrain_pipeline.pso, + &lod_terrain::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + map: (lod.map.srv.clone(), lod.map.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -722,11 +1508,29 @@ impl Renderer { pub fn render_particles( &mut self, model: &Model, - globals: &Consts, + global: &GlobalModel, instances: &Instances, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -739,37 +1543,46 @@ impl Renderer { &particle::pipe::Data { vbuf: model.vbuf.clone(), ibuf: instances.ibuf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } /// Queue the rendering of the provided UI element in the upcoming frame. - pub fn render_ui_element( + pub fn render_ui_element>( &mut self, - model: &Model, - tex: &Texture, + model: Model, + tex: &Texture, scissor: Aabr, globals: &Consts, locals: &Consts, - ) { + ) where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { let Aabr { min, max } = scissor; self.encoder.draw( &gfx::Slice { - start: model.vertex_range().start, - end: model.vertex_range().end, + start: model.vertex_range.start, + end: model.vertex_range.end, base_vertex: 0, instances: None, buffer: gfx::IndexBuffer::Auto, }, &self.ui_pipeline.pso, &ui::pipe::Data { - vbuf: model.vbuf.clone(), + vbuf: model.vbuf, scissor: gfx::Rect { x: min.x, y: min.y, @@ -820,9 +1633,8 @@ struct GfxPipeline { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 fn create_pipelines( factory: &mut gfx_backend::Factory, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, + mode: &RenderMode, + has_shadow_views: bool, shader_reload_indicator: &mut ReloadIndicator, ) -> Result< ( @@ -833,11 +1645,20 @@ fn create_pipelines( GfxPipeline>, GfxPipeline>, GfxPipeline>, + GfxPipeline>, GfxPipeline>, GfxPipeline>, + Option>>, + Option>>, + Option>>, ), RenderError, > { + let constants = assets::load_watched::( + "voxygen.shaders.include.constants", + shader_reload_indicator, + ) + .unwrap(); let globals = assets::load_watched::("voxygen.shaders.include.globals", shader_reload_indicator) .unwrap(); @@ -853,9 +1674,50 @@ fn create_pipelines( let random = assets::load_watched::("voxygen.shaders.include.random", shader_reload_indicator) .unwrap(); + let lod = + assets::load_watched::("voxygen.shaders.include.lod", shader_reload_indicator) + .unwrap(); + let shadows = + assets::load_watched::("voxygen.shaders.include.shadows", shader_reload_indicator) + .unwrap(); + + // We dynamically add extra configuration settings to the constants file. + let constants = format!( + r#" +{} + +#define VOXYGEN_COMPUTATION_PREERENCE {} +#define FLUID_MODE {} +#define CLOUD_MODE {} +#define LIGHTING_ALGORITHM {} +#define SHADOW_MODE {} + +"#, + constants, + // TODO: Configurable vertex/fragment shader preference. + "VOXYGEN_COMPUTATION_PREERENCE_FRAGMENT", + match mode.fluid { + FluidMode::Cheap => "FLUID_MODE_CHEAP", + FluidMode::Shiny => "FLUID_MODE_SHINY", + }, + match mode.cloud { + CloudMode::None => "CLOUD_MODE_NONE", + CloudMode::Regular => "CLOUD_MODE_REGULAR", + }, + match mode.lighting { + LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN", + LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG", + LightingMode::Lambertian => "CLOUD_MODE_NONE", + }, + match mode.shadow { + ShadowMode::None => "SHADOW_MODE_NONE", + ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP", + ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP", + }, + ); let anti_alias = assets::load_watched::( - &["voxygen.shaders.antialias.", match aa_mode { + &["voxygen.shaders.antialias.", match mode.aa { AaMode::None | AaMode::SsaaX4 => "none", AaMode::Fxaa => "fxaa", AaMode::MsaaX4 => "msaa-x4", @@ -868,7 +1730,7 @@ fn create_pipelines( .unwrap(); let cloud = assets::load_watched::( - &["voxygen.shaders.include.cloud.", match cloud_mode { + &["voxygen.shaders.include.cloud.", match mode.cloud { CloudMode::None => "none", CloudMode::Regular => "regular", }] @@ -878,14 +1740,45 @@ fn create_pipelines( .unwrap(); let mut include_ctx = IncludeContext::new(); + include_ctx.include("constants.glsl", &constants); include_ctx.include("globals.glsl", &globals); + include_ctx.include("shadows.glsl", &shadows); include_ctx.include("sky.glsl", &sky); include_ctx.include("light.glsl", &light); include_ctx.include("srgb.glsl", &srgb); include_ctx.include("random.glsl", &random); + include_ctx.include("lod.glsl", &lod); include_ctx.include("anti-aliasing.glsl", &anti_alias); include_ctx.include("cloud.glsl", &cloud); + let figure_vert = + assets::load_watched::("voxygen.shaders.figure-vert", shader_reload_indicator) + .unwrap(); + + let terrain_point_shadow_vert = assets::load_watched::( + "voxygen.shaders.light-shadows-vert", + shader_reload_indicator, + ) + .unwrap(); + + let terrain_directed_shadow_vert = assets::load_watched::( + "voxygen.shaders.light-shadows-directed-vert", + shader_reload_indicator, + ) + .unwrap(); + + let figure_directed_shadow_vert = assets::load_watched::( + "voxygen.shaders.light-shadows-figure-vert", + shader_reload_indicator, + ) + .unwrap(); + + let directed_shadow_frag = &assets::load_watched::( + "voxygen.shaders.light-shadows-directed-frag", + shader_reload_indicator, + ) + .unwrap(); + // Construct a pipeline for rendering skyboxes let skybox_pipeline = create_pipeline( factory, @@ -902,8 +1795,7 @@ fn create_pipelines( let figure_pipeline = create_pipeline( factory, figure::pipe::new(), - &assets::load_watched::("voxygen.shaders.figure-vert", shader_reload_indicator) - .unwrap(), + &figure_vert, &assets::load_watched::("voxygen.shaders.figure-frag", shader_reload_indicator) .unwrap(), &include_ctx, @@ -929,7 +1821,7 @@ fn create_pipelines( &assets::load_watched::("voxygen.shaders.fluid-vert", shader_reload_indicator) .unwrap(), &assets::load_watched::( - &["voxygen.shaders.fluid-frag.", match fluid_mode { + &["voxygen.shaders.fluid-frag.", match mode.fluid { FluidMode::Cheap => "cheap", FluidMode::Shiny => "shiny", }] @@ -977,6 +1869,24 @@ fn create_pipelines( gfx::state::CullFace::Back, )?; + // Construct a pipeline for rendering terrain + let lod_terrain_pipeline = create_pipeline( + factory, + lod_terrain::pipe::new(), + &assets::load_watched::( + "voxygen.shaders.lod-terrain-vert", + shader_reload_indicator, + ) + .unwrap(), + &assets::load_watched::( + "voxygen.shaders.lod-terrain-frag", + shader_reload_indicator, + ) + .unwrap(), + &include_ctx, + gfx::state::CullFace::Back, + )?; + // Construct a pipeline for rendering our post-processing let postprocess_pipeline = create_pipeline( factory, @@ -999,18 +1909,15 @@ fn create_pipelines( let player_shadow_pipeline = create_pipeline( factory, figure::pipe::Init { - tgt_depth_stencil: ( - gfx::preset::depth::PASS_TEST, - Stencil::new( - Comparison::Equal, - 0xff, - (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep), - ), - ), + tgt_depth_stencil: (gfx::preset::depth::PASS_TEST/*, + Stencil::new( + Comparison::Equal, + 0xff, + (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep), + ),*/), ..figure::pipe::new() }, - &assets::load_watched::("voxygen.shaders.figure-vert", shader_reload_indicator) - .unwrap(), + &figure_vert, &assets::load_watched::( "voxygen.shaders.player-shadow-frag", shader_reload_indicator, @@ -1020,6 +1927,76 @@ fn create_pipelines( gfx::state::CullFace::Back, )?; + // Construct a pipeline for rendering point light terrain shadow maps. + let point_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::pipe::new(), + &terrain_point_shadow_vert, + Some( + &assets::load_watched::( + "voxygen.shaders.light-shadows-geom", + shader_reload_indicator, + ) + .unwrap(), + ), + &assets::load_watched::( + "voxygen.shaders.light-shadows-frag", + shader_reload_indicator, + ) + .unwrap(), + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 0)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!("Could not load point shadow map pipeline: {:?}", err); + None + }, + }; + + // Construct a pipeline for rendering directional light terrain shadow maps. + let terrain_directed_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::pipe::new(), + &terrain_directed_shadow_vert, + None, + &directed_shadow_frag, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 1)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!( + "Could not load directed terrain shadow map pipeline: {:?}", + err + ); + None + }, + }; + + // Construct a pipeline for rendering directional light figure shadow maps. + let figure_directed_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::figure_pipe::new(), + &figure_directed_shadow_vert, + None, + &directed_shadow_frag, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 1)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!( + "Could not load directed figure shadow map pipeline: {:?}", + err + ); + None + }, + }; + Ok(( skybox_pipeline, figure_pipeline, @@ -1028,14 +2005,17 @@ fn create_pipelines( sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, )) } /// Create a new pipeline from the provided vertex shader and fragment shader. -#[allow(clippy::extra_unused_lifetimes)] // TODO: Pending review in #587 -fn create_pipeline<'a, P: gfx::pso::PipelineInit>( +fn create_pipeline( factory: &mut gfx_backend::Factory, pipe: P, vs: &str, @@ -1048,7 +2028,7 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>( let program = factory.link_program(vs.as_bytes(), fs.as_bytes())?; - Ok(GfxPipeline { + let result = Ok(GfxPipeline { pso: factory.create_pipeline_from_program( &program, gfx::Primitive::TriangleList, @@ -1061,5 +2041,51 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>( }, pipe, )?, + }); + + result +} + +/// Create a new shadow map pipeline. +fn create_shadow_pipeline( + factory: &mut gfx_backend::Factory, + pipe: P, + vs: &str, + gs: Option<&str>, + fs: &str, + ctx: &IncludeContext, + cull_face: gfx::state::CullFace, + offset: Option, +) -> Result, RenderError> { + let vs = ctx.expand(vs)?; + let gs = gs.map(|gs| ctx.expand(gs)).transpose()?; + let fs = ctx.expand(fs)?; + + let shader_set = if let Some(gs) = gs { + factory.create_shader_set_geometry(vs.as_bytes(), gs.as_bytes(), fs.as_bytes())? + } else { + factory.create_shader_set(vs.as_bytes(), fs.as_bytes())? + }; + + Ok(GfxPipeline { + pso: factory.create_pipeline_state( + &shader_set, + gfx::Primitive::TriangleList, + gfx::state::Rasterizer { + front_face: gfx::state::FrontFace::CounterClockwise, + // Second-depth shadow mapping: should help reduce z-fighting provided all objects + // are "watertight" (every triangle edge is shared with at most one other + // triangle); this *should* be true for Veloren. + cull_face: match cull_face { + gfx::state::CullFace::Front => gfx::state::CullFace::Back, + gfx::state::CullFace::Back => gfx::state::CullFace::Front, + gfx::state::CullFace::Nothing => gfx::state::CullFace::Nothing, + }, + method: gfx::state::RasterMethod::Fill, + offset, + samples: None, + }, + pipe, + )?, }) } diff --git a/voxygen/src/render/texture.rs b/voxygen/src/render/texture.rs index f166b9d2c4..1c4cb1fa50 100644 --- a/voxygen/src/render/texture.rs +++ b/voxygen/src/render/texture.rs @@ -31,7 +31,14 @@ where image: &DynamicImage, filter_method: Option, wrap_mode: Option, + border: Option, ) -> Result { + // TODO: Actualy handle images that aren't in rgba format properly. + let buffer = image.as_flat_samples_u8().ok_or_else(|| { + RenderError::CustomError( + "We currently do not support color formats using more than 4 bytes / pixel.".into(), + ) + })?; let (tex, srv) = factory .create_texture_immutable_u8::( gfx::texture::Kind::D2( @@ -40,17 +47,24 @@ where gfx::texture::AaMode::Single, ), gfx::texture::Mipmap::Provided, - &[&image.raw_pixels()], + // Guarenteed to be correct, since all the conversions from DynamicImage to + // FlatSamples go through the underlying ImageBuffer's implementation of + // as_flat_samples(), which guarantees that the resulting FlatSamples is + // well-formed. + &[buffer.as_slice()], ) .map_err(RenderError::CombinedError)?; + let mut sampler_info = gfx::texture::SamplerInfo::new( + filter_method.unwrap_or(gfx::texture::FilterMethod::Scale), + wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp), + ); + let transparent = [0.0, 0.0, 0.0, 1.0].into(); + sampler_info.border = border.unwrap_or(transparent); Ok(Self { tex, srv, - sampler: factory.create_sampler(gfx::texture::SamplerInfo::new( - filter_method.unwrap_or(gfx::texture::FilterMethod::Scale), - wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp), - )), + sampler: factory.create_sampler(sampler_info), }) } @@ -86,6 +100,56 @@ where }) } + pub fn new_immutable_raw( + factory: &mut gfx_backend::Factory, + kind: gfx::texture::Kind, + mipmap: gfx::texture::Mipmap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result { + let (tex, srv) = factory + .create_texture_immutable::(kind, mipmap, data) + .map_err(RenderError::CombinedError)?; + + Ok(Self { + tex, + srv, + sampler: factory.create_sampler(sampler_info), + }) + } + + pub fn new_raw( + _device: &mut gfx_backend::Device, + factory: &mut gfx_backend::Factory, + kind: gfx::texture::Kind, + max_levels: u8, + bind: gfx::memory::Bind, + usage: gfx::memory::Usage, + levels: (u8, u8), + swizzle: gfx::format::Swizzle, + sampler_info: gfx::texture::SamplerInfo, + ) -> Result { + let tex = factory + .create_texture( + kind, + max_levels as gfx::texture::Level, + bind | gfx::memory::Bind::SHADER_RESOURCE, + usage, + Some(<::Channel as gfx::format::ChannelTyped>::get_channel_type()) + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + + let srv = factory + .view_texture_as_shader_resource::(&tex, levels, swizzle) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?; + + Ok(Self { + tex, + srv, + sampler: factory.create_sampler(sampler_info), + }) + } + /// Update a texture with the given data (used for updating the glyph cache /// texture). diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index b738aeb511..9d68f02ef1 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -137,10 +137,16 @@ fn handle_main_events_cleared( } if let Some(last) = states.last_mut() { - global_state.window.renderer_mut().clear(); - last.render(global_state.window.renderer_mut(), &global_state.settings); + let renderer = global_state.window.renderer_mut(); + // Clear the shadow maps. + renderer.clear_shadows(); + // Clear the screen + renderer.clear(); + // Render the screen using the global renderer + last.render(renderer, &global_state.settings); // Finish the frame. global_state.window.renderer_mut().flush(); + // Display the frame on the window. global_state .window .swap_buffers() diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index deaadcc963..a0fa8af2be 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -3,8 +3,8 @@ use std::f32::consts::PI; use treeculler::Frustum; use vek::*; -const NEAR_PLANE: f32 = 0.5; -const FAR_PLANE: f32 = 100000.0; +pub const NEAR_PLANE: f32 = 0.25; +pub const FAR_PLANE: f32 = 100000.0; const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; @@ -46,6 +46,7 @@ pub struct Camera { last_time: Option, dependents: Dependents, + frustum: Frustum, } impl Camera { @@ -70,6 +71,7 @@ impl Camera { cam_pos: Vec3::zero(), cam_dir: Vec3::unit_y(), }, + frustum: Frustum::from_modelview_projection(Mat4::identity().into_col_arrays()), } } @@ -99,22 +101,24 @@ impl Camera { * Mat4::rotation_x(self.ori.y) * Mat4::rotation_y(self.ori.x) * Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x()) - * Mat4::translation_3d(-self.focus); + * Mat4::translation_3d(-self.focus.map(|e| e.fract())); self.dependents.proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE); // TODO: Make this more efficient. self.dependents.cam_pos = Vec3::from(self.dependents.view_mat.inverted() * Vec4::unit_w()); + self.frustum = Frustum::from_modelview_projection( + (self.dependents.proj_mat + * self.dependents.view_mat + * Mat4::translation_3d(-self.focus.map(|e| e.trunc()))) + .into_col_arrays(), + ); self.dependents.cam_dir = Vec3::from(self.dependents.view_mat.inverted() * -Vec4::unit_z()); } - pub fn frustum(&self) -> Frustum { - Frustum::from_modelview_projection( - (self.dependents.proj_mat * self.dependents.view_mat).into_col_arrays(), - ) - } + pub fn frustum(&self) -> &Frustum { &self.frustum } pub fn dependents(&self) -> Dependents { self.dependents } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 98ee3824e3..4a9526dc58 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -1,85 +1,166 @@ -use super::load::*; +use super::{load::*, FigureModelEntry}; use crate::{ - mesh::Meshable, - render::{FigurePipeline, Mesh, Model, Renderer}, + mesh::{greedy::GreedyMesh, Meshable}, + render::{BoneMeshes, FigureModel, FigurePipeline, Mesh, Renderer, TerrainPipeline}, scene::camera::CameraMode, }; use anim::Skeleton; use common::{ assets::watch::ReloadIndicator, comp::{ - item::{armor::ArmorKind, tool::ToolKind, ItemKind}, + item::{ + armor::{Armor, ArmorKind}, + tool::ToolKind, + ItemKind, + }, Body, CharacterState, Loadout, }, figure::Segment, vol::BaseVol, }; +use core::convert::TryInto; use hashbrown::{hash_map::Entry, HashMap}; -use std::{ - convert::TryInto, - mem::{discriminant, Discriminant}, -}; use vek::*; -#[allow(clippy::large_enum_variant)] // TODO: Pending review in #587 -#[derive(PartialEq, Eq, Hash, Clone)] -enum FigureKey { - Simple(Body), - Complex(Body, CameraMode, CharacterCacheKey), +pub type FigureModelEntryLod = FigureModelEntry<3>; + +#[derive(Eq, Hash, PartialEq)] +struct FigureKey { + /// Body pointed to by this key. + body: Body, + /// Extra state. + extra: Option>, } -#[derive(PartialEq, Eq, Hash, Clone)] +/// Character data that should be visible when tools are visible (i.e. in third +/// person or when the character is in a tool-using state). +#[derive(Eq, Hash, PartialEq)] +pub struct CharacterToolKey { + active: Option, + second: Option, +} + +/// Character data that exists in third person only. +#[derive(Eq, Hash, PartialEq)] +struct CharacterThirdPersonKey { + shoulder: Option, + chest: Option, + belt: Option, + back: Option, + pants: Option, +} + +#[derive(Eq, Hash, PartialEq)] +/// NOTE: To avoid spamming the character cache with player models, we try to +/// store only the minimum information required to correctly update the model. +/// +/// TODO: Memoize, etc. struct CharacterCacheKey { - state: Option>, // TODO: Can this be simplified? - active_tool: Option, - second_tool: Option, - shoulder: Option, - chest: Option, - belt: Option, - back: Option, + /// Character state that is only visible in third person. + third_person: Option, + /// Tool state should be present when a character is either in third person, + /// or is in first person and the character state is tool-using. + /// + /// NOTE: This representation could be tightened in various ways to + /// eliminate incorrect states, e.g. setting active_tool to None when no + /// tools are equipped, but currently we are more focused on the big + /// performance impact of recreating the whole model whenever the character + /// state changes, so for now we don't bother with this. + tool: Option, lantern: Option, - hand: Option, - pants: Option, - foot: Option, + hand: Option, + foot: Option, } impl CharacterCacheKey { - fn from(cs: Option<&CharacterState>, loadout: &Loadout) -> Self { + fn from(cs: Option<&CharacterState>, camera_mode: CameraMode, loadout: &Loadout) -> Self { + let is_first_person = match camera_mode { + CameraMode::FirstPerson => true, + CameraMode::ThirdPerson | CameraMode::Freefly => false, + }; + + // Third person tools are only modeled when the camera is either not first + // person, or the camera is first person and we are in a tool-using + // state. + let are_tools_visible = !is_first_person + || cs + .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) + // If there's no provided character state but we're still somehow in first person, + // We currently assume there's no need to visually model tools. + // + // TODO: Figure out what to do here, and/or refactor how this works. + .unwrap_or(false); + Self { - state: cs.map(|cs| discriminant(cs)), - active_tool: if let Some(ItemKind::Tool(tool)) = - loadout.active_item.as_ref().map(|i| &i.item.kind) - { - Some(tool.kind.clone()) - } else { + // Third person armor is only modeled when the camera mode is not first person. + third_person: if is_first_person { None - }, - second_tool: if let Some(ItemKind::Tool(tool)) = - loadout.second_item.as_ref().map(|i| &i.item.kind) - { - Some(tool.kind.clone()) } else { - None + Some(CharacterThirdPersonKey { + shoulder: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Shoulder(armor), + .. + })) = loadout.shoulder.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + chest: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Chest(armor), + .. + })) = loadout.chest.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + belt: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Belt(armor), + .. + })) = loadout.belt.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + back: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Back(armor), + .. + })) = loadout.back.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + pants: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Pants(armor), + .. + })) = loadout.pants.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + }) }, - shoulder: if let Some(ItemKind::Armor(armor)) = - loadout.shoulder.as_ref().map(|i| &i.kind) - { - Some(armor.kind.clone()) - } else { - None - }, - chest: if let Some(ItemKind::Armor(armor)) = loadout.chest.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) - } else { - None - }, - belt: if let Some(ItemKind::Armor(armor)) = loadout.belt.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) - } else { - None - }, - back: if let Some(ItemKind::Armor(armor)) = loadout.back.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) + tool: if are_tools_visible { + Some(CharacterToolKey { + active: if let Some(ItemKind::Tool(tool)) = + loadout.active_item.as_ref().map(|i| &i.item.kind) + { + Some(tool.kind.clone()) + } else { + None + }, + second: if let Some(ItemKind::Tool(tool)) = + loadout.second_item.as_ref().map(|i| &i.item.kind) + { + Some(tool.kind.clone()) + } else { + None + }, + }) } else { None }, @@ -90,18 +171,21 @@ impl CharacterCacheKey { } else { None }, - hand: if let Some(ItemKind::Armor(armor)) = loadout.hand.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) + hand: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Hand(armor), + .. + })) = loadout.hand.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) } else { None }, - pants: if let Some(ItemKind::Armor(armor)) = loadout.pants.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) - } else { - None - }, - foot: if let Some(ItemKind::Armor(armor)) = loadout.foot.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) + foot: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Foot(armor), + .. + })) = loadout.foot.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) } else { None }, @@ -114,7 +198,7 @@ pub struct FigureModelCache where Skel: Skeleton, { - models: HashMap; 3], Skel::Attr), u64)>, + models: HashMap, manifest_indicator: ReloadIndicator, } @@ -127,16 +211,18 @@ impl FigureModelCache { } } + /// NOTE: We deliberately call this function with only the key into the + /// cache, to enforce that the cached state only depends on the key. We + /// may end up using a mechanism different from this cache eventually, + /// in which case this strategy might change. fn bone_meshes( - body: Body, - loadout: Option<&Loadout>, - character_state: Option<&CharacterState>, - camera_mode: CameraMode, + FigureKey { body, extra }: &FigureKey, manifest_indicator: &mut ReloadIndicator, - generate_mesh: fn(&Segment, Vec3) -> Mesh, - ) -> [Option>; 16] { + mut generate_mesh: impl FnMut(Segment, Vec3) -> BoneMeshes, + ) -> [Option; 16] { match body { Body::Humanoid(body) => { + let humanoid_color_spec = HumColorSpec::load_watched(manifest_indicator); let humanoid_head_spec = HumHeadSpec::load_watched(manifest_indicator); let humanoid_armor_shoulder_spec = HumArmorShoulderSpec::load_watched(manifest_indicator); @@ -150,94 +236,126 @@ impl FigureModelCache { let humanoid_armor_foot_spec = HumArmorFootSpec::load_watched(manifest_indicator); let humanoid_main_weapon_spec = HumMainWeaponSpec::load_watched(manifest_indicator); + const DEFAULT_LOADOUT: CharacterCacheKey = CharacterCacheKey { + third_person: None, + tool: None, + lantern: None, + hand: None, + foot: None, + }; + // TODO: This is bad code, maybe this method should return Option<_> - let default_loadout = Loadout::default(); - let loadout = loadout.unwrap_or(&default_loadout); + let loadout = extra.as_deref().unwrap_or(&DEFAULT_LOADOUT); + let third_person = loadout.third_person.as_ref(); + let tool = loadout.tool.as_ref(); + let lantern = loadout.lantern.as_deref(); + let hand = loadout.hand.as_deref(); + let foot = loadout.foot.as_deref(); [ - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_head_spec.mesh_head(&body, generate_mesh)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => Some( - humanoid_armor_chest_spec.mesh_chest(&body, loadout, generate_mesh), - ), - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout, generate_mesh)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_back_spec.mesh_back(&body, loadout, generate_mesh)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => Some( - humanoid_armor_pants_spec.mesh_pants(&body, loadout, generate_mesh), - ), - CameraMode::FirstPerson => None, - }, - Some(humanoid_armor_hand_spec.mesh_left_hand(&body, loadout, generate_mesh)), - Some(humanoid_armor_hand_spec.mesh_right_hand(&body, loadout, generate_mesh)), - Some(humanoid_armor_foot_spec.mesh_left_foot(&body, loadout, generate_mesh)), - Some(humanoid_armor_foot_spec.mesh_right_foot(&body, loadout, generate_mesh)), - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_shoulder_spec.mesh_left_shoulder( - &body, - loadout, - generate_mesh, - )) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_shoulder_spec.mesh_right_shoulder( - &body, - loadout, - generate_mesh, - )) - }, - CameraMode::FirstPerson => None, - }, - Some(mesh_glider(generate_mesh)), - if camera_mode != CameraMode::FirstPerson - || character_state - .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) - .unwrap_or_default() - { - Some(humanoid_main_weapon_spec.mesh_main_weapon( - loadout.active_item.as_ref().map(|i| &i.item.kind), + third_person.map(|_| { + humanoid_head_spec.mesh_head( + body, + &humanoid_color_spec, + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_chest_spec.mesh_chest( + body, + &humanoid_color_spec, + loadout.chest.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_belt_spec.mesh_belt( + body, + &humanoid_color_spec, + loadout.belt.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_back_spec.mesh_back( + body, + &humanoid_color_spec, + loadout.back.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_pants_spec.mesh_pants( + body, + &humanoid_color_spec, + loadout.pants.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + Some(humanoid_armor_hand_spec.mesh_left_hand( + body, + &humanoid_color_spec, + hand, + |segment, offset| generate_mesh(segment, offset), + )), + Some(humanoid_armor_hand_spec.mesh_right_hand( + body, + &humanoid_color_spec, + hand, + |segment, offset| generate_mesh(segment, offset), + )), + Some(humanoid_armor_foot_spec.mesh_left_foot( + body, + &humanoid_color_spec, + foot, + |segment, offset| generate_mesh(segment, offset), + )), + Some(humanoid_armor_foot_spec.mesh_right_foot( + body, + &humanoid_color_spec, + foot, + |segment, offset| generate_mesh(segment, offset), + )), + third_person.map(|loadout| { + humanoid_armor_shoulder_spec.mesh_left_shoulder( + body, + &humanoid_color_spec, + loadout.shoulder.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_shoulder_spec.mesh_right_shoulder( + body, + &humanoid_color_spec, + loadout.shoulder.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + Some(mesh_glider(|segment, offset| { + generate_mesh(segment, offset) + })), + tool.map(|tool| { + humanoid_main_weapon_spec.mesh_main_weapon( + tool.active.as_ref(), false, - generate_mesh, - )) - } else { - None - }, - if camera_mode != CameraMode::FirstPerson - || character_state - .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) - .unwrap_or_default() - { - Some(humanoid_main_weapon_spec.mesh_main_weapon( - loadout.second_item.as_ref().map(|i| &i.item.kind), + |segment, offset| generate_mesh(segment, offset), + ) + }), + tool.map(|tool| { + humanoid_main_weapon_spec.mesh_main_weapon( + tool.second.as_ref(), true, - generate_mesh, - )) - } else { - None - }, - Some(humanoid_armor_lantern_spec.mesh_lantern(&body, loadout, generate_mesh)), - Some(mesh_hold(generate_mesh)), + |segment, offset| generate_mesh(segment, offset), + ) + }), + Some(humanoid_armor_lantern_spec.mesh_lantern( + body, + &humanoid_color_spec, + lantern, + |segment, offset| generate_mesh(segment, offset), + )), + Some(mesh_hold(|segment, offset| generate_mesh(segment, offset))), ] }, Body::QuadrupedSmall(body) => { @@ -250,37 +368,37 @@ impl FigureModelCache { Some(quadruped_small_central_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_central_spec.mesh_chest( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_central_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -303,77 +421,77 @@ impl FigureModelCache { Some(quadruped_medium_central_spec.mesh_head_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_head_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_jaw( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_torso_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_torso_back( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_ears( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, ] @@ -388,37 +506,37 @@ impl FigureModelCache { Some(bird_medium_center_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_center_spec.mesh_torso( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_center_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_wing_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_wing_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_foot_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_foot_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -432,12 +550,24 @@ impl FigureModelCache { ] }, Body::FishMedium(body) => [ - Some(mesh_fish_medium_head(body.head, generate_mesh)), - Some(mesh_fish_medium_torso(body.torso, generate_mesh)), - Some(mesh_fish_medium_rear(body.rear, generate_mesh)), - Some(mesh_fish_medium_tail(body.tail, generate_mesh)), - Some(mesh_fish_medium_fin_l(body.fin_l, generate_mesh)), - Some(mesh_fish_medium_fin_r(body.fin_r, generate_mesh)), + Some(mesh_fish_medium_head(body.head, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_torso(body.torso, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_rear(body.rear, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_tail(body.tail, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_fin_l(body.fin_l, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_fin_r(body.fin_r, |segment, offset| { + generate_mesh(segment, offset) + })), None, None, None, @@ -457,82 +587,94 @@ impl FigureModelCache { Some(dragon_center_spec.mesh_head_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_head_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), + )), + Some(dragon_center_spec.mesh_jaw( + body.species, + body.body_type, + |segment, offset| generate_mesh(segment, offset), )), - Some(dragon_center_spec.mesh_jaw(body.species, body.body_type, generate_mesh)), Some(dragon_center_spec.mesh_chest_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_chest_rear( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_tail_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_tail_rear( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_in_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_in_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_out_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_out_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, ] }, Body::BirdSmall(body) => [ - Some(mesh_bird_small_head(body.head, generate_mesh)), - Some(mesh_bird_small_torso(body.torso, generate_mesh)), - Some(mesh_bird_small_wing_l(body.wing_l, generate_mesh)), - Some(mesh_bird_small_wing_r(body.wing_r, generate_mesh)), + Some(mesh_bird_small_head(body.head, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_bird_small_torso(body.torso, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_bird_small_wing_l(body.wing_l, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_bird_small_wing_r(body.wing_r, |segment, offset| { + generate_mesh(segment, offset) + })), None, None, None, @@ -547,8 +689,12 @@ impl FigureModelCache { None, ], Body::FishSmall(body) => [ - Some(mesh_fish_small_torso(body.torso, generate_mesh)), - Some(mesh_fish_small_tail(body.tail, generate_mesh)), + Some(mesh_fish_small_torso(body.torso, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_small_tail(body.tail, |segment, offset| { + generate_mesh(segment, offset) + })), None, None, None, @@ -574,77 +720,77 @@ impl FigureModelCache { Some(biped_large_center_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_jaw( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_torso_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_torso_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_main( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_second( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_shoulder_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_shoulder_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_hand_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_hand_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_leg_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_leg_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_foot_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_foot_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, ] @@ -654,51 +800,55 @@ impl FigureModelCache { let golem_lateral_spec = GolemLateralSpec::load_watched(manifest_indicator); [ - Some(golem_center_spec.mesh_head(body.species, body.body_type, generate_mesh)), + Some(golem_center_spec.mesh_head( + body.species, + body.body_type, + |segment, offset| generate_mesh(segment, offset), + )), Some(golem_center_spec.mesh_torso_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_shoulder_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_shoulder_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_hand_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_hand_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_leg_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_leg_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_foot_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_foot_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -715,27 +865,27 @@ impl FigureModelCache { Some(critter_center_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_chest( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_feet_f( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_feet_b( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -760,52 +910,52 @@ impl FigureModelCache { Some(quadruped_low_central_spec.mesh_head_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_head_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_jaw( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_chest( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_tail_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_tail_rear( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -839,24 +989,26 @@ impl FigureModelCache { pub fn get_or_create_model( &mut self, renderer: &mut Renderer, + col_lights: &mut super::FigureColLights, body: Body, loadout: Option<&Loadout>, tick: u64, camera_mode: CameraMode, character_state: Option<&CharacterState>, - ) -> &([Model; 3], Skel::Attr) + ) -> &(FigureModelEntryLod, Skel::Attr) where for<'a> &'a common::comp::Body: std::convert::TryInto, Skel::Attr: Default, { - let key = if let Some(loadout) = loadout { - FigureKey::Complex( - body, - camera_mode, - CharacterCacheKey::from(character_state, loadout), - ) - } else { - FigureKey::Simple(body) + let key = FigureKey { + body, + extra: loadout.map(|loadout| { + Box::new(CharacterCacheKey::from( + character_state, + camera_mode, + loadout, + )) + }), }; match self.models.entry(key) { @@ -866,92 +1018,171 @@ impl FigureModelCache { model }, Entry::Vacant(v) => { - &v.insert(( - { - let skeleton_attr = (&body) - .try_into() - .ok() - .unwrap_or_else(::default); + let key = v.key(); + let model = { + let skeleton_attr = (&body) + .try_into() + .ok() + .unwrap_or_else(::default); - let manifest_indicator = &mut self.manifest_indicator; - let mut make_model = |generate_mesh| { - let mut mesh = Mesh::new(); - Self::bone_meshes( - body, - loadout, - character_state, - camera_mode, - manifest_indicator, - generate_mesh, - ) + let manifest_indicator = &mut self.manifest_indicator; + let mut greedy = FigureModel::make_greedy(); + let mut opaque = Mesh::::new(); + // Choose the most conservative bounds for any LOD model. + let mut figure_bounds = anim::vek::Aabb { + min: anim::vek::Vec3::zero(), + max: anim::vek::Vec3::zero(), + }; + // Meshes all bone models for this figure using the given mesh generation + // function, attaching it to the current greedy mesher and opaque vertex + // list. Returns the vertex bounds of the meshed model within the opaque + // mesh. + let mut make_model = |generate_mesh: for<'a, 'b> fn( + &mut GreedyMesh<'a>, + &'b mut _, + _, + _, + ) + -> _| { + let vertex_start = opaque.vertices().len(); + let meshes = + Self::bone_meshes(key, manifest_indicator, |segment, offset| { + generate_mesh(&mut greedy, &mut opaque, segment, offset) + }); + meshes .iter() .enumerate() - .filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm))) - .for_each(|(i, bone_mesh)| { - mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8)) + // NOTE: Cast to u8 is safe because i <= 16. + .filter_map(|(i, bm)| bm.as_ref().map(|bm| (i as u8, bm.clone()))) + .for_each(|(i, (_opaque_mesh, (bounds, vertex_range)))| { + // Update the bone index for all vertices that belong to this + // model. + opaque + .iter_mut(vertex_range) + .for_each(|vert| { + vert.set_bone_idx(i); + }); + + // Update the figure bounds to the largest granularity seen so far + // (NOTE: this is more than a little imeprfect). + // + // FIXME: Maybe use the default bone position in the idle animation + // to figure this out instead? + figure_bounds.expand_to_contain(bounds); }); - renderer.create_model(&mesh).unwrap() - }; + // NOTE: vertex_start and vertex_end *should* fit in a u32, by the + // following logic: + // + // Our new figure maximum is constrained to at most 2^8 × 2^8 × 2^8. + // This uses at most 24 bits to store every vertex exactly once. + // Greedy meshing can store each vertex in up to 3 quads, we have 3 + // greedy models, and we store 1.5x the vertex count, so the maximum + // total space a model can take up is 3 * 3 * 1.5 * 2^24; rounding + // up to 4 * 4 * 2^24 gets us to 2^28, which clearly still fits in a + // u32. + // + // (We could also, though we prefer not to, reason backwards from the + // maximum figure texture size of 2^15 × 2^15, also fits in a u32; we + // can also see that, since we can have at most one texture entry per + // vertex, any texture atlas of size 2^14 × 2^14 or higher should be + // able to store data for any figure. So the only reason we would fail + // here would be if the user's computer could not store a texture large + // enough to fit all the LOD models for the figure, not for fundamental + // reasons related to fitting in a u32). + // + // Therefore, these casts are safe. + vertex_start as u32..opaque.vertices().len() as u32 + }; - fn generate_mesh( - segment: &Segment, - offset: Vec3, - ) -> Mesh { - Meshable::::generate_mesh( + fn generate_mesh<'a>( + greedy: &mut GreedyMesh<'a>, + opaque_mesh: &mut Mesh, + segment: Segment, + offset: Vec3, + ) -> BoneMeshes { + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( segment, - (offset, Vec3::one()), - ) - .0 - } + (greedy, opaque_mesh, offset, Vec3::one()), + ); + (opaque, bounds) + } - fn generate_mesh_lod_mid( - segment: &Segment, - offset: Vec3, - ) -> Mesh { - let lod_scale = Vec3::broadcast(0.6); - Meshable::::generate_mesh( - &segment.scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0 - } + fn generate_mesh_lod_mid<'a>( + greedy: &mut GreedyMesh<'a>, + opaque_mesh: &mut Mesh, + segment: Segment, + offset: Vec3, + ) -> BoneMeshes { + let lod_scale = 0.6; + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment.scaled_by(Vec3::broadcast(lod_scale)), + ( + greedy, + opaque_mesh, + offset * lod_scale, + Vec3::one() / lod_scale, + ), + ); + (opaque, bounds) + } - fn generate_mesh_lod_low( - segment: &Segment, - offset: Vec3, - ) -> Mesh { - let lod_scale = Vec3::broadcast(0.3); - Meshable::::generate_mesh( - &segment.scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0 - } + fn generate_mesh_lod_low<'a>( + greedy: &mut GreedyMesh<'a>, + opaque_mesh: &mut Mesh, + segment: Segment, + offset: Vec3, + ) -> BoneMeshes { + let lod_scale = 0.3; + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment.scaled_by(Vec3::broadcast(lod_scale)), + ( + greedy, + opaque_mesh, + offset * lod_scale, + Vec3::one() / lod_scale, + ), + ); + (opaque, bounds) + } - ( - [ - make_model(generate_mesh), - make_model(generate_mesh_lod_mid), - make_model(generate_mesh_lod_low), - ], - skeleton_attr, - ) - }, - tick, - )) - .0 + let models = [ + make_model(generate_mesh), + make_model(generate_mesh_lod_mid), + make_model(generate_mesh_lod_low), + ]; + ( + col_lights + .create_figure(renderer, greedy, (opaque, figure_bounds), models) + .expect("Failed to upload figure data to the GPU!"), + skeleton_attr, + ) + }; + &v.insert((model, tick)).0 }, } } - pub fn clean(&mut self, tick: u64) { + pub fn clean(&mut self, col_lights: &mut super::FigureColLights, tick: u64) { // Check for reloaded manifests // TODO: maybe do this in a different function, maintain? if self.manifest_indicator.reloaded() { + col_lights.atlas.clear(); self.models.clear(); } // TODO: Don't hard-code this. - self.models - .retain(|_, (_, last_used)| *last_used + 60 > tick); + if tick % 60 == 0 { + self.models.retain(|_, ((model_entry, _), last_used)| { + // Wait about a minute at 60 fps before invalidating old models. + let delta = 60 * 60; + let alive = *last_used + delta > tick; + if !alive { + col_lights.atlas.deallocate(model_entry.allocation.id); + } + alive + }); + } } } diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index e03d9ff608..c499f6d29d 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -1,4 +1,4 @@ -use crate::render::{FigurePipeline, Mesh}; +use crate::render::{BoneMeshes, Mesh}; use common::{ assets::{self, watch::ReloadIndicator, Asset}, comp::{ @@ -9,17 +9,12 @@ use common::{ dragon::{BodyType as DBodyType, Species as DSpecies}, fish_medium, fish_small, golem::{BodyType as GBodyType, Species as GSpecies}, - humanoid::{Body, BodyType, EyeColor, Skin, Species}, - item::{ - armor::{Armor, ArmorKind}, - tool::{Tool, ToolKind}, - ItemKind, Lantern, - }, + humanoid::{self, Body, BodyType, EyeColor, Skin, Species}, + item::tool::ToolKind, object, quadruped_low::{BodyType as QLBodyType, Species as QLSpecies}, quadruped_medium::{BodyType as QMBodyType, Species as QMSpecies}, quadruped_small::{BodyType as QSBodyType, Species as QSSpecies}, - Loadout, }, figure::{DynaUnionizer, MatSegment, Material, Segment}, }; @@ -60,28 +55,9 @@ fn graceful_load_mat_segment_flipped(mesh_name: &str) -> MatSegment { pub fn load_mesh( mesh_name: &str, position: Vec3, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { - generate_mesh(&load_segment(mesh_name), position) -} - -fn color_segment( - mat_segment: MatSegment, - skin: Skin, - hair_color: Rgb, - eye_color: EyeColor, -) -> Segment { - // TODO move some of the colors to common - mat_segment.to_segment(|mat| match mat { - Material::Skin => skin.rgb(), - Material::SkinDark => skin.dark_rgb(), - Material::SkinLight => skin.light_rgb(), - Material::Hair => hair_color, - // TODO add back multiple colors - Material::EyeLight => eye_color.light_rgb(), - Material::EyeDark => eye_color.dark_rgb(), - Material::EyeWhite => eye_color.white_rgb(), - }) + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { + generate_mesh(load_segment(mesh_name), position) } fn recolor_grey(rgb: Rgb, color: Rgb) -> Rgb { @@ -126,6 +102,63 @@ struct MobSidedVoxSpec { right: ArmorVoxSpec, } +/// Color information not found in voxels, for humanoids. +#[derive(Serialize, Deserialize)] +pub struct HumColorSpec { + hair_colors: humanoid::species::PureCases>, + eye_colors_light: humanoid::eye_color::PureCases<(u8, u8, u8)>, + eye_colors_dark: humanoid::eye_color::PureCases<(u8, u8, u8)>, + eye_white: (u8, u8, u8), + skin_colors_plain: humanoid::skin::PureCases<(u8, u8, u8)>, + skin_colors_light: humanoid::skin::PureCases<(u8, u8, u8)>, + skin_colors_dark: humanoid::skin::PureCases<(u8, u8, u8)>, +} + +impl Asset for HumColorSpec { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) + } +} + +impl HumColorSpec { + pub fn load_watched(indicator: &mut ReloadIndicator) -> Arc { + assets::load_watched::("voxygen.voxel.humanoid_color_manifest", indicator).unwrap() + } + + fn hair_color(&self, species: Species, val: u8) -> (u8, u8, u8) { + species + .elim_case_pure(&self.hair_colors) + .get(val as usize) + .copied() + .unwrap_or((0, 0, 0)) + } + + fn color_segment( + &self, + mat_segment: MatSegment, + skin: Skin, + hair_color: (u8, u8, u8), + eye_color: EyeColor, + ) -> Segment { + // TODO move some of the colors to common + mat_segment.to_segment(|mat| { + match mat { + Material::Skin => *skin.elim_case_pure(&self.skin_colors_plain), + Material::SkinDark => *skin.elim_case_pure(&self.skin_colors_dark), + Material::SkinLight => *skin.elim_case_pure(&self.skin_colors_light), + Material::Hair => hair_color, + // TODO add back multiple colors + Material::EyeLight => *eye_color.elim_case_pure(&self.eye_colors_light), + Material::EyeDark => *eye_color.elim_case_pure(&self.eye_colors_dark), + Material::EyeWhite => self.eye_white, + } + .into() + }) + } +} + // All reliant on humanoid::Species and humanoid::BodyType #[derive(Serialize, Deserialize)] struct HumHeadSubSpec { @@ -155,8 +188,9 @@ impl HumHeadSpec { pub fn mesh_head( &self, body: &Body, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + color_spec: &HumColorSpec, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(body.species, body.body_type)) { Some(spec) => spec, None => { @@ -169,7 +203,8 @@ impl HumHeadSpec { }, }; - let hair_rgb = body.species.hair_color(body.hair_color); + let hair_color = color_spec.hair_color(body.species, body.hair_color); + let hair_rgb = hair_color.into(); let skin_rgb = body.species.skin_color(body.skin); let eye_rgb = body.species.eye_color(body.eye_color); @@ -178,10 +213,10 @@ impl HumHeadSpec { let eyes = match spec.eyes.get(body.eyes as usize) { Some(Some(spec)) => Some(( - color_segment( + color_spec.color_segment( graceful_load_mat_segment(&spec.0).map_rgb(|rgb| recolor_grey(rgb, hair_rgb)), skin_rgb, - hair_rgb, + hair_color, eye_rgb, ), Vec3::from(spec.1), @@ -225,7 +260,7 @@ impl HumHeadSpec { let (head, origin_offset) = DynaUnionizer::new() .add( - color_segment(bare_head, skin_rgb, hair_rgb, eye_rgb), + color_spec.color_segment(bare_head, skin_rgb, hair_color, eye_rgb), spec.head.1.into(), ) .maybe_add(eyes) @@ -235,7 +270,7 @@ impl HumHeadSpec { .unify(); generate_mesh( - &head, + head, Vec3::from(spec.offset) + origin_offset.map(|e| e as f32 * -1.0), ) } @@ -361,15 +396,12 @@ impl HumArmorShoulderSpec { fn mesh_shoulder( &self, body: &Body, - loadout: &Loadout, + color_spec: &HumColorSpec, + shoulder: Option<&str>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Shoulder(shoulder), - .. - })) = loadout.shoulder.as_ref().map(|i| &i.kind) - { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(shoulder) = shoulder { match self.0.map.get(shoulder) { Some(spec) => spec, None => { @@ -381,14 +413,14 @@ impl HumArmorShoulderSpec { &self.0.default }; - let mut shoulder_segment = color_segment( + let mut shoulder_segment = color_spec.color_segment( if flipped { graceful_load_mat_segment_flipped(&spec.left.vox_spec.0) } else { graceful_load_mat_segment(&spec.right.vox_spec.0) }, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -413,25 +445,27 @@ impl HumArmorShoulderSpec { shoulder_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(shoulder_color))); } - generate_mesh(&shoulder_segment, Vec3::from(offset)) + generate_mesh(shoulder_segment, Vec3::from(offset)) } pub fn mesh_left_shoulder( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_shoulder(body, loadout, true, generate_mesh) + color_spec: &HumColorSpec, + shoulder: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_shoulder(body, color_spec, shoulder, true, generate_mesh) } pub fn mesh_right_shoulder( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_shoulder(body, loadout, false, generate_mesh) + color_spec: &HumColorSpec, + shoulder: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_shoulder(body, color_spec, shoulder, false, generate_mesh) } } // Chest @@ -444,18 +478,15 @@ impl HumArmorChestSpec { pub fn mesh_chest( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Chest(chest), - .. - })) = loadout.chest.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + chest: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(chest) = chest { match self.0.map.get(chest) { Some(spec) => spec, None => { - error!(?loadout.chest, "No chest specification exists"); + error!(?chest, "No chest specification exists"); return load_mesh("not_found", Vec3::new(-7.0, -3.5, 2.0), generate_mesh); }, } @@ -464,10 +495,10 @@ impl HumArmorChestSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -487,7 +518,7 @@ impl HumArmorChestSpec { .unify() .0; - generate_mesh(&chest, Vec3::from(spec.vox_spec.1)) + generate_mesh(chest, Vec3::from(spec.vox_spec.1)) } } // Hand @@ -500,15 +531,12 @@ impl HumArmorHandSpec { fn mesh_hand( &self, body: &Body, - loadout: &Loadout, + color_spec: &HumColorSpec, + hand: Option<&str>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Hand(hand), - .. - })) = loadout.hand.as_ref().map(|i| &i.kind) - { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(hand) = hand { match self.0.map.get(hand) { Some(spec) => spec, None => { @@ -520,14 +548,14 @@ impl HumArmorHandSpec { &self.0.default }; - let mut hand_segment = color_segment( + let mut hand_segment = color_spec.color_segment( if flipped { graceful_load_mat_segment_flipped(&spec.left.vox_spec.0) } else { graceful_load_mat_segment(&spec.right.vox_spec.0) }, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -546,25 +574,27 @@ impl HumArmorHandSpec { hand_segment = hand_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(hand_color))); } - generate_mesh(&hand_segment, Vec3::from(offset)) + generate_mesh(hand_segment, Vec3::from(offset)) } pub fn mesh_left_hand( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_hand(body, loadout, true, generate_mesh) + color_spec: &HumColorSpec, + hand: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_hand(body, color_spec, hand, true, generate_mesh) } pub fn mesh_right_hand( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_hand(body, loadout, false, generate_mesh) + color_spec: &HumColorSpec, + hand: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_hand(body, color_spec, hand, false, generate_mesh) } } // Belt @@ -577,14 +607,11 @@ impl HumArmorBeltSpec { pub fn mesh_belt( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Belt(belt), - .. - })) = loadout.belt.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + belt: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(belt) = belt { match self.0.map.get(belt) { Some(spec) => spec, None => { @@ -596,10 +623,10 @@ impl HumArmorBeltSpec { &self.0.default }; - let mut belt_segment = color_segment( + let mut belt_segment = color_spec.color_segment( graceful_load_mat_segment(&spec.vox_spec.0), body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -608,7 +635,7 @@ impl HumArmorBeltSpec { belt_segment = belt_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(belt_color))); } - generate_mesh(&belt_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(belt_segment, Vec3::from(spec.vox_spec.1)) } } // Cape @@ -621,14 +648,11 @@ impl HumArmorBackSpec { pub fn mesh_back( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Back(back), - .. - })) = loadout.back.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + back: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(back) = back { match self.0.map.get(back) { Some(spec) => spec, None => { @@ -640,10 +664,10 @@ impl HumArmorBackSpec { &self.0.default }; - let mut back_segment = color_segment( + let mut back_segment = color_spec.color_segment( graceful_load_mat_segment(&spec.vox_spec.0), body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); if let Some(color) = spec.color { @@ -651,7 +675,7 @@ impl HumArmorBackSpec { back_segment = back_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(back_color))); } - generate_mesh(&back_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(back_segment, Vec3::from(spec.vox_spec.1)) } } // Legs @@ -664,14 +688,11 @@ impl HumArmorPantsSpec { pub fn mesh_pants( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Pants(pants), - .. - })) = loadout.pants.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + pants: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(pants) = pants { match self.0.map.get(pants) { Some(spec) => spec, None => { @@ -684,10 +705,10 @@ impl HumArmorPantsSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -707,7 +728,7 @@ impl HumArmorPantsSpec { .unify() .0; - generate_mesh(&pants, Vec3::from(spec.vox_spec.1)) + generate_mesh(pants, Vec3::from(spec.vox_spec.1)) } } // Foot @@ -720,15 +741,12 @@ impl HumArmorFootSpec { fn mesh_foot( &self, body: &Body, - loadout: &Loadout, + color_spec: &HumColorSpec, + foot: Option<&str>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Foot(foot), - .. - })) = loadout.foot.as_ref().map(|i| &i.kind) - { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(foot) = foot { match self.0.map.get(foot) { Some(spec) => spec, None => { @@ -740,14 +758,14 @@ impl HumArmorFootSpec { &self.0.default }; - let mut foot_segment = color_segment( + let mut foot_segment = color_spec.color_segment( if flipped { graceful_load_mat_segment_flipped(&spec.vox_spec.0) } else { graceful_load_mat_segment(&spec.vox_spec.0) }, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -756,25 +774,27 @@ impl HumArmorFootSpec { foot_segment = foot_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(foot_color))); } - generate_mesh(&foot_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(foot_segment, Vec3::from(spec.vox_spec.1)) } pub fn mesh_left_foot( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_foot(body, loadout, true, generate_mesh) + color_spec: &HumColorSpec, + foot: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_foot(body, color_spec, foot, true, generate_mesh) } pub fn mesh_right_foot( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_foot(body, loadout, false, generate_mesh) + color_spec: &HumColorSpec, + foot: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_foot(body, color_spec, foot, false, generate_mesh) } } @@ -786,14 +806,14 @@ impl HumMainWeaponSpec { pub fn mesh_main_weapon( &self, - item_kind: Option<&ItemKind>, + tool_kind: Option<&ToolKind>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let tool_kind = if let Some(ItemKind::Tool(Tool { kind, .. })) = item_kind { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let tool_kind = if let Some(kind) = tool_kind { kind } else { - return Mesh::new(); + return (Mesh::new(), (anim::vek::Aabb::default(), 0..0)); }; let spec = match self.0.get(tool_kind) { @@ -822,7 +842,7 @@ impl HumMainWeaponSpec { spec.vox_spec.1[2], ); - generate_mesh(&tool_kind_segment, offset) + generate_mesh(tool_kind_segment, offset) } } @@ -835,12 +855,11 @@ impl HumArmorLanternSpec { pub fn mesh_lantern( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Lantern(Lantern { kind, .. })) = - loadout.lantern.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + lantern: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(kind) = lantern { match self.0.map.get(kind) { Some(spec) => spec, None => { @@ -852,10 +871,10 @@ impl HumArmorLanternSpec { &self.0.default }; - let mut lantern_segment = color_segment( + let mut lantern_segment = color_spec.color_segment( graceful_load_mat_segment(&spec.vox_spec.0), body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); if let Some(color) = spec.color { @@ -864,7 +883,7 @@ impl HumArmorLanternSpec { lantern_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(lantern_color))); } - generate_mesh(&lantern_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(lantern_segment, Vec3::from(spec.vox_spec.1)) } } impl HumArmorHeadSpec { @@ -876,14 +895,11 @@ impl HumArmorHeadSpec { pub fn mesh_head( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Head(head), - .. - })) = loadout.head.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + head: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(head) = head { match self.0.map.get(head) { Some(spec) => spec, None => { @@ -896,10 +912,10 @@ impl HumArmorHeadSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -919,7 +935,7 @@ impl HumArmorHeadSpec { .unify() .0; - generate_mesh(&head, Vec3::from(spec.vox_spec.1)) + generate_mesh(head, Vec3::from(spec.vox_spec.1)) } } impl HumArmorTabardSpec { @@ -931,14 +947,11 @@ impl HumArmorTabardSpec { pub fn mesh_tabard( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Tabard(tabard), - .. - })) = loadout.tabard.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + tabard: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(tabard) = tabard { match self.0.map.get(tabard) { Some(spec) => spec, None => { @@ -951,10 +964,10 @@ impl HumArmorTabardSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -974,13 +987,11 @@ impl HumArmorTabardSpec { .unify() .0; - generate_mesh(&tabard, Vec3::from(spec.vox_spec.1)) + generate_mesh(tabard, Vec3::from(spec.vox_spec.1)) } } // TODO: Inventory -pub fn mesh_glider( - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { +pub fn mesh_glider(generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes) -> BoneMeshes { load_mesh( "object.glider", Vec3::new(-26.0, -26.0, -5.0), @@ -988,9 +999,7 @@ pub fn mesh_glider( ) } -pub fn mesh_hold( - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { +pub fn mesh_hold(generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes) -> BoneMeshes { load_mesh( "weapon.projectile.simple-arrow", Vec3::new(-0.5, -6.0, -1.5), @@ -1056,8 +1065,8 @@ impl QuadrupedSmallCentralSpec { &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1070,15 +1079,15 @@ impl QuadrupedSmallCentralSpec { }; let central = graceful_load_segment(&spec.head.central.0); - generate_mesh(¢ral, Vec3::from(spec.head.offset)) + generate_mesh(central, Vec3::from(spec.head.offset)) } pub fn mesh_chest( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1091,15 +1100,15 @@ impl QuadrupedSmallCentralSpec { }; let central = graceful_load_segment(&spec.chest.central.0); - generate_mesh(¢ral, Vec3::from(spec.chest.offset)) + generate_mesh(central, Vec3::from(spec.chest.offset)) } pub fn mesh_tail( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1112,7 +1121,7 @@ impl QuadrupedSmallCentralSpec { }; let central = graceful_load_segment(&spec.tail.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail.offset)) + generate_mesh(central, Vec3::from(spec.tail.offset)) } } @@ -1126,8 +1135,8 @@ impl QuadrupedSmallLateralSpec { &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1140,15 +1149,15 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.left_front.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.left_front.offset)) + generate_mesh(lateral, Vec3::from(spec.left_front.offset)) } pub fn mesh_foot_fr( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1161,15 +1170,15 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.right_front.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.right_front.offset)) + generate_mesh(lateral, Vec3::from(spec.right_front.offset)) } pub fn mesh_foot_bl( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1182,15 +1191,15 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.left_back.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.left_back.offset)) + generate_mesh(lateral, Vec3::from(spec.left_back.offset)) } pub fn mesh_foot_br( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1203,7 +1212,7 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.right_back.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.right_back.offset)) + generate_mesh(lateral, Vec3::from(spec.right_back.offset)) } } @@ -1272,8 +1281,8 @@ impl QuadrupedMediumCentralSpec { &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1286,15 +1295,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.upper.central.0); - generate_mesh(¢ral, Vec3::from(spec.upper.offset)) + generate_mesh(central, Vec3::from(spec.upper.offset)) } pub fn mesh_head_lower( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1307,15 +1316,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.lower.central.0); - generate_mesh(¢ral, Vec3::from(spec.lower.offset)) + generate_mesh(central, Vec3::from(spec.lower.offset)) } pub fn mesh_jaw( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1328,15 +1337,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.jaw.central.0); - generate_mesh(¢ral, Vec3::from(spec.jaw.offset)) + generate_mesh(central, Vec3::from(spec.jaw.offset)) } pub fn mesh_ears( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1349,15 +1358,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.ears.central.0); - generate_mesh(¢ral, Vec3::from(spec.ears.offset)) + generate_mesh(central, Vec3::from(spec.ears.offset)) } pub fn mesh_torso_front( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1370,15 +1379,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.torso_front.central.0); - generate_mesh(¢ral, Vec3::from(spec.torso_front.offset)) + generate_mesh(central, Vec3::from(spec.torso_front.offset)) } pub fn mesh_torso_back( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1391,15 +1400,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.torso_back.central.0); - generate_mesh(¢ral, Vec3::from(spec.torso_back.offset)) + generate_mesh(central, Vec3::from(spec.torso_back.offset)) } pub fn mesh_tail( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1412,7 +1421,7 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.tail.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail.offset)) + generate_mesh(central, Vec3::from(spec.tail.offset)) } } @@ -1426,8 +1435,8 @@ impl QuadrupedMediumLateralSpec { &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1440,15 +1449,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_fl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_fl.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_fl.offset)) } pub fn mesh_leg_fr( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1461,15 +1470,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_fr.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_fr.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_fr.offset)) } pub fn mesh_leg_bl( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1482,15 +1491,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_bl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_bl.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_bl.offset)) } pub fn mesh_leg_br( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1503,15 +1512,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_br.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_br.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_br.offset)) } pub fn mesh_foot_fl( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1524,15 +1533,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_fl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_fl.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_fl.offset)) } pub fn mesh_foot_fr( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1545,15 +1554,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_fr.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_fr.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_fr.offset)) } pub fn mesh_foot_bl( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1566,15 +1575,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_bl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_bl.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_bl.offset)) } pub fn mesh_foot_br( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1587,7 +1596,7 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_br.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_br.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_br.offset)) } } @@ -1649,8 +1658,8 @@ impl BirdMediumCenterSpec { &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1663,15 +1672,15 @@ impl BirdMediumCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_torso( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1684,15 +1693,15 @@ impl BirdMediumCenterSpec { }; let center = graceful_load_segment(&spec.torso.center.0); - generate_mesh(¢er, Vec3::from(spec.torso.offset)) + generate_mesh(center, Vec3::from(spec.torso.offset)) } pub fn mesh_tail( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1705,7 +1714,7 @@ impl BirdMediumCenterSpec { }; let center = graceful_load_segment(&spec.tail.center.0); - generate_mesh(¢er, Vec3::from(spec.tail.offset)) + generate_mesh(center, Vec3::from(spec.tail.offset)) } } impl BirdMediumLateralSpec { @@ -1718,8 +1727,8 @@ impl BirdMediumLateralSpec { &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1732,15 +1741,15 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_l.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_l.offset)) } pub fn mesh_wing_r( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1753,15 +1762,15 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_r.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_r.offset)) } pub fn mesh_foot_l( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1774,15 +1783,15 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_l.offset)) } pub fn mesh_foot_r( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1795,7 +1804,7 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_r.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_r.offset)) } } //// @@ -1833,8 +1842,8 @@ impl CritterCenterSpec { &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1847,15 +1856,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_chest( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1868,15 +1877,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.chest.center.0); - generate_mesh(¢er, Vec3::from(spec.chest.offset)) + generate_mesh(center, Vec3::from(spec.chest.offset)) } pub fn mesh_feet_f( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1889,15 +1898,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.feet_f.center.0); - generate_mesh(¢er, Vec3::from(spec.feet_f.offset)) + generate_mesh(center, Vec3::from(spec.feet_f.offset)) } pub fn mesh_feet_b( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1910,15 +1919,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.feet_b.center.0); - generate_mesh(¢er, Vec3::from(spec.feet_b.offset)) + generate_mesh(center, Vec3::from(spec.feet_b.offset)) } pub fn mesh_tail( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1931,14 +1940,14 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.tail.center.0); - generate_mesh(¢er, Vec3::from(spec.tail.offset)) + generate_mesh(center, Vec3::from(spec.tail.offset)) } } //// pub fn mesh_fish_medium_head( head: fish_medium::Head, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match head { fish_medium::Head::Default => "npc.marlin.head", @@ -1950,8 +1959,8 @@ pub fn mesh_fish_medium_head( pub fn mesh_fish_medium_torso( torso: fish_medium::Torso, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match torso { fish_medium::Torso::Default => "npc.marlin.torso", @@ -1963,8 +1972,8 @@ pub fn mesh_fish_medium_torso( pub fn mesh_fish_medium_rear( rear: fish_medium::Rear, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match rear { fish_medium::Rear::Default => "npc.marlin.rear", @@ -1976,8 +1985,8 @@ pub fn mesh_fish_medium_rear( pub fn mesh_fish_medium_tail( tail: fish_medium::Tail, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match tail { fish_medium::Tail::Default => "npc.marlin.tail", @@ -1989,8 +1998,8 @@ pub fn mesh_fish_medium_tail( pub fn mesh_fish_medium_fin_l( fin_l: fish_medium::FinL, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match fin_l { fish_medium::FinL::Default => "npc.marlin.fin_l", @@ -2002,8 +2011,8 @@ pub fn mesh_fish_medium_fin_l( pub fn mesh_fish_medium_fin_r( fin_r: fish_medium::FinR, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match fin_r { fish_medium::FinR::Default => "npc.marlin.fin_r", @@ -2079,8 +2088,8 @@ impl DragonCenterSpec { &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2093,15 +2102,15 @@ impl DragonCenterSpec { }; let central = graceful_load_segment(&spec.upper.center.0); - generate_mesh(¢ral, Vec3::from(spec.upper.offset)) + generate_mesh(central, Vec3::from(spec.upper.offset)) } pub fn mesh_head_lower( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2114,15 +2123,15 @@ impl DragonCenterSpec { }; let central = graceful_load_segment(&spec.lower.center.0); - generate_mesh(¢ral, Vec3::from(spec.lower.offset)) + generate_mesh(central, Vec3::from(spec.lower.offset)) } pub fn mesh_jaw( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2135,15 +2144,15 @@ impl DragonCenterSpec { }; let central = graceful_load_segment(&spec.jaw.center.0); - generate_mesh(¢ral, Vec3::from(spec.jaw.offset)) + generate_mesh(central, Vec3::from(spec.jaw.offset)) } pub fn mesh_chest_front( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2156,15 +2165,15 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.chest_front.center.0); - generate_mesh(¢er, Vec3::from(spec.chest_front.offset)) + generate_mesh(center, Vec3::from(spec.chest_front.offset)) } pub fn mesh_chest_rear( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2177,15 +2186,15 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.chest_rear.center.0); - generate_mesh(¢er, Vec3::from(spec.chest_rear.offset)) + generate_mesh(center, Vec3::from(spec.chest_rear.offset)) } pub fn mesh_tail_front( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2198,15 +2207,15 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.tail_front.center.0); - generate_mesh(¢er, Vec3::from(spec.tail_front.offset)) + generate_mesh(center, Vec3::from(spec.tail_front.offset)) } pub fn mesh_tail_rear( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2219,7 +2228,7 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.tail_rear.center.0); - generate_mesh(¢er, Vec3::from(spec.tail_rear.offset)) + generate_mesh(center, Vec3::from(spec.tail_rear.offset)) } } impl DragonLateralSpec { @@ -2231,8 +2240,8 @@ impl DragonLateralSpec { &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2245,15 +2254,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_in_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_in_l.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_in_l.offset)) } pub fn mesh_wing_in_r( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2266,15 +2275,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_in_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_in_r.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_in_r.offset)) } pub fn mesh_wing_out_l( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2287,15 +2296,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_out_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_out_l.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_out_l.offset)) } pub fn mesh_wing_out_r( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2308,15 +2317,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_out_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_out_r.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_out_r.offset)) } pub fn mesh_foot_fl( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2329,15 +2338,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_fl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_fl.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_fl.offset)) } pub fn mesh_foot_fr( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2350,15 +2359,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_fr.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_fr.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_fr.offset)) } pub fn mesh_foot_bl( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2371,15 +2380,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_bl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_bl.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_bl.offset)) } pub fn mesh_foot_br( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2392,15 +2401,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_br.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_br.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_br.offset)) } } //// pub fn mesh_bird_small_head( head: bird_small::Head, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match head { bird_small::Head::Default => "npc.crow.head", @@ -2412,8 +2421,8 @@ pub fn mesh_bird_small_head( pub fn mesh_bird_small_torso( torso: bird_small::Torso, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match torso { bird_small::Torso::Default => "npc.crow.torso", @@ -2425,8 +2434,8 @@ pub fn mesh_bird_small_torso( pub fn mesh_bird_small_wing_l( wing_l: bird_small::WingL, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match wing_l { bird_small::WingL::Default => "npc.crow.wing_l", @@ -2438,8 +2447,8 @@ pub fn mesh_bird_small_wing_l( pub fn mesh_bird_small_wing_r( wing_r: bird_small::WingR, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match wing_r { bird_small::WingR::Default => "npc.crow.wing_r", @@ -2451,8 +2460,8 @@ pub fn mesh_bird_small_wing_r( //// pub fn mesh_fish_small_torso( torso: fish_small::Torso, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match torso { fish_small::Torso::Default => "npc.cardinalfish.torso", @@ -2464,8 +2473,8 @@ pub fn mesh_fish_small_torso( pub fn mesh_fish_small_tail( tail: fish_small::Tail, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match tail { fish_small::Tail::Default => "npc.cardinalfish.tail", @@ -2540,8 +2549,8 @@ impl BipedLargeCenterSpec { &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2554,15 +2563,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_jaw( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2575,15 +2584,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.jaw.center.0); - generate_mesh(¢er, Vec3::from(spec.jaw.offset)) + generate_mesh(center, Vec3::from(spec.jaw.offset)) } pub fn mesh_torso_upper( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2596,15 +2605,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.torso_upper.center.0); - generate_mesh(¢er, Vec3::from(spec.torso_upper.offset)) + generate_mesh(center, Vec3::from(spec.torso_upper.offset)) } pub fn mesh_torso_lower( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2617,15 +2626,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.torso_lower.center.0); - generate_mesh(¢er, Vec3::from(spec.torso_lower.offset)) + generate_mesh(center, Vec3::from(spec.torso_lower.offset)) } pub fn mesh_tail( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2638,15 +2647,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.tail.center.0); - generate_mesh(¢er, Vec3::from(spec.tail.offset)) + generate_mesh(center, Vec3::from(spec.tail.offset)) } pub fn mesh_main( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2659,15 +2668,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.main.center.0); - generate_mesh(¢er, Vec3::from(spec.main.offset)) + generate_mesh(center, Vec3::from(spec.main.offset)) } pub fn mesh_second( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2680,7 +2689,7 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.second.center.0); - generate_mesh(¢er, Vec3::from(spec.second.offset)) + generate_mesh(center, Vec3::from(spec.second.offset)) } } impl BipedLargeLateralSpec { @@ -2693,8 +2702,8 @@ impl BipedLargeLateralSpec { &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2707,15 +2716,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_l.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_l.offset)) } pub fn mesh_shoulder_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2728,15 +2737,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_r.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_r.offset)) } pub fn mesh_hand_l( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2749,15 +2758,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_l.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_l.offset)) } pub fn mesh_hand_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2770,15 +2779,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_r.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_r.offset)) } pub fn mesh_leg_l( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2791,15 +2800,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_l.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_l.offset)) } pub fn mesh_leg_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2812,15 +2821,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_r.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_r.offset)) } pub fn mesh_foot_l( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2833,15 +2842,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_l.offset)) } pub fn mesh_foot_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2854,7 +2863,7 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_r.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_r.offset)) } } //// @@ -2917,8 +2926,8 @@ impl GolemCenterSpec { &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2931,15 +2940,15 @@ impl GolemCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_torso_upper( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2952,7 +2961,7 @@ impl GolemCenterSpec { }; let center = graceful_load_segment(&spec.torso_upper.center.0); - generate_mesh(¢er, Vec3::from(spec.torso_upper.offset)) + generate_mesh(center, Vec3::from(spec.torso_upper.offset)) } } impl GolemLateralSpec { @@ -2964,8 +2973,8 @@ impl GolemLateralSpec { &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2978,15 +2987,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_l.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_l.offset)) } pub fn mesh_shoulder_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2999,15 +3008,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_r.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_r.offset)) } pub fn mesh_hand_l( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3020,15 +3029,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_l.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_l.offset)) } pub fn mesh_hand_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3041,15 +3050,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_r.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_r.offset)) } pub fn mesh_leg_l( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3062,15 +3071,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_l.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_l.offset)) } pub fn mesh_leg_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3083,15 +3092,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_r.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_r.offset)) } pub fn mesh_foot_l( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3104,15 +3113,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_l.offset)) } pub fn mesh_foot_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3125,7 +3134,7 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_r.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_r.offset)) } } @@ -3190,8 +3199,8 @@ impl QuadrupedLowCentralSpec { &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3204,15 +3213,15 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.upper.central.0); - generate_mesh(¢ral, Vec3::from(spec.upper.offset)) + generate_mesh(central, Vec3::from(spec.upper.offset)) } pub fn mesh_head_lower( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3225,15 +3234,15 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.lower.central.0); - generate_mesh(¢ral, Vec3::from(spec.lower.offset)) + generate_mesh(central, Vec3::from(spec.lower.offset)) } pub fn mesh_jaw( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3246,15 +3255,15 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.jaw.central.0); - generate_mesh(¢ral, Vec3::from(spec.jaw.offset)) + generate_mesh(central, Vec3::from(spec.jaw.offset)) } pub fn mesh_chest( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3267,15 +3276,15 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.chest.central.0); - generate_mesh(¢ral, Vec3::from(spec.chest.offset)) + generate_mesh(central, Vec3::from(spec.chest.offset)) } pub fn mesh_tail_rear( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3288,15 +3297,15 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.tail_rear.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail_rear.offset)) + generate_mesh(central, Vec3::from(spec.tail_rear.offset)) } pub fn mesh_tail_front( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3309,7 +3318,7 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.tail_front.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail_front.offset)) + generate_mesh(central, Vec3::from(spec.tail_front.offset)) } } @@ -3323,8 +3332,8 @@ impl QuadrupedLowLateralSpec { &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3337,15 +3346,15 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.front_left.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.front_left.offset)) + generate_mesh(lateral, Vec3::from(spec.front_left.offset)) } pub fn mesh_foot_fr( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3358,15 +3367,15 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.front_right.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.front_right.offset)) + generate_mesh(lateral, Vec3::from(spec.front_right.offset)) } pub fn mesh_foot_bl( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3379,15 +3388,15 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.back_left.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.back_left.offset)) + generate_mesh(lateral, Vec3::from(spec.back_left.offset)) } pub fn mesh_foot_br( &self, species: QLSpecies, body_type: QLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -3400,16 +3409,16 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.back_right.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.back_right.offset)) + generate_mesh(lateral, Vec3::from(spec.back_right.offset)) } } /// pub fn mesh_object( - obj: object::Body, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + obj: &object::Body, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { use object::Body; let (name, offset) = match obj { diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 8197620eb1..f45f660c61 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -6,10 +6,14 @@ pub use load::load_mesh; // TODO: Don't make this public. use crate::{ ecs::comp::Interpolated, - render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow}, + mesh::greedy::GreedyMesh, + render::{ + ColLightFmt, Consts, FigureBoneData, FigureLocals, FigureModel, GlobalModel, Mesh, + RenderError, Renderer, ShadowPipeline, TerrainPipeline, Texture, + }, scene::{ - camera::{Camera, CameraMode}, - SceneData, + camera::{Camera, CameraMode, Dependents}, + math, LodData, SceneData, }, }; use anim::{ @@ -30,29 +34,52 @@ use common::{ terrain::TerrainChunk, vol::RectRasterableVol, }; +use core::{ + borrow::Borrow, + convert::TryFrom, + hash::Hash, + ops::{Deref, DerefMut, Range}, +}; +use guillotiere::AtlasAllocator; use hashbrown::HashMap; -use specs::{Entity as EcsEntity, Join, WorldExt}; -use tracing::trace; +use specs::{Entity as EcsEntity, Join, LazyUpdate, WorldExt}; use treeculler::{BVol, BoundingSphere}; -use vek::*; const DAMAGE_FADE_COEFFICIENT: f64 = 5.0; const MOVING_THRESHOLD: f32 = 0.7; const MOVING_THRESHOLD_SQR: f32 = MOVING_THRESHOLD * MOVING_THRESHOLD; -pub struct FigureMgr { - model_cache: FigureModelCache, - critter_model_cache: FigureModelCache, - quadruped_small_model_cache: FigureModelCache, - quadruped_medium_model_cache: FigureModelCache, - quadruped_low_model_cache: FigureModelCache, - bird_medium_model_cache: FigureModelCache, - bird_small_model_cache: FigureModelCache, - dragon_model_cache: FigureModelCache, - fish_medium_model_cache: FigureModelCache, - fish_small_model_cache: FigureModelCache, - biped_large_model_cache: FigureModelCache, - golem_model_cache: FigureModelCache, +/// camera data, figure LOD render distance. +pub type CameraData<'a> = (&'a Camera, f32); + +/// Enough data to render a figure model. +pub type FigureModelRef<'a> = ( + &'a Consts, + &'a Consts, + &'a FigureModel, + &'a Texture, +); + +/// An entry holding enough information to draw or destroy a figure in a +/// particular cache. +pub struct FigureModelEntry { + /// The estimated bounds of this figure, in voxels. This may not be very + /// useful yet. + _bounds: math::Aabb, + /// Hypothetical texture atlas allocation data for the current figure. + /// Will be useful if we decide to use a packed texture atlas for figures + /// like we do for terrain. + allocation: guillotiere::Allocation, + /// Texture used to store color/light information for this figure entry. + /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different + * LOD levels. */ + col_lights: Texture, + /// Models stored in this figure entry; there may be several for one figure, + /// because of LOD models. + pub models: [FigureModel; N], +} + +struct FigureMgrStates { character_states: HashMap>, quadruped_small_states: HashMap>, quadruped_medium_states: HashMap>, @@ -68,22 +95,9 @@ pub struct FigureMgr { object_states: HashMap>, } -impl FigureMgr { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { +impl FigureMgrStates { + pub fn default() -> Self { Self { - model_cache: FigureModelCache::new(), - critter_model_cache: FigureModelCache::new(), - quadruped_small_model_cache: FigureModelCache::new(), - quadruped_medium_model_cache: FigureModelCache::new(), - quadruped_low_model_cache: FigureModelCache::new(), - bird_medium_model_cache: FigureModelCache::new(), - bird_small_model_cache: FigureModelCache::new(), - dragon_model_cache: FigureModelCache::new(), - fish_medium_model_cache: FigureModelCache::new(), - fish_small_model_cache: FigureModelCache::new(), - biped_large_model_cache: FigureModelCache::new(), - golem_model_cache: FigureModelCache::new(), character_states: HashMap::new(), quadruped_small_states: HashMap::new(), quadruped_medium_states: HashMap::new(), @@ -100,22 +114,251 @@ impl FigureMgr { } } - pub fn clean(&mut self, tick: u64) { - self.model_cache.clean(tick); - self.critter_model_cache.clean(tick); - self.quadruped_small_model_cache.clean(tick); - self.quadruped_medium_model_cache.clean(tick); - self.quadruped_low_model_cache.clean(tick); - self.bird_medium_model_cache.clean(tick); - self.bird_small_model_cache.clean(tick); - self.dragon_model_cache.clean(tick); - self.fish_medium_model_cache.clean(tick); - self.fish_small_model_cache.clean(tick); - self.biped_large_model_cache.clean(tick); - self.golem_model_cache.clean(tick); + fn get_mut<'a, Q: ?Sized>( + &'a mut self, + body: &Body, + entity: &Q, + ) -> Option<&'a mut FigureStateMeta> + where + EcsEntity: Borrow, + Q: Hash + Eq, + { + match body { + Body::Humanoid(_) => self + .character_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::QuadrupedSmall(_) => self + .quadruped_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::QuadrupedMedium(_) => self + .quadruped_medium_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::QuadrupedLow(_) => self + .quadruped_low_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::BirdMedium(_) => self + .bird_medium_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::FishMedium(_) => self + .fish_medium_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::Critter(_) => self + .critter_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::Dragon(_) => self.dragon_states.get_mut(&entity).map(DerefMut::deref_mut), + Body::BirdSmall(_) => self + .bird_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::FishSmall(_) => self + .fish_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::BipedLarge(_) => self + .biped_large_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::Golem(_) => self.golem_states.get_mut(&entity).map(DerefMut::deref_mut), + Body::Object(_) => self.object_states.get_mut(&entity).map(DerefMut::deref_mut), + } } - #[allow(clippy::redundant_pattern_matching)] // TODO: Pending review in #587 + fn remove<'a, Q: ?Sized>(&'a mut self, body: &Body, entity: &Q) -> Option + where + EcsEntity: Borrow, + Q: Hash + Eq, + { + match body { + Body::Humanoid(_) => self.character_states.remove(&entity).map(|e| e.meta), + Body::QuadrupedSmall(_) => self.quadruped_small_states.remove(&entity).map(|e| e.meta), + Body::QuadrupedMedium(_) => { + self.quadruped_medium_states.remove(&entity).map(|e| e.meta) + }, + Body::QuadrupedLow(_) => self.quadruped_low_states.remove(&entity).map(|e| e.meta), + Body::BirdMedium(_) => self.bird_medium_states.remove(&entity).map(|e| e.meta), + Body::FishMedium(_) => self.fish_medium_states.remove(&entity).map(|e| e.meta), + Body::Critter(_) => self.critter_states.remove(&entity).map(|e| e.meta), + Body::Dragon(_) => self.dragon_states.remove(&entity).map(|e| e.meta), + Body::BirdSmall(_) => self.bird_small_states.remove(&entity).map(|e| e.meta), + Body::FishSmall(_) => self.fish_small_states.remove(&entity).map(|e| e.meta), + Body::BipedLarge(_) => self.biped_large_states.remove(&entity).map(|e| e.meta), + Body::Golem(_) => self.golem_states.remove(&entity).map(|e| e.meta), + Body::Object(_) => self.object_states.remove(&entity).map(|e| e.meta), + } + } + + fn retain(&mut self, mut f: impl FnMut(&EcsEntity, &mut FigureStateMeta) -> bool) { + self.character_states.retain(|k, v| f(k, &mut *v)); + self.quadruped_small_states.retain(|k, v| f(k, &mut *v)); + self.quadruped_medium_states.retain(|k, v| f(k, &mut *v)); + self.quadruped_low_states.retain(|k, v| f(k, &mut *v)); + self.bird_medium_states.retain(|k, v| f(k, &mut *v)); + self.fish_medium_states.retain(|k, v| f(k, &mut *v)); + self.critter_states.retain(|k, v| f(k, &mut *v)); + self.dragon_states.retain(|k, v| f(k, &mut *v)); + self.bird_small_states.retain(|k, v| f(k, &mut *v)); + self.fish_small_states.retain(|k, v| f(k, &mut *v)); + self.biped_large_states.retain(|k, v| f(k, &mut *v)); + self.golem_states.retain(|k, v| f(k, &mut *v)); + self.object_states.retain(|k, v| f(k, &mut *v)); + } + + fn count(&self) -> usize { + self.character_states.len() + + self.quadruped_small_states.len() + + self.character_states.len() + + self.quadruped_medium_states.len() + + self.quadruped_low_states.len() + + self.bird_medium_states.len() + + self.fish_medium_states.len() + + self.critter_states.len() + + self.dragon_states.len() + + self.bird_small_states.len() + + self.fish_small_states.len() + + self.biped_large_states.len() + + self.golem_states.len() + + self.object_states.len() + } + + fn count_visible(&self) -> usize { + self.character_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .quadruped_small_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .quadruped_medium_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .quadruped_low_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .bird_medium_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .critter_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .dragon_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .fish_medium_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .bird_small_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .fish_small_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .biped_large_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .golem_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + + self + .object_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + } +} + +pub struct FigureMgr { + col_lights: FigureColLights, + model_cache: FigureModelCache, + critter_model_cache: FigureModelCache, + quadruped_small_model_cache: FigureModelCache, + quadruped_medium_model_cache: FigureModelCache, + quadruped_low_model_cache: FigureModelCache, + bird_medium_model_cache: FigureModelCache, + bird_small_model_cache: FigureModelCache, + dragon_model_cache: FigureModelCache, + fish_medium_model_cache: FigureModelCache, + fish_small_model_cache: FigureModelCache, + biped_large_model_cache: FigureModelCache, + golem_model_cache: FigureModelCache, + states: FigureMgrStates, +} + +impl FigureMgr { + pub fn new(renderer: &mut Renderer) -> Self { + Self { + col_lights: FigureColLights::new(renderer), + model_cache: FigureModelCache::new(), + critter_model_cache: FigureModelCache::new(), + quadruped_small_model_cache: FigureModelCache::new(), + quadruped_medium_model_cache: FigureModelCache::new(), + quadruped_low_model_cache: FigureModelCache::new(), + bird_medium_model_cache: FigureModelCache::new(), + bird_small_model_cache: FigureModelCache::new(), + dragon_model_cache: FigureModelCache::new(), + fish_medium_model_cache: FigureModelCache::new(), + fish_small_model_cache: FigureModelCache::new(), + biped_large_model_cache: FigureModelCache::new(), + golem_model_cache: FigureModelCache::new(), + states: FigureMgrStates::default(), + } + } + + pub fn col_lights(&self) -> &FigureColLights { &self.col_lights } + + pub fn clean(&mut self, tick: u64) { + self.model_cache.clean(&mut self.col_lights, tick); + self.critter_model_cache.clean(&mut self.col_lights, tick); + self.quadruped_small_model_cache + .clean(&mut self.col_lights, tick); + self.quadruped_medium_model_cache + .clean(&mut self.col_lights, tick); + self.quadruped_low_model_cache + .clean(&mut self.col_lights, tick); + self.bird_medium_model_cache + .clean(&mut self.col_lights, tick); + self.bird_small_model_cache + .clean(&mut self.col_lights, tick); + self.dragon_model_cache.clean(&mut self.col_lights, tick); + self.fish_medium_model_cache + .clean(&mut self.col_lights, tick); + self.fish_small_model_cache + .clean(&mut self.col_lights, tick); + self.biped_large_model_cache + .clean(&mut self.col_lights, tick); + self.golem_model_cache.clean(&mut self.col_lights, tick); + } + + #[allow(clippy::redundant_pattern_matching)] + // TODO: Pending review in #587 pub fn update_lighting(&mut self, scene_data: &SceneData) { let ecs = scene_data.state.ecs(); for (entity, light_emitter) in (&ecs.entities(), &ecs.read_storage::()).join() @@ -124,7 +367,7 @@ impl FigureMgr { let mut anim_storage = ecs.write_storage::(); if let None = anim_storage.get_mut(entity) { let anim = LightAnimation { - offset: Vec3::zero(), + offset: vek::Vec3::zero(), col: light_emitter.col, strength: 0.0, }; @@ -132,9 +375,10 @@ impl FigureMgr { } } let dt = ecs.fetch::().0; + let updater = ecs.read_resource::(); for (entity, waypoint, light_emitter_opt, light_anim) in ( &ecs.entities(), - ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), &mut ecs.write_storage::(), ) @@ -144,7 +388,7 @@ impl FigureMgr { if let Some(emitter) = light_emitter_opt { ( emitter.col, - if emitter.strength.is_finite() { + if emitter.strength.is_normal() { emitter.strength } else { 0.0 @@ -153,15 +397,15 @@ impl FigureMgr { emitter.animated, ) } else { - (Rgb::zero(), 0.0, 0.0, true) + (vek::Rgb::zero(), 0.0, 0.0, true) }; if let Some(_) = waypoint { - light_anim.offset = Vec3::unit_z() * 0.5; + light_anim.offset = vek::Vec3::unit_z() * 5.0; } - if let Some(state) = self.character_states.get(&entity) { - light_anim.offset = state.lantern_offset; + if let Some(state) = self.states.character_states.get(&entity) { + light_anim.offset = vek::Vec3::from(state.lantern_offset); } - if !light_anim.strength.is_finite() { + if !light_anim.strength.is_normal() { light_anim.strength = 0.0; } if animated { @@ -175,23 +419,104 @@ impl FigureMgr { light_anim.strength = target_strength; light_anim.col = target_col; } + // NOTE: We add `LIGHT_EPSILON` because if we wait for numbers to become + // equal to target (or even within a subnormal), it will take a minimum + // of 30 seconds for a light to fully turn off (for initial + // strength ≥ 1), which prevents optimizations (particularly those that + // can kick in with zero lights). + const LIGHT_EPSILON: f32 = 0.0001; + if (light_anim.strength - target_strength).abs() < LIGHT_EPSILON { + light_anim.strength = target_strength; + if light_anim.strength == 0.0 { + updater.remove::(entity); + } + } } } - #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 - pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData, camera: &Camera) { + #[allow(clippy::or_fun_call)] + // TODO: Pending review in #587 + pub fn maintain( + &mut self, + renderer: &mut Renderer, + scene_data: &SceneData, + // Visible chunk data. + visible_psr_bounds: math::Aabr, + camera: &Camera, + ) -> anim::vek::Aabb { let state = scene_data.state; let time = state.get_time(); let tick = scene_data.tick; let ecs = state.ecs(); let view_distance = scene_data.view_distance; let dt = state.get_delta_time(); + let dt_lerp = (15.0 * dt).min(1.0); let frustum = camera.frustum(); + + // Sun shadows--find the bounding box of the shadow map plane (i.e. the bounds + // of the image rendered from the light). If the position projected + // with the ray_mat matrix is valid, and shadows are otherwise enabled, + // we mark can_shadow. + let can_shadow_sun = { + let ray_direction = scene_data.get_sun_dir(); + let is_daylight = ray_direction.z < 0.0/*0.6*/; + // Are shadows enabled at all? + let can_shadow_sun = renderer.render_mode().shadow.is_map() && is_daylight; + let Dependents { + proj_mat: _, + view_mat: _, + cam_pos, + .. + } = camera.dependents(); + let cam_pos = math::Vec3::from(cam_pos); + let ray_direction = math::Vec3::from(ray_direction); + + // Transform (semi) world space to light space. + let ray_mat: math::Mat4 = + math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, math::Vec3::up()); + let focus_off = math::Vec3::from(camera.get_focus_pos().map(f32::trunc)); + let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off); + + let collides_with_aabr = |a: math::Aabr, b: math::Aabr| { + let min = math::Vec4::new(a.min.x, a.min.y, b.min.x, b.min.y); + let max = math::Vec4::new(b.max.x, b.max.y, a.max.x, a.max.y); + min.partial_cmple_simd(max).reduce_and() + }; + move |pos: (anim::vek::Vec3,), radius: f32| { + // Short circuit when there are no shadows to cast. + if !can_shadow_sun { + return false; + } + // First project center onto shadow map. + let center = (ray_mat * math::Vec4::new(pos.0.x, pos.0.y, pos.0.z, 1.0)).xy(); + // Then, create an approximate bounding box (± radius). + let figure_box = math::Aabr { + min: center - radius, + max: center + radius, + }; + // Quick intersection test for membership in the PSC (potential shader caster) + // list. + collides_with_aabr(figure_box, visible_psr_bounds) + } + }; + // Get player position. let player_pos = ecs .read_storage::() .get(scene_data.player_entity) - .map_or(Vec3::zero(), |pos| pos.0); + .map_or(anim::vek::Vec3::zero(), |pos| anim::vek::Vec3::from(pos.0)); + let visible_aabb = anim::vek::Aabb { + min: player_pos - 2.0, + max: player_pos + 2.0, + }; + let camera_mode = camera.get_mode(); + let character_state_storage = state.read_storage::(); + let character_state = character_state_storage.get(scene_data.player_entity); + + let focus_pos = anim::vek::Vec3::::from(camera.get_focus_pos()); + + let mut update_buf = [Default::default(); anim::MAX_BONE_COUNT]; + for ( i, ( @@ -225,6 +550,27 @@ impl FigureMgr { .join() .enumerate() { + let vel = (anim::vek::Vec3::::from(vel.0),); + let is_player = scene_data.player_entity == entity; + let player_camera_mode = if is_player { + camera_mode + } else { + CameraMode::default() + }; + let player_character_state = if is_player { character_state } else { None }; + + let (pos, ori) = interpolated + .map(|i| { + ( + (anim::vek::Vec3::from(i.pos),), + anim::vek::Vec3::from(*i.ori), + ) + }) + .unwrap_or(( + (anim::vek::Vec3::::from(pos.0),), + anim::vek::Vec3::::unit_y(), + )); + // Maintaining figure data and sending new figure data to the GPU turns out to // be a very expensive operation. We want to avoid doing it as much // as possible, so we make the assumption that players don't care so @@ -233,8 +579,9 @@ impl FigureMgr { // TODO: Investigate passing the velocity into the shader so we can at least // interpolate motion const MIN_PERFECT_RATE_DIST: f32 = 50.0; + if (i as u64 + tick) - % (1 + ((pos.0.distance_squared(camera.get_focus_pos()).powf(0.25) + % (1 + ((pos.0.distance_squared(focus_pos).powf(0.25) - MIN_PERFECT_RATE_DIST.powf(0.5)) .max(0.0) / 3.0) as u64) @@ -243,289 +590,75 @@ impl FigureMgr { continue; } - let is_player = scene_data.player_entity == entity; - - let (pos, ori) = interpolated - .map(|i| (Pos(i.pos), *i.ori)) - .unwrap_or((*pos, Vec3::unit_y())); + // Check whether we could have been shadowing last frame. + let mut state = self.states.get_mut(body, &entity); + let can_shadow_prev = state + .as_mut() + .map(|state| state.can_shadow_sun()) + .unwrap_or(false); // Don't process figures outside the vd - let vd_frac = Vec2::from(pos.0 - player_pos) - .map2(TerrainChunk::RECT_SIZE, |d: f32, sz| { - d.abs() as f32 / sz as f32 - }) + let vd_frac = anim::vek::Vec2::from(pos.0 - player_pos) + .map2( + anim::vek::Vec2::::from(TerrainChunk::RECT_SIZE), + |d: f32, sz| d.abs() as f32 / sz as f32, + ) .magnitude() / view_distance as f32; + // Keep from re-adding/removing entities on the border of the vd if vd_frac > 1.2 { - match body { - Body::Humanoid(_) => { - self.character_states.remove(&entity); - }, - Body::QuadrupedSmall(_) => { - self.quadruped_small_states.remove(&entity); - }, - Body::QuadrupedMedium(_) => { - self.quadruped_medium_states.remove(&entity); - }, - Body::QuadrupedLow(_) => { - self.quadruped_low_states.remove(&entity); - }, - Body::BirdMedium(_) => { - self.bird_medium_states.remove(&entity); - }, - Body::FishMedium(_) => { - self.fish_medium_states.remove(&entity); - }, - Body::Critter(_) => { - self.critter_states.remove(&entity); - }, - Body::Dragon(_) => { - self.dragon_states.remove(&entity); - }, - Body::BirdSmall(_) => { - self.bird_small_states.remove(&entity); - }, - Body::FishSmall(_) => { - self.fish_small_states.remove(&entity); - }, - Body::BipedLarge(_) => { - self.biped_large_states.remove(&entity); - }, - Body::Golem(_) => { - self.biped_large_states.remove(&entity); - }, - Body::Object(_) => { - self.object_states.remove(&entity); - }, - } + self.states.remove(body, &entity); continue; } else if vd_frac > 1.0 { - match body { - Body::Humanoid(_) => { - self.character_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::QuadrupedSmall(_) => { - self.quadruped_small_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::QuadrupedMedium(_) => { - self.quadruped_medium_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::QuadrupedLow(_) => { - self.quadruped_low_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::BirdMedium(_) => { - self.bird_medium_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::FishMedium(_) => { - self.fish_medium_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Critter(_) => { - self.critter_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Dragon(_) => { - self.dragon_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::BirdSmall(_) => { - self.bird_small_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::FishSmall(_) => { - self.fish_small_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::BipedLarge(_) => { - self.biped_large_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Golem(_) => { - self.golem_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Object(_) => { - self.object_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - } - continue; - } - - // Don't process figures outside the frustum spectrum - let (in_frustum, lpindex) = - BoundingSphere::new(pos.0.into_array(), scale.unwrap_or(&Scale(1.0)).0 * 2.0) - .coherent_test_against_frustum( - &frustum, - match body { - Body::Humanoid(_) => self - .character_states - .get(&entity) - .map(|state| state.lpindex), - Body::QuadrupedSmall(_) => self - .quadruped_small_states - .get(&entity) - .map(|state| state.lpindex), - Body::QuadrupedMedium(_) => self - .quadruped_medium_states - .get(&entity) - .map(|state| state.lpindex), - Body::QuadrupedLow(_) => self - .quadruped_low_states - .get(&entity) - .map(|state| state.lpindex), - Body::BirdMedium(_) => self - .bird_medium_states - .get(&entity) - .map(|state| state.lpindex), - Body::FishMedium(_) => self - .fish_medium_states - .get(&entity) - .map(|state| state.lpindex), - Body::Critter(_) => { - self.critter_states.get(&entity).map(|state| state.lpindex) - }, - Body::Dragon(_) => { - self.dragon_states.get(&entity).map(|state| state.lpindex) - }, - Body::BirdSmall(_) => self - .bird_small_states - .get(&entity) - .map(|state| state.lpindex), - Body::FishSmall(_) => self - .fish_small_states - .get(&entity) - .map(|state| state.lpindex), - Body::BipedLarge(_) => self - .biped_large_states - .get(&entity) - .map(|state| state.lpindex), - Body::Golem(_) => { - self.golem_states.get(&entity).map(|state| state.lpindex) - }, - Body::Object(_) => { - self.object_states.get(&entity).map(|state| state.lpindex) - }, - } - .unwrap_or(0), - ); - - if !in_frustum { - match body { - Body::Humanoid(_) => { - self.character_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::QuadrupedSmall(_) => { - self.quadruped_small_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::QuadrupedMedium(_) => { - self.quadruped_medium_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::QuadrupedLow(_) => { - self.quadruped_low_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::BirdMedium(_) => { - self.bird_medium_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::FishMedium(_) => { - self.fish_medium_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Critter(_) => { - self.critter_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Dragon(_) => { - self.dragon_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::BirdSmall(_) => { - self.bird_small_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::FishSmall(_) => { - self.fish_small_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::BipedLarge(_) => { - self.biped_large_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Golem(_) => { - self.golem_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Object(_) => { - self.object_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, + state.as_mut().map(|state| state.visible = false); + // Keep processing if this might be a shadow caster. + if !can_shadow_prev { + continue; } } + // Don't display figures outside the frustum spectrum (this is important to do + // for any figure that potentially casts a shadow, since we use this + // to estimate bounds for shadow maps). Currently, we don't do this before the + // update cull, so it's possible that faraway figures will not + // shadow correctly until their next update. For now, we treat this + // as an acceptable tradeoff. + let radius = scale.unwrap_or(&Scale(1.0)).0 * 2.0; + let (in_frustum, lpindex) = if let Some(mut meta) = state { + let (in_frustum, lpindex) = BoundingSphere::new(pos.0.into_array(), radius) + .coherent_test_against_frustum(frustum, meta.lpindex); + meta.visible = in_frustum; + meta.lpindex = lpindex; + if in_frustum { + /* // Update visible bounds. + visible_aabb.expand_to_contain(Aabb { + min: pos.0 - radius, + max: pos.0 + radius, + }); */ + } else { + // Check whether we can shadow. + meta.can_shadow_sun = can_shadow_sun(pos, radius); + } + (in_frustum, lpindex) + } else { + (true, 0) + }; + // Change in health as color! let col = stats .map(|s| { - Rgba::broadcast(1.0) - + Rgba::new(2.0, 2.0, 2., 0.00).map(|c| { + vek::Rgba::broadcast(1.0) + + vek::Rgba::new(2.0, 2.0, 2., 0.00).map(|c| { (c / (1.0 + DAMAGE_FADE_COEFFICIENT * s.health.last_change.0)) as f32 }) }) - .unwrap_or(Rgba::broadcast(1.0)) + .unwrap_or(vek::Rgba::broadcast(1.0)) // Highlight targeted collectible entities * if item.is_some() && scene_data.target_entity.map_or(false, |e| e == entity) { - Rgba::new(2.0, 2.0, 2.0, 1.0) + vek::Rgba::new(2.0, 2.0, 2.0, 1.0) } else { - Rgba::one() + vek::Rgba::one() }; let scale = scale.map(|s| s.0).unwrap_or(1.0); @@ -553,22 +686,23 @@ impl FigureMgr { match body { Body::Humanoid(_) => { - let skeleton_attr = &self - .model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); let state = self + .states .character_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, CharacterSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), _ => continue, @@ -585,7 +719,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -598,7 +732,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::character::RunAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -614,7 +748,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::character::JumpAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -628,7 +762,7 @@ impl FigureMgr { ), // Swim (_, _, true) => anim::character::SwimAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -722,7 +856,7 @@ impl FigureMgr { }, CharacterState::Sneak { .. } => { anim::character::SneakAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, vel.0, ori, state.last_ori, time), state.state_time, &mut state_animation_rate, @@ -796,7 +930,7 @@ impl FigureMgr { }, CharacterState::BasicBlock { .. } => { anim::character::BlockIdleAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, time), state.state_time, &mut state_animation_rate, @@ -859,7 +993,7 @@ impl FigureMgr { }, CharacterState::Climb { .. } => { anim::character::ClimbAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, vel.0, ori, time), state.state_time, &mut state_animation_rate, @@ -868,7 +1002,7 @@ impl FigureMgr { }, CharacterState::Sit { .. } => { anim::character::SitAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, time), state.state_time, &mut state_animation_rate, @@ -877,7 +1011,7 @@ impl FigureMgr { }, CharacterState::GlideWield { .. } => { anim::character::GlideWieldAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind, second_tool_kind, @@ -893,7 +1027,7 @@ impl FigureMgr { }, CharacterState::Dance { .. } => { anim::character::DanceAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, time), state.state_time, &mut state_animation_rate, @@ -903,7 +1037,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -912,29 +1046,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::QuadrupedSmall(_) => { - let skeleton_attr = &self - .quadruped_small_model_cache - .get_or_create_model( + let (model, skeleton_attr) = + self.quadruped_small_model_cache.get_or_create_model( renderer, + &mut self.col_lights, *body, loadout, tick, - CameraMode::default(), - None, - ) - .1; + player_camera_mode, + player_character_state, + ); let state = self + .states .quadruped_small_states .entry(entity) .or_insert_with(|| { - FigureState::new(renderer, QuadrupedSmallSkeleton::new()) + FigureState::new(renderer, QuadrupedSmallSkeleton::default()) }); let (character, last_character) = match (character, last_character) { @@ -954,7 +1091,7 @@ impl FigureMgr { // Standing (true, false, false) => { anim::quadruped_small::IdleAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -964,7 +1101,7 @@ impl FigureMgr { // Running (true, true, false) => { anim::quadruped_small::RunAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -973,14 +1110,14 @@ impl FigureMgr { }, // In air (false, _, false) => anim::quadruped_small::JumpAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::quadruped_small::IdleAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1001,7 +1138,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1010,29 +1147,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::QuadrupedMedium(_) => { - let skeleton_attr = &self - .quadruped_medium_model_cache - .get_or_create_model( + let (model, skeleton_attr) = + self.quadruped_medium_model_cache.get_or_create_model( renderer, + &mut self.col_lights, *body, loadout, tick, - CameraMode::default(), - None, - ) - .1; + player_camera_mode, + player_character_state, + ); let state = self + .states .quadruped_medium_states .entry(entity) .or_insert_with(|| { - FigureState::new(renderer, QuadrupedMediumSkeleton::new()) + FigureState::new(renderer, QuadrupedMediumSkeleton::default()) }); let (character, last_character) = match (character, last_character) { @@ -1052,7 +1192,7 @@ impl FigureMgr { // Standing (true, false, false) => { anim::quadruped_medium::IdleAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1061,7 +1201,7 @@ impl FigureMgr { }, // Running (true, true, _) => anim::quadruped_medium::RunAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -1070,7 +1210,7 @@ impl FigureMgr { // In air (false, _, false) => { anim::quadruped_medium::JumpAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1078,7 +1218,7 @@ impl FigureMgr { ) }, _ => anim::quadruped_medium::IdleAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1099,7 +1239,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1108,28 +1248,33 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::QuadrupedLow(_) => { - let skeleton_attr = &self - .quadruped_low_model_cache - .get_or_create_model( + let (model, skeleton_attr) = + self.quadruped_low_model_cache.get_or_create_model( renderer, + &mut self.col_lights, *body, loadout, tick, - CameraMode::default(), - None, - ) - .1; + player_camera_mode, + player_character_state, + ); let state = self + .states .quadruped_low_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, QuadrupedLowSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, QuadrupedLowSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1148,7 +1293,7 @@ impl FigureMgr { // Standing (true, false, false) => { anim::quadruped_low::IdleAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1157,7 +1302,7 @@ impl FigureMgr { }, // Running (true, true, _) => anim::quadruped_low::RunAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -1165,14 +1310,14 @@ impl FigureMgr { ), // In air (false, _, false) => anim::quadruped_low::JumpAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::quadruped_low::IdleAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1193,7 +1338,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1202,28 +1347,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::BirdMedium(_) => { - let skeleton_attr = &self - .bird_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.bird_medium_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); let state = self + .states .bird_medium_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, BirdMediumSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, BirdMediumSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1241,7 +1390,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1249,7 +1398,7 @@ impl FigureMgr { ), // Running (true, true, _) => anim::bird_medium::RunAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1257,14 +1406,14 @@ impl FigureMgr { ), // In air (false, _, false) => anim::bird_medium::FlyAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::bird_medium::IdleAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1285,7 +1434,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1294,28 +1443,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::FishMedium(_) => { - let skeleton_attr = &self - .fish_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.fish_medium_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); let state = self + .states .fish_medium_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, FishMediumSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, FishMediumSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1333,7 +1486,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton( - &FishMediumSkeleton::new(), + &FishMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1341,7 +1494,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::fish_medium::RunAnimation::update_skeleton( - &FishMediumSkeleton::new(), + &FishMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1349,7 +1502,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::fish_medium::JumpAnimation::update_skeleton( - &FishMediumSkeleton::new(), + &FishMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1357,10 +1510,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::fish_medium::IdleAnimation::update_skeleton( + &FishMediumSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1369,28 +1528,29 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::Dragon(_) => { - let skeleton_attr = &self - .dragon_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.dragon_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); - let state = self - .dragon_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, DragonSkeleton::new())); + let state = + self.states.dragon_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, DragonSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1408,7 +1568,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::dragon::IdleAnimation::update_skeleton( - &DragonSkeleton::new(), + &DragonSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1416,7 +1576,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::dragon::RunAnimation::update_skeleton( - &DragonSkeleton::new(), + &DragonSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -1424,17 +1584,23 @@ impl FigureMgr { ), // In air (false, _, false) => anim::dragon::FlyAnimation::update_skeleton( - &DragonSkeleton::new(), + &DragonSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::dragon::IdleAnimation::update_skeleton( + &DragonSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1443,28 +1609,29 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::Critter(_) => { - let skeleton_attr = &self - .critter_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.critter_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); - let state = self - .critter_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, CritterSkeleton::new())); + let state = + self.states.critter_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, CritterSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1482,7 +1649,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::critter::IdleAnimation::update_skeleton( - &CritterSkeleton::new(), + &CritterSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1490,7 +1657,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::critter::RunAnimation::update_skeleton( - &CritterSkeleton::new(), + &CritterSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1498,7 +1665,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::critter::JumpAnimation::update_skeleton( - &CritterSkeleton::new(), + &CritterSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1506,10 +1673,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::critter::IdleAnimation::update_skeleton( + &CritterSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1518,28 +1691,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::BirdSmall(_) => { - let skeleton_attr = &self - .bird_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.bird_small_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); let state = self + .states .bird_small_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, BirdSmallSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, BirdSmallSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1557,7 +1734,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::bird_small::IdleAnimation::update_skeleton( - &BirdSmallSkeleton::new(), + &BirdSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1565,7 +1742,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::bird_small::RunAnimation::update_skeleton( - &BirdSmallSkeleton::new(), + &BirdSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1573,7 +1750,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::bird_small::JumpAnimation::update_skeleton( - &BirdSmallSkeleton::new(), + &BirdSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1581,10 +1758,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::bird_small::IdleAnimation::update_skeleton( + &BirdSmallSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1593,28 +1776,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::FishSmall(_) => { - let skeleton_attr = &self - .fish_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.fish_small_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); let state = self + .states .fish_small_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, FishSmallSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, FishSmallSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1632,7 +1819,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::fish_small::IdleAnimation::update_skeleton( - &FishSmallSkeleton::new(), + &FishSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1640,7 +1827,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::fish_small::RunAnimation::update_skeleton( - &FishSmallSkeleton::new(), + &FishSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1648,7 +1835,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::fish_small::JumpAnimation::update_skeleton( - &FishSmallSkeleton::new(), + &FishSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1656,10 +1843,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::fish_small::IdleAnimation::update_skeleton( + &FishSmallSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1668,28 +1861,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::BipedLarge(_) => { - let skeleton_attr = &self - .biped_large_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.biped_large_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); let state = self + .states .biped_large_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, BipedLargeSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, BipedLargeSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1707,7 +1904,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::biped_large::IdleAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1715,7 +1912,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::biped_large::RunAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1723,14 +1920,14 @@ impl FigureMgr { ), // In air (false, _, false) => anim::biped_large::JumpAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), time, state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::biped_large::IdleAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1760,7 +1957,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1769,28 +1966,29 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::Golem(_) => { - let skeleton_attr = &self - .golem_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.golem_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); - let state = self - .golem_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, GolemSkeleton::new())); + let state = + self.states.golem_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, GolemSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1808,7 +2006,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::golem::IdleAnimation::update_skeleton( - &GolemSkeleton::new(), + &GolemSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1816,7 +2014,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::golem::RunAnimation::update_skeleton( - &GolemSkeleton::new(), + &GolemSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1824,7 +2022,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::golem::JumpAnimation::update_skeleton( - &GolemSkeleton::new(), + &GolemSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1832,10 +2030,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::golem::IdleAnimation::update_skeleton( + &GolemSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1844,18 +2048,30 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::Object(_) => { - let state = self - .object_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, ObjectSkeleton::new())); + let (model, _) = &self.model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); + + let state = + self.states.object_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, ObjectSkeleton::default()) + }); - state.skeleton = state.skeleton_mut().clone(); state.update( renderer, pos.0, @@ -1864,9 +2080,12 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, true, is_player, + camera, + &mut update_buf, ); }, } @@ -1876,32 +2095,60 @@ impl FigureMgr { self.update_lighting(scene_data); // Clear states that have deleted entities. - self.character_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.quadruped_small_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.quadruped_medium_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.quadruped_low_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.bird_medium_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.fish_medium_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.critter_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.dragon_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.bird_small_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.fish_small_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.biped_large_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.golem_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.object_states + self.states .retain(|entity, _| ecs.entities().is_alive(*entity)); + + visible_aabb + } + + pub fn render_shadows( + &mut self, + renderer: &mut Renderer, + state: &State, + tick: u64, + global: &GlobalModel, + (is_daylight, _light_data): super::LightData, + (camera, figure_lod_render_distance): CameraData, + ) { + let ecs = state.ecs(); + + if is_daylight && renderer.render_mode().shadow.is_map() { + ( + &ecs.entities(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ) + .join() + // Don't render dead entities + .filter(|(_, _, _, _, stats, _, _)| stats.map_or(true, |s| !s.is_dead)) + .for_each(|(entity, pos, _, body, _, loadout, _)| { + if let Some((locals, bone_consts, model, _)) = self.get_model_for_render( + renderer, + tick, + camera, + None, + entity, + body, + loadout, + false, + pos.0, + figure_lod_render_distance, + |state| state.can_shadow_sun(), + ) { + renderer.render_figure_shadow_directed( + model, + global, + locals, + bone_consts, + &global.shadow_mats, + ); + } + }); + } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 @@ -1911,11 +2158,9 @@ impl FigureMgr { state: &State, player_entity: EcsEntity, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, - camera: &Camera, - figure_lod_render_distance: f32, + global: &GlobalModel, + lod: &LodData, + (camera, figure_lod_render_distance): CameraData, ) { let ecs = state.ecs(); @@ -1938,12 +2183,9 @@ impl FigureMgr { let is_player = entity == player_entity; if !is_player { - self.render_figure( + if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( renderer, tick, - globals, - lights, - shadows, camera, character_state, entity, @@ -1952,7 +2194,10 @@ impl FigureMgr { false, pos.0, figure_lod_render_distance, - ); + |state| state.visible(), + ) { + renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod); + } } } } @@ -1964,11 +2209,9 @@ impl FigureMgr { state: &State, player_entity: EcsEntity, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, - camera: &Camera, - figure_lod_render_distance: f32, + global: &GlobalModel, + lod: &LodData, + (camera, figure_lod_render_distance): CameraData, ) { let ecs = state.ecs(); @@ -1989,12 +2232,9 @@ impl FigureMgr { let loadout_storage = ecs.read_storage::(); let loadout = loadout_storage.get(player_entity); - self.render_figure( + if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( renderer, tick, - globals, - lights, - shadows, camera, character_state, player_entity, @@ -2003,35 +2243,47 @@ impl FigureMgr { true, pos.0, figure_lod_render_distance, - ); + |state| state.visible(), + ) { + renderer.render_player(model, &col_lights, global, locals, bone_consts, lod); + renderer.render_player_shadow( + model, + &col_lights, + global, + bone_consts, + lod, + &global.shadow_mats, + ); + } } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 - fn render_figure( + fn get_model_for_render( &mut self, renderer: &mut Renderer, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, camera: &Camera, character_state: Option<&CharacterState>, entity: EcsEntity, body: &Body, loadout: Option<&Loadout>, is_player: bool, - pos: Vec3, + pos: vek::Vec3, figure_lod_render_distance: f32, - ) { + filter_state: impl Fn(&FigureStateMeta) -> bool, + ) -> Option { let player_camera_mode = if is_player { camera.get_mode() } else { CameraMode::default() }; + let focus_pos = camera.get_focus_pos(); + let cam_pos = camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); let character_state = if is_player { character_state } else { None }; let FigureMgr { + col_lights: ref mut col_lights_, model_cache, critter_model_cache, quadruped_small_model_cache, @@ -2044,31 +2296,276 @@ impl FigureMgr { fish_small_model_cache, biped_large_model_cache, golem_model_cache, - character_states, - quadruped_small_states, - quadruped_medium_states, - quadruped_low_states, - bird_medium_states, - fish_medium_states, - critter_states, - dragon_states, - bird_small_states, - fish_small_states, - biped_large_states, - golem_states, - object_states, + states: + FigureMgrStates { + character_states, + quadruped_small_states, + quadruped_medium_states, + quadruped_low_states, + bird_medium_states, + fish_medium_states, + critter_states, + dragon_states, + bird_small_states, + fish_small_states, + biped_large_states, + golem_states, + object_states, + }, } = self; - if let Some((locals, bone_consts, model)) = match body { + let col_lights = &mut *col_lights_; + if let Some((locals, bone_consts, model_entry)) = match body { Body::Humanoid(_) => character_states .get(&entity) - .filter(|state| state.visible) - .map(|state| { + .filter(|state| filter_state(&*state)) + .map(move |state| { ( state.locals(), state.bone_consts(), &model_cache .get_or_create_model( renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::QuadrupedSmall(_) => quadruped_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &quadruped_small_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::QuadrupedMedium(_) => quadruped_medium_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &quadruped_medium_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::QuadrupedLow(_) => quadruped_low_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &quadruped_low_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::BirdMedium(_) => bird_medium_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &bird_medium_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::FishMedium(_) => fish_medium_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &fish_medium_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Critter(_) => critter_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &critter_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Dragon(_) => dragon_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &dragon_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::BirdSmall(_) => bird_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &bird_small_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::FishSmall(_) => fish_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &fish_small_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::BipedLarge(_) => biped_large_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &biped_large_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Golem(_) => golem_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &golem_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Object(_) => object_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &model_cache + .get_or_create_model( + renderer, + col_lights, *body, loadout, tick, @@ -2078,367 +2575,244 @@ impl FigureMgr { .0, ) }), - Body::QuadrupedSmall(_) => quadruped_small_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &quadruped_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::QuadrupedMedium(_) => quadruped_medium_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &quadruped_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::QuadrupedLow(_) => quadruped_low_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &quadruped_low_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::BirdMedium(_) => bird_medium_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &bird_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::FishMedium(_) => fish_medium_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &fish_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Critter(_) => critter_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &critter_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Dragon(_) => dragon_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &dragon_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::BirdSmall(_) => bird_small_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &bird_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::FishSmall(_) => fish_small_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &fish_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::BipedLarge(_) => biped_large_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &biped_large_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Golem(_) => golem_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &golem_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Object(_) => object_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), } { let figure_low_detail_distance = figure_lod_render_distance * 0.75; let figure_mid_detail_distance = figure_lod_render_distance * 0.5; - let model = if pos.distance_squared(camera.get_focus_pos()) - > figure_low_detail_distance.powf(2.0) - { - &model[2] - } else if pos.distance_squared(camera.get_focus_pos()) - > figure_mid_detail_distance.powf(2.0) - { - &model[1] + let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powf(2.0) { + &model_entry.models[2] + } else if pos.distance_squared(cam_pos) > figure_mid_detail_distance.powf(2.0) { + &model_entry.models[1] } else { - &model[0] + &model_entry.models[0] }; - if is_player { - renderer.render_player(model, globals, locals, bone_consts, lights, shadows); - renderer.render_player_shadow(model, globals, locals, bone_consts, lights, shadows); - } else { - renderer.render_figure(model, globals, locals, bone_consts, lights, shadows); - } + Some((locals, bone_consts, model, col_lights_.texture(model_entry))) } else { - trace!("Body has no saved figure"); + // trace!("Body has no saved figure"); + None } } - pub fn figure_count(&self) -> usize { - self.character_states.len() - + self.quadruped_small_states.len() - + self.character_states.len() - + self.quadruped_medium_states.len() - + self.quadruped_low_states.len() - + self.bird_medium_states.len() - + self.fish_medium_states.len() - + self.critter_states.len() - + self.dragon_states.len() - + self.bird_small_states.len() - + self.fish_small_states.len() - + self.biped_large_states.len() - + self.golem_states.len() - + self.object_states.len() + pub fn figure_count(&self) -> usize { self.states.count() } + + pub fn figure_count_visible(&self) -> usize { self.states.count_visible() } +} + +pub struct FigureColLights { + atlas: AtlasAllocator, + // col_lights: Texture, +} + +impl FigureColLights { + pub fn new(renderer: &mut Renderer) -> Self { + let atlas = Self::make_atlas(renderer).expect("Failed to create texture atlas for figures"); + Self { + atlas, /* col_lights, */ + } } - pub fn figure_count_visible(&self) -> usize { - self.character_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .quadruped_small_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .quadruped_medium_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .quadruped_low_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .bird_medium_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .critter_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self.dragon_states.iter().filter(|(_, c)| c.visible).count() - + self - .fish_medium_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .bird_small_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .fish_small_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .biped_large_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self.golem_states.iter().filter(|(_, c)| c.visible).count() - + self.object_states.iter().filter(|(_, c)| c.visible).count() + /// Find the correct texture for this model entry. + pub fn texture<'a, const N: usize>( + &'a self, + model: &'a FigureModelEntry, + ) -> &'a Texture { + /* &self.col_lights */ + &model.col_lights + } + + /// NOTE: Panics if the opaque model's length does not fit in a u32. + /// This is part of the function contract. + /// + /// NOTE: Panics if the vertex range bounds are not in range of the opaque + /// model stored in the BoneMeshes parameter. This is part of the + /// function contract. + pub fn create_figure<'a, const N: usize>( + &mut self, + renderer: &mut Renderer, + greedy: GreedyMesh<'a>, + (opaque, bounds): (Mesh, math::Aabb), + vertex_range: [Range; N], + ) -> Result, RenderError> { + let (tex, tex_size) = greedy.finalize(); + let atlas = &mut self.atlas; + let allocation = atlas + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) + .expect("Not yet implemented: allocate new atlas on allocation failure."); + let col_lights = ShadowPipeline::create_col_lights(renderer, (tex, tex_size))?; + let model_len = u32::try_from(opaque.vertices().len()) + .expect("The model size for this figure does not fit in a u32!"); + let model = renderer.create_model(&opaque)?; + + Ok(FigureModelEntry { + _bounds: bounds, + models: vertex_range.map(|range| { + assert!( + range.start <= range.end && range.end <= model_len, + "The provided vertex range for figure mesh {:?} does not fit in the model, \ + which is of size {:?}!", + range, + model_len + ); + FigureModel { + opaque: model.submodel(range), + } + }), + col_lights, + allocation, + }) + } + + fn make_atlas(renderer: &mut Renderer) -> Result { + let max_texture_size = renderer.max_texture_size(); + let atlas_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { + // TODO: Verify some good empirical constants. + small_size_threshold: 32, + large_size_threshold: 256, + ..guillotiere::AllocatorOptions::default() + }); + // TODO: Consider using a single texture atlas to store all figures, much like + // we do for terrain chunks. We previously avoided this due to + // perceived performance degradation for the figure use case, but with a + // smaller atlas size this may be less likely. + /* let texture = renderer.create_texture_raw( + gfx::texture::Kind::D2( + max_texture_size, + max_texture_size, + gfx::texture::AaMode::Single, + ), + 1 as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE, + gfx::memory::Usage::Dynamic, + (0, 0), + gfx::format::Swizzle::new(), + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + )?; + Ok((atlas, texture)) */ + Ok(atlas) } } -pub struct FigureState { +pub struct FigureStateMeta { bone_consts: Consts, locals: Consts, - lantern_offset: Vec3, + lantern_offset: anim::vek::Vec3, state_time: f64, - skeleton: S, - last_ori: Vec3, + last_ori: anim::vek::Vec3, lpindex: u8, + can_shadow_sun: bool, visible: bool, - last_pos: Option>, - avg_vel: Vec3, + last_pos: Option>, + avg_vel: anim::vek::Vec3, +} + +impl FigureStateMeta { + pub fn visible(&self) -> bool { self.visible } + + pub fn can_shadow_sun(&self) -> bool { + // Either visible, or explicitly a shadow caster. + self.visible || self.can_shadow_sun + } +} + +pub struct FigureState { + meta: FigureStateMeta, + skeleton: S, +} + +impl Deref for FigureState { + type Target = FigureStateMeta; + + fn deref(&self) -> &Self::Target { &self.meta } +} + +impl DerefMut for FigureState { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.meta } } impl FigureState { pub fn new(renderer: &mut Renderer, skeleton: S) -> Self { - let (bone_mats, lantern_offset) = skeleton.compute_matrices(); - let bone_consts = figure_bone_data_from_anim(bone_mats); + let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; + let lantern_offset = + anim::compute_matrices(&skeleton, anim::vek::Mat4::identity(), &mut buf); + let bone_consts = figure_bone_data_from_anim(&buf); Self { - bone_consts: renderer.create_consts(&bone_consts).unwrap(), - locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), - lantern_offset, - state_time: 0.0, + meta: FigureStateMeta { + bone_consts: renderer.create_consts(bone_consts).unwrap(), + locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), + lantern_offset, + state_time: 0.0, + last_ori: anim::vek::Vec3::zero(), + lpindex: 0, + visible: false, + can_shadow_sun: false, + last_pos: None, + avg_vel: anim::vek::Vec3::zero(), + }, skeleton, - last_ori: Vec3::zero(), - lpindex: 0, - visible: false, - last_pos: None, - avg_vel: Vec3::zero(), } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 - pub fn update( + pub fn update( &mut self, renderer: &mut Renderer, - pos: Vec3, - ori: Vec3, + pos: anim::vek::Vec3, + ori: anim::vek::Vec3, scale: f32, - col: Rgba, + col: vek::Rgba, dt: f32, state_animation_rate: f32, - lpindex: u8, - visible: bool, + model: &FigureModelEntry, + _lpindex: u8, + _visible: bool, is_player: bool, + camera: &Camera, + buf: &mut [anim::FigureBoneData; anim::MAX_BONE_COUNT], ) { - self.visible = visible; - self.lpindex = lpindex; - // What is going on here? - // (note: that ori is now the slerped ori) - self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt); + let _frustum = camera.frustum(); + + // Approximate as a sphere with radius equal to the + // largest dimension (if we were exact, it should just be half the largest + // dimension, but we're not, so we double it and use size() instead of + // half_size()). + /* let radius = vek::Extent3::::from(model.bounds.half_size()).reduce_partial_max(); + let _bounds = BoundingSphere::new(pos.into_array(), scale * 0.8 * radius); */ + + self.last_ori = vek::Lerp::lerp(self.last_ori, ori, 15.0 * dt); self.state_time += (dt * state_animation_rate) as f64; - let mat = Mat4::::identity() - * Mat4::translation_3d(pos) - * Mat4::rotation_z(-ori.x.atan2(ori.y)) - * Mat4::rotation_x(ori.z.atan2(Vec2::from(ori).magnitude())) - * Mat4::scaling_3d(Vec3::from(0.8 * scale)); + let mat = anim::vek::Mat4::rotation_z(-ori.x.atan2(ori.y)) + * anim::vek::Mat4::rotation_x(ori.z.atan2(anim::vek::Vec2::from(ori).magnitude())) + * anim::vek::Mat4::scaling_3d(anim::vek::Vec3::from(0.8 * scale)); - let locals = FigureLocals::new(mat, col, is_player); + let atlas_offs = model.allocation.rectangle.min; + let locals = FigureLocals::new( + mat, + col, + pos, + vek::Vec2::new(atlas_offs.x, atlas_offs.y), + is_player, + ); renderer.update_consts(&mut self.locals, &[locals]).unwrap(); - let (new_bone_mats, lantern_offset) = self.skeleton.compute_matrices(); - let new_bone_consts = figure_bone_data_from_anim(new_bone_mats); + let lantern_offset = anim::compute_matrices(&self.skeleton, mat, buf); + + let new_bone_consts = figure_bone_data_from_anim(buf); renderer .update_consts( - &mut self.bone_consts, - &new_bone_consts[0..self.skeleton.bone_count()], + &mut self.meta.bone_consts, + &new_bone_consts[0..S::BONE_COUNT], ) .unwrap(); self.lantern_offset = lantern_offset; @@ -2457,23 +2831,8 @@ impl FigureState { pub fn skeleton_mut(&mut self) -> &mut S { &mut self.skeleton } } -fn figure_bone_data_from_anim(mats: [anim::FigureBoneData; 16]) -> [FigureBoneData; 16] { - [ - FigureBoneData::new(mats[0].0), - FigureBoneData::new(mats[1].0), - FigureBoneData::new(mats[2].0), - FigureBoneData::new(mats[3].0), - FigureBoneData::new(mats[4].0), - FigureBoneData::new(mats[5].0), - FigureBoneData::new(mats[6].0), - FigureBoneData::new(mats[7].0), - FigureBoneData::new(mats[8].0), - FigureBoneData::new(mats[9].0), - FigureBoneData::new(mats[10].0), - FigureBoneData::new(mats[11].0), - FigureBoneData::new(mats[12].0), - FigureBoneData::new(mats[13].0), - FigureBoneData::new(mats[14].0), - FigureBoneData::new(mats[15].0), - ] +fn figure_bone_data_from_anim( + mats: &[anim::FigureBoneData; anim::MAX_BONE_COUNT], +) -> &[FigureBoneData] { + gfx::memory::cast_slice(mats) } diff --git a/voxygen/src/scene/lod.rs b/voxygen/src/scene/lod.rs new file mode 100644 index 0000000000..f241d8fa0d --- /dev/null +++ b/voxygen/src/scene/lod.rs @@ -0,0 +1,97 @@ +use crate::{ + render::{ + pipelines::lod_terrain::{Locals, LodData, Vertex}, + Consts, GlobalModel, LodTerrainPipeline, Mesh, Model, Quad, Renderer, + }, + settings::Settings, +}; +use client::Client; +use common::{spiral::Spiral2d, util::srgba_to_linear}; +use vek::*; + +pub struct Lod { + model: Option<(u32, Model)>, + locals: Consts, + data: LodData, +} + +// TODO: Make constant when possible. +pub fn water_color() -> Rgba { + /* Rgba::new(0.2, 0.5, 1.0, 0.0) */ + srgba_to_linear(Rgba::new(0.0, 0.25, 0.5, 0.0)) +} + +impl Lod { + pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self { + Self { + model: None, + locals: renderer.create_consts(&[Locals::default()]).unwrap(), + data: LodData::new( + renderer, + client.world_map.1, + &client.lod_base, + &client.lod_alt, + &client.lod_horizon, + settings.graphics.lod_detail.max(100).min(2500), + water_color().into_array().into(), + ), + } + } + + pub fn get_data(&self) -> &LodData { &self.data } + + pub fn set_detail(&mut self, detail: u32) { + // Make sure the recorded detail is even. + self.data.tgt_detail = (detail - detail % 2).max(100).min(2500); + } + + pub fn maintain(&mut self, renderer: &mut Renderer) { + if self + .model + .as_ref() + .map(|(detail, _)| *detail != self.data.tgt_detail) + .unwrap_or(true) + { + self.model = Some(( + self.data.tgt_detail, + renderer + .create_model(&create_lod_terrain_mesh(self.data.tgt_detail)) + .unwrap(), + )); + } + } + + pub fn render(&self, renderer: &mut Renderer, global: &GlobalModel) { + if let Some((_, model)) = self.model.as_ref() { + renderer.render_lod_terrain(&model, global, &self.locals, &self.data); + } + } +} + +fn create_lod_terrain_mesh(detail: u32) -> Mesh { + // detail is even, so we choose odd detail (detail + 1) to create two even + // halves with an empty hole. + let detail = detail + 1; + Spiral2d::new() + .take((detail * detail) as usize) + .skip(1) + .map(|pos| { + let x = pos.x + detail as i32 / 2; + let y = pos.y + detail as i32 / 2; + + let transform = |x| (2.0 * x as f32) / detail as f32 - 1.0; + + Quad::new( + Vertex::new(Vec2::new(x, y).map(transform)), + Vertex::new(Vec2::new(x + 1, y).map(transform)), + Vertex::new(Vec2::new(x + 1, y + 1).map(transform)), + Vertex::new(Vec2::new(x, y + 1).map(transform)), + ) + .rotated_by(if (x > detail as i32 / 2) ^ (y > detail as i32 / 2) { + 0 + } else { + 1 + }) + }) + .collect() +} diff --git a/voxygen/src/scene/math.rs b/voxygen/src/scene/math.rs new file mode 100644 index 0000000000..3ebb5fdfad --- /dev/null +++ b/voxygen/src/scene/math.rs @@ -0,0 +1,390 @@ +use core::{iter, mem}; +use hashbrown::HashMap; +use num::traits::Float; +pub use vek::{geom::repr_simd::*, mat::repr_simd::column_major::Mat4, ops::*, vec::repr_simd::*}; +// pub use vek::{geom::repr_c::*, mat::repr_c::column_major::Mat4, ops::*, +// vec::repr_c::*}; + +pub fn aabb_to_points(bounds: Aabb) -> [Vec3; 8] { + [ + Vec3::new(bounds.min.x, bounds.min.y, bounds.min.z), + Vec3::new(bounds.max.x, bounds.min.y, bounds.min.z), + Vec3::new(bounds.max.x, bounds.max.y, bounds.min.z), + Vec3::new(bounds.min.x, bounds.max.y, bounds.min.z), + Vec3::new(bounds.min.x, bounds.min.y, bounds.max.z), + Vec3::new(bounds.max.x, bounds.min.y, bounds.max.z), + Vec3::new(bounds.max.x, bounds.max.y, bounds.max.z), + Vec3::new(bounds.min.x, bounds.max.y, bounds.max.z), + ] +} + +/// Each Vec4 should be interpreted as reprenting plane +/// equation +/// +/// a(x - x0) + b(y - y0) + c(z - z0) = 0, i.e. +/// ax + by + cz - (a * x0 + b * y0 + c * z0) = 0, i.e. +/// ax + by + cz = (a * x0 + b * y0 + c * z0), i.e. +/// (lettiing d = a * x0 + b * y0 + c * z0) +/// ax + by + cz = d +/// +/// where d is the distance of the plane from the origin. +pub fn aabb_to_planes(bounds: Aabb) -> [Vec4; 6] { + let zero = T::zero(); + let one = T::one(); + let bounds = bounds.map(|e| e.abs()); + [ + // bottom + Vec4::new(zero, -one, zero, -bounds.min.y), + // top + Vec4::new(zero, one, zero, -bounds.max.y), + // left + Vec4::new(-one, zero, zero, -bounds.min.x), + // right + Vec4::new(one, zero, zero, -bounds.max.x), + // near + Vec4::new(zero, zero, -one, -bounds.min.z), + // far + Vec4::new(zero, zero, one, -bounds.max.z), + ] +} + +pub fn mat_mul_points>( + mat: Mat4, + pts: &mut [Vec3], + mut do_p: impl FnMut(Vec4) -> Vec3, +) { + pts.iter_mut().for_each(|p| { + *p = do_p(mat * Vec4::from_point(*p)); + }); +} + +/// NOTE: Expects points computed from aabb_to_points. +pub fn calc_view_frust_object(pts: &[Vec3; 8]) -> Vec>> { + vec![ + // near (CCW) + vec![pts[0], pts[1], pts[2], pts[3]], + // far (CCW) + vec![pts[7], pts[6], pts[5], pts[4]], + // left (CCW) + vec![pts[0], pts[3], pts[7], pts[4]], + // right (CCW) + vec![pts[1], pts[5], pts[6], pts[2]], + // bottom (CCW) + vec![pts[4], pts[5], pts[1], pts[0]], + // top (CCW) + vec![pts[6], pts[7], pts[3], pts[2]], + ] +} + +pub fn calc_view_frustum_world_coord>( + inv_proj_view: Mat4, +) -> [Vec3; 8] { + let mut world_pts = aabb_to_points(Aabb { + min: -Vec3::one(), + max: Vec3::one(), + }); + mat_mul_points(inv_proj_view, &mut world_pts, |p| Vec3::from(p) / p.w); + world_pts +} + +pub fn point_plane_distance(point: Vec3, norm_dist: Vec4) -> T { + norm_dist.dot(Vec4::from_point(point)) +} + +pub fn point_before_plane(point: Vec3, plane: Vec4) -> bool { + point_plane_distance(point, plane) > T::zero() +} + +/// Returns true if and only if the final point in the polygon (i.e. the +/// first point added to the new polygon) is outside the clipping plane +/// (this implies that the polygon must be non-degenerate). +pub fn clip_points_by_plane + core::fmt::Debug>( + points: &mut Vec>, + plane: Vec4, + intersection_points: &mut Vec>, +) -> bool { + if points.len() < 3 { + return false; + } + // NOTE: Guaranteed to succeed since points.len() > 3. + let mut current_point = points[points.len() - 1]; + let intersect_plane_edge = |a, b| { + let diff: Vec3<_> = b - a; + let t = plane.dot(Vec4::from_direction(diff)); + if t == T::zero() { + None + } else { + let t = -(plane.dot(Vec4::from_point(a)) / t); + if t < T::zero() || T::one() < t { + None + } else { + Some(diff * t + a) + } + } + }; + let last_is_outside = point_before_plane(current_point, plane); + let mut is_outside = last_is_outside; + let mut old_points = Vec::with_capacity((3 * points.len()) / 2); + mem::swap(&mut old_points, points); + old_points.into_iter().for_each(|point| { + let prev_point = mem::replace(&mut current_point, point); + let before_plane = point_before_plane(current_point, plane); + let prev_is_outside = mem::replace(&mut is_outside, before_plane); + if !prev_is_outside { + // Push previous point. + points.push(prev_point); + } + if prev_is_outside != is_outside { + if let Some(intersection_point) = intersect_plane_edge(prev_point, current_point) { + // Push intersection point. + intersection_points.push(intersection_point); + points.push(intersection_point); + } + } + }); + last_is_outside +} + +fn append_intersection_points( + polys: &mut Vec>>, + intersection_points: Vec>, + tolerance: T, +) { + // NOTE: We use decoded versions of each polygon, with rounded entries. + // + // The line segments in intersection_points are consistently ordered as follows: + // each segment represents the intersection of the cutting plane with the + // polygon from which the segment came. The polygon can thus be split into + // two parts: the part "inside" the new surface (below the plane), and the + // part "outside" it (above the plane). Thus, when oriented + // with the polygon normal pointing into the camera, and the cutting plane as + // the x axis, with the "outside" part on top and the "inside" part on the + // bottom, there is a leftmost point (the point going from outside to + // inside, counterclockwise) and a rightmost point (the point going from + // inside to outside, counterclockwise). Our consistent ordering guarantees + // that the leftmost point comes before the rightmost point in each line + // segment. + // + // Why does this help us? To see that, consider the polygon adjacent to the + // considered polygon which also has the same right intersection point (we + // know there will be exactly one of these, because we have a solid + // structure and are only considering polygons that intersect the plane + // exactly two times; this means that we are ignoring polygons that intersect + // the plane at just one point, which means the two polygons must share a + // point, not be coplanar, and both intersect the plane; no theorem here, + // but I believe there can provably be at most one such instance given that + // we have at least three polygons with such a line segment). + // + // Now, for the adjacent polygon, repeat the above process. If the intersection + // point shared by the polygons is on the right in both cases, then we can + // see that the polygon's normal must be facing in the opposite direction of + // the original polygon despite being adjacent. But this + // should be impossible for a closed object! The same applies to the leftmost + // point. + // + // What is the practical upshot of all this? It means that we can consistently + // hash each line segment by its first point, which we can look up using the + // second point of a previous line segment. This will produce a chain of + // entries terminating in the original segment we looked up. As an added + // bonus, by going from leftmost point to leftmost point, we also ensure that + // we produce a polygon whose face is oriented counterclockwise around its + // normal; this can be seen by following the right-hand rule (TODO: provide + // more rigorous proof). + let tol = tolerance.recip(); + let make_key = move |point: Vec3| { + // We use floating points rounded to tolerance in order to make our HashMap + // lookups work. Otherwise we'd have to use a sorted structure, like a + // btree, which wouldn't be the end of the world but would have + // theoretically worse complexity. + // + // NOTE: Definitely non-ideal that we panic if the rounded value can't fit in an + // i64... + // + // TODO: If necessary, let the caller specify how to hash these keys, since in + // cases where we know the kind of floating point we're using we can + // just cast to bits or something. + point.map(|e| { + (e * tol) + .round() + .to_i64() + .expect("We don't currently try to handle floats that won't fit in an i64.") + }) + }; + let mut lines_iter = intersection_points.chunks_exact(2).filter_map(|uv| { + let u_key = make_key(uv[0]); + let v = uv[1]; + // NOTE: The reason we need to make sure this doesn't happen is that it's + // otherwise possible for two points to hash to the same value due to + // epsilon being too low. Because of the ordering mentioned previously, + // we know we should *eventually* find a pair of points starting with + // make_key(u) and ending with a different make_key(v) in such cases, so + // we just discard all the other pairs (treating them as points rather + // than lines). + (u_key != make_key(v)).then_some((u_key, v)) + }); + + if let Some((last_key, first)) = lines_iter.next() { + let lines = lines_iter.collect::>(); + if lines.len() < 2 { + // You need at least 3 sides for a polygon + return; + } + // NOTE: Guaranteed to terminate, provided we have no cycles besides the one + // that touches every point (which should be the case given how these + // points were generated). + let poly_iter = iter::successors(Some(first), |&cur| lines.get(&make_key(cur)).copied()); + let poly: Vec<_> = poly_iter.collect(); + // We have to check to make sure we really went through the whole cycle. + // TODO: Consider adaptively decreasing precision until we can make the cycle + // happen. + if poly.last().copied().map(make_key) == Some(last_key) { + // Push the new polygon onto the object. + polys.push(poly); + } + } +} + +pub fn clip_object_by_plane + core::fmt::Debug>( + polys: &mut Vec>>, + plane: Vec4, + tolerance: T, +) { + let mut intersection_points = Vec::new(); + polys.drain_filter(|points| { + let len = intersection_points.len(); + let outside_first = clip_points_by_plane(points, plane, &mut intersection_points); + // Only remember intersections that are not coplanar with this side; i.e. those + // that have segment length 2. + if len + 2 != intersection_points.len() { + intersection_points.truncate(len); + } else if !outside_first { + // Order the two intersection points consistently, so that, when considered + // counterclockwise: + // - the first point goes from the exterior of the polygon (above the cutting + // plane) to its interior. + // - the second point goes from the interior of the polygon (below the cutting + // plane) to its exterior. + // the second is always going + // + // This allows us to uniquely map each line segment to an "owning" point (the + // one going from outside to inside), which happens to also point + // the segment in a counterclockwise direction around the new + // polygon normal composed of all the lines we clipped. + intersection_points.swap(len, len + 1); + } + // Remove polygon if it was clipped away + points.is_empty() + }); + // Add a polygon of all intersection points with the plane to close out the + // object. + append_intersection_points(polys, intersection_points, tolerance); +} + +pub fn clip_object_by_aabb + core::fmt::Debug>( + polys: &mut Vec>>, + bounds: Aabb, + tolerance: T, +) { + let planes = aabb_to_planes(bounds); + planes.iter().for_each(|&plane| { + clip_object_by_plane(polys, plane, tolerance); + }); +} + +/// Return value is 'Some(segment)' if line segment intersects the current +/// test plane. Otherwise 'None' is returned in which case the line +/// segment is entirely clipped. +pub fn clip_test(p: T, q: T, (u1, u2): (T, T)) -> Option<(T, T)> { + if p == T::zero() { + if q >= T::zero() { Some((u1, u2)) } else { None } + } else { + let r = q / p; + if p < T::zero() { + if r > u2 { + None + } else { + Some((if r > u1 { r } else { u1 }, u2)) + } + } else if r < u1 { + None + } else { + Some((u1, if r < u2 { r } else { u2 })) + } + } +} + +pub fn intersection_line_aabb + core::fmt::Debug>( + p: Vec3, + dir: Vec3, + bounds: Aabb, +) -> Option> { + clip_test(-dir.z, p.z - bounds.min.z, (T::zero(), T::infinity())) + .and_then(|t| clip_test(dir.z, bounds.max.z - p.z, t)) + .and_then(|t| clip_test(-dir.y, p.y - bounds.min.y, t)) + .and_then(|t| clip_test(dir.y, bounds.max.y - p.y, t)) + .and_then(|t| clip_test(-dir.x, p.x - bounds.min.x, t)) + .and_then(|t| clip_test(dir.x, bounds.max.x - p.x, t)) + .and_then(|(t1, t2)| { + if T::zero() <= t2 { + Some(dir.mul_add(Vec3::broadcast(t2), p)) + } else if T::zero() <= t1 { + Some(dir.mul_add(Vec3::broadcast(t1), p)) + } else { + None + } + }) +} + +pub fn include_object_light_volume< + T: Float + MulAdd + core::fmt::Debug, + I: Iterator>, +>( + obj: I, + light_dir: Vec3, + bounds: Aabb, +) -> impl Iterator> { + obj.flat_map(move |pt| iter::once(pt).chain(intersection_line_aabb(pt, -light_dir, bounds))) +} + +// NOTE: Currently specialized to skip extending to the end of the light ray, +// since our light ray is already infinite. Correct code is commented out +// below. +pub fn calc_focused_light_volume_points + core::fmt::Debug>( + inv_proj_view: Mat4, + _light_dir: Vec3, + scene_bounding_box: Aabb, + tolerance: T, +) -> impl Iterator> { + let world_pts = calc_view_frustum_world_coord(inv_proj_view); + let mut world_frust_object = calc_view_frust_object(&world_pts); + clip_object_by_aabb(&mut world_frust_object, scene_bounding_box, tolerance); + world_frust_object.into_iter().flat_map(|e| e.into_iter()) + /* include_object_light_volume( + world_frust_object.into_iter().flat_map(|e| e.into_iter()), + light_dir, + scene_bounding_box, + ) */ +} + +/// NOTE: Will not yield useful results if pts is empty! +pub fn fit_psr< + T: Float + MulAdd, + I: Iterator>, + F: FnMut(Vec4) -> Vec4, +>( + mat: Mat4, + pts: I, + mut do_p: F, +) -> Aabb { + let mut min = Vec4::broadcast(T::infinity()); + let mut max = Vec4::broadcast(T::neg_infinity()); + pts.map(|p| do_p(mat * Vec4::::from_point(p))) + .for_each(|p| { + min = Vec4::partial_min(min, p); + max = Vec4::partial_max(max, p); + }); + Aabb { + min: min.xyz(), + max: max.xyz(), + } +} diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index c4e338d463..d69cf98c2c 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1,5 +1,7 @@ pub mod camera; pub mod figure; +pub mod lod; +pub mod math; pub mod particle; pub mod simple; pub mod terrain; @@ -7,18 +9,22 @@ pub mod terrain; pub use self::{ camera::{Camera, CameraMode}, figure::FigureMgr, + lod::Lod, particle::ParticleMgr, terrain::Terrain, }; use crate::{ audio::{music::MusicMgr, sfx::SfxMgr, AudioFrontend}, render::{ - create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals, - PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, + create_pp_mesh, create_skybox_mesh, Consts, GlobalModel, Globals, Light, LodData, Model, + PostProcessLocals, PostProcessPipeline, Renderer, Shadow, ShadowLocals, SkyboxLocals, + SkyboxPipeline, }, + settings::Settings, window::{AnalogGameInput, Event}, }; use anim::character::SkeletonAttr; +use client::Client; use common::{ comp, outcome::Outcome, @@ -27,22 +33,32 @@ use common::{ vol::ReadVol, }; use comp::item::Reagent; +use num::traits::{Float, FloatConst}; use specs::{Entity as EcsEntity, Join, WorldExt}; use vek::*; // TODO: Don't hard-code this. const CURSOR_PAN_SCALE: f32 = 0.005; -const MAX_LIGHT_COUNT: usize = 32; +const MAX_LIGHT_COUNT: usize = 31; const MAX_SHADOW_COUNT: usize = 24; +const NUM_DIRECTED_LIGHTS: usize = 1; const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not emit light from their origin const SHADOW_DIST_RADIUS: f32 = 8.0; const SHADOW_MAX_DIST: f32 = 96.0; // The distance beyond which shadows may not be visible +/// The minimum sin γ we will use before switching to uniform mapping. +const EPSILON_UPSILON: f64 = -1.0; + +const SHADOW_NEAR: f32 = 0.25; // Near plane for shadow map point light rendering. +const SHADOW_FAR: f32 = 128.0; // Far plane for shadow map point light rendering. /// Above this speed is considered running /// Used for first person camera effects const RUNNING_THRESHOLD: f32 = 0.7; +/// is_daylight, array of active lights. +pub type LightData<'a> = (bool, &'a [Light]); + struct EventLight { light: Light, timeout: f32, @@ -60,9 +76,7 @@ struct PostProcess { } pub struct Scene { - globals: Consts, - lights: Consts, - shadows: Consts, + data: GlobalModel, camera: Camera, camera_input_state: Vec2, event_lights: Vec, @@ -70,8 +84,14 @@ pub struct Scene { skybox: Skybox, postprocess: PostProcess, terrain: Terrain, + pub lod: Lod, loaded_distance: f32, + /// x coordinate is sea level (minimum height for any land chunk), and y + /// coordinate is the maximum height above the mnimimum for any land + /// chunk. + map_bounds: Vec2, select_pos: Option>, + light_data: Vec, particle_mgr: ParticleMgr, figure_mgr: FigureMgr, @@ -95,19 +115,164 @@ pub struct SceneData<'a> { pub is_aiming: bool, } +impl<'a> SceneData<'a> { + pub fn get_sun_dir(&self) -> Vec3 { Globals::get_sun_dir(self.state.get_time_of_day()) } + + pub fn get_moon_dir(&self) -> Vec3 { Globals::get_moon_dir(self.state.get_time_of_day()) } +} + +/// Approximte a scalar field of view angle using the parameterization from +/// section 4.3 of Lloyd's thesis: +/// +/// W_e = 2 n_e tan θ +/// +/// where +/// +/// W_e = 2 is the width of the image plane (for our projections, since they go +/// from -1 to 1) n_e = near_plane is the near plane for the view frustum +/// θ = (fov / 2) is the half-angle of the FOV (the one passed to +/// Mat4::projection_rh_no). +/// +/// Although the widths for the x and y image planes are the same, they are +/// different in this framework due to the introduction of an aspect ratio: +/// +/// y'(p) = 1.0 / tan(fov / 2) * p.y / -p.z +/// x'(p) = 1.0 / (aspect * tan(fov / 2)) * p.x / -p.z +/// +/// i.e. +/// +/// y'(x, y, -near, w) = 1 / tan(fov / 2) p.y / near +/// x'(x, y, -near, w) = 1 / (aspect * tan(fov / 2)) p.x / near +/// +/// W_e,y = 2 * near_plane * tan(fov / 2) +/// W_e,x = 2 * near_plane * aspect * W_e,y +/// +/// Θ_x = atan(W_e_y / 2 / near_plane) = atanfov / t() +/// +/// i.e. we have an "effective" W_e_x of +/// +/// 2 = 2 * near_plane * tan Θ +/// +/// atan(1 / near_plane) = θ +/// +/// y' +/// x(-near) +/// W_e = 2 * near_plane * +/// +/// W_e_y / n_e = tan (fov / 2) +/// W_e_x = 2 n +fn compute_scalar_fov(_near_plane: F, fov: F, aspect: F) -> F { + let two = F::one() + F::one(); + let theta_y = fov / two; + let theta_x = (aspect * theta_y.tan()).atan(); + theta_x.min(theta_y) +} + +/// Compute a near-optimal warping parameter that helps minimize error in a +/// shadow map. +/// +/// See section 5.2 of Brandon Lloyd's thesis: +/// +/// [http://gamma.cs.unc.edu/papers/documents/dissertations/lloyd07.pdf](Logarithmic Perspective Shadow Maps). +/// +/// η = +/// 0 γ < γ_a +/// -1 + (η_b + 1)(1 + cos(90 (γ - γ_a)/(γ_b - γ_a))) γ_a ≤ γ < γ_b +/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c +/// η_c γ_c ≤ γ +/// +/// NOTE: Equation's described behavior is *wrong!* I have pieced together a +/// slightly different function that seems to more closely satisfy the author's +/// intent: +/// +/// η = +/// -1 γ < γ_a +/// -1 + (η_b + 1) (γ - γ_a)/(γ_b - γ_a) γ_a ≤ γ < γ_b +/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c +/// η_c γ_c ≤ γ +/// +/// There are other alternatives that may have more desirable properties, such +/// as: +/// +/// η = +/// -1 γ < γ_a +/// -1 + (η_b + 1)(1 - cos(90 (γ - γ_a)/(γ_b - γ_a))) γ_a ≤ γ < γ_b +/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c +/// η_c γ_c ≤ γ +fn compute_warping_parameter( + gamma: F, + (gamma_a, gamma_b, gamma_c): (F, F, F), + (eta_b, eta_c): (F, F), +) -> F { + if gamma < gamma_a { + -F::one() + /* F::zero() */ + } else if gamma_a <= gamma && gamma < gamma_b { + /* -F::one() + (eta_b + F::one()) * (F::one() + (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos()) */ + -F::one() + (eta_b + F::one()) * (F::one() - (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos()) + // -F::one() + (eta_b + F::one()) * ((gamma - gamma_a) / (gamma_b - gamma_a)) + } else if gamma_b <= gamma && gamma < gamma_c { + eta_b + (eta_c - eta_b) * (F::FRAC_PI_2() * (gamma - gamma_b) / (gamma_c - gamma_b)).sin() + } else { + eta_c + } + // NOTE: Just in case we go out of range due to floating point imprecision. + .max(-F::one()).min(F::one()) +} + +/// Compute a near-optimal warping parameter that falls off quickly enough +/// when the warp angle goes past the minimum field of view angle, for +/// perspective projections. +/// +/// For F_p (perspective warping) and view fov angle θ,the parameters are: +/// +/// γ_a = θ / 3 +/// γ_b = θ +/// γ_c = θ + 0.3(90 - θ) +/// +/// η_b = -0.2 +/// η_c = 0 +/// +/// See compute_warping_parameter. +fn compute_warping_parameter_perspective( + gamma: F, + near_plane: F, + fov: F, + aspect: F, +) -> F { + let theta = compute_scalar_fov(near_plane, fov, aspect); + let two = F::one() + F::one(); + let three = two + F::one(); + let ten = three + three + three + F::one(); + compute_warping_parameter( + gamma, + ( + theta / three, + theta, + theta + (three / ten) * (F::FRAC_PI_2() - theta), + ), + (-two / ten, F::zero()), + ) +} + impl Scene { /// Create a new `Scene` with default parameters. - pub fn new(renderer: &mut Renderer) -> Self { + pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self { let resolution = renderer.get_resolution().map(|e| e as f32); Self { - globals: renderer.create_consts(&[Globals::default()]).unwrap(), - lights: renderer - .create_consts(&[Light::default(); MAX_LIGHT_COUNT]) - .unwrap(), - shadows: renderer - .create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]) - .unwrap(), + data: GlobalModel { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + lights: renderer + .create_consts(&[Light::default(); MAX_LIGHT_COUNT]) + .unwrap(), + shadows: renderer + .create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]) + .unwrap(), + shadow_mats: renderer + .create_consts(&[ShadowLocals::default(); MAX_LIGHT_COUNT * 6 + 6]) + .unwrap(), + }, camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson), camera_input_state: Vec2::zero(), event_lights: Vec::new(), @@ -123,18 +288,20 @@ impl Scene { .unwrap(), }, terrain: Terrain::new(renderer), + lod: Lod::new(renderer, client, settings), loaded_distance: 0.0, + map_bounds: client.world_map.2, select_pos: None, - + light_data: Vec::new(), particle_mgr: ParticleMgr::new(renderer), - figure_mgr: FigureMgr::new(), + figure_mgr: FigureMgr::new(renderer), sfx_mgr: SfxMgr::new(), music_mgr: MusicMgr::new(), } } /// Get a reference to the scene's globals. - pub fn globals(&self) -> &Consts { &self.globals } + pub fn globals(&self) -> &Consts { &self.data.globals } /// Get a reference to the scene's camera. pub fn camera(&self) -> &Camera { &self.camera } @@ -142,6 +309,9 @@ impl Scene { /// Get a reference to the scene's terrain. pub fn terrain(&self) -> &Terrain { &self.terrain } + /// Get a reference to the scene's lights. + pub fn lights(&self) -> &Vec { &self.light_data } + /// Get a reference to the scene's particle manager. pub fn particle_mgr(&self) -> &ParticleMgr { &self.particle_mgr } @@ -328,55 +498,60 @@ impl Scene { } = self.camera.dependents(); // Update chunk loaded distance smoothly for nice shader fog - self.loaded_distance = + let loaded_distance = (0.98 * self.loaded_distance + 0.02 * scene_data.loaded_distance).max(0.01); // Update light constants - let mut lights = ( - &scene_data.state.ecs().read_storage::(), - scene_data.state.ecs().read_storage::().maybe(), - scene_data - .state - .ecs() - .read_storage::() - .maybe(), - &scene_data - .state - .ecs() - .read_storage::(), - ) - .join() - .filter(|(pos, _, _, _)| { - (pos.0.distance_squared(player_pos) as f32) - < self.loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS - }) - .map(|(pos, ori, interpolated, light_anim)| { - // Use interpolated values if they are available - let (pos, ori) = - interpolated.map_or((pos.0, ori.map(|o| o.0)), |i| (i.pos, Some(i.ori))); - let rot = { - if let Some(o) = ori { - Mat3::rotation_z(-o.x.atan2(o.y)) - } else { - Mat3::identity() - } - }; - Light::new( - pos + (rot * light_anim.offset), - light_anim.col, - light_anim.strength, - ) - }) - .chain( - self.event_lights - .iter() - .map(|el| el.light.with_strength((el.fadeout)(el.timeout))), + let lights = &mut self.light_data; + lights.clear(); + lights.extend( + ( + &scene_data.state.ecs().read_storage::(), + scene_data.state.ecs().read_storage::().maybe(), + scene_data + .state + .ecs() + .read_storage::() + .maybe(), + &scene_data + .state + .ecs() + .read_storage::(), ) - .collect::>(); + .join() + .filter(|(pos, _, _, light_anim)| { + light_anim.col != Rgb::zero() + && light_anim.strength > 0.0 + && (pos.0.distance_squared(player_pos) as f32) + < loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS + }) + .map(|(pos, ori, interpolated, light_anim)| { + // Use interpolated values if they are available + let (pos, ori) = + interpolated.map_or((pos.0, ori.map(|o| o.0)), |i| (i.pos, Some(i.ori))); + let rot = { + if let Some(o) = ori { + Mat3::rotation_z(-o.x.atan2(o.y)) + } else { + Mat3::identity() + } + }; + Light::new( + pos + (rot * light_anim.offset), + light_anim.col, + light_anim.strength, + ) + }) + .chain( + self.event_lights + .iter() + .map(|el| el.light.with_strength((el.fadeout)(el.timeout))), + ), + ); lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32); lights.truncate(MAX_LIGHT_COUNT); renderer - .update_consts(&mut self.lights, &lights) + .update_consts(&mut self.data.lights, &lights) .expect("Failed to update light constants"); // Update event lights @@ -402,7 +577,7 @@ impl Scene { .filter(|(_, _, _, _, stats)| !stats.is_dead) .filter(|(pos, _, _, _, _)| { (pos.0.distance_squared(player_pos) as f32) - < (self.loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0) + < (loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0) }) .map(|(pos, interpolated, scale, _, _)| { Shadow::new( @@ -415,47 +590,353 @@ impl Scene { shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32); shadows.truncate(MAX_SHADOW_COUNT); renderer - .update_consts(&mut self.shadows, &shadows) + .update_consts(&mut self.data.shadows, &shadows) .expect("Failed to update light constants"); + // Remember to put the new loaded distance back in the scene. + self.loaded_distance = loaded_distance; + + // Update light projection matrices for the shadow map. + let time_of_day = scene_data.state.get_time_of_day(); + let focus_pos = self.camera.get_focus_pos(); + let focus_off = focus_pos.map(|e| e.trunc()); + // Update global constants. renderer - .update_consts(&mut self.globals, &[Globals::new( + .update_consts(&mut self.data.globals, &[Globals::new( view_mat, proj_mat, cam_pos, - self.camera.get_focus_pos(), + focus_pos, self.loaded_distance, - scene_data.state.get_time_of_day(), + self.lod.get_data().tgt_detail as f32, + self.map_bounds, + time_of_day, scene_data.state.get_time(), renderer.get_resolution(), + Vec2::new(SHADOW_NEAR, SHADOW_FAR), lights.len(), shadows.len(), + NUM_DIRECTED_LIGHTS, scene_data .state .terrain() - .get(cam_pos.map(|e| e.floor() as i32)) + .get((cam_pos + focus_off).map(|e| e.floor() as i32)) .map(|b| b.kind()) .unwrap_or(BlockKind::Air), - self.select_pos, + self.select_pos.map(|e| e - focus_off.map(|e| e as i32)), scene_data.gamma, self.camera.get_mode(), scene_data.sprite_render_distance as f32 - 20.0, )]) .expect("Failed to update global constants"); + // Maintain LoD. + self.lod.maintain(renderer); + // Maintain the terrain. - self.terrain.maintain( + let (_visible_bounds, visible_light_volume, visible_psr_bounds) = self.terrain.maintain( renderer, &scene_data, - self.camera.get_focus_pos(), + focus_pos, self.loaded_distance, view_mat, proj_mat, ); // Maintain the figures. - self.figure_mgr.maintain(renderer, scene_data, &self.camera); + let _figure_bounds = + self.figure_mgr + .maintain(renderer, scene_data, visible_psr_bounds, &self.camera); + + let sun_dir = scene_data.get_sun_dir(); + let is_daylight = sun_dir.z < 0.0; + if renderer.render_mode().shadow.is_map() && (is_daylight || !lights.is_empty()) { + let fov = self.camera.get_fov(); + let aspect_ratio = self.camera.get_aspect_ratio(); + + let view_dir = ((focus_pos.map(f32::fract)) - cam_pos).normalized(); + let (point_shadow_res, _directed_shadow_res) = renderer.get_shadow_resolution(); + // NOTE: The aspect ratio is currently always 1 for our cube maps, since they + // are equal on all sides. + let point_shadow_aspect = point_shadow_res.x as f32 / point_shadow_res.y as f32; + // Construct matrices to transform from world space to light space for the sun + // and moon. + let directed_light_dir = math::Vec3::from(sun_dir); + + // Optimal warping for directed lights: + // + // n_opt = 1 / sin y (z_n + √(z_n + (f - n) sin y)) + // + // where n is near plane, f is far plane, y is the tilt angle between view and + // light directon, and n_opt is the optimal near plane. + // We also want a way to transform and scale this matrix (* 0.5 + 0.5) in order + // to transform it correctly into texture coordinates, as well as + // OpenGL coordinates. Note that the matrix for directional light + // is *already* linear in the depth buffer. + let texture_mat = Mat4::scaling_3d(0.5f32) * Mat4::translation_3d(1.0f32); + // We need to compute these offset matrices to tranform world space coordinates + // to the translated ones we use when multiplying by the light space + // matrix; this helps avoid precision loss during the + // multiplication. + let look_at = math::Vec3::from(cam_pos); + // We upload view matrices as well, to assist in linearizing vertex positions. + // (only for directional lights, so far). + let mut directed_shadow_mats = Vec::with_capacity(6); + let new_dir = math::Vec3::from(view_dir); + let new_dir = new_dir.normalized(); + let up: math::Vec3 = math::Vec3::up(); + directed_shadow_mats.push(math::Mat4::look_at_rh( + look_at, + look_at + directed_light_dir, + up, + )); + // This leaves us with five dummy slots, which we push as defaults. + directed_shadow_mats + .extend_from_slice(&[math::Mat4::default(); 6 - NUM_DIRECTED_LIGHTS] as _); + // Now, construct the full projection matrices in the first two directed light + // slots. + let mut shadow_mats = Vec::with_capacity(6 * (lights.len() + 1)); + shadow_mats.extend(directed_shadow_mats.iter().enumerate().map( + move |(idx, &light_view_mat)| { + if idx >= NUM_DIRECTED_LIGHTS { + return ShadowLocals::new(Mat4::identity(), Mat4::identity()); + } + + let v_p_orig = + math::Vec3::from(light_view_mat * math::Vec4::from_direction(new_dir)); + let mut v_p = v_p_orig.normalized(); + let cos_gamma = new_dir + .map(f64::from) + .dot(directed_light_dir.map(f64::from)); + let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt(); + let gamma = sin_gamma.asin(); + let view_mat = math::Mat4::from_col_array(view_mat.into_col_array()); + let bounds1 = math::fit_psr( + view_mat.map_cols(math::Vec4::from), + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + let n_e = f64::from(-bounds1.max.z); + let factor = compute_warping_parameter_perspective( + gamma, + n_e, + f64::from(fov), + f64::from(aspect_ratio), + ); + + v_p.z = 0.0; + v_p.normalize(); + let l_r: math::Mat4 = if factor > EPSILON_UPSILON { + math::Mat4::look_at_rh(math::Vec3::zero(), math::Vec3::forward_rh(), v_p) + } else { + math::Mat4::identity() + }; + let directed_proj_mat = math::Mat4::new( + 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, + 1.0, + ); + + let light_all_mat = l_r * directed_proj_mat * light_view_mat; + let bounds0 = math::fit_psr( + light_all_mat, + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + // Vague idea: project z_n from the camera view to the light view (where it's + // tilted by γ). + let (z_0, z_1) = { + let p_z = bounds1.max.z; + let p_y = bounds0.min.y; + let p_x = bounds0.center().x; + let view_inv = view_mat.inverted(); + let light_all_inv = light_all_mat.inverted(); + + let view_point = view_inv * math::Vec4::new(0.0, 0.0, p_z, 1.0); + let view_plane = + view_inv * math::Vec4::from_direction(math::Vec3::unit_z()); + + let light_point = light_all_inv * math::Vec4::new(0.0, p_y, 0.0, 1.0); + let light_plane = + light_all_inv * math::Vec4::from_direction(math::Vec3::unit_y()); + + let shadow_point = light_all_inv * math::Vec4::new(p_x, 0.0, 0.0, 1.0); + let shadow_plane = + light_all_inv * math::Vec4::from_direction(math::Vec3::unit_x()); + + let solve_p0 = math::Mat4::new( + view_plane.x, + view_plane.y, + view_plane.z, + -view_plane.dot(view_point), + light_plane.x, + light_plane.y, + light_plane.z, + -light_plane.dot(light_point), + shadow_plane.x, + shadow_plane.y, + shadow_plane.z, + -shadow_plane.dot(shadow_point), + 0.0, + 0.0, + 0.0, + 1.0, + ); + + let p0_world = solve_p0.inverted() * math::Vec4::unit_w(); + let p0 = light_all_mat * p0_world; + let mut p1 = p0; + p1.y = bounds0.max.y; + + let view_from_light_mat = view_mat * light_all_inv; + let z0 = view_from_light_mat * p0; + let z1 = view_from_light_mat * p1; + + (f64::from(z0.z), f64::from(z1.z)) + }; + + let mut light_focus_pos: math::Vec3 = math::Vec3::zero(); + light_focus_pos.x = bounds0.center().x; + light_focus_pos.y = bounds0.min.y; + light_focus_pos.z = bounds0.center().z; + + let d = f64::from(bounds0.max.y - bounds0.min.y).abs(); + + let w_l_y = d; + + // NOTE: See section 5.1.2.2 of Lloyd's thesis. + let alpha = z_1 / z_0; + let alpha_sqrt = alpha.sqrt(); + let directed_near_normal = if factor < 0.0 { + // Standard shadow map to LiSPSM + (1.0 + alpha_sqrt - factor * (alpha - 1.0)) + / ((alpha - 1.0) * (factor + 1.0)) + } else { + // LiSPSM to PSM + ((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip() + }; + + // Equation 5.14 - 5.16 + let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs(); + let directed_near = y_(0.0) as f32; + let directed_far = y_(1.0) as f32; + light_focus_pos.y = if factor > EPSILON_UPSILON { + light_focus_pos.y - directed_near + } else { + light_focus_pos.y + }; + let w_v: math::Mat4 = math::Mat4::translation_3d(-math::Vec3::new( + light_focus_pos.x, + light_focus_pos.y, + light_focus_pos.z, + )); + let shadow_view_mat: math::Mat4 = w_v * light_all_mat; + let w_p: math::Mat4 = { + if factor > EPSILON_UPSILON { + // Projection for y + let near = directed_near; + let far = directed_far; + let left = -1.0; + let right = 1.0; + let bottom = -1.0; + let top = 1.0; + let s_x = 2.0 * near / (right - left); + let o_x = (right + left) / (right - left); + let s_z = 2.0 * near / (top - bottom); + let o_z = (top + bottom) / (top - bottom); + + let s_y = (far + near) / (far - near); + let o_y = -2.0 * far * near / (far - near); + + math::Mat4::new( + s_x, o_x, 0.0, 0.0, 0.0, s_y, 0.0, o_y, 0.0, o_z, s_z, 0.0, 0.0, + 1.0, 0.0, 0.0, + ) + } else { + math::Mat4::identity() + } + }; + + let shadow_all_mat: math::Mat4 = w_p * shadow_view_mat; + let math::Aabb:: { + min: + math::Vec3 { + x: xmin, + y: ymin, + z: zmin, + }, + max: + math::Vec3 { + x: xmax, + y: ymax, + z: zmax, + }, + } = math::fit_psr( + shadow_all_mat, + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + let s_x = 2.0 / (xmax - xmin); + let s_y = 2.0 / (ymax - ymin); + let s_z = 2.0 / (zmax - zmin); + let o_x = -(xmax + xmin) / (xmax - xmin); + let o_y = -(ymax + ymin) / (ymax - ymin); + let o_z = -(zmax + zmin) / (zmax - zmin); + let directed_proj_mat = Mat4::new( + s_x, 0.0, 0.0, o_x, 0.0, s_y, 0.0, o_y, 0.0, 0.0, s_z, o_z, 0.0, 0.0, 0.0, + 1.0, + ); + + let shadow_all_mat: Mat4 = + Mat4::from_col_arrays(shadow_all_mat.into_col_arrays()); + + let directed_texture_proj_mat = texture_mat * directed_proj_mat; + ShadowLocals::new( + directed_proj_mat * shadow_all_mat, + directed_texture_proj_mat * shadow_all_mat, + ) + }, + )); + // Now, we tackle point lights. + // First, create a perspective projection matrix at 90 degrees (to cover a whole + // face of the cube map we're using). + let shadow_proj = Mat4::perspective_rh_no( + 90.0f32.to_radians(), + point_shadow_aspect, + SHADOW_NEAR, + SHADOW_FAR, + ); + // Next, construct the 6 orientations we'll use for the six faces, in terms of + // their (forward, up) vectors. + let orientations = [ + (Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, -1.0, 0.0)), + (Vec3::new(-1.0, 0.0, 0.0), Vec3::new(0.0, -1.0, 0.0)), + (Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 0.0, 1.0)), + (Vec3::new(0.0, -1.0, 0.0), Vec3::new(0.0, 0.0, -1.0)), + (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, -1.0, 0.0)), + (Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, -1.0, 0.0)), + ]; + // NOTE: We could create the shadow map collection at the same time as the + // lights, but then we'd have to sort them both, which wastes time. Plus, we + // want to prepend our directed lights. + shadow_mats.extend(lights.iter().flat_map(|light| { + // Now, construct the full projection matrix by making the light look at each + // cube face. + let eye = Vec3::new(light.pos[0], light.pos[1], light.pos[2]) - focus_off; + orientations.iter().map(move |&(forward, up)| { + // NOTE: We don't currently try to linearize point lights or need a separate + // transform for them. + ShadowLocals::new( + shadow_proj * Mat4::look_at_rh(eye, eye + forward, up), + Mat4::identity(), + ) + }) + })); + + renderer + .update_consts(&mut self.data.shadow_mats, &shadow_mats) + .expect("Failed to update light constants"); + } // Remove unused figures. self.figure_mgr.clean(scene_data.tick); @@ -482,61 +963,79 @@ impl Scene { tick: u64, scene_data: &SceneData, ) { - // Render terrain and figures. - self.terrain.render( - renderer, - &self.globals, - &self.lights, - &self.shadows, - self.camera.get_focus_pos(), - ); - self.figure_mgr.render( - renderer, - state, - player_entity, - tick, - &self.globals, - &self.lights, - &self.shadows, - &self.camera, - scene_data.figure_lod_render_distance, - ); + let sun_dir = scene_data.get_sun_dir(); + let is_daylight = sun_dir.z < 0.0; + let focus_pos = self.camera.get_focus_pos(); + let cam_pos = self.camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); - self.particle_mgr.render( - renderer, - scene_data, - &self.globals, - &self.lights, - &self.shadows, - ); + let global = &self.data; + let light_data = (is_daylight, &*self.light_data); + let camera_data = (&self.camera, scene_data.figure_lod_render_distance); - // Render the skybox. - renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); + // would instead have this as an extension. + if renderer.render_mode().shadow.is_map() && (is_daylight || !light_data.1.is_empty()) { + if is_daylight { + // Set up shadow mapping. + renderer.start_shadows(); + } + + // Render terrain shadows. + self.terrain + .render_shadows(renderer, global, light_data, focus_pos); + + // Render figure shadows. + self.figure_mgr + .render_shadows(renderer, state, tick, global, light_data, camera_data); + + if is_daylight { + // Flush shadows. + renderer.flush_shadows(); + } + } + let lod = self.lod.get_data(); self.figure_mgr.render_player( renderer, state, player_entity, tick, - &self.globals, - &self.lights, - &self.shadows, - &self.camera, - scene_data.figure_lod_render_distance, + global, + lod, + camera_data, ); + // Render terrain and figures. + self.terrain.render(renderer, global, lod, focus_pos); + + self.figure_mgr.render( + renderer, + state, + player_entity, + tick, + global, + lod, + camera_data, + ); + self.lod.render(renderer, global); + + // Render particle effects. + self.particle_mgr.render(renderer, scene_data, global, lod); + + // Render the skybox. + renderer.render_skybox(&self.skybox.model, global, &self.skybox.locals, lod); + self.terrain.render_translucent( renderer, - &self.globals, - &self.lights, - &self.shadows, - self.camera.get_focus_pos(), + global, + lod, + focus_pos, + cam_pos, scene_data.sprite_render_distance, ); renderer.render_post_process( &self.postprocess.model, - &self.globals, + &global.globals, &self.postprocess.locals, ); } diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 7bc5fb6ff3..32f6ce5c49 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -1,9 +1,9 @@ use super::SceneData; use crate::{ - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - pipelines::particle::ParticleMode, Consts, Globals, Instances, Light, Model, - ParticleInstance, ParticlePipeline, Renderer, Shadow, + pipelines::particle::ParticleMode, GlobalModel, Instances, LodData, Model, + ParticleInstance, ParticlePipeline, Renderer, }, }; use common::{ @@ -43,7 +43,6 @@ impl ParticleMgr { } } - #[allow(clippy::same_item_push)] // TODO: Pending review in #587 pub fn handle_outcome(&mut self, outcome: &Outcome, scene_data: &SceneData) { let time = scene_data.state.get_time(); let mut rng = rand::thread_rng(); @@ -54,8 +53,9 @@ impl ParticleMgr { power, reagent, } => { - for _ in 0..150 { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + 150, + Particle::new( Duration::from_millis(if reagent.is_some() { 1000 } else { 250 }), time, match reagent { @@ -67,17 +67,17 @@ impl ParticleMgr { None => ParticleMode::Shrapnel, }, *pos, - )); - } + ), + ); - for _ in 0..200 { - self.particles.push(Particle::new( + self.particles.resize_with(self.particles.len() + 200, || { + Particle::new( Duration::from_secs(4), time, ParticleMode::CampfireSmoke, *pos + Vec2::::zero().map(|_| rng.gen_range(-1.0, 1.0) * power), - )); - } + ) + }); }, Outcome::ProjectileShot { .. } => {}, } @@ -108,14 +108,7 @@ impl ParticleMgr { fn maintain_body_particles(&mut self, scene_data: &SceneData) { let ecs = scene_data.state.ecs(); - for (_i, (_entity, body, pos)) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ) - .join() - .enumerate() - { + for (body, pos) in (&ecs.read_storage::(), &ecs.read_storage::()).join() { match body { Body::Object(object::Body::CampfireLit) => { self.maintain_campfirelit_particles(scene_data, pos) @@ -178,29 +171,30 @@ impl ParticleMgr { } } - #[allow(clippy::same_item_push)] // TODO: Pending review in #587 fn maintain_boltfirebig_particles(&mut self, scene_data: &SceneData, pos: &Pos) { let time = scene_data.state.get_time(); // fire - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(3)) { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(3))), + Particle::new( Duration::from_millis(250), time, ParticleMode::CampfireFire, pos.0, - )); - } + ), + ); // smoke - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(5)) { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))), + Particle::new( Duration::from_secs(2), time, ParticleMode::CampfireSmoke, pos.0, - )); - } + ), + ); } fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos) { @@ -225,29 +219,28 @@ impl ParticleMgr { } } - #[allow(clippy::same_item_push)] // TODO: Pending review in #587 fn maintain_boost_particles(&mut self, scene_data: &SceneData) { let state = scene_data.state; let ecs = state.ecs(); let time = state.get_time(); - for (_i, (_entity, pos, character_state)) in ( - &ecs.entities(), + for (pos, character_state) in ( &ecs.read_storage::(), &ecs.read_storage::(), ) .join() - .enumerate() { if let CharacterState::Boost(_) = character_state { - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(10)) { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + + usize::from(self.scheduler.heartbeats(Duration::from_millis(10))), + Particle::new( Duration::from_secs(15), time, ParticleMode::CampfireSmoke, pos.0, - )); - } + ), + ); } } } @@ -271,9 +264,8 @@ impl ParticleMgr { &self, renderer: &mut Renderer, scene_data: &SceneData, - globals: &Consts, - lights: &Consts, - shadows: &Consts, + global: &GlobalModel, + lod: &LodData, ) { if scene_data.particles_enabled { let model = &self @@ -281,7 +273,7 @@ impl ParticleMgr { .get(DEFAULT_MODEL_KEY) .expect("Expected particle model in cache"); - renderer.render_particles(model, globals, &self.instances, lights, shadows); + renderer.render_particles(model, global, &self.instances, lod); } } @@ -304,19 +296,26 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model(DEFAULT_MODEL_KEY); - let mesh = &Meshable::::generate_mesh( - &Segment::from(vox.as_ref()), - (offset * lod_scale, Vec3::one() / lod_scale), + // NOTE: If we add texturing we may eventually try to share it among all + // particles in a single atlas. + let max_texture_size = renderer.max_texture_size(); + let max_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + let mut greedy = GreedyMesh::new(max_size); + + let mesh = Meshable::::generate_mesh( + Segment::from(vox.as_ref()), + &mut greedy, ) .0; + // NOTE: Ignoring coloring / lighting for now. + drop(greedy); + renderer - .create_model(mesh) + .create_model(&mesh) .expect("Failed to create particle model") }); @@ -390,6 +389,7 @@ impl HeartbeatScheduler { pub fn clear(&mut self) { self.timers.clear() } } +#[derive(Clone, Copy)] struct Particle { alive_until: f64, // created_at + lifespan instance: ParticleInstance, diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 259419fee6..785b8a6784 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -1,20 +1,23 @@ use crate::{ - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Mesh, Model, - PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, + create_pp_mesh, create_skybox_mesh, BoneMeshes, Consts, FigureModel, FigurePipeline, + GlobalModel, Globals, Light, Mesh, Model, PostProcessLocals, PostProcessPipeline, Renderer, + Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline, TerrainPipeline, }, scene::{ camera::{self, Camera, CameraMode}, - figure::{load_mesh, FigureModelCache, FigureState}, + figure::{load_mesh, FigureColLights, FigureModelCache, FigureModelEntry, FigureState}, + LodData, }, window::{Event, PressState}, }; use anim::{ character::{CharacterSkeleton, IdleAnimation, SkeletonAttr}, fixture::FixtureSkeleton, - Animation, Skeleton, + Animation, }; +use client::Client; use common::{ comp::{humanoid, item::ItemKind, Body, Loadout}, figure::Segment, @@ -42,8 +45,18 @@ impl ReadVol for VoidVol { fn get<'a>(&'a self, _pos: Vec3) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) } } -fn generate_mesh(segment: &Segment, offset: Vec3) -> Mesh { - Meshable::::generate_mesh(segment, (offset, Vec3::one())).0 +fn generate_mesh<'a>( + greedy: &mut GreedyMesh<'a>, + mesh: &mut Mesh, + segment: Segment, + offset: Vec3, +) -> BoneMeshes { + let (opaque, _, /* shadow */ _, bounds) = + Meshable::::generate_mesh( + segment, + (greedy, mesh, offset, Vec3::one()), + ); + (opaque /* , shadow */, bounds) } struct Skybox { @@ -57,15 +70,16 @@ struct PostProcess { } pub struct Scene { - globals: Consts, - lights: Consts, - shadows: Consts, + data: GlobalModel, camera: Camera, skybox: Skybox, postprocess: PostProcess, - backdrop: Option<(Model, FigureState)>, + lod: LodData, + map_bounds: Vec2, + col_lights: FigureColLights, + backdrop: Option<(FigureModelEntry<1>, FigureState)>, figure_model_cache: FigureModelCache, figure_state: FigureState, @@ -84,19 +98,32 @@ pub struct SceneData { } impl Scene { - pub fn new(renderer: &mut Renderer, backdrop: Option<&str>) -> Self { + pub fn new(renderer: &mut Renderer, backdrop: Option<&str>, client: &Client) -> Self { + let start_angle = 90.0f32.to_radians(); let resolution = renderer.get_resolution().map(|e| e as f32); + let map_bounds = client.world_map.2; + let map_border = [0.0, 0.0, 0.0, 0.0]; + let map_image = [0]; + let alt_image = [0]; + let horizon_image = [0x_00_01_00_01]; + let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson); camera.set_focus_pos(Vec3::unit_z() * 1.5); camera.set_distance(3.4); - camera.set_orientation(Vec3::new(0.0, 0.0, 0.0)); + camera.set_orientation(Vec3::new(start_angle, 0.0, 0.0)); + + let mut col_lights = FigureColLights::new(renderer); Self { - globals: renderer.create_consts(&[Globals::default()]).unwrap(), - lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), - shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), - camera, + data: GlobalModel { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), + shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), + shadow_mats: renderer + .create_consts(&[ShadowLocals::default(); 6]) + .unwrap(), + }, skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), @@ -108,28 +135,65 @@ impl Scene { .create_consts(&[PostProcessLocals::default()]) .unwrap(), }, + lod: LodData::new( + renderer, + Vec2::new(1, 1), + &map_image, + &alt_image, + &horizon_image, + 1, + map_border.into(), + ), + map_bounds, + figure_model_cache: FigureModelCache::new(), - figure_state: FigureState::new(renderer, CharacterSkeleton::new()), + figure_state: FigureState::new(renderer, CharacterSkeleton::default()), backdrop: backdrop.map(|specifier| { - ( - renderer - .create_model(&load_mesh( - specifier, - Vec3::new(-55.0, -49.5, -2.0), - generate_mesh, - )) - .unwrap(), - FigureState::new(renderer, FixtureSkeleton::new()), - ) + let mut state = FigureState::new(renderer, FixtureSkeleton::default()); + let mut greedy = FigureModel::make_greedy(); + let mut opaque_mesh = Mesh::new(); + let (_opaque_mesh, (bounds, range)) = load_mesh( + specifier, + Vec3::new(-55.0, -49.5, -2.0), + |segment, offset| generate_mesh(&mut greedy, &mut opaque_mesh, segment, offset), + ); + // NOTE: Since MagicaVoxel sizes are limited to 256 × 256 × 256, and there are + // at most 3 meshed vertices per unique vertex, we know the + // total size is bounded by 2^24 * 3 * 1.5 which is bounded by + // 2^27, which fits in a u32. + let range = range.start as u32..range.end as u32; + let model = col_lights + .create_figure(renderer, greedy, (opaque_mesh, bounds), [range]) + .unwrap(); + let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; + state.update( + renderer, + anim::vek::Vec3::zero(), + anim::vek::Vec3::new(start_angle.sin(), -start_angle.cos(), 0.0), + 1.0, + Rgba::broadcast(1.0), + 15.0, // Want to get there immediately. + 1.0, + &model, + 0, + true, + false, + &camera, + &mut buf, + ); + (model, state) }), + col_lights, + + camera, turning: false, - char_ori: 0.0, + char_ori: -start_angle, } } - pub fn globals(&self) -> &Consts { &self.globals } + pub fn globals(&self) -> &Consts { &self.data.globals } pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera } @@ -163,8 +227,11 @@ impl Scene { scene_data: SceneData, loadout: Option<&Loadout>, ) { - self.camera - .update(scene_data.time, 1.0 / 60.0, scene_data.mouse_smoothing); + self.camera.update( + scene_data.time, + /* 1.0 / 60.0 */ scene_data.delta_time, + scene_data.mouse_smoothing, + ); self.camera.compute_dependents(&VoidVol); let camera::Dependents { @@ -174,16 +241,23 @@ impl Scene { .. } = self.camera.dependents(); const VD: f32 = 115.0; // View Distance - const TIME: f64 = 43200.0; // 12 hours*3600 seconds - if let Err(e) = renderer.update_consts(&mut self.globals, &[Globals::new( + const TIME: f64 = 10.0 * 60.0 * 60.0; + const SHADOW_NEAR: f32 = 1.0; + const SHADOW_FAR: f32 = 25.0; + + if let Err(e) = renderer.update_consts(&mut self.data.globals, &[Globals::new( view_mat, proj_mat, cam_pos, self.camera.get_focus_pos(), VD, + self.lod.tgt_detail as f32, + self.map_bounds, TIME, scene_data.time, renderer.get_resolution(), + Vec2::new(SHADOW_NEAR, SHADOW_FAR), + 0, 0, 0, BlockKind::Air, @@ -195,7 +269,8 @@ impl Scene { error!(?e, "Renderer failed to update"); } - self.figure_model_cache.clean(scene_data.tick); + self.figure_model_cache + .clean(&mut self.col_lights, scene_data.tick); let active_item_kind = loadout .and_then(|l| l.active_item.as_ref()) @@ -225,23 +300,39 @@ impl Scene { &mut 0.0, &SkeletonAttr::from(&body), ); - self.figure_state - .skeleton_mut() - .interpolate(&tgt_skeleton, scene_data.delta_time); - } + let dt_lerp = (scene_data.delta_time * 15.0).min(1.0); + *self.figure_state.skeleton_mut() = + anim::vek::Lerp::lerp(&*self.figure_state.skeleton_mut(), &tgt_skeleton, dt_lerp); - self.figure_state.update( - renderer, - Vec3::zero(), - Vec3::new(self.char_ori.sin(), -self.char_ori.cos(), 0.0), - 1.0, - Rgba::broadcast(1.0), - 1.0 / 60.0, // TODO: Use actual deltatime here? - 1.0, - 0, - true, - false, - ); + let model = &self + .figure_model_cache + .get_or_create_model( + renderer, + &mut self.col_lights, + Body::Humanoid(body), + loadout, + scene_data.tick, + CameraMode::default(), + None, + ) + .0; + let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; + self.figure_state.update( + renderer, + anim::vek::Vec3::zero(), + anim::vek::Vec3::new(self.char_ori.sin(), -self.char_ori.cos(), 0.0), + 1.0, + Rgba::broadcast(1.0), + scene_data.delta_time, + 1.0, + &model, + 0, + true, + false, + &self.camera, + &mut buf, + ); + } } pub fn render( @@ -251,13 +342,19 @@ impl Scene { body: Option, loadout: Option<&Loadout>, ) { - renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); + renderer.render_skybox( + &self.skybox.model, + &self.data, + &self.skybox.locals, + &self.lod, + ); if let Some(body) = body { let model = &self .figure_model_cache .get_or_create_model( renderer, + &mut self.col_lights, Body::Humanoid(body), loadout, tick, @@ -267,29 +364,29 @@ impl Scene { .0; renderer.render_figure( - &model[0], - &self.globals, + &model.models[0], + &self.col_lights.texture(model), + &self.data, self.figure_state.locals(), self.figure_state.bone_consts(), - &self.lights, - &self.shadows, + &self.lod, ); } if let Some((model, state)) = &self.backdrop { renderer.render_figure( - model, - &self.globals, + &model.models[0], + &self.col_lights.texture(model), + &self.data, state.locals(), state.bone_consts(), - &self.lights, - &self.shadows, + &self.lod, ); } renderer.render_post_process( &self.postprocess.model, - &self.globals, + &self.data.globals, &self.postprocess.locals, ); } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index f3d95593ad..06ec276b93 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1,12 +1,13 @@ use crate::{ - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, Shadow, - SpriteInstance, SpritePipeline, TerrainLocals, TerrainPipeline, Texture, + ColLightFmt, ColLightInfo, Consts, FluidPipeline, GlobalModel, Instances, Mesh, Model, + RenderError, Renderer, ShadowPipeline, SpriteInstance, SpriteLocals, SpritePipeline, + TerrainLocals, TerrainPipeline, Texture, }, }; -use super::SceneData; +use super::{math, LodData, SceneData}; use common::{ assets, figure::Segment, @@ -15,23 +16,37 @@ use common::{ vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox}, volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError}, }; +use core::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration}; use crossbeam::channel; use dot_vox::DotVoxData; +use guillotiere::AtlasAllocator; use hashbrown::HashMap; -use std::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration}; +use std::sync::Arc; use tracing::warn; use treeculler::{BVol, Frustum, AABB}; use vek::*; +const SPRITE_SCALE: Vec3 = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0); + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum Visibility { + OutOfRange = 0, + InRange = 1, + Visible = 2, +} + struct TerrainChunkData { // GPU data load_time: f32, opaque_model: Model, fluid_model: Option>, + col_lights: guillotiere::AllocId, sprite_instances: HashMap<(BlockKind, usize), Instances>, locals: Consts, - visible: bool, + visible: Visibility, + can_shadow_point: bool, + can_shadow_sun: bool, z_bounds: (f32, f32), frustum_last_plane_index: u8, } @@ -49,6 +64,7 @@ struct MeshWorkerResponse { z_bounds: (f32, f32), opaque_mesh: Mesh, fluid_mesh: Mesh, + col_lights_info: ColLightInfo, sprite_instances: HashMap<(BlockKind, usize), Vec>, started_tick: u64, } @@ -143,7 +159,7 @@ fn sprite_config_for(kind: BlockKind) -> Option { wind_sway: 0.1, }), BlockKind::LargeGrass => Some(SpriteConfig { - variations: 1, + variations: 3, wind_sway: 0.5, }), @@ -375,14 +391,18 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( z_bounds: (f32, f32), started_tick: u64, volume: as SampleVol>>::Sample, + max_texture_size: u16, range: Aabb, + sprite_data: &HashMap<(BlockKind, usize), Vec>, ) -> MeshWorkerResponse { - let (opaque_mesh, fluid_mesh) = volume.generate_mesh(range); + let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info)) = + volume.generate_mesh((range, Vec2::new(max_texture_size, max_texture_size))); MeshWorkerResponse { pos, - z_bounds, + z_bounds: (bounds.min.z, bounds.max.z), opaque_mesh, fluid_mesh, + col_lights_info, // Extract sprite locations from volume sprite_instances: { let mut instances = HashMap::new(); @@ -390,8 +410,8 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( for x in 0..V::RECT_SIZE.x as i32 { for y in 0..V::RECT_SIZE.y as i32 { for z in z_bounds.0 as i32..z_bounds.1 as i32 + 1 { - let wpos = Vec3::from(pos * V::RECT_SIZE.map(|e: u32| e as i32)) - + Vec3::new(x, y, z); + let rel_pos = Vec3::new(x, y, z); + let wpos = Vec3::from(pos * V::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos; let block = volume.get(wpos).ok().copied().unwrap_or(Block::empty()); @@ -399,22 +419,26 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( let seed = wpos.x as u64 * 3 + wpos.y as u64 * 7 + wpos.x as u64 * wpos.y as u64; // Awful PRNG - let ori = block.get_ori().unwrap_or((seed % 4) as u8 * 2); - + let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111; + let variation = seed as usize % cfg.variations; + let key = (block.kind(), variation); + // NOTE: Safe bbecause we called sprite_config_for already. + // NOTE: Safe because 0 ≤ ori < 8 + let sprite_data = &sprite_data[&key][0]; let instance = SpriteInstance::new( Mat4::identity() + .translated_3d(sprite_data.offset) .rotated_z(f32::consts::PI * 0.25 * ori as f32) .translated_3d( - wpos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0), + (rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)) + / SPRITE_SCALE, ), - Rgb::broadcast(1.0), cfg.wind_sway, + rel_pos, + ori, ); - instances - .entry((block.kind(), seed as usize % cfg.variations)) - .or_insert_with(Vec::new) - .push(instance); + instances.entry(key).or_insert(Vec::new()).push(instance); } } } @@ -426,9 +450,32 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( } } -pub struct Terrain { - chunks: HashMap, TerrainChunkData>, +struct SpriteData { + /* mat: Mat4, */ + locals: Consts, + model: Model, + /* scale: Vec3, */ + offset: Vec3, +} +pub struct Terrain { + atlas: AtlasAllocator, + chunks: HashMap, TerrainChunkData>, + /// Temporary storage for dead chunks that might still be shadowing chunks + /// in view. We wait until either the chunk definitely cannot be + /// shadowing anything the player can see, the chunk comes back into + /// view, or for daylight to end, before removing it (whichever comes + /// first). + /// + /// Note that these chunks are not complete; for example, they are missing + /// texture data. + shadow_chunks: Vec<(Vec2, TerrainChunkData)>, + /* /// Secondary index into the terrain chunk table, used to sort through chunks by z index from + /// the top down. + z_index_down: BTreeSet>, + /// Secondary index into the terrain chunk table, used to sort through chunks by z index from + /// the bottom up. + z_index_up: BTreeSet>, */ // The mpsc sender and receiver used for talking to meshing worker threads. // We keep the sender component for no reason other than to clone it and send it to new // workers. @@ -437,12 +484,20 @@ pub struct Terrain { mesh_todo: HashMap, ChunkMeshState>, // GPU data - sprite_models: HashMap<(BlockKind, usize), Vec>>, + sprite_data: Arc>>, + sprite_col_lights: Texture, + col_lights: Texture, waves: Texture, phantom: PhantomData, } +impl TerrainChunkData { + pub fn can_shadow_sun(&self) -> bool { + self.visible == Visibility::Visible || self.can_shadow_sun + } +} + impl Terrain { #[allow(clippy::float_cmp)] // TODO: Pending review in #587 pub fn new(renderer: &mut Renderer) -> Self { @@ -450,2414 +505,1962 @@ impl Terrain { // with worker threads that are meshing chunks. let (send, recv) = channel::unbounded(); - let mut make_models = |s, offset, lod_axes: Vec3| { + let (atlas, col_lights) = + Self::make_atlas(renderer).expect("Failed to create atlas texture"); + + let max_texture_size = renderer.max_texture_size(); + let max_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + let mut greedy = GreedyMesh::new(max_size); + let mut locals_buffer = [SpriteLocals::default(); 8]; + // NOTE: Tracks the start vertex of the next model to be meshed. + let mut make_models = |(kind, variation), s, offset, lod_axes: Vec3| { let scaled = [1.0, 0.8, 0.6, 0.4, 0.2]; - scaled - .iter() - .map(|lod_scale| { - if *lod_scale == 1.0 { - Vec3::broadcast(1.0) - } else { - lod_axes * *lod_scale + lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 }) - } - }) - .map(|lod_scale| { - renderer - .create_model( - &Meshable::::generate_mesh( - &Segment::from(assets::load_expect::(s).as_ref()) - .scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0, - ) - .unwrap() - }) - .collect::>() + let model = assets::load_expect::(s); + let zero = Vec3::zero(); + let model_size = model + .models + .first() + .map( + |&dot_vox::Model { + size: dot_vox::Size { x, y, z }, + .. + }| Vec3::new(x, y, z), + ) + .unwrap_or(zero); + let max_model_size = Vec3::new(15.0, 15.0, 63.0); + let model_scale = max_model_size.map2(model_size, |max_sz: f32, cur_sz| { + let scale = max_sz / max_sz.max(cur_sz as f32); + if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz { + scale - 0.001 + } else { + scale + } + }); + let wind_sway = sprite_config_for(kind).map(|c| c.wind_sway).unwrap_or(0.0); + let sprite_mat: Mat4 = Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE); + ( + (kind, variation), + scaled + .iter() + .map(|&lod_scale_orig| { + let lod_scale = model_scale + * if lod_scale_orig == 1.0 { + Vec3::broadcast(1.0) + } else { + lod_axes * lod_scale_orig + + lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 }) + }; + // Mesh generation exclusively acts using side effects; it has no + // interesting return value, but updates the mesh. + let mut opaque_mesh = Mesh::new(); + Meshable::::generate_mesh( + Segment::from(model.as_ref()).scaled_by(lod_scale), + ( + &mut greedy, + &mut opaque_mesh, + wind_sway >= 0.4 && lod_scale_orig == 1.0, + ), + ); + let model = renderer + .create_model(&opaque_mesh) + .expect("Failed to upload sprite model data to the GPU!"); + + let sprite_scale = Vec3::one() / lod_scale; + let sprite_mat: Mat4 = sprite_mat * Mat4::scaling_3d(sprite_scale); + locals_buffer + .iter_mut() + .enumerate() + .for_each(|(ori, locals)| { + let sprite_mat = + sprite_mat.rotated_z(f32::consts::PI * 0.25 * ori as f32); + *locals = + SpriteLocals::new(sprite_mat, sprite_scale, offset, wind_sway); + }); + + SpriteData { + /* vertex_range */ model, + offset, + locals: renderer + .create_consts(&locals_buffer) + .expect("Failed to upload sprite locals to the GPU!"), + } + }) + .collect::>(), + ) }; + let sprite_data: HashMap<(BlockKind, usize), _> = vec![ + // Windows + make_models( + (BlockKind::Window1, 0), + "voxygen.voxel.sprite.window.window-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Window2, 0), + "voxygen.voxel.sprite.window.window-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Window3, 0), + "voxygen.voxel.sprite.window.window-2", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Window4, 0), + "voxygen.voxel.sprite.window.window-3", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Cacti + make_models( + (BlockKind::LargeCactus, 0), + "voxygen.voxel.sprite.cacti.large_cactus", + Vec3::new(-13.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LargeCactus, 1), + "voxygen.voxel.sprite.cacti.tall", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BarrelCactus, 0), + "voxygen.voxel.sprite.cacti.barrel_cactus", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RoundCactus, 0), + "voxygen.voxel.sprite.cacti.cactus_round", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortCactus, 0), + "voxygen.voxel.sprite.cacti.cactus_short", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MedFlatCactus, 0), + "voxygen.voxel.sprite.cacti.flat_cactus_med", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortFlatCactus, 0), + "voxygen.voxel.sprite.cacti.flat_cactus_short", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Fruit + make_models( + (BlockKind::Apple, 0), + "voxygen.voxel.sprite.fruit.apple", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Flowers + make_models( + (BlockKind::BlueFlower, 0), + "voxygen.voxel.sprite.flowers.flower_blue_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 1), + "voxygen.voxel.sprite.flowers.flower_blue_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 2), + "voxygen.voxel.sprite.flowers.flower_blue_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 3), + "voxygen.voxel.sprite.flowers.flower_blue_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 4), + "voxygen.voxel.sprite.flowers.flower_blue_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 5), + "voxygen.voxel.sprite.flowers.flower_blue_6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 6), + "voxygen.voxel.sprite.flowers.flower_blue_7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 7), + "voxygen.voxel.sprite.flowers.flower_blue-8", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 8), + "voxygen.voxel.sprite.flowers.flower_blue-9", + Vec3::new(-4.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 9), + "voxygen.voxel.sprite.flowers.flower_blue-10", + Vec3::new(-1.5, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 0), + "voxygen.voxel.sprite.flowers.flower_pink_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 1), + "voxygen.voxel.sprite.flowers.flower_pink_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 2), + "voxygen.voxel.sprite.flowers.flower_pink_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 3), + "voxygen.voxel.sprite.flowers.flower_pink_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 0), + "voxygen.voxel.sprite.flowers.flower_purple_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 1), + "voxygen.voxel.sprite.flowers.flower_purple-2", + Vec3::new(-5.0, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 2), + "voxygen.voxel.sprite.flowers.flower_purple-3", + Vec3::new(-3.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 3), + "voxygen.voxel.sprite.flowers.flower_purple-4", + Vec3::new(-5.0, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 4), + "voxygen.voxel.sprite.flowers.flower_purple-5", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 5), + "voxygen.voxel.sprite.flowers.flower_purple-6", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 6), + "voxygen.voxel.sprite.flowers.flower_purple-7", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 7), + "voxygen.voxel.sprite.flowers.flower_purple-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 0), + "voxygen.voxel.sprite.flowers.flower_red_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 1), + "voxygen.voxel.sprite.flowers.flower_red_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 2), + "voxygen.voxel.sprite.flowers.flower_red_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 3), + "voxygen.voxel.sprite.flowers.flower_red-4", + Vec3::new(-6.5, -6.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 4), + "voxygen.voxel.sprite.flowers.flower_red-5", + Vec3::new(-3.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 0), + "voxygen.voxel.sprite.flowers.flower_white_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 1), + "voxygen.voxel.sprite.flowers.flower_white_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 2), + "voxygen.voxel.sprite.flowers.flower_white-3", + Vec3::new(-1.5, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 3), + "voxygen.voxel.sprite.flowers.flower_white-4", + Vec3::new(-5.0, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 4), + "voxygen.voxel.sprite.flowers.flower_white-5", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::YellowFlower, 0), + "voxygen.voxel.sprite.flowers.flower_yellow-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::YellowFlower, 1), + "voxygen.voxel.sprite.flowers.flower_yellow-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Sunflower, 0), + "voxygen.voxel.sprite.flowers.sunflower_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Sunflower, 1), + "voxygen.voxel.sprite.flowers.sunflower_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Grass + make_models( + (BlockKind::LargeGrass, 0), + "voxygen.voxel.sprite.grass.grass_large-0", + Vec3::new(-2.0, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LargeGrass, 1), + "voxygen.voxel.sprite.grass.grass_large-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LargeGrass, 2), + "voxygen.voxel.sprite.grass.grass_large-2", + Vec3::new(-5.5, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 0), + "voxygen.voxel.sprite.grass.grass_long_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 1), + "voxygen.voxel.sprite.grass.grass_long_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 2), + "voxygen.voxel.sprite.grass.grass_long_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 3), + "voxygen.voxel.sprite.grass.grass_long_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 4), + "voxygen.voxel.sprite.grass.grass_long_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 5), + "voxygen.voxel.sprite.grass.grass_long_6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 6), + "voxygen.voxel.sprite.grass.grass_long_7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 0), + "voxygen.voxel.sprite.grass.grass_med_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 1), + "voxygen.voxel.sprite.grass.grass_med_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 2), + "voxygen.voxel.sprite.grass.grass_med_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 3), + "voxygen.voxel.sprite.grass.grass_med_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 4), + "voxygen.voxel.sprite.grass.grass_med_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 0), + "voxygen.voxel.sprite.grass.grass_short_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 1), + "voxygen.voxel.sprite.grass.grass_short_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 2), + "voxygen.voxel.sprite.grass.grass_short_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 3), + "voxygen.voxel.sprite.grass.grass_short_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 4), + "voxygen.voxel.sprite.grass.grass_short_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 0), + "voxygen.voxel.sprite.mushrooms.mushroom-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 1), + "voxygen.voxel.sprite.mushrooms.mushroom-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 2), + "voxygen.voxel.sprite.mushrooms.mushroom-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 3), + "voxygen.voxel.sprite.mushrooms.mushroom-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 4), + "voxygen.voxel.sprite.mushrooms.mushroom-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 5), + "voxygen.voxel.sprite.mushrooms.mushroom-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 6), + "voxygen.voxel.sprite.mushrooms.mushroom-6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 7), + "voxygen.voxel.sprite.mushrooms.mushroom-7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 8), + "voxygen.voxel.sprite.mushrooms.mushroom-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 9), + "voxygen.voxel.sprite.mushrooms.mushroom-9", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 10), + "voxygen.voxel.sprite.mushrooms.mushroom-10", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 11), + "voxygen.voxel.sprite.mushrooms.mushroom-11", + Vec3::new(-8.0, -8.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 12), + "voxygen.voxel.sprite.mushrooms.mushroom-12", + Vec3::new(-5.0, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 13), + "voxygen.voxel.sprite.mushrooms.mushroom-13", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 14), + "voxygen.voxel.sprite.mushrooms.mushroom-14", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 15), + "voxygen.voxel.sprite.mushrooms.mushroom-15", + Vec3::new(-1.5, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 16), + "voxygen.voxel.sprite.mushrooms.mushroom-16", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Liana, 0), + "voxygen.voxel.sprite.lianas.liana-0", + Vec3::new(-1.5, -0.5, -88.0), + Vec3::unit_z() * 0.5, + ), + make_models( + (BlockKind::Liana, 1), + "voxygen.voxel.sprite.lianas.liana-1", + Vec3::new(-1.0, -0.5, -55.0), + Vec3::unit_z() * 0.5, + ), + make_models( + (BlockKind::Velorite, 0), + "voxygen.voxel.sprite.velorite.velorite_ore", + Vec3::new(-5.0, -5.0, -5.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 0), + "voxygen.voxel.sprite.velorite.velorite_1", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 1), + "voxygen.voxel.sprite.velorite.velorite_2", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 2), + "voxygen.voxel.sprite.velorite.velorite_3", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 3), + "voxygen.voxel.sprite.velorite.velorite_4", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 4), + "voxygen.voxel.sprite.velorite.velorite_5", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 5), + "voxygen.voxel.sprite.velorite.velorite_6", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 6), + "voxygen.voxel.sprite.velorite.velorite_7", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 7), + "voxygen.voxel.sprite.velorite.velorite_8", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 8), + "voxygen.voxel.sprite.velorite.velorite_9", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 9), + "voxygen.voxel.sprite.velorite.velorite_10", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 0), + "voxygen.voxel.sprite.chests.chest", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 1), + "voxygen.voxel.sprite.chests.chest_gold", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 2), + "voxygen.voxel.sprite.chests.chest_dark", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 3), + "voxygen.voxel.sprite.chests.chest_vines", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + //Welwitch + make_models( + (BlockKind::Welwitch, 0), + "voxygen.voxel.sprite.welwitch.1", + Vec3::new(-15.0, -17.0, -0.0), + Vec3::unit_z() * 0.7, + ), + //Pumpkins + make_models( + (BlockKind::Pumpkin, 0), + "voxygen.voxel.sprite.pumpkin.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 1), + "voxygen.voxel.sprite.pumpkin.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 2), + "voxygen.voxel.sprite.pumpkin.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 3), + "voxygen.voxel.sprite.pumpkin.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 4), + "voxygen.voxel.sprite.pumpkin.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 5), + "voxygen.voxel.sprite.pumpkin.6", + Vec3::new(-7.0, -6.5, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 6), + "voxygen.voxel.sprite.pumpkin.7", + Vec3::new(-7.0, -9.5, -0.0), + Vec3::one(), + ), + //Lingonberries + make_models( + (BlockKind::LingonBerry, 0), + "voxygen.voxel.sprite.lingonberry.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LingonBerry, 1), + "voxygen.voxel.sprite.lingonberry.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LingonBerry, 2), + "voxygen.voxel.sprite.lingonberry.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Leafy Plants + make_models( + (BlockKind::LeafyPlant, 0), + "voxygen.voxel.sprite.leafy_plant.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 1), + "voxygen.voxel.sprite.leafy_plant.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 2), + "voxygen.voxel.sprite.leafy_plant.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 3), + "voxygen.voxel.sprite.leafy_plant.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 4), + "voxygen.voxel.sprite.leafy_plant.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 5), + "voxygen.voxel.sprite.leafy_plant.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 6), + "voxygen.voxel.sprite.leafy_plant.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 7), + "voxygen.voxel.sprite.leafy_plant.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 8), + "voxygen.voxel.sprite.leafy_plant.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 9), + "voxygen.voxel.sprite.leafy_plant.10", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Ferns + make_models( + (BlockKind::Fern, 0), + "voxygen.voxel.sprite.ferns.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 1), + "voxygen.voxel.sprite.ferns.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 2), + "voxygen.voxel.sprite.ferns.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 3), + "voxygen.voxel.sprite.ferns.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 4), + "voxygen.voxel.sprite.ferns.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 5), + "voxygen.voxel.sprite.ferns.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 6), + "voxygen.voxel.sprite.ferns.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 7), + "voxygen.voxel.sprite.ferns.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 8), + "voxygen.voxel.sprite.ferns.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 9), + "voxygen.voxel.sprite.ferns.10", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 10), + "voxygen.voxel.sprite.ferns.11", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 11), + "voxygen.voxel.sprite.ferns.12", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 12), + "voxygen.voxel.sprite.ferns.fern-0", + Vec3::new(-6.5, -11.5, 0.0), + Vec3::unit_z(), + ), + // Dead Bush + make_models( + (BlockKind::DeadBush, 0), + "voxygen.voxel.sprite.dead_bush.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DeadBush, 1), + "voxygen.voxel.sprite.dead_bush.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DeadBush, 2), + "voxygen.voxel.sprite.dead_bush.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DeadBush, 3), + "voxygen.voxel.sprite.dead_bush.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Blueberries + make_models( + (BlockKind::Blueberry, 0), + "voxygen.voxel.sprite.blueberry.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 1), + "voxygen.voxel.sprite.blueberry.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 2), + "voxygen.voxel.sprite.blueberry.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 3), + "voxygen.voxel.sprite.blueberry.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 4), + "voxygen.voxel.sprite.blueberry.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 5), + "voxygen.voxel.sprite.blueberry.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 6), + "voxygen.voxel.sprite.blueberry.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 7), + "voxygen.voxel.sprite.blueberry.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 8), + "voxygen.voxel.sprite.blueberry.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Ember + make_models( + (BlockKind::Ember, 0), + "voxygen.voxel.sprite.ember.1", + Vec3::new(-7.0, -7.0, -2.9), + Vec3::new(1.0, 1.0, 0.0), + ), + // Corn + make_models( + (BlockKind::Corn, 0), + "voxygen.voxel.sprite.corn.corn-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 1), + "voxygen.voxel.sprite.corn.corn-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 2), + "voxygen.voxel.sprite.corn.corn-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 3), + "voxygen.voxel.sprite.corn.corn-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 4), + "voxygen.voxel.sprite.corn.corn-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 5), + "voxygen.voxel.sprite.corn.corn-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Yellow Wheat + make_models( + (BlockKind::WheatYellow, 0), + "voxygen.voxel.sprite.wheat_yellow.wheat-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 1), + "voxygen.voxel.sprite.wheat_yellow.wheat-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 2), + "voxygen.voxel.sprite.wheat_yellow.wheat-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 3), + "voxygen.voxel.sprite.wheat_yellow.wheat-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 4), + "voxygen.voxel.sprite.wheat_yellow.wheat-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 5), + "voxygen.voxel.sprite.wheat_yellow.wheat-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 6), + "voxygen.voxel.sprite.wheat_yellow.wheat-6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 7), + "voxygen.voxel.sprite.wheat_yellow.wheat-7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 8), + "voxygen.voxel.sprite.wheat_yellow.wheat-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 9), + "voxygen.voxel.sprite.wheat_yellow.wheat-9", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Green Wheat + make_models( + (BlockKind::WheatGreen, 0), + "voxygen.voxel.sprite.wheat_green.wheat-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 1), + "voxygen.voxel.sprite.wheat_green.wheat-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 2), + "voxygen.voxel.sprite.wheat_green.wheat-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 3), + "voxygen.voxel.sprite.wheat_green.wheat-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 4), + "voxygen.voxel.sprite.wheat_green.wheat-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 5), + "voxygen.voxel.sprite.wheat_green.wheat-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 6), + "voxygen.voxel.sprite.wheat_green.wheat-6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 7), + "voxygen.voxel.sprite.wheat_green.wheat-7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 8), + "voxygen.voxel.sprite.wheat_green.wheat-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 9), + "voxygen.voxel.sprite.wheat_green.wheat-9", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Cabbage + make_models( + (BlockKind::Cabbage, 0), + "voxygen.voxel.sprite.cabbage.cabbage-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Cabbage, 1), + "voxygen.voxel.sprite.cabbage.cabbage-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Cabbage, 2), + "voxygen.voxel.sprite.cabbage.cabbage-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Flax + make_models( + (BlockKind::Flax, 0), + "voxygen.voxel.sprite.flax.flax-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 1), + "voxygen.voxel.sprite.flax.flax-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 2), + "voxygen.voxel.sprite.flax.flax-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 3), + "voxygen.voxel.sprite.flax.flax-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 4), + "voxygen.voxel.sprite.flax.flax-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 5), + "voxygen.voxel.sprite.flax.flax-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Carrot + make_models( + (BlockKind::Carrot, 0), + "voxygen.voxel.sprite.carrot.0", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 1), + "voxygen.voxel.sprite.carrot.1", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 2), + "voxygen.voxel.sprite.carrot.2", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 3), + "voxygen.voxel.sprite.carrot.3", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 4), + "voxygen.voxel.sprite.carrot.4", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 5), + "voxygen.voxel.sprite.carrot.5", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 0), + "voxygen.voxel.sprite.tomato.0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 1), + "voxygen.voxel.sprite.tomato.1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 2), + "voxygen.voxel.sprite.tomato.2", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 3), + "voxygen.voxel.sprite.tomato.3", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 4), + "voxygen.voxel.sprite.tomato.4", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Radish + make_models( + (BlockKind::Radish, 0), + "voxygen.voxel.sprite.radish.0", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 1), + "voxygen.voxel.sprite.radish.1", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 2), + "voxygen.voxel.sprite.radish.2", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 3), + "voxygen.voxel.sprite.radish.3", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 4), + "voxygen.voxel.sprite.radish.4", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + // Turnip + make_models( + (BlockKind::Turnip, 0), + "voxygen.voxel.sprite.turnip.turnip-0", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 1), + "voxygen.voxel.sprite.turnip.turnip-1", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 2), + "voxygen.voxel.sprite.turnip.turnip-2", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 3), + "voxygen.voxel.sprite.turnip.turnip-3", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 4), + "voxygen.voxel.sprite.turnip.turnip-4", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 5), + "voxygen.voxel.sprite.turnip.turnip-5", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + // Coconut + make_models( + (BlockKind::Coconut, 0), + "voxygen.voxel.sprite.fruit.coconut", + Vec3::new(-6.0, -6.0, 2.0), + Vec3::one(), + ), + // Scarecrow + make_models( + (BlockKind::Scarecrow, 0), + "voxygen.voxel.sprite.misc.scarecrow", + Vec3::new(-9.5, -3.0, -0.25), + Vec3::unit_z(), + ), + // Street Light + make_models( + (BlockKind::StreetLamp, 0), + "voxygen.voxel.sprite.misc.street_lamp", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::unit_z(), + ), + make_models( + (BlockKind::StreetLampTall, 0), + "voxygen.voxel.sprite.furniture.street_lamp-0", + Vec3::new(-10.5, -10.5, 0.0), + Vec3::unit_z(), + ), + // Door + make_models( + (BlockKind::Door, 0), + "voxygen.voxel.sprite.door.door-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Bed + make_models( + (BlockKind::Bed, 0), + "voxygen.voxel.sprite.furniture.bed-0", + Vec3::new(-9.5, -14.5, 0.0), + Vec3::one(), + ), + // Bench + make_models( + (BlockKind::Bench, 0), + "voxygen.voxel.sprite.furniture.bench-0", + Vec3::new(-14.0, -4.0, 0.0), + Vec3::one(), + ), + // Chair + make_models( + (BlockKind::ChairSingle, 0), + "voxygen.voxel.sprite.furniture.chair_single-0", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ChairSingle, 1), + "voxygen.voxel.sprite.furniture.chair_single-1", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ChairDouble, 0), + "voxygen.voxel.sprite.furniture.chair_double-0", + Vec3::new(-9.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ChairDouble, 1), + "voxygen.voxel.sprite.furniture.chair_double-1", + Vec3::new(-9.5, -4.5, 0.0), + Vec3::one(), + ), + // CoatRack + make_models( + (BlockKind::CoatRack, 0), + "voxygen.voxel.sprite.furniture.coatrack-0", + Vec3::new(-6.5, -6.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::CoatRack, 1), + "voxygen.voxel.sprite.furniture.coatrack-1", + Vec3::new(-6.5, -6.5, 0.0), + Vec3::one(), + ), + // Crate + make_models( + (BlockKind::Crate, 0), + "voxygen.voxel.sprite.furniture.crate-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 1), + "voxygen.voxel.sprite.furniture.crate-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 2), + "voxygen.voxel.sprite.furniture.crate-2", + Vec3::new(-3.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 3), + "voxygen.voxel.sprite.furniture.crate-3", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 4), + "voxygen.voxel.sprite.furniture.crate-4", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 5), + "voxygen.voxel.sprite.furniture.crate-5", + Vec3::new(-5.5, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 6), + "voxygen.voxel.sprite.furniture.crate-6", + Vec3::new(-4.5, -3.0, 0.0), + Vec3::one(), + ), + // DrawerLarge + make_models( + (BlockKind::DrawerLarge, 0), + "voxygen.voxel.sprite.furniture.drawer_large-0", + Vec3::new(-11.5, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DrawerLarge, 1), + "voxygen.voxel.sprite.furniture.drawer_large-1", + Vec3::new(-11.5, -5.0, 0.0), + Vec3::one(), + ), + // DrawerMedium + make_models( + (BlockKind::DrawerMedium, 0), + "voxygen.voxel.sprite.furniture.drawer_medium-0", + Vec3::new(-11.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DrawerMedium, 1), + "voxygen.voxel.sprite.furniture.drawer_medium-1", + Vec3::new(-11.0, -5.0, 0.0), + Vec3::one(), + ), + // DrawerSmall + make_models( + (BlockKind::DrawerSmall, 0), + "voxygen.voxel.sprite.furniture.drawer_small-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DrawerSmall, 1), + "voxygen.voxel.sprite.furniture.drawer_small-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // DungeonWallDecor + make_models( + (BlockKind::DungeonWallDecor, 0), + "voxygen.voxel.sprite.furniture.dungeon_wall-0", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 1), + "voxygen.voxel.sprite.furniture.dungeon_wall-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 2), + "voxygen.voxel.sprite.furniture.dungeon_wall-2", + Vec3::new(-5.5, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 3), + "voxygen.voxel.sprite.furniture.dungeon_wall-3", + Vec3::new(-1.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 4), + "voxygen.voxel.sprite.furniture.dungeon_wall-4", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 5), + "voxygen.voxel.sprite.furniture.dungeon_wall-5", + Vec3::new(-5.5, -0.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 6), + "voxygen.voxel.sprite.furniture.dungeon_wall-6", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 7), + "voxygen.voxel.sprite.furniture.dungeon_wall-7", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 8), + "voxygen.voxel.sprite.furniture.dungeon_wall-8", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 9), + "voxygen.voxel.sprite.furniture.dungeon_wall-9", + Vec3::new(-1.5, -5.5, 0.0), + Vec3::one(), + ), + // HangingBasket + make_models( + (BlockKind::HangingBasket, 0), + "voxygen.voxel.sprite.furniture.hanging_basket-0", + Vec3::new(-6.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::HangingBasket, 1), + "voxygen.voxel.sprite.furniture.hanging_basket-1", + Vec3::new(-9.5, -5.5, 0.0), + Vec3::one(), + ), + // HangingSign + make_models( + (BlockKind::HangingSign, 0), + "voxygen.voxel.sprite.furniture.hanging_sign-0", + Vec3::new(-3.5, -28.0, -4.0), + Vec3::one(), + ), + // WallLamp + make_models( + (BlockKind::WallLamp, 0), + "voxygen.voxel.sprite.furniture.lamp_wall-0", + Vec3::new(-6.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WallLamp, 1), + "voxygen.voxel.sprite.furniture.lamp_wall-1", + Vec3::new(-10.5, -9.0, 0.0), + Vec3::one(), + ), + // Planter + make_models( + (BlockKind::Planter, 0), + "voxygen.voxel.sprite.furniture.planter-0", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 1), + "voxygen.voxel.sprite.furniture.planter-1", + Vec3::new(-13.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 2), + "voxygen.voxel.sprite.furniture.planter-2", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 3), + "voxygen.voxel.sprite.furniture.planter-3", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 4), + "voxygen.voxel.sprite.furniture.planter-4", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 5), + "voxygen.voxel.sprite.furniture.planter-5", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 6), + "voxygen.voxel.sprite.furniture.planter-6", + Vec3::new(-7.5, -3.5, 0.0), + Vec3::one(), + ), + //Pot + make_models( + (BlockKind::Pot, 0), + "voxygen.voxel.sprite.furniture.pot-0", + Vec3::new(-3.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pot, 1), + "voxygen.voxel.sprite.furniture.pot-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Shelf + make_models( + (BlockKind::Shelf, 0), + "voxygen.voxel.sprite.furniture.shelf-0", + Vec3::new(-14.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Shelf, 1), + "voxygen.voxel.sprite.furniture.shelf-1", + Vec3::new(-13.5, -3.5, 0.0), + Vec3::one(), + ), + // TableSide + make_models( + (BlockKind::TableSide, 0), + "voxygen.voxel.sprite.furniture.table_side-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::TableSide, 1), + "voxygen.voxel.sprite.furniture.table_side-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // TableDining + make_models( + (BlockKind::TableDining, 0), + "voxygen.voxel.sprite.furniture.table_dining-0", + Vec3::new(-8.5, -8.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::TableDining, 1), + "voxygen.voxel.sprite.furniture.table_dining-1", + Vec3::new(-8.5, -8.5, 0.0), + Vec3::one(), + ), + // TableDouble + make_models( + (BlockKind::TableDouble, 0), + "voxygen.voxel.sprite.furniture.table_double-0", + Vec3::new(-18.5, -11.5, 0.0), + Vec3::one(), + ), + // WardrobeSingle + make_models( + (BlockKind::WardrobeSingle, 0), + "voxygen.voxel.sprite.furniture.wardrobe_single-0", + Vec3::new(-5.5, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WardrobeSingle, 1), + "voxygen.voxel.sprite.furniture.wardrobe_single-1", + Vec3::new(-5.5, -6.5, 0.0), + Vec3::one(), + ), + //WardrobeDouble + make_models( + (BlockKind::WardrobeDouble, 0), + "voxygen.voxel.sprite.furniture.wardrobe_double-0", + Vec3::new(-10.5, -6.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WardrobeDouble, 1), + "voxygen.voxel.sprite.furniture.wardrobe_double-1", + Vec3::new(-10.5, -6.0, 0.0), + Vec3::one(), + ), + /* Stones */ + make_models( + (BlockKind::Stones, 0), + "voxygen.voxel.sprite.rocks.rock-0", + Vec3::new(-3.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Stones, 1), + "voxygen.voxel.sprite.rocks.rock-1", + Vec3::new(-4.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Stones, 2), + "voxygen.voxel.sprite.rocks.rock-2", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::one(), + ), + /* Twigs */ + make_models( + (BlockKind::Twigs, 0), + "voxygen.voxel.sprite.twigs.twigs-0", + Vec3::new(-3.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Twigs, 1), + "voxygen.voxel.sprite.twigs.twigs-1", + Vec3::new(-2.0, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Twigs, 2), + "voxygen.voxel.sprite.twigs.twigs-2", + Vec3::new(-4.0, -4.0, 0.0), + Vec3::one(), + ), + // Shiny Gems + make_models( + (BlockKind::ShinyGem, 0), + "voxygen.voxel.sprite.gem.gem_blue", + Vec3::new(-2.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShinyGem, 1), + "voxygen.voxel.sprite.gem.gem_green", + Vec3::new(-2.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShinyGem, 2), + "voxygen.voxel.sprite.gem.gem_red", + Vec3::new(-3.0, -2.0, -2.0), + Vec3::one(), + ), + // Drop Gate Parts + make_models( + (BlockKind::DropGate, 0), + "voxygen.voxel.sprite.castle.drop_gate_bars-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DropGateBottom, 0), + "voxygen.voxel.sprite.castle.drop_gate_bottom-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Snow covered Grass + make_models( + (BlockKind::GrassSnow, 0), + "voxygen.voxel.sprite.grass.grass_snow_0", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 1), + "voxygen.voxel.sprite.grass.grass_snow_1", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 2), + "voxygen.voxel.sprite.grass.grass_snow_2", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 3), + "voxygen.voxel.sprite.grass.grass_snow_3", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 4), + "voxygen.voxel.sprite.grass.grass_snow_4", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 5), + "voxygen.voxel.sprite.grass.grass_snow_5", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 6), + "voxygen.voxel.sprite.grass.grass_snow_6", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 7), + "voxygen.voxel.sprite.grass.grass_snow_7", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 8), + "voxygen.voxel.sprite.grass.grass_snow_8", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 9), + "voxygen.voxel.sprite.grass.grass_snow_9", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ] + .into_iter() + .collect(); + let sprite_col_lights = ShadowPipeline::create_col_lights(renderer, greedy.finalize()) + .expect("Failed to upload sprite color and light data to the GPU!"); + Self { + atlas, chunks: HashMap::default(), + shadow_chunks: Vec::default(), mesh_send_tmp: send, mesh_recv: recv, mesh_todo: HashMap::default(), - sprite_models: vec![ - // Windows - ( - (BlockKind::Window1, 0), - make_models( - "voxygen.voxel.sprite.window.window-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Window2, 0), - make_models( - "voxygen.voxel.sprite.window.window-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Window3, 0), - make_models( - "voxygen.voxel.sprite.window.window-2", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Window4, 0), - make_models( - "voxygen.voxel.sprite.window.window-3", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Cacti - ( - (BlockKind::LargeCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.large_cactus", - Vec3::new(-13.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LargeCactus, 1), - make_models( - "voxygen.voxel.sprite.cacti.tall", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BarrelCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.barrel_cactus", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RoundCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.cactus_round", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.cactus_short", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MedFlatCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.flat_cactus_med", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortFlatCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.flat_cactus_short", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Fruit - ( - (BlockKind::Apple, 0), - make_models( - "voxygen.voxel.sprite.fruit.apple", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Flowers - ( - (BlockKind::BlueFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 5), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 6), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 7), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue-8", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 8), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue-9", - Vec3::new(-4.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 9), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue-10", - Vec3::new(-1.5, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-2", - Vec3::new(-5.0, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-3", - Vec3::new(-3.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-4", - Vec3::new(-5.0, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-5", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 5), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-6", - Vec3::new(-4.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 6), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-7", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 7), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_red_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_red_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_red_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_red-4", - Vec3::new(-6.5, -6.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_red-5", - Vec3::new(-3.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_white_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_white_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_white-3", - Vec3::new(-1.5, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_white-4", - Vec3::new(-5.0, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_white-5", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::YellowFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_yellow-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::YellowFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_yellow-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Sunflower, 0), - make_models( - "voxygen.voxel.sprite.flowers.sunflower_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Sunflower, 1), - make_models( - "voxygen.voxel.sprite.flowers.sunflower_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Grass - ( - (BlockKind::LargeGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_large-0", - Vec3::new(-2.0, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_large-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_large-2", - Vec3::new(-5.5, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_long_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_long_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_long_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_long_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_long_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 5), - make_models( - "voxygen.voxel.sprite.grass.grass_long_6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 6), - make_models( - "voxygen.voxel.sprite.grass.grass_long_7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_med_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_med_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_med_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_med_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_med_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_short_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_short_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_short_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_short_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_short_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 0), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 1), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 2), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 3), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 4), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 5), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 6), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 7), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 8), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 9), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-9", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 10), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-10", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 11), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-11", - Vec3::new(-8.0, -8.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 12), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-12", - Vec3::new(-5.0, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 13), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-13", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 14), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-14", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 15), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-15", - Vec3::new(-1.5, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 16), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-16", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Liana, 0), - make_models( - "voxygen.voxel.sprite.lianas.liana-0", - Vec3::new(-1.5, -0.5, -88.0), - Vec3::unit_z() * 0.5, - ), - ), - ( - (BlockKind::Liana, 1), - make_models( - "voxygen.voxel.sprite.lianas.liana-1", - Vec3::new(-1.0, -0.5, -55.0), - Vec3::unit_z() * 0.5, - ), - ), - ( - (BlockKind::Velorite, 0), - make_models( - "voxygen.voxel.sprite.velorite.velorite_ore", - Vec3::new(-5.0, -5.0, -5.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 0), - make_models( - "voxygen.voxel.sprite.velorite.velorite_1", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 1), - make_models( - "voxygen.voxel.sprite.velorite.velorite_2", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 2), - make_models( - "voxygen.voxel.sprite.velorite.velorite_3", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 3), - make_models( - "voxygen.voxel.sprite.velorite.velorite_4", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 4), - make_models( - "voxygen.voxel.sprite.velorite.velorite_5", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 5), - make_models( - "voxygen.voxel.sprite.velorite.velorite_6", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 6), - make_models( - "voxygen.voxel.sprite.velorite.velorite_7", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 7), - make_models( - "voxygen.voxel.sprite.velorite.velorite_8", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 8), - make_models( - "voxygen.voxel.sprite.velorite.velorite_9", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 9), - make_models( - "voxygen.voxel.sprite.velorite.velorite_10", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 0), - make_models( - "voxygen.voxel.sprite.chests.chest", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 1), - make_models( - "voxygen.voxel.sprite.chests.chest_gold", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 2), - make_models( - "voxygen.voxel.sprite.chests.chest_dark", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 3), - make_models( - "voxygen.voxel.sprite.chests.chest_vines", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - //Welwitch - ( - (BlockKind::Welwitch, 0), - make_models( - "voxygen.voxel.sprite.welwitch.1", - Vec3::new(-15.0, -17.0, -0.0), - Vec3::unit_z() * 0.7, - ), - ), - //Pumpkins - ( - (BlockKind::Pumpkin, 0), - make_models( - "voxygen.voxel.sprite.pumpkin.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 1), - make_models( - "voxygen.voxel.sprite.pumpkin.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 2), - make_models( - "voxygen.voxel.sprite.pumpkin.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 3), - make_models( - "voxygen.voxel.sprite.pumpkin.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 4), - make_models( - "voxygen.voxel.sprite.pumpkin.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 5), - make_models( - "voxygen.voxel.sprite.pumpkin.6", - Vec3::new(-7.0, -6.5, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 6), - make_models( - "voxygen.voxel.sprite.pumpkin.7", - Vec3::new(-7.0, -9.5, -0.0), - Vec3::one(), - ), - ), - //Lingonberries - ( - (BlockKind::LingonBerry, 0), - make_models( - "voxygen.voxel.sprite.lingonberry.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LingonBerry, 1), - make_models( - "voxygen.voxel.sprite.lingonberry.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LingonBerry, 2), - make_models( - "voxygen.voxel.sprite.lingonberry.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Leafy Plants - ( - (BlockKind::LeafyPlant, 0), - make_models( - "voxygen.voxel.sprite.leafy_plant.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 1), - make_models( - "voxygen.voxel.sprite.leafy_plant.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 2), - make_models( - "voxygen.voxel.sprite.leafy_plant.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 3), - make_models( - "voxygen.voxel.sprite.leafy_plant.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 4), - make_models( - "voxygen.voxel.sprite.leafy_plant.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 5), - make_models( - "voxygen.voxel.sprite.leafy_plant.6", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 6), - make_models( - "voxygen.voxel.sprite.leafy_plant.7", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 7), - make_models( - "voxygen.voxel.sprite.leafy_plant.8", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 8), - make_models( - "voxygen.voxel.sprite.leafy_plant.9", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 9), - make_models( - "voxygen.voxel.sprite.leafy_plant.10", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Ferns - ( - (BlockKind::Fern, 0), - make_models( - "voxygen.voxel.sprite.ferns.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 1), - make_models( - "voxygen.voxel.sprite.ferns.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 2), - make_models( - "voxygen.voxel.sprite.ferns.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 3), - make_models( - "voxygen.voxel.sprite.ferns.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 4), - make_models( - "voxygen.voxel.sprite.ferns.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 5), - make_models( - "voxygen.voxel.sprite.ferns.6", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 6), - make_models( - "voxygen.voxel.sprite.ferns.7", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 7), - make_models( - "voxygen.voxel.sprite.ferns.8", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 8), - make_models( - "voxygen.voxel.sprite.ferns.9", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 9), - make_models( - "voxygen.voxel.sprite.ferns.10", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 10), - make_models( - "voxygen.voxel.sprite.ferns.11", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 11), - make_models( - "voxygen.voxel.sprite.ferns.12", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 12), - make_models( - "voxygen.voxel.sprite.ferns.fern-0", - Vec3::new(-6.5, -11.5, 0.0), - Vec3::unit_z(), - ), - ), - // Dead Bush - ( - (BlockKind::DeadBush, 0), - make_models( - "voxygen.voxel.sprite.dead_bush.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DeadBush, 1), - make_models( - "voxygen.voxel.sprite.dead_bush.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DeadBush, 2), - make_models( - "voxygen.voxel.sprite.dead_bush.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DeadBush, 3), - make_models( - "voxygen.voxel.sprite.dead_bush.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Blueberries - ( - (BlockKind::Blueberry, 0), - make_models( - "voxygen.voxel.sprite.blueberry.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 1), - make_models( - "voxygen.voxel.sprite.blueberry.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 2), - make_models( - "voxygen.voxel.sprite.blueberry.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 3), - make_models( - "voxygen.voxel.sprite.blueberry.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 4), - make_models( - "voxygen.voxel.sprite.blueberry.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 5), - make_models( - "voxygen.voxel.sprite.blueberry.6", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 6), - make_models( - "voxygen.voxel.sprite.blueberry.7", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 7), - make_models( - "voxygen.voxel.sprite.blueberry.8", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 8), - make_models( - "voxygen.voxel.sprite.blueberry.9", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Ember - ( - (BlockKind::Ember, 0), - make_models( - "voxygen.voxel.sprite.ember.1", - Vec3::new(-7.0, -7.0, -2.9), - Vec3::new(1.0, 1.0, 0.0), - ), - ), - // Corn - ( - (BlockKind::Corn, 0), - make_models( - "voxygen.voxel.sprite.corn.corn-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 1), - make_models( - "voxygen.voxel.sprite.corn.corn-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 2), - make_models( - "voxygen.voxel.sprite.corn.corn-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 3), - make_models( - "voxygen.voxel.sprite.corn.corn-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 4), - make_models( - "voxygen.voxel.sprite.corn.corn-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 5), - make_models( - "voxygen.voxel.sprite.corn.corn-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Yellow Wheat - ( - (BlockKind::WheatYellow, 0), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 1), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 2), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 3), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 4), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 5), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 6), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 7), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 8), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 9), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-9", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Green Wheat - ( - (BlockKind::WheatGreen, 0), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 1), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 2), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 3), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 4), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 5), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 6), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 7), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 8), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 9), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-9", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Cabbage - ( - (BlockKind::Cabbage, 0), - make_models( - "voxygen.voxel.sprite.cabbage.cabbage-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Cabbage, 1), - make_models( - "voxygen.voxel.sprite.cabbage.cabbage-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Cabbage, 2), - make_models( - "voxygen.voxel.sprite.cabbage.cabbage-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Flax - ( - (BlockKind::Flax, 0), - make_models( - "voxygen.voxel.sprite.flax.flax-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 1), - make_models( - "voxygen.voxel.sprite.flax.flax-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 2), - make_models( - "voxygen.voxel.sprite.flax.flax-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 3), - make_models( - "voxygen.voxel.sprite.flax.flax-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 4), - make_models( - "voxygen.voxel.sprite.flax.flax-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 5), - make_models( - "voxygen.voxel.sprite.flax.flax-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Carrot - ( - (BlockKind::Carrot, 0), - make_models( - "voxygen.voxel.sprite.carrot.0", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 1), - make_models( - "voxygen.voxel.sprite.carrot.1", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 2), - make_models( - "voxygen.voxel.sprite.carrot.2", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 3), - make_models( - "voxygen.voxel.sprite.carrot.3", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 4), - make_models( - "voxygen.voxel.sprite.carrot.4", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 5), - make_models( - "voxygen.voxel.sprite.carrot.5", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 0), - make_models( - "voxygen.voxel.sprite.tomato.0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 1), - make_models( - "voxygen.voxel.sprite.tomato.1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 2), - make_models( - "voxygen.voxel.sprite.tomato.2", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 3), - make_models( - "voxygen.voxel.sprite.tomato.3", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 4), - make_models( - "voxygen.voxel.sprite.tomato.4", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Radish - ( - (BlockKind::Radish, 0), - make_models( - "voxygen.voxel.sprite.radish.0", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 1), - make_models( - "voxygen.voxel.sprite.radish.1", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 2), - make_models( - "voxygen.voxel.sprite.radish.2", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 3), - make_models( - "voxygen.voxel.sprite.radish.3", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 4), - make_models( - "voxygen.voxel.sprite.radish.4", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - // Turnip - ( - (BlockKind::Turnip, 0), - make_models( - "voxygen.voxel.sprite.turnip.turnip-0", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 1), - make_models( - "voxygen.voxel.sprite.turnip.turnip-1", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 2), - make_models( - "voxygen.voxel.sprite.turnip.turnip-2", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 3), - make_models( - "voxygen.voxel.sprite.turnip.turnip-3", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 4), - make_models( - "voxygen.voxel.sprite.turnip.turnip-4", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 5), - make_models( - "voxygen.voxel.sprite.turnip.turnip-5", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - // Coconut - ( - (BlockKind::Coconut, 0), - make_models( - "voxygen.voxel.sprite.fruit.coconut", - Vec3::new(-6.0, -6.0, 2.0), - Vec3::one(), - ), - ), - // Scarecrow - ( - (BlockKind::Scarecrow, 0), - make_models( - "voxygen.voxel.sprite.misc.scarecrow", - Vec3::new(-9.5, -3.0, -0.25), - Vec3::unit_z(), - ), - ), - // Street Light - ( - (BlockKind::StreetLamp, 0), - make_models( - "voxygen.voxel.sprite.misc.street_lamp", - Vec3::new(-4.5, -4.5, 0.0), - Vec3::unit_z(), - ), - ), - ( - (BlockKind::StreetLampTall, 0), - make_models( - "voxygen.voxel.sprite.furniture.street_lamp-0", - Vec3::new(-10.5, -10.5, 0.0), - Vec3::unit_z(), - ), - ), - // Door - ( - (BlockKind::Door, 0), - make_models( - "voxygen.voxel.sprite.door.door-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Bed - ( - (BlockKind::Bed, 0), - make_models( - "voxygen.voxel.sprite.furniture.bed-0", - Vec3::new(-9.5, -14.5, 0.0), - Vec3::one(), - ), - ), - // Bench - ( - (BlockKind::Bench, 0), - make_models( - "voxygen.voxel.sprite.furniture.bench-0", - Vec3::new(-14.0, -4.0, 0.0), - Vec3::one(), - ), - ), - // Chair - ( - (BlockKind::ChairSingle, 0), - make_models( - "voxygen.voxel.sprite.furniture.chair_single-0", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ChairSingle, 1), - make_models( - "voxygen.voxel.sprite.furniture.chair_single-1", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ChairDouble, 0), - make_models( - "voxygen.voxel.sprite.furniture.chair_double-0", - Vec3::new(-9.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ChairDouble, 1), - make_models( - "voxygen.voxel.sprite.furniture.chair_double-1", - Vec3::new(-9.5, -4.5, 0.0), - Vec3::one(), - ), - ), - // CoatRack - ( - (BlockKind::CoatRack, 0), - make_models( - "voxygen.voxel.sprite.furniture.coatrack-0", - Vec3::new(-6.5, -6.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::CoatRack, 1), - make_models( - "voxygen.voxel.sprite.furniture.coatrack-1", - Vec3::new(-6.5, -6.5, 0.0), - Vec3::one(), - ), - ), - // Crate - ( - (BlockKind::Crate, 0), - make_models( - "voxygen.voxel.sprite.furniture.crate-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 1), - make_models( - "voxygen.voxel.sprite.furniture.crate-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 2), - make_models( - "voxygen.voxel.sprite.furniture.crate-2", - Vec3::new(-3.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 3), - make_models( - "voxygen.voxel.sprite.furniture.crate-3", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 4), - make_models( - "voxygen.voxel.sprite.furniture.crate-4", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 5), - make_models( - "voxygen.voxel.sprite.furniture.crate-5", - Vec3::new(-5.5, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 6), - make_models( - "voxygen.voxel.sprite.furniture.crate-6", - Vec3::new(-4.5, -3.0, 0.0), - Vec3::one(), - ), - ), - // DrawerLarge - ( - (BlockKind::DrawerLarge, 0), - make_models( - "voxygen.voxel.sprite.furniture.drawer_large-0", - Vec3::new(-11.5, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DrawerLarge, 1), - make_models( - "voxygen.voxel.sprite.furniture.drawer_large-1", - Vec3::new(-11.5, -5.0, 0.0), - Vec3::one(), - ), - ), - // DrawerMedium - ( - (BlockKind::DrawerMedium, 0), - make_models( - "voxygen.voxel.sprite.furniture.drawer_medium-0", - Vec3::new(-11.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DrawerMedium, 1), - make_models( - "voxygen.voxel.sprite.furniture.drawer_medium-1", - Vec3::new(-11.0, -5.0, 0.0), - Vec3::one(), - ), - ), - // DrawerSmall - ( - (BlockKind::DrawerSmall, 0), - make_models( - "voxygen.voxel.sprite.furniture.drawer_small-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DrawerSmall, 1), - make_models( - "voxygen.voxel.sprite.furniture.drawer_small-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // DungeonWallDecor - ( - (BlockKind::DungeonWallDecor, 0), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-0", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 1), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 2), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-2", - Vec3::new(-5.5, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 3), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-3", - Vec3::new(-1.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 4), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-4", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 5), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-5", - Vec3::new(-5.5, -0.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 6), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-6", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 7), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-7", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 8), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-8", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 9), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-9", - Vec3::new(-1.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // HangingBasket - ( - (BlockKind::HangingBasket, 0), - make_models( - "voxygen.voxel.sprite.furniture.hanging_basket-0", - Vec3::new(-6.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::HangingBasket, 1), - make_models( - "voxygen.voxel.sprite.furniture.hanging_basket-1", - Vec3::new(-9.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // HangingSign - ( - (BlockKind::HangingSign, 0), - make_models( - "voxygen.voxel.sprite.furniture.hanging_sign-0", - Vec3::new(-3.5, -28.0, -4.0), - Vec3::one(), - ), - ), - // WallLamp - ( - (BlockKind::WallLamp, 0), - make_models( - "voxygen.voxel.sprite.furniture.lamp_wall-0", - Vec3::new(-5.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WallLamp, 1), - make_models( - "voxygen.voxel.sprite.furniture.lamp_wall-1", - Vec3::new(-10.5, -9.0, 0.0), - Vec3::one(), - ), - ), - // Planter - ( - (BlockKind::Planter, 0), - make_models( - "voxygen.voxel.sprite.furniture.planter-0", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 1), - make_models( - "voxygen.voxel.sprite.furniture.planter-1", - Vec3::new(-13.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 2), - make_models( - "voxygen.voxel.sprite.furniture.planter-2", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 3), - make_models( - "voxygen.voxel.sprite.furniture.planter-3", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 4), - make_models( - "voxygen.voxel.sprite.furniture.planter-4", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 5), - make_models( - "voxygen.voxel.sprite.furniture.planter-5", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 6), - make_models( - "voxygen.voxel.sprite.furniture.planter-6", - Vec3::new(-7.5, -3.5, 0.0), - Vec3::one(), - ), - ), - //Pot - ( - (BlockKind::Pot, 0), - make_models( - "voxygen.voxel.sprite.furniture.pot-0", - Vec3::new(-3.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pot, 1), - make_models( - "voxygen.voxel.sprite.furniture.pot-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Shelf - ( - (BlockKind::Shelf, 0), - make_models( - "voxygen.voxel.sprite.furniture.shelf-0", - Vec3::new(-14.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Shelf, 1), - make_models( - "voxygen.voxel.sprite.furniture.shelf-1", - Vec3::new(-13.5, -3.5, 0.0), - Vec3::one(), - ), - ), - // TableSide - ( - (BlockKind::TableSide, 0), - make_models( - "voxygen.voxel.sprite.furniture.table_side-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::TableSide, 1), - make_models( - "voxygen.voxel.sprite.furniture.table_side-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // TableDining - ( - (BlockKind::TableDining, 0), - make_models( - "voxygen.voxel.sprite.furniture.table_dining-0", - Vec3::new(-8.5, -8.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::TableDining, 1), - make_models( - "voxygen.voxel.sprite.furniture.table_dining-1", - Vec3::new(-8.5, -8.5, 0.0), - Vec3::one(), - ), - ), - // TableDouble - ( - (BlockKind::TableDouble, 0), - make_models( - "voxygen.voxel.sprite.furniture.table_double-0", - Vec3::new(-18.5, -11.5, 0.0), - Vec3::one(), - ), - ), - // WardrobeSingle - ( - (BlockKind::WardrobeSingle, 0), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_single-0", - Vec3::new(-5.5, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WardrobeSingle, 1), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_single-1", - Vec3::new(-5.5, -6.5, 0.0), - Vec3::one(), - ), - ), - //WardrobeDouble - ( - (BlockKind::WardrobeDouble, 0), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_double-0", - Vec3::new(-10.5, -6.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WardrobeDouble, 1), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_double-1", - Vec3::new(-10.5, -6.0, 0.0), - Vec3::one(), - ), - ), - /* Stones */ - ( - (BlockKind::Stones, 0), - make_models( - "voxygen.voxel.sprite.rocks.rock-0", - Vec3::new(-3.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Stones, 1), - make_models( - "voxygen.voxel.sprite.rocks.rock-1", - Vec3::new(-4.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Stones, 2), - make_models( - "voxygen.voxel.sprite.rocks.rock-2", - Vec3::new(-4.5, -4.5, 0.0), - Vec3::one(), - ), - ), - /* Twigs */ - ( - (BlockKind::Twigs, 0), - make_models( - "voxygen.voxel.sprite.twigs.twigs-0", - Vec3::new(-3.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Twigs, 1), - make_models( - "voxygen.voxel.sprite.twigs.twigs-1", - Vec3::new(-2.0, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Twigs, 2), - make_models( - "voxygen.voxel.sprite.twigs.twigs-2", - Vec3::new(-4.0, -4.0, 0.0), - Vec3::one(), - ), - ), - // Shiny Gems - ( - (BlockKind::ShinyGem, 0), - make_models( - "voxygen.voxel.sprite.gem.gem_blue", - Vec3::new(-2.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShinyGem, 1), - make_models( - "voxygen.voxel.sprite.gem.gem_green", - Vec3::new(-2.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShinyGem, 2), - make_models( - "voxygen.voxel.sprite.gem.gem_red", - Vec3::new(-3.0, -2.0, -2.0), - Vec3::one(), - ), - ), - // Drop Gate Parts - ( - (BlockKind::DropGate, 0), - make_models( - "voxygen.voxel.sprite.castle.drop_gate_bars-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DropGateBottom, 0), - make_models( - "voxygen.voxel.sprite.castle.drop_gate_bottom-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Snow covered Grass - ( - (BlockKind::GrassSnow, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_0", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_1", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_2", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_3", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_4", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 5), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_5", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 6), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_6", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 7), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_7", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 8), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_8", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 9), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_9", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ] - .into_iter() - .collect(), + sprite_data: Arc::new(sprite_data), + sprite_col_lights, waves: renderer .create_texture( &assets::load_expect("voxygen.texture.waves"), Some(gfx::texture::FilterMethod::Trilinear), Some(gfx::texture::WrapMode::Tile), + None, ) .expect("Failed to create wave texture"), + col_lights, phantom: PhantomData, } } + fn make_atlas( + renderer: &mut Renderer, + ) -> Result<(AtlasAllocator, Texture), RenderError> { + let max_texture_size = renderer.max_texture_size(); + let atlas_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { + // TODO: Verify some good empirical constants. + small_size_threshold: 128, + large_size_threshold: 1024, + ..guillotiere::AllocatorOptions::default() + }); + let texture = renderer.create_texture_raw( + gfx::texture::Kind::D2( + max_texture_size, + max_texture_size, + gfx::texture::AaMode::Single, + ), + 1 as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE, + gfx::memory::Usage::Dynamic, + (0, 0), + gfx::format::Swizzle::new(), + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + )?; + Ok((atlas, texture)) + } + + fn remove_chunk_meta(&mut self, _pos: Vec2, chunk: &TerrainChunkData) { + self.atlas.deallocate(chunk.col_lights); + /* let (zmin, zmax) = chunk.z_bounds; + self.z_index_up.remove(Vec3::from(zmin, pos.x, pos.y)); + self.z_index_down.remove(Vec3::from(zmax, pos.x, pos.y)); */ + } + + fn insert_chunk(&mut self, pos: Vec2, chunk: TerrainChunkData) { + if let Some(old) = self.chunks.insert(pos, chunk) { + self.remove_chunk_meta(pos, &old); + } + /* let (zmin, zmax) = chunk.z_bounds; + self.z_index_up.insert(Vec3::from(zmin, pos.x, pos.y)); + self.z_index_down.insert(Vec3::from(zmax, pos.x, pos.y)); */ + } + + fn remove_chunk(&mut self, pos: Vec2) { + if let Some(chunk) = self.chunks.remove(&pos) { + self.remove_chunk_meta(pos, &chunk); + // Temporarily remember dead chunks for shadowing purposes. + self.shadow_chunks.push((pos, chunk)); + } + if let Some(_todo) = self.mesh_todo.remove(&pos) { + //Do nothing on todo mesh removal. + } + } + /// Maintain terrain data. To be called once per tick. #[allow(clippy::for_loops_over_fallibles)] // TODO: Pending review in #587 #[allow(clippy::len_zero)] // TODO: Pending review in #587 @@ -2869,9 +2472,10 @@ impl Terrain { loaded_distance: f32, view_mat: Mat4, proj_mat: Mat4, - ) { + ) -> (Aabb, Vec>, math::Aabr) { let current_tick = scene_data.tick; let current_time = scene_data.state.get_time(); + let mut visible_bounding_box: Option> = None; // Add any recently created or changed chunks to the list of chunks to be // meshed. @@ -2964,11 +2568,13 @@ impl Terrain { } // Remove any models for chunks that have been recently removed. - for pos in &scene_data.state.terrain_changes().removed_chunks { - self.chunks.remove(pos); - self.mesh_todo.remove(pos); + for &pos in &scene_data.state.terrain_changes().removed_chunks { + self.remove_chunk(pos); } + // Limit ourselves to u16::MAX even if larger textures are supported. + let max_texture_size = renderer.max_texture_size(); + for todo in self .mesh_todo .values_mut() @@ -3000,10 +2606,13 @@ impl Terrain { // a sample of the terrain that includes both the chunk we want and // its neighbours. let volume = match scene_data.state.terrain().sample(aabr) { - Ok(sample) => sample, + Ok(sample) => sample, /* TODO: Ensure that all of the chunk's neighbours still + * exist to avoid buggy shadow borders */ // Either this chunk or its neighbours doesn't yet exist, so we keep it in the // queue to be processed at a later date when we have its neighbours. - Err(VolGrid2dError::NoSuchChunk) => return, + Err(VolGrid2dError::NoSuchChunk) => { + continue; + }, _ => panic!("Unhandled edge case"), }; @@ -3026,13 +2635,17 @@ impl Terrain { // Queue the worker thread. let started_tick = todo.started_tick; + let sprite_data = Arc::clone(&self.sprite_data); scene_data.thread_pool.execute(move || { + let sprite_data = sprite_data; let _ = send.send(mesh_worker( pos, (min_z as f32, max_z as f32), started_tick, volume, + max_texture_size, aabb, + &sprite_data, )); }); todo.active_worker = Some(todo.started_tick); @@ -3047,12 +2660,36 @@ impl Terrain { // It's the mesh we want, insert the newly finished model into the terrain model // data structure (convert the mesh to a model first of course). Some(todo) if response.started_tick <= todo.started_tick => { + let started_tick = todo.started_tick; let load_time = self .chunks .get(&response.pos) .map(|chunk| chunk.load_time) .unwrap_or(current_time as f32); - self.chunks.insert(response.pos, TerrainChunkData { + // TODO: Allocate new atlas on allocation faillure. + let (tex, tex_size) = response.col_lights_info; + let atlas = &mut self.atlas; + let allocation = atlas + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) + .expect("Not yet implemented: allocate new atlas on allocation faillure."); + // NOTE: Cast is safe since the origin was a u16. + let atlas_offs = Vec2::new( + allocation.rectangle.min.x as u16, + allocation.rectangle.min.y as u16, + ); + if let Err(err) = renderer.update_texture( + &self.col_lights, + atlas_offs.into_array(), + tex_size.into_array(), + &tex, + ) { + warn!("Failed to update texture: {:?}", err); + } + + self.insert_chunk(response.pos, TerrainChunkData { load_time, opaque_model: renderer .create_model(&response.opaque_mesh) @@ -3066,6 +2703,7 @@ impl Terrain { } else { None }, + col_lights: allocation.id, sprite_instances: response .sprite_instances .into_iter() @@ -3086,40 +2724,55 @@ impl Terrain { }), ) .into_array(), + atlas_offs: Vec4::new( + i32::from(atlas_offs.x), + i32::from(atlas_offs.y), + 0, + 0, + ) + .into_array(), load_time, }]) .expect("Failed to upload chunk locals to the GPU!"), - visible: false, + visible: Visibility::OutOfRange, + can_shadow_point: false, + can_shadow_sun: false, z_bounds: response.z_bounds, frustum_last_plane_index: 0, }); - if response.started_tick == todo.started_tick { + if response.started_tick == started_tick { self.mesh_todo.remove(&response.pos); } }, // Chunk must have been removed, or it was spawned on an old tick. Drop the mesh // since it's either out of date or no longer needed. - _ => {}, + Some(_todo) => {}, + None => {}, } } // Construct view frustum - let frustum = Frustum::from_modelview_projection((proj_mat * view_mat).into_col_arrays()); + let focus_off = focus_pos.map(|e| e.trunc()); + let frustum = Frustum::from_modelview_projection( + (proj_mat * view_mat * Mat4::translation_3d(-focus_off)).into_col_arrays(), + ); // Update chunk visibility let chunk_sz = V::RECT_SIZE.x as f32; for (pos, chunk) in &mut self.chunks { - let chunk_pos = pos.map(|e| e as f32 * chunk_sz); + let chunk_pos = pos.as_::() * chunk_sz; + + chunk.can_shadow_sun = false; // Limit focus_pos to chunk bounds and ensure the chunk is within the fog // boundary let nearest_in_chunk = Vec2::from(focus_pos).clamped(chunk_pos, chunk_pos + chunk_sz); - let in_range = Vec2::::from(focus_pos).distance_squared(nearest_in_chunk) - < loaded_distance.powf(2.0); + let distance_2 = Vec2::::from(focus_pos).distance_squared(nearest_in_chunk); + let in_range = distance_2 < loaded_distance.powf(2.0); if !in_range { - chunk.visible = in_range; + chunk.visible = Visibility::OutOfRange; continue; } @@ -3135,52 +2788,237 @@ impl Terrain { .coherent_test_against_frustum(&frustum, chunk.frustum_last_plane_index); chunk.frustum_last_plane_index = last_plane_index; - chunk.visible = in_frustum; + chunk.visible = if in_frustum { + Visibility::Visible + } else { + Visibility::InRange + }; + let chunk_box = Aabb { + min: Vec3::from(chunk_min), + max: Vec3::from(chunk_max), + }; + + if in_frustum { + let visible_box = chunk_box; + visible_bounding_box = visible_bounding_box + .map(|e| e.union(visible_box)) + .or(Some(visible_box)); + } + // FIXME: Hack that only works when only the lantern casts point shadows + // (and hardcodes the shadow distance). Should ideally exist per-light, too. + chunk.can_shadow_point = distance_2 < (128.0 * 128.0); } + + // PSRs: potential shadow receivers + let visible_bounding_box = visible_bounding_box.unwrap_or(Aabb { + min: focus_pos - 2.0, + max: focus_pos + 2.0, + }); + + // PSCs: Potential shadow casters + let ray_direction = scene_data.get_sun_dir(); + let collides_with_aabr = |a: math::Aabb, b: math::Aabr| { + let min = math::Vec4::new(a.min.x, a.min.y, b.min.x, b.min.y); + let max = math::Vec4::new(b.max.x, b.max.y, a.max.x, a.max.y); + min.partial_cmple_simd(max).reduce_and() + }; + let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0 + && renderer.render_mode().shadow.is_map() + { + let visible_bounding_box = math::Aabb:: { + min: math::Vec3::from(visible_bounding_box.min - focus_off), + max: math::Vec3::from(visible_bounding_box.max - focus_off), + }; + let focus_off = math::Vec3::from(focus_off); + let visible_bounds_fine = visible_bounding_box.as_::(); + let inv_proj_view = + math::Mat4::from_col_arrays((proj_mat * view_mat).into_col_arrays()) + .as_::() + .inverted(); + let ray_direction = math::Vec3::::from(ray_direction); + let visible_light_volume = math::calc_focused_light_volume_points( + inv_proj_view, + ray_direction.as_::(), + visible_bounds_fine, + 1e-6, + ) + .map(|v| v.as_::()) + .collect::>(); + + let cam_pos = math::Vec4::from(view_mat.inverted() * Vec4::unit_w()).xyz(); + let up: math::Vec3 = { math::Vec3::up() }; + + let ray_mat = math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, up); + let visible_bounds = math::Aabr::from(math::fit_psr( + ray_mat, + visible_light_volume.iter().copied(), + |p| p, + )); + let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off); + + let can_shadow_sun = |pos: Vec2, chunk: &TerrainChunkData| { + let chunk_pos = pos.as_::() * chunk_sz; + + // Ensure the chunk is within the PSR set. + let chunk_box = math::Aabb { + min: math::Vec3::new(chunk_pos.x, chunk_pos.y, chunk.z_bounds.0), + max: math::Vec3::new( + chunk_pos.x + chunk_sz, + chunk_pos.y + chunk_sz, + chunk.z_bounds.1, + ), + }; + + let chunk_from_light = math::fit_psr( + ray_mat, + math::aabb_to_points(chunk_box).iter().copied(), + |p| p, + ); + collides_with_aabr(chunk_from_light, visible_bounds) + }; + + // Handle potential shadow casters (chunks that aren't visible, but are still in + // range) to see if they could cast shadows. + self.chunks.iter_mut() + // NOTE: We deliberately avoid doing this computation for chunks we already know + // are visible, since by definition they'll always intersect the visible view + // frustum. + .filter(|chunk| chunk.1.visible <= Visibility::InRange) + .for_each(|(&pos, chunk)| { + chunk.can_shadow_sun = can_shadow_sun(pos, chunk); + }); + + // Handle dead chunks that we kept around only to make sure shadows don't blink + // out when a chunk disappears. + // + // If the sun can currently cast shadows, we retain only those shadow chunks + // that both: 1. have not been replaced by a real chunk instance, + // and 2. are currently potential shadow casters (as witnessed by + // `can_shadow_sun` returning true). + // + // NOTE: Please make sure this runs *after* any code that could insert a chunk! + // Otherwise we may end up with multiple instances of the chunk trying to cast + // shadows at the same time. + let chunks = &self.chunks; + self.shadow_chunks + .retain(|(pos, chunk)| !chunks.contains_key(pos) && can_shadow_sun(*pos, chunk)); + + (visible_light_volume, visible_bounds) + } else { + // There's no daylight or no shadows, so there's no reason to keep any + // shadow chunks around. + self.shadow_chunks.clear(); + (Vec::new(), math::Aabr { + min: math::Vec2::zero(), + max: math::Vec2::zero(), + }) + }; + + ( + visible_bounding_box, + visible_light_volume, + visible_psr_bounds, + ) } pub fn chunk_count(&self) -> usize { self.chunks.len() } pub fn visible_chunk_count(&self) -> usize { - self.chunks.iter().filter(|(_, c)| c.visible).count() + self.chunks + .iter() + .filter(|(_, c)| c.visible == Visibility::Visible) + .count() + } + + pub fn shadow_chunk_count(&self) -> usize { self.shadow_chunks.len() } + + pub fn render_shadows( + &self, + renderer: &mut Renderer, + global: &GlobalModel, + (is_daylight, light_data): super::LightData, + focus_pos: Vec3, + ) { + if !renderer.render_mode().shadow.is_map() { + return; + }; + + let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { + (e as i32).div_euclid(sz as i32) + }); + + let chunk_iter = Spiral2d::new() + .filter_map(|rpos| { + let pos = focus_chunk + rpos; + self.chunks.get(&pos) + }) + .take(self.chunks.len()); + + // Directed shadows + // + // NOTE: We also render shadows for dead chunks that were found to still be + // potential shadow casters, to avoid shadows suddenly disappearing at + // very steep sun angles (e.g. sunrise / sunset). + if is_daylight { + chunk_iter + .clone() + .filter(|chunk| chunk.can_shadow_sun()) + .chain(self.shadow_chunks.iter().map(|(_, chunk)| chunk)) + .for_each(|chunk| { + // Directed light shadows. + renderer.render_terrain_shadow_directed( + &chunk.opaque_model, + global, + &chunk.locals, + &global.shadow_mats, + ); + }); + } + + // Point shadows + // + // NOTE: We don't bother retaining chunks unless they cast sun shadows, so we + // don't use `shadow_chunks` here. + light_data.iter().take(1).for_each(|_light| { + chunk_iter.clone().for_each(|chunk| { + if chunk.can_shadow_point { + renderer.render_shadow_point( + &chunk.opaque_model, + global, + &chunk.locals, + &global.shadow_mats, + ); + } + }); + }); } pub fn render( &self, renderer: &mut Renderer, - globals: &Consts, - lights: &Consts, - shadows: &Consts, + global: &GlobalModel, + lod: &LodData, focus_pos: Vec3, ) { let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) }); - let chunks = &self.chunks; let chunk_iter = Spiral2d::new() - .scan(0, |n, rpos| { - if *n >= chunks.len() { - None - } else { - let pos = focus_chunk + rpos; - Some(chunks.get(&pos).map(|c| { - *n += 1; - (pos, c) - })) - } + .filter_map(|rpos| { + let pos = focus_chunk + rpos; + self.chunks.get(&pos).map(|c| (pos, c)) }) - .filter_map(|x| x); + .take(self.chunks.len()); - // Opaque - for (_, chunk) in chunk_iter.clone() { - if chunk.visible { + for (_, chunk) in chunk_iter { + if chunk.visible == Visibility::Visible { renderer.render_terrain_chunk( &chunk.opaque_model, - globals, + &self.col_lights, + global, &chunk.locals, - lights, - shadows, + lod, ); } } @@ -3189,65 +3027,76 @@ impl Terrain { pub fn render_translucent( &self, renderer: &mut Renderer, - globals: &Consts, - lights: &Consts, - shadows: &Consts, + global: &GlobalModel, + lod: &LodData, focus_pos: Vec3, + cam_pos: Vec3, sprite_render_distance: f32, ) { let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) }); - let chunks = &self.chunks; + // Avoid switching textures let chunk_iter = Spiral2d::new() - .scan(0, |n, rpos| { - if *n >= chunks.len() { - None - } else { - let pos = focus_chunk + rpos; - Some(chunks.get(&pos).map(|c| { - *n += 1; - (pos, c) - })) - } + .filter_map(|rpos| { + let pos = focus_chunk + rpos; + self.chunks.get(&pos).map(|c| (pos, c)) }) - .filter_map(|x| x); + .take(self.chunks.len()); // Terrain sprites + let chunk_size = V::RECT_SIZE.map(|e| e as f32); + let chunk_mag = (chunk_size * (f32::consts::SQRT_2 * 0.5)).magnitude_squared(); for (pos, chunk) in chunk_iter.clone() { - if chunk.visible { + if chunk.visible == Visibility::Visible { let sprite_low_detail_distance = sprite_render_distance * 0.75; let sprite_mid_detail_distance = sprite_render_distance * 0.5; let sprite_hid_detail_distance = sprite_render_distance * 0.35; let sprite_high_detail_distance = sprite_render_distance * 0.15; - let chunk_center = - pos.map2(V::RECT_SIZE, |e, sz: u32| (e as f32 + 0.5) * sz as f32); - let dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); - if dist_sqrd < sprite_render_distance.powf(2.0) { - for (kind, instances) in &chunk.sprite_instances { - if let Some(models) = self.sprite_models.get(&kind) { - renderer.render_sprites( - if dist_sqrd < sprite_high_detail_distance.powf(2.0) { - &models[0] - } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { - &models[1] - } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { - &models[2] - } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { - &models[3] - } else { - &models[4] - }, - globals, - &instances, - lights, - shadows, - ); + let chunk_center = pos.map2(chunk_size, |e, sz| (e as f32 + 0.5) * sz); + let focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); + let dist_sqrd = + Vec2::from(cam_pos) + .distance_squared(chunk_center) + .min(Vec2::from(cam_pos).distance_squared(chunk_center - chunk_size * 0.5)) + .min(Vec2::from(cam_pos).distance_squared( + chunk_center - chunk_size.x * 0.5 + chunk_size.y * 0.5, + )) + .min( + Vec2::from(cam_pos).distance_squared(chunk_center + chunk_size.x * 0.5), + ) + .min(Vec2::from(cam_pos).distance_squared( + chunk_center + chunk_size.x * 0.5 - chunk_size.y * 0.5, + )); + if focus_dist_sqrd < sprite_render_distance.powf(2.0) { + for (kind, instances) in (&chunk.sprite_instances).into_iter() { + let SpriteData { model, locals, .. } = if sprite_config_for(kind.0) + .map(|config| config.wind_sway >= 0.4) + .unwrap_or(false) + && dist_sqrd <= chunk_mag + || dist_sqrd < sprite_high_detail_distance.powf(2.0) + { + &self.sprite_data[&kind][0] + } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { + &self.sprite_data[&kind][1] + } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { + &self.sprite_data[&kind][2] + } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { + &self.sprite_data[&kind][3] } else { - warn!("Sprite model for {:?} does not exists", kind); - } + &self.sprite_data[&kind][4] + }; + renderer.render_sprites( + model, + &self.sprite_col_lights, + global, + &chunk.locals, + locals, + &instances, + lod, + ); } } } @@ -3256,7 +3105,7 @@ impl Terrain { // Translucent chunk_iter .clone() - .filter(|(_, chunk)| chunk.visible) + .filter(|(_, chunk)| chunk.visible == Visibility::Visible) .filter_map(|(_, chunk)| { chunk .fluid_model @@ -3267,7 +3116,13 @@ impl Terrain { .into_iter() .rev() // Render back-to-front .for_each(|(model, locals)| { - renderer.render_fluid_chunk(model, globals, locals, lights, shadows, &self.waves) + renderer.render_fluid_chunk( + model, + global, + locals, + lod, + &self.waves, + ) }); } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index dc43964b3d..a12398c265 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -63,7 +63,11 @@ impl SessionState { pub fn new(global_state: &mut GlobalState, client: Rc>) -> Self { // Create a scene for this session. The scene handles visible elements of the // game world. - let mut scene = Scene::new(global_state.window.renderer_mut()); + let mut scene = Scene::new( + global_state.window.renderer_mut(), + &*client.borrow(), + &global_state.settings, + ); scene .camera_mut() .set_fov_deg(global_state.settings.graphics.fov); @@ -218,6 +222,9 @@ impl PlayState for SessionState { let camera::Dependents { cam_pos, cam_dir, .. } = self.scene.camera().dependents(); + let focus_pos = self.scene.camera().get_focus_pos(); + let focus_off = focus_pos.map(|e| e.trunc()); + let cam_pos = cam_pos + focus_off; let (is_aiming, aim_dir_offset) = { let client = self.client.borrow(); @@ -677,11 +684,13 @@ impl PlayState for SessionState { .camera_mut() .compute_dependents(&*self.client.borrow().state().terrain()); - // Extract HUD events ensuring the client borrow gets dropped. - let mut hud_events = self.hud.maintain( - &self.client.borrow(), - global_state, - DebugInfo { + // Generate debug info, if needed (it iterates through enough data that we might + // as well avoid it unless we need it). + let debug_info = global_state + .settings + .gameplay + .toggle_debug + .then(|| DebugInfo { tps: global_state.clock.get_tps(), ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), coordinates: self @@ -709,13 +718,21 @@ impl PlayState for SessionState { .get(self.client.borrow().entity()) .cloned(), num_chunks: self.scene.terrain().chunk_count() as u32, + num_lights: self.scene.lights().len() as u32, num_visible_chunks: self.scene.terrain().visible_chunk_count() as u32, + num_shadow_chunks: self.scene.terrain().shadow_chunk_count() as u32, num_figures: self.scene.figure_mgr().figure_count() as u32, num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32, num_particles: self.scene.particle_mgr().particle_count() as u32, num_particles_visible: self.scene.particle_mgr().particle_count_visible() as u32, - }, + }); + + // Extract HUD events ensuring the client borrow gets dropped. + let mut hud_events = self.hud.maintain( + &self.client.borrow(), + global_state, + &debug_info, &self.scene.camera(), global_state.clock.get_last_delta(), HudInfo { @@ -731,7 +748,9 @@ impl PlayState for SessionState { // Look for changes in the localization files if global_state.localization_watcher.reloaded() { - hud_events.push(HudEvent::ChangeLanguage(self.voxygen_i18n.metadata.clone())); + hud_events.push(HudEvent::ChangeLanguage(Box::new( + self.voxygen_i18n.metadata.clone(), + ))); } // Maintain the UI. @@ -806,6 +825,12 @@ impl PlayState for SessionState { global_state.settings.graphics.view_distance = view_distance; global_state.settings.save_to_file_warn(); }, + HudEvent::AdjustLodDetail(lod_detail) => { + self.scene.lod.set_detail(lod_detail); + + global_state.settings.graphics.lod_detail = lod_detail; + global_state.settings.save_to_file_warn(); + }, HudEvent::AdjustSpriteRenderDistance(sprite_render_distance) => { global_state.settings.graphics.sprite_render_distance = sprite_render_distance; @@ -919,34 +944,14 @@ impl PlayState for SessionState { global_state.settings.graphics.gamma = new_gamma; global_state.settings.save_to_file_warn(); }, - HudEvent::ChangeAaMode(new_aa_mode) => { + HudEvent::ChangeRenderMode(new_render_mode) => { // Do this first so if it crashes the setting isn't saved :) global_state .window .renderer_mut() - .set_aa_mode(new_aa_mode) + .set_render_mode((&*new_render_mode).clone()) .unwrap(); - global_state.settings.graphics.aa_mode = new_aa_mode; - global_state.settings.save_to_file_warn(); - }, - HudEvent::ChangeCloudMode(new_cloud_mode) => { - // Do this first so if it crashes the setting isn't saved :) - global_state - .window - .renderer_mut() - .set_cloud_mode(new_cloud_mode) - .unwrap(); - global_state.settings.graphics.cloud_mode = new_cloud_mode; - global_state.settings.save_to_file_warn(); - }, - HudEvent::ChangeFluidMode(new_fluid_mode) => { - // Do this first so if it crashes the setting isn't saved :) - global_state - .window - .renderer_mut() - .set_fluid_mode(new_fluid_mode) - .unwrap(); - global_state.settings.graphics.fluid_mode = new_fluid_mode; + global_state.settings.graphics.render_mode = *new_render_mode; global_state.settings.save_to_file_warn(); }, HudEvent::ChangeResolution(new_resolution) => { @@ -1170,7 +1175,7 @@ fn under_cursor( let cam_dist = cam_ray.0; // The ray hit something, is it within range? - let (build_pos, select_pos) = if matches!(cam_ray.1, Ok(Some(_)) if + let (build_pos, select_pos) = if matches!(cam_ray.1, Ok(Some(_)) if player_pos.distance_squared(cam_pos + cam_dir * cam_dist) <= MAX_PICKUP_RANGE_SQR) { diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index b953960231..bca5eec04e 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -1,7 +1,7 @@ use crate::{ hud::{BarNumbers, CrosshairType, Intro, PressBehavior, ShortcutNumbers, XpBar}, i18n, - render::{AaMode, CloudMode, FluidMode}, + render::RenderMode, ui::ScaleMode, window::{GameInput, KeyMouse}, }; @@ -616,14 +616,13 @@ pub struct GraphicsSettings { pub max_fps: u32, pub fov: u16, pub gamma: f32, - pub aa_mode: AaMode, - pub cloud_mode: CloudMode, - pub fluid_mode: FluidMode, + pub render_mode: RenderMode, pub resolution: [u16; 2], pub bit_depth: Option, pub refresh_rate: Option, pub window_size: [u16; 2], pub fullscreen: bool, + pub lod_detail: u32, } impl Default for GraphicsSettings { @@ -636,17 +635,17 @@ impl Default for GraphicsSettings { max_fps: 60, fov: 50, gamma: 1.0, - aa_mode: AaMode::Fxaa, - cloud_mode: CloudMode::Regular, - fluid_mode: FluidMode::Shiny, + render_mode: RenderMode::default(), resolution: [1920, 1080], bit_depth: None, refresh_rate: None, window_size: [1920, 1080], fullscreen: false, + lod_detail: 300, } } } + #[derive(Clone, Debug, Serialize, Deserialize)] pub enum AudioOutput { /// Veloren's audio system wont work on some systems, diff --git a/voxygen/src/ui/cache.rs b/voxygen/src/ui/cache.rs index e17017b9f5..fe2eaa13f5 100644 --- a/voxygen/src/ui/cache.rs +++ b/voxygen/src/ui/cache.rs @@ -1,18 +1,23 @@ use super::graphic::{Graphic, GraphicCache, Id as GraphicId}; use crate::{ - render::{Renderer, Texture}, + render::{Mesh, Renderer, Texture, UiPipeline}, Error, }; -use conrod_core::text::GlyphCache; +use conrod_core::{text::GlyphCache, widget::Id}; +use hashbrown::HashMap; use vek::*; // Multiplied by current window size const GLYPH_CACHE_SIZE: u16 = 1; // Glyph cache tolerances -const SCALE_TOLERANCE: f32 = 0.1; -const POSITION_TOLERANCE: f32 = 0.1; +const SCALE_TOLERANCE: f32 = 0.5; +const POSITION_TOLERANCE: f32 = 0.5; + +type TextCache = HashMap>; pub struct Cache { + // Map from text ids to their positioned glyphs. + text_cache: TextCache, glyph_cache: GlyphCache<'static>, glyph_cache_tex: Texture, graphic_cache: GraphicCache, @@ -26,9 +31,10 @@ impl Cache { let max_texture_size = renderer.max_texture_size(); let glyph_cache_dims = - Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512)); + Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); Ok(Self { + text_cache: Default::default(), glyph_cache: GlyphCache::builder() .dimensions(glyph_cache_dims.x as u32, glyph_cache_dims.y as u32) .scale_tolerance(SCALE_TOLERANCE) @@ -41,14 +47,24 @@ impl Cache { pub fn glyph_cache_tex(&self) -> &Texture { &self.glyph_cache_tex } - pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture) { - (&mut self.glyph_cache, &self.glyph_cache_tex) + pub fn cache_mut_and_tex( + &mut self, + ) -> ( + &mut GraphicCache, + &mut TextCache, + &mut GlyphCache<'static>, + &Texture, + ) { + ( + &mut self.graphic_cache, + &mut self.text_cache, + &mut self.glyph_cache, + &self.glyph_cache_tex, + ) } pub fn graphic_cache(&self) -> &GraphicCache { &self.graphic_cache } - pub fn graphic_cache_mut(&mut self) -> &mut GraphicCache { &mut self.graphic_cache } - pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId { self.graphic_cache.add_graphic(graphic) } @@ -57,17 +73,17 @@ impl Cache { self.graphic_cache.replace_graphic(id, graphic) } - // Resizes and clears the GraphicCache - pub fn resize_graphic_cache(&mut self, renderer: &mut Renderer) { + /// Resizes and clears the various caches. + /// + /// To be called when something like the scaling factor changes, + /// invalidating all existing cached UI state. + pub fn resize(&mut self, renderer: &mut Renderer) -> Result<(), Error> { self.graphic_cache.clear_cache(renderer); - } - - // Resizes and clears the GlyphCache - pub fn resize_glyph_cache(&mut self, renderer: &mut Renderer) -> Result<(), Error> { + self.text_cache.clear(); let max_texture_size = renderer.max_texture_size(); let cache_dims = renderer .get_resolution() - .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512)); + .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); self.glyph_cache = GlyphCache::builder() .dimensions(cache_dims.x as u32, cache_dims.y as u32) .scale_tolerance(SCALE_TOLERANCE) diff --git a/voxygen/src/ui/graphic/mod.rs b/voxygen/src/ui/graphic/mod.rs index aa696be782..171d4156ab 100644 --- a/voxygen/src/ui/graphic/mod.rs +++ b/voxygen/src/ui/graphic/mod.rs @@ -3,7 +3,7 @@ mod renderer; pub use renderer::{SampleStrat, Transform}; -use crate::render::{Renderer, Texture}; +use crate::render::{RenderError, Renderer, Texture}; use common::figure::Segment; use guillotiere::{size2, SimpleAtlasAllocator}; use hashbrown::{hash_map::Entry, HashMap}; @@ -15,7 +15,14 @@ use vek::*; #[derive(Clone)] pub enum Graphic { - Image(Arc), + /// NOTE: The second argument is an optional border color. If this is set, + /// we force the image into its own texture and use the border color + /// whenever we sample beyond the image extent. This can be useful, for + /// example, for the map and minimap, which both rotate and may be + /// non-square (meaning if we want to display the whole map and render to a + /// square, we may render out of bounds unless we perform proper + /// clipping). + Image(Arc, Option>), // Note: none of the users keep this Arc currently Voxel(Arc, Transform, SampleStrat), Blank, @@ -53,20 +60,73 @@ pub struct TexId(usize); type Parameters = (Id, Vec2); type GraphicMap = HashMap; -enum CacheLoc { +enum CachedDetails { Atlas { // Index of the atlas this is cached in atlas_idx: usize, + // Whether this texture is valid. + valid: bool, // Where in the cache texture this is aabr: Aabr, }, Texture { + // Index of the (unique, non-atlas) texture this is cached in. + index: usize, + // Whether this texture is valid. + valid: bool, + }, + Immutable { + // Index of the (unique, immutable, non-atlas) texture this is cached in. index: usize, }, } -struct CachedDetails { - location: CacheLoc, - valid: bool, + +impl CachedDetails { + /// Get information about this cache entry: texture index, + /// whether the entry is valid, and its bounding box in the referenced + /// texture. + fn info( + &self, + atlases: &[(SimpleAtlasAllocator, usize)], + dims: Vec2, + ) -> (usize, bool, Aabr) { + match *self { + CachedDetails::Atlas { + atlas_idx, + valid, + aabr, + } => (atlases[atlas_idx].1, valid, aabr), + CachedDetails::Texture { index, valid } => { + (index, valid, Aabr { + min: Vec2::zero(), + // Note texture should always match the cached dimensions + max: dims, + }) + }, + CachedDetails::Immutable { index } => { + (index, true, Aabr { + min: Vec2::zero(), + // Note texture should always match the cached dimensions + max: dims, + }) + }, + } + } + + /// Attempt to invalidate this cache entry. + pub fn invalidate(&mut self) -> Result<(), ()> { + match self { + Self::Atlas { ref mut valid, .. } => { + *valid = false; + Ok(()) + }, + Self::Texture { ref mut valid, .. } => { + *valid = false; + Ok(()) + }, + Self::Immutable { .. } => Err(()), + } + } } // Caches graphics, only deallocates when changing screen resolution (completely @@ -106,22 +166,18 @@ impl GraphicCache { } pub fn replace_graphic(&mut self, id: Id, graphic: Graphic) { - self.graphic_map.insert(id, graphic); + if self.graphic_map.insert(id, graphic).is_none() { + // This was not an update, so no need to search for keys. + return; + } // Remove from caches // Maybe make this more efficient if replace graphic is used more often - let uses = self - .cache_map - .keys() - .filter(|k| k.0 == id) - .copied() - .collect::>(); - for p in uses { - if let Some(details) = self.cache_map.get_mut(&p) { - // Reuse allocation - details.valid = false; - } - } + self.cache_map.retain(|&(key_id, _key_dims), details| { + // If the entry does not reference id, or it does but we can successfully + // invalidate, retain the entry; otherwise, discard this entry completely. + key_id != id || details.invalidate().is_ok() + }); } pub fn get_graphic(&self, id: Id) -> Option<&Graphic> { self.graphic_map.get(&id) } @@ -182,29 +238,31 @@ impl GraphicCache { // TODO: Verify rotation is being applied correctly. let transformed_aabr = |aabr| rotated_aabr(scaled_aabr(aabr)); - let details = match self.cache_map.entry(key) { + let Self { + textures, + atlases, + cache_map, + graphic_map, + .. + } = self; + + let details = match cache_map.entry(key) { Entry::Occupied(details) => { let details = details.get(); - let (idx, aabr) = match details.location { - CacheLoc::Atlas { - atlas_idx, aabr, .. - } => (self.atlases[atlas_idx].1, aabr), - CacheLoc::Texture { index } => { - (index, Aabr { - min: Vec2::new(0, 0), - // Note texture should always match the cached dimensions - max: dims, - }) - }, - }; + let (idx, valid, aabr) = details.info(atlases, dims); // Check if the cached version has been invalidated by replacing the underlying // graphic - if !details.valid { + if !valid { // Create image - let image = draw_graphic(&self.graphic_map, graphic_id, dims)?; + let (image, border) = draw_graphic(graphic_map, graphic_id, dims)?; + // If the cache location is invalid, we know the underlying texture is mutable, + // so we should be able to replace the graphic. However, we still want to make + // sure that we are not reusing textures for images that specify a border + // color. + assert!(border.is_none()); // Transfer to the gpu - upload_image(renderer, aabr, &self.textures[idx], &image); + upload_image(renderer, aabr, &textures[idx], &image); } return Some((transformed_aabr(aabr.map(|e| e as f64)), TexId(idx))); @@ -212,24 +270,39 @@ impl GraphicCache { Entry::Vacant(details) => details, }; - // Create image - let image = draw_graphic(&self.graphic_map, graphic_id, dims)?; + // Construct image + let (image, border_color) = draw_graphic(graphic_map, graphic_id, dims)?; + + // Upload + let atlas_size = atlas_size(renderer); // Allocate space on the gpu // Check size of graphic // Graphics over a particular size are sent to their own textures - let location = if Vec2::::from(self.atlases[0].0.size().to_tuple()) - .map(|e| e as u16) + let location = if let Some(border_color) = border_color { + // Create a new immutable texture. + let texture = create_image(renderer, image, border_color).unwrap(); + // NOTE: All mutations happen only after the upload succeeds! + let index = textures.len(); + textures.push(texture); + CachedDetails::Immutable { index } + } else if atlas_size .map2(dims, |a, d| a as f32 * ATLAS_CUTTOFF_FRAC >= d as f32) .reduce_and() { // Fit into an atlas let mut loc = None; - for (atlas_idx, (ref mut atlas, _)) in self.atlases.iter_mut().enumerate() { + for (atlas_idx, &mut (ref mut atlas, texture_idx)) in atlases.iter_mut().enumerate() { + let dims = dims.map(|e| e.max(1)); if let Some(rectangle) = atlas.allocate(size2(i32::from(dims.x), i32::from(dims.y))) { let aabr = aabr_from_alloc_rect(rectangle); - loc = Some(CacheLoc::Atlas { atlas_idx, aabr }); + loc = Some(CachedDetails::Atlas { + atlas_idx, + valid: true, + aabr, + }); + upload_image(renderer, aabr, &textures[texture_idx], &image); break; } } @@ -239,65 +312,70 @@ impl GraphicCache { // Create a new atlas None => { let (mut atlas, texture) = create_atlas_texture(renderer); + let dims = dims.map(|e| e.max(1)); let aabr = atlas .allocate(size2(i32::from(dims.x), i32::from(dims.y))) .map(aabr_from_alloc_rect) .unwrap(); - let tex_idx = self.textures.len(); - let atlas_idx = self.atlases.len(); - self.textures.push(texture); - self.atlases.push((atlas, tex_idx)); - CacheLoc::Atlas { atlas_idx, aabr } + // NOTE: All mutations happen only after the texture creation succeeds! + let tex_idx = textures.len(); + let atlas_idx = atlases.len(); + textures.push(texture); + atlases.push((atlas, tex_idx)); + upload_image(renderer, aabr, &textures[tex_idx], &image); + CachedDetails::Atlas { + atlas_idx, + valid: true, + aabr, + } }, } } else { // Create a texture just for this let texture = renderer.create_dynamic_texture(dims).unwrap(); - let index = self.textures.len(); - self.textures.push(texture); - CacheLoc::Texture { index } - }; - - let (idx, aabr) = match location { - CacheLoc::Atlas { - atlas_idx, aabr, .. - } => (self.atlases[atlas_idx].1, aabr), - CacheLoc::Texture { index } => { - (index, Aabr { - min: Vec2::new(0, 0), + // NOTE: All mutations happen only after the texture creation succeeds! + let index = textures.len(); + textures.push(texture); + upload_image( + renderer, + Aabr { + min: Vec2::zero(), // Note texture should always match the cached dimensions max: dims, - }) - }, + }, + &textures[index], + &image, + ); + CachedDetails::Texture { index, valid: true } }; - // Upload - upload_image(renderer, aabr, &self.textures[idx], &image); + + // Extract information from cache entry. + let (idx, _, aabr) = location.info(atlases, dims); + // Insert into cached map - details.insert(CachedDetails { - location, - valid: true, - }); + details.insert(location); Some((transformed_aabr(aabr.map(|e| e as f64)), TexId(idx))) } } // Draw a graphic at the specified dimensions -fn draw_graphic(graphic_map: &GraphicMap, graphic_id: Id, dims: Vec2) -> Option { +fn draw_graphic( + graphic_map: &GraphicMap, + graphic_id: Id, + dims: Vec2, +) -> Option<(RgbaImage, Option>)> { match graphic_map.get(&graphic_id) { Some(Graphic::Blank) => None, // Render image at requested resolution // TODO: Use source aabr. - Some(Graphic::Image(ref image)) => Some(resize_pixel_art( - &image.to_rgba(), - u32::from(dims.x), - u32::from(dims.y), + Some(&Graphic::Image(ref image, border_color)) => Some(( + resize_pixel_art(&image.to_rgba(), u32::from(dims.x), u32::from(dims.y)), + border_color, )), - Some(Graphic::Voxel(ref segment, trans, sample_strat)) => Some(renderer::draw_vox( - &segment, - dims, - trans.clone(), - *sample_strat, + Some(Graphic::Voxel(ref segment, trans, sample_strat)) => Some(( + renderer::draw_vox(&segment, dims, trans.clone(), *sample_strat), + None, )), None => { warn!( @@ -309,17 +387,18 @@ fn draw_graphic(graphic_map: &GraphicMap, graphic_id: Id, dims: Vec2) -> Op } } -fn create_atlas_texture(renderer: &mut Renderer) -> (SimpleAtlasAllocator, Texture) { - let (w, h) = renderer.get_resolution().into_tuple(); - +fn atlas_size(renderer: &Renderer) -> Vec2 { let max_texture_size = renderer.max_texture_size(); - let size = Vec2::new(w, h).map(|e| { + renderer.get_resolution().map(|e| { (e * GRAPHIC_CACHE_RELATIVE_SIZE) .max(512) - .min(max_texture_size as u16) - }); + .min(max_texture_size) + }) +} +fn create_atlas_texture(renderer: &mut Renderer) -> (SimpleAtlasAllocator, Texture) { + let size = atlas_size(renderer); let atlas = SimpleAtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))); let texture = renderer.create_dynamic_texture(size).unwrap(); (atlas, texture) @@ -340,8 +419,23 @@ fn upload_image(renderer: &mut Renderer, aabr: Aabr, tex: &Texture, image: tex, offset, size, - &image.pixels().map(|p| p.0).collect::>(), + // NOTE: Rgba texture, so each pixel is 4 bytes, ergo this cannot fail. + // We make the cast parameters explicit for clarity. + gfx::memory::cast_slice::(&image), ) { warn!(?e, "Failed to update texture"); } } + +fn create_image( + renderer: &mut Renderer, + image: RgbaImage, + border_color: Rgba, +) -> Result { + renderer.create_texture( + &DynamicImage::ImageRgba8(image), + None, + Some(gfx::texture::WrapMode::Border), + Some(border_color.into_array().into()), + ) +} diff --git a/voxygen/src/ui/graphic/pixel_art.rs b/voxygen/src/ui/graphic/pixel_art.rs index f3994b4375..fa32f569fd 100644 --- a/voxygen/src/ui/graphic/pixel_art.rs +++ b/voxygen/src/ui/graphic/pixel_art.rs @@ -47,7 +47,7 @@ pub fn resize_pixel_art(image: &RgbaImage, new_width: u32, new_height: u32) -> R for x in 0..new_width { // Calculate sampling strategy let xsmin = x as f32 * wratio; - let xsmax = xsmin + wratio; + let xsmax = (xsmin + wratio).min(width as f32); // Min and max pixels covered let xminp = xsmin.floor() as u32; let xmaxp = ((xsmax - EPSILON).ceil() as u32) @@ -63,7 +63,7 @@ pub fn resize_pixel_art(image: &RgbaImage, new_width: u32, new_height: u32) -> R for y in 0..new_height { // Calculate sampling strategy let ysmin = y as f32 * hratio; - let ysmax = ysmin + hratio; + let ysmax = (ysmin + hratio).min(height as f32); // Min and max of pixels covered let yminp = ysmin.floor() as u32; let ymaxp = ((ysmax - EPSILON).ceil() as u32) diff --git a/voxygen/src/ui/graphic/renderer.rs b/voxygen/src/ui/graphic/renderer.rs index 697a17a0f3..a456c8936b 100644 --- a/voxygen/src/ui/graphic/renderer.rs +++ b/voxygen/src/ui/graphic/renderer.rs @@ -199,7 +199,7 @@ pub fn draw_vox( .resize_exact( output_size.x as u32, output_size.y as u32, - image::FilterType::Triangle, + image::imageops::FilterType::Triangle, ) .to_rgba(), SampleStrat::PixelCoverage => super::pixel_art::resize_pixel_art( diff --git a/voxygen/src/ui/img_ids.rs b/voxygen/src/ui/img_ids.rs index 2e64e904f7..e0b8ec3a37 100644 --- a/voxygen/src/ui/img_ids.rs +++ b/voxygen/src/ui/img_ids.rs @@ -24,7 +24,7 @@ impl<'a> GraphicCreator<'a> for ImageGraphic { type Specifier = &'a str; fn new_graphic(specifier: Self::Specifier) -> Result { - Ok(Graphic::Image(load::(specifier)?)) + Ok(Graphic::Image(load::(specifier)?, None)) } } diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index d6eb8ae472..92743c625a 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -35,20 +35,20 @@ use cache::Cache; use common::{assets, util::srgba_to_linear}; use conrod_core::{ event::Input, - graph::Graph, + graph::{self, Graph}, image::{self, Map}, input::{touch::Touch, Motion, Widget}, render::{Primitive, PrimitiveKind}, text::{self, font}, widget::{self, id::Generator}, - Rect, UiBuilder, UiCell, + Rect, Scalar, UiBuilder, UiCell, }; +use core::{convert::TryInto, f32, f64, ops::Range}; use graphic::{Rotation, TexId}; +use hashbrown::hash_map::Entry; use std::{ - f32, f64, fs::File, io::{BufReader, Read}, - ops::Range, sync::Arc, time::Duration, }; @@ -66,7 +66,7 @@ enum DrawKind { Plain, } enum DrawCommand { - Draw { kind: DrawKind, verts: Range }, + Draw { kind: DrawKind, verts: Range }, Scissor(Aabr), WorldPos(Option), } @@ -74,14 +74,28 @@ impl DrawCommand { fn image(verts: Range, id: TexId) -> DrawCommand { DrawCommand::Draw { kind: DrawKind::Image(id), - verts, + verts: verts + .start + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!") + ..verts + .end + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!"), } } fn plain(verts: Range) -> DrawCommand { DrawCommand::Draw { kind: DrawKind::Plain, - verts, + verts: verts + .start + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!") + ..verts + .end + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!"), } } } @@ -103,6 +117,9 @@ pub struct Ui { cache: Cache, // Draw commands for the next render draw_commands: Vec, + // Mesh buffer for UI vertics; we reuse its allocation in order to limit vector reallocations + // during redrawing. + mesh: Mesh, // Model for drawing the ui model: DynamicModel, // Consts for default ui drawing position (ie the interface) @@ -128,6 +145,10 @@ impl Ui { let renderer = window.renderer_mut(); let mut ui = UiBuilder::new(win_dims).build(); + // NOTE: Since we redraw the actual frame each time whether or not the UI needs + // to be updated, there's no reason to set the redraw count higher than + // 1. + ui.set_num_redraw_frames(1); let tooltip_manager = TooltipManager::new( ui.widget_id_generator(), Duration::from_millis(1), @@ -140,6 +161,7 @@ impl Ui { image_map: Map::new(), cache: Cache::new(renderer)?, draw_commands: Vec::new(), + mesh: Mesh::new(), model: renderer.create_dynamic_model(100)?, interface_locals: renderer.create_consts(&[UiLocals::default()])?, default_globals: renderer.create_consts(&[Globals::default()])?, @@ -274,23 +296,55 @@ impl Ui { self.tooltip_manager .maintain(self.ui.global_input(), self.scale.scale_factor_logical()); - // Regenerate draw commands and associated models only if the ui changed - let mut primitives = match self.ui.draw_if_changed() { - Some(primitives) => primitives, - None => return, - }; + // Handle window resizing. + if let Some(new_dims) = self.window_resized.take() { + let (old_w, old_h) = self.scale.scaled_window_size().into_tuple(); + self.scale.window_resized(new_dims, renderer); + let (w, h) = self.scale.scaled_window_size().into_tuple(); + self.ui.handle_event(Input::Resize(w, h)); + + // Avoid panic in graphic cache when minimizing. + // Avoid resetting cache if window size didn't change + // Somewhat inefficient for elements that won't change size after a window + // resize + let res = renderer.get_resolution(); + self.need_cache_resize = res.x > 0 && res.y > 0 && !(old_w == w && old_h == h); + } if self.need_cache_resize { // Resize graphic cache - self.cache.resize_graphic_cache(renderer); - // Resize glyph cache - self.cache.resize_glyph_cache(renderer).unwrap(); + // FIXME: Handle errors here. + self.cache.resize(renderer).unwrap(); self.need_cache_resize = false; } - self.draw_commands.clear(); - let mut mesh = Mesh::new(); + let mut retry = false; + self.maintain_internal(renderer, view_projection_mat, &mut retry); + if retry { + // Update the glyph cache and try again. + self.maintain_internal(renderer, view_projection_mat, &mut retry); + } + } + + fn maintain_internal( + &mut self, + renderer: &mut Renderer, + view_projection_mat: Option>, + retry: &mut bool, + ) { + let (graphic_cache, text_cache, glyph_cache, cache_tex) = self.cache.cache_mut_and_tex(); + + let mut primitives = if *retry { + // If this is a retry, always redraw. + self.ui.draw() + } else { + // Otherwise, redraw only if widgets were actually updated. + match self.ui.draw_if_changed() { + Some(primitives) => primitives, + None => return, + } + }; let (half_res, x_align, y_align) = { let res = renderer.get_resolution(); @@ -301,6 +355,215 @@ impl Ui { ) }; + let ui = &self.ui; + let p_scale_factor = self.scale.scale_factor_physical(); + // Functions for converting for conrod scalar coords to GL vertex coords (-1.0 + // to 1.0). + let (ui_win_w, ui_win_h) = (ui.win_w, ui.win_h); + let vx = |x: f64| (x / ui_win_w * 2.0) as f32; + let vy = |y: f64| (y / ui_win_h * 2.0) as f32; + let gl_aabr = |rect: Rect| { + let (l, r, b, t) = rect.l_r_b_t(); + let min = Vec2::new( + ((vx(l) * half_res.x + x_align).round() - x_align) / half_res.x, + ((vy(b) * half_res.y + y_align).round() - y_align) / half_res.y, + ); + let max = Vec2::new( + ((vx(r) * half_res.x + x_align).round() - x_align) / half_res.x, + ((vy(t) * half_res.y + y_align).round() - y_align) / half_res.y, + ); + Aabr { min, max } + }; + + // let window_dim = ui.window_dim(); + let theme = &ui.theme; + let widget_graph = ui.widget_graph(); + let fonts = &ui.fonts; + let dpi_factor = p_scale_factor as f32; + + // We can use information about whether a widget was actually updated to more + // easily track cache invalidations. + let updated_widgets = ui.updated_widgets(); + let mut glyph_missing = false; + + updated_widgets.iter() + // Filter out widgets that are either: + // - not text primitives, or + // - are text primitives, but were both already in the cache, and not updated this + // frame. + // + // The reason the second condition is so complicated is that we want to handle cases + // where we cleared the whole cache, which can result in glyphs from text updated in a + // previous frame not being present in the text cache. + .filter_map(|(&widget_id, updated)| { + widget_graph.widget(widget_id) + .and_then(|widget| Some((widget.rect, widget.unique_widget_state::()?))) + .and_then(|(rect, text)| { + // NOTE: This fallback is weird and probably shouldn't exist. + let font_id = text.style.font_id(theme)/*.or_else(|| fonts.ids().next())*/?; + let font = fonts.get(font_id)?; + + Some((widget_id, updated, rect, text, font_id, font)) + }) + }) + // Recache the entry. + .for_each(|(widget_id, updated, rect, graph::UniqueWidgetState { state, style }, font_id, font)| { + let entry = match text_cache.entry(widget_id) { + Entry::Occupied(_) if !updated => return, + entry => entry, + }; + + // Retrieve styling. + let color = style.color(theme); + let font_size = style.font_size(theme); + let line_spacing = style.line_spacing(theme); + let justify = style.justify(theme); + let y_align = conrod_core::position::Align::End; + + let text = &state.string; + let line_infos = &state.line_infos; + + // Convert conrod coordinates to pixel coordinates. + let trans_x = |x: Scalar| (x + ui_win_w / 2.0) * dpi_factor as Scalar; + let trans_y = |y: Scalar| ((-y) + ui_win_h / 2.0) * dpi_factor as Scalar; + + // Produce the text layout iterators. + let lines = line_infos.iter().map(|info| &text[info.byte_range()]); + let line_rects = text::line::rects(line_infos.iter(), font_size, rect, + justify, y_align, line_spacing); + + // Grab the positioned glyphs from the text primitives. + let scale = text::f32_pt_to_scale(font_size as f32 * dpi_factor); + let positioned_glyphs = lines.zip(line_rects).flat_map(|(line, line_rect)| { + let (x, y) = (trans_x(line_rect.left()) as f32, trans_y(line_rect.bottom()) as f32); + let point = text::rt::Point { x, y }; + font.layout(line, scale, point) + }); + + // Reuse the mesh allocation if possible at this entry if possible; we + // then clear it to avoid using stale entries. + let mesh = entry.or_insert_with(Mesh::new); + mesh.clear(); + + let color = srgba_to_linear(color.to_fsa().into()); + + positioned_glyphs.for_each(|g| { + match glyph_cache.rect_for(font_id.index(), &g) { + Ok(Some((uv_rect, screen_rect))) => { + let uv = Aabr { + min: Vec2::new(uv_rect.min.x, uv_rect.max.y), + max: Vec2::new(uv_rect.max.x, uv_rect.min.y), + }; + let rect = Aabr { + min: Vec2::new( + vx(screen_rect.min.x as f64 / p_scale_factor + - ui.win_w / 2.0), + vy(ui.win_h / 2.0 + - screen_rect.max.y as f64 / p_scale_factor), + ), + max: Vec2::new( + vx(screen_rect.max.x as f64 / p_scale_factor + - ui.win_w / 2.0), + vy(ui.win_h / 2.0 + - screen_rect.min.y as f64 / p_scale_factor), + ), + }; + mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text)); + }, + // Glyph not found, no-op. + Ok(None) => {}, + // Glyph was found, but was not yet cached; we'll need to add it to the + // cache and retry. + Err(_) => { + // Queue the unknown glyph to be cached. + glyph_missing = true; + } + } + + // NOTE: Important to do this for *all* glyphs to try to make sure that + // any that are uncached are part of the graph. Because we always + // clear the entire cache whenever a new glyph is encountered, by + // adding and checking all glyphs as they come in we guarantee that (1) + // any glyphs in the text cache are in the queue, and (2) any glyphs + // not in the text cache are either in the glyph cache, or (during our + // processing here) we set the retry flag to force a glyph cache + // update. Setting the flag causes all glyphs in the current queue to + // become part of the glyph cache during the second call to + // `maintain_internal`, so as long as the cache refresh succeeded, + // during the second call all glyphs will hit this branch as desired. + glyph_cache.queue_glyph(font_id.index(), g); + }); + }); + + if glyph_missing { + if *retry { + // If a glyph was missing and this was our second try, we know something was + // messed up during the glyph_cache redraw. It is possible that + // the queue contained unneeded glyphs, so we don't necessarily + // have to give up; a more precise enumeration of the + // glyphs required to render this frame might work out. However, this is a + // pretty remote edge case, so we opt to not care about this + // frame (we skip rendering it, basically), and just clear the + // text cache and glyph queue; next frame will then + // start out with an empty slate, and therefore will enqueue precisely the + // glyphs needed for that frame. If *that* frame fails, we're + // in bigger trouble. + text_cache.clear(); + glyph_cache.clear(); + glyph_cache.clear_queue(); + self.ui.needs_redraw(); + warn!("Could not recache queued glyphs, skipping frame."); + } else { + // NOTE: If this is the first round after encountering a new glyph, we just + // refresh the whole glyph cache. Technically this is not necessary since + // positioned_glyphs should still be accurate, but it's just the easiest way + // to ensure that all glyph positions get updated. It also helps keep the glyph + // cache reasonable by making sure any glyphs that subsequently get rendered are + // actually in the cache, including glyphs that were mapped to ids but didn't + // happen to be rendered on the frame where the cache was + // refreshed. + text_cache.clear(); + tracing::debug!("Updating glyphs and clearing text cache."); + + if let Err(err) = glyph_cache.cache_queued(|rect, data| { + let offset = [rect.min.x as u16, rect.min.y as u16]; + let size = [rect.width() as u16, rect.height() as u16]; + + let new_data = data + .iter() + .map(|x| [255, 255, 255, *x]) + .collect::>(); + + if let Err(err) = renderer.update_texture(cache_tex, offset, size, &new_data) { + warn!("Failed to update texture: {:?}", err); + } + }) { + // FIXME: If we actually hit this error, it's still possible we could salvage + // things in various ways (for instance, the current queue might have extra + // stuff in it, so we could try calling maintain_internal a + // third time with a fully clean queue; or we could try to + // increase the glyph texture size, etc. But hopefully + // we will not actually encounter this error. + warn!("Failed to cache queued glyphs: {:?}", err); + + // Clear queued glyphs, so that (hopefully) next time we won't have the + // offending glyph or glyph set. We then exit the loop and don't try to + // rerender the frame. + glyph_cache.clear_queue(); + self.ui.needs_redraw(); + } else { + // Successfully cached, so repeat the loop. + *retry = true; + } + } + + return; + } + + self.draw_commands.clear(); + let mesh = &mut self.mesh; + mesh.clear(); + enum State { Image(TexId), Plain, @@ -321,7 +584,6 @@ impl Ui { }; let mut placement = Placement::Interface; - let p_scale_factor = self.scale.scale_factor_physical(); // Switches to the `Plain` state and completes the previous `Command` if not // already in the `Plain` state. @@ -341,9 +603,8 @@ impl Ui { kind, scizzor, rect, - .. + id: widget_id, } = prim; - // Check for a change in the scissor. let new_scissor = { let (l, b, w, h) = scizzor.l_b_w_h(); @@ -351,8 +612,8 @@ impl Ui { // Calculate minimum x and y coordinates while // flipping y axis (from +up to +down) and // moving origin to top-left corner (from middle). - let min_x = self.ui.win_w / 2.0 + l; - let min_y = self.ui.win_h / 2.0 - b - h; + let min_x = ui.win_w / 2.0 + l; + let min_y = ui.win_h / 2.0 - b - h; let intersection = Aabr { min: Vec2 { x: (min_x * scale_factor) as u16, @@ -415,24 +676,6 @@ impl Ui { Placement::Interface => {}, } - // Functions for converting for conrod scalar coords to GL vertex coords (-1.0 - // to 1.0). - let (ui_win_w, ui_win_h) = (self.ui.win_w, self.ui.win_h); - let vx = |x: f64| (x / ui_win_w * 2.0) as f32; - let vy = |y: f64| (y / ui_win_h * 2.0) as f32; - let gl_aabr = |rect: Rect| { - let (l, r, b, t) = rect.l_r_b_t(); - let min = Vec2::new( - ((vx(l) * half_res.x + x_align).round() - x_align) / half_res.x, - ((vy(b) * half_res.y + y_align).round() - y_align) / half_res.y, - ); - let max = Vec2::new( - ((vx(r) * half_res.x + x_align).round() - x_align) / half_res.x, - ((vy(t) * half_res.y + y_align).round() - y_align) / half_res.y, - ); - Aabr { min, max } - }; - match kind { PrimitiveKind::Image { image_id, @@ -443,7 +686,6 @@ impl Ui { .image_map .get(&image_id) .expect("Image does not exist in image map"); - let graphic_cache = self.cache.graphic_cache_mut(); let gl_aabr = gl_aabr(rect); let (source_aabr, gl_size) = { // Transform the source rectangle into uv coordinate. @@ -451,7 +693,7 @@ impl Ui { let ((uv_l, uv_r, uv_b, uv_t), gl_size) = match graphic_cache.get_graphic(*graphic_id) { Some(Graphic::Blank) | None => continue, - Some(Graphic::Image(image)) => { + Some(Graphic::Image(image, ..)) => { source_rect.and_then(|src_rect| { let (image_w, image_h) = image.dimensions(); let (source_w, source_h) = src_rect.w_h(); @@ -573,67 +815,16 @@ impl Ui { Rotation::TargetNorth => UiMode::ImageTargetNorth, })); }, - PrimitiveKind::Text { - color, - text, - font_id, - } => { + PrimitiveKind::Text { .. } => { switch_to_plain_state!(); - let positioned_glyphs = text.positioned_glyphs(p_scale_factor as f32); - let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex(); - // Queue the glyphs to be cached. - for glyph in positioned_glyphs { - glyph_cache.queue_glyph(font_id.index(), glyph.clone()); - } - - if let Err(err) = glyph_cache.cache_queued(|rect, data| { - let offset = [rect.min.x as u16, rect.min.y as u16]; - let size = [rect.width() as u16, rect.height() as u16]; - - let new_data = data - .iter() - .map(|x| [255, 255, 255, *x]) - .collect::>(); - - if let Err(err) = - renderer.update_texture(cache_tex, offset, size, &new_data) - { - warn!("Failed to update texture: {:?}", err); - } - }) { - warn!("Failed to cache queued glyphs: {:?}", err); - // Clear uncachable glyphs from the queue - glyph_cache.clear_queue(); - } - - let color = srgba_to_linear(color.to_fsa().into()); - - for g in positioned_glyphs { - if let Ok(Some((uv_rect, screen_rect))) = - glyph_cache.rect_for(font_id.index(), g) - { - let uv = Aabr { - min: Vec2::new(uv_rect.min.x, uv_rect.max.y), - max: Vec2::new(uv_rect.max.x, uv_rect.min.y), - }; - let rect = Aabr { - min: Vec2::new( - vx(screen_rect.min.x as f64 / p_scale_factor - - self.ui.win_w / 2.0), - vy(self.ui.win_h / 2.0 - - screen_rect.max.y as f64 / p_scale_factor), - ), - max: Vec2::new( - vx(screen_rect.max.x as f64 / p_scale_factor - - self.ui.win_w / 2.0), - vy(self.ui.win_h / 2.0 - - screen_rect.min.y as f64 / p_scale_factor), - ), - }; - mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text)); - } - } + // Mesh should already be cached. + mesh.push_mesh( + text_cache + .get(&widget_id) + .as_deref() + .unwrap_or(&Mesh::new()), + ); }, PrimitiveKind::Rectangle { color } => { let color = srgba_to_linear(color.to_fsa().into()); @@ -755,8 +946,8 @@ impl Ui { State::Image(id) => DrawCommand::image(start..mesh.vertices().len(), id), }); - // Draw glyph cache (use for debugging). - /*self.draw_commands + /* // Draw glyph cache (use for debugging). + self.draw_commands .push(DrawCommand::Scissor(default_scissor(renderer))); start = mesh.vertices().len(); mesh.push_quad(create_ui_quad( @@ -772,32 +963,17 @@ impl Ui { UiMode::Text, )); self.draw_commands - .push(DrawCommand::plain(start..mesh.vertices().len()));*/ + .push(DrawCommand::plain(start..mesh.vertices().len())); */ // Create a larger dynamic model if the mesh is larger than the current model // size. - if self.model.vbuf.len() < mesh.vertices().len() { + if self.model.vbuf.len() < self.mesh.vertices().len() { self.model = renderer - .create_dynamic_model(mesh.vertices().len() * 4 / 3) + .create_dynamic_model(self.mesh.vertices().len() * 4 / 3) .unwrap(); } // Update model with new mesh. - renderer.update_model(&self.model, &mesh, 0).unwrap(); - - // Handle window resizing. - if let Some(new_dims) = self.window_resized.take() { - let (old_w, old_h) = self.scale.scaled_window_size().into_tuple(); - self.scale.window_resized(new_dims, renderer); - let (w, h) = self.scale.scaled_window_size().into_tuple(); - self.ui.handle_event(Input::Resize(w, h)); - - // Avoid panic in graphic cache when minimizing. - // Avoid resetting cache if window size didn't change - // Somewhat inefficient for elements that won't change size after a window - // resize - let res = renderer.get_resolution(); - self.need_cache_resize = res.x > 0 && res.y > 0 && !(old_w == w && old_h == h); - } + renderer.update_model(&self.model, &self.mesh, 0).unwrap(); } pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts>) { @@ -818,7 +994,7 @@ impl Ui { DrawKind::Plain => self.cache.glyph_cache_tex(), }; let model = self.model.submodel(verts.clone()); - renderer.render_ui_element(&model, tex, scissor, globals, locals); + renderer.render_ui_element(model, tex, scissor, globals, locals); }, } } diff --git a/voxygen/src/ui/widgets/ingame.rs b/voxygen/src/ui/widgets/ingame.rs index e8f5ccf49f..957b049343 100644 --- a/voxygen/src/ui/widgets/ingame.rs +++ b/voxygen/src/ui/widgets/ingame.rs @@ -54,7 +54,7 @@ pub struct IngameParameters { } pub struct State { - id: Option, + id: widget::Id, pub parameters: IngameParameters, } @@ -78,7 +78,7 @@ impl Widget for Ingame { fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State { State { - id: Some(id_gen.next()), + id: id_gen.next(), parameters: IngameParameters { num: self.prim_num, pos: self.pos, @@ -112,7 +112,7 @@ impl Widget for Ingame { }); } - widget.set_ingame(state.id.unwrap(), ui) + widget.set_ingame(state.id, ui) } fn default_x_position(&self, _: &Ui) -> Position { Position::Absolute(0.0) } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 916281e36f..2a53eb5328 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -529,7 +529,7 @@ impl Window { let (window, device, factory, win_color_view, win_depth_view) = glutin::ContextBuilder::new() - .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) + .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 3))) .with_vsync(false) .with_gfx_color_depth::() .build_windowed(win_builder, &event_loop) @@ -585,9 +585,7 @@ impl Window { factory, win_color_view, win_depth_view, - settings.graphics.aa_mode, - settings.graphics.cloud_mode, - settings.graphics.fluid_mode, + settings.graphics.render_mode.clone(), )?, window, cursor_grabbed: false, diff --git a/world/Cargo.toml b/world/Cargo.toml index e2d3cb682e..dbee670446 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -9,9 +9,9 @@ bincode = "1.2.0" common = { package = "veloren-common", path = "../common" } bitvec = "0.17.4" fxhash = "0.2.1" -image = { version = "0.22.5", default-features = false, features = ["png"] } +image = { version = "0.23.8", default-features = false, features = ["png"] } itertools = "0.9" -vek = "0.11.0" +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } noise = { version = "0.6.0", default-features = false } num = "0.2" ordered-float = "1.0" @@ -23,10 +23,10 @@ rand_chacha = "0.2.1" arr_macro = "0.1.2" packed_simd = "0.3.3" rayon = "^1.3.0" -roots = "0.0.5" serde = { version = "1.0.110", features = ["derive"] } ron = { version = "0.6", default-features = false } [dev-dependencies] +criterion = "0.3" tracing-subscriber = { version = "0.2.3", default-features = false, features = ["fmt", "chrono", "ansi", "smallvec"] } minifb = "0.14.0" diff --git a/world/examples/settlement_viewer.rs b/world/examples/settlement_viewer.rs index 8c11222241..9813e7ca88 100644 --- a/world/examples/settlement_viewer.rs +++ b/world/examples/settlement_viewer.rs @@ -1,12 +1,15 @@ use rand::thread_rng; use vek::*; -use veloren_world::site::Settlement; +use veloren_world::{index::Index, site::Settlement, IndexRef}; const W: usize = 640; const H: usize = 480; #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn main() { + let seed = 1337; + let (ref index, ref colors) = Index::new(seed); + let mut win = minifb::Window::new("Settlement Viewer", W, H, minifb::WindowOptions::default()).unwrap(); @@ -14,6 +17,7 @@ fn main() { let mut focus = Vec2::::zero(); let mut zoom = 1.0; + let index = IndexRef { colors, index }; while win.is_open() { let mut buf = vec![0; W * H]; @@ -26,7 +30,7 @@ fn main() { let pos = focus + win_to_pos(Vec2::new(i, j)) * zoom; let color = settlement - .get_color(pos.map(|e| e.floor() as i32)) + .get_color(index, pos.map(|e| e.floor() as i32)) .unwrap_or(Rgb::new(35, 50, 20)); buf[j * W + i] = u32::from_le_bytes([color.b, color.g, color.r, 255]); diff --git a/world/examples/view.rs b/world/examples/view.rs index b137e8abbe..a8e5db830e 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -6,11 +6,13 @@ const W: usize = 640; const H: usize = 480; fn main() { - let world = World::generate(0, WorldOpts { + let (world, index) = World::generate(0, WorldOpts { seed_elements: true, ..WorldOpts::default() }); + let index = index.as_index_ref(); + let sampler = world.sample_columns(); let mut win = @@ -28,7 +30,7 @@ fn main() { let pos = focus + Vec2::new(i as i32, j as i32) * scale; let (alt, place) = sampler - .get((pos, world.index())) + .get((pos, index)) .map(|sample| { ( sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8, diff --git a/world/examples/water.rs b/world/examples/water.rs index 36303763c5..cba3bc7250 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -1,11 +1,19 @@ -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + terrain::{ + map::{MapConfig, MapDebug, MapSample}, + uniform_idx_as_vec2, vec2_as_uniform_idx, TerrainChunkSize, + }, + vol::RectVolSize, +}; +use rayon::prelude::*; use std::{f64, io::Write, path::PathBuf, time::SystemTime}; -use tracing::warn; -use tracing_subscriber; +use tracing::{warn, Level}; +use tracing_subscriber::{filter::LevelFilter, EnvFilter, FmtSubscriber}; use vek::*; use veloren_world::{ - sim::{self, MapConfig, MapDebug, WorldOpts, WORLD_SIZE}, - World, CONFIG, + sim::{self, get_horizon_map, sample_pos, sample_wpos, WorldOpts}, + util::Sampler, + ColumnSample, World, CONFIG, }; const W: usize = 1024; @@ -14,7 +22,10 @@ const H: usize = 1024; #[allow(clippy::needless_update)] // TODO: Pending review in #587 #[allow(clippy::unused_io_amount)] // TODO: Pending review in #587 fn main() { - tracing_subscriber::fmt::init(); + FmtSubscriber::builder() + .with_max_level(Level::ERROR) + .with_env_filter(EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into())) + .init(); // To load a map file of your choice, replace map_file with the name of your map // (stored locally in the map directory of your Veloren root), and swap the @@ -22,29 +33,110 @@ fn main() { let map_file = // "map_1575990726223.bin"; // "map_1575987666972.bin"; - "map_1585335358316.bin"; - let mut map_path = PathBuf::from("./maps"); - map_path.push(map_file); + // "map_1576046079066.bin"; + // "maps/071090_2x.bin"; + "map_1579539133272.bin"; + let mut _map_file = PathBuf::from("./maps"); + _map_file.push(map_file); - let world = World::generate(5284, WorldOpts { + let (world, index) = World::generate(5284, WorldOpts { seed_elements: false, - //world_file: sim::FileOpts::Load(map_path), - world_file: sim::FileOpts::Save, + world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()), + // world_file: sim::FileOpts::Load(_map_file), + // world_file: sim::FileOpts::Save, ..WorldOpts::default() }); + let index = index.as_index_ref(); + tracing::info!("Sampling data..."); + let sampler = world.sim(); + let map_size_lg = sampler.map_size_lg(); + + let samples_data = { + let column_sample = world.sample_columns(); + (0..map_size_lg.chunks_len()) + .into_par_iter() + .map(|posi| { + column_sample.get(( + uniform_idx_as_vec2(map_size_lg, posi) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + index, + )) + }) + .collect::>() + .into_boxed_slice() + }; + let refresh_map_samples = |config: &MapConfig, samples: Option<&[Option]>| { + (0..map_size_lg.chunks_len()) + .into_par_iter() + .map(|posi| { + sample_pos( + config, + sampler, + index, + samples, + uniform_idx_as_vec2(map_size_lg, posi), + ) + }) + .collect::>() + .into_boxed_slice() + }; + let get_map_sample = |map_samples: &[MapSample], pos: Vec2| { + if pos.reduce_partial_min() >= 0 + && pos.x < map_size_lg.chunks().x as i32 + && pos.y < map_size_lg.chunks().y as i32 + { + map_samples[vec2_as_uniform_idx(map_size_lg, pos)].clone() + } else { + MapSample { + alt: 0.0, + rgb: Rgb::new(0, 0, 0), + connections: None, + downhill_wpos: (pos + 1) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + } + } + }; + + let refresh_horizons = |is_basement, is_water| { + get_horizon_map( + map_size_lg, + Aabr { + min: Vec2::zero(), + max: map_size_lg.chunks().map(|e| e as i32), + }, + CONFIG.sea_level, + CONFIG.sea_level + sampler.max_height, + |posi| { + let sample = sampler.get(uniform_idx_as_vec2(map_size_lg, posi)).unwrap(); + if is_basement { + sample.alt + } else { + sample.basement + } + .max(if is_water { + sample.water_alt + } else { + -f32::INFINITY + }) + }, + |a| a, + |h| h, + /* |[al, ar]| [al, ar], + * |[hl, hr]| [hl, hr], */ + ) + .ok() + }; let mut win = minifb::Window::new("World Viewer", W, H, minifb::WindowOptions::default()).unwrap(); - let sampler = world.sim(); - let mut focus = Vec3::new(0.0, 0.0, CONFIG.sea_level as f64); // Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain // makes smaller differences in altitude appear larger. - let mut gain = CONFIG.mountain_scale; + let mut gain = /*CONFIG.mountain_scale*/sampler.max_height; // The Z component during normal calculations is multiplied by gain; thus, - let mut lgain = 1.0; - let mut scale = WORLD_SIZE.x as f64 / W as f64; + let mut fov = 1.0; + let mut scale = + (map_size_lg.chunks().x as f64 / W as f64).max(map_size_lg.chunks().y as f64 / H as f64); // Right-handed coordinate system: light is going left, down, and "backwards" // (i.e. on the map, where we translate the y coordinate on the world map to @@ -54,7 +146,7 @@ fn main() { // // "In world space the x-axis will be pointing east, the y-axis up and the // z-axis will be pointing south" - let mut light_direction = Vec3::new(-0.8, -1.0, 0.3); + let mut light_direction = Vec3::new(-/*0.8*/1.3, -1.0, 0.3); let mut is_basement = false; let mut is_water = true; @@ -62,14 +154,21 @@ fn main() { let mut is_temperature = true; let mut is_humidity = true; + let mut horizons = refresh_horizons(is_basement, is_water); + let mut samples = None; + + let mut samples_changed = true; + let mut map_samples: Box<[_]> = Box::new([]); while win.is_open() { let config = MapConfig { + map_size_lg, dimensions: Vec2::new(W, H), focus, gain, - lgain, + fov, scale, light_direction, + horizons: horizons.as_ref(), /* .map(|(a, b)| (&**a, &**b)) */ is_basement, is_water, @@ -79,36 +178,51 @@ fn main() { is_debug: true, }; + if samples_changed { + map_samples = refresh_map_samples(&config, samples); + }; + let mut buf = vec![0; W * H]; let MapDebug { rivers, lakes, oceans, quads, - } = config.generate(sampler, world.index(), |pos, (r, g, b, a)| { - let i = pos.x; - let j = pos.y; - buf[j * W + i] = u32::from_le_bytes([b, g, r, a]); - }); + } = config.generate( + |pos| get_map_sample(&map_samples, pos), + |pos| sample_wpos(&config, sampler, pos), + |pos, (r, g, b, a)| { + let i = pos.x; + let j = pos.y; + buf[j * W + i] = u32::from_le_bytes([b, g, r, a]); + }, + ); if win.is_key_down(minifb::Key::F4) { + // Feedback is important since on large maps it can be hard to tell if the + // keypress registered or not. + println!("Taking screenshot..."); if let Some(len) = (W * H) .checked_mul(scale as usize) .and_then(|acc| acc.checked_mul(scale as usize)) { let x = (W as f64 * scale) as usize; let y = (H as f64 * scale) as usize; - let config = sim::MapConfig { + let config = MapConfig { dimensions: Vec2::new(x, y), scale: 1.0, ..config }; let mut buf = vec![0u8; 4 * len]; - config.generate(sampler, world.index(), |pos, (r, g, b, a)| { - let i = pos.x; - let j = pos.y; - (&mut buf[(j * x + i) * 4..]).write(&[r, g, b, a]).unwrap(); - }); + config.generate( + |pos| get_map_sample(&map_samples, pos), + |pos| sample_wpos(&config, sampler, pos), + |pos, (r, g, b, a)| { + let i = pos.x; + let j = pos.y; + (&mut buf[(j * x + i) * 4..]).write(&[r, g, b, a]).unwrap(); + }, + ); // TODO: Justify fits in u32. let world_map = image::RgbaImage::from_raw(x as u32, y as u32, buf) .expect("Image dimensions must be valid"); @@ -140,7 +254,7 @@ fn main() { Land(adjacent): (X = temp, Y = humidity): {:?}\nRivers: {:?}\nLakes: \ {:?}\nOceans: {:?}\nTotal water: {:?}\nTotal land(adjacent): {:?}", gain, - lgain, + fov, scale, focus, light_direction, @@ -178,18 +292,39 @@ fn main() { let is_camera = win.is_key_down(minifb::Key::C); if win.is_key_down(minifb::Key::B) { is_basement ^= true; + samples_changed = true; + horizons = horizons.and_then(|_| refresh_horizons(is_basement, is_water)); } if win.is_key_down(minifb::Key::H) { is_humidity ^= true; + samples_changed = true; } if win.is_key_down(minifb::Key::T) { is_temperature ^= true; + samples_changed = true; } if win.is_key_down(minifb::Key::O) { is_water ^= true; + samples_changed = true; + horizons = horizons.and_then(|_| refresh_horizons(is_basement, is_water)); } if win.is_key_down(minifb::Key::L) { - is_shaded ^= true; + if is_camera { + // TODO: implement removing horizon mapping. + horizons = if horizons.is_some() { + None + } else { + refresh_horizons(is_basement, is_water) + }; + samples_changed = true; + } else { + is_shaded ^= true; + samples_changed = true; + } + } + if win.is_key_down(minifb::Key::M) { + samples = samples.xor(Some(&*samples_data)); + samples_changed = true; } if win.is_key_down(minifb::Key::W) { if is_camera { @@ -221,8 +356,8 @@ fn main() { } if win.is_key_down(minifb::Key::Q) { if is_camera { - if (lgain * 2.0).is_normal() { - lgain *= 2.0; + if (fov * 2.0).is_normal() { + fov *= 2.0; } } else { gain += 64.0; @@ -230,8 +365,8 @@ fn main() { } if win.is_key_down(minifb::Key::E) { if is_camera { - if (lgain / 2.0).is_normal() { - lgain /= 2.0; + if (fov / 2.0).is_normal() { + fov /= 2.0; } } else { gain = (gain - 64.0).max(64.0); @@ -240,6 +375,7 @@ fn main() { if win.is_key_down(minifb::Key::R) { if is_camera { focus.z += spd * scale; + samples_changed = true; } else { if (scale * 2.0).is_normal() { scale *= 2.0; @@ -249,6 +385,7 @@ fn main() { if win.is_key_down(minifb::Key::F) { if is_camera { focus.z -= spd * scale; + samples_changed = true; } else { if (scale / 2.0).is_normal() { scale /= 2.0; diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 77f7d3ae08..6d34ccc466 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -3,15 +3,28 @@ mod natural; use crate::{ column::{ColumnGen, ColumnSample}, util::{RandomField, Sampler, SmallCache}, - Index, + IndexRef, }; use common::{ - terrain::{structure::StructureBlock, Block, BlockKind, Structure}, + terrain::{ + structure::{self, StructureBlock}, + Block, BlockKind, Structure, + }, vol::{ReadVol, Vox}, }; -use std::ops::{Div, Mul}; +use core::ops::{Div, Mul, Range}; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub pyramid: (u8, u8, u8), + // TODO(@Sharp): After the merge, construct enough infrastructure to make it convenient to + // define mapping functions over the input; i.e. we should be able to interpret some fields as + // defining App, Arg>, where Fun : (Context, Arg) → (S, Type). + pub structure_blocks: structure::structure_block::PureCases>>, +} + pub struct BlockGen<'a> { pub column_cache: SmallCache>>, pub column_gen: ColumnGen<'a>, @@ -29,21 +42,21 @@ impl<'a> BlockGen<'a> { column_gen: &ColumnGen<'a>, cache: &'b mut SmallCache>>, wpos: Vec2, - index: &Index, + index: IndexRef<'a>, ) -> Option<&'b ColumnSample<'a>> { cache .get(wpos, |wpos| column_gen.get((wpos, index))) .as_ref() } - fn get_cliff_height( + pub fn get_cliff_height( column_gen: &ColumnGen<'a>, cache: &mut SmallCache>>, wpos: Vec2, close_cliffs: &[(Vec2, u32); 9], cliff_hill: f32, tolerance: f32, - index: &Index, + index: IndexRef<'a>, ) -> f32 { close_cliffs.iter().fold( 0.0f32, @@ -89,7 +102,7 @@ impl<'a> BlockGen<'a> { ) } - pub fn get_z_cache(&mut self, wpos: Vec2, index: &'a Index) -> Option> { + pub fn get_z_cache(&mut self, wpos: Vec2, index: IndexRef<'a>) -> Option> { let BlockGen { column_cache, column_gen, @@ -143,7 +156,7 @@ impl<'a> BlockGen<'a> { wpos: Vec3, z_cache: Option<&ZCache>, only_structures: bool, - index: &Index, + index: IndexRef<'a>, ) -> Option { let BlockGen { column_cache, @@ -237,18 +250,7 @@ impl<'a> BlockGen<'a> { // Sample blocks - // let stone_col = Rgb::new(195, 187, 201); - - // let dirt_col = Rgb::new(79, 67, 60); - - let _air = Block::empty(); - // let stone = Block::new(2, stone_col); - // let surface_stone = Block::new(1, Rgb::new(200, 220, 255)); - // let dirt = Block::new(1, dirt_col); - // let sand = Block::new(1, Rgb::new(180, 150, 50)); - // let warm_stone = Block::new(1, Rgb::new(165, 165, 130)); - - let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190)); + let water = Block::new(BlockKind::Water, Rgb::zero()); let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height); let block = if (wposf.z as f32) < height - grass_depth { @@ -389,7 +391,7 @@ impl<'a> BlockGen<'a> { .iter() .find_map(|st| { let (st, st_sample) = st.as_ref()?; - st.get(wpos, st_sample) + st.get(index, wpos, st_sample) }) .or(block); @@ -404,7 +406,11 @@ pub struct ZCache<'a> { } impl<'a> ZCache<'a> { - pub fn get_z_limits(&self, block_gen: &mut BlockGen, index: &Index) -> (f32, f32, f32) { + pub fn get_z_limits<'b>( + &self, + block_gen: &mut BlockGen<'b>, + index: IndexRef<'b>, + ) -> (f32, f32, f32) { let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0); let min = min - 4.0; @@ -492,7 +498,7 @@ impl StructureInfo { } } - fn get(&self, wpos: Vec3, sample: &ColumnSample) -> Option { + fn get(&self, index: IndexRef, wpos: Vec3, sample: &ColumnSample) -> Option { match self.meta { StructureMeta::Pyramid { height } => { if wpos.z - self.pos.z @@ -501,7 +507,10 @@ impl StructureInfo { .map(|e: i32| (e.abs() / 2) * 2) .reduce_max() { - Some(Block::new(BlockKind::Dense, Rgb::new(203, 170, 146))) + Some(Block::new( + BlockKind::Dense, + index.colors.block.pyramid.into(), + )) } else { None } @@ -517,6 +526,7 @@ impl StructureInfo { .ok() .and_then(|b| { block_from_structure( + index, *b, block_pos, self.pos.into(), @@ -530,6 +540,7 @@ impl StructureInfo { } pub fn block_from_structure( + index: IndexRef, sblock: StructureBlock, pos: Vec3, structure_pos: Vec2, @@ -543,85 +554,58 @@ pub fn block_from_structure( match sblock { StructureBlock::None => None, + StructureBlock::Hollow => Some(Block::empty()), StructureBlock::Grass => Some(Block::new( BlockKind::Normal, sample.surface_color.map(|e| (e * 255.0) as u8), )), - StructureBlock::TemperateLeaves => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(0.0, 132.0, 94.0), - Rgb::new(142.0, 181.0, 0.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::PineLeaves => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp) - .map(|e| e as u8), - )), - StructureBlock::PalmLeavesInner => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(61.0, 166.0, 43.0), - Rgb::new(29.0, 130.0, 32.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::PalmLeavesOuter => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(62.0, 171.0, 38.0), - Rgb::new(45.0, 171.0, 65.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::new(100, 150, 255))), - StructureBlock::GreenSludge => Some(Block::new(BlockKind::Water, Rgb::new(30, 126, 23))), - StructureBlock::Acacia => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(15.0, 126.0, 50.0), - Rgb::new(30.0, 180.0, 10.0), - lerp, - ) - .map(|e| e as u8), + StructureBlock::Normal(color) => { + Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty()) + }, + // Water / sludge throw away their color bits currently, so we don't set anyway. + StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::zero())), + StructureBlock::GreenSludge => Some(Block::new( + BlockKind::Water, + // TODO: If/when liquid supports other colors again, revisit this. + Rgb::zero(), )), + // None of these BlockKinds has an orientation, so we just use zero for the other color + // bits. StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 3 > 0 { Block::empty() } else { - Block::new(BlockKind::Apple, Rgb::new(1, 1, 1)) + Block::new(BlockKind::Apple, Rgb::zero()) }), StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 { Block::empty() } else { - Block::new(BlockKind::Coconut, Rgb::new(1, 1, 1)) + Block::new(BlockKind::Coconut, Rgb::zero()) }), StructureBlock::Chest => Some(if structure_seed % 10 < 7 { Block::empty() } else { - Block::new(BlockKind::Chest, Rgb::new(1, 1, 1)) + Block::new(BlockKind::Chest, Rgb::zero()) }), - StructureBlock::Liana => Some(Block::new( - BlockKind::Liana, - Lerp::lerp( - Rgb::new(0.0, 125.0, 107.0), - Rgb::new(0.0, 155.0, 129.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::Mangrove => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp(Rgb::new(32.0, 56.0, 22.0), Rgb::new(57.0, 69.0, 27.0), lerp) - .map(|e| e as u8), - )), - StructureBlock::Hollow => Some(Block::empty()), - StructureBlock::Normal(color) => { - Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty()) - }, + // We interpolate all these BlockKinds as needed. + StructureBlock::TemperateLeaves + | StructureBlock::PineLeaves + | StructureBlock::PalmLeavesInner + | StructureBlock::PalmLeavesOuter + | StructureBlock::Acacia + | StructureBlock::Liana + | StructureBlock::Mangrove => sblock + .elim_case_pure(&index.colors.block.structure_blocks) + .as_ref() + .map(|range| { + Block::new( + BlockKind::Leaves, + Rgb::::lerp( + Rgb::::from(range.start).map(f32::from), + Rgb::::from(range.end).map(f32::from), + lerp, + ) + .map(|e| e as u8), + ) + }), } } diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index e09ca2158a..8ccff506b0 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -3,7 +3,7 @@ use crate::{ all::ForestKind, column::{ColumnGen, ColumnSample}, util::{RandomPerm, Sampler, SmallCache, UnitChooser}, - Index, CONFIG, + IndexRef, CONFIG, }; use common::terrain::Structure; use lazy_static::lazy_static; @@ -20,7 +20,7 @@ pub fn structure_gen<'a>( st_pos: Vec2, st_seed: u32, st_sample: &ColumnSample, - index: &'a Index, + index: IndexRef<'a>, ) -> Option { // Assuming it's a tree... figure out when it SHOULDN'T spawn let random_seed = (st_seed as f64) / (u32::MAX as f64); diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 9d9058b925..d49f610d4f 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -4,6 +4,7 @@ mod econ; use self::{Occupation::*, Stock::*}; use crate::{ + config::CONFIG, sim::WorldSim, site::{Castle, Dungeon, Settlement, Site as WorldSite}, util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS}, @@ -14,7 +15,7 @@ use common::{ path::Path, spiral::Spiral2d, store::{Id, Store}, - terrain::TerrainChunkSize, + terrain::{MapSizeLg, TerrainChunkSize}, vol::RectVolSize, }; use core::{ @@ -29,7 +30,13 @@ use rand_chacha::ChaChaRng; use tracing::{debug, info, warn}; use vek::*; -const INITIAL_CIV_COUNT: usize = (crate::sim::WORLD_SIZE.x * crate::sim::WORLD_SIZE.y * 3) / 65536; //48 at default scale +const fn initial_civ_count(map_size_lg: MapSizeLg) -> u32 { + // NOTE: since map_size_lg's dimensions must fit in a u16, we can safely add + // them here. + // + // NOTE: 48 at "default" scale of 10 × 10 chunk bits (1024 × 1024 chunks). + (3 << (map_size_lg.vec().x + map_size_lg.vec().y)) >> 16 +} #[allow(clippy::type_complexity)] // TODO: Pending review in #587 #[derive(Default)] @@ -74,21 +81,23 @@ impl Civs { pub fn generate(seed: u32, sim: &mut WorldSim, index: &mut Index) -> Self { let mut this = Self::default(); let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + let initial_civ_count = initial_civ_count(sim.map_size_lg()); let mut ctx = GenCtx { sim, rng }; + // TODO: Care about world size when generating caves. for _ in 0..100 { this.generate_cave(&mut ctx); } - for _ in 0..INITIAL_CIV_COUNT { + for _ in 0..initial_civ_count { debug!("Creating civilisation..."); if this.birth_civ(&mut ctx.reseed()).is_none() { warn!("Failed to find starting site for civilisation."); } } - info!(?INITIAL_CIV_COUNT, "all civilisations created"); + info!(?initial_civ_count, "all civilisations created"); - for _ in 0..INITIAL_CIV_COUNT * 3 { + for _ in 0..initial_civ_count * 3 { attempt(5, || { let (kind, size) = match ctx.rng.gen_range(0, 8) { 0 => (SiteKind::Castle, 3), @@ -163,6 +172,11 @@ impl Civs { .filter(|chunk| !chunk.river.near_water()) .map(|chunk| { let diff = Lerp::lerp_precise(chunk.alt, center_alt, factor) - chunk.alt; + // Make sure we don't fall below sea level (fortunately, we don't have + // to worry about the case where water_alt is already set to a correct + // value higher than alt, since this chunk should have been filtered + // out in that case). + chunk.water_alt = CONFIG.sea_level.max(chunk.water_alt + diff); chunk.alt += diff; chunk.basement += diff; chunk.rockiness = 0.0; diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 5f08f73f39..c8e07476e1 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,16 +1,19 @@ use crate::{ all::ForestKind, block::StructureMeta, - sim::{ - local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, Cave, Path, RiverKind, SimChunk, - WorldSim, - }, + sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim}, util::Sampler, - Index, CONFIG, + IndexRef, CONFIG, +}; +use common::{ + terrain::{ + quadratic_nearest_point, river_spline_coeffs, uniform_idx_as_vec2, vec2_as_uniform_idx, + TerrainChunkSize, + }, + vol::RectVolSize, }; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use noise::NoiseFn; -use roots::find_roots_cubic; +use serde::{Deserialize, Serialize}; use std::{ cmp::Reverse, f32, f64, @@ -23,6 +26,31 @@ pub struct ColumnGen<'a> { pub sim: &'a WorldSim, } +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub cold_grass: (f32, f32, f32), + pub warm_grass: (f32, f32, f32), + pub dark_grass: (f32, f32, f32), + pub wet_grass: (f32, f32, f32), + pub cold_stone: (f32, f32, f32), + pub hot_stone: (f32, f32, f32), + pub warm_stone: (f32, f32, f32), + pub beach_sand: (f32, f32, f32), + pub desert_sand: (f32, f32, f32), + pub snow: (f32, f32, f32), + + pub stone_col: (u8, u8, u8), + + pub dirt_low: (f32, f32, f32), + pub dirt_high: (f32, f32, f32), + + pub snow_high: (f32, f32, f32), + pub warm_stone_high: (f32, f32, f32), + + pub grass_high: (f32, f32, f32), + pub tropical_high: (f32, f32, f32), +} + impl<'a> ColumnGen<'a> { pub fn new(sim: &'a WorldSim) -> Self { Self { sim } } @@ -77,102 +105,8 @@ impl<'a> ColumnGen<'a> { } } -fn river_spline_coeffs( - // _sim: &WorldSim, - chunk_pos: Vec2, - spline_derivative: Vec2, - downhill_pos: Vec2, -) -> Vec3> { - let dxy = downhill_pos - chunk_pos; - // Since all splines have been precomputed, we don't have to do that much work - // to evaluate the spline. The spline is just ax^2 + bx + c = 0, where - // - // a = dxy - chunk.river.spline_derivative - // b = chunk.river.spline_derivative - // c = chunk_pos - let spline_derivative = spline_derivative.map(|e| e as f64); - Vec3::new(dxy - spline_derivative, spline_derivative, chunk_pos) -} - -/// Find the nearest point from a quadratic spline to this point (in terms of t, -/// the "distance along the curve" by which our spline is parameterized). Note -/// that if t < 0.0 or t >= 1.0, we probably shouldn't be considered "on the -/// curve"... hopefully this works out okay and gives us what we want (a -/// river that extends outwards tangent to a quadratic curve, with width -/// configured by distance along the line). -#[allow(clippy::let_and_return)] // TODO: Pending review in #587 -#[allow(clippy::many_single_char_names)] -pub fn quadratic_nearest_point( - spline: &Vec3>, - point: Vec2, -) -> Option<(f64, Vec2, f64)> { - let a = spline.z.x; - let b = spline.y.x; - let c = spline.x.x; - let d = point.x; - let e = spline.z.y; - let f = spline.y.y; - let g = spline.x.y; - let h = point.y; - // This is equivalent to solving the following cubic equation (derivation is a - // bit annoying): - // - // A = 2(c^2 + g^2) - // B = 3(b * c + g * f) - // C = ((a - d) * 2 * c + b^2 + (e - h) * 2 * g + f^2) - // D = ((a - d) * b + (e - h) * f) - // - // Ax³ + Bx² + Cx + D = 0 - // - // Once solved, this yield up to three possible values for t (reflecting minimal - // and maximal values). We should choose the minimal such real value with t - // between 0.0 and 1.0. If we fall outside those bounds, then we are - // outside the spline and return None. - let a_ = (c * c + g * g) * 2.0; - let b_ = (b * c + g * f) * 3.0; - let a_d = a - d; - let e_h = e - h; - let c_ = a_d * c * 2.0 + b * b + e_h * g * 2.0 + f * f; - let d_ = a_d * b + e_h * f; - let roots = find_roots_cubic(a_, b_, c_, d_); - let roots = roots.as_ref(); - - let min_root = roots - .iter() - .copied() - .filter_map(|root| { - let river_point = spline.x * root * root + spline.y * root + spline.z; - let river_zero = spline.z; - let river_one = spline.x + spline.y + spline.z; - if root > 0.0 && root < 1.0 { - Some((root, river_point)) - } else if river_point.distance_squared(river_zero) < 0.5 { - Some((root, /*river_point*/ river_zero)) - } else if river_point.distance_squared(river_one) < 0.5 { - Some((root, /*river_point*/ river_one)) - } else { - None - } - }) - .map(|(root, river_point)| { - let river_distance = river_point.distance_squared(point); - (root, river_point, river_distance) - }) - // In the (unlikely?) case that distances are equal, prefer the earliest point along the - // river. - .min_by(|&(ap, _, a), &(bp, _, b)| { - (a, ap < 0.0 || ap > 1.0, ap) - .partial_cmp(&(b, bp < 0.0 || bp > 1.0, bp)) - .unwrap() - }); - min_root -} - -impl<'a, 'b> Sampler<'b> for ColumnGen<'a> -where - 'a: 'b, -{ - type Index = (Vec2, &'b Index); +impl<'a> Sampler<'a> for ColumnGen<'a> { + type Index = (Vec2, IndexRef<'a>); type Sample = Option>; #[allow(clippy::float_cmp)] // TODO: Pending review in #587 @@ -202,12 +136,13 @@ where let chunk_warp_factor = sim.get_interpolated_monotone(wpos, |chunk| chunk.warp_factor)?; let sim_chunk = sim.get(chunk_pos)?; let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); - let my_chunk_idx = vec2_as_uniform_idx(chunk_pos); - let neighbor_river_data = local_cells(my_chunk_idx).filter_map(|neighbor_idx: usize| { - let neighbor_pos = uniform_idx_as_vec2(neighbor_idx); - let neighbor_chunk = sim.get(neighbor_pos)?; - Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river)) - }); + let my_chunk_idx = vec2_as_uniform_idx(self.sim.map_size_lg(), chunk_pos); + let neighbor_river_data = + local_cells(self.sim.map_size_lg(), my_chunk_idx).filter_map(|neighbor_idx: usize| { + let neighbor_pos = uniform_idx_as_vec2(self.sim.map_size_lg(), neighbor_idx); + let neighbor_chunk = sim.get(neighbor_pos)?; + Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river)) + }); let lake_width = (TerrainChunkSize::RECT_SIZE.x as f64 * (2.0f64.sqrt())) + 12.0; let neighbor_river_data = neighbor_river_data.map(|(posj, chunkj, river)| { let kind = match river.river_kind { @@ -856,25 +791,47 @@ where .add(marble_small.sub(0.5).mul(0.25)); // Colours - let cold_grass = Rgb::new(0.0, 0.5, 0.25); - let warm_grass = Rgb::new(0.4, 0.8, 0.0); - let dark_grass = Rgb::new(0.15, 0.4, 0.1); - let wet_grass = Rgb::new(0.1, 0.8, 0.2); - let cold_stone = Rgb::new(0.57, 0.67, 0.8); - let hot_stone = Rgb::new(0.07, 0.07, 0.06); - let warm_stone = Rgb::new(0.77, 0.77, 0.64); - let beach_sand = Rgb::new(0.8, 0.75, 0.5); - let desert_sand = Rgb::new(0.7, 0.4, 0.25); - let snow = Rgb::new(0.8, 0.85, 1.0); + let Colors { + cold_grass, + warm_grass, + dark_grass, + wet_grass, + cold_stone, + hot_stone, + warm_stone, + beach_sand, + desert_sand, + snow, + stone_col, + dirt_low, + dirt_high, + snow_high, + warm_stone_high, + grass_high, + tropical_high, + } = index.colors.column; - let stone_col = Rgb::new(195, 187, 201); - let dirt = Lerp::lerp( - Rgb::new(0.075, 0.07, 0.3), - Rgb::new(0.75, 0.55, 0.1), - marble, - ); - let tundra = Lerp::lerp(snow, Rgb::new(0.01, 0.3, 0.0), 0.4 + marble * 0.6); - let dead_tundra = Lerp::lerp(warm_stone, Rgb::new(0.3, 0.12, 0.2), marble); + let cold_grass = cold_grass.into(); + let warm_grass = warm_grass.into(); + let dark_grass = dark_grass.into(); + let wet_grass = wet_grass.into(); + let cold_stone = cold_stone.into(); + let hot_stone = hot_stone.into(); + let warm_stone: Rgb = warm_stone.into(); + let beach_sand = beach_sand.into(); + let desert_sand = desert_sand.into(); + let snow = snow.into(); + let stone_col = stone_col.into(); + let dirt_low: Rgb = dirt_low.into(); + let dirt_high = dirt_high.into(); + let snow_high = snow_high.into(); + let warm_stone_high = warm_stone_high.into(); + let grass_high = grass_high.into(); + let tropical_high = tropical_high.into(); + + let dirt = Lerp::lerp(dirt_low, dirt_high, marble); + let tundra = Lerp::lerp(snow, snow_high, 0.4 + marble * 0.6); + let dead_tundra = Lerp::lerp(warm_stone, warm_stone_high, marble); let cliff = Rgb::lerp(cold_stone, hot_stone, marble); let grass = Rgb::lerp( @@ -890,14 +847,14 @@ where let tropical = Rgb::lerp( Rgb::lerp( grass, - Rgb::new(0.15, 0.2, 0.15), + grass_high, marble_small .sub(0.5) .mul(0.2) .add(0.75.mul(1.0.sub(humidity))) .powf(0.667), ), - Rgb::new(0.87, 0.62, 0.56), + tropical_high, marble.powf(1.5).sub(0.5).mul(4.0), ); diff --git a/world/src/config.rs b/world/src/config.rs index dffd712f7d..5f8914d6e1 100644 --- a/world/src/config.rs +++ b/world/src/config.rs @@ -64,5 +64,5 @@ pub const CONFIG: Config = Config { river_roughness: 0.06125, river_max_width: 2.0, river_min_height: 0.25, - river_width_to_depth: 1.0, + river_width_to_depth: 8.0, }; diff --git a/world/src/index.rs b/world/src/index.rs index ed6a6ce157..d8dedd23ed 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -1,21 +1,104 @@ -use crate::site::Site; -use common::store::Store; +use crate::{site::Site, Colors}; +use common::{ + assets::{self, watch::ReloadIndicator}, + store::Store, +}; +use core::ops::Deref; use noise::{Seedable, SuperSimplex}; +use std::sync::Arc; + +const WORLD_COLORS_MANIFEST: &str = "world.style.colors"; pub struct Index { pub seed: u32, pub time: f32, pub noise: Noise, pub sites: Store, + indicator: ReloadIndicator, +} + +/// An owned reference to indexed data. +/// +/// The data are split out so that we can replace the colors without disturbing +/// the rest of the index, while also keeping all the data within a single +/// indirection. +#[derive(Clone)] +pub struct IndexOwned { + colors: Arc, + index: Arc, +} + +impl Deref for IndexOwned { + type Target = Index; + + fn deref(&self) -> &Self::Target { &self.index } +} + +/// A shared reference to indexed data. +/// +/// This is copyable and can be used from either style of index. +#[derive(Clone, Copy)] +pub struct IndexRef<'a> { + pub colors: &'a Colors, + pub index: &'a Index, +} + +impl<'a> Deref for IndexRef<'a> { + type Target = Index; + + fn deref(&self) -> &Self::Target { &self.index } } impl Index { - pub fn new(seed: u32) -> Self { + /// NOTE: Panics if the color manifest cannot be loaded. + pub fn new(seed: u32) -> (Self, Arc) { + let mut indicator = ReloadIndicator::new(); + let colors = assets::load_watched::(WORLD_COLORS_MANIFEST, &mut indicator) + .expect("Could not load world colors!"); + + ( + Self { + seed, + time: 0.0, + noise: Noise::new(seed), + sites: Store::default(), + indicator, + }, + colors, + ) + } +} + +impl IndexOwned { + pub fn new(index: Index, colors: Arc) -> Self { Self { - seed, - time: 0.0, - noise: Noise::new(seed), - sites: Store::default(), + index: Arc::new(index), + colors, + } + } + + /// NOTE: Callback is called only when colors actually have to be reloaded. + /// The server is responsible for making sure that all affected chunks are + /// reloaded; a naive approach will just regenerate every chunk on the + /// server, but it is possible that eventually we can find a better + /// solution. + /// + /// Ideally, this should be called about once per tick. + pub fn reload_colors_if_changed( + &mut self, + reload: impl FnOnce(&mut Self) -> R, + ) -> Option { + self.indicator.reloaded().then(move || { + // We know the asset was loaded before, so load_expect should be fine. + self.colors = assets::load_expect::(WORLD_COLORS_MANIFEST); + reload(self) + }) + } + + pub fn as_index_ref(&self) -> IndexRef { + IndexRef { + colors: &self.colors, + index: &self.index, } } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 72986bb1ec..d358e6286e 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -2,7 +2,7 @@ use crate::{ column::ColumnSample, sim::SimChunk, util::{RandomField, Sampler}, - Index, + IndexRef, }; use common::{ assets, comp, @@ -13,12 +13,19 @@ use common::{ }; use noise::NoiseFn; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use std::{ f32, ops::{Mul, Sub}, }; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub bridge: (u8, u8, u8), + pub stalagtite: (u8, u8, u8), +} + fn close(x: f32, tgt: f32, falloff: f32) -> f32 { (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5) } @@ -27,7 +34,7 @@ pub fn apply_scatter_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + index: IndexRef, chunk: &SimChunk, ) { use BlockKind::*; @@ -150,7 +157,7 @@ pub fn apply_paths_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - _index: &Index, + index: IndexRef, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -213,7 +220,10 @@ pub fn apply_paths_to<'a>( let _ = vol.set( Vec3::new(offs.x, offs.y, surface_z + z), if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 { - Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) + Block::new( + BlockKind::Normal, + noisy_color(index.colors.layer.bridge.into(), 8), + ) } else { let path_color = path.surface_color( col_sample.sub_surface_color.map(|e| (e * 255.0) as u8), @@ -238,7 +248,7 @@ pub fn apply_caves_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + index: IndexRef, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -297,7 +307,7 @@ pub fn apply_caves_to<'a>( for z in cave_roof - stalagtites..cave_roof { let _ = vol.set( Vec3::new(offs.x, offs.y, z), - Block::new(BlockKind::Rock, Rgb::broadcast(200)), + Block::new(BlockKind::Rock, index.colors.layer.stalagtite.into()), ); } @@ -325,7 +335,7 @@ pub fn apply_caves_supplement<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &(impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + index: IndexRef, supplement: &mut ChunkSupplement, ) { for y in 0..vol.size_xy().y as i32 { diff --git a/world/src/lib.rs b/world/src/lib.rs index d6ca1a0023..6a1a6e954d 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -1,7 +1,14 @@ #![deny(unsafe_code)] #![allow(incomplete_features)] #![allow(clippy::option_map_unit_fn)] -#![feature(arbitrary_enum_discriminant, const_generics, label_break_value)] +#![feature( + arbitrary_enum_discriminant, + bool_to_option, + const_generics, + const_panic, + label_break_value, + or_patterns +)] mod all; mod block; @@ -17,21 +24,26 @@ pub mod util; // Reexports pub use crate::config::CONFIG; +pub use block::BlockGen; +pub use column::ColumnSample; +pub use index::{IndexOwned, IndexRef}; use crate::{ - block::BlockGen, - column::{ColumnGen, ColumnSample}, + column::ColumnGen, index::Index, util::{Grid, Sampler}, }; use common::{ + assets::{self, Asset}, comp::{self, bird_medium, critter, quadruped_low, quadruped_medium, quadruped_small}, generation::{ChunkSupplement, EntityInfo}, + msg::server::WorldMapMsg, terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, vol::{ReadVol, RectVolSize, Vox, WriteVol}, }; use rand::Rng; -use std::time::Duration; +use serde::{Deserialize, Serialize}; +use std::{fs::File, io::BufReader, time::Duration}; use vek::*; #[derive(Debug)] @@ -42,35 +54,51 @@ pub enum Error { pub struct World { sim: sim::WorldSim, civs: civ::Civs, - index: Index, +} + +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub deep_stone_color: (u8, u8, u8), + pub block: block::Colors, + pub column: column::Colors, + pub layer: layer::Colors, + pub site: site::Colors, +} + +impl Asset for Colors { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) + } } impl World { - pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self { + pub fn generate(seed: u32, opts: sim::WorldOpts) -> (Self, IndexOwned) { + // NOTE: Generating index first in order to quickly fail if the color manifest + // is broken. + let (mut index, colors) = Index::new(seed); let mut sim = sim::WorldSim::generate(seed, opts); - let mut index = Index::new(seed); let civs = civ::Civs::generate(seed, &mut sim, &mut index); sim2::simulate(&mut index, &mut sim); - Self { sim, civs, index } + (Self { sim, civs }, IndexOwned::new(index, colors)) } pub fn sim(&self) -> &sim::WorldSim { &self.sim } pub fn civs(&self) -> &civ::Civs { &self.civs } - pub fn index(&self) -> &Index { &self.index } - pub fn tick(&self, _dt: Duration) { // TODO } - pub fn get_map_data(&self) -> Vec { self.sim.get_map(&self.index) } + pub fn get_map_data(&self, index: IndexRef) -> WorldMapMsg { self.sim.get_map(index) } pub fn sample_columns( &self, - ) -> impl Sampler, &Index), Sample = Option> + '_ { + ) -> impl Sampler, IndexRef), Sample = Option> + '_ { ColumnGen::new(&self.sim) } @@ -79,6 +107,7 @@ impl World { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn generate_chunk( &self, + index: IndexRef, chunk_pos: Vec2, // TODO: misleading name mut should_continue: impl FnMut() -> bool, @@ -89,7 +118,7 @@ impl World { let grid_border = 4; let zcache_grid = Grid::populate_from( TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, - |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, &self.index), + |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index), ); let air = Block::empty(); @@ -99,9 +128,9 @@ impl World { .get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2) .and_then(|zcache| zcache.as_ref()) .map(|zcache| zcache.sample.stone_col) - .unwrap_or(Rgb::new(125, 120, 130)), + .unwrap_or(index.colors.deep_stone_color.into()), ); - let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190)); + let water = Block::new(BlockKind::Water, Rgb::zero()); let _chunk_size2d = TerrainChunkSize::RECT_SIZE; let (base_z, sim_chunk) = match self @@ -145,7 +174,7 @@ impl World { }; let (min_z, only_structures_min_z, max_z) = - z_cache.get_z_limits(&mut sampler, &self.index); + z_cache.get_z_limits(&mut sampler, index); (base_z..min_z as i32).for_each(|z| { let _ = chunk.set(Vec3::new(x, y, z), stone); @@ -157,7 +186,7 @@ impl World { let only_structures = lpos.z >= only_structures_min_z as i32; if let Some(block) = - sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, &self.index) + sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, index) { let _ = chunk.set(lpos, block); } @@ -176,13 +205,13 @@ impl World { let mut rng = rand::thread_rng(); // Apply layers (paths, caves, etc.) - layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, &self.index, sim_chunk); - layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); - layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); + layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, index, sim_chunk); + layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, index); + layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, index); // Apply site generation sim_chunk.sites.iter().for_each(|site| { - self.index.sites[*site].apply_to(chunk_wpos2d, sample_get, &mut chunk) + index.sites[*site].apply_to(index, chunk_wpos2d, sample_get, &mut chunk) }); let gen_entity_pos = || { @@ -235,18 +264,13 @@ impl World { chunk_wpos2d, sample_get, &chunk, - &self.index, + index, &mut supplement, ); // Apply site supplementary information sim_chunk.sites.iter().for_each(|site| { - self.index.sites[*site].apply_supplement( - &mut rng, - chunk_wpos2d, - sample_get, - &mut supplement, - ) + index.sites[*site].apply_supplement(&mut rng, chunk_wpos2d, sample_get, &mut supplement) }); Ok((chunk, supplement)) diff --git a/world/src/sim/erosion.rs b/world/src/sim/erosion.rs index 19b946982b..e5128c60b5 100644 --- a/world/src/sim/erosion.rs +++ b/world/src/sim/erosion.rs @@ -1,9 +1,12 @@ -use super::{ - diffusion, downhill, neighbors, uniform_idx_as_vec2, uphill, vec2_as_uniform_idx, - NEIGHBOR_DELTA, WORLD_SIZE, -}; +use super::{diffusion, downhill, uphill}; use crate::{config::CONFIG, util::RandomField}; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + terrain::{ + neighbors, uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize, + NEIGHBOR_DELTA, + }, + vol::RectVolSize, +}; use tracing::{debug, error, warn}; // use faster::*; use itertools::izip; @@ -27,7 +30,12 @@ pub type Computex8 = [Compute; 8]; /// Compute the water flux at all chunks, given a list of chunk indices sorted /// by increasing height. -pub fn get_drainage(newh: &[u32], downhill: &[isize], _boundary_len: usize) -> Box<[f32]> { +pub fn get_drainage( + map_size_lg: MapSizeLg, + newh: &[u32], + downhill: &[isize], + _boundary_len: usize, +) -> Box<[f32]> { // FIXME: Make the below work. For now, we just use constant flux. // Initially, flux is determined by rainfall. We currently treat this as the // same as humidity, so we just use humidity as a proxy. The total flux @@ -35,10 +43,10 @@ pub fn get_drainage(newh: &[u32], downhill: &[isize], _boundary_len: usize) -> B // to be 0.5. To figure out how far from normal any given chunk is, we use // its logit. NOTE: If there are no non-boundary chunks, we just set // base_flux to 1.0; this should still work fine because in that case - // there's no erosion anyway. let base_flux = 1.0 / ((WORLD_SIZE.x * - // WORLD_SIZE.y) as f32); + // there's no erosion anyway. let base_flux = 1.0 / ((map_size_lg.chunks_len()) + // as f32); let base_flux = 1.0; - let mut flux = vec![base_flux; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut flux = vec![base_flux; map_size_lg.chunks_len()].into_boxed_slice(); newh.iter().rev().for_each(|&chunk_idx| { let chunk_idx = chunk_idx as usize; let downhill_idx = downhill[chunk_idx]; @@ -52,6 +60,7 @@ pub fn get_drainage(newh: &[u32], downhill: &[isize], _boundary_len: usize) -> B /// Compute the water flux at all chunks for multiple receivers, given a list of /// chunk indices sorted by increasing height and weights for each receiver. pub fn get_multi_drainage( + map_size_lg: MapSizeLg, mstack: &[u32], mrec: &[u8], mwrec: &[Computex8], @@ -66,12 +75,12 @@ pub fn get_multi_drainage( // base_flux to 1.0; this should still work fine because in that case // there's no erosion anyway. let base_area = 1.0; - let mut area = vec![base_area; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut area = vec![base_area; map_size_lg.chunks_len()].into_boxed_slice(); mstack.iter().for_each(|&ij| { let ij = ij as usize; let wrec_ij = &mwrec[ij]; let area_ij = area[ij]; - mrec_downhill(mrec, ij).for_each(|(k, ijr)| { + mrec_downhill(map_size_lg, mrec, ij).for_each(|(k, ijr)| { area[ijr] += area_ij * wrec_ij[k]; }); }); @@ -111,11 +120,11 @@ pub enum RiverKind { } impl RiverKind { - pub fn is_ocean(&self) -> bool { matches!(self, RiverKind::Ocean) } + pub fn is_ocean(&self) -> bool { matches!(*self, RiverKind::Ocean) } - pub fn is_river(&self) -> bool { matches!(self, RiverKind::River { .. }) } + pub fn is_river(&self) -> bool { matches!(*self, RiverKind::River { .. }) } - pub fn is_lake(&self) -> bool { matches!(self, RiverKind::Lake { .. }) } + pub fn is_lake(&self) -> bool { matches!(*self, RiverKind::Lake { .. }) } } impl PartialOrd for RiverKind { @@ -208,6 +217,8 @@ impl RiverData { /// liberties with the constant factors etc. in order to make it more likely /// that we draw rivers at all. pub fn get_rivers, G: Float + Into>( + map_size_lg: MapSizeLg, + continent_scale_hack: f64, newh: &[u32], water_alt: &[F], downhill: &[isize], @@ -218,7 +229,7 @@ pub fn get_rivers, G: Float + Into>( // to build up the derivatives from the top down. Fortunately this // computation seems tractable. - let mut rivers = vec![RiverData::default(); WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut rivers = vec![RiverData::default(); map_size_lg.chunks_len()].into_boxed_slice(); let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); // (Roughly) area of a chunk, times minutes per second. // NOTE: Clearly, this should "actually" be 1/60 (or maybe 1/64, if you want to @@ -229,7 +240,7 @@ pub fn get_rivers, G: Float + Into>( // increases, mins_per_sec should decrease, until it hits 1 / 60 or 1/ 64. // For example, if grid_scale is multiplied by 4, mins_per_sec should be // multiplied by 1 / (4.0 * 4.0). - let mins_per_sec = 1.0/*1.0 / 16.0*//*1.0 / 64.0*/; + let mins_per_sec = 1.0 / (continent_scale_hack * continent_scale_hack)/*1.0 / 16.0*//*1.0 / 64.0*/; let chunk_area_factor = neighbor_coef.x * neighbor_coef.y * mins_per_sec; // NOTE: This technically makes us discontinuous, so we should be cautious about // using this. @@ -244,8 +255,8 @@ pub fn get_rivers, G: Float + Into>( return; } let downhill_idx = downhill_idx as usize; - let downhill_pos = uniform_idx_as_vec2(downhill_idx); - let dxy = (downhill_pos - uniform_idx_as_vec2(chunk_idx)).map(|e| e as f64); + let downhill_pos = uniform_idx_as_vec2(map_size_lg, downhill_idx); + let dxy = (downhill_pos - uniform_idx_as_vec2(map_size_lg, chunk_idx)).map(|e| e as f64); let neighbor_dim = neighbor_coef * dxy; // First, we calculate the river's volumetric flow rate. let chunk_drainage = drainage[chunk_idx].into(); @@ -364,10 +375,16 @@ pub fn get_rivers, G: Float + Into>( // This is a pass, so set our flow direction to point to the neighbor pass // rather than downhill. // NOTE: Safe because neighbor_pass_idx >= 0. - (uniform_idx_as_vec2(downhill_idx), river_spline_derivative) + ( + uniform_idx_as_vec2(map_size_lg, downhill_idx), + river_spline_derivative, + ) } else { // Try pointing towards the lake side of the pass. - (uniform_idx_as_vec2(pass_idx), river_spline_derivative) + ( + uniform_idx_as_vec2(map_size_lg, pass_idx), + river_spline_derivative, + ) }; let mut lake = &mut rivers[chunk_idx]; lake.spline_derivative = river_spline_derivative; @@ -409,12 +426,12 @@ pub fn get_rivers, G: Float + Into>( error!( "Our chunk (and downhill, lake, pass, neighbor_pass): {:?} (to {:?}, in {:?} via \ {:?} to {:?}), chunk water alt: {:?}, lake water alt: {:?}", - uniform_idx_as_vec2(chunk_idx), - uniform_idx_as_vec2(downhill_idx), - uniform_idx_as_vec2(lake_idx), - uniform_idx_as_vec2(pass_idx), + uniform_idx_as_vec2(map_size_lg, chunk_idx), + uniform_idx_as_vec2(map_size_lg, downhill_idx), + uniform_idx_as_vec2(map_size_lg, lake_idx), + uniform_idx_as_vec2(map_size_lg, pass_idx), if neighbor_pass_idx >= 0 { - Some(uniform_idx_as_vec2(neighbor_pass_idx as usize)) + Some(uniform_idx_as_vec2(map_size_lg, neighbor_pass_idx as usize)) } else { None }, @@ -532,6 +549,7 @@ pub fn get_rivers, G: Float + Into>( /// TODO: See if allocating in advance is worthwhile. #[allow(clippy::let_and_return)] // TODO: Pending review in #587 fn get_max_slope( + map_size_lg: MapSizeLg, h: &[Alt], rock_strength_nz: &(impl NoiseFn> + Sync), height_scale: impl Fn(usize) -> Alt + Sync, @@ -542,7 +560,7 @@ fn get_max_slope( h.par_iter() .enumerate() .map(|(posi, &z)| { - let wposf = uniform_idx_as_vec2(posi).map(|e| e as f64) + let wposf = uniform_idx_as_vec2(map_size_lg, posi).map(|e| e as f64) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); let height_scale = height_scale(posi); let wposz = z as f64 / height_scale as f64; @@ -667,6 +685,8 @@ fn get_max_slope( #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] fn erode( + // Underlying map dimensions. + map_size_lg: MapSizeLg, // Height above sea level of topsoil h: &mut [Alt], // Height above sea level of bedrock @@ -715,8 +735,8 @@ fn erode( // at the cost of less interesting erosion behavior (linear vs. nonlinear). let q_ = 1.5; let k_da = 2.5 * k_da_scale(q); - let nx = WORLD_SIZE.x; - let ny = WORLD_SIZE.y; + let nx = usize::from(map_size_lg.chunks().x); + let ny = usize::from(map_size_lg.chunks().y); let dx = TerrainChunkSize::RECT_SIZE.x as f64; let dy = TerrainChunkSize::RECT_SIZE.y as f64; @@ -777,11 +797,17 @@ fn erode( let g_fs_mult_sed = 1.0; let ((dh, newh, maxh, mrec, mstack, mwrec, area), (mut max_slopes, h_t)) = rayon::join( || { - let mut dh = downhill(|posi| h[posi], |posi| is_ocean(posi) && h[posi] <= 0.0); + let mut dh = downhill( + map_size_lg, + |posi| h[posi], + |posi| is_ocean(posi) && h[posi] <= 0.0, + ); debug!("Computed downhill..."); - let (boundary_len, _indirection, newh, maxh) = get_lakes(|posi| h[posi], &mut dh); + let (boundary_len, _indirection, newh, maxh) = + get_lakes(map_size_lg, |posi| h[posi], &mut dh); debug!("Got lakes..."); let (mrec, mstack, mwrec) = get_multi_rec( + map_size_lg, |posi| h[posi], &dh, &newh, @@ -795,16 +821,17 @@ fn erode( debug!("Got multiple receivers..."); // TODO: Figure out how to switch between single-receiver and multi-receiver // drainage, as the former is much less computationally costly. - // let area = get_drainage(&newh, &dh, boundary_len); - let area = get_multi_drainage(&mstack, &mrec, &*mwrec, boundary_len); + // let area = get_drainage(map_size_lg, &newh, &dh, boundary_len); + let area = get_multi_drainage(map_size_lg, &mstack, &mrec, &*mwrec, boundary_len); debug!("Got flux..."); (dh, newh, maxh, mrec, mstack, mwrec, area) }, || { rayon::join( || { - let max_slope = - get_max_slope(h, rock_strength_nz, |posi| height_scale(n_f(posi))); + let max_slope = get_max_slope(map_size_lg, h, rock_strength_nz, |posi| { + height_scale(n_f(posi)) + }); debug!("Got max slopes..."); max_slope }, @@ -852,9 +879,10 @@ fn erode( let m = m_f(posi) as f64; let mwrec_i = &mwrec[posi]; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)) - .map(|e| e as f64); + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let knew = (k * (p as f64 @@ -901,7 +929,7 @@ fn erode( let k_da = k_da * kd_factor; let mwrec_i = &mwrec[posi]; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { let mwrec_kk = mwrec_i[kk] as f64; #[rustfmt::skip] @@ -947,8 +975,9 @@ fn erode( let k = (mwrec_kk * (uplift_i + max_uplift as f64 * g_i / p as f64)) / (1.0 + k_da * (mwrec_kk * chunk_neutral_area).powf(q)) / max_slope.powf(q_); - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)) - .map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let knew = (k @@ -966,11 +995,10 @@ fn erode( debug!("Computed stream power factors..."); - let mut lake_water_volume = - vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut elev = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut h_p = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut deltah = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut lake_water_volume = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut elev = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut h_p = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut deltah = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); // calculate the elevation / SPL, including sediment flux let tol1 = 1.0e-4 as Compute * (maxh as Compute + 1.0); @@ -1004,8 +1032,8 @@ fn erode( // Gauss-Seidel iteration - let mut lake_silt = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut lake_sill = vec![-1isize; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut lake_silt = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut lake_sill = vec![-1isize; map_size_lg.chunks_len()].into_boxed_slice(); let mut n_gs_stream_power_law = 0; // NOTE: Increasing this can theoretically sometimes be necessary for @@ -1102,7 +1130,7 @@ fn erode( } } let mwrec_i = &mwrec[posi]; - mrec_downhill(&mrec, posi).for_each(|(k, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(k, posj)| { let stack_posj = mstack_inv[posj]; deltah[stack_posj] += deltah_i * mwrec_i[k]; }); @@ -1251,7 +1279,7 @@ fn erode( if (n - 1.0).abs() <= 1.0e-3 && (q_ - 1.0).abs() <= 1.0e-3 { let mut f = h0; let mut df = 1.0; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { let posj_stack = mstack_inv[posj]; let h_j = h_stack[posj_stack] as f64; // This can happen in cases where receiver kk is neither uphill of @@ -1277,7 +1305,7 @@ fn erode( let mut errp = 2.0 * tolp; let mut rec_heights = [0.0; 8]; let mut mask = [MaskType::new(false); 8]; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { let posj_stack = mstack_inv[posj]; let h_j = h_stack[posj_stack]; // NOTE: Fastscape does h_t[posi] + uplift(posi) as f64 >= h_t[posj] @@ -1365,8 +1393,9 @@ fn erode( // of wh_j. new_h_i = wh_j; } else if compute_stats && new_h_i > 0.0 { - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)) - .map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let dz = (new_h_i - wh_j).max(0.0); let mag_slope = dz / neighbor_distance; @@ -1461,7 +1490,9 @@ fn erode( } else { let posj = posj as usize; let h_j = h[posj]; - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)).map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance_squared = (neighbor_coef * dxy).magnitude_squared(); let dh = (h_i - h_j) as f64; // H_i_fact = (h_i - h_j) / (||p_i - p_j||^2 + (h_i - h_j)^2) @@ -1590,7 +1621,9 @@ fn erode( // redistribute uplift to other neighbors. let (posk, h_k) = (posj, h_j); let (posk, h_k) = if h_k < h_j { (posk, h_k) } else { (posj, h_j) }; - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posk)).map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posk)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let dz = (new_h_i - h_k).max(0.0); let mag_slope = dz / neighbor_distance; @@ -1688,6 +1721,7 @@ fn erode( /// /// See https://github.com/mewo2/terrain/blob/master/terrain.js pub fn fill_sinks( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F + Sync, is_ocean: impl Fn(usize) -> bool + Sync, ) -> Box<[F]> { @@ -1695,7 +1729,7 @@ pub fn fill_sinks( // but doesn't change altitudes. let epsilon = F::zero(); let infinity = F::infinity(); - let range = 0..WORLD_SIZE.x * WORLD_SIZE.y; + let range = 0..map_size_lg.chunks_len(); let oldh = range .into_par_iter() .map(|posi| h(posi)) @@ -1724,7 +1758,7 @@ pub fn fill_sinks( if nh == oh { return; } - for nposi in neighbors(posi) { + for nposi in neighbors(map_size_lg, posi) { let onbh = newh[nposi]; let nbh = onbh + epsilon; // If there is even one path downhill from this node's original height, fix @@ -1775,6 +1809,7 @@ pub fn fill_sinks( /// indirection vector. #[allow(clippy::filter_next)] // TODO: Pending review in #587 pub fn get_lakes( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F, downhill: &mut [isize], ) -> (usize, Box<[i32]>, Box<[u32]>, F) { @@ -1799,7 +1834,7 @@ pub fn get_lakes( // node, so we should access that entry and increment it, then set our own // entry to it. let mut boundary = Vec::with_capacity(downhill.len()); - let mut indirection = vec![/*-1i32*/0i32; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut indirection = vec![/*-1i32*/0i32; map_size_lg.chunks_len()].into_boxed_slice(); let mut newh = Vec::with_capacity(downhill.len()); @@ -1837,7 +1872,7 @@ pub fn get_lakes( let mut cur = start; while cur < newh.len() { let node = newh[cur as usize]; - uphill(downhill, node as usize).for_each(|child| { + uphill(map_size_lg, downhill, node as usize).for_each(|child| { // lake_idx is the index of our lake root. indirection[child] = chunk_idx as i32; indirection_[child] = indirection_idx; @@ -1878,7 +1913,7 @@ pub fn get_lakes( // our own lake's entry list. If the maximum of the heights we get out // from this process is greater than the maximum of this chunk and its // neighbor chunk, we switch to this new edge. - neighbors(chunk_idx).for_each(|neighbor_idx| { + neighbors(map_size_lg, chunk_idx).for_each(|neighbor_idx| { let neighbor_height = h(neighbor_idx); let neighbor_lake_idx_ = indirection_[neighbor_idx]; let neighbor_lake_idx = neighbor_lake_idx_ as usize; @@ -1944,24 +1979,33 @@ pub fn get_lakes( "For edge {:?} between lakes {:?}, couldn't find partner for pass \ {:?}. Should never happen; maybe forgot to set both edges?", ( - (chunk_idx, uniform_idx_as_vec2(chunk_idx as usize)), - (neighbor_idx, uniform_idx_as_vec2(neighbor_idx as usize)) + ( + chunk_idx, + uniform_idx_as_vec2(map_size_lg, chunk_idx as usize) + ), + ( + neighbor_idx, + uniform_idx_as_vec2(map_size_lg, neighbor_idx as usize) + ) ), ( ( lake_chunk_idx, - uniform_idx_as_vec2(lake_chunk_idx as usize), + uniform_idx_as_vec2(map_size_lg, lake_chunk_idx as usize), lake_idx_ ), ( neighbor_lake_chunk_idx, - uniform_idx_as_vec2(neighbor_lake_chunk_idx as usize), + uniform_idx_as_vec2( + map_size_lg, + neighbor_lake_chunk_idx as usize + ), neighbor_lake_idx_ ) ), ( - (pass.0, uniform_idx_as_vec2(pass.0 as usize)), - (pass.1, uniform_idx_as_vec2(pass.1 as usize)) + (pass.0, uniform_idx_as_vec2(map_size_lg, pass.0 as usize)), + (pass.1, uniform_idx_as_vec2(map_size_lg, pass.1 as usize)) ), ); } @@ -2050,7 +2094,7 @@ pub fn get_lakes( downhill[lake_chunk_idx] = neighbor_idx as isize; // Also set the indirection of the lake bottom to the negation of the // lake side of the chosen pass (chunk_idx). - // NOTE: This can't overflow i32 because WORLD_SIZE.x * WORLD_SIZE.y should fit + // NOTE: This can't overflow i32 because map_size_lg.chunks_len() should fit // in an i32. indirection[lake_chunk_idx] = -(chunk_idx as i32); // Add this edge to the sorted list. @@ -2096,7 +2140,7 @@ pub fn get_lakes( InQueue, WithRcv, } - let mut tag = vec![Tag::UnParsed; WORLD_SIZE.x * WORLD_SIZE.y]; + let mut tag = vec![Tag::UnParsed; map_size_lg.chunks_len()]; // TODO: Combine with adding to vector. let mut filling_queue = Vec::with_capacity(downhill.len()); @@ -2164,17 +2208,17 @@ pub fn get_lakes( tag[neighbor_pass_idx] = Tag::WithRcv; tag[pass_idx] = Tag::InQueue; - let outflow_coords = uniform_idx_as_vec2(neighbor_pass_idx); + let outflow_coords = uniform_idx_as_vec2(map_size_lg, neighbor_pass_idx); let elev = h(neighbor_pass_idx).max(h(pass_idx)); while let Some(node) = filling_queue.pop() { - let coords = uniform_idx_as_vec2(node); + let coords = uniform_idx_as_vec2(map_size_lg, node); let mut rcv = -1; let mut rcv_cost = -f64::INFINITY; /*f64::EPSILON;*/ let outflow_distance = (outflow_coords - coords).map(|e| e as f64); - neighbors(node).for_each(|ineighbor| { + neighbors(map_size_lg, node).for_each(|ineighbor| { if indirection[ineighbor] != chunk_idx as i32 && ineighbor != chunk_idx && ineighbor != neighbor_pass_idx @@ -2182,7 +2226,8 @@ pub fn get_lakes( { return; } - let dxy = (uniform_idx_as_vec2(ineighbor) - coords).map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, ineighbor) - coords) + .map(|e| e as f64); let neighbor_distance = /*neighbor_coef * */dxy; let tag = &mut tag[ineighbor]; match *tag { @@ -2224,7 +2269,7 @@ pub fn get_lakes( let mut cur = start; let mut node = first_idx; loop { - uphill(downhill, node as usize).for_each(|child| { + uphill(map_size_lg, downhill, node as usize).for_each(|child| { // lake_idx is the index of our lake root. // Check to make sure child (flowing into us) is in the same lake. if indirection[child] == chunk_idx as i32 || child == chunk_idx { @@ -2245,10 +2290,11 @@ pub fn get_lakes( /// Iterate through set neighbors of multi-receiver flow. pub fn mrec_downhill<'a>( + map_size_lg: MapSizeLg, mrec: &'a [u8], posi: usize, ) -> impl Clone + Iterator + 'a { - let pos = uniform_idx_as_vec2(posi); + let pos = uniform_idx_as_vec2(map_size_lg, posi); let mrec_i = mrec[posi]; NEIGHBOR_DELTA .iter() @@ -2257,13 +2303,14 @@ pub fn mrec_downhill<'a>( .map(move |(k, &(x, y))| { ( k, - vec2_as_uniform_idx(Vec2::new(pos.x + x as i32, pos.y + y as i32)), + vec2_as_uniform_idx(map_size_lg, Vec2::new(pos.x + x as i32, pos.y + y as i32)), ) }) } /// Algorithm for computing multi-receiver flow. /// +/// * `map_size_lg`: Size of the underlying map. /// * `h`: altitude /// * `downhill`: single receiver /// * `newh`: single receiver stack @@ -2281,6 +2328,7 @@ pub fn mrec_downhill<'a>( #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] // TODO: Pending review in #587 pub fn get_multi_rec>( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F + Sync, downhill: &[isize], newh: &[u32], @@ -2361,18 +2409,15 @@ pub fn get_multi_rec>( let mut mrec_ij = 0u8; let mut ndon_ij = 0u8; let neighbor_iter = |posi| { - let pos = uniform_idx_as_vec2(posi); + let pos = uniform_idx_as_vec2(map_size_lg, posi); NEIGHBOR_DELTA .iter() .map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y)) .enumerate() .filter(move |&(_, pos)| { - pos.x >= 0 - && pos.y >= 0 - && pos.x < WORLD_SIZE.x as i32 - && pos.y < WORLD_SIZE.y as i32 + pos.x >= 0 && pos.y >= 0 && pos.x < nx as i32 && pos.y < ny as i32 }) - .map(move |(k, pos)| (k, vec2_as_uniform_idx(pos))) + .map(move |(k, pos)| (k, vec2_as_uniform_idx(map_size_lg, pos))) }; neighbor_iter(ij).for_each(|(k, ijk)| { @@ -2399,9 +2444,10 @@ pub fn get_multi_rec>( let mut sumweight = czero; let mut wrec = [czero; 8]; let mut nrec = 0; - mrec_downhill(&mrec, ij).for_each(|(k, ijk)| { - let lrec_ijk = ((uniform_idx_as_vec2(ijk) - uniform_idx_as_vec2(ij)) - .map(|e| e as Compute) + mrec_downhill(map_size_lg, &mrec, ij).for_each(|(k, ijk)| { + let lrec_ijk = ((uniform_idx_as_vec2(map_size_lg, ijk) + - uniform_idx_as_vec2(map_size_lg, ij)) + .map(|e| e as Compute) * dxdy) .magnitude(); let wrec_ijk = (wh[ij] - wh[ijk]).into() / lrec_ijk; @@ -2446,7 +2492,7 @@ pub fn get_multi_rec>( while let Some(ijn) = parse.pop() { // we add the node to the stack stack.push(ijn as u32); - mrec_downhill(&mrec, ijn).for_each(|(_, ijr)| { + mrec_downhill(map_size_lg, &mrec, ijn).for_each(|(_, ijr)| { let (_, ref mut vis_ijr) = don_vis[ijr]; if *vis_ijr >= 1 { *vis_ijr -= 1; @@ -2474,6 +2520,7 @@ pub fn get_multi_rec>( #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] pub fn do_erosion( + map_size_lg: MapSizeLg, _max_uplift: f32, n_steps: usize, seed: &RandomField, @@ -2495,59 +2542,59 @@ pub fn do_erosion( k_da_scale: impl Fn(f64) -> f64, ) -> (Box<[Alt]>, Box<[Alt]> /* , Box<[Alt]> */) { debug!("Initializing erosion arrays..."); - let oldh_ = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let oldh_ = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| oldh(posi) as Alt) .collect::>() .into_boxed_slice(); // Topographic basement (The height of bedrock, not including sediment). - let mut b = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let mut b = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| oldb(posi) as Alt) .collect::>() .into_boxed_slice(); // Stream power law slope exponent--link between channel slope and erosion rate. - let n = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let n = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| n(posi)) .collect::>() .into_boxed_slice(); // Stream power law concavity index (θ = m/n), turned into an exponent on // drainage (which is a proxy for discharge according to Hack's Law). - let m = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let m = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| theta(posi) * n[posi]) .collect::>() .into_boxed_slice(); // Stream power law erodability constant for fluvial erosion (bedrock) - let kf = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let kf = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| kf(posi)) .collect::>() .into_boxed_slice(); // Stream power law erodability constant for hillslope diffusion (bedrock) - let kd = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let kd = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| kd(posi)) .collect::>() .into_boxed_slice(); // Deposition coefficient - let g = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let g = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| g(posi)) .collect::>() .into_boxed_slice(); - let epsilon_0 = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let epsilon_0 = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| epsilon_0(posi)) .collect::>() .into_boxed_slice(); - let alpha = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let alpha = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| alpha(posi)) .collect::>() .into_boxed_slice(); - let mut wh = vec![0.0; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut wh = vec![0.0; map_size_lg.chunks_len()].into_boxed_slice(); // TODO: Don't do this, maybe? // (To elaborate, maybe we should have varying uplift or compute it some other // way). @@ -2595,6 +2642,7 @@ pub fn do_erosion( (0..n_steps).for_each(|i| { debug!("Erosion iteration #{:?}", i); erode( + map_size_lg, &mut h, &mut b, &mut wh, diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 2dee374295..02254e01a1 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -1,340 +1,274 @@ use crate::{ - sim::{RiverKind, WorldSim, WORLD_SIZE}, - Index, CONFIG, + column::ColumnSample, + sim::{RiverKind, WorldSim}, + IndexRef, CONFIG, +}; +use common::{ + terrain::{ + map::{Connection, ConnectionKind, MapConfig, MapSample}, + vec2_as_uniform_idx, TerrainChunkSize, NEIGHBOR_DELTA, + }, + vol::RectVolSize, }; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use std::{f32, f64}; use vek::*; -pub struct MapConfig { - /// Dimensions of the window being written to. Defaults to WORLD_SIZE. - pub dimensions: Vec2, - /// x, y, and z of top left of map (defaults to (0.0, 0.0, - /// CONFIG.sea_level)). - pub focus: Vec3, - /// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain - /// makes smaller differences in altitude appear larger. - /// - /// Defaults to CONFIG.mountain_scale. - pub gain: f32, - /// lgain is used for shading purposes and refers to how much impact a - /// change in the z direction has on the perceived slope relative to the - /// same change in x and y. - /// - /// Defaults to TerrainChunkSize::RECT_SIZE.x. - pub lgain: f64, - /// Scale is like gain, but for x and y rather than z. - /// - /// Defaults to WORLD_SIZE.x / dimensions.x (NOTE: fractional, not integer, - /// division!). - pub scale: f64, - /// Vector that indicates which direction light is coming from, if shading - /// is turned on. - /// - /// Right-handed coordinate system: light is going left, down, and - /// "backwards" (i.e. on the map, where we translate the y coordinate on - /// the world map to z in the coordinate system, the light comes from -y - /// on the map and points towards +y on the map). In a right - /// handed coordinate system, the "camera" points towards -z, so positive z - /// is backwards "into" the camera. - /// - /// "In world space the x-axis will be pointing east, the y-axis up and the - /// z-axis will be pointing south" - /// - /// Defaults to (-0.8, -1.0, 0.3). - pub light_direction: Vec3, - /// If true, only the basement (bedrock) is used for altitude; otherwise, - /// the surface is used. - /// - /// Defaults to false. - pub is_basement: bool, - /// If true, water is rendered; otherwise, the surface without water is - /// rendered, even if it is underwater. - /// - /// Defaults to true. - pub is_water: bool, - /// If true, 3D lighting and shading are turned on. Otherwise, a plain - /// altitude map is used. - /// - /// Defaults to true. - pub is_shaded: bool, - /// If true, the red component of the image is also used for temperature - /// (redder is hotter). Defaults to false. - pub is_temperature: bool, - /// If true, the blue component of the image is also used for humidity - /// (bluer is wetter). - /// - /// Defaults to false. - pub is_humidity: bool, - /// Record debug information. - /// - /// Defaults to false. - pub is_debug: bool, -} +/// A sample function that grabs the connections at a chunk. +/// +/// Currently this just supports rivers, but ideally it can be extended past +/// that. +/// +/// A sample function that grabs surface altitude at a column. +/// (correctly reflecting settings like is_basement and is_water). +/// +/// The altitude produced by this function at a column corresponding to a +/// particular chunk should be identical to the altitude produced by +/// sample_pos at that chunk. +/// +/// You should generally pass a closure over this function into generate +/// when constructing a map for the first time. +/// However, if repeated construction is needed, or alternate base colors +/// are to be used for some reason, one should pass a custom function to +/// generate instead (e.g. one that just looks up the height in a cached +/// array). +pub fn sample_wpos(config: &MapConfig, sampler: &WorldSim, wpos: Vec2) -> f32 { + let MapConfig { + focus, + gain, -pub const QUADRANTS: usize = 4; + is_basement, + is_water, + .. + } = *config; -pub struct MapDebug { - pub quads: [[u32; QUADRANTS]; QUADRANTS], - pub rivers: u32, - pub lakes: u32, - pub oceans: u32, -} - -impl Default for MapConfig { - fn default() -> Self { - let dimensions = WORLD_SIZE; - Self { - dimensions, - focus: Vec3::new(0.0, 0.0, CONFIG.sea_level as f64), - gain: CONFIG.mountain_scale, - lgain: TerrainChunkSize::RECT_SIZE.x as f64, - scale: WORLD_SIZE.x as f64 / dimensions.x as f64, - light_direction: Vec3::new(-0.8, -1.0, 0.3), - - is_basement: false, - is_water: true, - is_shaded: true, - is_temperature: false, - is_humidity: false, - is_debug: false, - } - } -} - -impl MapConfig { - /// Generates a map image using the specified settings. Note that it will - /// write from left to write from (0, 0) to dimensions - 1, inclusive, - /// with 4 1-byte color components provided as (r, g, b, a). It is up - /// to the caller to provide a function that translates this information - /// into the correct format for a buffer and writes to it. - #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 - #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 - #[allow(clippy::map_identity)] // TODO: Pending review in #587 - #[allow(clippy::many_single_char_names)] - pub fn generate( - &self, - sampler: &WorldSim, - index: &Index, - mut write_pixel: impl FnMut(Vec2, (u8, u8, u8, u8)), - ) -> MapDebug { - let MapConfig { - dimensions, - focus, - gain, - lgain, - scale, - light_direction, - - is_basement, - is_water, - is_shaded, - is_temperature, - is_humidity, - is_debug, - } = *self; - - let light = light_direction.normalized(); - let mut quads = [[0u32; QUADRANTS]; QUADRANTS]; - let mut rivers = 0u32; - let mut lakes = 0u32; - let mut oceans = 0u32; - - let focus_rect = Vec2::from(focus); - let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64; - - (0..dimensions.y * dimensions.x).for_each(|chunk_idx| { - let i = chunk_idx % dimensions.x as usize; - let j = chunk_idx / dimensions.x as usize; - - let pos = (focus_rect + Vec2::new(i as f64, j as f64) * scale).map(|e: f64| e as i32); - - let ( - alt, - basement, - water_alt, - humidity, - temperature, - downhill, - river_kind, - is_path, - is_cave, - near_site, - ) = sampler - .get(pos) - .map(|sample| { - ( - sample.alt, - sample.basement, - sample.water_alt, - sample.humidity, - sample.temp, - sample.downhill, - sample.river.river_kind, - sample.path.0.is_way(), - sample.cave.0.is_way(), - sample.sites.iter().any(|site| { - index.sites[*site] - .get_origin() - .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) - < 64i32.pow(2) - }), - ) - }) - .unwrap_or(( - CONFIG.sea_level, - CONFIG.sea_level, - CONFIG.sea_level, - 0.0, - 0.0, - None, - None, - false, - false, - false, - )); - let humidity = humidity.min(1.0).max(0.0); - let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; - let pos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); - let downhill_pos = (downhill - .map(|downhill_pos| downhill_pos) - .unwrap_or(pos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - - pos) - + pos; - let downhill_alt = sampler - .get_wpos(downhill_pos) - .map(|s| if is_basement { s.basement } else { s.alt }) - .unwrap_or(CONFIG.sea_level); - let alt = if is_basement { basement } else { alt }; - let cross_pos = pos - + ((downhill_pos - pos) - .map(|e| e as f32) - .rotated_z(f32::consts::FRAC_PI_2) - .map(|e| e as i32)); - let cross_alt = sampler - .get_wpos(cross_pos) - .map(|s| if is_basement { s.basement } else { s.alt }) - .unwrap_or(CONFIG.sea_level); - // Pointing downhill, forward - // (index--note that (0,0,1) is backward right-handed) - let forward_vec = Vec3::new( - (downhill_pos.x - pos.x) as f64, - (downhill_alt - alt) as f64 * lgain, - (downhill_pos.y - pos.y) as f64, - ); - // Pointing 90 degrees left (in horizontal xy) of downhill, up - // (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed) - let up_vec = Vec3::new( - (cross_pos.x - pos.x) as f64, - (cross_alt - alt) as f64 * lgain, - (cross_pos.y - pos.y) as f64, - ); - // Then cross points "to the right" (upwards) on a right-handed coordinate - // system. (right-handed coordinate system means (0, 0, 1.0) is - // "forward" into the screen). - let surface_normal = forward_vec.cross(up_vec).normalized(); - let light = (surface_normal.dot(light) + 1.0) / 2.0; - let light = (light * 0.9) + 0.1; - - let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; - let true_alt = (alt as f64 - focus.z) / gain as f64; - let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0); - let water_alt = true_water_alt.min(1.0).max(0.0); - let alt = true_alt.min(1.0).max(0.0); - if is_debug { - let quad = - |x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1); - if river_kind.is_none() || humidity != 0.0 { - quads[quad(humidity)][quad(temperature)] += 1; - } - match river_kind { - Some(RiverKind::River { .. }) => { - rivers += 1; - }, - Some(RiverKind::Lake { .. }) => { - lakes += 1; - }, - Some(RiverKind::Ocean { .. }) => { - oceans += 1; - }, - None => {}, - } - } - - let water_color_factor = 2.0; - let g_water = 32.0 * water_color_factor; - let b_water = 64.0 * water_color_factor; - let rgba = match (river_kind, (is_water, true_alt >= true_sea_level)) { - (_, (false, _)) | (None, (_, true)) => { - let (r, g, b) = ( - (if is_shaded { alt } else { alt } - * if is_temperature { - temperature as f64 - } else if is_shaded { - alt - } else { - 0.0 - }) - .sqrt(), - if is_shaded { 0.4 + (alt * 0.6) } else { alt }, - (if is_shaded { alt } else { alt } - * if is_humidity { - humidity as f64 - } else if is_shaded { - alt - } else { - 0.0 - }) - .sqrt(), - ); - let light = if is_shaded { light } else { 1.0 }; - ( - (r * light * 255.0) as u8, - (g * light * 255.0) as u8, - (b * light * 255.0) as u8, - 255, - ) - }, - (Some(RiverKind::Ocean), _) => ( - 0, - ((g_water - water_depth * g_water) * 1.0) as u8, - ((b_water - water_depth * b_water) * 1.0) as u8, - 255, - ), - (Some(RiverKind::River { .. }), _) => ( - 0, - g_water as u8 + (alt * (127.0 - g_water)) as u8, - b_water as u8 + (alt * (255.0 - b_water)) as u8, - 255, - ), - (None, _) | (Some(RiverKind::Lake { .. }), _) => ( - 0, - (((g_water + water_alt * (127.0 - 32.0)) + (-water_depth * g_water)) * 1.0) - as u8, - (((b_water + water_alt * (255.0 - b_water)) + (-water_depth * b_water)) * 1.0) - as u8, - 255, - ), - }; - - let rgba = if near_site { - (0x57, 0x39, 0x33, 0xFF) - } else if is_path { - (0x37, 0x29, 0x23, 0xFF) - } else if is_cave { - (0x37, 0x37, 0x37, 0xFF) + (sampler + .get_wpos(wpos) + .map(|s| { + if is_basement { s.basement } else { s.alt }.max(if is_water { + s.water_alt } else { - rgba - }; + -f32::INFINITY + }) + }) + .unwrap_or(CONFIG.sea_level) + - focus.z as f32) + / gain as f32 +} - write_pixel(Vec2::new(i, j), rgba); +/// Samples a MapSample at a chunk. +/// +/// You should generally pass a closure over this function into generate +/// when constructing a map for the first time. +/// However, if repeated construction is needed, or alternate base colors +/// are to be used for some reason, one should pass a custom function to +/// generate instead (e.g. one that just looks up the color in a cached +/// array). +// NOTE: Deliberately not putting Rgb colors here in the config file; they +// aren't hot reloaded anyway, and for various reasons they're probably not a +// good idea to update in that way (for example, we currently want water colors +// to match voxygen's). Eventually we'll fix these sorts of issues in some +// other way. +pub fn sample_pos( + config: &MapConfig, + sampler: &WorldSim, + index: IndexRef, + samples: Option<&[Option]>, + pos: Vec2, +) -> MapSample { + let map_size_lg = config.map_size_lg(); + let MapConfig { + focus, + gain, + + is_basement, + is_water, + is_shaded, + is_temperature, + is_humidity, + // is_debug, + .. + } = *config; + + let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64; + + let ( + chunk_idx, + alt, + basement, + water_alt, + humidity, + temperature, + downhill, + river_kind, + spline_derivative, + is_path, + is_cave, + near_site, + ) = sampler + .get(pos) + .map(|sample| { + ( + Some(vec2_as_uniform_idx(map_size_lg, pos)), + sample.alt, + sample.basement, + sample.water_alt, + sample.humidity, + sample.temp, + sample.downhill, + sample.river.river_kind, + sample.river.spline_derivative, + sample.path.0.is_way(), + sample.cave.0.is_way(), + sample.sites.iter().any(|site| { + index.sites[*site] + .get_origin() + .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) + < 64i32.pow(2) + }), + ) + }) + .unwrap_or(( + None, + CONFIG.sea_level, + CONFIG.sea_level, + CONFIG.sea_level, + 0.0, + 0.0, + None, + None, + Vec2::zero(), + false, + false, + false, + )); + + let humidity = humidity.min(1.0).max(0.0); + let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; + let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let column_rgb = samples + .and_then(|samples| { + chunk_idx + .and_then(|chunk_idx| samples.get(chunk_idx)) + .map(Option::as_ref) + .flatten() + }) + .map(|sample| { + // TODO: Eliminate the redundancy between this and the block renderer. + let alt = sample.alt; + let basement = sample.basement; + let grass_depth = (1.5 + 2.0 * sample.chaos).min(alt - basement); + let wposz = if is_basement { basement } else { alt }; + if is_basement && wposz < alt - grass_depth { + Lerp::lerp( + sample.sub_surface_color, + sample.stone_col.map(|e| e as f32 / 255.0), + (alt - grass_depth - wposz as f32) * 0.15, + ) + .map(|e| e as f64) + } else { + Lerp::lerp( + sample.sub_surface_color, + sample.surface_color, + ((wposz as f32 - (alt - grass_depth)) / grass_depth).powf(0.5), + ) + .map(|e| e as f64) + } }); - MapDebug { - quads, - rivers, - lakes, - oceans, - } + let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); + let alt = if is_basement { basement } else { alt }; + + let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; + let true_alt = (alt as f64 - focus.z) / gain as f64; + let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0); + let alt = true_alt.min(1.0).max(0.0); + + let water_color_factor = 2.0; + let g_water = 32.0 * water_color_factor; + let b_water = 64.0 * water_color_factor; + let default_rgb = Rgb::new( + if is_shaded || is_temperature { + 1.0 + } else { + 0.0 + }, + if is_shaded { 1.0 } else { alt }, + if is_shaded || is_humidity { 1.0 } else { 0.0 }, + ); + let column_rgb = column_rgb.unwrap_or(default_rgb); + let mut connections = [None; 8]; + let mut has_connections = false; + // TODO: Support non-river connections. + // TODO: Support multiple connections. + let river_width = river_kind.map(|river| match river { + RiverKind::River { cross_section } => cross_section.x, + RiverKind::Lake { .. } | RiverKind::Ocean => TerrainChunkSize::RECT_SIZE.x as f32, + }); + if let (Some(river_width), true) = (river_width, is_water) { + let downhill_pos = downhill_wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + NEIGHBOR_DELTA + .iter() + .zip((&mut connections).iter_mut()) + .filter(|&(&offset, _)| downhill_pos - pos == Vec2::from(offset)) + .for_each(|(_, connection)| { + has_connections = true; + *connection = Some(Connection { + kind: ConnectionKind::River, + spline_derivative, + width: river_width, + }); + }); + }; + let rgb = match (river_kind, (is_water, true_alt >= true_sea_level)) { + (_, (false, _)) | (None, (_, true)) | (Some(RiverKind::River { .. }), _) => { + let (r, g, b) = ( + (column_rgb.r + * if is_temperature { + temperature as f64 + } else { + column_rgb.r + }) + .sqrt(), + column_rgb.g, + (column_rgb.b + * if is_humidity { + humidity as f64 + } else { + column_rgb.b + }) + .sqrt(), + ); + Rgb::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8) + }, + (None | Some(RiverKind::Lake { .. } | RiverKind::Ocean), _) => Rgb::new( + 0, + ((g_water - water_depth * g_water) * 1.0) as u8, + ((b_water - water_depth * b_water) * 1.0) as u8, + ), + }; + // TODO: Make principled. + let rgb = if near_site { + Rgb::new(0x57, 0x39, 0x33) + } else if is_path { + Rgb::new(0x37, 0x29, 0x23) + } else if is_cave { + Rgb::new(0x37, 0x37, 0x37) + } else { + rgb + }; + + MapSample { + rgb, + alt: if is_water { + true_alt.max(true_water_alt) + } else { + true_alt + }, + downhill_wpos, + connections: if has_connections { + Some(connections) + } else { + None + }, } } diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index d8a3239e23..7eff3d9d8a 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -14,33 +14,38 @@ pub use self::{ get_rivers, mrec_downhill, Alt, RiverData, RiverKind, }, location::Location, - map::{MapConfig, MapDebug}, + map::{sample_pos, sample_wpos}, util::{ - cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors, - uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, - NEIGHBOR_DELTA, + cdf_irwin_hall, downhill, get_horizon_map, get_oceans, local_cells, map_edge_factor, + uniform_noise, uphill, InverseCdf, ScaleBias, }, way::{Cave, Path, Way}, }; use crate::{ all::ForestKind, + block::BlockGen, civ::Place, + column::ColumnGen, site::Site, - util::{seed_expan, FastNoise, RandomField, StructureGen2d, LOCALITY, NEIGHBORS}, - Index, CONFIG, + util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d, LOCALITY, NEIGHBORS}, + IndexRef, CONFIG, }; use common::{ assets, + msg::server::WorldMapMsg, store::Id, - terrain::{BiomeKind, TerrainChunkSize}, + terrain::{ + map::MapConfig, uniform_idx_as_vec2, vec2_as_uniform_idx, BiomeKind, MapSizeLg, + TerrainChunkSize, + }, vol::RectVolSize, }; use noise::{ BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti, Seedable, SuperSimplex, Worley, }; -use num::{Float, Signed}; +use num::{traits::FloatConst, Float, Signed}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; use rayon::prelude::*; @@ -55,19 +60,17 @@ use std::{ use tracing::{debug, warn}; use vek::*; -// NOTE: I suspect this is too small (1024 * 16 * 1024 * 16 * 8 doesn't fit in -// an i32), but we'll see what happens, I guess! We could always store sizes >> -// 3. I think 32 or 64 is the absolute limit though, and would require -// substantial changes. Also, 1024 * 16 * 1024 * 16 is no longer -// cleanly representable in f32 (that stops around 1024 * 4 * 1024 * 4, for -// signed floats anyway) but I think that is probably less important since I -// don't think we actually cast a chunk id to float, just coordinates... could -// be wrong though! -#[allow(clippy::identity_op)] // TODO: Pending review in #587 -pub const WORLD_SIZE: Vec2 = Vec2 { - x: 1024 * 1, - y: 1024 * 1, -}; +/// Default base two logarithm of the world size, in chunks, per dimension. +/// +/// Currently, our default map dimensions are 2^10 × 2^10 chunks, +/// mostly for historical reasons. It is likely that we will increase this +/// default at some point. +const DEFAULT_WORLD_CHUNKS_LG: MapSizeLg = + if let Ok(map_size_lg) = MapSizeLg::new(Vec2 { x: 10, y: 10 }) { + map_size_lg + } else { + panic!("Default world chunk size does not satisfy required invariants."); + }; /// A structure that holds cached noise values and cumulative distribution /// functions for the input that led to those values. See the definition of @@ -182,6 +185,23 @@ pub struct WorldMap_0_5_0 { pub basement: Box<[Alt]>, } +/// Version of the world map intended for use in Veloren 0.7.0. +#[derive(Serialize, Deserialize)] +#[repr(C)] +pub struct WorldMap_0_7_0 { + /// Saved map size. + pub map_size_lg: Vec2, + /// Saved continent_scale hack, to try to better approximate the correct + /// seed according to varying map size. + /// + /// TODO: Remove when generating new maps becomes more principled. + pub continent_scale_hack: f64, + /// Saved altitude height map. + pub alt: Box<[Alt]>, + /// Saved basement height map. + pub basement: Box<[Alt]>, +} + /// Errors when converting a map to the most recent type (currently, /// shared by the various map types, but at some point we might switch to /// version-specific errors if it feels worthwhile). @@ -220,11 +240,12 @@ pub enum WorldFileError { #[repr(u32)] pub enum WorldFile { Veloren0_5_0(WorldMap_0_5_0) = 0, + Veloren0_7_0(WorldMap_0_7_0) = 1, } /// Data for the most recent map type. Update this when you add a new map /// verson. -pub type ModernMap = WorldMap_0_5_0; +pub type ModernMap = WorldMap_0_7_0; /// The default world map. /// @@ -245,9 +266,9 @@ impl WorldFileLegacy { /// should construct a call chain that ultimately ends up with a modern /// version. pub fn into_modern(self) -> Result { - if self.alt.len() != self.basement.len() - || self.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize - { + // NOTE: At this point, we ssume that any remaining legacy maps were 1024 × + // 1024. + if self.alt.len() != self.basement.len() || self.alt.len() != 1024 * 1024 { return Err(WorldFileError::WorldSizeInvalid); } @@ -261,10 +282,35 @@ impl WorldFileLegacy { } impl WorldMap_0_5_0 { + #[inline] + pub fn into_modern(self) -> Result { + let pow_size = (self.alt.len().trailing_zeros()) / 2; + let two_coord_size = 1 << (2 * pow_size); + if self.alt.len() != self.basement.len() || self.alt.len() != two_coord_size { + return Err(WorldFileError::WorldSizeInvalid); + } + + // The recommended continent scale for maps from version 0.5.0 is (in all + // existing cases) just 1.0 << (f64::from(pow_size) - 10.0). + let continent_scale_hack = (f64::from(pow_size) - 10.0).exp2(); + + let map = WorldMap_0_7_0 { + map_size_lg: Vec2::new(pow_size, pow_size), + continent_scale_hack, + alt: self.alt, + basement: self.basement, + }; + + map.into_modern() + } +} + +impl WorldMap_0_7_0 { #[inline] pub fn into_modern(self) -> Result { if self.alt.len() != self.basement.len() - || self.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize + || self.alt.len() != (1 << (self.map_size_lg.x + self.map_size_lg.y)) + || self.continent_scale_hack <= 0.0 { return Err(WorldFileError::WorldSizeInvalid); } @@ -279,7 +325,7 @@ impl WorldFile { /// variant we construct here to make sure we're using the latest map /// version. - pub fn new(map: ModernMap) -> Self { WorldFile::Veloren0_5_0(map) } + pub fn new(map: ModernMap) -> Self { WorldFile::Veloren0_7_0(map) } #[inline] /// Turns a WorldFile into the latest version. Whenever a new map version @@ -287,12 +333,15 @@ impl WorldFile { pub fn into_modern(self) -> Result { match self { WorldFile::Veloren0_5_0(map) => map.into_modern(), + WorldFile::Veloren0_7_0(map) => map.into_modern(), } } } pub struct WorldSim { pub seed: u32, + /// Base 2 logarithm of the map size. + map_size_lg: MapSizeLg, /// Maximum height above sea level of any chunk in the map (not including /// post-erosion warping, cliffs, and other things like that). pub max_height: f32, @@ -307,569 +356,6 @@ impl WorldSim { #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 pub fn generate(seed: u32, opts: WorldOpts) -> Self { - let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); - // NOTE: Change 1.0 to 4.0, while multiplying grid_size by 4, for a 4x - // improvement in world detail. You may also want to set mins_per_sec to 1 / - // (4.0 * 4.0) in ./erosion.rs, in order to get a similar rate of river - // formation. - let continent_scale = 1.0/*4.0*/ - * 5_000.0f64 - .div(32.0) - .mul(TerrainChunkSize::RECT_SIZE.x as f64); - let rock_lacunarity = 2.0; - let uplift_scale = 128.0; - let uplift_turb_scale = uplift_scale / 4.0; - - // NOTE: Changing order will significantly change WorldGen, so try not to! - let gen_ctx = GenCtx { - turb_x_nz: SuperSimplex::new().set_seed(rng.gen()), - turb_y_nz: SuperSimplex::new().set_seed(rng.gen()), - chaos_nz: RidgedMulti::new() - .set_octaves(7) - .set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale)) - .set_seed(rng.gen()), - hill_nz: SuperSimplex::new().set_seed(rng.gen()), - alt_nz: util::HybridMulti::new() - .set_octaves(8) - .set_frequency((10_000.0 / continent_scale) as f64) - // persistence = lacunarity^(-(1.0 - fractal increment)) - .set_lacunarity(util::HybridMulti::DEFAULT_LACUNARITY) - .set_persistence(util::HybridMulti::DEFAULT_LACUNARITY.powf(-(1.0 - 0.0))) - .set_offset(0.0) - .set_seed(rng.gen()), - temp_nz: Fbm::new() - .set_octaves(6) - .set_persistence(0.5) - .set_frequency(1.0 / (((1 << 6) * 64) as f64)) - .set_lacunarity(2.0) - .set_seed(rng.gen()), - - small_nz: BasicMulti::new().set_octaves(2).set_seed(rng.gen()), - rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), - cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), - warp_nz: FastNoise::new(rng.gen()), - tree_nz: BasicMulti::new() - .set_octaves(12) - .set_persistence(0.75) - .set_seed(rng.gen()), - cave_0_nz: SuperSimplex::new().set_seed(rng.gen()), - cave_1_nz: SuperSimplex::new().set_seed(rng.gen()), - - structure_gen: StructureGen2d::new(rng.gen(), 32, 16), - region_gen: StructureGen2d::new(rng.gen(), 400, 96), - cliff_gen: StructureGen2d::new(rng.gen(), 80, 56), - humid_nz: Billow::new() - .set_octaves(9) - .set_persistence(0.4) - .set_frequency(0.2) - .set_seed(rng.gen()), - - fast_turb_x_nz: FastNoise::new(rng.gen()), - fast_turb_y_nz: FastNoise::new(rng.gen()), - - town_gen: StructureGen2d::new(rng.gen(), 2048, 1024), - river_seed: RandomField::new(rng.gen()), - rock_strength_nz: Fbm::new() - .set_octaves(10) - .set_lacunarity(rock_lacunarity) - // persistence = lacunarity^(-(1.0 - fractal increment)) - // NOTE: In paper, fractal increment is roughly 0.25. - .set_persistence(rock_lacunarity.powf(-(1.0 - 0.25))) - .set_frequency( - 1.0 * (5_000.0 / continent_scale) - / (2.0 * TerrainChunkSize::RECT_SIZE.x as f64 * 2.0.powi(10 - 1)), - ) - .set_seed(rng.gen()), - uplift_nz: Worley::new() - .set_seed(rng.gen()) - .set_frequency(1.0 / (TerrainChunkSize::RECT_SIZE.x as f64 * uplift_scale)) - .set_displacement(1.0) - .set_range_function(RangeFunction::Euclidean), - }; - - let river_seed = &gen_ctx.river_seed; - let rock_strength_nz = &gen_ctx.rock_strength_nz; - - // Suppose the old world has grid spacing Δx' = Δy', new Δx = Δy. - // We define grid_scale such that Δx = height_scale * Δx' ⇒ - // grid_scale = Δx / Δx'. - let grid_scale = 1.0f64 / 4.0/*1.0*/; - - // Now, suppose we want to generate a world with "similar" topography, defined - // in this case as having roughly equal slopes at steady state, with the - // simulation taking roughly as many steps to get to the point the - // previous world was at when it finished being simulated. - // - // Some computations with our coupled SPL/debris flow give us (for slope S - // constant) the following suggested scaling parameters to make this - // work: k_fs_scale ≡ (K𝑓 / K𝑓') = grid_scale^(-2m) = - // grid_scale^(-2θn) - let k_fs_scale = |theta, n| grid_scale.powf(-2.0 * (theta * n) as f64); - - // k_da_scale ≡ (K_da / K_da') = grid_scale^(-2q) - let k_da_scale = |q| grid_scale.powf(-2.0 * q); - // - // Some other estimated parameters are harder to come by and *much* more - // dubious, not being accurate for the coupled equation. But for the SPL - // only one we roughly find, for h the height at steady state and time τ - // = time to steady state, with Hack's Law estimated b = 2.0 and various other - // simplifying assumptions, the estimate: - // height_scale ≡ (h / h') = grid_scale^(n) - let height_scale = |n: f32| grid_scale.powf(n as f64) as Alt; - // time_scale ≡ (τ / τ') = grid_scale^(n) - let time_scale = |n: f32| grid_scale.powf(n as f64); - // - // Based on this estimate, we have: - // delta_t_scale ≡ (Δt / Δt') = time_scale - let delta_t_scale = |n: f32| time_scale(n); - // alpha_scale ≡ (α / α') = height_scale^(-1) - let alpha_scale = |n: f32| height_scale(n).recip() as f32; - // - // Slightly more dubiously (need to work out the math better) we find: - // k_d_scale ≡ (K_d / K_d') = grid_scale^2 / (/*height_scale * */ time_scale) - let k_d_scale = |n: f32| grid_scale.powi(2) / (/* height_scale(n) * */time_scale(n)); - // epsilon_0_scale ≡ (ε₀ / ε₀') = height_scale(n) / time_scale(n) - let epsilon_0_scale = |n| (height_scale(n) / time_scale(n) as Alt) as f32; - - // Approximate n for purposes of computation of parameters above over the whole - // grid (when a chunk isn't available). - let n_approx = 1.0; - let max_erosion_per_delta_t = 64.0 * delta_t_scale(n_approx); - let n_steps = 100; - let n_small_steps = 0; - let n_post_load_steps = 0; - - // Logistic regression. Make sure x ∈ (0, 1). - let logit = |x: f64| x.ln() - (-x).ln_1p(); - // 0.5 + 0.5 * tanh(ln(1 / (1 - 0.1) - 1) / (2 * (sqrt(3)/pi))) - let logistic_2_base = 3.0f64.sqrt() * f64::consts::FRAC_2_PI; - // Assumes μ = 0, σ = 1 - let logistic_cdf = |x: f64| (x / logistic_2_base).tanh() * 0.5 + 0.5; - - let min_epsilon = - 1.0 / (WORLD_SIZE.x as f64 * WORLD_SIZE.y as f64).max(f64::EPSILON as f64 * 0.5); - let max_epsilon = (1.0 - 1.0 / (WORLD_SIZE.x as f64 * WORLD_SIZE.y as f64)) - .min(1.0 - f64::EPSILON as f64 * 0.5); - - // No NaNs in these uniform vectors, since the original noise value always - // returns Some. - let ((alt_base, _), (chaos, _)) = rayon::join( - || { - uniform_noise(|_, wposf| { - // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied - // value is from -0.35 * (CONFIG.mountain_scale * 1.05) to - // 0.35 * (CONFIG.mountain_scale * 0.95), but value here is from -0.3675 to - // 0.3325). - Some( - (gen_ctx - .alt_nz - .get((wposf.div(10_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .sub(0.05) - .mul(0.35), - ) - }) - }, - || { - uniform_noise(|_, wposf| { - // From 0 to 1.6, but the distribution before the max is from -1 and 1.6, so - // there is a 50% chance that hill will end up at 0.3 or - // lower, and probably a very high change it will be exactly - // 0. - let hill = (0.0f64 - + gen_ctx - .hill_nz - .get( - (wposf - .mul(32.0) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(1_500.0)) - .into_array(), - ) - .min(1.0) - .max(-1.0) - .mul(1.0) - + gen_ctx - .hill_nz - .get( - (wposf - .mul(32.0) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(400.0)) - .into_array(), - ) - .min(1.0) - .max(-1.0) - .mul(0.3)) - .add(0.3) - .max(0.0); - - // chaos produces a value in [0.12, 1.32]. It is a meta-level factor intended - // to reflect how "chaotic" the region is--how much weird - // stuff is going on on this terrain. - Some( - ((gen_ctx - .chaos_nz - .get((wposf.div(3_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .add(1.0) - .mul(0.5) - // [0, 1] * [0.4, 1] = [0, 1] (but probably towards the lower end) - .mul( - (gen_ctx - .chaos_nz - .get((wposf.div(6_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .abs() - .max(0.4) - .min(1.0), - ) - // Chaos is always increased by a little when we're on a hill (but remember - // that hill is 0.3 or less about 50% of the time). - // [0, 1] + 0.2 * [0, 1.6] = [0, 1.32] - .add(0.2 * hill) - // We can't have *no* chaos! - .max(0.12)) as f32, - ) - }) - }, - ); - - // We ignore sea level because we actually want to be relative to sea level here - // and want things in CONFIG.mountain_scale units, but otherwise this is - // a correct altitude calculation. Note that this is using the - // "unadjusted" temperature. - // - // No NaNs in these uniform vectors, since the original noise value always - // returns Some. - let (alt_old, _) = uniform_noise(|posi, wposf| { - // This is the extension upwards from the base added to some extra noise from -1 - // to 1. - // - // The extra noise is multiplied by alt_main (the mountain part of the - // extension) powered to 0.8 and clamped to [0.15, 1], to get a - // value between [-1, 1] again. - // - // The sides then receive the sequence (y * 0.3 + 1.0) * 0.4, so we have - // [-1*1*(1*0.3+1)*0.4, 1*(1*0.3+1)*0.4] = [-0.52, 0.52]. - // - // Adding this to alt_main thus yields a value between -0.4 (if alt_main = 0 and - // gen_ctx = -1, 0+-1*(0*.3+1)*0.4) and 1.52 (if alt_main = 1 and gen_ctx = 1). - // Most of the points are above 0. - // - // Next, we add again by a sin of alt_main (between [-1, 1])^pow, getting - // us (after adjusting for sign) another value between [-1, 1], and then this is - // multiplied by 0.045 to get [-0.045, 0.045], which is added to [-0.4, 0.52] to - // get [-0.445, 0.565]. - let alt_main = { - // Extension upwards from the base. A positive number from 0 to 1 curved to be - // maximal at 0. Also to be multiplied by CONFIG.mountain_scale. - let alt_main = (gen_ctx - .alt_nz - .get((wposf.div(2_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .abs() - .powf(1.35); - - fn spring(x: f64, pow: f64) -> f64 { x.abs().powf(pow) * x.signum() } - - 0.0 + alt_main - + (gen_ctx - .small_nz - .get( - (wposf - .mul(32.0) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(300.0)) - .into_array(), - ) - .min(1.0) - .max(-1.0)) - .mul(alt_main.powf(0.8).max(/* 0.25 */ 0.15)) - .mul(0.3) - .add(1.0) - .mul(0.4) - + spring(alt_main.abs().powf(0.5).min(0.75).mul(60.0).sin(), 4.0).mul(0.045) - }; - - // Now we can compute the final altitude using chaos. - // We multiply by chaos clamped to [0.1, 1.32] to get a value between [0.03, - // 2.232] for alt_pre, then multiply by CONFIG.mountain_scale and - // add to the base and sea level to get an adjusted value, then - // multiply the whole thing by map_edge_factor (TODO: compute final - // bounds). - // - // [-.3675, .3325] + [-0.445, 0.565] * [0.12, 1.32]^1.2 - // ~ [-.3675, .3325] + [-0.445, 0.565] * [0.07, 1.40] - // = [-.3675, .3325] + ([-0.5785, 0.7345]) - // = [-0.946, 1.067] - Some( - ((alt_base[posi].1 + alt_main.mul((chaos[posi].1 as f64).powf(1.2))) - .mul(map_edge_factor(posi) as f64) - .add( - (CONFIG.sea_level as f64) - .div(CONFIG.mountain_scale as f64) - .mul(map_edge_factor(posi) as f64), - ) - .sub((CONFIG.sea_level as f64).div(CONFIG.mountain_scale as f64))) - as f32, - ) - }); - - // Calculate oceans. - let is_ocean = get_oceans(|posi: usize| alt_old[posi].1); - // NOTE: Uncomment if you want oceans to exclusively be on the border of the - // map. - /* let is_ocean = (0..WORLD_SIZE.x * WORLD_SIZE.y) - .into_par_iter() - .map(|i| map_edge_factor(i) == 0.0) - .collect::>(); */ - let is_ocean_fn = |posi: usize| is_ocean[posi]; - - let turb_wposf_div = 8.0; - let n_func = |posi| { - if is_ocean_fn(posi) { - return 1.0; - } - 1.0 - }; - let old_height = |posi: usize| { - alt_old[posi].1 * CONFIG.mountain_scale * height_scale(n_func(posi)) as f32 - }; - - // NOTE: Needed if you wish to use the distance to the point defining the Worley - // cell, not just the value within that cell. - // let uplift_nz_dist = gen_ctx.uplift_nz.clone().enable_range(true); - - // Recalculate altitudes without oceans. - // NaNs in these uniform vectors wherever is_ocean_fn returns true. - let (alt_old_no_ocean, _) = uniform_noise(|posi, _| { - if is_ocean_fn(posi) { - None - } else { - Some(old_height(posi)) - } - }); - let (uplift_uniform, _) = uniform_noise(|posi, _wposf| { - if is_ocean_fn(posi) { - None - } else { - let oheight = alt_old_no_ocean[posi].0 as f64 - 0.5; - let height = (oheight + 0.5).powf(2.0); - Some(height) - } - }); - - let alt_old_min_uniform = 0.0; - let alt_old_max_uniform = 1.0; - - let inv_func = |x: f64| x; - let alt_exp_min_uniform = inv_func(min_epsilon); - let alt_exp_max_uniform = inv_func(max_epsilon); - - let erosion_factor = |x: f64| { - (inv_func(x) - alt_exp_min_uniform) / (alt_exp_max_uniform - alt_exp_min_uniform) - }; - let rock_strength_div_factor = (2.0 * TerrainChunkSize::RECT_SIZE.x as f64) / 8.0; - let theta_func = |_posi| 0.4; - let kf_func = { - |posi| { - let kf_scale_i = k_fs_scale(theta_func(posi), n_func(posi)) as f64; - if is_ocean_fn(posi) { - return 1.0e-4 * kf_scale_i; - } - - let kf_i = // kf = 1.5e-4: high-high (plateau [fan sediment]) - // kf = 1e-4: high (plateau) - // kf = 2e-5: normal (dike [unexposed]) - // kf = 1e-6: normal-low (dike [exposed]) - // kf = 2e-6: low (mountain) - // -- - // kf = 2.5e-7 to 8e-7: very low (Cordonnier papers on plate tectonics) - // ((1.0 - uheight) * (1.5e-4 - 2.0e-6) + 2.0e-6) as f32 - // - // ACTUAL recorded values worldwide: much lower... - 1.0e-6 - ; - kf_i * kf_scale_i - } - }; - let kd_func = { - |posi| { - let n = n_func(posi); - let kd_scale_i = k_d_scale(n); - if is_ocean_fn(posi) { - let kd_i = 1.0e-2 / 4.0; - return kd_i * kd_scale_i; - } - // kd = 1e-1: high (mountain, dike) - // kd = 1.5e-2: normal-high (plateau [fan sediment]) - // kd = 1e-2: normal (plateau) - let kd_i = 1.0e-2 / 4.0; - kd_i * kd_scale_i - } - }; - let g_func = |posi| { - if map_edge_factor(posi) == 0.0 { - return 0.0; - } - // G = d* v_s / p_0, where - // v_s is the settling velocity of sediment grains - // p_0 is the mean precipitation rate - // d* is the sediment concentration ratio (between concentration near riverbed - // interface, and average concentration over the water column). - // d* varies with Rouse number which defines relative contribution of bed, - // suspended, and washed loads. - // - // G is typically on the order of 1 or greater. However, we are only guaranteed - // to converge for G ≤ 1, so we keep it in the chaos range of [0.12, - // 1.32]. - 1.0 - }; - let epsilon_0_func = |posi| { - // epsilon_0_scale is roughly [using Hack's Law with b = 2 and SPL without - // debris flow or hillslopes] equal to the ratio of the old to new - // area, to the power of -n_i. - let epsilon_0_scale_i = epsilon_0_scale(n_func(posi)); - if is_ocean_fn(posi) { - // marine: ε₀ = 2.078e-3 - let epsilon_0_i = 2.078e-3 / 4.0; - return epsilon_0_i * epsilon_0_scale_i; - } - let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - .map(|e| e as f64); - let turb_wposf = wposf - .mul(5_000.0 / continent_scale) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(turb_wposf_div); - let turb = Vec2::new( - gen_ctx.turb_x_nz.get(turb_wposf.into_array()), - gen_ctx.turb_y_nz.get(turb_wposf.into_array()), - ) * uplift_turb_scale - * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); - let turb_wposf = wposf + turb; - let uheight = gen_ctx - .uplift_nz - .get(turb_wposf.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let wposf3 = Vec3::new( - wposf.x, - wposf.y, - uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, - ); - let rock_strength = gen_ctx - .rock_strength_nz - .get(wposf3.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let center = 0.4; - let dmin = center - 0.05; - let dmax = center + 0.05; - let log_odds = |x: f64| logit(x) - logit(center); - let ustrength = logistic_cdf( - 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) - + 1.0 * log_odds(uheight.min(dmax).max(dmin)), - ); - // marine: ε₀ = 2.078e-3 - // San Gabriel Mountains: ε₀ = 3.18e-4 - // Oregon Coast Range: ε₀ = 2.68e-4 - // Frogs Hollow (peak production = 0.25): ε₀ = 1.41e-4 - // Point Reyes: ε₀ = 8.1e-5 - // Nunnock River (fractured granite, least weathered?): ε₀ = 5.3e-5 - let epsilon_0_i = ((1.0 - ustrength) * (2.078e-3 - 5.3e-5) + 5.3e-5) as f32 / 4.0; - epsilon_0_i * epsilon_0_scale_i - }; - let alpha_func = |posi| { - let alpha_scale_i = alpha_scale(n_func(posi)); - if is_ocean_fn(posi) { - // marine: α = 3.7e-2 - return 3.7e-2 * alpha_scale_i; - } - let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - .map(|e| e as f64); - let turb_wposf = wposf - .mul(5_000.0 / continent_scale) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(turb_wposf_div); - let turb = Vec2::new( - gen_ctx.turb_x_nz.get(turb_wposf.into_array()), - gen_ctx.turb_y_nz.get(turb_wposf.into_array()), - ) * uplift_turb_scale - * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); - let turb_wposf = wposf + turb; - let uheight = gen_ctx - .uplift_nz - .get(turb_wposf.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let wposf3 = Vec3::new( - wposf.x, - wposf.y, - uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, - ); - let rock_strength = gen_ctx - .rock_strength_nz - .get(wposf3.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let center = 0.4; - let dmin = center - 0.05; - let dmax = center + 0.05; - let log_odds = |x: f64| logit(x) - logit(center); - let ustrength = logistic_cdf( - 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) - + 1.0 * log_odds(uheight.min(dmax).max(dmin)), - ); - // Frog Hollow (peak production = 0.25): α = 4.2e-2 - // San Gabriel Mountains: α = 3.8e-2 - // marine: α = 3.7e-2 - // Oregon Coast Range: α = 3e-2 - // Nunnock river (fractured granite, least weathered?): α = 2e-3 - // Point Reyes: α = 1.6e-2 - // The stronger the rock, the faster the decline in soil production. - let alpha_i = (ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32; - alpha_i * alpha_scale_i - }; - let uplift_fn = |posi| { - if is_ocean_fn(posi) { - return 0.0; - } - let height = (uplift_uniform[posi].1 - alt_old_min_uniform) as f64 - / (alt_old_max_uniform - alt_old_min_uniform) as f64; - - let height = height.mul(max_epsilon - min_epsilon).add(min_epsilon); - let height = erosion_factor(height); - assert!(height >= 0.0); - assert!(height <= 1.0); - - // u = 1e-3: normal-high (dike, mountain) - // u = 5e-4: normal (mid example in Yuan, average mountain uplift) - // u = 2e-4: low (low example in Yuan; known that lagoons etc. may have u ~ - // 0.05). u = 0: low (plateau [fan, altitude = 0.0]) - let height = height.mul(max_erosion_per_delta_t); - height as f64 - }; - let alt_func = |posi| { - if is_ocean_fn(posi) { - old_height(posi) - } else { - (old_height(posi) as f64 / CONFIG.mountain_scale as f64) as f32 - 0.5 - } - }; - // Parse out the contents of various map formats into the values we need. let parsed_world_file = (|| { let map = match opts.world_file { @@ -958,12 +444,598 @@ impl WorldSim { } })(); + // NOTE: Change 1.0 to 4.0 for a 4x + // improvement in world detail. We also use this to automatically adjust + // grid_scale (multiplying by 4.0) and multiply mins_per_sec by + // 1.0 / (4.0 * 4.0) in ./erosion.rs, in order to get a similar rate of river + // formation. + // + // FIXME: This is a hack! At some point we will hae a more principled way of + // dealing with this. + let continent_scale_hack = 1.0/*4.0*/; + let (parsed_world_file, map_size_lg) = parsed_world_file + .and_then(|map| match MapSizeLg::new(map.map_size_lg) { + Ok(map_size_lg) => Some((Some(map), map_size_lg)), + Err(e) => { + warn!("World size of map does not satisfy invariants: {:?}", e); + None + }, + }) + .unwrap_or((None, DEFAULT_WORLD_CHUNKS_LG)); + let continent_scale_hack = if let Some(map) = &parsed_world_file { + map.continent_scale_hack + } else { + continent_scale_hack + }; + + let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + let continent_scale = continent_scale_hack + * 5_000.0f64 + .div(32.0) + .mul(TerrainChunkSize::RECT_SIZE.x as f64); + let rock_lacunarity = 2.0; + let uplift_scale = 128.0; + let uplift_turb_scale = uplift_scale / 4.0; + + // NOTE: Changing order will significantly change WorldGen, so try not to! + let gen_ctx = GenCtx { + turb_x_nz: SuperSimplex::new().set_seed(rng.gen()), + turb_y_nz: SuperSimplex::new().set_seed(rng.gen()), + chaos_nz: RidgedMulti::new() + .set_octaves(7) + .set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale)) + .set_seed(rng.gen()), + hill_nz: SuperSimplex::new().set_seed(rng.gen()), + alt_nz: util::HybridMulti::new() + .set_octaves(8) + .set_frequency((10_000.0 / continent_scale) as f64) + // persistence = lacunarity^(-(1.0 - fractal increment)) + .set_lacunarity(util::HybridMulti::DEFAULT_LACUNARITY) + .set_persistence(util::HybridMulti::DEFAULT_LACUNARITY.powf(-(1.0 - 0.0))) + .set_offset(0.0) + .set_seed(rng.gen()), + temp_nz: Fbm::new() + .set_octaves(6) + .set_persistence(0.5) + .set_frequency(1.0 / (((1 << 6) * 64) as f64)) + .set_lacunarity(2.0) + .set_seed(rng.gen()), + + small_nz: BasicMulti::new().set_octaves(2).set_seed(rng.gen()), + rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), + cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), + warp_nz: FastNoise::new(rng.gen()), + tree_nz: BasicMulti::new() + .set_octaves(12) + .set_persistence(0.75) + .set_seed(rng.gen()), + cave_0_nz: SuperSimplex::new().set_seed(rng.gen()), + cave_1_nz: SuperSimplex::new().set_seed(rng.gen()), + + structure_gen: StructureGen2d::new(rng.gen(), 32, 16), + region_gen: StructureGen2d::new(rng.gen(), 400, 96), + cliff_gen: StructureGen2d::new(rng.gen(), 80, 56), + humid_nz: Billow::new() + .set_octaves(9) + .set_persistence(0.4) + .set_frequency(0.2) + .set_seed(rng.gen()), + + fast_turb_x_nz: FastNoise::new(rng.gen()), + fast_turb_y_nz: FastNoise::new(rng.gen()), + + town_gen: StructureGen2d::new(rng.gen(), 2048, 1024), + river_seed: RandomField::new(rng.gen()), + rock_strength_nz: Fbm::new() + .set_octaves(10) + .set_lacunarity(rock_lacunarity) + // persistence = lacunarity^(-(1.0 - fractal increment)) + // NOTE: In paper, fractal increment is roughly 0.25. + .set_persistence(rock_lacunarity.powf(-(1.0 - 0.25))) + .set_frequency( + 1.0 * (5_000.0 / continent_scale) + / (2.0 * TerrainChunkSize::RECT_SIZE.x as f64 * 2.0.powi(10 - 1)), + ) + .set_seed(rng.gen()), + uplift_nz: Worley::new() + .set_seed(rng.gen()) + .set_frequency(1.0 / (TerrainChunkSize::RECT_SIZE.x as f64 * uplift_scale)) + .set_displacement(1.0) + .set_range_function(RangeFunction::Euclidean), + }; + + let river_seed = &gen_ctx.river_seed; + let rock_strength_nz = &gen_ctx.rock_strength_nz; + + // Suppose the old world has grid spacing Δx' = Δy', new Δx = Δy. + // We define grid_scale such that Δx = height_scale * Δx' ⇒ + // grid_scale = Δx / Δx'. + let grid_scale = 1.0f64 / (4.0 / continent_scale_hack)/*1.0*/; + + // Now, suppose we want to generate a world with "similar" topography, defined + // in this case as having roughly equal slopes at steady state, with the + // simulation taking roughly as many steps to get to the point the + // previous world was at when it finished being simulated. + // + // Some computations with our coupled SPL/debris flow give us (for slope S + // constant) the following suggested scaling parameters to make this + // work: k_fs_scale ≡ (K𝑓 / K𝑓') = grid_scale^(-2m) = + // grid_scale^(-2θn) + let k_fs_scale = |theta, n| grid_scale.powf(-2.0 * (theta * n) as f64); + + // k_da_scale ≡ (K_da / K_da') = grid_scale^(-2q) + let k_da_scale = |q| grid_scale.powf(-2.0 * q); + // + // Some other estimated parameters are harder to come by and *much* more + // dubious, not being accurate for the coupled equation. But for the SPL + // only one we roughly find, for h the height at steady state and time τ + // = time to steady state, with Hack's Law estimated b = 2.0 and various other + // simplifying assumptions, the estimate: + // height_scale ≡ (h / h') = grid_scale^(n) + let height_scale = |n: f32| grid_scale.powf(n as f64) as Alt; + // time_scale ≡ (τ / τ') = grid_scale^(n) + let time_scale = |n: f32| grid_scale.powf(n as f64); + // + // Based on this estimate, we have: + // delta_t_scale ≡ (Δt / Δt') = time_scale + let delta_t_scale = |n: f32| time_scale(n); + // alpha_scale ≡ (α / α') = height_scale^(-1) + let alpha_scale = |n: f32| height_scale(n).recip() as f32; + // + // Slightly more dubiously (need to work out the math better) we find: + // k_d_scale ≡ (K_d / K_d') = grid_scale^2 / (/*height_scale * */ time_scale) + let k_d_scale = |n: f32| grid_scale.powi(2) / (/* height_scale(n) * */time_scale(n)); + // epsilon_0_scale ≡ (ε₀ / ε₀') = height_scale(n) / time_scale(n) + let epsilon_0_scale = |n| (height_scale(n) / time_scale(n) as Alt) as f32; + + // Approximate n for purposes of computation of parameters above over the whole + // grid (when a chunk isn't available). + let n_approx = 1.0; + let max_erosion_per_delta_t = 64.0 * delta_t_scale(n_approx); + let n_steps = 100; + let n_small_steps = 0; + let n_post_load_steps = 0; + + // Logistic regression. Make sure x ∈ (0, 1). + let logit = |x: f64| x.ln() - (-x).ln_1p(); + // 0.5 + 0.5 * tanh(ln(1 / (1 - 0.1) - 1) / (2 * (sqrt(3)/pi))) + let logistic_2_base = 3.0f64.sqrt() * f64::consts::FRAC_2_PI; + // Assumes μ = 0, σ = 1 + let logistic_cdf = |x: f64| (x / logistic_2_base).tanh() * 0.5 + 0.5; + + let map_size_chunks_len_f64 = map_size_lg.chunks().map(f64::from).product(); + let min_epsilon = 1.0 / map_size_chunks_len_f64.max(f64::EPSILON as f64 * 0.5); + let max_epsilon = + (1.0 - 1.0 / map_size_chunks_len_f64).min(1.0 - f64::EPSILON as f64 * 0.5); + + // No NaNs in these uniform vectors, since the original noise value always + // returns Some. + let ((alt_base, _), (chaos, _)) = rayon::join( + || { + uniform_noise(map_size_lg, |_, wposf| { + // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied + // value is from -0.35 * (CONFIG.mountain_scale * 1.05) to + // 0.35 * (CONFIG.mountain_scale * 0.95), but value here is from -0.3675 to + // 0.3325). + Some( + (gen_ctx + .alt_nz + .get((wposf.div(10_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .sub(0.05) + .mul(0.35), + ) + }) + }, + || { + uniform_noise(map_size_lg, |_, wposf| { + // From 0 to 1.6, but the distribution before the max is from -1 and 1.6, so + // there is a 50% chance that hill will end up at 0.3 or + // lower, and probably a very high change it will be exactly + // 0. + let hill = (0.0f64 + + gen_ctx + .hill_nz + .get( + (wposf + .mul(32.0) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(1_500.0)) + .into_array(), + ) + .min(1.0) + .max(-1.0) + .mul(1.0) + + gen_ctx + .hill_nz + .get( + (wposf + .mul(32.0) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(400.0)) + .into_array(), + ) + .min(1.0) + .max(-1.0) + .mul(0.3)) + .add(0.3) + .max(0.0); + + // chaos produces a value in [0.12, 1.32]. It is a meta-level factor intended + // to reflect how "chaotic" the region is--how much weird + // stuff is going on on this terrain. + Some( + ((gen_ctx + .chaos_nz + .get((wposf.div(3_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .add(1.0) + .mul(0.5) + // [0, 1] * [0.4, 1] = [0, 1] (but probably towards the lower end) + .mul( + (gen_ctx + .chaos_nz + .get((wposf.div(6_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .abs() + .max(0.4) + .min(1.0), + ) + // Chaos is always increased by a little when we're on a hill (but remember + // that hill is 0.3 or less about 50% of the time). + // [0, 1] + 0.2 * [0, 1.6] = [0, 1.32] + .add(0.2 * hill) + // We can't have *no* chaos! + .max(0.12)) as f32, + ) + }) + }, + ); + + // We ignore sea level because we actually want to be relative to sea level here + // and want things in CONFIG.mountain_scale units, but otherwise this is + // a correct altitude calculation. Note that this is using the + // "unadjusted" temperature. + // + // No NaNs in these uniform vectors, since the original noise value always + // returns Some. + let (alt_old, _) = uniform_noise(map_size_lg, |posi, wposf| { + // This is the extension upwards from the base added to some extra noise from -1 + // to 1. + // + // The extra noise is multiplied by alt_main (the mountain part of the + // extension) powered to 0.8 and clamped to [0.15, 1], to get a + // value between [-1, 1] again. + // + // The sides then receive the sequence (y * 0.3 + 1.0) * 0.4, so we have + // [-1*1*(1*0.3+1)*0.4, 1*(1*0.3+1)*0.4] = [-0.52, 0.52]. + // + // Adding this to alt_main thus yields a value between -0.4 (if alt_main = 0 and + // gen_ctx = -1, 0+-1*(0*.3+1)*0.4) and 1.52 (if alt_main = 1 and gen_ctx = 1). + // Most of the points are above 0. + // + // Next, we add again by a sin of alt_main (between [-1, 1])^pow, getting + // us (after adjusting for sign) another value between [-1, 1], and then this is + // multiplied by 0.045 to get [-0.045, 0.045], which is added to [-0.4, 0.52] to + // get [-0.445, 0.565]. + let alt_main = { + // Extension upwards from the base. A positive number from 0 to 1 curved to be + // maximal at 0. Also to be multiplied by CONFIG.mountain_scale. + let alt_main = (gen_ctx + .alt_nz + .get((wposf.div(2_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .abs() + .powf(1.35); + + fn spring(x: f64, pow: f64) -> f64 { x.abs().powf(pow) * x.signum() } + + 0.0 + alt_main + + (gen_ctx + .small_nz + .get( + (wposf + .mul(32.0) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(300.0)) + .into_array(), + ) + .min(1.0) + .max(-1.0)) + .mul(alt_main.powf(0.8).max(/* 0.25 */ 0.15)) + .mul(0.3) + .add(1.0) + .mul(0.4) + + spring(alt_main.abs().powf(0.5).min(0.75).mul(60.0).sin(), 4.0).mul(0.045) + }; + + // Now we can compute the final altitude using chaos. + // We multiply by chaos clamped to [0.1, 1.32] to get a value between [0.03, + // 2.232] for alt_pre, then multiply by CONFIG.mountain_scale and + // add to the base and sea level to get an adjusted value, then + // multiply the whole thing by map_edge_factor (TODO: compute final + // bounds). + // + // [-.3675, .3325] + [-0.445, 0.565] * [0.12, 1.32]^1.2 + // ~ [-.3675, .3325] + [-0.445, 0.565] * [0.07, 1.40] + // = [-.3675, .3325] + ([-0.5785, 0.7345]) + // = [-0.946, 1.067] + Some( + ((alt_base[posi].1 + alt_main.mul((chaos[posi].1 as f64).powf(1.2))) + .mul(map_edge_factor(map_size_lg, posi) as f64) + .add( + (CONFIG.sea_level as f64) + .div(CONFIG.mountain_scale as f64) + .mul(map_edge_factor(map_size_lg, posi) as f64), + ) + .sub((CONFIG.sea_level as f64).div(CONFIG.mountain_scale as f64))) + as f32, + ) + }); + + // Calculate oceans. + let is_ocean = get_oceans(map_size_lg, |posi: usize| alt_old[posi].1); + // NOTE: Uncomment if you want oceans to exclusively be on the border of the + // map. + /* let is_ocean = (0..map_size_lg.chunks()) + .into_par_iter() + .map(|i| map_edge_factor(map_size_lg, i) == 0.0) + .collect::>(); */ + let is_ocean_fn = |posi: usize| is_ocean[posi]; + + let turb_wposf_div = 8.0; + let n_func = |posi| { + if is_ocean_fn(posi) { + return 1.0; + } + 1.0 + }; + let old_height = |posi: usize| { + alt_old[posi].1 * CONFIG.mountain_scale * height_scale(n_func(posi)) as f32 + }; + + // NOTE: Needed if you wish to use the distance to the point defining the Worley + // cell, not just the value within that cell. + // let uplift_nz_dist = gen_ctx.uplift_nz.clone().enable_range(true); + + // Recalculate altitudes without oceans. + // NaNs in these uniform vectors wherever is_ocean_fn returns true. + let (alt_old_no_ocean, _) = uniform_noise(map_size_lg, |posi, _| { + if is_ocean_fn(posi) { + None + } else { + Some(old_height(posi)) + } + }); + let (uplift_uniform, _) = uniform_noise(map_size_lg, |posi, _wposf| { + if is_ocean_fn(posi) { + None + } else { + let oheight = alt_old_no_ocean[posi].0 as f64 - 0.5; + let height = (oheight + 0.5).powf(2.0); + Some(height) + } + }); + + let alt_old_min_uniform = 0.0; + let alt_old_max_uniform = 1.0; + + let inv_func = |x: f64| x; + let alt_exp_min_uniform = inv_func(min_epsilon); + let alt_exp_max_uniform = inv_func(max_epsilon); + + let erosion_factor = |x: f64| { + (inv_func(x) - alt_exp_min_uniform) / (alt_exp_max_uniform - alt_exp_min_uniform) + }; + let rock_strength_div_factor = (2.0 * TerrainChunkSize::RECT_SIZE.x as f64) / 8.0; + let theta_func = |_posi| 0.4; + let kf_func = { + |posi| { + let kf_scale_i = k_fs_scale(theta_func(posi), n_func(posi)) as f64; + if is_ocean_fn(posi) { + return 1.0e-4 * kf_scale_i; + } + + let kf_i = // kf = 1.5e-4: high-high (plateau [fan sediment]) + // kf = 1e-4: high (plateau) + // kf = 2e-5: normal (dike [unexposed]) + // kf = 1e-6: normal-low (dike [exposed]) + // kf = 2e-6: low (mountain) + // -- + // kf = 2.5e-7 to 8e-7: very low (Cordonnier papers on plate tectonics) + // ((1.0 - uheight) * (1.5e-4 - 2.0e-6) + 2.0e-6) as f32 + // + // ACTUAL recorded values worldwide: much lower... + 1.0e-6 + ; + kf_i * kf_scale_i + } + }; + let kd_func = { + |posi| { + let n = n_func(posi); + let kd_scale_i = k_d_scale(n); + if is_ocean_fn(posi) { + let kd_i = 1.0e-2 / 4.0; + return kd_i * kd_scale_i; + } + // kd = 1e-1: high (mountain, dike) + // kd = 1.5e-2: normal-high (plateau [fan sediment]) + // kd = 1e-2: normal (plateau) + let kd_i = 1.0e-2 / 4.0; + kd_i * kd_scale_i + } + }; + let g_func = |posi| { + if map_edge_factor(map_size_lg, posi) == 0.0 { + return 0.0; + } + // G = d* v_s / p_0, where + // v_s is the settling velocity of sediment grains + // p_0 is the mean precipitation rate + // d* is the sediment concentration ratio (between concentration near riverbed + // interface, and average concentration over the water column). + // d* varies with Rouse number which defines relative contribution of bed, + // suspended, and washed loads. + // + // G is typically on the order of 1 or greater. However, we are only guaranteed + // to converge for G ≤ 1, so we keep it in the chaos range of [0.12, + // 1.32]. + 1.0 + }; + let epsilon_0_func = |posi| { + // epsilon_0_scale is roughly [using Hack's Law with b = 2 and SPL without + // debris flow or hillslopes] equal to the ratio of the old to new + // area, to the power of -n_i. + let epsilon_0_scale_i = epsilon_0_scale(n_func(posi)); + if is_ocean_fn(posi) { + // marine: ε₀ = 2.078e-3 + let epsilon_0_i = 2.078e-3 / 4.0; + return epsilon_0_i * epsilon_0_scale_i; + } + let wposf = (uniform_idx_as_vec2(map_size_lg, posi) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + .map(|e| e as f64); + let turb_wposf = wposf + .mul(5_000.0 / continent_scale) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(turb_wposf_div); + let turb = Vec2::new( + gen_ctx.turb_x_nz.get(turb_wposf.into_array()), + gen_ctx.turb_y_nz.get(turb_wposf.into_array()), + ) * uplift_turb_scale + * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); + let turb_wposf = wposf + turb; + let uheight = gen_ctx + .uplift_nz + .get(turb_wposf.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let wposf3 = Vec3::new( + wposf.x, + wposf.y, + uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, + ); + let rock_strength = gen_ctx + .rock_strength_nz + .get(wposf3.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let center = 0.4; + let dmin = center - 0.05; + let dmax = center + 0.05; + let log_odds = |x: f64| logit(x) - logit(center); + let ustrength = logistic_cdf( + 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) + + 1.0 * log_odds(uheight.min(dmax).max(dmin)), + ); + // marine: ε₀ = 2.078e-3 + // San Gabriel Mountains: ε₀ = 3.18e-4 + // Oregon Coast Range: ε₀ = 2.68e-4 + // Frogs Hollow (peak production = 0.25): ε₀ = 1.41e-4 + // Point Reyes: ε₀ = 8.1e-5 + // Nunnock River (fractured granite, least weathered?): ε₀ = 5.3e-5 + let epsilon_0_i = ((1.0 - ustrength) * (2.078e-3 - 5.3e-5) + 5.3e-5) as f32 / 4.0; + epsilon_0_i * epsilon_0_scale_i + }; + let alpha_func = |posi| { + let alpha_scale_i = alpha_scale(n_func(posi)); + if is_ocean_fn(posi) { + // marine: α = 3.7e-2 + return 3.7e-2 * alpha_scale_i; + } + let wposf = (uniform_idx_as_vec2(map_size_lg, posi) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + .map(|e| e as f64); + let turb_wposf = wposf + .mul(5_000.0 / continent_scale) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(turb_wposf_div); + let turb = Vec2::new( + gen_ctx.turb_x_nz.get(turb_wposf.into_array()), + gen_ctx.turb_y_nz.get(turb_wposf.into_array()), + ) * uplift_turb_scale + * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); + let turb_wposf = wposf + turb; + let uheight = gen_ctx + .uplift_nz + .get(turb_wposf.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let wposf3 = Vec3::new( + wposf.x, + wposf.y, + uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, + ); + let rock_strength = gen_ctx + .rock_strength_nz + .get(wposf3.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let center = 0.4; + let dmin = center - 0.05; + let dmax = center + 0.05; + let log_odds = |x: f64| logit(x) - logit(center); + let ustrength = logistic_cdf( + 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) + + 1.0 * log_odds(uheight.min(dmax).max(dmin)), + ); + // Frog Hollow (peak production = 0.25): α = 4.2e-2 + // San Gabriel Mountains: α = 3.8e-2 + // marine: α = 3.7e-2 + // Oregon Coast Range: α = 3e-2 + // Nunnock river (fractured granite, least weathered?): α = 2e-3 + // Point Reyes: α = 1.6e-2 + // The stronger the rock, the faster the decline in soil production. + let alpha_i = (ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32; + alpha_i * alpha_scale_i + }; + let uplift_fn = |posi| { + if is_ocean_fn(posi) { + return 0.0; + } + let height = (uplift_uniform[posi].1 - alt_old_min_uniform) as f64 + / (alt_old_max_uniform - alt_old_min_uniform) as f64; + + let height = height.mul(max_epsilon - min_epsilon).add(min_epsilon); + let height = erosion_factor(height); + assert!(height >= 0.0); + assert!(height <= 1.0); + + // u = 1e-3: normal-high (dike, mountain) + // u = 5e-4: normal (mid example in Yuan, average mountain uplift) + // u = 2e-4: low (low example in Yuan; known that lagoons etc. may have u ~ + // 0.05). u = 0: low (plateau [fan, altitude = 0.0]) + let height = height.mul(max_erosion_per_delta_t); + height as f64 + }; + let alt_func = |posi| { + if is_ocean_fn(posi) { + old_height(posi) + } else { + (old_height(posi) as f64 / CONFIG.mountain_scale as f64) as f32 - 0.5 + } + }; + // Perform some erosion. let (alt, basement) = if let Some(map) = parsed_world_file { (map.alt, map.basement) } else { let (alt, basement) = do_erosion( + map_size_lg, max_erosion_per_delta_t as f32, n_steps, &river_seed, @@ -990,6 +1062,7 @@ impl WorldSim { // Quick "small scale" erosion cycle in order to lower extreme angles. do_erosion( + map_size_lg, 1.0f32, n_small_steps, &river_seed, @@ -1013,7 +1086,12 @@ impl WorldSim { // Save map, if necessary. // NOTE: We wll always save a map with latest version. - let map = WorldFile::new(ModernMap { alt, basement }); + let map = WorldFile::new(ModernMap { + continent_scale_hack, + map_size_lg: map_size_lg.vec(), + alt, + basement, + }); (|| { if let FileOpts::Save = opts.world_file { use std::time::SystemTime; @@ -1050,13 +1128,19 @@ impl WorldSim { // Skip validation--we just performed a no-op conversion for this map, so it had // better be valid! - let ModernMap { alt, basement } = map.into_modern().unwrap(); + let ModernMap { + continent_scale_hack: _, + map_size_lg: _, + alt, + basement, + } = map.into_modern().unwrap(); // Additional small-scale eroson after map load, only used during testing. let (alt, basement) = if n_post_load_steps == 0 { (alt, basement) } else { do_erosion( + map_size_lg, 1.0f32, n_post_load_steps, &river_seed, @@ -1078,29 +1162,32 @@ impl WorldSim { ) }; - let is_ocean = get_oceans(|posi| alt[posi]); + let is_ocean = get_oceans(map_size_lg, |posi| alt[posi]); let is_ocean_fn = |posi: usize| is_ocean[posi]; - let mut dh = downhill(|posi| alt[posi], is_ocean_fn); - let (boundary_len, indirection, water_alt_pos, maxh) = get_lakes(|posi| alt[posi], &mut dh); + let mut dh = downhill(map_size_lg, |posi| alt[posi], is_ocean_fn); + let (boundary_len, indirection, water_alt_pos, maxh) = + get_lakes(map_size_lg, |posi| alt[posi], &mut dh); debug!(?maxh, "Max height"); let (mrec, mstack, mwrec) = { - let mut wh = vec![0.0; WORLD_SIZE.x * WORLD_SIZE.y]; + let mut wh = vec![0.0; map_size_lg.chunks_len()]; get_multi_rec( + map_size_lg, |posi| alt[posi], &dh, &water_alt_pos, &mut wh, - WORLD_SIZE.x, - WORLD_SIZE.y, + usize::from(map_size_lg.chunks().x), + usize::from(map_size_lg.chunks().y), TerrainChunkSize::RECT_SIZE.x as Compute, TerrainChunkSize::RECT_SIZE.y as Compute, maxh, ) }; - let flux_old = get_multi_drainage(&mstack, &mrec, &*mwrec, boundary_len); - let flux_rivers = get_drainage(&water_alt_pos, &dh, boundary_len); - // TODO: Make rivers work with multi-direction flux as well. - // let flux_rivers = flux_old.clone(); + let flux_old = get_multi_drainage(map_size_lg, &mstack, &mrec, &*mwrec, boundary_len); + // let flux_rivers = get_drainage(map_size_lg, &water_alt_pos, &dh, + // boundary_len); TODO: Make rivers work with multi-direction flux as + // well. + let flux_rivers = flux_old.clone(); let water_height_initial = |chunk_idx| { let indirection_idx = indirection[chunk_idx]; @@ -1150,13 +1237,21 @@ impl WorldSim { // may comment out this line and replace it with the commented-out code // below; however, there are no guarantees that this // will work correctly. - let water_alt = fill_sinks(water_height_initial, is_ocean_fn); - /* let water_alt = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let water_alt = fill_sinks(map_size_lg, water_height_initial, is_ocean_fn); + /* let water_alt = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| water_height_initial(posi)) .collect::>(); */ - let rivers = get_rivers(&water_alt_pos, &water_alt, &dh, &indirection, &flux_rivers); + let rivers = get_rivers( + map_size_lg, + continent_scale_hack, + &water_alt_pos, + &water_alt, + &dh, + &indirection, + &flux_rivers, + ); let water_alt = indirection .par_iter() @@ -1195,11 +1290,15 @@ impl WorldSim { // Check whether any tiles around this tile are not water (since Lerp will // ensure that they are included). let pure_water = |posi: usize| { - let pos = uniform_idx_as_vec2(posi); + let pos = uniform_idx_as_vec2(map_size_lg, posi); for x in pos.x - 1..(pos.x + 1) + 1 { for y in pos.y - 1..(pos.y + 1) + 1 { - if x >= 0 && y >= 0 && x < WORLD_SIZE.x as i32 && y < WORLD_SIZE.y as i32 { - let posi = vec2_as_uniform_idx(Vec2::new(x, y)); + if x >= 0 + && y >= 0 + && x < map_size_lg.chunks().x as i32 + && y < map_size_lg.chunks().y as i32 + { + let posi = vec2_as_uniform_idx(map_size_lg, Vec2::new(x, y)); if !is_underwater(posi) { return false; } @@ -1214,7 +1313,7 @@ impl WorldSim { || { rayon::join( || { - uniform_noise(|posi, _| { + uniform_noise(map_size_lg, |posi, _| { if pure_water(posi) { None } else { @@ -1225,7 +1324,7 @@ impl WorldSim { }) }, || { - uniform_noise(|posi, _| { + uniform_noise(map_size_lg, |posi, _| { if pure_water(posi) { None } else { @@ -1238,7 +1337,7 @@ impl WorldSim { || { rayon::join( || { - uniform_noise(|posi, wposf| { + uniform_noise(map_size_lg, |posi, wposf| { if pure_water(posi) { None } else { @@ -1248,7 +1347,7 @@ impl WorldSim { }) }, || { - uniform_noise(|posi, wposf| { + uniform_noise(map_size_lg, |posi, wposf| { // Check whether any tiles around this tile are water. if pure_water(posi) { None @@ -1280,13 +1379,14 @@ impl WorldSim { rivers, }; - let chunks = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let chunks = (0..map_size_lg.chunks_len()) .into_par_iter() - .map(|i| SimChunk::generate(i, &gen_ctx, &gen_cdf)) + .map(|i| SimChunk::generate(map_size_lg, i, &gen_ctx, &gen_cdf)) .collect::>(); let mut this = Self { seed, + map_size_lg, max_height: maxh as f32, chunks, locations: Vec::new(), @@ -1301,21 +1401,122 @@ impl WorldSim { this } - pub fn get_size(&self) -> Vec2 { WORLD_SIZE.map(|e| e as u32) } + #[inline(always)] + pub const fn map_size_lg(&self) -> MapSizeLg { self.map_size_lg } + + pub fn get_size(&self) -> Vec2 { self.map_size_lg().chunks().map(u32::from) } /// Draw a map of the world based on chunk information. Returns a buffer of /// u32s. - pub fn get_map(&self, index: &Index) -> Vec { - let mut v = vec![0u32; WORLD_SIZE.x * WORLD_SIZE.y]; + pub fn get_map(&self, index: IndexRef) -> WorldMapMsg { + let mut map_config = MapConfig::orthographic( + self.map_size_lg(), + core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height), + ); + // Build a horizon map. + let scale_angle = |angle: Alt| { + (/* 0.0.max( */angle /* ) */ + .atan() + * ::FRAC_2_PI() + * 255.0) + .floor() as u8 + }; + let scale_height = |height: Alt| { + (/* 0.0.max( */height/*)*/ as Alt * 255.0 / self.max_height as Alt).floor() as u8 + }; + + let samples_data = { + let column_sample = ColumnGen::new(self); + (0..self.map_size_lg().chunks_len()) + .into_par_iter() + .map_init( + || Box::new(BlockGen::new(ColumnGen::new(self))), + |block_gen, posi| { + let wpos = uniform_idx_as_vec2(self.map_size_lg(), posi); + let mut sample = column_sample.get( + (uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + index) + )?; + let alt = sample.alt; + /* let z_cache = block_gen.get_z_cache(wpos); + sample.alt = alt.max(z_cache.get_z_limits(&mut block_gen).2); */ + sample.alt = alt.max(BlockGen::get_cliff_height( + &block_gen.column_gen, + &mut block_gen.column_cache, + wpos.map(|e| e as f32), + &sample.close_cliffs, + sample.cliff_hill, + 32.0, + index, + )); + sample.basement += sample.alt - alt; + // sample.water_level = CONFIG.sea_level.max(sample.water_level); + + Some(sample) + }, + ) + /* .map(|posi| { + let mut sample = column_sample.get( + uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ); + }) */ + .collect::>() + .into_boxed_slice() + }; + + let horizons = get_horizon_map( + self.map_size_lg(), + Aabr { + min: Vec2::zero(), + max: self.map_size_lg().chunks().map(|e| e as i32), + }, + CONFIG.sea_level, + CONFIG.sea_level + self.max_height, + |posi| { + /* let chunk = &self.chunks[posi]; + chunk.alt.max(chunk.water_alt) as Alt */ + let sample = samples_data[posi].as_ref(); + sample + .map(|s| s.alt.max(s.water_level)) + .unwrap_or(CONFIG.sea_level) + }, + |a| scale_angle(a.into()), + |h| scale_height(h.into()), + ) + .unwrap(); + + let mut v = vec![0u32; self.map_size_lg().chunks_len()]; + let mut alts = vec![0u32; self.map_size_lg().chunks_len()]; // TODO: Parallelize again. - MapConfig { - gain: self.max_height, - ..MapConfig::default() + map_config.is_shaded = false; + + map_config.generate( + |pos| sample_pos(&map_config, self, index, Some(&samples_data), pos), + |pos| sample_wpos(&map_config, self, pos), + |pos, (r, g, b, _a)| { + // We currently ignore alpha and replace it with the height at pos, scaled to + // u8. + let alt = sample_wpos( + &map_config, + self, + pos.map(|e| e as i32) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ); + let a = 0; //(alt.min(1.0).max(0.0) * 255.0) as u8; + + // NOTE: Safe by invariants on map_size_lg. + let posi = (pos.y << self.map_size_lg().vec().x) | pos.x; + v[posi] = u32::from_le_bytes([r, g, b, a]); + alts[posi] = (((alt.min(1.0).max(0.0) * 8191.0) as u32) & 0x1FFF) << 3; + }, + ); + WorldMapMsg { + dimensions_lg: self.map_size_lg().vec(), + sea_level: CONFIG.sea_level, + max_height: self.max_height, + rgba: v, + alt: alts, + horizons, } - .generate(&self, index, |pos, (r, g, b, a)| { - v[pos.y * WORLD_SIZE.x + pos.x] = u32::from_le_bytes([r, g, b, a]); - }); - v } /// Prepare the world for simulation @@ -1323,7 +1524,7 @@ impl WorldSim { let mut rng = self.rng.clone(); let cell_size = 16; - let grid_size = WORLD_SIZE / cell_size; + let grid_size = self.map_size_lg().chunks().map(usize::from) / cell_size; let loc_count = 100; let mut loc_grid = vec![None; grid_size.product()]; @@ -1391,7 +1592,7 @@ impl WorldSim { .par_iter_mut() .enumerate() .for_each(|(ij, chunk)| { - let chunk_pos = uniform_idx_as_vec2(ij); + let chunk_pos = uniform_idx_as_vec2(self.map_size_lg(), ij); let i = chunk_pos.x as usize; let j = chunk_pos.y as usize; let block_pos = Vec2::new( @@ -1431,10 +1632,10 @@ impl WorldSim { // Create waypoints const WAYPOINT_EVERY: usize = 16; let this = &self; - let waypoints = (0..WORLD_SIZE.x) + let waypoints = (0..this.map_size_lg().chunks().x) .step_by(WAYPOINT_EVERY) .map(|i| { - (0..WORLD_SIZE.y) + (0..this.map_size_lg().chunks().y) .step_by(WAYPOINT_EVERY) .map(move |j| (i, j)) }) @@ -1477,10 +1678,10 @@ impl WorldSim { pub fn get(&self, chunk_pos: Vec2) -> Option<&SimChunk> { if chunk_pos - .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) + .map2(self.map_size_lg().chunks(), |e, sz| e >= 0 && e < sz as i32) .reduce_and() { - Some(&self.chunks[vec2_as_uniform_idx(chunk_pos)]) + Some(&self.chunks[vec2_as_uniform_idx(self.map_size_lg(), chunk_pos)]) } else { None } @@ -1508,28 +1709,31 @@ impl WorldSim { } pub fn get_mut(&mut self, chunk_pos: Vec2) -> Option<&mut SimChunk> { + let map_size_lg = self.map_size_lg(); if chunk_pos - .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) + .map2(map_size_lg.chunks(), |e, sz| e >= 0 && e < sz as i32) .reduce_and() { - Some(&mut self.chunks[vec2_as_uniform_idx(chunk_pos)]) + Some(&mut self.chunks[vec2_as_uniform_idx(map_size_lg, chunk_pos)]) } else { None } } pub fn get_base_z(&self, chunk_pos: Vec2) -> Option { - if !chunk_pos - .map2(WORLD_SIZE, |e, sz| e > 0 && e < sz as i32 - 2) - .reduce_and() - { + let in_bounds = chunk_pos + .map2(self.map_size_lg().chunks(), |e, sz| { + e > 0 && e < sz as i32 - 2 + }) + .reduce_and(); + if !in_bounds { return None; } - let chunk_idx = vec2_as_uniform_idx(chunk_pos); - local_cells(chunk_idx) + let chunk_idx = vec2_as_uniform_idx(self.map_size_lg(), chunk_pos); + local_cells(self.map_size_lg(), chunk_idx) .flat_map(|neighbor_idx| { - let neighbor_pos = uniform_idx_as_vec2(neighbor_idx); + let neighbor_pos = uniform_idx_as_vec2(self.map_size_lg(), neighbor_idx); let neighbor_chunk = self.get(neighbor_pos); let river_kind = neighbor_chunk.and_then(|c| c.river.river_kind); let has_water = river_kind.is_some() && river_kind != Some(RiverKind::Ocean); @@ -1835,11 +2039,10 @@ pub struct RegionInfo { impl SimChunk { #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 - fn generate(posi: usize, gen_ctx: &GenCtx, gen_cdf: &GenCdf) -> Self { - let pos = uniform_idx_as_vec2(posi); + fn generate(map_size_lg: MapSizeLg, posi: usize, gen_ctx: &GenCtx, gen_cdf: &GenCdf) -> Self { + let pos = uniform_idx_as_vec2(map_size_lg, posi); let wposf = (pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)).map(|e| e as f64); - let _map_edge_factor = map_edge_factor(posi); let (_, chaos) = gen_cdf.chaos[posi]; let alt_pre = gen_cdf.alt[posi] as f32; let basement_pre = gen_cdf.basement[posi] as f32; @@ -1860,7 +2063,7 @@ impl SimChunk { // can always add a small x component). // // Not clear that we want this yet, let's see. - let latitude_uniform = (pos.y as f32 / WORLD_SIZE.y as f32).sub(0.5).mul(2.0); + let latitude_uniform = (pos.y as f32 / f32::from(self.map_size_lg().chunks().y)).sub(0.5).mul(2.0); // Even less granular--if this matters we can make the sign affect the quantiy slightly. let abs_lat_uniform = latitude_uniform.abs(); */ @@ -1893,7 +2096,7 @@ impl SimChunk { panic!("Uh... shouldn't this never, ever happen?"); } else { Some( - uniform_idx_as_vec2(downhill_pre as usize) + uniform_idx_as_vec2(map_size_lg, downhill_pre as usize) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), ) }; diff --git a/world/src/sim/util.rs b/world/src/sim/util.rs index 679bce94b0..3fda529d13 100644 --- a/world/src/sim/util.rs +++ b/world/src/sim/util.rs @@ -1,6 +1,8 @@ -use super::WORLD_SIZE; use bitvec::prelude::{bitbox, BitBox}; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + terrain::{neighbors, uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize}, + vol::RectVolSize, +}; use noise::{MultiFractal, NoiseFn, Perlin, Point2, Point3, Point4, Seedable}; use num::Float; use rayon::prelude::*; @@ -8,14 +10,15 @@ use std::{f32, f64, ops::Mul, u32}; use vek::*; /// Calculates the smallest distance along an axis (x, y) from an edge of -/// the world. This value is maximal at WORLD_SIZE / 2 and minimized at the -/// extremes (0 or WORLD_SIZE on one or more axes). It then divides the -/// quantity by cell_size, so the final result is 1 when we are not in a cell -/// along the edge of the world, and ranges between 0 and 1 otherwise (lower -/// when the chunk is closer to the edge). -pub fn map_edge_factor(posi: usize) -> f32 { - uniform_idx_as_vec2(posi) - .map2(WORLD_SIZE.map(|e| e as i32), |e, sz| { +/// the world. This value is maximal at map_size_lg.chunks() / 2 and +/// minimized at the +/// extremes (0 or map_size_lg.chunks() on one or more axes). It then divides +/// the quantity by cell_size, so the final result is 1 when we are not in a +/// cell along the edge of the world, and ranges between 0 and 1 otherwise +/// (lower when the chunk is closer to the edge). +pub fn map_edge_factor(map_size_lg: MapSizeLg, posi: usize) -> f32 { + uniform_idx_as_vec2(map_size_lg, posi) + .map2(map_size_lg.chunks().map(i32::from), |e, sz| { (sz / 2 - (e - sz / 2).abs()) as f32 / (16.0 / 1024.0 * sz as f32) }) .reduce_partial_min() @@ -118,22 +121,12 @@ pub fn cdf_irwin_hall(weights: &[f32; N], samples: [f32; N]) -> /// returned by the noise function applied to every chunk in the game). Second /// component is the cached value of the noise function that generated the /// index. -/// -/// NOTE: Length should always be WORLD_SIZE.x * WORLD_SIZE.y. pub type InverseCdf = Box<[(f32, F)]>; -/// Computes the position Vec2 of a SimChunk from an index, where the index was -/// generated by uniform_noise. -pub fn uniform_idx_as_vec2(idx: usize) -> Vec2 { - Vec2::new((idx % WORLD_SIZE.x) as i32, (idx / WORLD_SIZE.x) as i32) -} - -/// Computes the index of a Vec2 of a SimChunk from a position, where the index -/// is generated by uniform_noise. NOTE: Both components of idx should be -/// in-bounds! -pub fn vec2_as_uniform_idx(idx: Vec2) -> usize { - (idx.y as usize * WORLD_SIZE.x + idx.x as usize) as usize -} +/// NOTE: First component is estimated horizon angles at each chunk; second +/// component is estimated heights of maximal occluder at each chunk (used +/// for making shadows volumetric). +pub type HorizonMap = (Vec, Vec); /// Compute inverse cumulative distribution function for arbitrary function f, /// the hard way. We pre-generate noise values prior to worldgen, then sort @@ -173,15 +166,17 @@ pub fn vec2_as_uniform_idx(idx: Vec2) -> usize { /// value actually uses the same one we were using here easier). Also returns /// the "inverted index" pointing from a position to a noise. pub fn uniform_noise( + map_size_lg: MapSizeLg, f: impl Fn(usize, Vec2) -> Option + Sync, ) -> (InverseCdf, Box<[(usize, F)]>) { - let mut noise = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let mut noise = (0..map_size_lg.chunks_len()) .into_par_iter() .filter_map(|i| { f( i, - (uniform_idx_as_vec2(i) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - .map(|e| e as f64), + (uniform_idx_as_vec2(map_size_lg, i) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + .map(|e| e as f64), ) .map(|res| (i, res)) }) @@ -197,7 +192,7 @@ pub fn uniform_noise( // position of the noise in the sorted vector (divided by the vector length). // This guarantees a uniform distribution among the samples (excluding those // that returned None, which will remain at zero). - let mut uniform_noise = vec![(0.0, F::nan()); WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut uniform_noise = vec![(0.0, F::nan()); map_size_lg.chunks_len()].into_boxed_slice(); // NOTE: Consider using try_into here and elsewhere in this function, since // i32::MAX technically doesn't fit in an f32 (even if we should never reach // that limit). @@ -218,8 +213,8 @@ pub fn uniform_noise( /// its top-right/down-right/down neighbors, the twelve chunks surrounding this /// box (its "perimeter") are also inspected. #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 -pub fn local_cells(posi: usize) -> impl Clone + Iterator { - let pos = uniform_idx_as_vec2(posi); +pub fn local_cells(map_size_lg: MapSizeLg, posi: usize) -> impl Clone + Iterator { + let pos = uniform_idx_as_vec2(map_size_lg, posi); // NOTE: want to keep this such that the chunk index is in ascending order! let grid_size = 3i32; let grid_bounds = 2 * grid_size + 1; @@ -231,51 +226,35 @@ pub fn local_cells(posi: usize) -> impl Clone + Iterator { pos.y + (index / grid_bounds) - grid_size, ) }) - .filter(|pos| { - pos.x >= 0 && pos.y >= 0 && pos.x < WORLD_SIZE.x as i32 && pos.y < WORLD_SIZE.y as i32 + .filter(move |pos| { + pos.x >= 0 + && pos.y >= 0 + && pos.x < map_size_lg.chunks().x as i32 + && pos.y < map_size_lg.chunks().y as i32 }) - .map(vec2_as_uniform_idx) -} - -// NOTE: want to keep this such that the chunk index is in ascending order! -pub const NEIGHBOR_DELTA: [(i32, i32); 8] = [ - (-1, -1), - (0, -1), - (1, -1), - (-1, 0), - (1, 0), - (-1, 1), - (0, 1), - (1, 1), -]; - -/// Iterate through all cells adjacent to a chunk. -pub fn neighbors(posi: usize) -> impl Clone + Iterator { - let pos = uniform_idx_as_vec2(posi); - NEIGHBOR_DELTA - .iter() - .map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y)) - .filter(|pos| { - pos.x >= 0 && pos.y >= 0 && pos.x < WORLD_SIZE.x as i32 && pos.y < WORLD_SIZE.y as i32 - }) - .map(vec2_as_uniform_idx) + .map(move |e| vec2_as_uniform_idx(map_size_lg, e)) } // Note that we should already have okay cache locality since we have a grid. -pub fn uphill<'a>(dh: &'a [isize], posi: usize) -> impl Clone + Iterator + 'a { - neighbors(posi).filter(move |&posj| dh[posj] == posi as isize) +pub fn uphill<'a>( + map_size_lg: MapSizeLg, + dh: &'a [isize], + posi: usize, +) -> impl Clone + Iterator + 'a { + neighbors(map_size_lg, posi).filter(move |&posj| dh[posj] == posi as isize) } /// Compute the neighbor "most downhill" from all chunks. /// /// TODO: See if allocating in advance is worthwhile. pub fn downhill( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F + Sync, is_ocean: impl Fn(usize) -> bool + Sync, ) -> Box<[isize]> { // Constructs not only the list of downhill nodes, but also computes an ordering // (visiting nodes in order from roots to leaves). - (0..WORLD_SIZE.x * WORLD_SIZE.y) + (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| { let nh = h(posi); @@ -284,7 +263,7 @@ pub fn downhill( } else { let mut best = -1; let mut besth = nh; - for nposi in neighbors(posi) { + for nposi in neighbors(map_size_lg, posi) { let nbh = h(nposi); if nbh < besth { besth = nbh; @@ -298,48 +277,174 @@ pub fn downhill( .into_boxed_slice() } +/* /// Bilinear interpolation. +/// +/// Linear interpolation in both directions (i.e. quadratic interpolation). +fn get_interpolated_bilinear(&self, pos: Vec2, mut f: F) -> Option + where + T: Copy + Default + Signed + Float + Add + Mul, + F: FnMut(Vec2) -> Option, +{ + // (i) Find downhill for all four points. + // (ii) Compute distance from each downhill point and do linear interpolation on + // their heights. (iii) Compute distance between each neighboring point + // and do linear interpolation on their distance-interpolated + // heights. + + // See http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1990A%26A...239..443S&defaultprint=YES&page_ind=0&filetype=.pdf + // + // Note that these are only guaranteed monotone in one dimension; fortunately, + // that is sufficient for our purposes. + let pos = pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { + e as f64 / sz as f64 + }); + + // Orient the chunk in the direction of the most downhill point of the four. If + // there is no "most downhill" point, then we don't care. + let x0 = pos.map2(Vec2::new(0, 0), |e, q| e.max(0.0) as i32 + q); + let y0 = f(x0)?; + + let x1 = pos.map2(Vec2::new(1, 0), |e, q| e.max(0.0) as i32 + q); + let y1 = f(x1)?; + + let x2 = pos.map2(Vec2::new(0, 1), |e, q| e.max(0.0) as i32 + q); + let y2 = f(x2)?; + + let x3 = pos.map2(Vec2::new(1, 1), |e, q| e.max(0.0) as i32 + q); + let y3 = f(x3)?; + + let z0 = y0 + .mul(1.0 - pos.x.fract() as f32) + .mul(1.0 - pos.y.fract() as f32); + let z1 = y1.mul(pos.x.fract() as f32).mul(1.0 - pos.y.fract() as f32); + let z2 = y2.mul(1.0 - pos.x.fract() as f32).mul(pos.y.fract() as f32); + let z3 = y3.mul(pos.x.fract() as f32).mul(pos.y.fract() as f32); + + Some(z0 + z1 + z2 + z3) +} */ + /// Find all ocean tiles from a height map, using an inductive definition of /// ocean as one of: /// - posi is at the side of the world (map_edge_factor(posi) == 0.0) /// - posi has a neighboring ocean tile, and has a height below sea level /// (oldh(posi) <= 0.0). -pub fn get_oceans(oldh: impl Fn(usize) -> F + Sync) -> BitBox { +pub fn get_oceans(map_size_lg: MapSizeLg, oldh: impl Fn(usize) -> F + Sync) -> BitBox { // We can mark tiles as ocean candidates by scanning row by row, since the top // edge is ocean, the sides are connected to it, and any subsequent ocean // tiles must be connected to it. - let mut is_ocean = bitbox![0; WORLD_SIZE.x * WORLD_SIZE.y]; + let mut is_ocean = bitbox![0; map_size_lg.chunks_len()]; let mut stack = Vec::new(); let mut do_push = |pos| { - let posi = vec2_as_uniform_idx(pos); + let posi = vec2_as_uniform_idx(map_size_lg, pos); if oldh(posi) <= F::zero() { stack.push(posi); } }; - for x in 0..WORLD_SIZE.x as i32 { + for x in 0..map_size_lg.chunks().x as i32 { do_push(Vec2::new(x, 0)); - do_push(Vec2::new(x, WORLD_SIZE.y as i32 - 1)); + do_push(Vec2::new(x, map_size_lg.chunks().y as i32 - 1)); } - for y in 1..WORLD_SIZE.y as i32 - 1 { + for y in 1..map_size_lg.chunks().y as i32 - 1 { do_push(Vec2::new(0, y)); - do_push(Vec2::new(WORLD_SIZE.x as i32 - 1, y)); + do_push(Vec2::new(map_size_lg.chunks().x as i32 - 1, y)); } while let Some(chunk_idx) = stack.pop() { - // println!("Ocean chunk {:?}: {:?}", uniform_idx_as_vec2(chunk_idx), - // oldh(chunk_idx)); + // println!("Ocean chunk {:?}: {:?}", uniform_idx_as_vec2(map_size_lg, + // chunk_idx), oldh(chunk_idx)); let mut is_ocean = is_ocean.get_mut(chunk_idx).unwrap(); if *is_ocean { continue; } *is_ocean = true; - stack.extend(neighbors(chunk_idx).filter(|&neighbor_idx| { - // println!("Ocean neighbor: {:?}: {:?}", uniform_idx_as_vec2(neighbor_idx), - // oldh(neighbor_idx)); + stack.extend(neighbors(map_size_lg, chunk_idx).filter(|&neighbor_idx| { + // println!("Ocean neighbor: {:?}: {:?}", uniform_idx_as_vec2(map_size_lg, + // neighbor_idx), oldh(neighbor_idx)); oldh(neighbor_idx) <= F::zero() })); } is_ocean } +/// Finds the horizon map for sunlight for the given chunks. +pub fn get_horizon_map( + map_size_lg: MapSizeLg, + bounds: Aabr, + minh: F, + maxh: F, + h: impl Fn(usize) -> F + Sync, + to_angle: impl Fn(F) -> A + Sync, + to_height: impl Fn(F) -> H + Sync, +) -> Result<[HorizonMap; 2], ()> { + if maxh < minh { + // maxh must be greater than minh + return Err(()); + } + let map_size = Vec2::::from(bounds.size()).map(|e| e as usize); + let map_len = map_size.product(); + + // Now, do the raymarching. + let chunk_x = if let Vec2 { x: Some(x), .. } = TerrainChunkSize::RECT_SIZE.map(F::from) { + x + } else { + return Err(()); + }; + // let epsilon = F::epsilon() * if let x = F::from(map_size.x) { x } else { + // return Err(()) }; + let march = |dx: isize, maxdx: fn(isize, map_size_lg: MapSizeLg) -> isize| { + let mut angles = Vec::with_capacity(map_len); + let mut heights = Vec::with_capacity(map_len); + (0..map_len) + .into_par_iter() + .map(|posi| { + let wposi = + bounds.min + Vec2::new((posi % map_size.x) as i32, (posi / map_size.x) as i32); + if wposi.reduce_partial_min() < 0 + || wposi.x as usize >= usize::from(map_size_lg.chunks().x) + || wposi.y as usize >= usize::from(map_size_lg.chunks().y) + { + return (to_angle(F::zero()), to_height(F::zero())); + } + let posi = vec2_as_uniform_idx(map_size_lg, wposi); + // March in the given direction. + let maxdx = maxdx(wposi.x as isize, map_size_lg); + let mut slope = F::zero(); + let h0 = h(posi); + let h = if h0 < minh { + F::zero() + } else { + let mut max_height = F::zero(); + let maxdz = maxh - h0; + let posi = posi as isize; + for deltax in 1..maxdx { + let posj = (posi + deltax * dx) as usize; + let deltax = chunk_x * F::from(deltax).unwrap(); + let h_j_est = slope * deltax; + if h_j_est > maxdz { + break; + } + let h_j_act = h(posj) - h0; + if + /* h_j_est - h_j_act <= epsilon */ + h_j_est <= h_j_act { + slope = h_j_act / deltax; + max_height = h_j_act; + } + } + h0 - minh + max_height + }; + let a = slope; + (to_angle(a), to_height(h)) + }) + .unzip_into_vecs(&mut angles, &mut heights); + (angles, heights) + }; + let west = march(-1, |x, _| x); + let east = march(1, |x, map_size_lg| { + (usize::from(map_size_lg.chunks().x) - x as usize) as isize + }); + Ok([west, east]) +} + /// A 2-dimensional vector, for internal use. type Vector2 = [T; 2]; /// A 3-dimensional vector, for internal use. diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index cc367448ed..ab5b15e638 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -3,9 +3,10 @@ use crate::{ column::ColumnSample, sim::WorldSim, site::settlement::building::{ - archetype::keep::{Attr, Keep as KeepArchetype}, + archetype::keep::{Attr, FlagColor, Keep as KeepArchetype, StoneColor}, Archetype, Ori, }, + IndexRef, }; use common::{ generation::ChunkSupplement, @@ -14,6 +15,7 @@ use common::{ }; use core::f32; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; struct Keep { @@ -47,6 +49,9 @@ pub struct GenCtx<'a, R: Rng> { rng: &'a mut R, } +#[derive(Deserialize, Serialize)] +pub struct Colors; + impl Castle { #[allow(clippy::let_and_return)] // TODO: Pending review in #587 pub fn generate(wpos: Vec2, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self { @@ -167,6 +172,7 @@ impl Castle { pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), @@ -273,14 +279,14 @@ impl Castle { let keep_archetype = KeepArchetype { flag_color: if self.evil { - Rgb::new(80, 10, 130) + FlagColor::Evil } else { - Rgb::new(200, 80, 40) + FlagColor::Good }, stone_color: if self.evil { - Rgb::new(65, 60, 55) + StoneColor::Evil } else { - Rgb::new(100, 100, 110) + StoneColor::Good }, }; @@ -294,6 +300,7 @@ impl Castle { } let mut mask = keep_archetype.draw( + index, Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt, wall_dist, border_pos, @@ -323,6 +330,7 @@ impl Castle { let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs()); mask = mask.resolve_with(keep_archetype.draw( + index, if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() { wpos - tower_wpos } else { @@ -364,6 +372,7 @@ impl Castle { let border_pos = (keep_wpos - wpos).xy().map(|e| e.abs()); mask = mask.resolve_with(keep_archetype.draw( + index, if (keep_wpos.x - wpos.x).abs() < (keep_wpos.y - wpos.y).abs() { wpos - keep_wpos } else { diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index b34619c487..4b3fe2f8a7 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -5,6 +5,7 @@ use crate::{ sim::WorldSim, site::BlockMask, util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, + IndexRef, }; use common::{ assets, @@ -20,6 +21,7 @@ use core::{f32, hash::BuildHasherDefault}; use fxhash::FxHasher64; use lazy_static::lazy_static; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use std::sync::Arc; use vek::*; @@ -37,6 +39,11 @@ pub struct GenCtx<'a, R: Rng> { rng: &'a mut R, } +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub stone: (u8, u8, u8), +} + const ALT_OFFSET: i32 = -2; const LEVELS: usize = 5; @@ -80,6 +87,7 @@ impl Dungeon { pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), @@ -112,7 +120,14 @@ impl Dungeon { .ok() .copied() .map(|sb| { - block_from_structure(sb, spos, self.origin, self.seed, col_sample) + block_from_structure( + index, + sb, + spos, + self.origin, + self.seed, + col_sample, + ) }) .unwrap_or(None) { @@ -125,7 +140,7 @@ impl Dungeon { for floor in &self.floors { z -= floor.total_depth(); - let mut sampler = floor.col_sampler(rpos, z); + let mut sampler = floor.col_sampler(index, rpos, z); for rz in 0..floor.total_depth() { if let Some(block) = sampler(rz).finish() { @@ -574,16 +589,29 @@ impl Floor { } #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 - pub fn col_sampler(&self, pos: Vec2, floor_z: i32) -> impl FnMut(i32) -> BlockMask + '_ { + pub fn col_sampler<'a>( + &'a self, + index: IndexRef<'a>, + pos: Vec2, + floor_z: i32, + ) -> impl FnMut(i32) -> BlockMask + 'a { let rpos = pos - self.tile_offset * TILE_SIZE; let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; let rtile_pos = rpos - tile_center; + let colors = &index.colors.site.dungeon; + let empty = BlockMask::new(Block::empty(), 1); let make_staircase = move |pos: Vec3, radius: f32, inner_radius: f32, stretch: f32| { - let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5); + let stone = BlockMask::new( + Block::new( + BlockKind::Normal, + /* Rgb::new(150, 150, 175) */ colors.stone.into(), + ), + 5, + ); if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) { stone diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 82c83b2183..01f9985f4d 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -10,15 +10,23 @@ pub use self::{ settlement::Settlement, }; -use crate::column::ColumnSample; +use crate::{column::ColumnSample, IndexRef}; use common::{ generation::ChunkSupplement, terrain::Block, vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, }; use rand::Rng; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub castle: castle::Colors, + pub dungeon: dungeon::Colors, + pub settlement: settlement::Colors, +} + pub struct SpawnRules { pub trees: bool, } @@ -86,14 +94,15 @@ impl Site { pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { match &self.kind { - SiteKind::Settlement(s) => s.apply_to(wpos2d, get_column, vol), - SiteKind::Dungeon(d) => d.apply_to(wpos2d, get_column, vol), - SiteKind::Castle(c) => c.apply_to(wpos2d, get_column, vol), + SiteKind::Settlement(s) => s.apply_to(index, wpos2d, get_column, vol), + SiteKind::Dungeon(d) => d.apply_to(index, wpos2d, get_column, vol), + SiteKind::Castle(c) => c.apply_to(index, wpos2d, get_column, vol), } } diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 970e3a4698..aa15f844e8 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -4,64 +4,103 @@ use super::{super::skeleton::*, Archetype}; use crate::{ site::BlockMask, util::{RandomField, Sampler}, + IndexRef, }; use common::{ + make_case_elim, terrain::{Block, BlockKind}, vol::Vox, }; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; -pub struct ColorTheme { - roof: Rgb, - wall: Rgb, - support: Rgb, +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub foundation: (u8, u8, u8), + pub floor: (u8, u8, u8), + pub roof: roof_color::PureCases<(u8, u8, u8)>, + pub wall: wall_color::PureCases<(u8, u8, u8)>, + pub support: support_color::PureCases<(u8, u8, u8)>, } -const ROOF_COLORS: &[Rgb] = &[ - // Rgb::new(0x1D, 0x4D, 0x45), - // Rgb::new(0xB3, 0x7D, 0x60), - // Rgb::new(0xAC, 0x5D, 0x26), - // Rgb::new(0x32, 0x46, 0x6B), - // Rgb::new(0x2B, 0x19, 0x0F), - // Rgb::new(0x93, 0x78, 0x51), - // Rgb::new(0x92, 0x57, 0x24), - // Rgb::new(0x4A, 0x4E, 0x4E), - // Rgb::new(0x2F, 0x32, 0x47), - // Rgb::new(0x8F, 0x35, 0x43), - // Rgb::new(0x6D, 0x1E, 0x3A), - // Rgb::new(0x6D, 0xA7, 0x80), - // Rgb::new(0x4F, 0xA0, 0x95), - // Rgb::new(0xE2, 0xB9, 0x99), - // Rgb::new(0x7A, 0x30, 0x22), - // Rgb::new(0x4A, 0x06, 0x08), - // Rgb::new(0x8E, 0xB4, 0x57), - Rgb::new(0x99, 0x5E, 0x54), - Rgb::new(0x43, 0x63, 0x64), - Rgb::new(0x76, 0x6D, 0x68), - Rgb::new(0x7B, 0x41, 0x61), - Rgb::new(0x52, 0x20, 0x20), - Rgb::new(0x1A, 0x4A, 0x59), - Rgb::new(0xCC, 0x76, 0x4E), +pub struct ColorTheme { + roof: RoofColor, + wall: WallColor, + support: SupportColor, +} + +make_case_elim!( + roof_color, + #[repr(u32)] + #[derive(Clone, Copy)] + pub enum RoofColor { + Roof1 = 0, + Roof2 = 1, + Roof3 = 2, + Roof4 = 3, + Roof5 = 4, + Roof6 = 5, + Roof7 = 6, + } +); + +make_case_elim!( + wall_color, + #[repr(u32)] + #[derive(Clone, Copy)] + pub enum WallColor { + Wall1 = 0, + Wall2 = 1, + Wall3 = 2, + Wall4 = 3, + Wall5 = 4, + Wall6 = 5, + Wall7 = 6, + Wall8 = 7, + Wall9 = 8, + } +); + +make_case_elim!( + support_color, + #[repr(u32)] + #[derive(Clone, Copy)] + pub enum SupportColor { + Support1 = 0, + Support2 = 1, + Support3 = 2, + Support4 = 3, + } +); + +const ROOF_COLORS: [RoofColor; roof_color::NUM_VARIANTS] = [ + RoofColor::Roof1, + RoofColor::Roof2, + RoofColor::Roof3, + RoofColor::Roof4, + RoofColor::Roof4, + RoofColor::Roof6, + RoofColor::Roof7, ]; -const WALL_COLORS: &[Rgb] = &[ - Rgb::new(200, 180, 150), - Rgb::new(0xB8, 0xB4, 0xA4), - Rgb::new(0x76, 0x6D, 0x68), - Rgb::new(0xF3, 0xC9, 0x8F), - Rgb::new(0xD3, 0xB7, 0x99), - Rgb::new(0xE1, 0xAB, 0x91), - Rgb::new(0x82, 0x57, 0x4C), - Rgb::new(0xB9, 0x96, 0x77), - Rgb::new(0xAE, 0x8D, 0x9C), +const WALL_COLORS: [WallColor; wall_color::NUM_VARIANTS] = [ + WallColor::Wall1, + WallColor::Wall2, + WallColor::Wall3, + WallColor::Wall4, + WallColor::Wall5, + WallColor::Wall6, + WallColor::Wall7, + WallColor::Wall8, + WallColor::Wall9, ]; -const SUPPORT_COLORS: &[Rgb] = &[ - Rgb::new(60, 45, 30), - Rgb::new(0x65, 0x55, 0x56), - Rgb::new(0x53, 0x33, 0x13), - Rgb::new(0x58, 0x42, 0x33), +const SUPPORT_COLORS: [SupportColor; support_color::NUM_VARIANTS] = [ + SupportColor::Support1, + SupportColor::Support2, + SupportColor::Support3, + SupportColor::Support4, ]; pub struct House { @@ -210,6 +249,7 @@ impl Archetype for House { #[allow(clippy::int_plus_one)] // TODO: Pending review in #587 fn draw( &self, + index: IndexRef, _pos: Vec3, dist: i32, bound_offset: Vec2, @@ -220,6 +260,11 @@ impl Archetype for House { _len: i32, attr: &Self::Attr, ) -> BlockMask { + let colors = &index.colors.site.settlement.building.archetype.house; + let roof_color = *self.colors.roof.elim_case_pure(&colors.roof); + let wall_color = *self.colors.wall.elim_case_pure(&colors.wall); + let support_color = *self.colors.support.elim_case_pure(&colors.support); + let profile = Vec2::new(bound_offset.x, z); let make_meta = |ori| { @@ -240,6 +285,7 @@ impl Archetype for House { BlockMask::new( Block::new( BlockKind::Normal, + // TODO: Clarify exactly how this affects the color. Rgb::new(r, g, b) .map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)), ), @@ -253,11 +299,11 @@ impl Archetype for House { let foundation_layer = internal_layer + 1; let floor_layer = foundation_layer + 1; - let foundation = make_block((100, 100, 100)).with_priority(foundation_layer); - let log = make_block(self.colors.support.into_tuple()); - let floor = make_block((100, 75, 50)); - let wall = make_block(self.colors.wall.into_tuple()).with_priority(facade_layer); - let roof = make_block(self.colors.roof.into_tuple()).with_priority(facade_layer - 1); + let foundation = make_block(colors.foundation).with_priority(foundation_layer); + let log = make_block(support_color); + let floor = make_block(colors.floor); + let wall = make_block(wall_color).with_priority(facade_layer); + let roof = make_block(roof_color).with_priority(facade_layer - 1); let empty = BlockMask::nothing(); let internal = BlockMask::new(Block::empty(), internal_layer); let end_window = BlockMask::new( diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 2028fb75d0..160772f6e4 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -2,17 +2,29 @@ use super::{super::skeleton::*, Archetype}; use crate::{ site::BlockMask, util::{RandomField, Sampler}, + IndexRef, }; use common::{ + make_case_elim, terrain::{Block, BlockKind}, vol::Vox, }; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub brick_base: (u8, u8, u8), + pub floor_base: (u8, u8, u8), + pub pole: (u8, u8, u8), + pub flag: flag_color::PureCases<(u8, u8, u8)>, + pub stone: stone_color::PureCases<(u8, u8, u8)>, +} + pub struct Keep { - pub flag_color: Rgb, - pub stone_color: Rgb, + pub flag_color: FlagColor, + pub stone_color: StoneColor, } pub struct Attr { @@ -24,6 +36,24 @@ pub struct Attr { pub has_doors: bool, } +make_case_elim!( + flag_color, + #[repr(u32)] + pub enum FlagColor { + Good = 0, + Evil = 1, + } +); + +make_case_elim!( + stone_color, + #[repr(u32)] + pub enum StoneColor { + Good = 0, + Evil = 1, + } +); + impl Archetype for Keep { type Attr = Attr; @@ -71,8 +101,8 @@ impl Archetype for Keep { ( Self { - flag_color: Rgb::new(200, 80, 40), - stone_color: Rgb::new(100, 100, 110), + flag_color: FlagColor::Good, + stone_color: StoneColor::Good, }, skel, ) @@ -81,6 +111,7 @@ impl Archetype for Keep { #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 fn draw( &self, + index: IndexRef, pos: Vec3, _dist: i32, bound_offset: Vec2, @@ -91,6 +122,11 @@ impl Archetype for Keep { _len: i32, attr: &Self::Attr, ) -> BlockMask { + let dungeon_stone = index.colors.site.dungeon.stone; + let colors = &index.colors.site.settlement.building.archetype.keep; + let flag_color = self.flag_color.elim_case_pure(&colors.flag); + let stone_color = self.stone_color.elim_case_pure(&colors.stone); + let profile = Vec2::new(bound_offset.x, z); let weak_layer = 1; @@ -118,30 +154,35 @@ impl Archetype for Keep { let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1); let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24; - let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex); + let foundation = make_block( + colors.brick_base.0 + brick_tex, + colors.brick_base.1 + brick_tex, + colors.brick_base.2 + brick_tex, + ); let wall = make_block( - self.stone_color.r + brick_tex, - self.stone_color.g + brick_tex, - self.stone_color.b + brick_tex, + stone_color.0 + brick_tex, + stone_color.1 + brick_tex, + stone_color.2 + brick_tex, ); let window = BlockMask::new( Block::new(BlockKind::Window1, make_meta(ori.flip())), normal_layer, ); let floor = make_block( - 80 + (pos.y.abs() % 2) as u8 * 15, - 60 + (pos.y.abs() % 2) as u8 * 15, - 10 + (pos.y.abs() % 2) as u8 * 15, + colors.floor_base.0 + (pos.y.abs() % 2) as u8 * 15, + colors.floor_base.1 + (pos.y.abs() % 2) as u8 * 15, + colors.floor_base.2 + (pos.y.abs() % 2) as u8 * 15, ) .with_priority(important_layer); - let pole = make_block(90, 70, 50).with_priority(important_layer); - let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b) - .with_priority(important_layer); + let pole = + make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer); + let flag = + make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer); let internal = BlockMask::new(Block::empty(), internal_layer); let empty = BlockMask::nothing(); let make_staircase = move |pos: Vec3, radius: f32, inner_radius: f32, stretch: f32| { - let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5); + let stone = BlockMask::new(Block::new(BlockKind::Normal, dungeon_stone.into()), 5); if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) { stone diff --git a/world/src/site/settlement/building/archetype/mod.rs b/world/src/site/settlement/building/archetype/mod.rs index d20e943ef2..a8e67d5aff 100644 --- a/world/src/site/settlement/building/archetype/mod.rs +++ b/world/src/site/settlement/building/archetype/mod.rs @@ -2,10 +2,17 @@ pub mod house; pub mod keep; use super::skeleton::*; -use crate::site::BlockMask; +use crate::{site::BlockMask, IndexRef}; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub house: house::Colors, + pub keep: keep::Colors, +} + pub trait Archetype { type Attr; @@ -16,6 +23,7 @@ pub trait Archetype { #[allow(clippy::too_many_arguments)] fn draw( &self, + index: IndexRef, pos: Vec3, dist: i32, bound_offset: Vec2, diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 0f2e6096cd..d58ee8ad5c 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -7,10 +7,17 @@ pub use self::{ skeleton::*, }; +use crate::IndexRef; use common::terrain::Block; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub archetype: archetype::Colors, +} + pub struct Building { skel: Skeleton, archetype: A, @@ -46,13 +53,14 @@ impl Building { } } - pub fn sample(&self, pos: Vec3) -> Option { + pub fn sample(&self, index: IndexRef, pos: Vec3) -> Option { let rpos = pos - self.origin; self.skel .sample_closest( rpos, |pos, dist, bound_offset, center_offset, ori, branch| { self.archetype.draw( + index, pos, dist, bound_offset, diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 4ead8da0bc..50d22b3c52 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -10,6 +10,7 @@ use crate::{ column::ColumnSample, sim::WorldSim, util::{RandomField, Sampler, StructureGen2d}, + IndexRef, }; use common::{ assets, @@ -25,9 +26,30 @@ use common::{ use fxhash::FxHasher64; use hashbrown::{HashMap, HashSet}; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use std::{collections::VecDeque, f32, hash::BuildHasherDefault}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub building: building::Colors, + + pub plot_town_path: (u8, u8, u8), + + pub plot_field_dirt: (u8, u8, u8), + pub plot_field_mound: (u8, u8, u8), + + pub wall_low: (u8, u8, u8), + pub wall_high: (u8, u8, u8), + + pub tower_color: (u8, u8, u8), + + pub plot_dirt: (u8, u8, u8), + pub plot_grass: (u8, u8, u8), + pub plot_water: (u8, u8, u8), + pub plot_town: (u8, u8, u8), +} + #[allow(dead_code)] pub fn gradient(line: [Vec2; 2]) -> f32 { let r = (line[0].y - line[1].y) / (line[0].x - line[1].x); @@ -109,10 +131,10 @@ impl Structure { } } - pub fn sample(&self, rpos: Vec3) -> Option { + pub fn sample(&self, index: IndexRef, rpos: Vec3) -> Option { match &self.kind { - StructureKind::House(house) => house.sample(rpos), - StructureKind::Keep(keep) => keep.sample(rpos), + StructureKind::House(house) => house.sample(index, rpos), + StructureKind::Keep(keep) => keep.sample(index, rpos), } } } @@ -527,10 +549,13 @@ impl Settlement { #[allow(clippy::modulo_one)] // TODO: Pending review in #587 pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { + let colors = &index.colors.site.settlement; + for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { let offs = Vec2::new(x, y); @@ -586,47 +611,6 @@ impl Settlement { } } - // Paths - // if let Some((WayKind::Path, dist, nearest)) = sample.way { - // let inset = -1; - - // // Try to use the column at the centre of the path for sampling to make - // them // flatter - // let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) - // .unwrap_or(col_sample); - // let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist { - // ( - // ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * - // 5.0, ((1.0 - ((water_dist + 2.0) * - // 0.3).min(0.0).cos().abs()) - // * (col.riverless_alt + 5.0 - col.alt).max(0.0) - // * 1.75 - // + 3.0) as i32, - // ) - // } else { - // (0.0, 3) - // }; - // let surface_z = (col.riverless_alt + bridge_offset).floor() as i32; - - // for z in inset - depth..inset { - // let _ = vol.set( - // Vec3::new(offs.x, offs.y, surface_z + z), - // if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { - // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, - // 100), 8)) } else { - // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50, - // 30), 8)) }, - // ); - // } - // let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1); - // for z in inset..inset + head_space { - // let pos = Vec3::new(offs.x, offs.y, surface_z + z); - // if vol.get(pos).unwrap().kind() != BlockKind::Water { - // let _ = vol.set(pos, Block::empty()); - // } - // } - // // Ground colour - // } else { let mut surface_block = None; @@ -634,9 +618,9 @@ impl Settlement { |seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n; let color = match sample.plot { - Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)), - Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), - Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), + Some(Plot::Dirt) => Some(colors.plot_dirt.into()), + Some(Plot::Grass) => Some(colors.plot_grass.into()), + Some(Plot::Water) => Some(colors.plot_water.into()), //Some(Plot::Town { district }) => None, Some(Plot::Town { .. }) => { if let Some((_, path_nearest, _, _)) = col_sample.path { @@ -659,13 +643,16 @@ impl Settlement { } } - Some(Rgb::new(100, 95, 65).map2(Rgb::iota(), |e: u8, i: i32| { - e.saturating_add( - (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1) - as u8, - ) - .saturating_sub(8) - })) + Some(Rgb::from(colors.plot_town_path).map2( + Rgb::iota(), + |e: u8, i: i32| { + e.saturating_add( + (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1) + as u8, + ) + .saturating_sub(8) + }, + )) }, Some(Plot::Field { seed, crop, .. }) => { let furrow_dirs = [ @@ -677,12 +664,13 @@ impl Settlement { let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()]; let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2; - let dirt = Rgb::new(80, 55, 35).map(|e| { + let dirt = Rgb::::from(colors.plot_field_dirt).map(|e| { e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32) as u8 }); - let mound = - Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| { + let mound = Rgb::::from(colors.plot_field_mound) + .map(|e| e + roll(0, 8) as u8) + .map(|e| { e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32)) % 32) as u8 }); @@ -769,8 +757,8 @@ impl Settlement { // Walls if let Some((WayKind::Wall, dist, _)) = sample.way { let color = Lerp::lerp( - Rgb::new(130i32, 100, 0), - Rgb::new(90, 70, 50), + Rgb::::from(colors.wall_low).map(i32::from), + Rgb::::from(colors.wall_high).map(i32::from), (RandomField::new(0).get(wpos2d.into()) % 256) as f32 / 256.0, ) .map(|e| (e % 256) as u8); @@ -797,7 +785,7 @@ impl Settlement { for z in -2..16 { let _ = vol.set( Vec3::new(offs.x, offs.y, surface_z + z), - Block::new(BlockKind::Normal, Rgb::new(50, 50, 50)), + Block::new(BlockKind::Normal, colors.tower_color.into()), ); } } @@ -832,7 +820,7 @@ impl Settlement { let wpos = Vec3::from(self.origin) + rpos; let coffs = wpos - Vec3::from(wpos2d); - if let Some(block) = structure.sample(rpos) { + if let Some(block) = structure.sample(index, rpos) { let _ = vol.set(coffs, block); } } @@ -938,30 +926,24 @@ impl Settlement { } } - pub fn get_color(&self, pos: Vec2) -> Option> { + pub fn get_color(&self, index: IndexRef, pos: Vec2) -> Option> { + let colors = &index.colors.site.settlement; + let sample = self.land.get_at_block(pos); - // match sample.tower { - // Some((Tower::Wall, _)) => return Some(Rgb::new(50, 50, 50)), - // _ => {}, - // } - - // match sample.way { - // Some((WayKind::Path, _, _)) => return Some(Rgb::new(90, 70, 50)), - // Some((WayKind::Hedge, _, _)) => return Some(Rgb::new(0, 150, 0)), - // Some((WayKind::Wall, _, _)) => return Some(Rgb::new(60, 60, 60)), - // _ => {}, - // } - match sample.plot { - Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)), - Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)), - Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)), + Some(Plot::Dirt) => return Some(colors.plot_dirt.into()), + Some(Plot::Grass) => return Some(colors.plot_grass.into()), + Some(Plot::Water) => return Some(colors.plot_water.into()), Some(Plot::Town { .. }) => { - return Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| { - e.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8) + return Some( + Rgb::from(colors.plot_town).map2(Rgb::iota(), |e: u8, i: i32| { + e.saturating_add( + (self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8, + ) .saturating_sub(8) - })); + }), + ); }, Some(Plot::Field { seed, .. }) => { let furrow_dirs = [ @@ -972,6 +954,12 @@ impl Settlement { ]; let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()]; let furrow = (pos * furrow_dir).sum().rem_euclid(6) < 3; + // NOTE: Very hard to understand how to make this dynamically configurable. The + // base values can easily cause the others to go out of range, and there's some + // weird scaling going on. For now, we just let these remain hardcoded. + // + // FIXME: Rewrite this so that validity is not so heavily dependent on the exact + // color values. return Some(Rgb::new( if furrow { 100 @@ -1003,6 +991,8 @@ pub enum Crop { Sunflower, } +// NOTE: No support for struct variants in make_case_elim yet, unfortunately, so +// we can't use it. #[derive(Copy, Clone, PartialEq)] pub enum Plot { Hazard,