Remove examples

Too much work to maintain wrt upgrades
main
Elijah C. Voigt 2 years ago
parent 023dd9dc00
commit 618d0c87b4

@ -1,59 +0,0 @@
use bevy::{gltf::Gltf, prelude::*};
fn main() {
App::new()
.add_plugins((DefaultPlugins,))
.add_systems(Update, (inspect, load_gltf))
.run();
}
#[derive(Debug, Component)]
struct InspectScene;
fn inspect(
mut events: EventReader<AssetEvent<Scene>>,
current: Query<Entity, With<InspectScene>>,
mut commands: Commands,
) {
events.read().for_each(|event| match event {
AssetEvent::LoadedWithDependencies { id } => {
// Cleanup existing scenes
current.iter().for_each(|e| {
commands.entity(e).despawn_recursive();
});
// Spawn default GLTF scene
commands.spawn((
SceneBundle {
scene: Handle::Weak(*id),
..default()
},
InspectScene,
));
}
_ => (),
});
}
fn load_gltf(
mut events: EventReader<FileDragAndDrop>,
server: Res<AssetServer>,
mut handle: Local<Handle<Gltf>>,
) {
events.read().for_each(|event| match event {
FileDragAndDrop::DroppedFile { path_buf, .. } => {
let p = path_buf
.clone()
.into_os_string()
.into_string()
.unwrap()
.replace(
"D:\\Projects\\src\\gitea.elijah.run\\martian-chess\\assets\\",
"",
);
info!("Loading {:?}", p);
*handle = server.load(p);
}
_ => warn!("ignored"),
})
}

@ -1,9 +0,0 @@
use gltf::*;
fn main() -> gltf::Result<()> {
let g = Gltf::open("assets/models/Martian Chess.glb")?;
g.animations().into_iter().for_each(|x| {
println!("{:?}", x.name());
});
Ok(())
}

@ -1,92 +0,0 @@
//! This example demonstrates how to use the FmodPlugin to play a sound.
//! Make sure to follow the instructions in the README.md to set up the demo project.
use bevy::input::keyboard::KeyboardInput;
use bevy::input::ButtonState;
use bevy::prelude::*;
use bevy_fmod::prelude::AudioSource;
use bevy_fmod::prelude::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
FmodPlugin {
audio_banks_paths: &[
"./assets/audio/Martian Chess/Build/Desktop/Master.bank",
"./assets/audio/Martian Chess/Build/Desktop/Master.strings.bank",
"./assets/audio/Martian Chess/Build/Desktop/Music.bank",
"./assets/audio/Martian Chess/Build/Desktop/SFX.bank",
],
},
))
.add_systems(Startup, startup)
.add_systems(Update, play_music)
.run();
}
#[derive(Component)]
struct MyMusicPlayer;
fn startup(mut commands: Commands, studio: Res<FmodStudio>) {
commands
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
..default()
},
..default()
})
.with_children(|parent| {
studio
.0
.get_bank_list(10)
.expect("List banks")
.into_iter()
.flat_map(|bank| {
bank.get_event_list(100)
.into_iter()
.flat_map(|events| events.into_iter())
.filter_map(|event| event.get_path().ok())
})
.for_each(|event_path| {
parent
.spawn((
ButtonBundle {
style: Style { ..default() },
..default()
},
AudioSource::new(studio.0.get_event(event_path.as_str()).unwrap()),
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
event_path,
TextStyle {
color: Color::BLACK.into(),
..default()
},
));
});
});
});
commands.spawn((
Camera2dBundle { ..default() },
UiCameraConfig { show_ui: true },
));
}
fn play_music(
mut events: Query<
(&Interaction, &mut AudioSource),
(Changed<Interaction>, With<Button>, With<AudioSource>),
>,
) {
events
.iter_mut()
.filter(|(&i, _)| i == Interaction::Pressed)
.for_each(|(_, a)| {
info!("Toggling Audio");
a.play();
});
}

@ -1,35 +0,0 @@
//! This example demonstrates Bevy's immediate mode drawing API intended for visual debugging.
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, system)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0., 1.5, 6.).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
// light
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 1500.0,
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});
}
fn system(mut gizmos: Gizmos) {
gizmos.cuboid(
Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.)),
Color::BLACK,
);
}

