Skip to content
This repository has been archived by the owner on Jun 6, 2018. It is now read-only.

Commit

Permalink
feature(polygon::MeshBuilder): add mesh builder system
Browse files Browse the repository at this point in the history
Remove Mesh::from_raw_data() and replace it with MeshBuilder which
provides better typesafety and performs validation on the data.
  • Loading branch information
randomPoison committed Dec 5, 2015
1 parent 137f3c3 commit 907bf23
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 42 deletions.
2 changes: 1 addition & 1 deletion lib/polygon_math/src/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl Point {
}
}

pub fn slice_as_f32_slice(raw: &[f32]) -> &[Point] {
pub fn slice_from_f32_slice(raw: &[f32]) -> &[Point] {
assert!(raw.len() % 4 == 0, "To convert a slice of f32 to a slice of Point it must have a length that is a multiple of 4");

unsafe {
Expand Down
211 changes: 180 additions & 31 deletions lib/polygon_rs/src/geometry/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,198 @@
use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug)]
pub struct Mesh {
pub raw_data: Vec<f32>,
pub indices: Vec<u32>,
pub position_attribute: VertexAttribute,
pub normal_attribute: Option<VertexAttribute>,
// pub uv_attribute: Option<VertexAttribute>,
// pub color_attribute: Option<VertexAttribute>,
vertex_data: Vec<f32>,
indices: Vec<MeshIndex>,

position: VertexAttribute,
normal: Option<VertexAttribute>,
texcoord: Vec<VertexAttribute>,
}

impl Mesh {
/// Create a new mesh from existing data passed as slices.
pub fn from_raw_data(positions_raw: &[f32], indices_raw: &[u32]) -> Mesh {
let mut raw_data: Vec<f32> = Vec::with_capacity(positions_raw.len());
raw_data.extend(positions_raw);

let mut indices: Vec<u32> = Vec::with_capacity(indices_raw.len());
indices.extend(indices_raw);

Mesh {
raw_data: raw_data,
indices: indices,
position_attribute: VertexAttribute {
stride: 4,
offset: 0,
},
normal_attribute: None,
}
pub fn vertex_data(&self) -> &[f32] {
&*self.vertex_data
}

pub fn indices(&self) -> &[MeshIndex] {
&*self.indices
}

/// Adds the normals data to the mesh's raw data and creates the associated `VertexAttribute`.
pub fn add_normals(&mut self, normals_raw: &[f32]) {
self.normal_attribute = Some(VertexAttribute {
stride: 3,
offset: self.raw_data.len(),
});
self.raw_data.extend(normals_raw);
pub fn position(&self) -> VertexAttribute {
self.position
}

pub fn normal(&self) -> Option<VertexAttribute> {
self.normal
}

pub fn texcoord(&self) -> &[VertexAttribute] {
&*self.texcoord
}
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
pub position: Point,
pub normal: Option<Vector3>,

/// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
/// and so is not limited by polygon directly. If the number of
pub texcoord: Vec<Vector2>,
}

impl Vertex {
pub fn new(position: Point) -> Vertex {
Vertex {
position: position,
normal: None,
texcoord: Vec::new(),
}
}
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
pub stride: usize,
pub offset: usize,
pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
IndexOutOfBounds {
vertex_count: MeshIndex,
index: MeshIndex,
},

/// Indicates that one or more attributes had a count that did not match the total number of
/// vertices.
IncorrectAttributeCount,
}

#[derive(Debug, Clone)]
pub struct MeshBuilder {
position_data: Vec<Point>,
normal_data: Vec<Vector3>,
texcoord_data: Vec<Vec<Vector3>>,

indices: Vec<u32>,
}

// TODO: I'd like to support building the mesh by specifying all of each attribute at once, since
// that seems like a common use case for game development. We still want to support building it
// vert-by-vert because that works best in other situations (e.g. COLLADA files).
impl MeshBuilder {
pub fn new() -> MeshBuilder {
MeshBuilder {
position_data: Vec::new(),
normal_data: Vec::new(),
texcoord_data: Vec::new(),
indices: Vec::new(),
}
}

pub fn add_vertex(mut self, vertex: Vertex) -> MeshBuilder {
self.position_data.push(vertex.position);

if let Some(normal) = vertex.normal {
self.normal_data.push(normal);
}

// TODO: Handle texcoord data.

self
}

pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
self.indices.push(index);
self
}

pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
self.position_data.clear();
self.position_data.extend(position_data);
self
}

pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
self.normal_data.clear();
self.normal_data.extend(normal_data);
self
}

pub fn set_texcoord_data(mut self, _texcoord_data: &[Vector2], _texcoord_index: MeshIndex) -> MeshBuilder {
// TODO

self
}

pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
self.indices.clear();
self.indices.extend(indices);
self
}

