PathsRenderer.cpp 37.1 KB
Newer Older
1
2
3
/**
 * paths rendering widget
 * @author Tobias Weber <tweber@ill.fr>
Tobias WEBER's avatar
Tobias WEBER committed
4
 * @date feb-2021
5
 * @note initially forked from my tlibs2 library: https://code.ill.fr/scientific-software/takin/tlibs2/-/blob/master/libs/qt/glplot.cpp
6
7
8
9
10
 * @license GPLv3, see 'LICENSE' file
 *
 * References:
 *   - http://doc.qt.io/qt-5/qopenglwidget.html#details
 *   - http://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/threadedqopenglwidget
Tobias WEBER's avatar
Tobias WEBER committed
11
 *   - https://doc.qt.io/qt-5/qtgui-openglwindow-example.html
Tobias WEBER's avatar
Tobias WEBER committed
12
 *   - http://doc.qt.io/qt-5/qopengltexture.html
Tobias WEBER's avatar
Tobias WEBER committed
13
 *   - (Sellers 2014) G. Sellers et al., ISBN: 978-0-321-90294-8 (2014).
14
15
16
 *
 * ----------------------------------------------------------------------------
 * TAS-Paths (part of the Takin software suite)
Tobias WEBER's avatar
Tobias WEBER committed
17
 * Copyright (C) 2021  Tobias WEBER (Institut Laue-Langevin (ILL),
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 *                     Grenoble, France).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * ----------------------------------------------------------------------------
32
33
34
 */

#include "PathsRenderer.h"
35
36
#include "Resources.h"
#include "settings_variables.h"
37

38
39
#include <QtCore/QtGlobal>
#include <QtCore/QThread>
40
41
42
43
44
45
46
47
48
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QSurfaceFormat>
#include <QtGui/QPainter>
#include <QtGui/QGuiApplication>

#include <iostream>
#include <boost/scope_exit.hpp>

Tobias WEBER's avatar
Tobias WEBER committed
49
#include "tlibs2/libs/str.h"
Tobias WEBER's avatar
Tobias WEBER committed
50
51
#include "tlibs2/libs/file.h"

52

Tobias WEBER's avatar
Tobias WEBER committed
53
54
#define OBJNAME_COORD_CROSS "coord_cross"
#define OBJNAME_FLOOR_PLANE "floor"
Tobias WEBER's avatar
Tobias WEBER committed
55
#define MAX_LIGHTS 4	// max. number allowed in shader
Tobias WEBER's avatar
Tobias WEBER committed
56
57


Tobias WEBER's avatar
Tobias WEBER committed
58
PathsRenderer::PathsRenderer(QWidget *pParent) : QOpenGLWidget(pParent)
59
{
Tobias WEBER's avatar
Tobias WEBER committed
60
61
	connect(&m_timer, &QTimer::timeout,
		this, static_cast<void (PathsRenderer::*)()>(&PathsRenderer::tick));
Tobias WEBER's avatar
Tobias WEBER committed
62
	EnableTimer(true);
63
64

	UpdateCam();
Tobias WEBER's avatar
Tobias WEBER committed
65
	setMouseTracking(true);
Tobias WEBER's avatar
Tobias WEBER committed
66
	setFocusPolicy(Qt::StrongFocus);
67
68
69
}


Tobias WEBER's avatar
Tobias WEBER committed
70
PathsRenderer::~PathsRenderer()
71
{
Tobias WEBER's avatar
Tobias WEBER committed
72
	EnableTimer(false);
Tobias WEBER's avatar
Tobias WEBER committed
73
	setMouseTracking(false);
Tobias WEBER's avatar
Tobias WEBER committed
74
	Clear();
75
76
77

	// delete gl objects within current gl context
	m_pShaders.reset();
Tobias WEBER's avatar
Tobias WEBER committed
78
79
80
}


Tobias WEBER's avatar
Tobias WEBER committed
81
82
83
84
85
86
87
88
89
void PathsRenderer::EnableTimer(bool enabled)
{
	if(enabled)
		m_timer.start(std::chrono::milliseconds(1000 / g_timer_fps));
	else
		m_timer.stop();
}


Tobias WEBER's avatar
Tobias WEBER committed
90
91
92
/**
 * renderer versions and driver descriptions
 */
Tobias WEBER's avatar
Tobias WEBER committed
93
94
std::tuple<std::string, std::string, std::string, std::string>
PathsRenderer::GetGlDescr() const
Tobias WEBER's avatar
Tobias WEBER committed
95
{
Tobias WEBER's avatar
Tobias WEBER committed
96
97
98
	return std::make_tuple(
		m_strGlVer, m_strGlShaderVer,
		m_strGlVendor, m_strGlRenderer);
Tobias WEBER's avatar
Tobias WEBER committed
99
100
}

101

Tobias WEBER's avatar
Tobias WEBER committed
102
103
104
105
106
107
108
109
110
/**
 * clear instrument scene
 */
void PathsRenderer::Clear()
{
	makeCurrent();
	BOOST_SCOPE_EXIT(this_) { this_->doneCurrent(); } BOOST_SCOPE_EXIT_END

	QMutexLocker _locker{&m_mutexObj};
Tobias WEBER's avatar
Tobias WEBER committed
111
112
	for(auto &[obj_name, obj] : m_objs)
		DeleteObject(obj);
113
114
115
116
117

	m_objs.clear();
}


Tobias WEBER's avatar
Tobias WEBER committed
118
119
120
/**
 * create a 3d representation of the instrument and walls
 */
121
void PathsRenderer::LoadInstrument(const InstrumentSpace& instrspace)
Tobias WEBER's avatar
Tobias WEBER committed
122
123
124
{
	Clear();

Tobias WEBER's avatar
Tobias WEBER committed
125
126
127
	// upper and lower floor plane
	// the lower floor plane just serves to hide clipping artefacts
	std::string lowerFloor = "lower " OBJNAME_FLOOR_PLANE;
128
	AddFloorPlane(OBJNAME_FLOOR_PLANE, instrspace.GetFloorLenX(), instrspace.GetFloorLenY());
Tobias WEBER's avatar
Tobias WEBER committed
129
130
	AddFloorPlane(lowerFloor, instrspace.GetFloorLenX(), instrspace.GetFloorLenY());
	m_objs[lowerFloor].m_mat(2,3) = -0.01;
131
132

	// instrument
Tobias WEBER's avatar
Tobias WEBER committed
133
134
135
136
	const Instrument& instr = instrspace.GetInstrument();
	const Axis& mono = instr.GetMonochromator();
	const Axis& sample = instr.GetSample();
	const Axis& ana = instr.GetAnalyser();
137

Tobias WEBER's avatar
Tobias WEBER committed
138
	for(const Axis* axis : { &mono, &sample, &ana })
139
	{
140
		// get geometries relative to incoming, internal, and outgoing axis
Tobias WEBER's avatar
Tobias WEBER committed
141
		for(AxisAngle axisangle : {AxisAngle::INCOMING, AxisAngle::INTERNAL, AxisAngle::OUTGOING})
142
		{
Tobias WEBER's avatar
Tobias WEBER committed
143
			t_mat_gl matAxis = tl2::convert<t_mat_gl>(axis->GetTrafo(axisangle));
Tobias WEBER's avatar
Tobias WEBER committed
144

Tobias WEBER's avatar
Tobias WEBER committed
145
			for(const auto& comp : axis->GetComps(axisangle))
Tobias WEBER's avatar
Tobias WEBER committed
146
			{
Tobias WEBER's avatar
Tobias WEBER committed
147
				auto [_verts, _norms, _uvs] = comp->GetTriangles();
Tobias WEBER's avatar
Tobias WEBER committed
148
149
150
151
152
153
154
155

				auto verts = tl2::convert<t_vec3_gl>(_verts);
				auto norms = tl2::convert<t_vec3_gl>(_norms);
				auto uvs = tl2::convert<t_vec3_gl>(_uvs);
				auto cols = tl2::convert<t_vec3_gl>(comp->GetColour());

				AddTriangleObject(comp->GetId(), verts, norms, uvs,
					cols[0], cols[1], cols[2], 1);
Tobias WEBER's avatar
Tobias WEBER committed
156

Tobias WEBER's avatar
Tobias WEBER committed
157
				const t_mat& _matGeo = comp->GetTrafo();
Tobias WEBER's avatar
Tobias WEBER committed
158
159
160
				t_mat_gl matGeo = tl2::convert<t_mat_gl>(_matGeo);
				t_mat_gl mat = matAxis * matGeo;

Tobias WEBER's avatar
Tobias WEBER committed
161
162
				m_objs[comp->GetId()].m_mat = mat;
			}
163
		}
Tobias WEBER's avatar
Tobias WEBER committed
164
	}
Tobias WEBER's avatar
Tobias WEBER committed
165
166

	// walls
167
	for(const auto& wall : instrspace.GetWalls())
Tobias WEBER's avatar
Tobias WEBER committed
168
	{
Tobias WEBER's avatar
Tobias WEBER committed
169
170
171
172
		if(!wall)
			continue;
		AddWall(*wall, false);
	}
Tobias WEBER's avatar
Tobias WEBER committed
173

Tobias WEBER's avatar
Tobias WEBER committed
174
175
	update();
}
Tobias WEBER's avatar
Tobias WEBER committed
176