@ -1,227 +0,0 @@
#![feature(iter_array_chunks)]
/// Example to illustrate selecting objects in 3d space
///
use bevy::{
prelude::*,
render::mesh::MeshVertexAttribute,
render::render_resource::{Extent3d, TextureDimension, TextureFormat},
render::{mesh::VertexAttributeValues, render_resource::VertexFormat},
window::PrimaryWindow,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, select)
.run();
}
fn startup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut images: ResMut<Assets<Image>>,
) {
let debug_material = materials.add(StandardMaterial {
base_color_texture: Some(images.add(uv_debug_texture())),
..default()
});
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(0.0, 0.0, 5.0),
..default()
});
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Cube::default().into()),
material: debug_material.clone(),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
});
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Torus::default().into()),
material: debug_material.clone(),
transform: Transform::from_xyz(3.0, 0.0, 0.0),
..default()
});
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Icosphere::default().try_into().unwrap()),
material: debug_material.clone(),
transform: Transform::from_xyz(0.0, 3.0, 0.0),
..default()
});
}
/// Creates a colorful test pattern
fn uv_debug_texture() -> Image {
const TEXTURE_SIZE: usize = 8;
let mut palette: [u8; 32] = [
255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
];
let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
for y in 0..TEXTURE_SIZE {
let offset = TEXTURE_SIZE * y * 4;
texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
palette.rotate_right(4);
}
Image::new_fill(
Extent3d {
width: TEXTURE_SIZE as u32,
height: TEXTURE_SIZE as u32,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&texture_data,
TextureFormat::Rgba8UnormSrgb,
)
}
fn select(
query: Query<(Entity, &Handle<Mesh>, &GlobalTransform)>,
meshes: Res<Assets<Mesh>>,
cameras: Query<(&Camera, &GlobalTransform)>,
windows: Query<&Window, With<PrimaryWindow>>,
) {
// TODO:
// When mouse moves
// Ray trace to find object being selected
windows.iter().for_each(|window| {
if let Some(pos) = window.cursor_position() {
cameras.iter().for_each(|(camera, gt)| {
if let Some(ray) = camera.viewport_to_world(gt, pos) {
query
.iter()
.filter_map(|(entity, handle, gt)| {
meshes.get(handle).map(|mesh| (entity, mesh, gt))
})
.for_each(|(entity, mesh, gt)| {
let hit = intersects(&ray, &gt, mesh);
if hit.is_some() {
info!("We got a hit on {:?} at {:?}", entity, hit);
}
});
}
});
}
});
}
#[derive(Debug)]
struct Triangle {
v0: Vec3,
v1: Vec3,
v2: Vec3,
}
impl Triangle {
fn normal(&self) -> Vec3 {
(self.edge_a()).cross(self.edge_b())
}
fn edge_a(&self) -> Vec3 {
self.v1 - self.v0
}
fn edge_b(&self) -> Vec3 {
self.v2 - self.v0
}
fn edge0(&self) -> Vec3 {
self.v1 - self.v0
}
fn edge1(&self) -> Vec3 {
self.v2 - self.v1
}
fn edge2(&self) -> Vec3 {
self.v0 - self.v2
}
}
#[derive(Debug)]
struct Hit {
distance: f32,
point: Vec3,
}
/// Heavily synthesized by these two things:
/// * Textbook: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution.html
/// * Example: https://github.com/aevyrie/bevy_mod_raycast/blob/435d8ef100738797161ac3a9b910ea346a4ed6e6/src/raycast.rs#L43
fn intersects(ray: &Ray, gt: &GlobalTransform, mesh: &Mesh) -> Option<Hit> {
let attr = MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);
if let Some(verts) = mesh.attribute(attr) {
if let Some(idxs) = mesh.indices() {
match verts {
VertexAttributeValues::Float32x3(vals) => {
idxs.iter()
.array_chunks::<3>()
// Convert arrays to vec3
.map(|[x, y, z]| {
[
Vec3::from_array(vals[x]),
Vec3::from_array(vals[y]),
Vec3::from_array(vals[z]),
]
})
// Transform each point by the global transform
.map(|[a, b, c]| {
[
gt.transform_point(a),
gt.transform_point(b),
gt.transform_point(c),
]
})
// Collect everything into a triangle for easy operations
.map(|[v0, v1, v2]| Triangle { v0, v1, v2 })
.filter_map(|triangle| {
// Calculate the distance this ray hits the plane normal to the tri
if let Some(d) = ray.intersect_plane(triangle.v0, triangle.normal()) {
// Calculate the point on that plane which intersects
let p = ray.get_point(d);
// Inside out test
let hit = {
// Determine if p is w/in edge0
let c0 = triangle.edge0().cross(p - triangle.v0);
// Determine if p is w/in edge1
let c1 = triangle.edge1().cross(p - triangle.v1);
// Determine if p is w/in edge2
let c2 = triangle.edge2().cross(p - triangle.v2);
// Check all three at once
triangle.normal().dot(c0) > 0.0
&& triangle.normal().dot(c1) > 0.0
&& triangle.normal().dot(c2) > 0.0
};
hit.then_some(Hit {
distance: d,
point: p,
})
} else {
None
}
})
.min_by(|a, b| a.distance.total_cmp(&b.distance))
}
_ => None,
}
} else {
None
}
} else {
None
}
}

