~luyu/comp5411-rendering-project

7d064a84ce28c7b4b49ae55df613581906b8962d — Luyu Cheng a month ago 79fd0c4
feat: use world map generated from WFC
5 files changed, 97 insertions(+), 24 deletions(-)

M src/lib.rs
M src/render/mod.rs
M src/wfc/mod.rs
M src/wfc/tool.rs
A src/world.rs
M src/lib.rs => src/lib.rs +5 -1
@@ 1,10 1,14 @@
use std::rc::Rc;

use wasm_bindgen::prelude::*;

mod render;
mod wfc;
mod world;

#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
    render::render()?;
    let world = Rc::new(wfc::generate()?);
    render::render(world)?;
    Ok(())
}

M src/render/mod.rs => src/render/mod.rs +6 -22
@@ 1,8 1,6 @@
use cgmath::Matrix4;
use cgmath::SquareMatrix;
use cgmath::Vector3;
use ndarray::Array2;
use rand::prelude::*;
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;


@@ 10,6 8,8 @@ use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::WebGl2RenderingContext;

use crate::world::WorldMap;

mod gl;
mod model;
mod texture;


@@ 25,22 25,7 @@ enum DragMode {
    Rotate,
}

struct WorldMap {
    pub tile: Array2<u32>,
}

impl WorldMap {
    pub fn new() -> WorldMap {
        let mut tile = Array2::<u32>::zeros((16, 16));
        let mut rng = rand::thread_rng();
        for x in tile.iter_mut() {
            *x = rng.gen_range(0..=16);
        }
        WorldMap { tile }
    }
}

pub fn render() -> Result<(), JsValue> {
pub fn render(world_map: Rc<WorldMap>) -> Result<(), JsValue> {
    let window = web_sys::window().ok_or("no global `window` exists")?;
    let document = window.document().ok_or("cannot access `document`")?;
    let canvas = document


@@ 81,7 66,6 @@ pub fn render() -> Result<(), JsValue> {
    let program = gl::link_program(&context, &vert_shader, &frag_shader)?;
    context.use_program(Some(&program));

    let world = WorldMap::new();
    let cube = model::Model::cube();

    if cfg!(debug_assertions) {


@@ 308,9 292,9 @@ pub fn render() -> Result<(), JsValue> {
                WebGl2RenderingContext::COLOR_BUFFER_BIT | WebGl2RenderingContext::DEPTH_BUFFER_BIT,
            );

            let half_width = world.tile.len_of(ndarray::Axis(0)) as f32 / 2.0;
            let half_height = world.tile.len_of(ndarray::Axis(1)) as f32 / 2.0;
            for ((i, j), num_cubes) in world.tile.indexed_iter() {
            let half_width = world_map.tile.len_of(ndarray::Axis(0)) as f32 / 2.0;
            let half_height = world_map.tile.len_of(ndarray::Axis(1)) as f32 / 2.0;
            for ((i, j), num_cubes) in world_map.tile.indexed_iter() {
                if *num_cubes == 0 {
                    continue;
                }

M src/wfc/mod.rs => src/wfc/mod.rs +67 -1
@@ 2,5 2,71 @@ mod coordinate;
mod tool;
mod wfc;

use ndarray::Array2;
pub use tool::WaveFunctionCollapse2D;
pub use wfc::WaveFunctionCollapse;
\ No newline at end of file
pub use wfc::WaveFunctionCollapse;

use crate::{wfc::coordinate::Coordinate, world::WorldMap};

pub fn generate() -> Result<WorldMap, String> {
    let tiles = vec![
        ("@@@\n@@@\n@@@", 5.0),
        ("...\n...\n...", 1.0),
        ("@..\n@-=\n@..", 1.0),
        ("@..\n...\n...", 1.0),
        ("...\n===\n...", 1.0),
        (".-.\n===\n.-.", 1.0),
        ("...\n=--\n...", 1.0),
        (".=.\n.==\n.=.", 1.0),
        (".=.\n.==\n...", 1.0),
    ];
    let mut tool = tool::WaveFunctionCollapse2D::new((3, 3));
    for (s, w) in tiles {
        tool.add_tile(s, w);
        tool.transform_last(tool::rotate90);
        tool.transform_last(tool::rotate90);
        tool.transform_last(tool::rotate90);
    }

    // Just FYI. They are useful now.
    // tool.add_color('@', [55, 45, 45]);
    // tool.add_color('.', [30, 100, 20]);
    // tool.add_color('-', [180, 170, 170]);
    // tool.add_color('=', [125, 145, 25]);

    let (weights, rules, _) = tool.generate_input();
    let mut wfc = wfc::WaveFunctionCollapse::<2>::new(weights, rules);

    // First round with size -3..3 and -3..3.
    wfc.expand(&[-3, -3].into(), &[3, 3].into());
    while !wfc.step() {}

    // Second round with size -6..6 and -6..6.
    wfc.expand(&[-6, -6].into(), &[6, 6].into());
    while !wfc.step() {}

    // Make a world map!
    let mut tiles = Array2::zeros((12 * 3, 12 * 3));
    for row in -6..6 {
        for col in -6..6 {
            if let Some(pattern) = wfc.wave.get(&Coordinate([col, row])) {
                let tile = &tool.tiles[*pattern];
                for tile_row in 0..3 {
                    for tile_col in 0..3 {
                        let height = match tile[tile_row][tile_col] {
                            '@' => 5,
                            '.' => 1,
                            '-' => 2,
                            '=' => 3,
                            _ => 0,
                        };
                        let x = (col + 6) * 3 + tile_col as isize;
                        let y = (row + 6) * 3 + tile_row as isize;
                        tiles[[x as usize, y as usize]] = height;
                    }
                }
            }
        }
    }
    Ok(WorldMap { tile: tiles })
}

M src/wfc/tool.rs => src/wfc/tool.rs +1 -0
@@ 56,6 56,7 @@ impl WaveFunctionCollapse2D {
        }
    }

    #[allow(dead_code)]
    pub fn add_color(&mut self, symbol: char, color: [u8; 3]) {
        self.colors.insert(symbol, color);
    }

A src/world.rs => src/world.rs +18 -0
@@ 0,0 1,18 @@
use ndarray::Array2;
use rand::prelude::*;

pub struct WorldMap {
    pub tile: Array2<u32>,
}

impl WorldMap {
    #[allow(dead_code)]
    pub fn new() -> WorldMap {
        let mut tile = Array2::<u32>::zeros((16, 16));
        let mut rng = rand::thread_rng();
        for x in tile.iter_mut() {
            *x = rng.gen_range(0..=16);
        }
        WorldMap { tile }
    }
}