Tobias WEBER's avatar
Tobias WEBER committed
177

Tobias WEBER's avatar
Tobias WEBER committed
178
179
180
181
182
183
/**
 * insert a wall into the scene
 */
void PathsRenderer::AddWall(const Geometry& wall, bool update_scene)
{
	auto [_verts, _norms, _uvs] = wall.GetTriangles();
Tobias WEBER's avatar
Tobias WEBER committed
184

Tobias WEBER's avatar
Tobias WEBER committed
185
186
187
188
189
190
191
192
193
194
195
196
197
198
	auto verts = tl2::convert<t_vec3_gl>(_verts);
	auto norms = tl2::convert<t_vec3_gl>(_norms);
	auto uvs = tl2::convert<t_vec3_gl>(_uvs);
	auto cols = tl2::convert<t_vec3_gl>(wall.GetColour());

	AddTriangleObject(wall.GetId(), verts, norms, uvs,
		cols[0], cols[1], cols[2], 1);

	const t_mat& _mat = wall.GetTrafo();
	t_mat_gl mat = tl2::convert<t_mat_gl>(_mat);
	m_objs[wall.GetId()].m_mat = mat;

	if(update_scene)
		update();
Tobias WEBER's avatar
Tobias WEBER committed
199
200
201
}


Tobias WEBER's avatar
Tobias WEBER committed
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/**
 * instrument space has been changed (e.g. walls have been moved)
 */
void PathsRenderer::UpdateInstrumentSpace(const InstrumentSpace& instr)
{
	// update wall matrices
	for(const auto& wall : instr.GetWalls())
	{
		m_objs[wall->GetId()].m_mat = wall->GetTrafo();
	}

	update();
}


Tobias WEBER's avatar
Tobias WEBER committed
217
218
219
/**
 * move the instrument to a new position
 */
Tobias WEBER's avatar
Tobias WEBER committed
220
void PathsRenderer::UpdateInstrument(const Instrument& instr)
Tobias WEBER's avatar
Tobias WEBER committed
221
{
Tobias WEBER's avatar
Tobias WEBER committed
222
223
224
225
226
	// instrument axes
	const auto& mono = instr.GetMonochromator();
	const auto& sample = instr.GetSample();
	const auto& ana = instr.GetAnalyser();

Tobias WEBER's avatar
Tobias WEBER committed
227
	for(const Axis* axis : { &mono, &sample, &ana })
Tobias WEBER's avatar
Tobias WEBER committed
228
229
	{
		// get geometries both relative to incoming and to outgoing axis
Tobias WEBER's avatar
Tobias WEBER committed
230
		for(AxisAngle axisangle : {AxisAngle::INCOMING, AxisAngle::INTERNAL, AxisAngle::OUTGOING})
Tobias WEBER's avatar
Tobias WEBER committed
231
232
233
234
235
236
237
238
239
		{
			t_mat_gl matAxis = tl2::convert<t_mat_gl>(axis->GetTrafo(axisangle));

			for(const auto& comp : axis->GetComps(axisangle))
			{
				auto iter = m_objs.find(comp->GetId());
				if(iter == m_objs.end())
					continue;

Tobias WEBER's avatar
Tobias WEBER committed
240
				const t_mat& _matGeo = comp->GetTrafo();
Tobias WEBER's avatar
Tobias WEBER committed
241
242
243
244
245
246
247
248
249
				t_mat_gl matGeo = tl2::convert<t_mat_gl>(_matGeo);
				t_mat_gl mat = matAxis * matGeo;

				iter->second.m_mat = mat;
			}
		}
	}

	update();
Tobias WEBER's avatar
Tobias WEBER committed
250
251
252
}


Tobias WEBER's avatar
Tobias WEBER committed
253
254
255
256
257
258
259
void PathsRenderer::SetInstrumentStatus(bool in_angular_limits, bool colliding)
{
	m_in_angular_limits = in_angular_limits;
	m_colliding = colliding;
}


260
QPointF PathsRenderer::GlToScreenCoords(const t_vec_gl& vec4, bool *pVisible) const
261
262
263
264
265
266
267
268
269
{
	auto [ vecPersp, vec ] =
		tl2::hom_to_screen_coords<t_mat_gl, t_vec_gl>
			(vec4, m_matCam, m_matPerspective, m_matViewport, true);

	// position not visible -> return a point outside the viewport
	if(vecPersp[2] > 1.)
	{
		if(pVisible) *pVisible = false;
270
		return QPointF(-1*m_screenDims[0], -1*m_screenDims[1]);
271
272
273
274
275
276
277
	}

	if(pVisible) *pVisible = true;
	return QPointF(vec[0], vec[1]);
}


Tobias WEBER's avatar
Tobias WEBER committed
278
279
280
281
282
/**
 * delete an object
 */
void PathsRenderer::DeleteObject(PathsObj& obj)
{
Tobias WEBER's avatar
Tobias WEBER committed
283
	tl2::delete_render_object(obj);
Tobias WEBER's avatar
Tobias WEBER committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
}


/**
 * delete an object by name
 */
void PathsRenderer::DeleteObject(const std::string& obj_name)
{
	QMutexLocker _locker{&m_mutexObj};
	auto iter = m_objs.find(obj_name);

	if(iter != m_objs.end())
	{
		DeleteObject(iter->second);
		m_objs.erase(iter);
	}
}


Tobias WEBER's avatar
Tobias WEBER committed
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/**
 * rename an object
 */
void PathsRenderer::RenameObject(const std::string& oldname, const std::string& newname)
{
	QMutexLocker _locker{&m_mutexObj};
	auto iter = m_objs.find(oldname);

	if(iter != m_objs.end())
	{
		// get node handle to rename key
		decltype(m_objs)::node_type node = m_objs.extract(iter);
		node.key() = newname;
		m_objs.insert(std::move(node));
	}

}