@ -1,186 +0,0 @@
use crate::prelude::*;
type MyMat = ExtendedMaterial<StandardMaterial, MatExt>;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(ImagePlugin::default_nearest()),
MaterialPlugin::<MyMat>::default(),
))
.insert_resource(Msaa::Off)
.add_systems(Startup, setup)
.add_systems(Update, apply_skybox)
.add_systems(Update, set_scene)
.add_systems(Update, rotate)
.add_systems(
Update,
toggle_material
.run_if(|keys: Res<Input<KeyCode>>| -> bool { keys.just_pressed(KeyCode::Space) }),
)
.add_systems(
Update,
init_materials
.run_if(|events: Query<Entity, Added<Transform>>| -> bool { !events.is_empty() }),
)
.run();
}
#[derive(Component)]
struct Root;
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
commands.spawn((SceneBundle { ..default() }, Root));
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 10.0,
..default()
},
transform: Transform::from_xyz(-1.0, 4.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
commands.spawn((Camera3dBundle {
transform: Transform::from_xyz(-1.0, 1.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},));
}
fn apply_skybox(
cameras: Query<Entity, With<Camera>>,
mut images: ResMut<Assets<Image>>,
assets: Res<AssetServer>,
mut commands: Commands,
mut done: Local<bool>,
mut handle: Local<Handle<Image>>,
) {
if !*done {
info!("Applying skybox...");
*handle = assets.load("images/skybox.png");
if let Some(image) = images.get_mut(handle.clone()) {
info!("Loaded skybox image");
// NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
// so they appear as one texture. The following code reconfigures the texture as necessary.
if image.texture_descriptor.array_layer_count() == 1 {
image.reinterpret_stacked_2d_as_array(
image.texture_descriptor.size.height / image.texture_descriptor.size.width,
);
image.texture_view_descriptor = Some(TextureViewDescriptor {
dimension: Some(TextureViewDimension::Cube),
..default()
});
cameras.iter().for_each(|e| {
commands.entity(e).insert(Skybox { image: handle.clone(), brightness: 1.0 });
});
*done = true;
}
} else {
*done = false;
}
}
}
fn set_scene(
mut events: Query<&mut Handle<Scene>, (With<Root>, Added<Root>)>,
assets: Res<AssetServer>,
) {
events.iter_mut().for_each(|mut scene| {
*scene = assets.load("models/Martian Chess.glb#Scene0");
});
}
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
struct MatExt {
#[uniform(100)]
cutoff: f32,
}
impl MaterialExtension for MatExt {
fn fragment_shader() -> ShaderRef {
"examples/shaders/dissolve.wgsl".into()
}
}
#[derive(Debug, Component)]
struct Backup<T: Component>(T);
fn init_materials(
events: Query<(Entity, &Handle<StandardMaterial>), Added<Handle<StandardMaterial>>>,
standard_materials: Res<Assets<StandardMaterial>>,
mut materials: ResMut<Assets<MyMat>>,
mut commands: Commands,
) {
info!("initializing materials");
events.iter().for_each(|(entity, std_handle)| {
// Extension we will add to existing gltf-sourced materials
let extension = MatExt { cutoff: 1.0 };
// Base material we will extend for the duration of the dissolve effect
let mut base = standard_materials
.get(std_handle)
.expect("Resolve material data")
.clone();
base.opaque_render_method = OpaqueRendererMethod::Auto;
let ext_handle = materials.add(ExtendedMaterial { base, extension });
commands.entity(entity).insert(Backup(ext_handle.clone()));
});
}
fn toggle_material(
query: Query<Entity, Or<(With<Handle<StandardMaterial>>, With<Handle<MyMat>>)>>,
std_mat: Query<(&Handle<StandardMaterial>, Option<&Backup<Handle<MyMat>>>)>,
ext_mat: Query<(&Handle<MyMat>, Option<&Backup<Handle<StandardMaterial>>>)>,
mut commands: Commands,
) {
query.iter().for_each(|entity| {
// Entity has standard material, and had extended material
// Swap these materials
if let Ok((std_handle, Some(Backup(ext_handle)))) = std_mat.get(entity) {
info!("Swapping standard material for extended material");
commands
.entity(entity)
.insert(ext_handle.clone())
.insert(Backup(std_handle.clone()))
.remove::<Backup<Handle<MyMat>>>()
.remove::<Handle<StandardMaterial>>();
return;
}
// In this branch we have the extended material assigned to the object
if let Ok((ext_handle, Some(Backup(std_handle)))) = ext_mat.get(entity) {
// Entity has standard material, and had extended material // Swap these materials
info!("Swapping extended material for standard material");
commands
.entity(entity)
.insert(std_handle.clone())
.insert(Backup(ext_handle.clone()))
.remove::<Backup<Handle<StandardMaterial>>>()
.remove::<Handle<MyMat>>();
return;
}
panic!("What is happening?")
});
}
fn rotate(
mut query: Query<&mut Transform, With<Handle<Mesh>>>,
time: Res<Time>,
mut materials: ResMut<Assets<MyMat>>,
) {
query.iter_mut().for_each(|mut t| {
t.rotate_local_y(time.delta_seconds() / 2.0);
});
materials.iter_mut().for_each(|(_id, m)| {
m.extension.cutoff = time.elapsed_seconds().sin().abs();
})
}

@ -1,70 +0,0 @@
use bevy::{
core_pipeline::Skybox,
input::mouse::MouseMotion,
prelude::*,
render::render_resource::{TextureViewDescriptor, TextureViewDimension},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, init)
.add_systems(Update, (add_skybox, move_camera))
.run()
}
fn init(mut commands: Commands) {
commands.spawn(Camera3dBundle {
camera: Camera {
is_active: true,
..default()
},
..default()
});
}
fn add_skybox(
server: Res<AssetServer>,
query: Query<Entity, With<Camera>>,
mut images: ResMut<Assets<Image>>,
mut commands: Commands,
mut handle: Local<Option<Handle<Image>>>,
mut loaded: Local<bool>,
) {
if !(*loaded) {
if let Some(handle) = (*handle).clone() {
if let Some(image) = images.get_mut(&handle) {
info!("Loaded skybox image");
// NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
// so they appear as one texture. The following code reconfigures the texture as necessary.
if image.texture_descriptor.array_layer_count() == 1 {
image.reinterpret_stacked_2d_as_array(
image.texture_descriptor.size.height / image.texture_descriptor.size.width,
);
image.texture_view_descriptor = Some(TextureViewDescriptor {
dimension: Some(TextureViewDimension::Cube),
..default()
});
}
query.iter().for_each(|entity| {
commands.entity(entity).insert(Skybox(handle.clone()));
});
*loaded = true;
}
} else {
*handle = Some(server.load("images/skybox.png"));
}
}
}
fn move_camera(
mut events: EventReader<MouseMotion>,
mut camera: Query<&mut Transform, With<Camera>>,
) {
events.iter().for_each(|MouseMotion { delta }| {
camera.iter_mut().for_each(|mut t| {
t.rotate_local_y(-delta.x / 256.0);
t.rotate_local_x(-delta.y / 256.0);
})
});
}

