use std::path::PathBuf;
use hashbrown::HashMap;
use ogmo3::{Layer, Level, Project};
use tetra::graphics::{self, Color, DrawParams, Rectangle, Texture};
use tetra::math::Vec2;
use tetra::{Context, ContextBuilder, State};
struct GameState {
color_texture: Texture,
tilesets: Vec<TilesetData>,
decals: Vec<Texture>,
sprites: Vec<Sprite>,
}
impl GameState {
fn new(ctx: &mut Context) -> anyhow::Result<GameState> {
let base_path = PathBuf::from("./examples/sample_project");
let project = Project::from_file(base_path.join("test.ogmo"))?;
let level = Level::from_file(base_path.join("levels/uno.json"))?;
let mut tilesets = Vec::new();
let mut tileset_mappings = HashMap::new();
for tileset in project.tilesets {
let texture = Texture::new(ctx, base_path.join(&tileset.path))?;
let tiles = tileset
.tile_coords(texture.width(), texture.height())
.map(|t| {
Rectangle::new(
t.x as f32,
t.y as f32,
tileset.tile_width as f32,
tileset.tile_height as f32,
)
})
.collect();
let id = tilesets.len();
tilesets.push(TilesetData { texture, tiles });
tileset_mappings.insert(tileset.label, id);
}
let mut sprites = Vec::new();
let mut decals = Vec::new();
for layer in level.layers {
match layer {
Layer::Tile(layer) => {
for tile in layer.unpack() {
if let Some(id) = tile.id {
sprites.push(Sprite::TileIndex {
tileset: tileset_mappings[&layer.tileset],
tile: id as usize,
position: Vec2::new(
tile.pixel_position.x as f32,
tile.pixel_position.y as f32,
),
});
}
}
}
Layer::TileCoords(layer) => {
for tile in layer.unpack() {
if let Some(coords) = tile.pixel_coords {
sprites.push(Sprite::TileUV {
tileset: tileset_mappings[&layer.tileset],
uv: Rectangle::new(
coords.x as f32,
coords.y as f32,
layer.grid_cell_width as f32,
layer.grid_cell_height as f32,
),
position: Vec2::new(
tile.pixel_position.x as f32,
tile.pixel_position.y as f32,
),
});
}
}
}
Layer::Grid(layer) => {
for cell in layer.unpack() {
if cell.value != "0" {
sprites.push(Sprite::Rect {
rect: Rectangle::new(
cell.pixel_position.x as f32,
cell.pixel_position.y as f32,
layer.grid_cell_width as f32,
layer.grid_cell_height as f32,
),
color: Color::BLACK,
});
}
}
}
Layer::Entity(layer) => {
for entity in &layer.entities {
sprites.push(Sprite::Rect {
color: Color::RED,
rect: Rectangle::new(entity.x, entity.y, 16.0, 16.0),
});
}
}
Layer::Decal(layer) => {
let folder_path = base_path.join(layer.folder);
for decal in layer.decals {
let texture = Texture::new(ctx, folder_path.join(decal.texture))?;
let id = decals.len();
decals.push(texture);
sprites.push(Sprite::Decal {
decal: id,
position: Vec2::new(decal.x, decal.y),
rotation: decal.rotation.unwrap_or(0.0),
scale: Vec2::new(
decal.scale_x.unwrap_or(1.0),
decal.scale_y.unwrap_or(1.0),
),
});
}
}
}
}
Ok(GameState {
color_texture: Texture::from_rgba(ctx, 1, 1, &[255, 255, 255, 255])?,
tilesets,
decals,
sprites,
})
}
}
impl State<anyhow::Error> for GameState {
fn draw(&mut self, ctx: &mut Context) -> anyhow::Result<()> {
graphics::clear(ctx, Color::WHITE);
for sprite in &self.sprites {
match sprite {
Sprite::TileIndex {
tileset,
tile,
position,
} => {
let tileset = &self.tilesets[*tileset];
let uv = tileset.tiles[*tile];
tileset.texture.draw_region(ctx, uv, *position);
}
Sprite::TileUV {
tileset,
uv,
position,
} => {
let tileset = &self.tilesets[*tileset];
tileset.texture.draw_region(ctx, *uv, *position);
}
Sprite::Rect { rect, color } => {
self.color_texture.draw(
ctx,
DrawParams::new()
.position(Vec2::new(rect.x, rect.y))
.scale(Vec2::new(rect.width, rect.height))
.color(*color),
);
}
Sprite::Decal {
decal,
position,
rotation,
scale,
} => {
let texture = &self.decals[*decal];
texture.draw(
ctx,
DrawParams::new()
.origin(Vec2::new(
texture.width() as f32 / 2.0,
texture.height() as f32 / 2.0,
))
.position(*position)
.rotation(*rotation)
.scale(*scale),
);
}
}
}
Ok(())
}
}
fn main() -> anyhow::Result<()> {
ContextBuilder::new("Rendering an Ogmo Project", 640, 480)
.build()?
.run(GameState::new)
}
enum Sprite {
TileIndex {
tileset: usize,
tile: usize,
position: Vec2<f32>,
},
TileUV {
tileset: usize,
uv: Rectangle,
position: Vec2<f32>,
},
Rect {
rect: Rectangle,
color: Color,
},
Decal {
decal: usize,
position: Vec2<f32>,
rotation: f32,
scale: Vec2<f32>,
},
}
struct TilesetData {
texture: Texture,
tiles: Vec<Rectangle>,
}