Trying to map a 2d array into a disk

I can finally say with confidence, 99% of the artifacts are removed or at least locally removed.

fun fact, to make the cubes locally squared means expanding the minor radius while the major radius stays the same. causing the donut to implode if the world depth is to large and we go down there.

2 Likes

here is the code, should be as simple as just making a rust project and add bevy then swap main.rs with this code. might be a bit heavy so so feel free to reduce (GRID_X, GRID_Z, GRID_Y and the time resource which is 24.0)

code
use std::f32::consts::PI;

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            bevy::pbr::wireframe::WireframePlugin::default(),
        ))
        .insert_resource(Time::<Fixed>::from_hz(24.0)) // has to update every now and then to change the transformation values, lower values are more efficient but makes it choppy
        .add_systems(Startup, setup)
        .add_systems(Update, (mouse_rotation, player_movement, follow_player).chain())
        .add_systems(FixedUpdate, update_boxes)
        .add_systems(Update, show_position)
        .run();
}

#[derive(Component)]
struct Position(Vec3);

#[derive(Component)]
struct Player;


#[derive(Component)]
struct PlanetCoords {
    x: f32,
    y: f32,
    z: f32,
}

#[derive(Component)]
struct PlanetRotation {
    yaw: f32,
    pitch: f32,
}

const GRID_X: usize = 100;
const GRID_Z: usize = 40;
const GRID_Y: usize = 1;

const TAU: f32 = std::f32::consts::TAU;

struct TorusTransform {
    translation: Vec3,
    rotation: Quat,
    scale: Vec3,
}


fn planet_coords_to_transform(position: Vec3, player_pos: Vec3) -> TorusTransform {
    //to avoid "as f32" spam
    const FGRID_X: f32 = GRID_X as f32;
    const FGRID_Y: f32 = GRID_Y as f32;
    const FGRID_Z: f32 = GRID_Z as f32;

    //planetary constants that can be moved out to into something like a planet::new()
    //they arent dependent on cube position
    let correction = FGRID_Z * (1.0 - FGRID_X / (FGRID_X * FGRID_X + FGRID_Z * FGRID_Z).sqrt());
    let r0 = FGRID_X / TAU;
    let r1 = (FGRID_Z - correction) / TAU;
    let du = 2.0 * PI / FGRID_X;
    let dv = TAU / FGRID_Z;

    let r = r1 / r0;
    let sqrt_factor = ((1.0 + r) / (1.0 - r)).sqrt();

    //dynamic part
    //to be fair, half of the math here is just. "uhm, this artifact is weird, where can i add in some numbers to make it go away?"
    let u = position.x / FGRID_X * TAU;
    let arg = position.z / FGRID_Z * PI;
    let layer = position.y - player_pos.y.clamp(0.0, FGRID_Y) + 1.0;

    let tan_half_b = sqrt_factor * f32::tan(arg);
    let b = 2.0 * f32::atan(tan_half_b);

    let cos_u = u.cos();
    let sin_u = u.sin();
    let cos_b = b.cos();
    let sin_b = b.sin();
    let rho = r0 + r1 * cos_b;

    let x = rho * cos_u;
    let y = rho * sin_u;
    let z = r1 * sin_b;

    let tangent_u = Vec3::new(-sin_u, cos_u, 0.0);
    let tangent_b = Vec3::new(-sin_b * cos_u, -sin_b * sin_u, cos_b);
    let normal = Vec3::new(cos_b * cos_u, cos_b * sin_u, sin_b);
    let rotation = Quat::from_mat3(&Mat3::from_cols(tangent_u, normal, -tangent_b));

    let arg_player = player_pos.z / FGRID_Z * PI;
    let tan_half_b_player = sqrt_factor * f32::tan(arg_player);
    let b_player = 2.0 * f32::atan(tan_half_b_player);

    let width_correction = (1.0 + r * b_player.cos()) * (FGRID_Z / FGRID_X).hypot(1.0);
    

    let exp_factor = (layer * dv * width_correction).exp();
    let offset = normal * (-r1 * (1.0 - exp_factor));

    let translation = Vec3::new(x, y, z) + offset;
        
    let scale = Vec3::new((r0 + r1 * cos_b * exp_factor) * du,exp_factor * width_correction * dv * r1, rho * du * exp_factor);
    
    TorusTransform { translation, rotation, scale }
}

fn update_boxes(
    mut query: Query<(&mut Transform, &Position), With<Mesh3d>>,
    player_query: Query<&PlanetCoords, With<Player>>
) {
    let player = player_query.single().unwrap();
    query.par_iter_mut().for_each(|(mut transform, position)| {

        let new_transform = planet_coords_to_transform(position.0, Vec3::new(player.x, player.y, player.z));

        transform.translation = new_transform.translation;
        transform.rotation = new_transform.rotation;
        
        transform.scale = new_transform.scale;
    });
}

const PLAYER_SPEED: f32 = 2.0;
const MOUSE_SENSITIVITY: f32 = 0.005;

fn mouse_rotation(
    mouse_motion: Res<bevy::input::mouse::AccumulatedMouseMotion>,
    mut player_query: Query<&mut PlanetRotation, With<Player>>,
) {
    let delta = mouse_motion.delta;
    if delta != Vec2::ZERO && let Ok(mut coords) = player_query.single_mut() {
        coords.yaw += -delta.x * MOUSE_SENSITIVITY;
        coords.pitch += -delta.y * MOUSE_SENSITIVITY;
        coords.pitch = coords.pitch.clamp(-1.4, 1.4);
    }
}

