use bevy::{
math::{cubic_splines::CubicCurve, vec3},
prelude::*,
render::{
mesh::{Indices, PrimitiveTopology},
render_asset::RenderAssetUsages,
texture::{ImageAddressMode, ImageLoaderSettings, ImageSampler, ImageSamplerDescriptor},
},
};
use std::f32::consts::PI;
use rand::{thread_rng, Rng};
#[derive(Resource)]
pub struct Track {
pub curve: CubicCurve<Vec3>,
radius: f32,
rings: usize,
ring_indices: Vec<u32>,
circle_segments: u32,
vertices: Vec<Vec3>,
uvs: Vec<[f32; 2]>,
vertex_indices: Vec<u32>,
}
impl Track {
pub fn new(curve: CubicCurve<Vec3>, radius: f32, circle_segments: u32, rings: usize) -> Track {
let mut track = Track {
curve: curve,
radius,
rings,
ring_indices: vec![],
circle_segments,
vertices: vec![],
uvs: vec![],
vertex_indices: vec![],
};
let mut forward = track.curve.velocity(0.0).normalize();
let mut up = Vec3::new(0.0, 1.0, 0.0);
if up.dot(forward) > 0.9 {
up = Vec3::new(1.0, 0.0, 0.0);
}
let mut right = up.cross(forward);
let sz = track.curve.segments().len();
for t in 0..track.rings {
track.add_ring(
sz as f32 * t as f32 / track.rings as f32,
&mut forward,
&mut right,
t as f32,
);
if t == 0 || t == track.rings - 1 {
let center_index = track.vertices.len() as u32;
track.uvs.push([0.5, t as f32]);
track.vertices.push(
track
.curve
.position(sz as f32 * t as f32 / track.rings as f32),
);
track.add_cap(center_index, track.ring_indices[t]);
}
if t > 0 {
track.add_sides(track.ring_indices[t - 1], track.ring_indices[t]);
}
}
track
}
fn add_cap(&mut self, center_index: u32, ring_index: u32) {
for i in 0..self.circle_segments - 1 {
self.vertex_indices.push(center_index);
if ring_index != 0 {
self.vertex_indices.push(ring_index + i);
self.vertex_indices.push(ring_index + i + 1);
} else {
self.vertex_indices.push(ring_index + i + 1);
self.vertex_indices.push(ring_index + i);
}
}
self.vertex_indices.push(center_index);
if ring_index != 0 {
self.vertex_indices
.push(ring_index + self.circle_segments - 1);
self.vertex_indices.push(ring_index);
} else {
self.vertex_indices.push(ring_index);
self.vertex_indices
.push(ring_index + self.circle_segments - 1);
}
}
fn add_sides(&mut self, first_ring: u32, next_ring: u32) {
for i in 0..self.circle_segments - 1 {
let a = first_ring + i;
let b = next_ring + i;
let c = first_ring + i + 1;
let d = next_ring + i + 1;
self.vertex_indices.extend([c, d, a, a, d, b].into_iter());
}
let a = first_ring + self.circle_segments - 1;
let b = next_ring + self.circle_segments - 1;
let c = first_ring;
let d = next_ring;
self.vertex_indices.extend([c, d, a, a, d, b].into_iter());
}
fn add_ring(&mut self, t: f32, forward: &mut Vec3, right: &mut Vec3, uv_y: f32) {
self.ring_indices.push(self.vertices.len() as u32);
let center = self.curve.position(t);
let up = -right.normalize().cross(*forward);
*forward = self.curve.velocity(t).normalize();
*right = up.cross(*forward);
for i in 0..self.circle_segments + 1 {
let angle = i as f32 * 2.0 * PI / self.circle_segments as f32;
let x = angle.cos() * self.radius;
let y = angle.sin() * self.radius;
self.vertices.push(center + *right * x + up * y);
self.uvs
.push([i as f32 / self.circle_segments as f32, uv_y]);
}
}
pub fn mesh(&self) -> Mesh {
let mut mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, self.vertices.clone())
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, self.uvs.clone())
.with_inserted_indices(Indices::U32(self.vertex_indices.clone()));
mesh.compute_normals();
mesh
}
}