summaryrefslogtreecommitdiff
path: root/gfx/gl/vertexArrayObject.h
blob: 57daaf3056ba31cf6dd6c0132face2720a5ca4ac (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
#pragma once

#include "collections.h"
#include "gl_traits.h"
#include "special_members.h"
#include <glad/gl.h>

class VertexArrayObject {
public:
	template<typename T> [[nodiscard]] VertexArrayObject(const T & arrayObject)
	{
		glBindVertexArray(arrayObject);
	}

	~VertexArrayObject()
	{
		glBindVertexArray(0);
	}

	NO_MOVE(VertexArrayObject);
	NO_COPY(VertexArrayObject);

	template<typename m, typename T> struct MP {
		constexpr MP(m T::*p) : P {p} { }

		operator void *() const
		{
			return &(static_cast<T *>(nullptr)->*P);
		}

		m T::*P;
		using value_type = m;
	};

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

	template<typename VertexT, MP... attribs>
	VertexArrayObject &
	addAttribs(const GLuint arrayBuffer, const SequentialCollection<VertexT> auto & vertices, const GLuint divisor = 0)
	{
		addAttribs<VertexT, attribs...>(arrayBuffer, divisor);
		data(vertices, arrayBuffer, GL_ARRAY_BUFFER);
		return *this;
	}

	template<typename VertexT, MP... attribs>
	VertexArrayObject &
	addAttribs(const GLuint arrayBuffer, const GLuint divisor = 0)
	{
		configure_attribs<VertexT, attribs...>(arrayBuffer, divisor);
		return *this;
	}

	// Customisation point
	template<typename VertexT> VertexArrayObject & addAttribsFor(const GLuint arrayBuffer, const GLuint divisor = 0);

	template<typename Indices>
	VertexArrayObject &
	addIndices(const GLuint arrayBuffer, const Indices & indices)
	{
		data(indices, arrayBuffer, GL_ELEMENT_ARRAY_BUFFER);
		return *this;
	}

	VertexArrayObject &
	addIndices(const GLuint arrayBuffer)
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrayBuffer);
		return *this;
	}

	template<typename Data>
	static void
	data(const Data & data, const GLuint arrayBuffer, GLenum target)
	{
		using Value = typename Data::value_type;
		glBindBuffer(target, arrayBuffer);
		glBufferData(target, static_cast<GLsizeiptr>(sizeof(Value) * data.size()), data.data(), GL_STATIC_DRAW);
	}

private:
	template<typename VertexT, typename T>
	static auto
	set_pointer(const GLuint vertexArrayId, const void * ptr, const GLuint divisor)
	{
		using traits = gl_traits<T>;
		const auto usedAttribs
				= traits::vertexAttribFunc(vertexArrayId, traits::size, traits::type, sizeof(VertexT), ptr);
		for (GLuint i {}; i < usedAttribs; i++) {
			glEnableVertexAttribArray(vertexArrayId + i);
			glVertexAttribDivisor(vertexArrayId + i, divisor);
		}
		return usedAttribs;
	}

	template<typename VertexT, MP attrib>
	static auto
	set_pointer(const GLuint vertexArrayId, const GLuint divisor)
	{
		return set_pointer<VertexT, typename decltype(attrib)::value_type>(vertexArrayId, attrib, divisor);
	}

	template<typename VertexT, MP... attribs>
	void
	configure_attribs(const GLuint arrayBuffer, const GLuint divisor)
	{
		glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
		if constexpr (sizeof...(attribs) == 0) {
			vertexArrayId += set_pointer<VertexT, VertexT>(vertexArrayId, nullptr, divisor);
		}
		else {
			((vertexArrayId += set_pointer<VertexT, attribs>(vertexArrayId, divisor)), ...);
		}
	}

	GLuint vertexArrayId {};
};