mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-09 00:09:55 +00:00
Upgrade to Bevy v0.12 (#12)
* Migrate `System::pipe(...)` calls to `System::map(...)` * Migrate `EventReader::iter()` and `EventReader::into_iter()` calls to `EventReader::read()` * Migrate asset handling * Generate `*.meta` files for assets * Migrate a shader import * Fix emissive materials double-applying their emissive strength * Fix non-normalized bone weights resulting in incorrect vertex positions * Set shadow filtering to `Hardware2x2` to mitigate increase in shader compilation times
This commit is contained in:
Vendored
+3
@@ -6,4 +6,7 @@
|
||||
"rust-analyzer.cargo.features": [
|
||||
"wasm"
|
||||
],
|
||||
"files.associations": {
|
||||
"*.meta": "ron"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+329
-266
File diff suppressed because it is too large
Load Diff
+2
-5
@@ -1,12 +1,9 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"./bevy_mod_outline",
|
||||
"./studio"
|
||||
]
|
||||
members = ["./studio"]
|
||||
|
||||
[workspace.dependencies.bevy]
|
||||
version = "0.11.3"
|
||||
version = "0.12.0"
|
||||
features = ["tonemapping_luts"]
|
||||
|
||||
[profile.dev-custom]
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## bevy_mod_outline 0.5.0 (2023-08-14)
|
||||
|
||||
### Changed
|
||||
- Updated Bevy dependency to 0.11. (@ramirezmike and @zainthemaynnn)
|
||||
- Removed panic if specialising mesh pipeline fails. (@arjo129)
|
||||
|
||||
## bevy_mod_outline 0.4.2 (2023-05-30)
|
||||
|
||||
### Fixed
|
||||
- Fixed failures to propagate ComputedOutlineDepth when needed.
|
||||
- Fixed Z-fighting between overlay and stencil with OpenGL wgpu back-end.
|
||||
|
||||
## bevy_mod_outline 0.4.1 (2023-04-11)
|
||||
|
||||
### Fixed
|
||||
- Fixed panic if mesh has unused vertex indices.
|
||||
- Fixed panic if the DepthMode hasn't propagated before rendering.
|
||||
|
||||
### Changed
|
||||
- Changed normal weighting to use vertex rather than face normals if available.
|
||||
|
||||
## bevy_mod_outline 0.4.0 (2023-03-22)
|
||||
|
||||
### Added
|
||||
- Added enabled flag to OutlineStencil.
|
||||
- Added hollow example.
|
||||
|
||||
### Fixed
|
||||
- Fixed outline depth propagating when inheritance not enabled.
|
||||
|
||||
### Changed
|
||||
- Updated Bevy dependency to 0.10.
|
||||
- Changed outline normal generator to use face normals.
|
||||
|
||||
## bevy_mod_outline 0.3.5 (2023-03-08)
|
||||
|
||||
### Fixed
|
||||
- Fixed regression breaking SetOutlineDepth::Real.
|
||||
|
||||
## bevy_mod_outline 0.3.4 (2023-03-08)
|
||||
|
||||
### Fixed
|
||||
- Fixed texture format error when HDR is enabled.
|
||||
- Fixed bad clipping of outlines behind the camera.
|
||||
|
||||
## bevy_mod_outline 0.3.3 (2023-02-21)
|
||||
|
||||
### Fixed
|
||||
- Fixed SetOutlineDepth being ignored in some cases (@mxgrey).
|
||||
- Fixed defaulting to SetOutlineDepth::Real in some cases.
|
||||
- Fixed missing examples from README.
|
||||
|
||||
## bevy_mod_outline 0.3.2 (2023-01-15)
|
||||
|
||||
### Added
|
||||
- Added support for (Outline)RenderLayers components (@mxgrey).
|
||||
- Added render_layers example.
|
||||
|
||||
## bevy_mod_outline 0.3.1 (2023-01-05)
|
||||
|
||||
### Added
|
||||
- Added Lerp impls for OutlineVolume and OutlineStencil.
|
||||
- Added Real mode to SetOutlineDepth enum.
|
||||
|
||||
## bevy_mod_outline 0.3.0 (2022-11-22)
|
||||
|
||||
### Added
|
||||
- Added ComputedOutlineDepth, SetOutlineDepth, and InheritOutlineDepth.
|
||||
- Added offset field to OutlineStencil.
|
||||
- Added pieces example.
|
||||
|
||||
### Removed
|
||||
- Removed align16 feature.
|
||||
|
||||
### Fixed
|
||||
- Fixed errant debug println in AutoGenerateOutlineNormalsPlugin.
|
||||
|
||||
### Changed
|
||||
- Updated Bevy dependency to 0.9.
|
||||
- Renamed Outline component to OutlineVolume.
|
||||
|
||||
## bevy_mod_outline 0.2.5 (2023-01-14)
|
||||
|
||||
### Added
|
||||
- Added support for (Outline)RenderLayers components (@mxgrey) (back-port from 0.3.2).
|
||||
- Added render_layers example (back-port from 0.3.2).
|
||||
|
||||
### Fixed
|
||||
- Fixed errant debug println in AutoGenerateOutlineNormalsPlugin (back-port from 0.3.0).
|
||||
|
||||
## bevy_mod_outline 0.2.4 (2022-10-12)
|
||||
|
||||
### Fixed
|
||||
- Fixed adding outlines to jointed (skinned) meshes.
|
||||
|
||||
## bevy_mod_outline 0.2.3 (2022-08-28)
|
||||
|
||||
### Added
|
||||
- Added AutoGenerateOutlineNormalsPlugin.
|
||||
|
||||
## bevy_mod_outline 0.2.2 (2022-08-23)
|
||||
|
||||
### Added
|
||||
- Added a feature flag to control the dependency on bevy_ui (@Shatur).
|
||||
- Added a feature flag to control uniform struct alignment.
|
||||
|
||||
### Fixed
|
||||
- Fixed compilation on 32-bit platforms.
|
||||
- Fixed insufficient alignment causing errors with WebGL.
|
||||
|
||||
### Changed
|
||||
- Removed dependency on bevy's monolithic render feature flag (@Shatur).
|
||||
|
||||
## bevy_mod_outline 0.2.1 (2022-08-10)
|
||||
|
||||
### Added
|
||||
- Added a constant to expose the outline pass node name.
|
||||
|
||||
### Fixed
|
||||
- Fixed outlines causing UI to disappear when MSAA is enabled.
|
||||
- Fixed bad derive allowing OutlineBundle to be inserted as a component.
|
||||
|
||||
## bevy_mod_outline 0.2.0 (2022-08-09)
|
||||
|
||||
### Added
|
||||
- Added support for outline normals.
|
||||
|
||||
### Changed
|
||||
- Updated Bevy dependency to 0.8.
|
||||
- Changed from rendering in main pass to separate pass.
|
||||
- Changed from using asset handles to plain components.
|
||||
|
||||
## bevy_mod_outline 0.1.0 (2022-06-14)
|
||||
|
||||
- Initial release
|
||||
@@ -1,37 +0,0 @@
|
||||
[package]
|
||||
name = "bevy_mod_outline"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A mesh outlining plugin for Bevy."
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/komadori/bevy_mod_outline/"
|
||||
repository = "https://github.com/komadori/bevy_mod_outline/"
|
||||
keywords = ["gamedev", "bevy", "outline"]
|
||||
categories = ["game-engines", "rendering"]
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.11", default-features = false, features = [
|
||||
"bevy_asset",
|
||||
"bevy_render",
|
||||
"bevy_pbr",
|
||||
"ktx2",
|
||||
"tonemapping_luts",
|
||||
"zstd",
|
||||
"bevy_core_pipeline",
|
||||
] }
|
||||
bitfield = "0.14"
|
||||
interpolation = "0.2"
|
||||
thiserror = "1.0"
|
||||
wgpu-types = "0.16.1"
|
||||
|
||||
[dev-dependencies]
|
||||
bevy = { version = "0.11", default-features = false, features = [
|
||||
"animation",
|
||||
"bevy_gltf",
|
||||
"bevy_pbr",
|
||||
"bevy_scene",
|
||||
"bevy_winit",
|
||||
"png",
|
||||
"x11",
|
||||
] }
|
||||
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,23 +0,0 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,96 +0,0 @@
|
||||
> **NOTE**: This is a temporary fork of [bevy_mod_outline](https://github.com/komandori/bevy_mod_outline)
|
||||
> to fix [this issue](https://github.com/komadori/bevy_mod_outline/issues/19).
|
||||
>
|
||||
> This can be removed and replaced with the crates.io import once the issue is
|
||||
> fixed upstream.
|
||||
|
||||
# bevy_mod_outline
|
||||
|
||||
[](https://crates.io/crates/bevy_mod_outline)
|
||||
[](https://docs.rs/bevy_mod_outline)
|
||||
|
||||

|
||||
|
||||
This crate provides a Bevy plugin for drawing outlines around meshes using the
|
||||
vertex extrusion method.
|
||||
|
||||
## Dependency
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
bevy_mod_outline = "0.5"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
A rotating cube and torus with opaque and transparent outlines.
|
||||
|
||||
```shell
|
||||
cargo run --example shapes
|
||||
```
|
||||
|
||||
Multiple intersecting meshes sharing an outline plane. The outline stencil is offset to create
|
||||
a gap between the object and its outline.
|
||||
|
||||
```shell
|
||||
cargo run --example pieces
|
||||
```
|
||||
|
||||
Many instances of the same mesh, with two different outline configurations, flying towards the
|
||||
camera.
|
||||
|
||||
```shell
|
||||
cargo run --example flying_objects
|
||||
```
|
||||
|
||||
An outlined torus viewed through four cameras with different combinations of render layers
|
||||
enabled.
|
||||
|
||||
```shell
|
||||
cargo run --example render_layers
|
||||
```
|
||||
|
||||
An animated jointed glTF model with an outline.
|
||||
|
||||
```shell
|
||||
cargo run --example animated_fox
|
||||
```
|
||||
|
||||
A glTF model with pre-baked outline normals.
|
||||
|
||||
```shell
|
||||
cargo run --example hollow
|
||||
```
|
||||
|
||||
An animated morphing glTF model with an outline.
|
||||
|
||||
```shell
|
||||
cargo run --example morph_targets
|
||||
```
|
||||
|
||||
## Versions
|
||||
|
||||
| This Version | Bevy version |
|
||||
|--------------|--------------|
|
||||
| 0.5.x | 0.11.x |
|
||||
| 0.4.x | 0.10.x |
|
||||
| 0.3.x | 0.9.x |
|
||||
| 0.2.x | 0.8.x |
|
||||
| 0.1.x | 0.7.x |
|
||||
|
||||
## Features
|
||||
|
||||
- `bevy_ui` _(default)_ - Adds a render graph edge to prevent clashing with the
|
||||
UI. This adds a dependency on the `bevy_ui` crate and can be disabled if it is
|
||||
not used.
|
||||
|
||||
## Licence
|
||||
|
||||
This crate is licensed under the Apache License, Version 2.0 (see
|
||||
LICENCE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>) or the MIT
|
||||
licence (see LICENCE-MIT or <http://opensource.org/licenses/MIT>), at your
|
||||
option.
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "bevy_mod_outline",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
"sourceRoot": "./bevy_mod_outline/src",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nxrs/cargo:build",
|
||||
"options": {},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"release": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nxrs/cargo:test",
|
||||
"options": {}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nxrs/cargo:clippy",
|
||||
"options": {
|
||||
"fix": false,
|
||||
"failOnWarnings": true,
|
||||
"noDeps": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::uniforms::DepthMode;
|
||||
|
||||
/// A component for storing the computed depth at which the outline lies.
|
||||
#[derive(Clone, Component, Default)]
|
||||
pub struct ComputedOutlineDepth {
|
||||
pub(crate) world_origin: Vec3,
|
||||
pub(crate) depth_mode: DepthMode,
|
||||
pub(crate) inherited: Option<Entity>,
|
||||
}
|
||||
|
||||
/// A component which specifies how the outline depth should be computed.
|
||||
#[derive(Clone, Component)]
|
||||
#[non_exhaustive]
|
||||
pub enum SetOutlineDepth {
|
||||
/// A flat plane facing the camera and intersecting the specified point in model-space.
|
||||
Flat { model_origin: Vec3 },
|
||||
/// Real model-space.
|
||||
Real,
|
||||
}
|
||||
|
||||
/// A component which specifies that this outline lies at the same depth as its parent.
|
||||
#[derive(Clone, Component, Default)]
|
||||
pub struct InheritOutlineDepth;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn compute_outline_depth(
|
||||
mut root_query: Query<
|
||||
(
|
||||
Entity,
|
||||
&mut ComputedOutlineDepth,
|
||||
&GlobalTransform,
|
||||
Changed<GlobalTransform>,
|
||||
Option<(&SetOutlineDepth, Changed<SetOutlineDepth>)>,
|
||||
Option<&Children>,
|
||||
),
|
||||
Without<InheritOutlineDepth>,
|
||||
>,
|
||||
mut computed_query: Query<&mut ComputedOutlineDepth, With<InheritOutlineDepth>>,
|
||||
child_query: Query<&Children>,
|
||||
) {
|
||||
for (entity, mut computed, transform, changed_transform, set_depth, children) in
|
||||
root_query.iter_mut()
|
||||
{
|
||||
let changed = !computed.depth_mode.is_valid()
|
||||
|| computed.inherited.is_some()
|
||||
|| changed_transform
|
||||
|| set_depth.filter(|(_, c)| *c).is_some();
|
||||
if changed {
|
||||
let (origin, depth_mode) = if let Some((sd, _)) = set_depth {
|
||||
match sd {
|
||||
SetOutlineDepth::Flat {
|
||||
model_origin: origin,
|
||||
} => (*origin, DepthMode::Flat),
|
||||
SetOutlineDepth::Real => (Vec3::NAN, DepthMode::Real),
|
||||
}
|
||||
} else {
|
||||
(Vec3::ZERO, DepthMode::Flat)
|
||||
};
|
||||
let matrix = transform.compute_matrix();
|
||||
computed.world_origin = matrix.project_point3(origin);
|
||||
computed.depth_mode = depth_mode;
|
||||
computed.inherited = None;
|
||||
}
|
||||
if let Some(cs) = children {
|
||||
for child in cs.iter() {
|
||||
propagate_outline_depth(
|
||||
&computed,
|
||||
changed,
|
||||
entity,
|
||||
*child,
|
||||
&mut computed_query,
|
||||
&child_query,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn propagate_outline_depth(
|
||||
root_computed: &ComputedOutlineDepth,
|
||||
mut changed: bool,
|
||||
parent: Entity,
|
||||
entity: Entity,
|
||||
computed_query: &mut Query<&mut ComputedOutlineDepth, With<InheritOutlineDepth>>,
|
||||
child_query: &Query<&Children>,
|
||||
) {
|
||||
if let Ok(mut computed) = computed_query.get_mut(entity) {
|
||||
changed |= !computed.depth_mode.is_valid() | (computed.inherited != Some(parent));
|
||||
if changed {
|
||||
*computed = root_computed.clone();
|
||||
computed.inherited = Some(parent);
|
||||
}
|
||||
if let Ok(cs) = child_query.get(entity) {
|
||||
for child in cs.iter() {
|
||||
propagate_outline_depth(
|
||||
root_computed,
|
||||
changed,
|
||||
entity,
|
||||
*child,
|
||||
computed_query,
|
||||
child_query,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
use bevy::pbr::{DrawMesh, SetMeshBindGroup, SetMeshViewBindGroup};
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::render_asset::RenderAssets;
|
||||
use bevy::render::render_phase::{DrawFunctions, RenderPhase, SetItemPipeline};
|
||||
use bevy::render::render_resource::{PipelineCache, SpecializedMeshPipelines};
|
||||
use bevy::render::renderer::RenderAdapterInfo;
|
||||
use bevy::render::view::{ExtractedView, RenderLayers};
|
||||
use wgpu_types::Backend;
|
||||
|
||||
use crate::node::{OpaqueOutline, StencilOutline, TransparentOutline};
|
||||
use crate::pipeline::{OutlinePipeline, PassType, PipelineKey};
|
||||
use crate::uniforms::{
|
||||
DepthMode, OutlineFragmentUniform, OutlineStencilFlags, OutlineStencilUniform,
|
||||
OutlineVolumeFlags, OutlineVolumeUniform, SetOutlineStencilBindGroup,
|
||||
SetOutlineVolumeBindGroup,
|
||||
};
|
||||
use crate::view_uniforms::SetOutlineViewBindGroup;
|
||||
use crate::OutlineRenderLayers;
|
||||
|
||||
pub(crate) type DrawStencil = (
|
||||
SetItemPipeline,
|
||||
SetMeshViewBindGroup<0>,
|
||||
SetMeshBindGroup<1>,
|
||||
SetOutlineViewBindGroup<2>,
|
||||
SetOutlineStencilBindGroup<3>,
|
||||
DrawMesh,
|
||||
);
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
pub(crate) fn queue_outline_stencil_mesh(
|
||||
stencil_draw_functions: Res<DrawFunctions<StencilOutline>>,
|
||||
stencil_pipeline: Res<OutlinePipeline>,
|
||||
msaa: Res<Msaa>,
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
render_meshes: Res<RenderAssets<Mesh>>,
|
||||
adapter_info: Res<RenderAdapterInfo>,
|
||||
material_meshes: Query<(
|
||||
Entity,
|
||||
&Handle<Mesh>,
|
||||
&OutlineStencilUniform,
|
||||
&OutlineStencilFlags,
|
||||
&OutlineRenderLayers,
|
||||
)>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&mut RenderPhase<StencilOutline>,
|
||||
Option<&RenderLayers>,
|
||||
)>,
|
||||
) {
|
||||
let draw_stencil = stencil_draw_functions
|
||||
.read()
|
||||
.get_id::<DrawStencil>()
|
||||
.unwrap();
|
||||
|
||||
let base_key = PipelineKey::new()
|
||||
.with_msaa(*msaa)
|
||||
.with_pass_type(PassType::Stencil)
|
||||
.with_opengl_workaround(adapter_info.0.backend == Backend::Gl);
|
||||
|
||||
for (view, mut stencil_phase, view_mask) in views.iter_mut() {
|
||||
let rangefinder = view.rangefinder3d();
|
||||
let view_mask = view_mask.copied().unwrap_or_default();
|
||||
for (entity, mesh_handle, stencil_uniform, stencil_flags, outline_mask) in
|
||||
material_meshes.iter()
|
||||
{
|
||||
if !view_mask.intersects(outline_mask) {
|
||||
continue; // Layer not enabled
|
||||
}
|
||||
if stencil_flags.depth_mode == DepthMode::Invalid {
|
||||
continue; // DepthMode not propagated
|
||||
}
|
||||
let Some(mesh) = render_meshes.get(mesh_handle) else {
|
||||
continue; // No mesh
|
||||
};
|
||||
let key = base_key
|
||||
.with_primitive_topology(mesh.primitive_topology)
|
||||
.with_depth_mode(stencil_flags.depth_mode)
|
||||
.with_offset_zero(stencil_uniform.offset == 0.0)
|
||||
.with_morph_targets(mesh.morph_targets.is_some());
|
||||
let Ok(pipeline) =
|
||||
pipelines.specialize(&pipeline_cache, &stencil_pipeline, key, &mesh.layout)
|
||||
else {
|
||||
continue; // No pipeline
|
||||
};
|
||||
let distance = rangefinder.distance(&Mat4::from_translation(stencil_uniform.origin));
|
||||
stencil_phase.add(StencilOutline {
|
||||
entity,
|
||||
pipeline,
|
||||
draw_function: draw_stencil,
|
||||
distance,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type DrawOutline = (
|
||||
SetItemPipeline,
|
||||
SetMeshViewBindGroup<0>,
|
||||
SetMeshBindGroup<1>,
|
||||
SetOutlineViewBindGroup<2>,
|
||||
SetOutlineVolumeBindGroup<3>,
|
||||
DrawMesh,
|
||||
);
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
pub(crate) fn queue_outline_volume_mesh(
|
||||
opaque_draw_functions: Res<DrawFunctions<OpaqueOutline>>,
|
||||
transparent_draw_functions: Res<DrawFunctions<TransparentOutline>>,
|
||||
outline_pipeline: Res<OutlinePipeline>,
|
||||
msaa: Res<Msaa>,
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
render_meshes: Res<RenderAssets<Mesh>>,
|
||||
adapter_info: Res<RenderAdapterInfo>,
|
||||
material_meshes: Query<(
|
||||
Entity,
|
||||
&Handle<Mesh>,
|
||||
&OutlineVolumeUniform,
|
||||
&OutlineVolumeFlags,
|
||||
&OutlineFragmentUniform,
|
||||
&OutlineRenderLayers,
|
||||
)>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&mut RenderPhase<OpaqueOutline>,
|
||||
&mut RenderPhase<TransparentOutline>,
|
||||
Option<&RenderLayers>,
|
||||
)>,
|
||||
) {
|
||||
let draw_opaque_outline = opaque_draw_functions
|
||||
.read()
|
||||
.get_id::<DrawOutline>()
|
||||
.unwrap();
|
||||
let draw_transparent_outline = transparent_draw_functions
|
||||
.read()
|
||||
.get_id::<DrawOutline>()
|
||||
.unwrap();
|
||||
|
||||
let base_key = PipelineKey::new()
|
||||
.with_msaa(*msaa)
|
||||
.with_opengl_workaround(adapter_info.0.backend == Backend::Gl);
|
||||
|
||||
for (view, mut opaque_phase, mut transparent_phase, view_mask) in views.iter_mut() {
|
||||
let view_mask = view_mask.copied().unwrap_or_default();
|
||||
let rangefinder = view.rangefinder3d();
|
||||
for (entity, mesh_handle, volume_uniform, volume_flags, fragment_uniform, outline_mask) in
|
||||
material_meshes.iter()
|
||||
{
|
||||
if !view_mask.intersects(outline_mask) {
|
||||
continue; // Layer not enabled
|
||||
}
|
||||
if volume_flags.depth_mode == DepthMode::Invalid {
|
||||
continue; // DepthMode not propagated
|
||||
}
|
||||
let Some(mesh) = render_meshes.get(mesh_handle) else {
|
||||
continue; // No mesh
|
||||
};
|
||||
let transparent = fragment_uniform.colour[3] < 1.0;
|
||||
let key = base_key
|
||||
.with_primitive_topology(mesh.primitive_topology)
|
||||
.with_pass_type(if transparent {
|
||||
PassType::Transparent
|
||||
} else {
|
||||
PassType::Opaque
|
||||
})
|
||||
.with_depth_mode(volume_flags.depth_mode)
|
||||
.with_offset_zero(volume_uniform.offset == 0.0)
|
||||
.with_hdr_format(view.hdr)
|
||||
.with_morph_targets(mesh.morph_targets.is_some());
|
||||
let Ok(pipeline) =
|
||||
pipelines.specialize(&pipeline_cache, &outline_pipeline, key, &mesh.layout)
|
||||
else {
|
||||
continue; // No pipeline
|
||||
};
|
||||
let distance = rangefinder.distance(&Mat4::from_translation(volume_uniform.origin));
|
||||
if transparent {
|
||||
transparent_phase.add(TransparentOutline {
|
||||
entity,
|
||||
pipeline,
|
||||
draw_function: draw_transparent_outline,
|
||||
distance,
|
||||
});
|
||||
} else {
|
||||
opaque_phase.add(OpaqueOutline {
|
||||
entity,
|
||||
pipeline,
|
||||
draw_function: draw_opaque_outline,
|
||||
distance,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
struct FragmentOutput {
|
||||
@location(0) colour: vec4<f32>,
|
||||
#ifdef OPENGL_WORKAROUND
|
||||
@builtin(frag_depth) frag_depth: f32,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct OutlineFragmentUniform {
|
||||
@align(16)
|
||||
colour: vec4<f32>,
|
||||
};
|
||||
|
||||
#ifdef VOLUME
|
||||
@group(3) @binding(1)
|
||||
var<uniform> fstage: OutlineFragmentUniform;
|
||||
#endif
|
||||
|
||||
@fragment
|
||||
#ifdef OPENGL_WORKAROUND
|
||||
fn fragment(@location(0) normalised_depth: f32) -> FragmentOutput {
|
||||
#else
|
||||
fn fragment() -> FragmentOutput {
|
||||
#endif
|
||||
var out: FragmentOutput;
|
||||
#ifdef VOLUME
|
||||
out.colour = fstage.colour;
|
||||
#endif
|
||||
#ifdef OPENGL_WORKAROUND
|
||||
out.frag_depth = normalised_depth;
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
render::{
|
||||
mesh::{Indices, VertexAttributeValues},
|
||||
render_resource::{PrimitiveTopology, VertexFormat},
|
||||
},
|
||||
utils::{FloatOrd, HashMap, HashSet},
|
||||
};
|
||||
|
||||
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||
|
||||
enum IndexIterator<'a> {
|
||||
ExplicitU16(std::slice::Iter<'a, u16>),
|
||||
ExplicitU32(std::slice::Iter<'a, u32>),
|
||||
Implicit(std::ops::Range<usize>),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Mesh> for IndexIterator<'a> {
|
||||
fn from(value: &'a Mesh) -> Self {
|
||||
match value.indices() {
|
||||
Some(Indices::U16(vec)) => IndexIterator::ExplicitU16(vec.iter()),
|
||||
Some(Indices::U32(vec)) => IndexIterator::ExplicitU32(vec.iter()),
|
||||
None => IndexIterator::Implicit(0..value.count_vertices()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for IndexIterator<'_> {
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
IndexIterator::ExplicitU16(iter) => iter.next().map(|val| *val as usize),
|
||||
IndexIterator::ExplicitU32(iter) => iter.next().map(|val| *val as usize),
|
||||
IndexIterator::Implicit(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
IndexIterator::ExplicitU16(iter) => iter.size_hint(),
|
||||
IndexIterator::ExplicitU32(iter) => iter.size_hint(),
|
||||
IndexIterator::Implicit(iter) => iter.size_hint(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for IndexIterator<'_> {}
|
||||
|
||||
/// Failed to generate outline normals for the mesh.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum GenerateOutlineNormalsError {
|
||||
#[error("unsupported primitive topology '{0:?}'")]
|
||||
UnsupportedPrimitiveTopology(PrimitiveTopology),
|
||||
#[error("missing vertex attributes '{0}'")]
|
||||
MissingVertexAttribute(&'static str),
|
||||
#[error("the '{0}' vertex attribute should have {1:?} format, but had {2:?} format")]
|
||||
InvalidVertexAttributeFormat(&'static str, VertexFormat, VertexFormat),
|
||||
}
|
||||
|
||||
/// Extension methods for [`Mesh`].
|
||||
pub trait OutlineMeshExt {
|
||||
/// Generates outline normals for the mesh.
|
||||
///
|
||||
/// Vertex extrusion only works for meshes with smooth surface normals. Hard edges cause
|
||||
/// visual artefacts. This function generates faux-smooth normals for outlining purposes
|
||||
/// by grouping vertices by their position and averaging the normals at each point. These
|
||||
/// outline normals are then inserted as a separate vertex attribute so that the regular
|
||||
/// normals remain untouched. However, insofar as the outline normals are not
|
||||
/// perpendicular to the surface of the mesh, this technique may result in non-uniform
|
||||
/// outline thickness.
|
||||
///
|
||||
/// This function only supports meshes with TriangleList topology.
|
||||
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError>;
|
||||
}
|
||||
|
||||
impl OutlineMeshExt for Mesh {
|
||||
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError> {
|
||||
if self.primitive_topology() != PrimitiveTopology::TriangleList {
|
||||
return Err(GenerateOutlineNormalsError::UnsupportedPrimitiveTopology(
|
||||
self.primitive_topology(),
|
||||
));
|
||||
}
|
||||
let positions = match self.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
|
||||
GenerateOutlineNormalsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
||||
)? {
|
||||
VertexAttributeValues::Float32x3(p) => Ok(p),
|
||||
v => Err(GenerateOutlineNormalsError::InvalidVertexAttributeFormat(
|
||||
Mesh::ATTRIBUTE_POSITION.name,
|
||||
VertexFormat::Float32x3,
|
||||
v.into(),
|
||||
)),
|
||||
}?;
|
||||
let normals = match self.attribute(Mesh::ATTRIBUTE_NORMAL) {
|
||||
Some(VertexAttributeValues::Float32x3(p)) => Some(p),
|
||||
_ => None,
|
||||
};
|
||||
let mut map = HashMap::<[FloatOrd; 3], Vec3>::with_capacity(positions.len());
|
||||
let mut it = IndexIterator::from(&*self);
|
||||
while let (Some(i0), Some(i1), Some(i2)) = (it.next(), it.next(), it.next()) {
|
||||
for (j0, j1, j2) in [(i0, i1, i2), (i1, i2, i0), (i2, i0, i1)] {
|
||||
let p0 = Vec3::from(positions[j0]);
|
||||
let p1 = Vec3::from(positions[j1]);
|
||||
let p2 = Vec3::from(positions[j2]);
|
||||
let angle = (p1 - p0).angle_between(p2 - p0);
|
||||
let n = map
|
||||
.entry([FloatOrd(p0.x), FloatOrd(p0.y), FloatOrd(p0.z)])
|
||||
.or_default();
|
||||
*n += angle
|
||||
* if let Some(ns) = normals {
|
||||
// Use vertex normal
|
||||
Vec3::from(ns[j0])
|
||||
} else {
|
||||
// Calculate face normal
|
||||
(p1 - p0).cross(p2 - p0).normalize_or_zero()
|
||||
};
|
||||
}
|
||||
}
|
||||
let mut outlines = Vec::with_capacity(positions.len());
|
||||
for p in positions.iter() {
|
||||
let key = [FloatOrd(p[0]), FloatOrd(p[1]), FloatOrd(p[2])];
|
||||
outlines.push(
|
||||
map.get(&key)
|
||||
.copied()
|
||||
.unwrap_or(Vec3::ZERO)
|
||||
.normalize_or_zero()
|
||||
.to_array(),
|
||||
);
|
||||
}
|
||||
self.insert_attribute(
|
||||
ATTRIBUTE_OUTLINE_NORMAL,
|
||||
VertexAttributeValues::Float32x3(outlines),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_generate_outline_normals(
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut events: EventReader<'_, '_, AssetEvent<Mesh>>,
|
||||
mut squelch: Local<HashSet<Handle<Mesh>>>,
|
||||
) {
|
||||
for event in events.iter() {
|
||||
match event {
|
||||
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
|
||||
if squelch.contains(handle) {
|
||||
// Suppress modification events created by this system
|
||||
squelch.remove(handle);
|
||||
} else if let Some(mesh) = meshes.get_mut(handle) {
|
||||
let _ = mesh.generate_outline_normals();
|
||||
squelch.insert(handle.clone_weak());
|
||||
}
|
||||
}
|
||||
AssetEvent::Removed { handle } => {
|
||||
squelch.remove(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically runs [`generate_outline_normals`](OutlineMeshExt::generate_outline_normals)
|
||||
/// on every mesh.
|
||||
///
|
||||
/// This is provided as a convenience for simple projects. It runs the outline normal
|
||||
/// generator every time a mesh asset is created or modified without consideration for
|
||||
/// whether this is necessary or appropriate.
|
||||
pub struct AutoGenerateOutlineNormalsPlugin;
|
||||
|
||||
impl Plugin for AutoGenerateOutlineNormalsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, auto_generate_outline_normals);
|
||||
}
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
//! This crate provides a Bevy plugin, [`OutlinePlugin`], and associated components for
|
||||
//! rendering outlines around meshes using the vertex extrusion method.
|
||||
//!
|
||||
//! Outlines are rendered in a seperate pass following the main 3D pass. The effect of this
|
||||
//! pass is to present the outlines in depth sorted order according to the model translation
|
||||
//! of each mesh. This ensures that outlines are not clipped by other geometry.
|
||||
//!
|
||||
//! The [`OutlineVolume`] component will, by itself, cover the original object entirely with
|
||||
//! the outline colour. The [`OutlineStencil`] component must also be added to prevent the body
|
||||
//! of an object from being filled it. This must be added to any entity which needs to appear on
|
||||
//! top of an outline.
|
||||
//!
|
||||
//! The [`OutlineBundle`] and [`OutlineStencilBundle`] bundles can be used to add the right
|
||||
//! components, including the required [`ComputedOutlineDepth`] component. Optionally, the
|
||||
//! [`SetOutlineDepth`] and [`InheritOutlineDepth`] components may also be added to control the
|
||||
//! depth ordering of outlines.
|
||||
//!
|
||||
//! Vertex extrusion works best with meshes that have smooth surfaces. To avoid visual
|
||||
//! artefacts when outlining meshes with hard edges, see the
|
||||
//! [`OutlineMeshExt::generate_outline_normals`] function and the
|
||||
//! [`AutoGenerateOutlineNormalsPlugin`].
|
||||
|
||||
use bevy::asset::load_internal_asset;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::extract_component::{
|
||||
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
||||
};
|
||||
use bevy::render::mesh::MeshVertexAttribute;
|
||||
use bevy::render::render_graph::RenderGraph;
|
||||
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
|
||||
use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
|
||||
use bevy::render::view::RenderLayers;
|
||||
use bevy::render::{Render, RenderApp, RenderSet};
|
||||
use bevy::transform::TransformSystem;
|
||||
use interpolation::Lerp;
|
||||
|
||||
use crate::draw::{
|
||||
queue_outline_stencil_mesh, queue_outline_volume_mesh, DrawOutline, DrawStencil,
|
||||
};
|
||||
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
|
||||
use crate::pipeline::{OutlinePipeline, FRAGMENT_SHADER_HANDLE, OUTLINE_SHADER_HANDLE};
|
||||
use crate::uniforms::{
|
||||
extract_outline_stencil_uniforms, extract_outline_volume_uniforms,
|
||||
queue_outline_stencil_bind_group, queue_outline_volume_bind_group, OutlineFragmentUniform,
|
||||
OutlineStencilUniform, OutlineVolumeUniform,
|
||||
};
|
||||
use crate::view_uniforms::{
|
||||
extract_outline_view_uniforms, queue_outline_view_bind_group, OutlineViewUniform,
|
||||
};
|
||||
|
||||
mod computed;
|
||||
mod draw;
|
||||
mod generate;
|
||||
mod node;
|
||||
mod pipeline;
|
||||
mod uniforms;
|
||||
mod view_uniforms;
|
||||
|
||||
pub use computed::*;
|
||||
pub use generate::*;
|
||||
|
||||
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
||||
|
||||
/// The direction to extrude the vertex when rendering the outline.
|
||||
pub const ATTRIBUTE_OUTLINE_NORMAL: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("Outline_Normal", 1585570526, VertexFormat::Float32x3);
|
||||
|
||||
/// Name of the render graph node which draws the outlines.
|
||||
///
|
||||
/// This node runs after the main 3D passes and before the UI pass. The name can be used to
|
||||
/// add additional constraints on node execution order with respect to other passes.
|
||||
pub const OUTLINE_PASS_NODE_NAME: &str = "bevy_mod_outline_node";
|
||||
|
||||
/// A component for stenciling meshes during outline rendering.
|
||||
#[derive(Clone, Component)]
|
||||
pub struct OutlineStencil {
|
||||
/// Enable rendering of the stencil
|
||||
pub enabled: bool,
|
||||
/// Offset of the stencil in logical pixels
|
||||
pub offset: f32,
|
||||
}
|
||||
|
||||
impl Default for OutlineStencil {
|
||||
fn default() -> Self {
|
||||
OutlineStencil {
|
||||
enabled: true,
|
||||
offset: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lerp_bool(this: bool, other: bool, scalar: f32) -> bool {
|
||||
if scalar <= 0.0 {
|
||||
this
|
||||
} else if scalar >= 1.0 {
|
||||
other
|
||||
} else {
|
||||
this | other
|
||||
}
|
||||
}
|
||||
|
||||
impl Lerp for OutlineStencil {
|
||||
type Scalar = f32;
|
||||
|
||||
fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self {
|
||||
OutlineStencil {
|
||||
enabled: lerp_bool(self.enabled, other.enabled, *scalar),
|
||||
offset: self.offset.lerp(&other.offset, scalar),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A component for rendering outlines around meshes.
|
||||
#[derive(Clone, Component, Default)]
|
||||
pub struct OutlineVolume {
|
||||
/// Enable rendering of the outline
|
||||
pub visible: bool,
|
||||
/// Width of the outline in logical pixels
|
||||
pub width: f32,
|
||||
/// Colour of the outline
|
||||
pub colour: Color,
|
||||
}
|
||||
|
||||
impl Lerp for OutlineVolume {
|
||||
type Scalar = f32;
|
||||
|
||||
fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self {
|
||||
OutlineVolume {
|
||||
visible: lerp_bool(self.visible, other.visible, *scalar),
|
||||
width: self.width.lerp(&other.width, scalar),
|
||||
colour: {
|
||||
let [r, g, b, a] = self
|
||||
.colour
|
||||
.as_linear_rgba_f32()
|
||||
.lerp(&other.colour.as_linear_rgba_f32(), scalar);
|
||||
Color::rgba_linear(r, g, b, a)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A component for specifying what layer(s) the outline should be rendered for.
|
||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut, Default)]
|
||||
pub struct OutlineRenderLayers(pub RenderLayers);
|
||||
|
||||
impl ExtractComponent for OutlineRenderLayers {
|
||||
type Query = (
|
||||
Option<&'static OutlineRenderLayers>,
|
||||
Option<&'static RenderLayers>,
|
||||
);
|
||||
type Filter = Or<(With<OutlineVolume>, With<OutlineStencil>)>;
|
||||
type Out = Self;
|
||||
|
||||
fn extract_component(
|
||||
(outline_mask, object_mask): (Option<&OutlineRenderLayers>, Option<&RenderLayers>),
|
||||
) -> Option<Self> {
|
||||
Some(
|
||||
outline_mask
|
||||
.copied()
|
||||
.or_else(|| object_mask.copied().map(OutlineRenderLayers))
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A bundle for rendering stenciled outlines around meshes.
|
||||
#[derive(Bundle, Clone, Default)]
|
||||
pub struct OutlineBundle {
|
||||
pub outline: OutlineVolume,
|
||||
pub stencil: OutlineStencil,
|
||||
pub plane: ComputedOutlineDepth,
|
||||
}
|
||||
|
||||
/// A bundle for stenciling meshes in the outlining pass.
|
||||
#[derive(Bundle, Clone, Default)]
|
||||
pub struct OutlineStencilBundle {
|
||||
pub stencil: OutlineStencil,
|
||||
pub plane: ComputedOutlineDepth,
|
||||
}
|
||||
|
||||
/// Adds support for rendering outlines.
|
||||
pub struct OutlinePlugin;
|
||||
|
||||
impl Plugin for OutlinePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
load_internal_asset!(
|
||||
app,
|
||||
OUTLINE_SHADER_HANDLE,
|
||||
"outline.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
load_internal_asset!(
|
||||
app,
|
||||
FRAGMENT_SHADER_HANDLE,
|
||||
"fragment.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
|
||||
app.add_plugins((
|
||||
ExtractComponentPlugin::<OutlineRenderLayers>::default(),
|
||||
UniformComponentPlugin::<OutlineStencilUniform>::default(),
|
||||
UniformComponentPlugin::<OutlineVolumeUniform>::default(),
|
||||
UniformComponentPlugin::<OutlineFragmentUniform>::default(),
|
||||
UniformComponentPlugin::<OutlineViewUniform>::default(),
|
||||
))
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
compute_outline_depth.after(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.sub_app_mut(RenderApp)
|
||||
.init_resource::<DrawFunctions<StencilOutline>>()
|
||||
.init_resource::<DrawFunctions<OpaqueOutline>>()
|
||||
.init_resource::<DrawFunctions<TransparentOutline>>()
|
||||
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
|
||||
.add_render_command::<StencilOutline, DrawStencil>()
|
||||
.add_render_command::<OpaqueOutline, DrawOutline>()
|
||||
.add_render_command::<TransparentOutline, DrawOutline>()
|
||||
.add_systems(ExtractSchedule, extract_outline_view_uniforms)
|
||||
.add_systems(ExtractSchedule, extract_outline_stencil_uniforms)
|
||||
.add_systems(ExtractSchedule, extract_outline_volume_uniforms)
|
||||
.add_systems(
|
||||
Render,
|
||||
sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_outline_view_bind_group.in_set(RenderSet::Queue),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_outline_stencil_bind_group.in_set(RenderSet::Queue),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_outline_volume_bind_group.in_set(RenderSet::Queue),
|
||||
)
|
||||
.add_systems(Render, queue_outline_stencil_mesh.in_set(RenderSet::Queue))
|
||||
.add_systems(Render, queue_outline_volume_mesh.in_set(RenderSet::Queue));
|
||||
|
||||
let world = &mut app.sub_app_mut(RenderApp).world;
|
||||
let node = OutlineNode::new(world);
|
||||
|
||||
let mut graph = world.resource_mut::<RenderGraph>();
|
||||
|
||||
let draw_3d_graph = graph
|
||||
.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME)
|
||||
.unwrap();
|
||||
draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node);
|
||||
|
||||
// Run after main 3D pass, but before UI psss
|
||||
draw_3d_graph.add_node_edge(
|
||||
bevy::core_pipeline::core_3d::graph::node::END_MAIN_PASS,
|
||||
OUTLINE_PASS_NODE_NAME,
|
||||
);
|
||||
#[cfg(feature = "bevy_ui")]
|
||||
draw_3d_graph.add_node_edge(
|
||||
OUTLINE_PASS_NODE_NAME,
|
||||
bevy::ui::draw_ui_graph::node::UI_PASS,
|
||||
);
|
||||
}
|
||||
|
||||
fn finish(&self, app: &mut App) {
|
||||
app.sub_app_mut(RenderApp)
|
||||
.init_resource::<OutlinePipeline>();
|
||||
}
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
use std::cmp::Reverse;
|
||||
|
||||
use bevy::ecs::system::lifetimeless::Read;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::camera::ExtractedCamera;
|
||||
use bevy::render::render_graph::NodeRunError;
|
||||
use bevy::render::render_phase::{
|
||||
CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, RenderPhase,
|
||||
};
|
||||
use bevy::render::render_resource::{
|
||||
CachedRenderPipelineId, LoadOp, Operations, RenderPassDepthStencilAttachment,
|
||||
RenderPassDescriptor,
|
||||
};
|
||||
use bevy::render::view::{ExtractedView, ViewDepthTexture, ViewTarget};
|
||||
use bevy::render::{
|
||||
render_graph::{Node, RenderGraphContext},
|
||||
renderer::RenderContext,
|
||||
};
|
||||
use bevy::utils::FloatOrd;
|
||||
|
||||
pub(crate) struct StencilOutline {
|
||||
pub distance: f32,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub entity: Entity,
|
||||
pub draw_function: DrawFunctionId,
|
||||
}
|
||||
|
||||
impl PhaseItem for StencilOutline {
|
||||
type SortKey = Reverse<FloatOrd>;
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
Reverse(FloatOrd(self.distance))
|
||||
}
|
||||
|
||||
fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for StencilOutline {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct OpaqueOutline {
|
||||
pub distance: f32,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub entity: Entity,
|
||||
pub draw_function: DrawFunctionId,
|
||||
}
|
||||
|
||||
impl PhaseItem for OpaqueOutline {
|
||||
type SortKey = Reverse<FloatOrd>;
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
Reverse(FloatOrd(self.distance))
|
||||
}
|
||||
|
||||
fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for OpaqueOutline {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TransparentOutline {
|
||||
pub distance: f32,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub entity: Entity,
|
||||
pub draw_function: DrawFunctionId,
|
||||
}
|
||||
|
||||
impl PhaseItem for TransparentOutline {
|
||||
type SortKey = FloatOrd;
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
FloatOrd(self.distance)
|
||||
}
|
||||
|
||||
fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for TransparentOutline {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) struct OutlineNode {
|
||||
query: QueryState<
|
||||
(
|
||||
Read<ExtractedCamera>,
|
||||
Read<RenderPhase<StencilOutline>>,
|
||||
Read<RenderPhase<OpaqueOutline>>,
|
||||
Read<RenderPhase<TransparentOutline>>,
|
||||
Read<Camera3d>,
|
||||
Read<ViewTarget>,
|
||||
Read<ViewDepthTexture>,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl OutlineNode {
|
||||
pub(crate) fn new(world: &mut World) -> Self {
|
||||
Self {
|
||||
query: world.query_filtered(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for OutlineNode {
|
||||
fn update(&mut self, world: &mut World) {
|
||||
self.query.update_archetypes(world);
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.view_entity();
|
||||
let (camera, stencil_phase, opaque_phase, transparent_phase, camera_3d, target, depth) =
|
||||
match self.query.get_manual(world, view_entity) {
|
||||
Ok(query) => query,
|
||||
Err(_) => {
|
||||
return Ok(());
|
||||
} // No window
|
||||
};
|
||||
|
||||
// If drawing anything, run stencil pass to clear the depth buffer
|
||||
if !opaque_phase.items.is_empty() || !transparent_phase.items.is_empty() {
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("outline_stencil_pass"),
|
||||
color_attachments: &[],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
||||
view: &depth.view,
|
||||
depth_ops: Some(Operations {
|
||||
load: camera_3d.depth_load_op.clone().into(),
|
||||
store: true,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
};
|
||||
let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
|
||||
if let Some(viewport) = camera.viewport.as_ref() {
|
||||
tracked_pass.set_camera_viewport(viewport);
|
||||
}
|
||||
stencil_phase.render(&mut tracked_pass, world, view_entity);
|
||||
}
|
||||
|
||||
if !opaque_phase.items.is_empty() {
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("outline_opaque_pass"),
|
||||
color_attachments: &[Some(target.get_color_attachment(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}))],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
||||
view: &depth.view,
|
||||
depth_ops: Some(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
};
|
||||
let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
|
||||
if let Some(viewport) = camera.viewport.as_ref() {
|
||||
tracked_pass.set_camera_viewport(viewport);
|
||||
}
|
||||
opaque_phase.render(&mut tracked_pass, world, view_entity);
|
||||
}
|
||||
|
||||
if !transparent_phase.items.is_empty() {
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("outline_transparent_pass"),
|
||||
color_attachments: &[Some(target.get_color_attachment(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}))],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
||||
view: &depth.view,
|
||||
depth_ops: Some(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
};
|
||||
let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
|
||||
if let Some(viewport) = camera.viewport.as_ref() {
|
||||
tracked_pass.set_camera_viewport(viewport);
|
||||
}
|
||||
transparent_phase.render(&mut tracked_pass, world, view_entity);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
#import bevy_render::view View
|
||||
#import bevy_pbr::mesh_types Mesh
|
||||
#import bevy_pbr::mesh_types SkinnedMesh
|
||||
|
||||
struct Vertex {
|
||||
#ifdef VERTEX_POSITIONS
|
||||
@location(0) position: vec3<f32>,
|
||||
#endif
|
||||
#ifndef OFFSET_ZERO
|
||||
@location(1) outline_normal: vec3<f32>,
|
||||
#endif
|
||||
#ifdef SKINNED
|
||||
@location(5) joint_indices: vec4<u32>,
|
||||
@location(6) joint_weights: vec4<f32>,
|
||||
#endif
|
||||
#ifdef MORPH_TARGETS
|
||||
@builtin(vertex_index) index: u32,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
#ifdef OPENGL_WORKAROUND
|
||||
@location(0) normalised_depth: f32,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct OutlineViewUniform {
|
||||
@align(16)
|
||||
scale: vec2<f32>,
|
||||
};
|
||||
|
||||
struct OutlineVertexUniform {
|
||||
@align(16)
|
||||
origin: vec3<f32>,
|
||||
offset: f32,
|
||||
};
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
|
||||
#import bevy_pbr::skinning
|
||||
#import bevy_pbr::morph
|
||||
|
||||
@group(2) @binding(0)
|
||||
var<uniform> view_uniform: OutlineViewUniform;
|
||||
|
||||
@group(3) @binding(0)
|
||||
var<uniform> vstage: OutlineVertexUniform;
|
||||
|
||||
#ifdef MORPH_TARGETS
|
||||
fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
||||
var vertex = vertex_in;
|
||||
let weight_count = bevy_pbr::morph::layer_count();
|
||||
for (var i: u32 = 0u; i < weight_count; i ++) {
|
||||
let weight = bevy_pbr::morph::weight_at(i);
|
||||
if weight == 0.0 {
|
||||
continue;
|
||||
}
|
||||
vertex.position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i);
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
#endif
|
||||
|
||||
fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
||||
return mat3x3<f32>(
|
||||
m[0].xyz, m[1].xyz, m[2].xyz
|
||||
);
|
||||
}
|
||||
|
||||
fn model_origin_z(plane: vec3<f32>, view_proj: mat4x4<f32>) -> f32 {
|
||||
var proj_zw = mat4x2<f32>(
|
||||
view_proj[0].zw, view_proj[1].zw,
|
||||
view_proj[2].zw, view_proj[3].zw);
|
||||
var zw = proj_zw * vec4<f32>(plane, 1.0);
|
||||
return zw.x / zw.y;
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
||||
#ifdef MORPH_TARGETS
|
||||
var vertex = morph_vertex(vertex_no_morph);
|
||||
#else
|
||||
var vertex = vertex_no_morph;
|
||||
#endif
|
||||
#ifdef SKINNED
|
||||
let model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
|
||||
#else
|
||||
let model = mesh.model;
|
||||
#endif
|
||||
let clip_pos = view.view_proj * (model * vec4<f32>(vertex.position, 1.0));
|
||||
#ifdef FLAT_DEPTH
|
||||
let out_z = model_origin_z(vstage.origin, view.view_proj) * clip_pos.w;
|
||||
#else
|
||||
let out_z = clip_pos.z;
|
||||
#endif
|
||||
#ifdef OFFSET_ZERO
|
||||
let out_xy = clip_pos.xy;
|
||||
#else
|
||||
let clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.outline_normal);
|
||||
let ndc_delta = vstage.offset * normalize(clip_norm.xy) * view_uniform.scale * clip_pos.w;
|
||||
let out_xy = clip_pos.xy + ndc_delta;
|
||||
#endif
|
||||
var out: VertexOutput;
|
||||
out.position = vec4<f32>(out_xy, out_z, clip_pos.w);
|
||||
#ifdef OPENGL_WORKAROUND
|
||||
out.normalised_depth = 10.0 + 0.5 * (out_z / clip_pos.w);
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use bevy::pbr::{setup_morph_and_skinning_defs, MeshPipelineKey};
|
||||
use bevy::prelude::*;
|
||||
use bevy::reflect::TypeUuid;
|
||||
use bevy::render::render_resource::{
|
||||
BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState,
|
||||
BufferBindingType, BufferSize, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
|
||||
DepthStencilState, Face, FragmentState, FrontFace, MultisampleState, PolygonMode,
|
||||
PrimitiveState, PrimitiveTopology, ShaderDefVal, ShaderSize, ShaderStages, StencilState,
|
||||
TextureFormat, VertexState,
|
||||
};
|
||||
use bevy::render::renderer::RenderDevice;
|
||||
use bevy::render::texture::BevyDefault;
|
||||
use bevy::render::view::ViewTarget;
|
||||
use bevy::{
|
||||
pbr::MeshPipeline,
|
||||
render::{
|
||||
mesh::MeshVertexBufferLayout,
|
||||
render_resource::{
|
||||
RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
||||
},
|
||||
},
|
||||
};
|
||||
use bitfield::{bitfield_bitrange, bitfield_fields};
|
||||
|
||||
use crate::uniforms::{
|
||||
DepthMode, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform,
|
||||
};
|
||||
use crate::view_uniforms::OutlineViewUniform;
|
||||
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||
|
||||
pub(crate) const OUTLINE_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2101625026478770097);
|
||||
|
||||
pub(crate) const FRAGMENT_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 12033806834125368121);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum PassType {
|
||||
Stencil = 1,
|
||||
Opaque = 2,
|
||||
Transparent = 3,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PipelineKey(u32);
|
||||
bitfield_bitrange! {struct PipelineKey(u32)}
|
||||
|
||||
impl PipelineKey {
|
||||
bitfield_fields! {
|
||||
u32;
|
||||
msaa_samples_minus_one, set_msaa_samples_minus_one: 5, 0;
|
||||
primitive_topology_int, set_primitive_topology_int: 8, 6;
|
||||
pass_type_int, set_pass_type_int: 10, 9;
|
||||
depth_mode_int, set_depth_mode_int: 12, 11;
|
||||
pub offset_zero, set_offset_zero: 13;
|
||||
pub hdr_format, set_hdr_format: 14;
|
||||
pub opengl_workaround, set_opengl_workaround: 15;
|
||||
pub morph_targets, set_morph_targets: 16;
|
||||
}
|
||||
|
||||
pub(crate) fn new() -> Self {
|
||||
PipelineKey(0)
|
||||
}
|
||||
|
||||
pub(crate) fn with_msaa(mut self, msaa: Msaa) -> Self {
|
||||
self.set_msaa_samples_minus_one(msaa as u32 - 1);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn msaa(&self) -> Msaa {
|
||||
match self.msaa_samples_minus_one() + 1 {
|
||||
x if x == Msaa::Off as u32 => Msaa::Off,
|
||||
x if x == Msaa::Sample2 as u32 => Msaa::Sample2,
|
||||
x if x == Msaa::Sample4 as u32 => Msaa::Sample4,
|
||||
x if x == Msaa::Sample8 as u32 => Msaa::Sample8,
|
||||
x => panic!("Invalid value for Msaa: {}", x),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_primitive_topology(mut self, primitive_topology: PrimitiveTopology) -> Self {
|
||||
self.set_primitive_topology_int(primitive_topology as u32);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn primitive_topology(&self) -> PrimitiveTopology {
|
||||
match self.primitive_topology_int() {
|
||||
x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList,
|
||||
x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList,
|
||||
x if x == PrimitiveTopology::LineStrip as u32 => PrimitiveTopology::LineStrip,
|
||||
x if x == PrimitiveTopology::TriangleList as u32 => PrimitiveTopology::TriangleList,
|
||||
x if x == PrimitiveTopology::TriangleStrip as u32 => PrimitiveTopology::TriangleStrip,
|
||||
x => panic!("Invalid value for PrimitiveTopology: {}", x),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_pass_type(mut self, pass_type: PassType) -> Self {
|
||||
self.set_pass_type_int(pass_type as u32);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn pass_type(&self) -> PassType {
|
||||
match self.pass_type_int() {
|
||||
x if x == PassType::Stencil as u32 => PassType::Stencil,
|
||||
x if x == PassType::Opaque as u32 => PassType::Opaque,
|
||||
x if x == PassType::Transparent as u32 => PassType::Transparent,
|
||||
x => panic!("Invalid value for PassType: {}", x),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_depth_mode(mut self, depth_mode: DepthMode) -> Self {
|
||||
self.set_depth_mode_int(depth_mode as u32);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn depth_mode(&self) -> DepthMode {
|
||||
match self.depth_mode_int() {
|
||||
x if x == DepthMode::Flat as u32 => DepthMode::Flat,
|
||||
x if x == DepthMode::Real as u32 => DepthMode::Real,
|
||||
x => panic!("Invalid value for DepthMode: {}", x),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_offset_zero(mut self, offset_zero: bool) -> Self {
|
||||
self.set_offset_zero(offset_zero);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_hdr_format(mut self, hdr_format: bool) -> Self {
|
||||
self.set_hdr_format(hdr_format);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_opengl_workaround(mut self, opengl_workaround: bool) -> Self {
|
||||
self.set_opengl_workaround(opengl_workaround);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_morph_targets(mut self, morph_targets: bool) -> Self {
|
||||
self.set_morph_targets(morph_targets);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineKey> for MeshPipelineKey {
|
||||
fn from(key: PipelineKey) -> Self {
|
||||
if key.morph_targets() {
|
||||
MeshPipelineKey::empty() | MeshPipelineKey::MORPH_TARGETS
|
||||
} else {
|
||||
MeshPipelineKey::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct OutlinePipeline {
|
||||
mesh_pipeline: MeshPipeline,
|
||||
pub outline_view_bind_group_layout: BindGroupLayout,
|
||||
pub outline_stencil_bind_group_layout: BindGroupLayout,
|
||||
pub outline_volume_bind_group_layout: BindGroupLayout,
|
||||
}
|
||||
|
||||
impl FromWorld for OutlinePipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let world = world.cell();
|
||||
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap().clone();
|
||||
let render_device = world.get_resource::<RenderDevice>().unwrap();
|
||||
let outline_view_bind_group_layout =
|
||||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("outline_view_bind_group_layout"),
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(OutlineViewUniform::SHADER_SIZE.get()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
});
|
||||
let outline_volume_bind_group_layout =
|
||||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("outline_volume_bind_group_layout"),
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(
|
||||
OutlineVolumeUniform::SHADER_SIZE.get(),
|
||||
),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(
|
||||
OutlineFragmentUniform::SHADER_SIZE.get(),
|
||||
),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
let outline_stencil_bind_group_layout =
|
||||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("outline_stencil_bind_group_layout"),
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(OutlineStencilUniform::SHADER_SIZE.get()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
});
|
||||
OutlinePipeline {
|
||||
mesh_pipeline,
|
||||
outline_view_bind_group_layout,
|
||||
outline_stencil_bind_group_layout,
|
||||
outline_volume_bind_group_layout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecializedMeshPipeline for OutlinePipeline {
|
||||
type Key = PipelineKey;
|
||||
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut targets = vec![];
|
||||
let mut vertex_defs = vec!["MESH_BINDGROUP_1".into()];
|
||||
let mut fragment_defs = vec![];
|
||||
let mut buffer_attrs = Vec::new();
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
vertex_defs.push("VERTEX_POSITIONS".into());
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
||||
}
|
||||
|
||||
let mut bind_layouts = vec![if key.msaa() == Msaa::Off {
|
||||
self.mesh_pipeline.view_layout.clone()
|
||||
} else {
|
||||
self.mesh_pipeline.view_layout_multisampled.clone()
|
||||
}];
|
||||
|
||||
bind_layouts.push(setup_morph_and_skinning_defs(
|
||||
&self.mesh_pipeline.mesh_layouts,
|
||||
layout,
|
||||
5,
|
||||
&key.into(),
|
||||
&mut vertex_defs,
|
||||
&mut buffer_attrs,
|
||||
));
|
||||
|
||||
bind_layouts.push(self.outline_view_bind_group_layout.clone());
|
||||
let cull_mode;
|
||||
if key.depth_mode() == DepthMode::Flat {
|
||||
vertex_defs.push(ShaderDefVal::from("FLAT_DEPTH"));
|
||||
cull_mode = Some(Face::Back);
|
||||
} else if key.pass_type() == PassType::Stencil {
|
||||
cull_mode = Some(Face::Back);
|
||||
} else {
|
||||
cull_mode = Some(Face::Front);
|
||||
}
|
||||
if key.offset_zero() {
|
||||
vertex_defs.push(ShaderDefVal::from("OFFSET_ZERO"));
|
||||
} else {
|
||||
buffer_attrs.push(
|
||||
if layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
|
||||
ATTRIBUTE_OUTLINE_NORMAL
|
||||
} else {
|
||||
Mesh::ATTRIBUTE_NORMAL
|
||||
}
|
||||
.at_shader_location(1),
|
||||
);
|
||||
}
|
||||
match key.pass_type() {
|
||||
PassType::Stencil => {
|
||||
bind_layouts.push(self.outline_stencil_bind_group_layout.clone());
|
||||
}
|
||||
PassType::Opaque | PassType::Transparent => {
|
||||
fragment_defs.push(ShaderDefVal::from("VOLUME"));
|
||||
targets.push(Some(ColorTargetState {
|
||||
format: if key.hdr_format() {
|
||||
ViewTarget::TEXTURE_FORMAT_HDR
|
||||
} else {
|
||||
TextureFormat::bevy_default()
|
||||
},
|
||||
blend: Some(if key.pass_type() == PassType::Transparent {
|
||||
BlendState::ALPHA_BLENDING
|
||||
} else {
|
||||
BlendState::REPLACE
|
||||
}),
|
||||
write_mask: ColorWrites::ALL,
|
||||
}));
|
||||
|
||||
bind_layouts.push(self.outline_volume_bind_group_layout.clone());
|
||||
}
|
||||
}
|
||||
if key.opengl_workaround() {
|
||||
let val = ShaderDefVal::from("OPENGL_WORKAROUND");
|
||||
vertex_defs.push(val.clone());
|
||||
fragment_defs.push(val);
|
||||
}
|
||||
let buffers = vec![layout.get_layout(&buffer_attrs)?];
|
||||
Ok(RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: OUTLINE_SHADER_HANDLE.typed::<Shader>(),
|
||||
entry_point: "vertex".into(),
|
||||
shader_defs: vertex_defs,
|
||||
buffers,
|
||||
},
|
||||
fragment: Some(FragmentState {
|
||||
shader: FRAGMENT_SHADER_HANDLE.typed::<Shader>(),
|
||||
shader_defs: fragment_defs,
|
||||
entry_point: "fragment".into(),
|
||||
targets,
|
||||
}),
|
||||
layout: bind_layouts,
|
||||
primitive: PrimitiveState {
|
||||
front_face: FrontFace::Ccw,
|
||||
cull_mode,
|
||||
unclipped_depth: false,
|
||||
polygon_mode: PolygonMode::Fill,
|
||||
conservative: false,
|
||||
topology: key.primitive_topology(),
|
||||
strip_index_format: None,
|
||||
},
|
||||
depth_stencil: Some(DepthStencilState {
|
||||
format: TextureFormat::Depth32Float,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: CompareFunction::Greater,
|
||||
stencil: StencilState::default(),
|
||||
bias: if key.depth_mode() == DepthMode::Flat && key.pass_type() == PassType::Stencil
|
||||
{
|
||||
DepthBiasState {
|
||||
// Values determined empirically
|
||||
constant: 3,
|
||||
slope_scale: 1.0,
|
||||
..default()
|
||||
}
|
||||
} else {
|
||||
default()
|
||||
},
|
||||
}),
|
||||
multisample: MultisampleState {
|
||||
count: key.msaa().samples(),
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
push_constant_ranges: default(),
|
||||
label: Some(Cow::Borrowed("outline_pipeline")),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
use bevy::{
|
||||
ecs::system::{
|
||||
lifetimeless::{Read, SRes},
|
||||
SystemParamItem,
|
||||
},
|
||||
prelude::*,
|
||||
render::{
|
||||
extract_component::{ComponentUniforms, DynamicUniformIndex},
|
||||
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry, ShaderType},
|
||||
renderer::RenderDevice,
|
||||
Extract,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
node::StencilOutline, pipeline::OutlinePipeline, ComputedOutlineDepth, OutlineStencil,
|
||||
OutlineVolume,
|
||||
};
|
||||
|
||||
#[derive(Clone, Component, ShaderType)]
|
||||
pub(crate) struct OutlineStencilUniform {
|
||||
#[align(16)]
|
||||
pub origin: Vec3,
|
||||
pub offset: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component, ShaderType)]
|
||||
pub(crate) struct OutlineVolumeUniform {
|
||||
#[align(16)]
|
||||
pub origin: Vec3,
|
||||
pub offset: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component, ShaderType)]
|
||||
pub(crate) struct OutlineFragmentUniform {
|
||||
#[align(16)]
|
||||
pub colour: Vec4,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
pub(crate) enum DepthMode {
|
||||
#[default]
|
||||
Invalid = 0,
|
||||
Flat = 1,
|
||||
Real = 2,
|
||||
}
|
||||
|
||||
impl DepthMode {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
*self != DepthMode::Invalid
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct OutlineStencilFlags {
|
||||
pub depth_mode: DepthMode,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct OutlineVolumeFlags {
|
||||
pub depth_mode: DepthMode,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct OutlineStencilBindGroup {
|
||||
pub bind_group: BindGroup,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct OutlineVolumeBindGroup {
|
||||
pub bind_group: BindGroup,
|
||||
}
|
||||
|
||||
pub(crate) fn extract_outline_stencil_uniforms(
|
||||
mut commands: Commands,
|
||||
query: Extract<Query<(Entity, &OutlineStencil, &ComputedOutlineDepth)>>,
|
||||
) {
|
||||
for (entity, stencil, computed) in query.iter() {
|
||||
if !stencil.enabled {
|
||||
continue;
|
||||
}
|
||||
commands
|
||||
.get_or_spawn(entity)
|
||||
.insert(OutlineStencilUniform {
|
||||
origin: computed.world_origin,
|
||||
offset: stencil.offset,
|
||||
})
|
||||
.insert(OutlineStencilFlags {
|
||||
depth_mode: computed.depth_mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extract_outline_volume_uniforms(
|
||||
mut commands: Commands,
|
||||
query: Extract<Query<(Entity, &OutlineVolume, &ComputedOutlineDepth)>>,
|
||||
) {
|
||||
for (entity, outline, computed) in query.iter() {
|
||||
if !outline.visible || outline.colour.a() == 0.0 {
|
||||
continue;
|
||||
}
|
||||
commands
|
||||
.get_or_spawn(entity)
|
||||
.insert(OutlineVolumeUniform {
|
||||
origin: computed.world_origin,
|
||||
offset: outline.width,
|
||||
})
|
||||
.insert(OutlineFragmentUniform {
|
||||
colour: outline.colour.as_linear_rgba_f32().into(),
|
||||
})
|
||||
.insert(OutlineVolumeFlags {
|
||||
depth_mode: computed.depth_mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn queue_outline_stencil_bind_group(
|
||||
mut commands: Commands,
|
||||
render_device: Res<RenderDevice>,
|
||||
outline_pipeline: Res<OutlinePipeline>,
|
||||
vertex: Res<ComponentUniforms<OutlineStencilUniform>>,
|
||||
) {
|
||||
if let Some(vertex_binding) = vertex.binding() {
|
||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: vertex_binding.clone(),
|
||||
}],
|
||||
label: Some("outline_stencil_bind_group"),
|
||||
layout: &outline_pipeline.outline_stencil_bind_group_layout,
|
||||
});
|
||||
commands.insert_resource(OutlineStencilBindGroup { bind_group });
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn queue_outline_volume_bind_group(
|
||||
mut commands: Commands,
|
||||
render_device: Res<RenderDevice>,
|
||||
outline_pipeline: Res<OutlinePipeline>,
|
||||
vertex: Res<ComponentUniforms<OutlineVolumeUniform>>,
|
||||
fragment: Res<ComponentUniforms<OutlineFragmentUniform>>,
|
||||
) {
|
||||
if let (Some(vertex_binding), Some(fragment_binding)) = (vertex.binding(), fragment.binding()) {
|
||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: vertex_binding.clone(),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: fragment_binding.clone(),
|
||||
},
|
||||
],
|
||||
label: Some("outline_volume_bind_group"),
|
||||
layout: &outline_pipeline.outline_volume_bind_group_layout,
|
||||
});
|
||||
commands.insert_resource(OutlineVolumeBindGroup { bind_group });
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SetOutlineStencilBindGroup<const I: usize>();
|
||||
|
||||
impl<const I: usize> RenderCommand<StencilOutline> for SetOutlineStencilBindGroup<I> {
|
||||
type ViewWorldQuery = ();
|
||||
type ItemWorldQuery = Read<DynamicUniformIndex<OutlineStencilUniform>>;
|
||||
type Param = SRes<OutlineStencilBindGroup>;
|
||||
fn render<'w>(
|
||||
_item: &StencilOutline,
|
||||
_view_data: (),
|
||||
entity_data: &DynamicUniformIndex<OutlineStencilUniform>,
|
||||
bind_group: SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
pass.set_bind_group(
|
||||
I,
|
||||
&bind_group.into_inner().bind_group,
|
||||
&[entity_data.index()],
|
||||
);
|
||||
RenderCommandResult::Success
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SetOutlineVolumeBindGroup<const I: usize>();
|
||||
|
||||
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineVolumeBindGroup<I> {
|
||||
type ViewWorldQuery = ();
|
||||
type ItemWorldQuery = (
|
||||
Read<DynamicUniformIndex<OutlineVolumeUniform>>,
|
||||
Read<DynamicUniformIndex<OutlineFragmentUniform>>,
|
||||
);
|
||||
type Param = SRes<OutlineVolumeBindGroup>;
|
||||
fn render<'w>(
|
||||
_item: &P,
|
||||
_view_data: (),
|
||||
entity_data: (
|
||||
&DynamicUniformIndex<OutlineVolumeUniform>,
|
||||
&DynamicUniformIndex<OutlineFragmentUniform>,
|
||||
),
|
||||
bind_group: SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
let (vertex, fragment) = entity_data;
|
||||
pass.set_bind_group(
|
||||
I,
|
||||
&bind_group.into_inner().bind_group,
|
||||
&[vertex.index(), fragment.index()],
|
||||
);
|
||||
RenderCommandResult::Success
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
use bevy::ecs::system::lifetimeless::{Read, SRes};
|
||||
use bevy::ecs::system::SystemParamItem;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::extract_component::{ComponentUniforms, DynamicUniformIndex};
|
||||
use bevy::render::render_phase::{
|
||||
PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, TrackedRenderPass,
|
||||
};
|
||||
use bevy::render::render_resource::ShaderType;
|
||||
use bevy::render::render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry};
|
||||
use bevy::render::renderer::RenderDevice;
|
||||
use bevy::render::view::RenderLayers;
|
||||
use bevy::render::Extract;
|
||||
|
||||
use crate::node::{OpaqueOutline, StencilOutline, TransparentOutline};
|
||||
use crate::pipeline::OutlinePipeline;
|
||||
|
||||
#[derive(Clone, Component, ShaderType)]
|
||||
pub(crate) struct OutlineViewUniform {
|
||||
#[align(16)]
|
||||
scale: Vec2,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct OutlineViewBindGroup {
|
||||
bind_group: BindGroup,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn extract_outline_view_uniforms(
|
||||
mut commands: Commands,
|
||||
query: Extract<Query<(Entity, &Camera, Option<&RenderLayers>), With<Camera3d>>>,
|
||||
) {
|
||||
for (entity, camera, view_mask) in query.iter() {
|
||||
if !camera.is_active {
|
||||
continue;
|
||||
}
|
||||
if let Some(size) = camera.logical_viewport_size() {
|
||||
let mut entity_commands = commands.get_or_spawn(entity);
|
||||
entity_commands
|
||||
.insert(OutlineViewUniform { scale: 2.0 / size })
|
||||
.insert(RenderPhase::<StencilOutline>::default())
|
||||
.insert(RenderPhase::<OpaqueOutline>::default())
|
||||
.insert(RenderPhase::<TransparentOutline>::default());
|
||||
|
||||
if let Some(view_mask) = view_mask {
|
||||
entity_commands.insert(*view_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn queue_outline_view_bind_group(
|
||||
mut commands: Commands,
|
||||
render_device: Res<RenderDevice>,
|
||||
outline_pipeline: Res<OutlinePipeline>,
|
||||
view_uniforms: Res<ComponentUniforms<OutlineViewUniform>>,
|
||||
) {
|
||||
if let Some(view_binding) = view_uniforms.binding() {
|
||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: view_binding.clone(),
|
||||
}],
|
||||
label: Some("outline_view_bind_group"),
|
||||
layout: &outline_pipeline.outline_view_bind_group_layout,
|
||||
});
|
||||
commands.insert_resource(OutlineViewBindGroup { bind_group });
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SetOutlineViewBindGroup<const I: usize>();
|
||||
|
||||
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineViewBindGroup<I> {
|
||||
type ViewWorldQuery = Read<DynamicUniformIndex<OutlineViewUniform>>;
|
||||
type ItemWorldQuery = ();
|
||||
type Param = SRes<OutlineViewBindGroup>;
|
||||
fn render<'w>(
|
||||
_item: &P,
|
||||
view_data: &DynamicUniformIndex<OutlineViewUniform>,
|
||||
_entity_data: (),
|
||||
bind_group: SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
pass.set_bind_group(I, &bind_group.into_inner().bind_group, &[view_data.index()]);
|
||||
RenderCommandResult::Success
|
||||
}
|
||||
}
|
||||
@@ -22,39 +22,40 @@ function getFiles(dir, files_): string[] {
|
||||
}
|
||||
|
||||
const mimeTypes = {
|
||||
".html": "text/html",
|
||||
".js": "text/javascript",
|
||||
".7z": "application/x-7z-compressed",
|
||||
".bin": "application/octet-stream",
|
||||
".css": "text/css",
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".eot": "font/eot",
|
||||
".gif": "image/gif",
|
||||
".gltf": "model/gltf+json",
|
||||
".gz": "application/gzip",
|
||||
".hdr": "image/vnd.radiance",
|
||||
".html": "text/html",
|
||||
".ico": "image/x-icon",
|
||||
".jpg": "image/jpeg",
|
||||
".js": "text/javascript",
|
||||
".json": "application/json",
|
||||
".ktx": "image/ktx",
|
||||
".ktx2": "image/ktx",
|
||||
".svg": "image/svg+xml",
|
||||
".json": "application/json",
|
||||
".woff": "font/woff",
|
||||
".woff2": "font/woff2",
|
||||
".ttf": "font/ttf",
|
||||
".eot": "font/eot",
|
||||
".otf": "font/otf",
|
||||
".wasm": "application/wasm",
|
||||
".meta": "text/plain",
|
||||
".mjs": "text/javascript",
|
||||
".txt": "text/plain",
|
||||
".xml": "text/xml",
|
||||
".mp3": "audio/mpeg",
|
||||
".mp4": "video/mp4",
|
||||
".otf": "font/otf",
|
||||
".png": "image/png",
|
||||
".rar": "application/x-rar-compressed",
|
||||
".svg": "image/svg+xml",
|
||||
".tiff": "image/tiff",
|
||||
".ttf": "font/ttf",
|
||||
".txt": "text/plain",
|
||||
".wasm": "application/wasm",
|
||||
".webm": "video/webm",
|
||||
".webp": "image/webp",
|
||||
".wgsl": "text/wgsl",
|
||||
".ico": "image/x-icon",
|
||||
".tiff": "image/tiff",
|
||||
".gz": "application/gzip",
|
||||
".woff": "font/woff",
|
||||
".woff2": "font/woff2",
|
||||
".xml": "text/xml",
|
||||
".zip": "application/zip",
|
||||
".rar": "application/x-rar-compressed",
|
||||
".7z": "application/x-7z-compressed",
|
||||
".bin": "application/octet-stream",
|
||||
}
|
||||
|
||||
export function ServerMiddleWare(server: ViteDevServer, assets: string[] = []) {
|
||||
|
||||
+5
-8
@@ -13,10 +13,10 @@ name = "studio-standalone"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
bevy-inspector-egui = "0.20.0"
|
||||
bevy_mod_raycast = "0.15.0"
|
||||
bevy_rapier3d = "0.22.0"
|
||||
# bevy_mod_outline = "0.5.0"
|
||||
bevy-inspector-egui = "0.21.0"
|
||||
bevy_mod_raycast = "0.16.0"
|
||||
bevy_rapier3d = "0.23.0"
|
||||
bevy_mod_outline = "0.6.0"
|
||||
bitflags = "2.4.0"
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
js-sys = { version = "0.3.65", optional = true }
|
||||
@@ -47,11 +47,8 @@ wasm = [
|
||||
[dependencies.bevy]
|
||||
workspace = true
|
||||
|
||||
[dependencies.bevy_mod_outline]
|
||||
path = "../bevy_mod_outline"
|
||||
|
||||
[dependencies.bevy_mod_picking]
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
default-features = false
|
||||
features = [
|
||||
"backend_bevy_ui",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_gltf::loader::GltfLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Ignore,
|
||||
)
|
||||
+13766
-13844
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_gltf::loader::GltfLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::forward_io::VertexOutput
|
||||
|
||||
fn inv_lerp(a: f32, b: f32, v: f32) -> f32 {
|
||||
if (a == b) { return 1.0; }
|
||||
@@ -32,7 +32,7 @@ fn grid_lines(pos: vec4<f32>, depth: f32, scale: f32, depth_min: f32, depth_max:
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
|
||||
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let depth = in.position.z;
|
||||
let pos = in.world_position;
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::render_resource::shader::ShaderLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Ignore,
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
|
||||
settings: (),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Load(
|
||||
loader: "bevy_render::texture::image_loader::ImageLoader",
|
||||
settings: (
|
||||
format: FromExtension,
|
||||
is_srgb: true,
|
||||
sampler: Default,
|
||||
),
|
||||
),
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user