Tobias WEBER's avatar
Tobias WEBER committed
322
323
324
/**
 * add a polygon-based object
 */
Tobias WEBER's avatar
Tobias WEBER committed
325
326
void PathsRenderer::AddTriangleObject(const std::string& obj_name,
	const std::vector<t_vec3_gl>& triag_verts,
Tobias WEBER's avatar
Tobias WEBER committed
327
328
	const std::vector<t_vec3_gl>& triag_norms,
	const std::vector<t_vec3_gl>& triag_uvs,
329
330
	t_real_gl r, t_real_gl g, t_real_gl b, t_real_gl a)
{
Tobias WEBER's avatar
Tobias WEBER committed
331
332
	auto [boundingSpherePos, boundingSphereRad] =
		tl2::bounding_sphere<t_vec3_gl>(triag_verts);
Tobias WEBER's avatar
Tobias WEBER committed
333
	auto col = tl2::create<t_vec_gl>({r,g,b,a});
334
335
336

	QMutexLocker _locker{&m_mutexObj};

Tobias WEBER's avatar
Tobias WEBER committed
337
	PathsObj obj;
Tobias WEBER's avatar
Tobias WEBER committed
338
339
	create_triangle_object(this, obj,
		triag_verts, triag_verts, triag_norms, triag_uvs, col,
Tobias WEBER's avatar
Tobias WEBER committed
340
		false, m_attrVertex, m_attrVertexNorm, m_attrVertexCol, m_attrTexCoords);
Tobias WEBER's avatar
Tobias WEBER committed
341

Tobias WEBER's avatar
Tobias WEBER committed
342
	obj.m_mat = tl2::hom_translation<t_mat_gl, t_real_gl>(0., 0., 0.);
343
344
345
	obj.m_boundingSpherePos = std::move(boundingSpherePos);
	obj.m_boundingSphereRad = boundingSphereRad;

Tobias WEBER's avatar
Tobias WEBER committed
346
	m_objs.insert(std::make_pair(obj_name, std::move(obj)));
347
348
349
}


Tobias WEBER's avatar
Tobias WEBER committed
350
351
352
/**
 * add the floor plane
 */
Tobias WEBER's avatar
Tobias WEBER committed
353
void PathsRenderer::AddFloorPlane(const std::string& obj_name, t_real_gl len_x, t_real_gl len_y)
Tobias WEBER's avatar
Tobias WEBER committed
354
{
Tobias WEBER's avatar
updates    
Tobias WEBER committed
355
	auto norm = tl2::create<t_vec3_gl>({0, 0, 1});
Tobias WEBER's avatar
Tobias WEBER committed
356
	auto plane = tl2::create_plane<t_mat_gl, t_vec3_gl>(norm, 0.5*len_x, 0.5*len_y);
357
	auto [verts, norms, uvs] = tl2::subdivide_triangles<t_vec3_gl>(
358
		tl2::create_triangles<t_vec3_gl>(plane), 1);
Tobias WEBER's avatar
Tobias WEBER committed
359

Tobias WEBER's avatar
Tobias WEBER committed
360
	AddTriangleObject(obj_name, verts, norms, uvs, 0.5,0.5,0.5,1);
Tobias WEBER's avatar
Tobias WEBER committed
361
	m_objs[obj_name].m_cull = false;
Tobias WEBER's avatar
Tobias WEBER committed
362
363
364
}


Tobias WEBER's avatar
Tobias WEBER committed
365
void PathsRenderer::UpdateCam()
366
{
Tobias WEBER's avatar
Tobias WEBER committed
367
368
369
370
371
372
373
374
375
376
377
378
379
	t_mat_gl matCamTrans = m_matCamTrans;
	matCamTrans(2,3) = 0.;
	t_mat_gl matCamTrans_inv = matCamTrans;
	matCamTrans_inv(0,3) = -matCamTrans(0,3);
	matCamTrans_inv(1,3) = -matCamTrans(1,3);
	matCamTrans_inv(2,3) = -matCamTrans(2,3);

	const t_vec_gl vecCamDir[2] =
	{
		tl2::create<t_vec_gl>({1., 0., 0., 0.}),
		tl2::create<t_vec_gl>({0. ,0., 1., 0.})
	};

Tobias WEBER's avatar
Tobias WEBER committed
380
381
	m_matCamRot = tl2::hom_rotation<t_mat_gl, t_vec_gl>(vecCamDir[0], m_theta, 0);
	m_matCamRot *= tl2::hom_rotation<t_mat_gl, t_vec_gl>(vecCamDir[1], m_phi, 0);
Tobias WEBER's avatar
Tobias WEBER committed
382

383
	m_matCam = tl2::unit<t_mat_gl>();
Tobias WEBER's avatar
Tobias WEBER committed
384
	m_matCam *= m_matCamTrans;
385
	m_matCam(2,3) /= m_zoom;
Tobias WEBER's avatar
Tobias WEBER committed
386
	m_matCam *= matCamTrans_inv * m_matCamRot * matCamTrans;
387
388
	std::tie(m_matCam_inv, std::ignore) = tl2::inv<t_mat_gl>(m_matCam);

389
	m_pickerNeedsUpdate = true;
Tobias WEBER's avatar
Tobias WEBER committed
390
	emit CamPositionChanged(m_matCamTrans(0,3), m_matCamTrans(1,3), m_matCamTrans(2,3));
Tobias WEBER's avatar
Tobias WEBER committed
391
	emit CamRotationChanged(m_phi, m_theta);
Tobias WEBER's avatar
Tobias WEBER committed
392

Tobias WEBER's avatar
Tobias WEBER committed
393
	update();
394
395
396
}


Tobias WEBER's avatar
Tobias WEBER committed
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/**
 * @brief centre camera around a given object
 */
void PathsRenderer::CentreCam(const std::string& objid)
{
	if(auto iter = m_objs.find(objid); iter!=m_objs.end())
	{
		PathsObj& obj = iter->second;
		m_matCamTrans(0,3) = -obj.m_mat(0,3);
		m_matCamTrans(1,3) = -obj.m_mat(1,3);
		//m_matCamTrans(2,3) = -bj.m_mat(2,3);

		UpdateCam();
	}
}


Tobias WEBER's avatar
Tobias WEBER committed
414
void PathsRenderer::SetLight(std::size_t idx, const t_vec3_gl& pos)
415
416
417
418
419
{
	if(m_lights.size() < idx+1)
		m_lights.resize(idx+1);

	m_lights[idx] = pos;
420
	m_lightsNeedUpdate = true;
Tobias WEBER's avatar
Tobias WEBER committed
421

Tobias WEBER's avatar
Tobias WEBER committed
422
423
	//t_vec3_gl target = tl2::create<t_vec3_gl>({0, 0, 0});
	t_vec3_gl target = pos;
Tobias WEBER's avatar
Tobias WEBER committed
424
425
	target[2] = 0;

Tobias WEBER's avatar
Tobias WEBER committed
426
427
	t_vec3_gl up = tl2::create<t_vec3_gl>({0, 1, 0});
	m_matLight = tl2::hom_lookat<t_mat_gl, t_vec3_gl>(pos, target, up);
Tobias WEBER's avatar
Tobias WEBER committed
428
429

	std::tie(m_matLight_inv, std::ignore) = tl2::inv<t_mat_gl>(m_matLight);
430
431
432
}


