#![macro_use]
macro_rules! impl_geometry_body {
($inner:ident) => {
fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
self.$inner().draw(viewer, program, render_states)
}
fn vertex_shader_source(&self) -> String {
self.$inner().vertex_shader_source()
}
fn id(&self) -> GeometryId {
self.$inner().id()
}
fn render_with_material(
&self,
material: &dyn Material,
viewer: &dyn Viewer,
lights: &[&dyn Light],
) {
self.$inner().render_with_material(material, viewer, lights)
}
fn render_with_effect(
&self,
material: &dyn Effect,
viewer: &dyn Viewer,
lights: &[&dyn Light],
color_texture: Option<ColorTexture>,
depth_texture: Option<DepthTexture>,
) {
self.$inner()
.render_with_effect(material, viewer, lights, color_texture, depth_texture)
}
fn aabb(&self) -> AxisAlignedBoundingBox {
self.$inner().aabb()
}
};
}
mod mesh;
#[doc(inline)]
pub use mesh::*;
mod instanced_mesh;
#[doc(inline)]
pub use instanced_mesh::*;
mod sprites;
#[doc(inline)]
pub use sprites::*;
mod particles;
#[doc(inline)]
pub use particles::*;
mod bounding_box;
#[doc(inline)]
pub use bounding_box::*;
mod line;
#[doc(inline)]
pub use line::*;
mod rectangle;
#[doc(inline)]
pub use rectangle::*;
mod circle;
#[doc(inline)]
pub use circle::*;
use crate::core::*;
use crate::renderer::*;
pub use three_d_asset::{
Geometry as CpuGeometry, Indices, KeyFrameAnimation, KeyFrames, PointCloud, Positions,
TriMesh as CpuMesh,
};
pub trait Geometry {
fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates);
fn vertex_shader_source(&self) -> String;
fn id(&self) -> GeometryId;
fn render_with_material(
&self,
material: &dyn Material,
viewer: &dyn Viewer,
lights: &[&dyn Light],
);
fn render_with_effect(
&self,
material: &dyn Effect,
viewer: &dyn Viewer,
lights: &[&dyn Light],
color_texture: Option<ColorTexture>,
depth_texture: Option<DepthTexture>,
);
fn aabb(&self) -> AxisAlignedBoundingBox;
fn animate(&mut self, _time: f32) {}
}
use std::ops::Deref;
impl<T: Geometry + ?Sized> Geometry for &T {
impl_geometry_body!(deref);
}
impl<T: Geometry + ?Sized> Geometry for &mut T {
impl_geometry_body!(deref);
fn animate(&mut self, time: f32) {
self.deref().animate(time)
}
}
impl<T: Geometry> Geometry for Box<T> {
impl_geometry_body!(as_ref);
}
impl<T: Geometry> Geometry for std::rc::Rc<T> {
impl_geometry_body!(as_ref);
}
impl<T: Geometry> Geometry for std::sync::Arc<T> {
impl_geometry_body!(as_ref);
}
impl<T: Geometry> Geometry for std::cell::RefCell<T> {
impl_geometry_body!(borrow);
fn animate(&mut self, time: f32) {
self.borrow_mut().animate(time)
}
}
impl<T: Geometry> Geometry for std::sync::RwLock<T> {
fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
self.read().unwrap().draw(viewer, program, render_states)
}
fn vertex_shader_source(&self) -> String {
self.read().unwrap().vertex_shader_source()
}
fn id(&self) -> GeometryId {
self.read().unwrap().id()
}
fn render_with_material(
&self,
material: &dyn Material,
viewer: &dyn Viewer,
lights: &[&dyn Light],
) {
self.read()
.unwrap()
.render_with_material(material, viewer, lights)
}
fn render_with_effect(
&self,
material: &dyn Effect,
viewer: &dyn Viewer,
lights: &[&dyn Light],
color_texture: Option<ColorTexture>,
depth_texture: Option<DepthTexture>,
) {
self.read().unwrap().render_with_effect(
material,
viewer,
lights,
color_texture,
depth_texture,
)
}
fn aabb(&self) -> AxisAlignedBoundingBox {
self.read().unwrap().aabb()
}
fn animate(&mut self, time: f32) {
self.write().unwrap().animate(time)
}
}
pub enum TriangleBuffer {
Unindexed {
number_of_vertices: u32,
},
IndexedU8(ElementBuffer<u8>),
IndexedU16(ElementBuffer<u16>),
IndexedU32(ElementBuffer<u32>),
}
impl TriangleBuffer {
pub fn new(context: &Context, cpu_mesh: &CpuMesh) -> Self {
match &cpu_mesh.indices {
Indices::U8(ind) => Self::IndexedU8(ElementBuffer::new_with_data(context, ind)),
Indices::U16(ind) => Self::IndexedU16(ElementBuffer::new_with_data(context, ind)),
Indices::U32(ind) => Self::IndexedU32(ElementBuffer::new_with_data(context, ind)),
Indices::None => Self::Unindexed {
number_of_vertices: cpu_mesh.vertex_count() as u32,
},
}
}
pub fn draw(&self, program: &Program, render_states: RenderStates, viewer: &dyn Viewer) {
match self {
Self::Unindexed { number_of_vertices } => {
program.draw_arrays(render_states, viewer.viewport(), *number_of_vertices)
}
Self::IndexedU8(element_buffer) => {
program.draw_elements(render_states, viewer.viewport(), element_buffer)
}
Self::IndexedU16(element_buffer) => {
program.draw_elements(render_states, viewer.viewport(), element_buffer)
}
Self::IndexedU32(element_buffer) => {
program.draw_elements(render_states, viewer.viewport(), element_buffer)
}
}
}
pub fn draw_instanced(
&self,
program: &Program,
render_states: RenderStates,
viewer: &dyn Viewer,
instance_count: u32,
) {
match self {
Self::Unindexed { number_of_vertices } => program.draw_arrays_instanced(
render_states,
viewer.viewport(),
*number_of_vertices,
instance_count,
),
Self::IndexedU8(element_buffer) => program.draw_elements_instanced(
render_states,
viewer.viewport(),
element_buffer,
instance_count,
),
Self::IndexedU16(element_buffer) => program.draw_elements_instanced(
render_states,
viewer.viewport(),
element_buffer,
instance_count,
),
Self::IndexedU32(element_buffer) => program.draw_elements_instanced(
render_states,
viewer.viewport(),
element_buffer,
instance_count,
),
}
}
pub fn vertex_count(&self) -> u32 {
match self {
Self::Unindexed { number_of_vertices } => *number_of_vertices,
Self::IndexedU8(element_buffer) => element_buffer.count(),
Self::IndexedU16(element_buffer) => element_buffer.count(),
Self::IndexedU32(element_buffer) => element_buffer.count(),
}
}
pub fn triangle_count(&self) -> u32 {
self.vertex_count() / 3
}
}
struct BaseMesh {
indices: TriangleBuffer,
positions: VertexBuffer<Vec3>,
normals: Option<VertexBuffer<Vec3>>,
tangents: Option<VertexBuffer<Vec4>>,
uvs: Option<VertexBuffer<Vec2>>,
colors: Option<VertexBuffer<Vec4>>,
}
impl BaseMesh {
pub fn new(context: &Context, cpu_mesh: &CpuMesh) -> Self {
#[cfg(debug_assertions)]
cpu_mesh.validate().expect("invalid cpu mesh");
Self {
indices: TriangleBuffer::new(context, cpu_mesh),
positions: VertexBuffer::new_with_data(context, &cpu_mesh.positions.to_f32()),
normals: cpu_mesh
.normals
.as_ref()
.map(|data| VertexBuffer::new_with_data(context, data)),
tangents: cpu_mesh
.tangents
.as_ref()
.map(|data| VertexBuffer::new_with_data(context, data)),
uvs: cpu_mesh.uvs.as_ref().map(|data| {
VertexBuffer::new_with_data(
context,
&data
.iter()
.map(|uv| vec2(uv.x, 1.0 - uv.y))
.collect::<Vec<_>>(),
)
}),
colors: cpu_mesh.colors.as_ref().map(|data| {
VertexBuffer::new_with_data(
context,
&data.iter().map(|c| c.to_linear_srgb()).collect::<Vec<_>>(),
)
}),
}
}
pub fn draw(&self, program: &Program, render_states: RenderStates, viewer: &dyn Viewer) {
self.use_attributes(program);
self.indices.draw(program, render_states, viewer);
}
pub fn draw_instanced(
&self,
program: &Program,
render_states: RenderStates,
viewer: &dyn Viewer,
instance_count: u32,
) {
self.use_attributes(program);
self.indices
.draw_instanced(program, render_states, viewer, instance_count);
}
fn use_attributes(&self, program: &Program) {
program.use_vertex_attribute("position", &self.positions);
if program.requires_attribute("normal") {
if let Some(normals) = &self.normals {
program.use_vertex_attribute("normal", normals);
}
}
if program.requires_attribute("tangent") {
if let Some(tangents) = &self.tangents {
program.use_vertex_attribute("tangent", tangents);
}
}
if program.requires_attribute("uv_coordinates") {
if let Some(uvs) = &self.uvs {
program.use_vertex_attribute("uv_coordinates", uvs);
}
}
if program.requires_attribute("color") {
if let Some(colors) = &self.colors {
program.use_vertex_attribute("color", colors);
}
}
}
fn vertex_shader_source(&self) -> String {
format!(
"{}{}{}{}{}{}",
if self.normals.is_some() {
"#define USE_NORMALS\n"
} else {
""
},
if self.tangents.is_some() {
"#define USE_TANGENTS\n"
} else {
""
},
if self.uvs.is_some() {
"#define USE_UVS\n"
} else {
""
},
if self.colors.is_some() {
"#define USE_VERTEX_COLORS\n"
} else {
""
},
include_str!("../core/shared.frag"),
include_str!("geometry/shaders/mesh.vert"),
)
}
}