@ -1,111 +0,0 @@
use std::time::Duration;
use bevy::{asset::ChangeWatcher, prelude::*, sprite::MaterialMesh2dBundle};
const SCALE: f32 = 80.0;
fn main() {
App::new()
.add_plugins((DefaultPlugins
.set(ImagePlugin::default_nearest())
.set(WindowPlugin {
primary_window: Some(Window {
title: "2D Sprites".into(),
resolution: (640., 480.).into(),
..default()
}),
..default()
})
.set(AssetPlugin {
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
..default()
}),))
.add_systems(Startup, (initialize_camera, load_spritesheet))
.add_systems(Update, initialize_board.run_if(check_initialize_board))
.run();
}
/// Sprite sheet Resource for later reference
#[derive(Debug, Resource)]
struct SpriteSheet {
handle: Handle<TextureAtlas>,
}
// Marker component for the 2d board entity
#[derive(Debug, Component)]
struct Board2d;
#[derive(Debug, Component)]
struct BoardIndex {
x: usize,
y: usize,
}
/// STARTUP: Initialize 2d gameplay Camera
fn initialize_camera(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
}
/// STARTUP: Load sprite sheet and insert texture atlas
fn load_spritesheet(
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
server: Res<AssetServer>,
mut commands: Commands,
) {
let atlas = TextureAtlas::from_grid(
server.load("sprites.png"),
Vec2::new(16.0, 16.0),
5,
1,
None,
None,
);
commands.insert_resource(SpriteSheet {
handle: texture_atlases.add(atlas),
});
}
fn check_initialize_board(query: Query<Entity, With<Board2d>>) -> bool {
query.is_empty()
}
/// STARTUP: Initialize the board for representation
fn initialize_board(sprite_sheet: Option<Res<SpriteSheet>>, mut commands: Commands) {
if let Some(sprite_sheet) = sprite_sheet {
commands
.spawn((
SpatialBundle {
transform: Transform::from_xyz(-SCALE * 3.5, -SCALE * 1.5, 0.0),
..default()
},
Board2d,
))
.with_children(|parent| {
for i in 0..32 {
let x = i % 8;
let y = i / 8;
let s = (x % 2) ^ (y % 2);
let transform = Transform::from_scale(Vec3::splat(5.0))
.with_translation(Vec3::new(SCALE * x as f32, SCALE * y as f32, 0.0));
let sprite = TextureAtlasSprite::new(s);
let texture_atlas = sprite_sheet.handle.clone();
let index = BoardIndex { x, y };
// Rectangle
parent.spawn((
SpriteSheetBundle {
texture_atlas,
sprite,
transform,
..default()
},
index,
));
}
});
}
}