Tobias WEBER's avatar
Tobias WEBER committed
433
void PathsRenderer::UpdateLights()
434
{
Tobias WEBER's avatar
Tobias WEBER committed
435
436
437
438
	auto *pGl = GetGlFunctions();
	if(!pGl)
		return;

439
440
441
442
443
444
445
446
447
448
	int num_lights = std::min(MAX_LIGHTS, static_cast<int>(m_lights.size()));
	t_real_gl pos[num_lights * 3];

	for(int i=0; i<num_lights; ++i)
	{
		pos[i*3 + 0] = m_lights[i][0];
		pos[i*3 + 1] = m_lights[i][1];
		pos[i*3 + 2] = m_lights[i][2];
	}

Tobias WEBER's avatar
Tobias WEBER committed
449
450
451
452
453
	// bind shaders
	m_pShaders->bind();
	BOOST_SCOPE_EXIT(m_pShaders) { m_pShaders->release(); } BOOST_SCOPE_EXIT_END
	LOGGLERR(pGl);

454
455
456
	m_pShaders->setUniformValueArray(m_uniLightPos, pos, num_lights, 3);
	m_pShaders->setUniformValue(m_uniNumActiveLights, num_lights);

Tobias WEBER's avatar
Tobias WEBER committed
457
	UpdateLightPerspective();
458
	m_lightsNeedUpdate = false;
459
460
461
}


Tobias WEBER's avatar
Tobias WEBER committed
462
void PathsRenderer::EnablePicker(bool b)
463
{
464
	m_pickerEnabled = b;
465
466
467
}


Tobias WEBER's avatar
Tobias WEBER committed
468
void PathsRenderer::UpdatePicker()
469
{
470
	if(!m_initialised || !m_pickerEnabled)
471
		return;
472
473
474
475
476
477
478
479
480
481
482

	// picker ray
	auto [org, dir] = tl2::hom_line_from_screen_coords<t_mat_gl, t_vec_gl>(
		m_posMouse.x(), m_posMouse.y(), 0., 1., m_matCam_inv,
		m_matPerspective_inv, m_matViewport_inv, &m_matViewport, true);
	t_vec3_gl org3 = tl2::create<t_vec3_gl>({org[0], org[1], org[2]});
	t_vec3_gl dir3 = tl2::create<t_vec3_gl>({dir[0], dir[1], dir[2]});


	// intersection with unit sphere around origin
	bool hasSphereInters = false;
Tobias WEBER's avatar
Tobias WEBER committed
483
	t_vec_gl vecClosestSphereInters = tl2::create<t_vec_gl>({0, 0, 0, 0});
484
485

	auto intersUnitSphere =
Tobias WEBER's avatar
Tobias WEBER committed
486
487
		tl2::intersect_line_sphere<t_vec3_gl, std::vector>(org3, dir3,
			tl2::create<t_vec3_gl>({0,0,0}), t_real_gl(m_pickerSphereRadius));
488
489
	for(const auto& result : intersUnitSphere)
	{
Tobias WEBER's avatar
Tobias WEBER committed
490
		t_vec_gl vecInters4 =
Tobias WEBER's avatar
Tobias WEBER committed
491
			tl2::create<t_vec_gl>({result[0], result[1], result[2], 1});
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511

		if(!hasSphereInters)
		{	// first intersection
			vecClosestSphereInters = vecInters4;
			hasSphereInters = true;
		}
		else
		{	// test if next intersection is closer...
			t_vec_gl oldPosTrafo = m_matCam * vecClosestSphereInters;
			t_vec_gl newPosTrafo = m_matCam * vecInters4;

			// ... it is closer.
			if(tl2::norm(newPosTrafo) < tl2::norm(oldPosTrafo))
				vecClosestSphereInters = vecInters4;
		}
	}


	// intersection with geometry
	bool hasInters = false;
Tobias WEBER's avatar
Tobias WEBER committed
512
	m_curObj = "";
Tobias WEBER's avatar
Tobias WEBER committed
513
	m_curActive = false;
Tobias WEBER's avatar
Tobias WEBER committed
514
	t_vec_gl vecClosestInters = tl2::create<t_vec_gl>({0, 0, 0, 0});
515
516
517

	QMutexLocker _locker{&m_mutexObj};

Tobias WEBER's avatar
Tobias WEBER committed
518
	for(const auto& [obj_name, obj] : m_objs)
519
	{
520
		if(obj.m_type != tl2::GlRenderObjType::TRIANGLES || !obj.m_visible)
521
522
523
			continue;


Tobias WEBER's avatar
Tobias WEBER committed
524
		const t_mat_gl& matTrafo = obj.m_mat;
525
526
527
528
529
530
531

		// scaling factor, TODO: maximum factor for non-uniform scaling
		auto scale = std::cbrt(std::abs(tl2::det(matTrafo)));

		// intersection with bounding sphere?
		auto boundingInters =
			tl2::intersect_line_sphere<t_vec3_gl, std::vector>(org3, dir3,
Tobias WEBER's avatar
Tobias WEBER committed
532
				matTrafo * obj.m_boundingSpherePos, scale*obj.m_boundingSphereRad);
533
534
535
536
537
		if(boundingInters.size() == 0)
			continue;


		// test actual polygons for intersection
Tobias WEBER's avatar
Tobias WEBER committed
538
		for(std::size_t startidx=0; startidx+2<obj.m_triangles.size(); startidx+=3)
539
540
		{
			std::vector<t_vec3_gl> poly{ {
Tobias WEBER's avatar
Tobias WEBER committed
541
542
543
				obj.m_triangles[startidx+0],
				obj.m_triangles[startidx+1],
				obj.m_triangles[startidx+2]
544
545
			} };

Tobias WEBER's avatar
Tobias WEBER committed
546
547
548
			std::vector<t_vec3_gl> polyuv{ {
				obj.m_uvs[startidx+0],
				obj.m_uvs[startidx+1],
549
550
551
				obj.m_uvs[startidx+2]
			} };

552
553
554
555
556
557
			auto [vecInters, bInters, lamInters] =
				tl2::intersect_line_poly<t_vec3_gl, t_mat_gl>(org3, dir3, poly, matTrafo);

			if(bInters)
			{
				t_vec_gl vecInters4 = tl2::create<t_vec_gl>({vecInters[0], vecInters[1], vecInters[2], 1});
Tobias WEBER's avatar
Tobias WEBER committed
558
559
560
561
562

				// intersection with floor plane
				if(obj_name == OBJNAME_FLOOR_PLANE)
				{
					auto uv = tl2::poly_uv<t_mat_gl, t_vec3_gl>
Tobias WEBER's avatar
Tobias WEBER committed
563
						(poly[0], poly[1], poly[2], polyuv[0], polyuv[1], polyuv[2], vecInters);
Tobias WEBER's avatar
Tobias WEBER committed
564
565

					// save intersections with base plane for drawing walls
Tobias WEBER's avatar
Tobias WEBER committed
566
567
568
569
					m_cursorUV[0] = uv[0];
					m_cursorUV[1] = uv[1];
					m_cursor[0] = vecInters4[0];
					m_cursor[1] = vecInters4[1];
Tobias WEBER's avatar
Tobias WEBER committed
570
					m_curActive = true;
Tobias WEBER's avatar
Tobias WEBER committed
571
572

					emit FloorPlaneCoordsChanged(vecInters4[0], vecInters4[1]);
Tobias WEBER's avatar
Tobias WEBER committed
573
574
575

					if(m_light_follows_cursor)
						SetLight(0, tl2::create<t_vec3_gl>({vecInters4[0], vecInters4[1], 10}));
Tobias WEBER's avatar
Tobias WEBER committed
576
577
578
				}

				// intersection with other objects
Tobias WEBER's avatar
Tobias WEBER committed
579
				bool updateUV = false;
580
581
582
583

				if(!hasInters)
				{	// first intersection
					vecClosestInters = vecInters4;
Tobias WEBER's avatar
Tobias WEBER committed
584
					m_curObj = obj_name;
585
					hasInters = true;
Tobias WEBER's avatar
Tobias WEBER committed
586
					updateUV = true;
587
588
589
590
591
592
593
594
595
				}
				else
				{	// test if next intersection is closer...
					t_vec_gl oldPosTrafo = m_matCam * vecClosestInters;
					t_vec_gl newPosTrafo = m_matCam * vecInters4;

					if(tl2::norm(newPosTrafo) < tl2::norm(oldPosTrafo))
					{	// ...it is closer
						vecClosestInters = vecInters4;
Tobias WEBER's avatar
Tobias WEBER committed
596
						m_curObj = obj_name;
Tobias WEBER's avatar
Tobias WEBER committed
597
598

						updateUV = true;
599
600
					}
				}
601

Tobias WEBER's avatar
Tobias WEBER committed
602
				if(updateUV)
Tobias WEBER's avatar
Tobias WEBER committed
603
604
				{
				}
605
606
607
608
			}
		}
	}

609
	m_pickerNeedsUpdate = false;
Tobias WEBER's avatar
Tobias WEBER committed
610
611
612
613
	t_vec3_gl vecClosestInters3 = tl2::create<t_vec3_gl>({
		vecClosestInters[0], vecClosestInters[1], vecClosestInters[2]});
	t_vec3_gl vecClosestSphereInters3 = tl2::create<t_vec3_gl>({
		vecClosestSphereInters[0], vecClosestSphereInters[1], vecClosestSphereInters[2]});
Tobias WEBER's avatar
Tobias WEBER committed
614

Tobias WEBER's avatar
Tobias WEBER committed
615
	update();
Tobias WEBER's avatar
Tobias WEBER committed
616
	emit PickerIntersection(hasInters ? &vecClosestInters3 : nullptr,
Tobias WEBER's avatar
Tobias WEBER committed
617
		m_curObj, hasSphereInters ? &vecClosestSphereInters3 : nullptr);
618
619
620
}


