-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgeometria.h
More file actions
249 lines (210 loc) · 8.63 KB
/
geometria.h
File metadata and controls
249 lines (210 loc) · 8.63 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Generador de geometría
#pragma once
#include <vector>
#include <array>
#include <cmath>
#include <stdint.h>
namespace tofu
{
using ui32 = std::uint32_t;
namespace detail
{
// Generador de puntos de fibonacci para la esfera
inline std::array<float, 3> fib(ui32 i, ui32 n) {
float k = i + .5f;
float phi = std::acos(1.f - 2.f * k / n);
float theta = M_PI * k * (1.f + std::sqrt(5.f));
return {std::cos(theta) * std::sin(phi),
std::sin(theta) * std::sin(phi),
std::cos(phi)};
}
// Slerp
inline std::array<float, 3> slerp(std::array<float, 3> v1, std::array<float, 3> v2, float t) {
float dot = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
float theta = std::acos(dot);
float sinTheta = std::sin(theta);
float s1 = std::sin((1.f - t) * theta) / sinTheta;
float s2 = std::sin(t * theta) / sinTheta;
return {s1 * v1[0] + s2 * v2[0],
s1 * v1[1] + s2 * v2[1],
s1 * v1[2] + s2 * v2[2]};
}
// Puntos de un plano
const std::vector<float> plane_vert = {
1.f, 1.f, 0.f,
-1.f, 1.f, 0.f,
-1.f,-1.f, 0.f,
1.f,-1.f, 0.f
};
const std::vector<ui32> plane_ind = {
0, 2, 1,
0, 3, 2
};
// Puntos de un cubo
const std::vector<float> cube_vert = {
1.f, 1.f, 1.f,
1.f, 1.f,-1.f,
-1.f, 1.f,-1.f,
-1.f, 1.f, 1.f,
1.f,-1.f, 1.f,
1.f,-1.f,-1.f,
-1.f,-1.f,-1.f,
-1.f,-1.f, 1.f
};
const std::vector<ui32> cube_ind = {
0, 2, 1, 0, 3, 2, // Arriba
4, 5, 6, 4, 6, 7, // Abajo
0, 1, 5, 0, 5, 4, // Frente
3, 6, 2, 3, 7, 6, // Atrás
0, 7, 3, 0, 4, 7, // Izquierda
1, 2, 6, 1, 6, 5 // Derecha
};
// Puntos de un octaedro
const std::vector<float> oct_vert = {
0.f, 1.f, 0.f, // Arriba
1.f, 0.f, 0.f,
0.f, 0.f, 1.f,
-1.f, 0.f, 0.f,
0.f, 0.f,-1.f,
0.f,-1.f, 0.f // Abajo
};
}
namespace geometria
{
// Plano
inline auto plano() {
return std::make_pair(detail::plane_vert, detail::plane_ind);
}
// Cubo
inline auto cubo() {
return std::make_pair(detail::cube_vert, detail::cube_ind);
}
// Circulo
// Lineas en vez de triágulos, no índices
inline auto circulo(ui32 n) {
std::vector<float> vertices;
for (ui32 i = 0; i <= n; i++) {
float theta = 2.f * M_PI * i / n;
vertices.push_back(std::cos(theta));
vertices.push_back(0.f);
vertices.push_back(std::sin(theta));
}
return vertices;
}
// Esfera a partir de octaedro
// Creamos las subdivisiones dividiendo cada arista n veces, no como se hace tradicionalmente, para tener más control sobre la división
inline auto esferaOct(ui32 n) {
using V = std::array<float, 3>;
using I = std::array<ui32, 3>;
auto& ov = detail::oct_vert;
std::vector<float> vertices;
std::vector<ui32> indices;
// Primer punto
V v0 = {ov[0], ov[1], ov[2]};
vertices.insert(vertices.end(), v0.begin(), v0.end());
//log::info("v0[{}] {} {} {}", vertices.size() / 3 - 1, v0[0], v0[1], v0[2]);
// Iteramos por cada subdivisión del eje
for (ui32 i = 1; i < 2*n; i++) {
// Ahora iteramos por cada punto intermedio (1-4)
for (ui32 j = 1; j <= 4; j++) {
// Eje 0 - j
V v1 = {ov[j * 3], ov[j * 3 + 1], ov[j * 3 + 2]};
// Eje 0 - (j-1)
ui32 k = (j == 1) ? 4 : j - 1;
V v2 = {ov[k * 3], ov[k * 3 + 1], ov[k * 3 + 2]};
// Punto intermedio de este eje
auto va = detail::slerp(v0, v1, (float)i / n);
// Punto intermedio del eje anterior
auto vb = detail::slerp(v0, v2, (float)i / n);
// Añadimos las subdivisiones entre los dos ejes
int ii = (i < n) ? i : 2*n - i;
for (ui32 l = 1; l < ii; l++) {
auto vc = detail::slerp(va, vb, (float)(ii-l) / ii);
vertices.insert(vertices.end(), vc.begin(), vc.end());
// log::info("vs[{}] {} {} {}", vertices.size() / 3 - 1, vc[0], vc[1], vc[2]);
}
// Y finalmente la subdivisión de este eje
vertices.insert(vertices.end(), va.begin(), va.end());
// log::info("v{}[{}] {} {} {}", j, vertices.size() / 3 - 1, va[0], va[1], va[2]);
}
}
// Ultimo punto
V vn = {ov[15], ov[16], ov[17]};
vertices.insert(vertices.end(), vn.begin(), vn.end());
// log::info("vn[{}] {} {} {}", vertices.size() / 3 - 1, vn[0], vn[1], vn[2]);
// Triangulación
// Recorremos horizontalmente los niveles
ui32 vert_acc = 1;
ui32 vert_anterior = 0;
I ind = {0, 0, 0}, ind_ccw = {0, 0, 0};
for (ui32 i = 1; i < n+1; i++) {
ui32 tri = (2 * (i-1) + 1) * 4;
ui32 vert = i * 4;
ui32 primer_vertice = vert_acc + i-1;
// Iniciamos los primeros índices
ind[0] = primer_vertice;
ind[1] = vert_anterior;
ind[2] = ind[0] + 1;
// Recorremos cada triángulo que hay que crear
for (ui32 j = 0; j < tri; j++) {
//log::info("{} - {} {} {}", j, ind[0], ind[1], ind[2]);
// Corregimos e insertamos los índices
ind_ccw = ind;
if (ind[0] > ind[1])
std::swap(ind_ccw[0], ind_ccw[1]);
indices.insert(indices.end(), ind_ccw.begin(), ind_ccw.end());
// Actualizamos los índices
if ((j+1) % (tri/4) == 0) { // Caso borde
ind[0] = ind[2];
} else {
ind[0] = ind[1];
ind[1] = ind[2];
}
ind[2] = ind[0] + 1;
// Repetición circular
if (ind[2] == vert_acc) ind[2] = vert_anterior - (i-2);
if (ind[2] == vert_acc + i*4) ind[2] = vert_acc;
}
vert_acc += vert;
vert_anterior = primer_vertice;
}
// Recorremos los niveles inferiores
vert_acc = 0;
for (ui32 i = 1; i < 2*n; i++)
vert_acc += ((i < n) ? i : 2*n - i) * 4;
vert_anterior = vert_acc + 1;
for (ui32 i = 1; i < n+1; i++) {
ui32 tri = (2 * (i-1) + 1) * 4;
ui32 vert = i * 4;
ui32 primer_vertice = vert_acc;
// Iniciamos los primeros índices
ind[0] = primer_vertice;
ind[1] = vert_anterior;
ind[2] = ind[0] - 1;
// Recorremos cada triángulo que hay que crear
for (ui32 j = 0; j < tri; j++) {
//log::info("{} - {} {} {}", j, ind[0], ind[1], ind[2]);
// Corregimos e insertamos los índices
ind_ccw = ind;
if (ind[0] < ind[1])
std::swap(ind_ccw[0], ind_ccw[1]);
indices.insert(indices.end(), ind_ccw.begin(), ind_ccw.end());
// Actualizamos los índices
if ((j+1) % (tri/4) == 0) { // Caso borde
ind[0] = ind[2];
} else {
ind[0] = ind[1];
ind[1] = ind[2];
}
ind[2] = ind[0] - 1;
// Repetición circular
if (ind[2] == vert_acc) ind[2] = vert_anterior;
if (ind[2] == vert_acc - i*4) ind[2] = vert_acc;
}
vert_acc -= vert;
vert_anterior = primer_vertice;
}
return std::make_pair(vertices, indices);
}
}
}