fn player_movement(
    mut player_query: Query<(&mut Transform, &mut PlanetCoords, &PlanetRotation), With<Player>>,
    input: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
) {
    let (mut transform, mut coords, rot) = player_query.single_mut().unwrap();
    let base_rot = planet_coords_to_transform(Vec3::new(coords.x, coords.y, coords.z), Vec3::new(coords.x, coords.y, coords.z));
    let basis = Mat3::from_quat(base_rot.rotation);
    let tangent_u = basis.col(0);
    let tangent_b = basis.col(1);

    let yaw = rot.yaw;
    let forward = -yaw.sin() * tangent_u + yaw.cos() * tangent_b;
    let right = yaw.cos() * tangent_u + yaw.sin() * tangent_b;

    let mut speed = PLAYER_SPEED;

    if input.pressed(KeyCode::ControlLeft) {
        speed *= 100.0;
    }

    let mut move_dir = Vec3::ZERO;
    if input.pressed(KeyCode::KeyW) {
        move_dir += speed * forward;
    }
    if input.pressed(KeyCode::KeyS) {
        move_dir -= speed * forward;
    }
    if input.pressed(KeyCode::KeyA) {
        move_dir -= speed * right;
    }
    if input.pressed(KeyCode::KeyD) {
        move_dir += speed * right;
    }
    if input.pressed(KeyCode::Space) {
        coords.y += speed * time.delta_secs();
    }
    if input.pressed(KeyCode::ShiftLeft) {
        coords.y -= speed * time.delta_secs();
    }

    if move_dir != Vec3::ZERO {
        move_dir = move_dir.normalize();

        let du = move_dir.dot(tangent_u) * speed * time.delta_secs();
        let dv = move_dir.dot(tangent_b) * speed * time.delta_secs();

        coords.x += du;
        coords.z += dv;
    }

    let base = planet_coords_to_transform(Vec3::new(coords.x, coords.y, coords.z), Vec3::new(coords.x, coords.y, coords.z));
    let final_rot = base_rot.rotation * Quat::from_axis_angle(Vec3::Y, rot.yaw);

    transform.translation = base.translation;
    transform.rotation = final_rot;
}

fn follow_player(
    player_query: Query<(&Transform, &PlanetRotation), (With<Player>, Without<Camera3d>)>,
    mut camera_query: Query<&mut Transform, (With<Camera3d>, Without<Player>)>,
) {
    let (player_transform, rot) = player_query.single().unwrap();
    let mut cam_transform = camera_query.single_mut().unwrap();

    let eye_pos = player_transform.translation;

    let pitch_rot = Quat::from_axis_angle(*player_transform.local_x(), rot.pitch);
    let final_rot = pitch_rot * player_transform.rotation;

    cam_transform.translation = eye_pos;
    cam_transform.rotation = final_rot;
}

fn show_position(
    camera_query: Query<&Transform, (Without<Mesh3d>, With<Camera3d>)>,
    player_query: Query<&PlanetCoords, With<Player>>,
    mut windows: Query<&mut Window, With<bevy::window::PrimaryWindow>>,
    time: Res<Time>,
) {
    let mut camera_position = Vec3::ZERO;
    let mut local_position = Vec3::ZERO;
    for camera_transform in camera_query.iter() {
        camera_position = camera_transform.translation;
    }

    for localtransform in player_query.iter() {
        local_position = Vec3::new(localtransform.x, localtransform.y, localtransform.z);
    }

    let fps = 1.0 / time.delta_secs();

    for mut window in windows.iter_mut() {
        window.title = format!("{}, {} | FPS: {:.0}", camera_position, local_position, fps);
    }
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let cube_mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0));

    for j in 0..GRID_Z {
    for k in 0..GRID_Y {
    for i in 0..GRID_X {

        //if i < 50 {continue;}// do this to see the inside
        //if j.rem_euclid(2) == 1 {continue;}

        let hue_theta = i as f32 / GRID_X as f32;
        let hue_phi = j as f32 / GRID_Z as f32;
        let depth_t = k as f32 / GRID_Y as f32;

        let cr = 0.2 + 0.7 * hue_theta;
        let cg = 0.1 + 0.5 * depth_t;
        let cb = 0.4 + 0.6 * hue_phi;

        commands.spawn((
            Mesh3d(cube_mesh.clone()),
            MeshMaterial3d(materials.add(StandardMaterial {
                base_color: Color::srgb(cr, cg, cb),
                ..default()
            })),
            Position(Vec3 {
                x: i as f32,
                y: k as f32,
                z: j as f32,
            }),
            Transform::from_xyz(0.0, 0.0, 0.0),
        ));
    }}}

    commands.spawn((
        PointLight {
            intensity: 5_000_000.0,
            shadows_enabled: true,
            ..default()
        },
        Transform::from_xyz(6.0, 8.0, 6.0),
    ));

    commands.spawn((
        PointLight {
            intensity: 2_000_000.0,
            ..default()
        },
        Transform::from_xyz(-6.0, -4.0, -6.0),
    ));

    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.0, 3.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    commands.spawn((
        Player,
        PlanetCoords {
            x: 0.0,
            y: GRID_Y as f32,
            z: 0.0,
        },
        PlanetRotation{
            yaw: 0.0,
            pitch: 0.0,
        },
        Mesh3d(meshes.add(Sphere::new(0.3))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(1.0, 0.8, 0.2),
            ..default()
        })),
        Transform {
            ..default()
        },
    ));
}
1 Like