Tobias WEBER's avatar
Tobias WEBER committed
621
void PathsRenderer::tick()
622
{
Tobias WEBER's avatar
Tobias WEBER committed
623
	tick(std::chrono::milliseconds(1000 / g_timer_fps));
624
625
626
}


Tobias WEBER's avatar
Tobias WEBER committed
627
void PathsRenderer::tick(const std::chrono::milliseconds& ms)
628
{
Tobias WEBER's avatar
Tobias WEBER committed
629
630
631
632
633
	// if a key is pressed, move and update the camera
	if(m_arrowDown[0] || m_arrowDown[1] || m_arrowDown[2] || m_arrowDown[3]
		|| m_pageDown[0] || m_pageDown[1])
	{
		t_real_gl move_scale = t_real_gl(ms.count()) * g_move_scale;
Tobias WEBER's avatar
Tobias WEBER committed
634

Tobias WEBER's avatar
Tobias WEBER committed
635
636
637
		t_vec_gl xdir = tl2::row<t_mat_gl, t_vec_gl>(m_matCamRot, 0);
		t_vec_gl ydir = tl2::row<t_mat_gl, t_vec_gl>(m_matCamRot, 1);
		t_vec_gl zdir = tl2::row<t_mat_gl, t_vec_gl>(m_matCamRot, 2);
Tobias WEBER's avatar
Tobias WEBER committed
638

Tobias WEBER's avatar
Tobias WEBER committed
639
640
641
642
643
644
		t_vec_gl xinc = xdir * move_scale *
			(t_real_gl(m_arrowDown[0]) - t_real_gl(m_arrowDown[1]));
		t_vec_gl yinc = ydir * move_scale *
			(t_real_gl(m_pageDown[0]) - t_real_gl(m_pageDown[1]));
		t_vec_gl zinc = zdir * move_scale *
			(t_real_gl(m_arrowDown[2]) - t_real_gl(m_arrowDown[3]));
Tobias WEBER's avatar
Tobias WEBER committed
645

Tobias WEBER's avatar
Tobias WEBER committed
646
647
648
		m_matCamTrans(0,3) += xinc[0] + yinc[0] + zinc[0];
		m_matCamTrans(1,3) += xinc[1] + yinc[1] + zinc[1];
		m_matCamTrans(2,3) += xinc[2] + yinc[2] + zinc[2];
Tobias WEBER's avatar
Tobias WEBER committed
649

Tobias WEBER's avatar
Tobias WEBER committed
650
651
		UpdateCam();
	}
652
653
654
}


