1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#[macro_export]
macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
    }
}

#[macro_export]
macro_rules! impl_vertex {
    ($name: ident { $($field: ident => [$attribute: tt; $format: tt; $size: tt; $normalized: tt],)* }) => (
        #[repr(C)]
        #[derive(Debug, Copy, Clone)]
        pub struct $name {
            $($field: impl_vertex_field!{VertexFormat::$format, $size}, )*
        }

        impl $name {
            pub fn new($($field: impl_vertex_field!{VertexFormat::$format, $size}, ) *) -> Self {
                $name {
                    $($field: $field,)*
                }
            }

            pub fn layout() -> $crate::graphics::resource::VertexLayout {
                let mut builder = $crate::graphics::resource::CustomVertexLayoutBuilder::new();

                $( builder.with(
                    $crate::graphics::resource::VertexAttribute::$attribute,
                    $crate::graphics::resource::VertexFormat::$format,
                    $size,
                    $normalized,
                    offset_of!($name, $field) as u8); ) *

                builder.finish(::std::mem::size_of::<$name>() as u8)
            }

            pub fn as_bytes(values: &[Self]) -> &[u8] {
                let len = values.len() * ::std::mem::size_of::<Self>();
                unsafe { ::std::slice::from_raw_parts(values.as_ptr() as *const u8, len) }
            }
        }
    )
}

#[macro_export]
macro_rules! impl_vertex_field {
    (VertexFormat::Byte, 2) => ([i8; 2]);
    (VertexFormat::Byte, 3) => ([i8; 3]);
    (VertexFormat::Byte, 4) => ([i8; 4]);
    (VertexFormat::UByte, 2) => ([u8; 2]);
    (VertexFormat::UByte, 3) => ([u8; 3]);
    (VertexFormat::UByte, 4) => ([u8; 4]);
    (VertexFormat::Short, 2) => ([i16; 2]);
    (VertexFormat::Short, 3) => ([i16; 3]);
    (VertexFormat::Short, 4) => ([i16; 4]);
    (VertexFormat::UShort, 2) => ([u16; 2]);
    (VertexFormat::UShort, 3) => ([u16; 3]);
    (VertexFormat::UShort, 4) => ([u16; 4]);
    (VertexFormat::Float, 2) => ([f32; 2]);
    (VertexFormat::Float, 3) => ([f32; 3]);
    (VertexFormat::Float, 4) => ([f32; 4]);
}

#[cfg(test)]
mod test {
    use super::super::resource::*;

    impl_vertex! {
        Vertex {
            position => [Position; Float; 3; false],
            texcoord => [Texcoord0; Float; 2; false],
        }
    }

    impl_vertex! {
        Vertex2 {
            position => [Position; Float; 2; false],
            color => [Color0; UByte; 4; true],
            texcoord => [Texcoord0; Byte; 2; false],
        }
    }

    fn as_bytes<T>(values: &[T]) -> &[u8]
        where T: Copy
    {
        let len = values.len() * ::std::mem::size_of::<T>();
        unsafe { ::std::slice::from_raw_parts(values.as_ptr() as *const u8, len) }
    }

    #[test]
    fn basic() {
        let layout = Vertex::layout();
        assert_eq!(layout.stride(), 20);
        assert_eq!(layout.offset(VertexAttribute::Position), Some(0));
        assert_eq!(layout.offset(VertexAttribute::Texcoord0), Some(12));
        assert_eq!(layout.offset(VertexAttribute::Normal), None);

        let bytes: [f32; 5] = [1.0, 1.0, 1.0, 0.0, 0.0];
        let bytes = as_bytes(&bytes);
        assert_eq!(bytes,
                   Vertex::as_bytes(&[Vertex::new([1.0, 1.0, 1.0], [0.0, 0.0])]));

        let bytes: [f32; 10] = [1.0, 1.0, 1.0, 0.0, 0.0, 2.0, 2.0, 2.0, 3.0, 3.0];
        let bytes = as_bytes(&bytes);
        assert_eq!(bytes,
                   Vertex::as_bytes(&[Vertex::new([1.0, 1.0, 1.0], [0.0, 0.0]),
                                      Vertex::new([2.0, 2.0, 2.0], [3.0, 3.0])]));
    }

    #[test]
    fn representation() {
        let layout = Vertex::layout();
        assert_eq!(layout.stride() as usize, ::std::mem::size_of::<Vertex>());

        let layout = Vertex2::layout();
        let _v = Vertex2::new([1.0, 1.0], [0, 0, 0, 0], [0, 0]);
        let _b = Vertex2::as_bytes(&[]);
        assert_eq!(layout.stride() as usize, ::std::mem::size_of::<Vertex2>());
    }
}