pub fn build(self) -> Result<Mesh, BuildMeshError> {
// Validate the mesh data.

// The vertex count is defined by the position data, since position is the only required
// vertex attribute.
let vertex_count = self.position_data.len();

// TODO: Validate texcoord data.
if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
return Err(BuildMeshError::IncorrectAttributeCount);
}

// Make sure all indices at least point to a valid vertex.
for index in self.indices.iter().cloned() {
if index >= vertex_count as MeshIndex {
return Err(BuildMeshError::IndexOutOfBounds {
vertex_count: vertex_count as MeshIndex,
index: index,
});
}
}

// TODO: Make sure all normals are normalized?

// Create the mesh.
let mut vertex_data =
Vec::<f32>::with_capacity(
self.position_data.len() * 4
+ self.normal_data.len() * 3);
vertex_data.extend(Point::as_ref(&*self.position_data));
vertex_data.extend(Vector3::as_ref(&*self.normal_data));

// TODO: Add texcoord data.

Ok(Mesh {
vertex_data: vertex_data,
indices: self.indices,

position: VertexAttribute {
offset: 0,
stride: 0, // TODO: Should stride 4 (as in every 4 floats) or 0 (as in tightly packed)? Does that change between OpenGL and DirectX?
},

normal: if self.normal_data.len() > 0 {
Some(VertexAttribute {
offset: self.position_data.len() * 4,
stride: 0, // TODO: Same as above.
})
} else {
None
},

texcoord: Vec::new(),
})
}
}
2 changes: 1 addition & 1 deletion lib/polygon_rs/src/geometry/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod mesh;

pub use self::mesh::{Mesh, VertexAttribute};
pub use self::mesh::*;
10 changes: 5 additions & 5 deletions lib/polygon_rs/src/gl_render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ impl GLRender {

gl.buffer_data(
BufferTarget::ArrayBuffer,
&*mesh.raw_data,
mesh.vertex_data(),
BufferUsage::StaticDraw);

let index_buffer = gl.gen_buffer();
gl.bind_buffer(BufferTarget::ElementArrayBuffer, index_buffer);

gl.buffer_data(
BufferTarget::ElementArrayBuffer,
&*mesh.indices,
mesh.indices(),
BufferUsage::StaticDraw);

// Unbind buffers.
Expand All @@ -89,9 +89,9 @@ impl GLRender {
vertex_array: vertex_array,
vertex_buffer: vertex_buffer,
index_buffer: index_buffer,
position_attribute: mesh.position_attribute,
normal_attribute: mesh.normal_attribute,
element_count: mesh.indices.len(),
position_attribute: mesh.position(),
normal_attribute: mesh.normal(),
element_count: mesh.indices().len(),
}
}

Expand Down
9 changes: 5 additions & 4 deletions lib/polygon_rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
extern crate polygon_math as math;
extern crate bootstrap_rs as bootstrap;
extern crate bootstrap_gl as gl;
extern crate bootstrap_rs as bootstrap;
extern crate polygon_math as math;

pub mod camera;
pub mod geometry;
pub mod gl_render;
pub mod camera;
pub mod light;

pub use camera::Camera;
pub use light::{Light, PointLight};
pub use geometry::*;
pub use gl_render::{GLRender, ShaderProgram};
pub use light::{Light, PointLight};

0 comments on commit 907bf23

Please sign in to comment.