Tobias WEBER's avatar
Tobias WEBER committed
655
void PathsRenderer::initializeGL()
656
{
657
	m_initialised = false;
658

Tobias WEBER's avatar
Tobias WEBER committed
659
660
661
	// --------------------------------------------------------------------
	// shaders
	// --------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
662
663
	std::string fragfile = g_res.FindResource("frag.shader");
	std::string vertexfile = g_res.FindResource("vertex.shader");
664
665
666

	auto [frag_ok, strFragShader] = tl2::load_file<std::string>(fragfile);
	auto [vertex_ok, strVertexShader] = tl2::load_file<std::string>(vertexfile);
667

Tobias WEBER's avatar
Tobias WEBER committed
668
	if(!frag_ok || !vertex_ok)
669
	{
Tobias WEBER's avatar
Tobias WEBER committed
670
671
672
673
		std::cerr << "Fragment or vertex shader could not be loaded." << std::endl;
		return;
	}
	// --------------------------------------------------------------------
674
675


Tobias WEBER's avatar
Tobias WEBER committed
676
	// set glsl version and constants
Tobias WEBER's avatar
Tobias WEBER committed
677
678
	const std::string strGlsl = tl2::var_to_str<t_real_gl>(_GLSL_MAJ_VER*100 + _GLSL_MIN_VER*10);
	std::string strPi = tl2::var_to_str<t_real_gl>(tl2::pi<t_real_gl>);
Tobias WEBER's avatar
Tobias WEBER committed
679
680
681
682
683
684
	algo::replace_all(strPi, std::string(","), std::string("."));	// ensure decimal point

	for(std::string* strSrc : { &strFragShader, &strVertexShader })
	{
		algo::replace_all(*strSrc, std::string("${GLSL_VERSION}"), strGlsl);
		algo::replace_all(*strSrc, std::string("${PI}"), strPi);
Tobias WEBER's avatar
Tobias WEBER committed
685
		algo::replace_all(*strSrc, std::string("${MAX_LIGHTS}"), tl2::var_to_str<unsigned int>(MAX_LIGHTS));
686
687
688
	}


Tobias WEBER's avatar
updates    
Tobias WEBER committed
689
	// get gl functions
690
	auto *pGl = tl2::get_gl_functions(this);
Tobias WEBER's avatar
Tobias WEBER committed
691
	if(!pGl) return;
692

Tobias WEBER's avatar
Tobias WEBER committed
693
694
695
696
697
	m_strGlVer = (char*)pGl->glGetString(GL_VERSION);
	m_strGlShaderVer = (char*)pGl->glGetString(GL_SHADING_LANGUAGE_VERSION);
	m_strGlVendor = (char*)pGl->glGetString(GL_VENDOR);
	m_strGlRenderer = (char*)pGl->glGetString(GL_RENDERER);
	LOGGLERR(pGl);
698
699


Tobias WEBER's avatar
Tobias WEBER committed
700
701
702
	static QMutex shadermutex;
	shadermutex.lock();
	BOOST_SCOPE_EXIT(&shadermutex) { shadermutex.unlock(); } BOOST_SCOPE_EXIT_END
703

Tobias WEBER's avatar
Tobias WEBER committed
704
705
706
707
	// shader compiler/linker error handler
	auto shader_err = [this](const char* err) -> void
	{
		std::cerr << err << std::endl;
708

Tobias WEBER's avatar
Tobias WEBER committed
709
710
711
712
		std::string strLog = m_pShaders->log().toStdString();
		if(strLog.size())
			std::cerr << "Shader log: " << strLog << std::endl;
	};
713

Tobias WEBER's avatar
Tobias WEBER committed
714
715
	// compile & link shaders
	m_pShaders = std::make_shared<QOpenGLShaderProgram>(this);
716

Tobias WEBER's avatar
Tobias WEBER committed
717
718
719
720
721
	if(!m_pShaders->addShaderFromSourceCode(QOpenGLShader::Fragment, strFragShader.c_str()))
	{
		shader_err("Cannot compile fragment shader.");
		return;
	}
722

Tobias WEBER's avatar
Tobias WEBER committed
723
724
725
726
727
	if(!m_pShaders->addShaderFromSourceCode(QOpenGLShader::Vertex, strVertexShader.c_str()))
	{
		shader_err("Cannot compile vertex shader.");
		return;
	}
728

Tobias WEBER's avatar
Tobias WEBER committed
729
730
731
732
733
	if(!m_pShaders->link())
	{
		shader_err("Cannot link shaders.");
		return;
	}
734

Tobias WEBER's avatar
Tobias WEBER committed
735

Tobias WEBER's avatar
Tobias WEBER committed
736
737
738
739
740
	// get attribute handles from shaders
	m_attrVertex = m_pShaders->attributeLocation("vertex");
	m_attrVertexNorm = m_pShaders->attributeLocation("normal");
	m_attrVertexCol = m_pShaders->attributeLocation("vertex_col");
	m_attrTexCoords = m_pShaders->attributeLocation("tex_coords");
Tobias WEBER's avatar
Tobias WEBER committed
741

Tobias WEBER's avatar
Tobias WEBER committed
742
	// get uniform handles from shaders
Tobias WEBER's avatar
Tobias WEBER committed
743
744
	m_uniMatrixCam = m_pShaders->uniformLocation("trafos_cam");
	m_uniMatrixCamInv = m_pShaders->uniformLocation("trafos_cam_inv");
Tobias WEBER's avatar
Tobias WEBER committed
745
746
	m_uniMatrixLight = m_pShaders->uniformLocation("trafos_light");
	m_uniMatrixLightInv = m_pShaders->uniformLocation("trafos_light_inv");
Tobias WEBER's avatar
Tobias WEBER committed
747
	m_uniMatrixProj = m_pShaders->uniformLocation("trafos_proj");
Tobias WEBER's avatar
Tobias WEBER committed
748
	m_uniMatrixLightProj = m_pShaders->uniformLocation("trafos_light_proj");
Tobias WEBER's avatar
Tobias WEBER committed
749
750
751
752
753
	m_uniMatrixObj = m_pShaders->uniformLocation("trafos_obj");

	m_uniConstCol = m_pShaders->uniformLocation("lights_const_col");
	m_uniLightPos = m_pShaders->uniformLocation("lights_pos");
	m_uniNumActiveLights = m_pShaders->uniformLocation("lights_numactive");
Tobias WEBER's avatar
Tobias WEBER committed
754

Tobias WEBER's avatar
Tobias WEBER committed
755
	m_uniShadowRenderingEnabled = m_pShaders->uniformLocation("shadow_enabled");
Tobias WEBER's avatar
Tobias WEBER committed
756
	m_uniShadowRenderPass = m_pShaders->uniformLocation("shadow_renderpass");
Tobias WEBER's avatar
Tobias WEBER committed
757
758
	m_uniShadowMap = m_pShaders->uniformLocation("shadow_map");

Tobias WEBER's avatar
Tobias WEBER committed
759
760
	m_uniCursorActive = m_pShaders->uniformLocation("cursor_active");
	m_uniCursorCoords = m_pShaders->uniformLocation("cursor_coords");
Tobias WEBER's avatar
Tobias WEBER committed
761
762
	LOGGLERR(pGl);

Tobias WEBER's avatar
Tobias WEBER committed
763
764
	SetLight(0, tl2::create<t_vec3_gl>({0, 0, 10}));

765
	m_initialised = true;
Tobias WEBER's avatar
Tobias WEBER committed
766
	emit AfterGLInitialisation();
767
768
769
770
771
}


void PathsRenderer::resizeGL(int w, int h)
{
772
773
	m_screenDims[0] = w;
	m_screenDims[1] = h;
Tobias WEBER's avatar
Tobias WEBER committed
774

775
776
	m_perspectiveNeedsUpdate = true;
	m_viewportNeedsUpdate = true;
Tobias WEBER's avatar
Tobias WEBER committed
777
778
	m_shadowFramebufferNeedsUpdate = true;
	m_lightsNeedUpdate = true;
Tobias WEBER's avatar
Tobias WEBER committed
779
	update();
780
781
}

Tobias WEBER's avatar
Tobias WEBER committed
782

Tobias WEBER's avatar
Tobias WEBER committed
783
qgl_funcs* PathsRenderer::GetGlFunctions()
784
{
785
	if(!m_initialised)
Tobias WEBER's avatar
Tobias WEBER committed
786
		return nullptr;
Tobias WEBER's avatar
Tobias WEBER committed
787
	if(auto *pContext = ((QOpenGLWidget*)this)->context(); !pContext)
Tobias WEBER's avatar
Tobias WEBER committed
788
		return nullptr;
789
	return tl2::get_gl_functions(this);
Tobias WEBER's avatar
Tobias WEBER committed
790
791
792
}


Tobias WEBER's avatar
Tobias WEBER committed
793
794
795
796
void PathsRenderer::SetCamViewingAngle(t_real_gl angle)
{
	m_camViewingAngle = angle;
	m_perspectiveNeedsUpdate = true;
Tobias WEBER's avatar
Tobias WEBER committed
797
	update();
Tobias WEBER's avatar
Tobias WEBER committed
798
799
800
801
802
803
804
805
806
807
808
809
}


void PathsRenderer::SetCamPosition(const t_vec3_gl& pos)
{
	m_matCamTrans(0,3) = pos[0];
	m_matCamTrans(1,3) = pos[1];
	m_matCamTrans(2,3) = pos[2];

	UpdateCam();
}

Tobias WEBER's avatar
Tobias WEBER committed
810

Tobias WEBER's avatar
Tobias WEBER committed
811
812
813
814
815
816
817
818
819
820
t_vec3_gl PathsRenderer::GetCamPosition() const
{
	return tl2::create<t_vec3_gl>({
		m_matCamTrans(0,3),
		m_matCamTrans(1,3),
		m_matCamTrans(2,3),
	});
}


Tobias WEBER's avatar
Tobias WEBER committed
821
822
void PathsRenderer::SetCamRotation(const t_vec2_gl& rot)
{
823
824
	m_phi_saved = m_phi = rot[0];
	m_theta_saved = m_theta = rot[1];
Tobias WEBER's avatar
Tobias WEBER committed
825
826
827
828

	UpdateCam();
}

Tobias WEBER's avatar
Tobias WEBER committed
829

