mod cavern;
mod dress;
mod geom;
mod layout;
pub mod manifest;
pub mod model;
use glam::Vec3;
use crate::gltf::{Asset, Collider, Material, Part, Physics};
use crate::mesh::Mesh;
use crate::palette::Palette;
use crate::recipe::DungeonParams;
use model::{Corridor, Door, DungeonModel, Room};
const TORCH_GLOW: Vec3 = Vec3::new(1.0, 0.55, 0.18);
pub fn generate(p: &DungeonParams, pal: &Palette) -> Result<Asset, String> {
let model = DungeonModel::new(p, pal)?;
Ok(merged_asset(&model))
}
fn dress_meshes(model: &DungeonModel, room: &Room) -> (Mesh, Mesh) {
let (mut opaque, mut emissive) = (Mesh::new(), Mesh::new());
for pp in dress::dress_room(
room,
model.p.theme,
model.p.density,
model.p.seed,
&model.pal,
) {
match pp.emissive {
Some(_) => emissive.merge(&pp.mesh),
None => opaque.merge(&pp.mesh),
}
}
(opaque, emissive)
}
fn carved_room(model: &DungeonModel, room: &Room, include_ceiling: bool) -> Mesh {
let walls = geom::room_mesh(
room,
model.p.theme,
&model.pal,
model.p.detail,
include_ceiling,
);
let doors: Vec<Door> = model
.doors
.iter()
.filter(|d| d.room == room.id)
.cloned()
.collect();
geom::carve_doorways(walls, &doors)
}
fn assemble(opaque: Mesh, emissive: Mesh) -> Asset {
let mut parts = vec![Part {
mesh: opaque,
material: Material {
roughness: 0.92,
..Default::default()
},
}];
if !emissive.positions.is_empty() {
parts.push(Part {
mesh: emissive,
material: Material {
roughness: 0.4,
emissive: TORCH_GLOW * 2.4,
..Default::default()
},
});
}
Asset::static_mesh(
"dungeon",
parts,
Some(Physics {
collider: Collider::TriMesh,
mass: 0.0,
friction: 0.9,
restitution: 0.0,
}),
)
}
fn build_merged(model: &DungeonModel, include_ceiling: bool) -> Asset {
let mut opaque = Mesh::new();
let mut emissive = Mesh::new();
let cavern = matches!(model.p.theme, crate::recipe::DungeonTheme::Cavern);
if cavern {
opaque.merge(&cavern::cavern_mesh(
&model.rooms,
&model.corridors,
&model.pal,
model.p.detail,
include_ceiling,
));
}
for room in &model.rooms {
if !cavern {
opaque.merge(&carved_room(model, room, include_ceiling));
}
let (o, e) = dress_meshes(model, room);
opaque.merge(&o);
emissive.merge(&e);
}
if !cavern {
for c in &model.corridors {
opaque.merge(&geom::corridor_mesh(
c,
model.p.theme,
&model.pal,
model.p.detail,
include_ceiling,
));
}
}
assemble(opaque, emissive)
}
pub fn merged_asset(model: &DungeonModel) -> Asset {
build_merged(model, true)
}
pub fn overview_asset(model: &DungeonModel) -> Asset {
build_merged(model, false)
}
pub(crate) fn room_asset(model: &DungeonModel, room: &Room) -> Asset {
let cavern = matches!(model.p.theme, crate::recipe::DungeonTheme::Cavern);
let owned: Vec<Corridor> = model
.corridors
.iter()
.filter(|c| c.a.min(c.b) == room.id)
.cloned()
.collect();
let mut opaque = if cavern {
cavern::cavern_mesh(
std::slice::from_ref(room),
&owned,
&model.pal,
model.p.detail,
true,
)
} else {
let mut m = carved_room(model, room, true);
for c in &owned {
m.merge(&geom::corridor_mesh(
c,
model.p.theme,
&model.pal,
model.p.detail,
true,
));
}
m
};
let (o, e) = dress_meshes(model, room);
opaque.merge(&o);
assemble(opaque, e)
}