@ -1,73 +0,0 @@
use bevy::prelude::Color;
use serde::Deserialize;
use toml::Table;
#[derive(Deserialize, Debug)]
enum Val {
A(String),
B(usize),
C(bool),
}
fn main() {
let s = r#"
[color]
asdf = true
[color.foo]
qwert = false
[color.foo.bar]
hjkl = [1,2,3]
[color.foo.bar.baz]
Rgba = { red = 1.0, blue = 1.0, green = 1.0, alpha = 1.0 }
"#;
let vt = toml::from_str::<Table>(s).unwrap();
iter_all(&vt, "")
.iter()
.for_each(|(k, v)| println!("k {:?} :: v {:?}", k, v));
let x = get::<Color>(&vt, "color_foo_bar_baz").unwrap();
println!("{:?}", x);
let y = get::<Vec<usize>>(&vt, "color_foo_bar_hjkl").unwrap();
println!("{:?}", y);
let z = get::<bool>(&vt, "color_foo_qwert").unwrap();
println!("{:?}", z);
}
fn iter_all(t: &toml::Table, key: &str) -> Vec<(String, toml::Value)> {
t.iter()
.flat_map(|(k, v)| {
let nk = if key.is_empty() {
k.to_string()
} else {
format!("{}_{}", key, k)
};
match v {
toml::Value::Table(nt) => iter_all(nt, nk.as_str()),
_ => vec![(nk, v.clone())],
}
})
.collect()
}
fn locate(t: &toml::Table, key: &str) -> Option<toml::Value> {
t.iter().find_map(|(k, v)| {
if key == k {
Some(v.clone())
} else if key.starts_with(k) {
let prefix = format!("{}_", k);
match v {
toml::Value::Table(nt) => locate(nt, key.strip_prefix(prefix.as_str()).unwrap()),
_ => Some(v.clone()),
}
} else {
None
}
})
}
fn get<'de, T: Deserialize<'de>>(t: &toml::Table, key: &str) -> Option<T> {
locate(t, key).and_then(|val| val.try_into().ok())
}

@ -795,13 +795,13 @@ fn select(
selections selections
.send(game::Selection(board_index)); .send(game::Selection(board_index));
}); });
()
}); });
}); });
()
}); });
}); });
()
}); });
}); });
}); });

@ -359,7 +359,7 @@ impl Board {
// Capture the intened move in the moves ledger // Capture the intened move in the moves ledger
moves.push(Move { moves.push(Move {
epoch, epoch,
from: from, from,
to: Some(to), to: Some(to),
}); });

@ -22,7 +22,7 @@ pub(crate) use bevy::{
ButtonState, ButtonState,
}, },
pbr::{ pbr::{
ScreenSpaceAmbientOcclusionQualityLevel, OpaqueRendererMethod, ScreenSpaceAmbientOcclusionQualityLevel,
ExtendedMaterial, MaterialExtension, ScreenSpaceAmbientOcclusionBundle, ExtendedMaterial, MaterialExtension, ScreenSpaceAmbientOcclusionBundle,
ScreenSpaceAmbientOcclusionSettings, ScreenSpaceAmbientOcclusionSettings,
}, },

Loading…
Cancel
Save