Tobias WEBER's avatar
Tobias WEBER committed
830
831
832
833
834
835
t_vec2_gl PathsRenderer::GetCamRotation() const
{
	return tl2::create<t_vec2_gl>({ m_phi, m_theta });
}


Tobias WEBER's avatar
Tobias WEBER committed
836
837
void PathsRenderer::SetPerspectiveProjection(bool b)
{
Tobias WEBER's avatar
Tobias WEBER committed
838
	m_perspectiveProjection = b;
Tobias WEBER's avatar
Tobias WEBER committed
839
	m_perspectiveNeedsUpdate = true;
Tobias WEBER's avatar
Tobias WEBER committed
840
	update();
Tobias WEBER's avatar
Tobias WEBER committed
841
842
843
}


Tobias WEBER's avatar
Tobias WEBER committed
844
845
846
void PathsRenderer::UpdatePerspective()
{
	auto *pGl = GetGlFunctions();
Tobias WEBER's avatar
Tobias WEBER committed
847
848
849
	if(!pGl)
		return;

850
851
852
	// projection
	const t_real_gl nearPlane = 0.1;
	const t_real_gl farPlane = 1000.;
Tobias WEBER's avatar
Tobias WEBER committed
853

854
	if(m_perspectiveProjection)
Tobias WEBER's avatar
Tobias WEBER committed
855
	{
856
		m_matPerspective = tl2::hom_perspective<t_mat_gl, t_real_gl>(
Tobias WEBER's avatar
Tobias WEBER committed
857
			nearPlane, farPlane, m_camViewingAngle,
Tobias WEBER's avatar
Tobias WEBER committed
858
859
			t_real_gl(m_screenDims[1])/t_real_gl(m_screenDims[0]));
	}
860
	else
Tobias WEBER's avatar
Tobias WEBER committed
861
	{
Tobias WEBER's avatar
Tobias WEBER committed
862
863
		m_matPerspective = tl2::hom_ortho_sym<t_mat_gl, t_real_gl>(
			nearPlane, farPlane, 20., 20.);
Tobias WEBER's avatar
Tobias WEBER committed
864
	}
Tobias WEBER's avatar
Tobias WEBER committed
865

Tobias WEBER's avatar
Tobias WEBER committed
866
867
	std::tie(m_matPerspective_inv, std::ignore) =
		tl2::inv<t_mat_gl>(m_matPerspective);
Tobias WEBER's avatar
Tobias WEBER committed
868
869
870
871
872
873
874
875
876
877

	// bind shaders
	m_pShaders->bind();
	BOOST_SCOPE_EXIT(m_pShaders) { m_pShaders->release(); } BOOST_SCOPE_EXIT_END
	LOGGLERR(pGl);

	// set matrices
	m_pShaders->setUniformValue(m_uniMatrixProj, m_matPerspective);
	LOGGLERR(pGl);

878
	m_perspectiveNeedsUpdate = false;
879
880
881
}


Tobias WEBER's avatar
Tobias WEBER committed
882
883
884
885
886
887
888
889
890
891
void PathsRenderer::UpdateLightPerspective()
{
	auto *pGl = GetGlFunctions();
	if(!pGl)
		return;

	// projection
	const t_real_gl nearPlane = 0.1;
	const t_real_gl farPlane = 1000.;

Tobias WEBER's avatar
Tobias WEBER committed
892
893
894
895
896
897
898
	t_real ratio = 1;
	if(m_pfboshadow)
	{
		ratio = t_real_gl(m_pfboshadow->height()) /
			t_real_gl(m_pfboshadow->width());
	}

Tobias WEBER's avatar
Tobias WEBER committed
899
900
901
902
903
904
	if(m_perspectiveProjection)
	{
		// viewing angle has to be large enough so that the
		// shadow map covers the entire scene
		t_real_gl viewingangle = tl2::pi<t_real_gl> * 0.75;
		m_matLightPerspective = tl2::hom_perspective<t_mat_gl, t_real_gl>(
Tobias WEBER's avatar
Tobias WEBER committed
905
			nearPlane, farPlane, viewingangle, ratio);
Tobias WEBER's avatar
Tobias WEBER committed
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
	}
	else
	{
		m_matLightPerspective = tl2::hom_ortho_sym<t_mat_gl, t_real_gl>(
			nearPlane, farPlane, 20., 20.);
	}

	std::tie(m_matLightPerspective_inv, std::ignore) =
		tl2::inv<t_mat_gl>(m_matLightPerspective);

	// bind shaders
	m_pShaders->bind();
	BOOST_SCOPE_EXIT(m_pShaders) { m_pShaders->release(); } BOOST_SCOPE_EXIT_END
	LOGGLERR(pGl);

	// set matrices
	m_pShaders->setUniformValue(m_uniMatrixLightProj, m_matLightPerspective);
	LOGGLERR(pGl);
}


927
void PathsRenderer::UpdateViewport()
Tobias WEBER's avatar
Tobias WEBER committed
928
{
Tobias WEBER's avatar
Tobias WEBER committed
929
	auto *pGl = GetGlFunctions();
930
931
932
933
	if(!pGl)
		return;

	// viewport
Tobias WEBER's avatar
Tobias WEBER committed
934
935
	const t_real z_near{0}, z_far{1};

Tobias WEBER's avatar
Tobias WEBER committed
936
937
	int w = m_screenDims[0];
	int h = m_screenDims[1];
938

Tobias WEBER's avatar
Tobias WEBER committed
939
940
941
942
943
944
945
	m_matViewport = tl2::hom_viewport<t_mat_gl, t_real_gl>(
		w, h, z_near, z_far);
	std::tie(m_matViewport_inv, std::ignore) =
		tl2::inv<t_mat_gl>(m_matViewport);

	pGl->glViewport(0, 0, w, h);
	pGl->glDepthRange(z_near, z_far);
946
947
	LOGGLERR(pGl);

948
	m_viewportNeedsUpdate = false;
Tobias WEBER's avatar
Tobias WEBER committed
949
950
951
}


Tobias WEBER's avatar
Tobias WEBER committed
952
953
954
955
/**
 * frambuffer for shadow rendering
 * @see (Sellers 2014) pp. 534-540
 */
Tobias WEBER's avatar
Tobias WEBER committed
956
void PathsRenderer::UpdateShadowFramebuffer()
Tobias WEBER's avatar
Tobias WEBER committed
957
958
959
960
961
{
	auto *pGl = GetGlFunctions();
	if(!pGl)
		return;

Tobias WEBER's avatar
Tobias WEBER committed
962
963
964
	t_real scale = devicePixelRatio();
	int w = m_screenDims[0] * scale;
	int h = m_screenDims[1] * scale;
Tobias WEBER's avatar
Tobias WEBER committed
965
	QOpenGLFramebufferObjectFormat fbformat;
Tobias WEBER's avatar
Tobias WEBER committed
966
	fbformat.setTextureTarget(GL_TEXTURE_2D);
Tobias WEBER's avatar
Tobias WEBER committed
967
	fbformat.setInternalTextureFormat(GL_RGBA32F);
Tobias WEBER's avatar
Tobias WEBER committed
968
	fbformat.setAttachment(QOpenGLFramebufferObject::Depth /*NoAttachment*/);
Tobias WEBER's avatar
Tobias WEBER committed
969
	m_pfboshadow = std::make_shared<QOpenGLFramebufferObject>(
Tobias WEBER's avatar
Tobias WEBER committed
970
		w, h, fbformat);
Tobias WEBER's avatar
Tobias WEBER committed
971
972
973
974
975
976
977
978
979
980

	BOOST_SCOPE_EXIT(pGl, m_pfboshadow)
	{
		pGl->glBindTexture(GL_TEXTURE_2D, 0);
		m_pfboshadow->release();
	} BOOST_SCOPE_EXIT_END

	m_pfboshadow->bind();
	pGl->glBindTexture(GL_TEXTURE_2D, m_pfboshadow->texture());

Tobias WEBER's avatar
Tobias WEBER committed
981
982
983
984
	// shadow texture parameters
	// see: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml
	pGl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	pGl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Tobias WEBER's avatar
Tobias WEBER committed
985

Tobias WEBER's avatar
Tobias WEBER committed
986
987
988
	pGl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
	pGl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

Tobias WEBER's avatar
Tobias WEBER committed
989
	m_shadowFramebufferNeedsUpdate = false;
Tobias WEBER's avatar
Tobias WEBER committed
990
991
992
}


