summaryrefslogtreecommitdiff
path: root/gfx/gl/glVertexArray.h
blob: 3594cbf72138284082d86cf1b13dfcf16dcd367d (plain)
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
121
122
123
124
125
#pragma once

#include "collections.h"
#include "glArrays.h"
#include "glBuffer.h"
#include "gl_traits.h"

namespace Impl {
	class VertexArrayConfigurator {
	public:
		template<typename M, typename T> struct MP {
			constexpr MP(M T::* ptr) : ptr {ptr} { }

			operator GLuint() const
			{
				return static_cast<GLuint>(reinterpret_cast<const char *>(&(static_cast<T *>(nullptr)->*ptr))
						- static_cast<const char *>(nullptr));
			}

			M T::* ptr;
			using ValueType = M;
		};

		template<typename M, typename T> MP(M T::*) -> MP<M, T>;

		explicit VertexArrayConfigurator(GLuint name) : name {name} { }

		VertexArrayConfigurator &
		addIndices(const glBuffer & buffer)
		{
			glVertexArrayElementBuffer(name, buffer);
			return *this;
		}

		VertexArrayConfigurator &
		addIndices(glBuffer & buffer, const SequentialCollection<GLuint> auto & indices)
		{
			buffer.storage(indices, 0);
			return addIndices(buffer);
		}

		// Customisation point
		template<typename VertexT> VertexArrayConfigurator & addAttribsFor(GLuint divisor, const glBuffer & buffer);

		template<typename VertexT, MP... Attribs>
		VertexArrayConfigurator &
		addAttribs(const GLuint divisor)
		{
			configureAttribs<VertexT, Attribs...>(divisor);
			return *this;
		}

		template<typename VertexT, MP... Attribs>
		VertexArrayConfigurator &
		addAttribs(const GLuint divisor, const glBuffer & buffer)
		{
			glVertexArrayVertexBuffer(name, binding, buffer, 0, sizeof(VertexT));
			return addAttribs<VertexT, Attribs...>(divisor);
		}

		template<typename VertexT, MP... Attribs>
		VertexArrayConfigurator &
		addAttribs(const GLuint divisor, glBuffer & buffer, const SequentialCollection<VertexT> auto & data)
		{
			buffer.storage(data, 0);
			return addAttribs<VertexT, Attribs...>(divisor, buffer);
		}

	private:
		void
		setPointerMeta(const GLuint usedAttribs)
		{
			while (attrib < usedAttribs) {
				glEnableVertexArrayAttrib(name, attrib);
				glVertexArrayAttribBinding(name, attrib++, binding);
			}
		}

		template<typename T>
		void
		setPointer(const GLuint offset)
		{
			setPointerMeta(attrib + gl_traits<T>::vertexArrayAttribFormat(name, attrib, offset));
		}

		template<MP Attrib>
		void
		setPointer()
		{
			setPointer<typename decltype(Attrib)::ValueType>(Attrib);
		}

		template<typename VertexT, MP... Attribs>
		void
		configureAttribs(const GLuint divisor)
		{
			if constexpr (sizeof...(Attribs) == 0) {
				setPointer<VertexT>(0);
			}
			else {
				((setPointer<Attribs>()), ...);
			}
			glVertexArrayBindingDivisor(name, binding++, divisor);
		}

		GLuint name;
		GLuint binding = 0;
		GLuint attrib = 0;
	};

	// NOLINTNEXTLINE(readability-identifier-naming)
	struct glVertexArray : Detail::glNamed {
		VertexArrayConfigurator
		configure()
		{
			return VertexArrayConfigurator {name};
		}
	};
}

// NOLINTBEGIN(readability-identifier-naming)
template<size_t N>
using glVertexArrays = glManagedArray<Impl::glVertexArray, N, &glCreateVertexArrays, &glDeleteVertexArrays>;
using glVertexArray = glManagedSingle<Impl::glVertexArray, &glCreateVertexArrays, &glDeleteVertexArrays>;
// NOLINTEND(readability-identifier-naming)