use crate::parse::mesh::instructions::*;
use crate::parse::mesh::{MeshError, MeshErrorKind};
use crate::parse::Span;
use cgmath::{Array, Vector2, Vector3};
use std::f32::consts::PI;
#[must_use]
#[bve_derive::span(INFO, "Post Processing Instructions", count = instructions.instructions.len())]
pub fn post_process(mut instructions: InstructionList) -> InstructionList {
let mut output = Vec::new();
let meshes = instructions
.instructions
.split(|i| i.data == InstructionData::CreateMeshBuilder(CreateMeshBuilder));
for mesh in meshes {
let mesh = process_compound(mesh);
let mesh = merge_texture_coords(&mesh, &mut instructions.errors);
output.push(Instruction {
span: Span::none(),
data: InstructionData::CreateMeshBuilder(CreateMeshBuilder),
});
output.extend(mesh);
}
instructions.instructions = output;
tracing::debug!(count = instructions.instructions.len(), "Finished post-processing.");
instructions
}
fn create_vertex(original: &Instruction, position: Vector3<f32>) -> Instruction {
Instruction {
span: original.span,
data: InstructionData::AddVertex(AddVertex {
position,
normal: Vector3::from_value(0.0),
texture_coord: Vector2::from_value(0.0),
}),
}
}
fn create_face(original: &Instruction, indexes: Vec<usize>) -> Instruction {
Instruction {
span: original.span,
data: InstructionData::AddFace(AddFace {
indexes,
sides: Sides::One,
}),
}
}
#[allow(clippy::identity_op)]
fn process_compound(mesh: &[Instruction]) -> Vec<Instruction> {
let mut result = Vec::new();
let mut vertex_index = 0;
for instruction in mesh {
match &instruction.data {
InstructionData::AddVertex(..) => {
result.push(instruction.clone());
vertex_index += 1;
}
InstructionData::Cube(cube) => {
let x = cube.half_dim.x;
let y = cube.half_dim.y;
let z = cube.half_dim.z;
result.push(create_vertex(instruction, Vector3::new(x, y, -z)));
result.push(create_vertex(instruction, Vector3::new(x, -y, -z)));
result.push(create_vertex(instruction, Vector3::new(-x, -y, -z)));
result.push(create_vertex(instruction, Vector3::new(-x, y, -z)));
result.push(create_vertex(instruction, Vector3::new(x, y, z)));
result.push(create_vertex(instruction, Vector3::new(x, -y, z)));
result.push(create_vertex(instruction, Vector3::new(-x, -y, z)));
result.push(create_vertex(instruction, Vector3::new(-x, y, z)));
let vi = vertex_index;
result.push(create_face(instruction, vec![vi + 0, vi + 1, vi + 2, vi + 3]));
result.push(create_face(instruction, vec![vi + 0, vi + 4, vi + 5, vi + 1]));
result.push(create_face(instruction, vec![vi + 0, vi + 3, vi + 7, vi + 4]));
result.push(create_face(instruction, vec![vi + 6, vi + 5, vi + 4, vi + 7]));
result.push(create_face(instruction, vec![vi + 6, vi + 7, vi + 3, vi + 2]));
result.push(create_face(instruction, vec![vi + 6, vi + 2, vi + 1, vi + 5]));
tracing::trace!(starting_index = vi, "Processed Cube");
vertex_index += 8;
}
InstructionData::Cylinder(cylinder) => {
let n = cylinder.sides;
let n_f32 = n as f32;
let r1 = cylinder.upper_radius;
let r2 = cylinder.lower_radius;
let h = cylinder.height;
for i in (0..n).map(|i| i as f32) {
let trig_arg = (2.0 * PI * i) / n_f32;
let cos = trig_arg.cos();
let sin = trig_arg.sin();
result.push(create_vertex(instruction, Vector3::new(cos * r1, h / 2.0, sin * r1)));
result.push(create_vertex(instruction, Vector3::new(cos * r2, -h / 2.0, sin * r2)));
}
let vi = vertex_index;
let split = n.saturating_sub(1) as usize;
for i in 0..split {
result.push(create_face(
instruction,
vec![vi + (2 * i + 2), vi + (2 * i + 3), vi + (2 * i + 1), vi + (2 * i + 0)],
));
result.push(create_face(
instruction,
vec![vi + 0, vi + 1, vi + (2 * i + 1), vi + (2 * i + 0)],
));
}
tracing::trace!(starting_index = vi, "Processed Cylinder");
vertex_index += (2 * n) as usize;
}
_ => {
result.push(instruction.clone());
}
}
}
result
}
fn merge_texture_coords(mesh: &[Instruction], errors: &mut Vec<MeshError>) -> Vec<Instruction> {
let mut result = Vec::new();
let mut vertex_indices = Vec::new();
for instruction in mesh {
match &instruction.data {
InstructionData::AddVertex(..) => {
vertex_indices.push(result.len());
result.push(instruction.clone());
}
InstructionData::SetTextureCoordinates(data) => {
if data.index >= vertex_indices.len() {
tracing::warn!(location = ?instruction.span, idx = data.index, "SetTextureCoords out of bounds!");
errors.push(MeshError {
location: instruction.span,
kind: MeshErrorKind::OutOfBounds { idx: data.index },
});
continue;
}
match &mut result[vertex_indices[data.index]].data {
InstructionData::AddVertex(vert) => {
vert.texture_coord = data.coords;
}
_ => unreachable!(),
}
}
_ => {
result.push(instruction.clone());
}
}
}
result
}