993
994
void PathsRenderer::paintGL()
{
995
	if(!m_initialised || thread() != QThread::currentThread())
996
997
		return;

Tobias WEBER's avatar
Tobias WEBER committed
998
999
1000
	QMutexLocker _locker{&m_mutexObj};

	if(auto *pContext = context(); !pContext) return;
Tobias WEBER's avatar
Tobias WEBER committed
1001
1002
1003
	auto *pGl = tl2::get_gl_functions(this);

	// shadow framebuffer render pass
Tobias WEBER's avatar
Tobias WEBER committed
1004
	if(m_shadowRenderingEnabled)
Tobias WEBER's avatar
Tobias WEBER committed
1005
1006
1007
1008
1009
	{
		m_shadowRenderPass = true;
		DoPaintGL(pGl);
		m_shadowRenderPass = false;
	}
Tobias WEBER's avatar
Tobias WEBER committed
1010

Tobias WEBER's avatar
Tobias WEBER committed
1011
	QPainter painter(this);
Tobias WEBER's avatar
Tobias WEBER committed
1012
1013
	painter.setRenderHint(QPainter::Antialiasing);

Tobias WEBER's avatar
Tobias WEBER committed
1014
	// gl main render pass
Tobias WEBER's avatar
Tobias WEBER committed
1015
	{
1016
1017
		if(m_pickerNeedsUpdate)
			UpdatePicker();
Tobias WEBER's avatar
Tobias WEBER committed
1018

Tobias WEBER's avatar
updates    
Tobias WEBER committed
1019
		BOOST_SCOPE_EXIT(&painter) { painter.endNativePainting(); } BOOST_SCOPE_EXIT_END
Tobias WEBER's avatar
Tobias WEBER committed
1020
		painter.beginNativePainting();
Tobias WEBER's avatar
Tobias WEBER committed
1021

Tobias WEBER's avatar
Tobias WEBER committed
1022
1023
1024
		DoPaintGL(pGl);
	}

Tobias WEBER's avatar
Tobias WEBER committed
1025
	// qt painting pass
Tobias WEBER's avatar
Tobias WEBER committed
1026
1027
1028
	{
		DoPaintQt(painter);
	}
1029
1030
1031
}


Tobias WEBER's avatar
updates    
Tobias WEBER committed
1032
1033
1034
1035
1036
/**
 * pure gl drawing
 */
void PathsRenderer::DoPaintGL(qgl_funcs *pGl)
{
Tobias WEBER's avatar
Tobias WEBER committed
1037
	BOOST_SCOPE_EXIT(m_pfboshadow, pGl)
Tobias WEBER's avatar
Tobias WEBER committed
1038
	{
Tobias WEBER's avatar
Tobias WEBER committed
1039
		pGl->glBindTexture(GL_TEXTURE_2D, 0);
Tobias WEBER's avatar
Tobias WEBER committed
1040
		if(m_pfboshadow)
Tobias WEBER's avatar
Tobias WEBER committed
1041
1042
			m_pfboshadow->release();
	} BOOST_SCOPE_EXIT_END
Tobias WEBER's avatar
Tobias WEBER committed
1043

Tobias WEBER's avatar
Tobias WEBER committed
1044
	if(m_shadowRenderingEnabled)
Tobias WEBER's avatar
Tobias WEBER committed
1045
	{
Tobias WEBER's avatar
Tobias WEBER committed
1046
1047
		if(m_shadowRenderPass)
		{
Tobias WEBER's avatar
Tobias WEBER committed
1048
			if(m_shadowFramebufferNeedsUpdate)
Tobias WEBER's avatar
Tobias WEBER committed
1049
				UpdateShadowFramebuffer();
Tobias WEBER's avatar
Tobias WEBER committed
1050

Tobias WEBER's avatar
Tobias WEBER committed
1051
1052
1053
1054
1055
1056
1057
1058
			if(m_pfboshadow)
				m_pfboshadow->bind();
		}
		else
		{
			if(m_pfboshadow)
				pGl->glBindTexture(GL_TEXTURE_2D, m_pfboshadow->texture());
		}
Tobias WEBER's avatar
Tobias WEBER committed
1059
	}
Tobias WEBER's avatar
Tobias WEBER committed
1060

Tobias WEBER's avatar
updates    
Tobias WEBER committed
1061
1062
1063
1064
1065
1066
1067
1068
	// default options
	pGl->glCullFace(GL_BACK);
	pGl->glFrontFace(GL_CCW);
	pGl->glEnable(GL_CULL_FACE);

	pGl->glDisable(GL_BLEND);
	//pGl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Tobias WEBER's avatar
Tobias WEBER committed
1069
1070
1071
1072
	if(m_shadowRenderPass)
		pGl->glDisable(GL_MULTISAMPLE);
	else
		pGl->glEnable(GL_MULTISAMPLE);
Tobias WEBER's avatar
updates    
Tobias WEBER committed
1073
1074
1075
1076
1077
1078
	pGl->glEnable(GL_LINE_SMOOTH);
	pGl->glEnable(GL_POLYGON_SMOOTH);
	pGl->glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	pGl->glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

	// clear
Tobias WEBER's avatar
Tobias WEBER committed
1079
	if(m_colliding || !m_in_angular_limits)
1080
		pGl->glClearColor(0.8, 0.8, 0.8, 1.);
Tobias WEBER's avatar
Tobias WEBER committed
1081
1082
	else
		pGl->glClearColor(1., 1., 1., 1.);
Tobias WEBER's avatar
updates    
Tobias WEBER committed
1083
1084
1085
	pGl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	pGl->glEnable(GL_DEPTH_TEST);

1086
	if(m_perspectiveNeedsUpdate)
Tobias WEBER's avatar
Tobias WEBER committed
1087
		UpdatePerspective();
Tobias WEBER's avatar
Tobias WEBER committed
1088
	if(m_viewportNeedsUpdate)
Tobias WEBER's avatar
Tobias WEBER committed
1089
		UpdateViewport();
1090
	if(m_lightsNeedUpdate)
Tobias WEBER's avatar
Tobias WEBER committed
1091
		UpdateLights();
Tobias WEBER's avatar
updates    
Tobias WEBER committed
1092
1093
1094
1095
1096
1097

	// bind shaders
	m_pShaders->bind();
	BOOST_SCOPE_EXIT(m_pShaders) { m_pShaders->release(); } BOOST_SCOPE_EXIT_END
	LOGGLERR(pGl);

Tobias WEBER's avatar
Tobias WEBER committed
1098
	m_pShaders->setUniformValue(m_uniShadowRenderingEnabled, m_shadowRenderingEnabled);
Tobias WEBER's avatar
Tobias WEBER committed
1099
	m_pShaders->setUniformValue(m_uniShadowRenderPass, m_shadowRenderPass);
<