Struct bevy_doryen::doryen::Image
source · pub struct Image { /* private fields */ }
Expand description
An easy way to load PNG images and blit them on the console
Implementations§
source§impl Image
impl Image
sourcepub fn new(file_path: &str) -> Image
pub fn new(file_path: &str) -> Image
Create an image and load a PNG file. On the web platform, image loading is asynchronous. Using blit methods before the image is loaded has no impact on the console.
Examples found in repository?
More examples
sourcepub fn width(&self) -> u32
pub fn width(&self) -> u32
Returns the image’s width in pixels or 0 if the image has not yet been loaded
Examples found in repository?
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
fn compute_lightmap(
mut light_map: NonSendMut<LightMap>,
size: Res<LevelSize>,
query: Query<(&Light, &Position)>,
level_map: Res<LevelMap>,
) {
let light_map = &mut light_map.0;
*light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let mut fov = FovRestrictive::new();
for (light, light_position) in &query {
let (px, py, intensity, radius) = if light.flicker {
// alter light position, radius and intensity over time
(
light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
)
} else {
(
light_position.x,
light_position.y,
light.intensity,
light.radius,
)
};
let minx = ((px - radius).floor() as i32).max(0) as u32;
let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
let miny = ((py - radius).floor() as i32).max(0) as u32;
let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
let width = maxx - minx + 1;
let height = maxy - miny + 1;
let mut map = MapData::new(width as usize, height as usize);
for y in miny..=maxy {
for x in minx..=maxx {
map.set_transparent(
(x - minx) as usize,
(y - miny) as usize,
level_map.0.is_transparent(x as usize, y as usize),
);
}
}
fov.compute_fov(
&mut map,
px as usize - minx as usize,
py as usize - miny as usize,
radius as usize,
true,
);
let light_color = color_scale(light.color, intensity);
let radius_squared = radius * radius;
let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
for y in miny..=maxy {
for x in minx..=maxx {
if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
let dx = x as f32 - px;
let dy = y as f32 - py;
// good looking lights.
let squared_dist = dx * dx + dy * dy;
let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
let intensity_coef = intensity_coef - radius_coef;
let intensity_coef = intensity_coef / (1.0 - radius_coef);
if intensity_coef > 0.0 {
let light = color_blend(BLACK, light_color, intensity_coef);
let cur_light = light_map.pixel(x, y).unwrap();
light_map.put_pixel(x, y, color_add(light, cur_light));
}
}
}
}
}
}
sourcepub fn height(&self) -> u32
pub fn height(&self) -> u32
Returns the image’s height in pixels or 0 if the image has not yet been loaded
Examples found in repository?
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
fn compute_lightmap(
mut light_map: NonSendMut<LightMap>,
size: Res<LevelSize>,
query: Query<(&Light, &Position)>,
level_map: Res<LevelMap>,
) {
let light_map = &mut light_map.0;
*light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let mut fov = FovRestrictive::new();
for (light, light_position) in &query {
let (px, py, intensity, radius) = if light.flicker {
// alter light position, radius and intensity over time
(
light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
)
} else {
(
light_position.x,
light_position.y,
light.intensity,
light.radius,
)
};
let minx = ((px - radius).floor() as i32).max(0) as u32;
let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
let miny = ((py - radius).floor() as i32).max(0) as u32;
let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
let width = maxx - minx + 1;
let height = maxy - miny + 1;
let mut map = MapData::new(width as usize, height as usize);
for y in miny..=maxy {
for x in minx..=maxx {
map.set_transparent(
(x - minx) as usize,
(y - miny) as usize,
level_map.0.is_transparent(x as usize, y as usize),
);
}
}
fov.compute_fov(
&mut map,
px as usize - minx as usize,
py as usize - miny as usize,
radius as usize,
true,
);
let light_color = color_scale(light.color, intensity);
let radius_squared = radius * radius;
let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
for y in miny..=maxy {
for x in minx..=maxx {
if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
let dx = x as f32 - px;
let dy = y as f32 - py;
// good looking lights.
let squared_dist = dx * dx + dy * dy;
let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
let intensity_coef = intensity_coef - radius_coef;
let intensity_coef = intensity_coef / (1.0 - radius_coef);
if intensity_coef > 0.0 {
let light = color_blend(BLACK, light_color, intensity_coef);
let cur_light = light_map.pixel(x, y).unwrap();
light_map.put_pixel(x, y, color_add(light, cur_light));
}
}
}
}
}
}
sourcepub fn new_empty(width: u32, height: u32) -> Image
pub fn new_empty(width: u32, height: u32) -> Image
Create an empty image.
Examples found in repository?
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
fn default() -> Self {
Self(Image::new_empty(0, 0))
}
}
impl Light {
pub fn new(radius: f32, color: Color, flicker: bool) -> Self {
Self {
radius,
color,
intensity: LIGHT_INTENSITY,
// random t initial value so that all lights don't flicker in sync
t: thread_rng().gen::<f32>() * 100. * 150.,
flicker,
}
}
}
fn init(world: &mut World) {
world.init_non_send_resource::<LightMap>();
}
fn update_time(mut query: Query<&mut Light>) {
for mut light in &mut query {
light.t += TIME_SCALE;
}
}
fn compute_lightmap(
mut light_map: NonSendMut<LightMap>,
size: Res<LevelSize>,
query: Query<(&Light, &Position)>,
level_map: Res<LevelMap>,
) {
let light_map = &mut light_map.0;
*light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let mut fov = FovRestrictive::new();
for (light, light_position) in &query {
let (px, py, intensity, radius) = if light.flicker {
// alter light position, radius and intensity over time
(
light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
)
} else {
(
light_position.x,
light_position.y,
light.intensity,
light.radius,
)
};
let minx = ((px - radius).floor() as i32).max(0) as u32;
let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
let miny = ((py - radius).floor() as i32).max(0) as u32;
let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
let width = maxx - minx + 1;
let height = maxy - miny + 1;
let mut map = MapData::new(width as usize, height as usize);
for y in miny..=maxy {
for x in minx..=maxx {
map.set_transparent(
(x - minx) as usize,
(y - miny) as usize,
level_map.0.is_transparent(x as usize, y as usize),
);
}
}
fov.compute_fov(
&mut map,
px as usize - minx as usize,
py as usize - miny as usize,
radius as usize,
true,
);
let light_color = color_scale(light.color, intensity);
let radius_squared = radius * radius;
let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
for y in miny..=maxy {
for x in minx..=maxx {
if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
let dx = x as f32 - px;
let dy = y as f32 - py;
// good looking lights.
let squared_dist = dx * dx + dy * dy;
let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
let intensity_coef = intensity_coef - radius_coef;
let intensity_coef = intensity_coef / (1.0 - radius_coef);
if intensity_coef > 0.0 {
let light = color_blend(BLACK, light_color, intensity_coef);
let cur_light = light_map.pixel(x, y).unwrap();
light_map.put_pixel(x, y, color_add(light, cur_light));
}
}
}
}
}
}
More examples
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
fn render_level(
mut root_console: ResMut<RootConsole>,
ground: NonSend<GroundImage>,
size: Res<LevelSize>,
map: Res<LevelMap>,
mut visited_2x: ResMut<Visited2x>,
light_map: NonSend<LightMap>,
) {
let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let light_map = &light_map.0;
for y in 0..size.height * 2 {
for x in 0..size.width * 2 {
let off = x + y * size.width * 2;
if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
visited_2x.0[off] = true;
let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
let light_col = light_map.pixel(x as u32, y as u32).unwrap();
let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
r = r.min(255.0);
g = g.min(255.0);
b = b.min(255.0);
render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
} else if visited_2x.0[off] {
let col = ground.0.pixel(x as u32, y as u32).unwrap();
let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
render_output.put_pixel(x as u32, y as u32, dark_col);
} else {
render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
}
}
}
render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
sourcepub fn pixel(&self, x: u32, y: u32) -> Option<(u8, u8, u8, u8)>
pub fn pixel(&self, x: u32, y: u32) -> Option<(u8, u8, u8, u8)>
get the color of a specific pixel inside the image
Examples found in repository?
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
fn render_player(
mut root_console: ResMut<RootConsole>,
light_map: NonSend<LightMap>,
query: Query<&Position, With<Player>>,
) {
let player_position = query.single();
let light = light_map
.0
.pixel(player_position.x as u32, player_position.y as u32)
.unwrap();
root_console.ascii(
player_position.character_x(),
player_position.character_y(),
'@' as u16,
);
root_console.fore(
player_position.character_x(),
player_position.character_y(),
light,
);
}
More examples
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
fn render_character(
mut root_console: ResMut<RootConsole>,
query: Query<(&Character, &Position)>,
level_map: Res<LevelMap>,
light_map: NonSend<LightMap>,
) {
for (character, position) in &query {
if !level_map
.0
.is_in_fov(position.x as usize, position.y as usize)
{
continue;
}
let (color, penumbra) = if character.light {
(character.color, false)
} else {
let light = light_map
.0
.pixel(position.x as u32, position.y as u32)
.unwrap();
let penumbra = is_penumbra(light, 100);
let mut color = color_mul(character.color, light);
if penumbra {
color = color_scale(color, LIGHT_COEF);
}
(color, penumbra)
};
root_console.ascii(
position.character_x(),
position.character_y(),
if penumbra { '?' as u16 } else { character.ch },
);
root_console.fore(position.character_x(), position.character_y(), color);
}
}
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
fn compute_lightmap(
mut light_map: NonSendMut<LightMap>,
size: Res<LevelSize>,
query: Query<(&Light, &Position)>,
level_map: Res<LevelMap>,
) {
let light_map = &mut light_map.0;
*light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let mut fov = FovRestrictive::new();
for (light, light_position) in &query {
let (px, py, intensity, radius) = if light.flicker {
// alter light position, radius and intensity over time
(
light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
)
} else {
(
light_position.x,
light_position.y,
light.intensity,
light.radius,
)
};
let minx = ((px - radius).floor() as i32).max(0) as u32;
let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
let miny = ((py - radius).floor() as i32).max(0) as u32;
let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
let width = maxx - minx + 1;
let height = maxy - miny + 1;
let mut map = MapData::new(width as usize, height as usize);
for y in miny..=maxy {
for x in minx..=maxx {
map.set_transparent(
(x - minx) as usize,
(y - miny) as usize,
level_map.0.is_transparent(x as usize, y as usize),
);
}
}
fov.compute_fov(
&mut map,
px as usize - minx as usize,
py as usize - miny as usize,
radius as usize,
true,
);
let light_color = color_scale(light.color, intensity);
let radius_squared = radius * radius;
let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
for y in miny..=maxy {
for x in minx..=maxx {
if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
let dx = x as f32 - px;
let dy = y as f32 - py;
// good looking lights.
let squared_dist = dx * dx + dy * dy;
let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
let intensity_coef = intensity_coef - radius_coef;
let intensity_coef = intensity_coef / (1.0 - radius_coef);
if intensity_coef > 0.0 {
let light = color_blend(BLACK, light_color, intensity_coef);
let cur_light = light_map.pixel(x, y).unwrap();
light_map.put_pixel(x, y, color_add(light, cur_light));
}
}
}
}
}
}
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
fn load_level(
mut level_image: NonSendMut<LevelImage>,
mut ground_image: NonSendMut<GroundImage>,
mut next_game_state: ResMut<NextState<GameState>>,
mut visited_2x: ResMut<Visited2x>,
mut walls: ResMut<Walls>,
mut commands: Commands,
mut size: ResMut<LevelSize>,
) {
let level_image = &mut level_image.0;
let level_image_try_load = level_image.try_load();
if level_image_try_load {
let image_size = level_image.try_get_size().unwrap();
size.width = image_size.0 as usize / 2;
size.height = image_size.1 as usize / 2;
walls.data = vec![false; size.width * size.height];
walls.level_width = size.width;
let mut map = MapData::new(image_size.0 as usize, image_size.1 as usize);
visited_2x.0.reserve((image_size.0 * image_size.1) as usize);
for y in 0..image_size.1 {
for x in 0..image_size.0 {
let p = level_image.pixel(x, y).unwrap();
map.set_transparent(x as usize, y as usize, p != WALL_COLOR);
visited_2x.0.push(false);
let pos_1x = (x as f32 / 2., y as f32 / 2.);
match p {
START_COLOR => commands.insert_resource(Start((x as f32, y as f32).into())),
LIGHT_COLOR => {
commands.spawn(LightBundle {
position: Position {
x: x as f32,
y: y as f32,
},
..LightBundle::new(LIGHT_RADIUS, LIGHT_COLOR, true)
});
}
GOBLIN_COLOR => {
let offset =
(pos_1x.0 as i32 + pos_1x.1 as i32 * size.width as i32) as usize;
walls.data[offset] = true;
commands.spawn(GoblinBundle {
position: Position {
x: x as f32,
y: y as f32,
},
..Default::default()
});
}
_ => (),
}
}
}
for y in 0..size.height {
for x in 0..size.width {
let mut count = 0;
let x2 = x * 2;
let y2 = y * 2;
if map.is_transparent(x2, y2) {
count += 1;
}
if map.is_transparent(x2 + 1, y2) {
count += 1;
}
if map.is_transparent(x2, y2 + 1) {
count += 1;
}
if map.is_transparent(x2 + 1, y2 + 1) {
count += 1;
}
if count < 2 {
let offset = x + y * size.width;
walls.data[offset] = true;
}
}
}
commands.insert_resource(LevelMap(map));
}
if level_image_try_load && ground_image.0.try_load() {
next_game_state.set(GameState::Running);
}
}
fn unload_level_image(world: &mut World) {
world.remove_non_send_resource::<LevelImage>();
}
fn initial_compute_fov(
player_query: Query<&Position, With<Player>>,
mut map: ResMut<LevelMap>,
mut fov: ResMut<PlayerFov>,
) {
let player_position = player_query.single();
map.0.clear_fov();
fov.0.compute_fov(
&mut map.0,
player_position.x as usize,
player_position.y as usize,
PLAYER_FOV_RADIUS,
true,
);
}
fn compute_fov(
player_query: Query<&Position, (With<Player>, Changed<Position>)>,
mut map: ResMut<LevelMap>,
mut fov: ResMut<PlayerFov>,
) {
if let Ok(player_position) = player_query.get_single() {
map.0.clear_fov();
fov.0.compute_fov(
&mut map.0,
player_position.x as usize,
player_position.y as usize,
PLAYER_FOV_RADIUS,
true,
);
}
}
fn render_loading_level(mut root_console: ResMut<RootConsole>) {
let x = (root_console.get_width() / 2) as i32;
let y = (root_console.get_height() / 2) as i32;
root_console.print_color(x, y, "#[white]Loading#[red]...", TextAlign::Center, None);
}
fn render_level(
mut root_console: ResMut<RootConsole>,
ground: NonSend<GroundImage>,
size: Res<LevelSize>,
map: Res<LevelMap>,
mut visited_2x: ResMut<Visited2x>,
light_map: NonSend<LightMap>,
) {
let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let light_map = &light_map.0;
for y in 0..size.height * 2 {
for x in 0..size.width * 2 {
let off = x + y * size.width * 2;
if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
visited_2x.0[off] = true;
let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
let light_col = light_map.pixel(x as u32, y as u32).unwrap();
let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
r = r.min(255.0);
g = g.min(255.0);
b = b.min(255.0);
render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
} else if visited_2x.0[off] {
let col = ground.0.pixel(x as u32, y as u32).unwrap();
let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
render_output.put_pixel(x as u32, y as u32, dark_col);
} else {
render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
}
}
}
render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
sourcepub fn put_pixel(&mut self, x: u32, y: u32, color: (u8, u8, u8, u8))
pub fn put_pixel(&mut self, x: u32, y: u32, color: (u8, u8, u8, u8))
sets the color of a specific pixel inside the image
Examples found in repository?
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
fn render_level(
mut root_console: ResMut<RootConsole>,
ground: NonSend<GroundImage>,
size: Res<LevelSize>,
map: Res<LevelMap>,
mut visited_2x: ResMut<Visited2x>,
light_map: NonSend<LightMap>,
) {
let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let light_map = &light_map.0;
for y in 0..size.height * 2 {
for x in 0..size.width * 2 {
let off = x + y * size.width * 2;
if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
visited_2x.0[off] = true;
let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
let light_col = light_map.pixel(x as u32, y as u32).unwrap();
let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
r = r.min(255.0);
g = g.min(255.0);
b = b.min(255.0);
render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
} else if visited_2x.0[off] {
let col = ground.0.pixel(x as u32, y as u32).unwrap();
let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
render_output.put_pixel(x as u32, y as u32, dark_col);
} else {
render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
}
}
}
render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
More examples
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
fn compute_lightmap(
mut light_map: NonSendMut<LightMap>,
size: Res<LevelSize>,
query: Query<(&Light, &Position)>,
level_map: Res<LevelMap>,
) {
let light_map = &mut light_map.0;
*light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let mut fov = FovRestrictive::new();
for (light, light_position) in &query {
let (px, py, intensity, radius) = if light.flicker {
// alter light position, radius and intensity over time
(
light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
)
} else {
(
light_position.x,
light_position.y,
light.intensity,
light.radius,
)
};
let minx = ((px - radius).floor() as i32).max(0) as u32;
let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
let miny = ((py - radius).floor() as i32).max(0) as u32;
let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
let width = maxx - minx + 1;
let height = maxy - miny + 1;
let mut map = MapData::new(width as usize, height as usize);
for y in miny..=maxy {
for x in minx..=maxx {
map.set_transparent(
(x - minx) as usize,
(y - miny) as usize,
level_map.0.is_transparent(x as usize, y as usize),
);
}
}
fov.compute_fov(
&mut map,
px as usize - minx as usize,
py as usize - miny as usize,
radius as usize,
true,
);
let light_color = color_scale(light.color, intensity);
let radius_squared = radius * radius;
let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
for y in miny..=maxy {
for x in minx..=maxx {
if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
let dx = x as f32 - px;
let dy = y as f32 - py;
// good looking lights.
let squared_dist = dx * dx + dy * dy;
let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
let intensity_coef = intensity_coef - radius_coef;
let intensity_coef = intensity_coef / (1.0 - radius_coef);
if intensity_coef > 0.0 {
let light = color_blend(BLACK, light_color, intensity_coef);
let cur_light = light_map.pixel(x, y).unwrap();
light_map.put_pixel(x, y, color_add(light, cur_light));
}
}
}
}
}
}
sourcepub fn try_load(&mut self) -> bool
pub fn try_load(&mut self) -> bool
Check if the image has been loaded.
Since there’s no background thread doing the work for you, you have to call some method on image for it to actually load.
Use either Image::try_load
, [Image::get_size
], Image::blit
or Image::blit_ex
to run the loading code.
Examples found in repository?
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
fn load_level(
mut level_image: NonSendMut<LevelImage>,
mut ground_image: NonSendMut<GroundImage>,
mut next_game_state: ResMut<NextState<GameState>>,
mut visited_2x: ResMut<Visited2x>,
mut walls: ResMut<Walls>,
mut commands: Commands,
mut size: ResMut<LevelSize>,
) {
let level_image = &mut level_image.0;
let level_image_try_load = level_image.try_load();
if level_image_try_load {
let image_size = level_image.try_get_size().unwrap();
size.width = image_size.0 as usize / 2;
size.height = image_size.1 as usize / 2;
walls.data = vec![false; size.width * size.height];
walls.level_width = size.width;
let mut map = MapData::new(image_size.0 as usize, image_size.1 as usize);
visited_2x.0.reserve((image_size.0 * image_size.1) as usize);
for y in 0..image_size.1 {
for x in 0..image_size.0 {
let p = level_image.pixel(x, y).unwrap();
map.set_transparent(x as usize, y as usize, p != WALL_COLOR);
visited_2x.0.push(false);
let pos_1x = (x as f32 / 2., y as f32 / 2.);
match p {
START_COLOR => commands.insert_resource(Start((x as f32, y as f32).into())),
LIGHT_COLOR => {
commands.spawn(LightBundle {
position: Position {
x: x as f32,
y: y as f32,
},
..LightBundle::new(LIGHT_RADIUS, LIGHT_COLOR, true)
});
}
GOBLIN_COLOR => {
let offset =
(pos_1x.0 as i32 + pos_1x.1 as i32 * size.width as i32) as usize;
walls.data[offset] = true;
commands.spawn(GoblinBundle {
position: Position {
x: x as f32,
y: y as f32,
},
..Default::default()
});
}
_ => (),
}
}
}
for y in 0..size.height {
for x in 0..size.width {
let mut count = 0;
let x2 = x * 2;
let y2 = y * 2;
if map.is_transparent(x2, y2) {
count += 1;
}
if map.is_transparent(x2 + 1, y2) {
count += 1;
}
if map.is_transparent(x2, y2 + 1) {
count += 1;
}
if map.is_transparent(x2 + 1, y2 + 1) {
count += 1;
}
if count < 2 {
let offset = x + y * size.width;
walls.data[offset] = true;
}
}
}
commands.insert_resource(LevelMap(map));
}
if level_image_try_load && ground_image.0.try_load() {
next_game_state.set(GameState::Running);
}
}
sourcepub fn try_get_size(&mut self) -> Option<(u32, u32)>
pub fn try_get_size(&mut self) -> Option<(u32, u32)>
If the image has already been loaded, return its size, else return None
Examples found in repository?
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
fn load_level(
mut level_image: NonSendMut<LevelImage>,
mut ground_image: NonSendMut<GroundImage>,
mut next_game_state: ResMut<NextState<GameState>>,
mut visited_2x: ResMut<Visited2x>,
mut walls: ResMut<Walls>,
mut commands: Commands,
mut size: ResMut<LevelSize>,
) {
let level_image = &mut level_image.0;
let level_image_try_load = level_image.try_load();
if level_image_try_load {
let image_size = level_image.try_get_size().unwrap();
size.width = image_size.0 as usize / 2;
size.height = image_size.1 as usize / 2;
walls.data = vec![false; size.width * size.height];
walls.level_width = size.width;
let mut map = MapData::new(image_size.0 as usize, image_size.1 as usize);
visited_2x.0.reserve((image_size.0 * image_size.1) as usize);
for y in 0..image_size.1 {
for x in 0..image_size.0 {
let p = level_image.pixel(x, y).unwrap();
map.set_transparent(x as usize, y as usize, p != WALL_COLOR);
visited_2x.0.push(false);
let pos_1x = (x as f32 / 2., y as f32 / 2.);
match p {
START_COLOR => commands.insert_resource(Start((x as f32, y as f32).into())),
LIGHT_COLOR => {
commands.spawn(LightBundle {
position: Position {
x: x as f32,
y: y as f32,
},
..LightBundle::new(LIGHT_RADIUS, LIGHT_COLOR, true)
});
}
GOBLIN_COLOR => {
let offset =
(pos_1x.0 as i32 + pos_1x.1 as i32 * size.width as i32) as usize;
walls.data[offset] = true;
commands.spawn(GoblinBundle {
position: Position {
x: x as f32,
y: y as f32,
},
..Default::default()
});
}
_ => (),
}
}
}
for y in 0..size.height {
for x in 0..size.width {
let mut count = 0;
let x2 = x * 2;
let y2 = y * 2;
if map.is_transparent(x2, y2) {
count += 1;
}
if map.is_transparent(x2 + 1, y2) {
count += 1;
}
if map.is_transparent(x2, y2 + 1) {
count += 1;
}
if map.is_transparent(x2 + 1, y2 + 1) {
count += 1;
}
if count < 2 {
let offset = x + y * size.width;
walls.data[offset] = true;
}
}
}
commands.insert_resource(LevelMap(map));
}
if level_image_try_load && ground_image.0.try_load() {
next_game_state.set(GameState::Running);
}
}
sourcepub fn blit(
&mut self,
con: &mut Console,
x: i32,
y: i32,
transparent: Option<(u8, u8, u8, u8)>
)
pub fn blit( &mut self, con: &mut Console, x: i32, y: i32, transparent: Option<(u8, u8, u8, u8)> )
blit an image on a console
x,y are the coordinate of the top left image pixel in the console
image pixels using the transparent color will be ignored
sourcepub fn blit_ex(
&mut self,
con: &mut Console,
x: f32,
y: f32,
scalex: f32,
scaley: f32,
angle: f32,
transparent: Option<(u8, u8, u8, u8)>
)
pub fn blit_ex( &mut self, con: &mut Console, x: f32, y: f32, scalex: f32, scaley: f32, angle: f32, transparent: Option<(u8, u8, u8, u8)> )
blit an image on a console
x,y are the coordinate of the image center in the console image can be scaled and rotated (angle is in radians) image pixels using the transparent color will be ignored
Examples found in repository?
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
fn render(mut root_console: ResMut<RootConsole>, mut skull: NonSendMut<SkullImage>) {
let root_console = &mut **root_console;
let skull = &mut *skull;
let scale = skull.scale_time.cos();
root_console.clear(None, Some((0, 0, 0, 255)), None);
skull.skull.blit_ex(
root_console,
(root_console.get_width() / 2) as f32,
(root_console.get_height() / 2) as f32,
scale,
scale,
skull.angle,
None,
);
}
sourcepub fn blit_2x(
&mut self,
con: &mut Console,
dx: i32,
dy: i32,
sx: i32,
sy: i32,
w: Option<i32>,
h: Option<i32>,
transparent: Option<(u8, u8, u8, u8)>
)
pub fn blit_2x( &mut self, con: &mut Console, dx: i32, dy: i32, sx: i32, sy: i32, w: Option<i32>, h: Option<i32>, transparent: Option<(u8, u8, u8, u8)> )
blit an image on the console, using the subcell characters to achieve twice the normal resolution. This uses the CHAR_SUBCELL_* ascii codes (from 226 to 232):
Comparison before/after subcell in the chronicles of Doryen :
Pyromancer! screenshot, making full usage of subcell resolution:
Examples found in repository?
41 42 43 44 45 46 47 48 49 50 51 52 53 54
fn render(mut root_console: ResMut<RootConsole>, mut skull: NonSendMut<SkullImage>) {
root_console.clear(None, Some((0, 0, 0, 255)), None);
skull
.skull
.blit_2x(&mut root_console, 23, 0, 0, 0, None, None, None);
root_console.print(
40,
4,
"Those pixels\nare twice smaller\nthan a console cell.\nMagic!",
TextAlign::Center,
Some((0, 0, 0, 255)),
None,
);
}
More examples
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
fn render_level(
mut root_console: ResMut<RootConsole>,
ground: NonSend<GroundImage>,
size: Res<LevelSize>,
map: Res<LevelMap>,
mut visited_2x: ResMut<Visited2x>,
light_map: NonSend<LightMap>,
) {
let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
let light_map = &light_map.0;
for y in 0..size.height * 2 {
for x in 0..size.width * 2 {
let off = x + y * size.width * 2;
if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
visited_2x.0[off] = true;
let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
let light_col = light_map.pixel(x as u32, y as u32).unwrap();
let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
r = r.min(255.0);
g = g.min(255.0);
b = b.min(255.0);
render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
} else if visited_2x.0[off] {
let col = ground.0.pixel(x as u32, y as u32).unwrap();
let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
render_output.put_pixel(x as u32, y as u32, dark_col);
} else {
render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
}
}
}
render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
Auto Trait Implementations§
impl RefUnwindSafe for Image
impl Send for Image
impl Sync for Image
impl Unpin for Image
impl UnwindSafe for Image
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.