/
PatchNode.cpp
598 lines (501 loc) · 17.7 KB
/
PatchNode.cpp
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
#include "PatchNode.h"
#include "ifilter.h"
#include "ientity.h"
#include "iradiant.h"
#include "icounter.h"
#include "math/Frustum.h"
#include "math/Hash.h"
// Construct a PatchNode with no arguments
PatchNode::PatchNode(patch::PatchDefType type) :
scene::SelectableNode(),
m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)),
m_patch(*this),
_untransformedOriginChanged(true),
_renderableSurfaceSolid(m_patch.getTesselation(), true),
_renderableSurfaceWireframe(m_patch.getTesselation(), false),
_renderableCtrlLattice(m_patch, m_ctrl_instances),
_renderableCtrlPoints(m_patch, m_ctrl_instances)
{
m_patch.setFixedSubdivisions(type == patch::PatchDefType::Def3, Subdivisions(m_patch.getSubdivisions()));
}
// Copy Constructor
PatchNode::PatchNode(const PatchNode& other) :
scene::SelectableNode(other),
scene::Cloneable(other),
Snappable(other),
IPatchNode(other),
SelectionTestable(other),
ComponentSelectionTestable(other),
ComponentEditable(other),
ComponentSnappable(other),
PlaneSelectable(other),
Transformable(other),
m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)),
m_patch(other.m_patch, *this), // create the patch out of the <other> one
_untransformedOriginChanged(true),
_renderableSurfaceSolid(m_patch.getTesselation(), true),
_renderableSurfaceWireframe(m_patch.getTesselation(), false),
_renderableCtrlLattice(m_patch, m_ctrl_instances),
_renderableCtrlPoints(m_patch, m_ctrl_instances)
{
}
scene::INode::Type PatchNode::getNodeType() const
{
return Type::Patch;
}
std::string PatchNode::getFingerprint()
{
constexpr std::size_t SignificantDigits = scene::SignificantFingerprintDoubleDigits;
if (m_patch.getHeight() * m_patch.getWidth() == 0)
{
return std::string(); // empty patches produce an empty fingerprint
}
math::Hash hash;
// Width & Height
hash.addSizet(m_patch.getHeight());
hash.addSizet(m_patch.getWidth());
// Subdivision Settings
if (m_patch.subdivisionsFixed())
{
hash.addSizet(static_cast<std::size_t>(m_patch.getSubdivisions().x()));
hash.addSizet(static_cast<std::size_t>(m_patch.getSubdivisions().y()));
}
// Material Name
hash.addString(m_patch.getShader());
// Combine all control point data
for (const auto& ctrl : m_patch.getControlPoints())
{
hash.addVector3(ctrl.vertex, SignificantDigits);
hash.addDouble(ctrl.texcoord.x(), SignificantDigits);
hash.addDouble(ctrl.texcoord.y(), SignificantDigits);
}
return hash;
}
void PatchNode::updateSelectableControls()
{
// Clear the control instance vector and reserve <size> memory
m_ctrl_instances.clear();
// We link the instances to the working control point set
auto& ctrlPoints = m_patch.getControlPointsTransformed();
m_ctrl_instances.reserve(ctrlPoints.size());
// greebo: Cycle through the patch's control vertices and add them as PatchControlInstance to the vector
// The PatchControlInstance constructor takes a pointer to a PatchControl and the SelectionChanged callback
// The passed callback points back to this class (the member method selectedChangedComponent() is called).
for(auto& ctrl : ctrlPoints)
{
m_ctrl_instances.emplace_back(ctrl,
std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1));
}
}
const AABB& PatchNode::localAABB() const {
return m_patch.localAABB();
}
std::string PatchNode::name() const {
return "Patch";
}
Patch& PatchNode::getPatchInternal() {
return m_patch;
}
IPatch& PatchNode::getPatch() {
return m_patch;
}
// Snappable implementation
void PatchNode::snapto(float snap) {
m_patch.snapto(snap);
}
bool PatchNode::selectedVertices() {
// Cycle through all the instances and return true as soon as the first selected one is found
for (PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) {
if (i->isSelected()) {
return true;
}
}
return false;
}
void PatchNode::snapComponents(float snap) {
// Are there any selected vertices
if (selectedVertices()) {
// Tell the patch to save the current undo state
m_patch.undoSave();
// Cycle through all the selected control instances and snap them to the grid
for (PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) {
if(i->isSelected()) {
i->snapto(snap);
}
}
// Tell the patch that control points have changed
m_patch.controlPointsChanged();
}
}
// Test the Patch instance for selection
void PatchNode::testSelect(Selector& selector, SelectionTest& test)
{
// Check if this patch has a twosided material
const auto& shader = m_patch.getSurfaceShader().getGLShader();
bool isTwosided = shader && shader->getMaterial()->getCullType() == Material::CULL_NONE;
test.BeginMesh(localToWorld(), isTwosided);
// Pass the selection test call to the patch
m_patch.testSelect(selector, test);
}
void PatchNode::selectPlanes(Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback) {
test.BeginMesh(localToWorld());
// Check if the drag planes pass the selection test
m_dragPlanes.selectPlanes(m_patch.localAABB(), selector, test, selectedPlaneCallback);
}
void PatchNode::selectReversedPlanes(Selector& selector, const SelectedPlanes& selectedPlanes) {
// Pass the call to the according dragplane member method
m_dragPlanes.selectReversedPlanes(m_patch.localAABB(), selector, selectedPlanes);
}
void PatchNode::selectCtrl(bool selected) {
// Cycle through all ControlInstances and set them to <select>
for (PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) {
i->setSelected(selected);
}
}
bool PatchNode::isSelectedComponents() const {
// Cycle through all ControlInstances and return true on the first selected one that is found
for (PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) {
if (i->isSelected()) {
return true;
}
}
return false;
}
void PatchNode::setSelectedComponents(bool select, selection::ComponentSelectionMode mode) {
// If we are in vertex edit mode, set the control vertices to <select>
if (mode == selection::ComponentSelectionMode::Vertex) {
selectCtrl(select);
}
// If we are in vertex edit mode, set the dragplanes to <select>
else if (mode == selection::ComponentSelectionMode::Face) {
m_dragPlanes.setSelected(select);
}
}
void PatchNode::invertSelectedComponents(selection::ComponentSelectionMode mode)
{
if (mode == selection::ComponentSelectionMode::Vertex)
{
// Cycle through the transformed patch vertices and set the colour of all selected control vertices to BLUE (hardcoded)
for (PatchControlInstance& i : m_ctrl_instances)
{
i.setSelected(!i.isSelected());
}
}
}
void PatchNode::testSelectComponents(Selector& selector, SelectionTest& test, selection::ComponentSelectionMode mode)
{
test.BeginMesh(localToWorld());
// Only react to eVertex selection mode
switch(mode) {
case selection::ComponentSelectionMode::Vertex:
{
// Cycle through all the control instances and test them for selection
for (auto& i : m_ctrl_instances)
{
i.testSelect(selector, test);
}
}
break;
default:
break;
}
}
const AABB& PatchNode::getSelectedComponentsBounds() const {
// Create a new axis aligned bounding box
m_aabb_component = AABB();
// Cycle through all the instances and extend the bounding box by using the selected control points
for (PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) {
if (i->isSelected()) {
m_aabb_component.includePoint(i->control.vertex);
}
}
return m_aabb_component;
}
bool PatchNode::isVisible() const
{
return visible() && hasVisibleMaterial();
}
bool PatchNode::hasVisibleMaterial() const
{
return m_patch.getSurfaceShader().getGLShader()->getMaterial()->isVisible();
}
void PatchNode::selectedChangedComponent(const ISelectable& selectable)
{
// We need to update our vertex colours next time we render them
_renderableCtrlPoints.queueUpdate();
// Notify the selection system that this PatchNode was selected. The RadiantSelectionSystem adds
// this to its internal list of selected nodes.
GlobalSelectionSystem().onComponentSelection(SelectableNode::getSelf(), selectable);
}
// Clones this node, allocates a new Node on the heap and passes itself to the constructor of the new node
scene::INodePtr PatchNode::clone() const
{
return std::make_shared<PatchNode>(*this);
}
void PatchNode::onInsertIntoScene(scene::IMapRootNode& root)
{
// Mark the GL shader as used from now on, this is used by the TextureBrowser's filtering
m_patch.getSurfaceShader().setInUse(true);
// When inserting a patch into the scene, it gets a parent entity assigned
// The colour of that entity will influence the tesselation's vertex colours
m_patch.queueTesselationUpdate();
_renderableSurfaceSolid.queueUpdate();
_renderableSurfaceWireframe.queueUpdate();
_renderableCtrlLattice.queueUpdate();
m_patch.connectUndoSystem(root.getUndoSystem());
GlobalCounters().getCounter(counterPatches).increment();
// Update the origin information needed for transformations
_untransformedOrigin = worldAABB().getOrigin();
SelectableNode::onInsertIntoScene(root);
}
void PatchNode::onRemoveFromScene(scene::IMapRootNode& root)
{
// De-select this node
setSelected(false);
// De-select all child components as well
setSelectedComponents(false, selection::ComponentSelectionMode::Vertex);
GlobalCounters().getCounter(counterPatches).decrement();
m_patch.disconnectUndoSystem(root.getUndoSystem());
_renderableSurfaceSolid.clear();
_renderableSurfaceWireframe.clear();
_renderableCtrlLattice.clear();
m_patch.getSurfaceShader().setInUse(false);
SelectableNode::onRemoveFromScene(root);
}
bool PatchNode::getIntersection(const Ray& ray, Vector3& intersection)
{
return m_patch.getIntersection(ray, intersection);
}
void PatchNode::onPreRender(const VolumeTest& volume)
{
// Don't do anything when invisible
if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
// Defer the tesselation calculation to the last minute
m_patch.evaluateTransform();
m_patch.updateTesselation();
if (volume.fill())
{
_renderableSurfaceSolid.update(m_patch._shader.getGLShader());
}
else
{
_renderableSurfaceWireframe.update(_renderEntity->getWireShader());
}
if (isSelected() && GlobalSelectionSystem().ComponentMode() == selection::ComponentSelectionMode::Vertex)
{
// Selected patches in component mode render the lattice connecting the control points
_renderableCtrlLattice.update(_ctrlLatticeShader);
_renderableCtrlPoints.update(_ctrlPointShader);
}
else
{
_renderableCtrlPoints.clear();
_renderableCtrlLattice.clear();
// Queue an update the next time it's rendered
_renderableCtrlPoints.queueUpdate();
_renderableCtrlLattice.queueUpdate();
}
}
void PatchNode::renderSolid(IRenderableCollector& collector, const VolumeTest& volume) const
{
// Don't render invisible patches
if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
#ifdef RENDERABLE_GEOMETRY
if (isSelected())
{
// Send the patch geometry for rendering highlights
collector.addGeometry(const_cast<Patch&>(m_patch)._solidRenderable,
IRenderableCollector::Highlight::Primitives | IRenderableCollector::Highlight::Flags::Faces);
}
#endif
assert(_renderEntity); // patches rendered without parent - no way!
#if 0
// Render the patch itself
collector.addRenderable(
*m_patch._shader.getGLShader(), m_patch._solidRenderable,
localToWorld(), this, _renderEntity
);
#endif
#if DEBUG_PATCH_NTB_VECTORS
m_patch._renderableVectors.render(collector, volume, localToWorld());
#endif
}
void PatchNode::renderWireframe(IRenderableCollector& collector, const VolumeTest& volume) const
{
// Don't render invisible shaders
if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
#if 0
const_cast<Patch&>(m_patch).evaluateTransform();
#endif
#if 0
// Render the selected components
renderComponentsSelected(collector, volume);
#endif
}
void PatchNode::renderHighlights(IRenderableCollector& collector, const VolumeTest& volume)
{
// Overlay the selected node with the quadrangulated wireframe
collector.addHighlightRenderable(_renderableSurfaceWireframe, localToWorld());
#if 0
// Render the selected components
renderComponentsSelected(collector, volume);
#endif
}
void PatchNode::setRenderSystem(const RenderSystemPtr& renderSystem)
{
SelectableNode::setRenderSystem(renderSystem);
m_patch.setRenderSystem(renderSystem);
_renderableSurfaceSolid.clear();
_renderableSurfaceWireframe.clear();
_renderableCtrlLattice.clear();
_renderableCtrlPoints.clear();
if (renderSystem)
{
_ctrlPointShader = renderSystem->capture("$POINT");
_ctrlLatticeShader = renderSystem->capture("$LATTICE");
}
else
{
_ctrlPointShader.reset();
_ctrlLatticeShader.reset();
}
}
// Renders the components of this patch instance
void PatchNode::renderComponents(IRenderableCollector& collector, const VolumeTest& volume) const
{
#if 0
// Don't render invisible shaders
if (!m_patch.getSurfaceShader().getGLShader()->getMaterial()->isVisible()) return;
// greebo: Don't know yet, what evaluateTransform() is really doing
const_cast<Patch&>(m_patch).evaluateTransform();
// Only render the components, if we are in the according ComponentMode
if (GlobalSelectionSystem().ComponentMode() == selection::ComponentSelectionMode::Vertex)
{
m_patch.submitRenderablePoints(collector, volume, localToWorld());
}
#endif
}
#if 0
void PatchNode::renderComponentsSelected(IRenderableCollector& collector, const VolumeTest& volume) const
{
const_cast<Patch&>(m_patch).evaluateTransform();
// Rebuild the array of selected control vertices
updateSelectedControlVertices();
// If there are any selected components, add them to the collector
if (!m_render_selected.empty())
{
collector.setHighlightFlag(IRenderableCollector::Highlight::Primitives, false);
collector.addRenderable(*m_state_selpoint, m_render_selected, localToWorld());
}
}
#endif
std::size_t PatchNode::getHighlightFlags()
{
if (!isSelected()) return Highlight::NoHighlight;
return isGroupMember() ? (Highlight::Selected | Highlight::GroupMember) : Highlight::Selected;
}
void PatchNode::evaluateTransform()
{
Matrix4 matrix = calculateTransform();
// Avoid transform calls when an identity matrix is passed,
// this equality check is cheaper than all the stuff going on in transform().
if (matrix == Matrix4::getIdentity()) return;
if (getType() == TRANSFORM_PRIMITIVE)
{
m_patch.transform(matrix);
}
else
{
transformComponents(matrix);
}
}
void PatchNode::transformComponents(const Matrix4& matrix) {
// Are there any selected vertices?
if (selectedVertices())
{
// Set the iterator to the start of the (transformed) control points array
PatchControlIter ctrl = m_patch.getControlPointsTransformed().begin();
// Cycle through the patch control instances and transform the selected ones
// greebo: Have to investigate this further, why there are actually two iterators needed
for (PatchNode::PatchControlInstances::iterator i = m_ctrl_instances.begin();
i != m_ctrl_instances.end(); ++i, ++ctrl)
{
if (i->isSelected())
{
ctrl->vertex = matrix.transformPoint(ctrl->vertex);
}
}
// mark this patch transform as dirty
m_patch.transformChanged();
}
// Also, check if there are any drag planes selected
// this should only be true when the transform is a pure translation.
if (m_dragPlanes.isSelected())
{
m_patch.transform(m_dragPlanes.evaluateTransform(matrix.translation()));
}
}
void PatchNode::_onTransformationChanged()
{
m_patch.transformChanged();
_renderableSurfaceSolid.queueUpdate();
_renderableSurfaceWireframe.queueUpdate();
_renderableCtrlLattice.queueUpdate();
_renderableCtrlPoints.queueUpdate();
}
void PatchNode::_applyTransformation()
{
// First, revert the changes, then recalculate the transformation and then freeze the changes
m_patch.revertTransform();
evaluateTransform();
m_patch.freezeTransform();
_untransformedOriginChanged = true;
}
const Vector3& PatchNode::getUntransformedOrigin()
{
if (_untransformedOriginChanged)
{
_untransformedOriginChanged = false;
_untransformedOrigin = worldAABB().getOrigin();
}
return _untransformedOrigin;
}
void PatchNode::onTesselationChanged()
{
_renderableSurfaceSolid.queueUpdate();
_renderableSurfaceWireframe.queueUpdate();
_renderableCtrlLattice.queueUpdate();
_renderableCtrlPoints.queueUpdate();
}
void PatchNode::onControlPointsChanged()
{
_renderableSurfaceSolid.queueUpdate();
_renderableSurfaceWireframe.queueUpdate();
_renderableCtrlLattice.queueUpdate();
_renderableCtrlPoints.queueUpdate();
}
void PatchNode::onMaterialChanged()
{
_renderableSurfaceSolid.queueUpdate();
_renderableSurfaceWireframe.queueUpdate();
}
void PatchNode::onVisibilityChanged(bool visible)
{
SelectableNode::onVisibilityChanged(visible);
if (!visible)
{
// Disconnect our renderable when the node is hidden
_renderableSurfaceSolid.clear();
_renderableSurfaceWireframe.clear();
_renderableCtrlLattice.clear();
_renderableCtrlPoints.clear();
}
else
{
// Update the vertex buffers next time we need to render
_renderableSurfaceSolid.queueUpdate();
_renderableSurfaceWireframe.queueUpdate();
_renderableCtrlLattice.queueUpdate();
_renderableCtrlPoints.queueUpdate();
}
}