-
Notifications
You must be signed in to change notification settings - Fork 47
/
PatchNode.cpp
491 lines (401 loc) · 14.5 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
#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_render_selected(GL_POINTS),
m_patch(*this),
_untransformedOriginChanged(true)
{
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),
LitObject(other),
Transformable(other),
m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)),
m_render_selected(GL_POINTS),
m_patch(other.m_patch, *this), // create the patch out of the <other> one
_untransformedOriginChanged(true)
{
}
PatchNode::~PatchNode()
{}
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::allocate(std::size_t size) {
// Clear the control instance vector and reserve <size> memory
m_ctrl_instances.clear();
m_ctrl_instances.reserve(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(PatchControlIter i = m_patch.begin(); i != m_patch.end(); ++i)
{
m_ctrl_instances.push_back(
PatchControlInstance(*i, 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, SelectionSystem::EComponentMode mode) {
// If we are in vertex edit mode, set the control vertices to <select>
if (mode == SelectionSystem::eVertex) {
selectCtrl(select);
}
// If we are in vertex edit mode, set the dragplanes to <select>
else if (mode == SelectionSystem::eFace) {
m_dragPlanes.setSelected(select);
}
}
void PatchNode::invertSelectedComponents(SelectionSystem::EComponentMode mode)
{
if (mode == SelectionSystem::eVertex)
{
// 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, SelectionSystem::EComponentMode mode) {
test.BeginMesh(localToWorld());
// Only react to eVertex selection mode
switch(mode) {
case SelectionSystem::eVertex: {
// Cycle through all the control instances and test them for selection
for (PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) {
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) {
// 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);
m_patch.connectUndoSystem(root.getUndoChangeTracker());
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, SelectionSystem::eVertex);
GlobalCounters().getCounter(counterPatches).decrement();
m_patch.disconnectUndoSystem(root.getUndoChangeTracker());
m_patch.getSurfaceShader().setInUse(false);
SelectableNode::onRemoveFromScene(root);
}
bool PatchNode::getIntersection(const Ray& ray, Vector3& intersection)
{
return m_patch.getIntersection(ray, intersection);
}
bool PatchNode::intersectsLight(const RendererLight& light) const {
return light.lightAABB().intersects(worldAABB());
}
void PatchNode::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
{
// Don't render invisible shaders
if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
// Defer the tesselation calculation to the last minute
const_cast<Patch&>(m_patch).evaluateTransform();
const_cast<Patch&>(m_patch).updateTesselation();
assert(_renderEntity); // patches rendered without parent - no way!
// Render the patch itself
collector.addRenderable(
*m_patch._shader.getGLShader(), m_patch._solidRenderable,
localToWorld(), this, _renderEntity
);
#if DEBUG_PATCH_NTB_VECTORS
m_patch._renderableVectors.render(collector, volume, localToWorld());
#endif
// Render the selected components
renderComponentsSelected(collector, volume);
}
void PatchNode::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
{
// Don't render invisible shaders
if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
const_cast<Patch&>(m_patch).evaluateTransform();
// Pass the call to the patch instance, it adds the renderable
m_patch.renderWireframe(collector, volume, localToWorld(), *_renderEntity);
// Render the selected components
renderComponentsSelected(collector, volume);
}
void PatchNode::setRenderSystem(const RenderSystemPtr& renderSystem)
{
SelectableNode::setRenderSystem(renderSystem);
m_patch.setRenderSystem(renderSystem);
if (renderSystem)
{
m_state_selpoint = renderSystem->capture("$SELPOINT");
}
else
{
m_state_selpoint.reset();
}
}
// Renders the components of this patch instance
void PatchNode::renderComponents(RenderableCollector& collector, const VolumeTest& volume) const
{
// 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() == SelectionSystem::eVertex)
{
m_patch.submitRenderablePoints(collector, volume, localToWorld());
}
}
void PatchNode::update_selected() const {
// Clear the renderable point vector that represents the selection
m_render_selected.clear();
// Cycle through the transformed patch vertices and set the colour of all selected control vertices to BLUE (hardcoded)
PatchControlConstIter ctrl = m_patch.getControlPointsTransformed().begin();
for (PatchControlInstances::const_iterator i = m_ctrl_instances.begin();
i != m_ctrl_instances.end(); ++i, ++ctrl)
{
if (i->isSelected()) {
const Colour4b colour_selected(0, 0, 0, 255);
// Add this patch control instance to the render list
m_render_selected.push_back(VertexCb(reinterpret_cast<const Vertex3f&>(ctrl->vertex), colour_selected));
}
}
}
void PatchNode::renderComponentsSelected(RenderableCollector& collector, const VolumeTest& volume) const
{
// greebo: Don't know yet, what evaluateTransform() is really doing
const_cast<Patch&>(m_patch).evaluateTransform();
// Rebuild the array of selected control vertices
update_selected();
// If there are any selected components, add them to the collector
if (!m_render_selected.empty())
{
collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
collector.addRenderable(*m_state_selpoint, m_render_selected, localToWorld());
}
}
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.tCol().getVector3()));
}
}
void PatchNode::_onTransformationChanged()
{
m_patch.transformChanged();
}
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;
}