framework2

Форк
0
518 строк · 15.3 Кб
1
#include "ofEasyCam.h"
2
#include "ofMath.h"
3
#include "ofUtils.h"
4
#include "ofGraphicsBaseTypes.h"
5

6
#define GLM_FORCE_CTOR_INIT
7
#include "glm/gtx/vector_angle.hpp"
8
#include <limits>
9

10
// when an ofEasyCam is moving due to momentum, this keeps it
11
// from moving forever by assuming small values are zero.
12
static const float minDifference = 0.1e-5f;
13

14
// this is the default on windows os
15
static const unsigned long doubleclickTime = 200;
16

17
//----------------------------------------
18
ofEasyCam::ofEasyCam(){
19
	reset();
20
	sensitivityTranslate = {1,1,1};
21
	sensitivityRot = {1,1,1};
22

23
    addInteraction(TRANSFORM_TRANSLATE_XY, OF_MOUSE_BUTTON_LEFT,doTranslationKey);
24
	addInteraction(TRANSFORM_ROTATE, OF_MOUSE_BUTTON_LEFT);
25
	addInteraction(TRANSFORM_TRANSLATE_Z, OF_MOUSE_BUTTON_RIGHT);
26
	addInteraction(TRANSFORM_TRANSLATE_XY, OF_MOUSE_BUTTON_MIDDLE);
27
	
28
}
29
//----------------------------------------
30
void ofEasyCam::update(ofEventArgs & args){
31
	if(this->viewport.isZero()){
32
		viewport = getViewport();
33
	}
34
	if(!bDistanceSet && bAutoDistance){
35
		setDistance(getImagePlaneDistance(viewport), true);
36
	}
37
	if(bMouseInputEnabled && events){
38
		if(events->getMousePressed()) {
39
			updateMouse(glm::vec2(events->getMouseX(),events->getMouseY()));
40
		}
41
		if (currentTransformType == TRANSFORM_ROTATE) {
42
			updateRotation();
43
		}else if (currentTransformType == TRANSFORM_TRANSLATE_XY ||
44
				  currentTransformType == TRANSFORM_TRANSLATE_Z  ||
45
				  currentTransformType == TRANSFORM_SCALE) {
46
			updateTranslation();
47
		}
48
	}	
49
}
50

51
//----------------------------------------
52
void ofEasyCam::begin(const ofRectangle & _viewport){
53
	if(!bEventsSet){
54
		setEvents(ofEvents());
55
	}
56
	viewport = _viewport;
57
	ofCamera::begin(viewport);
58
}
59

60
//----------------------------------------
61
void ofEasyCam::reset(){
62
	target.resetTransform();
63

64
	target.setPosition(0, 0, 0);
65
	lookAt(target);
66

67
	resetTransform();
68
	setPosition(0, 0, lastDistance);
69

70
	rot = {0,0,0};
71
	translate = {0,0,0};
72
    
73
    if(bAutoDistance){
74
        bDistanceSet = false;
75
    }
76
	bApplyInertia = false;
77
	currentTransformType = TRANSFORM_NONE;
78
}
79

80
//----------------------------------------
81
void ofEasyCam::setTarget(const glm::vec3& targetPoint){
82
	target.setPosition(targetPoint);
83
	lookAt(target);
84
}
85

86
//----------------------------------------
87
void ofEasyCam::setTarget(ofNode& targetNode){
88
	target = targetNode;
89
	lookAt(target);
90
}
91

92
//----------------------------------------
93
const ofNode& ofEasyCam::getTarget() const{
94
	return target;
95
}
96

97
//----------------------------------------
98
void ofEasyCam::setDistance(float distance){
99
	setDistance(distance, true);
100
}
101

102
//----------------------------------------
103
void ofEasyCam::setDistance(float distance, bool save){//should this be the distance from the camera to the target?
104
	if(distance > 0.0f){
105
		if(save){
106
			this->lastDistance = distance;
107
		}
108
		setPosition(target.getPosition() + (distance * getZAxis()));
109
		bDistanceSet = true;
110
	}
111
}
112

113
//----------------------------------------
114
float ofEasyCam::getDistance() const{
115
	return glm::distance(target.getPosition(), getPosition());
116
}
117

118
//----------------------------------------
119
void ofEasyCam::setAutoDistance(bool bAutoDistance){
120
	this->bAutoDistance = bAutoDistance;
121
	if(bAutoDistance){
122
		bDistanceSet = false;
123
	}
124
}
125

126
//----------------------------------------
127
void ofEasyCam::setDrag(float drag){
128
	this->drag = drag;
129
}
130

131
//----------------------------------------
132
float ofEasyCam::getDrag() const{
133
	return drag;
134
}
135

136
//----------------------------------------
137
void ofEasyCam::setTranslationKey(char key){
138
	doTranslationKey = key;
139
}
140

141
//----------------------------------------
142
char ofEasyCam::getTranslationKey() const{
143
	return doTranslationKey;
144
}
145

146
//----------------------------------------
147
void ofEasyCam::enableMouseInput(){
148
	if(!bMouseInputEnabled && events){
149
		listeners.push(events->update.newListener(this, &ofEasyCam::update));
150
		listeners.push(events->mousePressed.newListener(this, &ofEasyCam::mousePressed));
151
		listeners.push(events->mouseReleased.newListener(this, &ofEasyCam::mouseReleased));
152
		listeners.push(events->mouseScrolled.newListener(this, &ofEasyCam::mouseScrolled));
153
	}
154
	// if enableMouseInput was called within ofApp::setup()
155
	// `events` will still carry a null pointer, and bad things
156
	// will happen. Therefore we only update the flag. 
157
	bMouseInputEnabled = true;
158
	// setEvents() is called upon first load, and will make sure 
159
	// to enable the mouse input once the camera is fully loaded.
160
}
161

162
//----------------------------------------
163
void ofEasyCam::disableMouseInput(){
164
	if(bMouseInputEnabled && events){
165
		listeners.unsubscribeAll();
166
	}
167
	// if disableMouseInput was called within ofApp::setup()
168
	// `events` will still carry a null pointer, and bad things
169
	// will happen. Therefore we only update the flag. 
170
	bMouseInputEnabled = false;
171
	// setEvents() is called upon first load, and will make sure 
172
	// to enable the mouse input once the camera is fully loaded.
173
}
174
//----------------------------------------
175
bool ofEasyCam::getMouseInputEnabled() const{
176
	return bMouseInputEnabled;
177
}
178

179
//----------------------------------------
180
void ofEasyCam::setEvents(ofCoreEvents & _events){
181
	// If en/disableMouseInput were called within ofApp::setup(),
182
	// bMouseInputEnabled will tell us about whether the camera
183
	// mouse input needs to be initialised as enabled or disabled.
184
	// we will still set `events`, so that subsequent enabling
185
	// and disabling can work.
186

187
	// we need a temporary copy of bMouseInputEnabled, since it will 
188
	// get changed by disableMouseInput as a side-effect.
189
	bool wasMouseInputEnabled = bMouseInputEnabled;// || !events;
190
	disableMouseInput();
191
	events = &_events;
192
	if (wasMouseInputEnabled) {
193
		// note: this will set bMouseInputEnabled to true as a side-effect.
194
		enableMouseInput();
195
	}
196
	bEventsSet = true;
197
}
198

199
//----------------------------------------
200
void ofEasyCam::setRotationSensitivity(const glm::vec3& sensitivity){
201
	sensitivityRot = sensitivity;
202
}
203
//----------------------------------------
204
void ofEasyCam::setRotationSensitivity(float x, float y, float z){
205
	setRotationSensitivity({x,y,z});
206
}
207
//----------------------------------------
208
void ofEasyCam::setTranslationSensitivity(const glm::vec3& sensitivity){
209
	sensitivityTranslate = sensitivity;
210
}
211
//----------------------------------------
212
void ofEasyCam::setTranslationSensitivity(float x, float y, float z){
213
	sensitivityTranslate = {x,y,z};
214
}
215

216
//----------------------------------------
217
void ofEasyCam::enableMouseMiddleButton(){
218
	bEnableMouseMiddleButton = true;
219
}
220

221
//----------------------------------------
222
void ofEasyCam::disableMouseMiddleButton(){
223
	bEnableMouseMiddleButton = false;
224
}
225

226
//----------------------------------------
227
bool ofEasyCam::getMouseMiddleButtonEnabled() const{
228
	return bEnableMouseMiddleButton;
229
}
230

231
//----------------------------------------
232
glm::vec3 ofEasyCam::up() const{
233
	if(bRelativeYAxis){
234
		if(bApplyInertia){
235
			return getYAxis();
236
		}else{
237
			return lastPressAxisY;
238
		}
239
	}else{
240
		return upAxis;
241
	}
242
}
243

244
//----------------------------------------
245
void ofEasyCam::setRelativeYAxis(bool relative){
246
	bRelativeYAxis = relative;
247
}
248

249
//----------------------------------------
250
bool ofEasyCam::getRelativeYAxis() const{
251
	return bRelativeYAxis;
252
}
253

254
//----------------------------------------
255
void ofEasyCam::setUpAxis(const glm::vec3 & _up){
256
	upAxis = _up;
257
}
258

259
//----------------------------------------
260
const glm::vec3 & ofEasyCam::getUpAxis() const{
261
	return upAxis;
262
}
263

264
//----------------------------------------
265
void ofEasyCam::enableInertia(){
266
	doInertia = true;
267
}
268

269
//----------------------------------------
270
void ofEasyCam::disableInertia(){
271
	doInertia = false;
272
}
273

274
//----------------------------------------
275
bool ofEasyCam::getInertiaEnabled() const{
276
	return doInertia;
277
}
278

279
//----------------------------------------
280
void ofEasyCam::updateTranslation(){
281
	if(bApplyInertia){
282
		translate *= drag;
283
		if(std::abs(translate.x) <= minDifference && std::abs(translate.y) <= minDifference && std::abs(translate.z) <= minDifference){
284
			translate = {0,0,0};
285
			bApplyInertia = false;
286
			currentTransformType = TRANSFORM_NONE;
287

288
			bIsScrolling = false;
289
			return;
290
		}
291
		move((getXAxis() * translate.x) + (getYAxis() * translate.y) + (getZAxis() * translate.z));
292
	}
293
	if(currentTransformType == TRANSFORM_TRANSLATE_XY ||
294
	   currentTransformType == TRANSFORM_TRANSLATE_Z  ||
295
	   currentTransformType == TRANSFORM_SCALE) {
296
		if(getOrtho()){
297
			//In ortho mode moving along the z axis has no effect besides clipping.
298
			// Instead, scale is applied to achieve the effect of getting near or far from the target.
299
			glm::vec3 mousePre ;
300
			bool bDoScale = (currentTransformType == TRANSFORM_SCALE || currentTransformType == TRANSFORM_TRANSLATE_Z);
301
			if (bDoScale) {
302
				mousePre = screenToWorld(glm::vec3((bIsScrolling?mouseAtScroll:lastPressMouse),0));
303
			}
304
			move(glm::vec3(lastPressAxisX * translate.x) + (lastPressAxisY * translate.y));
305
			if (bDoScale) {
306
				setScale(getScale() + translate.z);
307
				// this move call is to keep the scaling centered below the mouse.
308
				move(mousePre - screenToWorld(glm::vec3((bIsScrolling?mouseAtScroll:lastPressMouse),0)));
309
			}
310
		}else{
311
			move(glm::vec3(lastPressAxisX * translate.x) + (lastPressAxisY * translate.y) + (lastPressAxisZ * translate.z));
312
		}
313
	}
314
	if (bIsScrolling) {
315
		//this it to avoid the transformation to keep on after scrolling ended.
316
		currentTransformType = TRANSFORM_NONE;
317
		bIsScrolling = false;
318
	}
319
}
320

321
//----------------------------------------
322
void ofEasyCam::updateRotation(){
323
	if(bApplyInertia){
324
		rot *=drag;
325
		if(std::abs(rot.x) <= minDifference && std::abs(rot.y) <= minDifference && std::abs(rot.z) <= minDifference){
326
			rot = {0,0,0};
327
			bApplyInertia = false;
328
			currentTransformType = TRANSFORM_NONE;
329
			return;
330
		}
331
		
332
	}
333
	if (bApplyInertia) {
334
		curRot = glm::angleAxis(rot.z, getZAxis()) * glm::angleAxis(rot.y, up()) * glm::angleAxis(rot.x, getXAxis());
335
		rotateAround(curRot, target.getGlobalPosition());
336
		rotate(curRot);
337
	}else{
338
		curRot = glm::angleAxis(rot.z, lastPressAxisZ) * glm::angleAxis(rot.y, up()) * glm::angleAxis(rot.x, lastPressAxisX);
339
		setOrientation(curRot * lastPressOrientation);
340
		setPosition(curRot * (lastPressPosition-target.getGlobalPosition()) + target.getGlobalPosition());
341
	}
342
}
343

344
//----------------------------------------
345
void ofEasyCam::setControlArea(const ofRectangle & _controlArea) {
346
	controlArea = _controlArea;
347
}
348

349
//----------------------------------------
350
void ofEasyCam::clearControlArea() {
351
	controlArea = ofRectangle();
352
}
353

354
//----------------------------------------
355
ofRectangle ofEasyCam::getControlArea() const {
356
	if (controlArea.isZero()) {
357
		if (viewport.isZero()) {
358
			return getRenderer()->getCurrentViewport();
359
		}
360
		return viewport;
361
	}
362
	return controlArea;
363
}
364

365
//----------------------------------------
366
void ofEasyCam::mousePressed(ofMouseEventArgs & mouse){
367
	ofRectangle area = getControlArea();
368
	if(area.inside(mouse.x, mouse.y)){
369
		lastPressMouse = mouse;
370
		prevMouse = mouse;
371
		lastPressAxisX = getXAxis();
372
		lastPressAxisY = getYAxis();
373
		lastPressAxisZ = getZAxis();
374
		lastPressPosition = ofCamera::getGlobalPosition();
375
		lastPressOrientation = ofCamera::getGlobalOrientation();
376

377
		currentTransformType = TRANSFORM_NONE;
378
		if (events) {
379
			for (const auto& i: interactions) {
380
                if (i.mouseButton == mouse.button && ((i.key == -1) || events->getKeyPressed(i.key)) ) {
381
					currentTransformType = i.transformType;
382
					break;
383
				}
384
			}
385
		}
386
		if(currentTransformType == TRANSFORM_ROTATE){
387
			bInsideArcball = glm::length(mouse - glm::vec2(area.getCenter())) < std::min(area.width/2, area.height/2);
388
		}
389
		bApplyInertia = false;
390
	}
391
}
392

393
//----------------------------------------
394
void ofEasyCam::mouseReleased(ofMouseEventArgs & mouse){
395
	ofRectangle area = getControlArea();
396

397
	if(area.inside(mouse)){
398
		// Check if it's double click
399
		unsigned long curTap = ofGetElapsedTimeMillis();
400
		if(lastTap != 0 && curTap - lastTap < doubleclickTime){
401
			reset();
402
			return;
403
		}
404
		lastTap = curTap;
405
	}
406

407
	if(doInertia){
408
		bApplyInertia = true;
409
	}else{
410
		currentTransformType = TRANSFORM_NONE;
411
		rot = {0,0,0};
412
		translate = {0,0,0};
413
	}
414
}
415
//----------------------------------------
416
void ofEasyCam::mouseScrolled(ofMouseEventArgs & mouse){
417
	ofRectangle area = getControlArea();
418
	if(area.inside(mouse)){
419
		mouseVel = mouse  - prevMouse;
420
		prevMouse = mouse;
421
		if (doInertia) {
422
			bApplyInertia = true;
423
		}
424
		lastPressPosition = ofCamera::getGlobalPosition();
425
		lastPressAxisZ = getZAxis();
426
		if (getOrtho()) {
427
			translate.z = sensitivityScroll * mouse.scrollY / viewport.height;
428
			mouseAtScroll = mouse;
429
		}else{
430
			translate.z = mouse.scrollY * 30 * sensitivityTranslate.z * (getDistance() + std::numeric_limits<float>::epsilon())/ area.height;
431
		}
432
		currentTransformType = TRANSFORM_SCALE;
433
		bIsScrolling = true;
434
	}
435
}
436

437
//----------------------------------------
438
void ofEasyCam::updateMouse(const glm::vec2 & mouse){
439
	ofRectangle area = getControlArea();
440
	int vFlip =(isVFlipped()?-1:1);
441

442
	rot = {0,0,0};
443
	translate = {0,0,0};
444
	switch (currentTransformType) {
445
	    case TRANSFORM_ROTATE:
446
			mouseVel = mouse  - lastPressMouse;
447
			if(bInsideArcball){
448
				rot.x = vFlip * -mouseVel.y * sensitivityRot.x * glm::pi<float>() / std::min(area.width, area.height);
449
				rot.y = -mouseVel.x * sensitivityRot.y * glm::pi<float>() / std::min(area.width, area.height);
450
			}else{
451
				glm::vec2 center(area.getCenter());
452
				rot.z = sensitivityRot.z * -vFlip * glm::orientedAngle(glm::normalize(mouse - center),
453
																	   glm::normalize(lastPressMouse - center));
454
			}
455
			break;
456
		case TRANSFORM_TRANSLATE_XY:
457
			mouseVel = mouse  - prevMouse;
458
			if (getOrtho()) {
459
				translate.x = -mouseVel.x * getScale().z;
460
				translate.y = vFlip * mouseVel.y * getScale().z;
461
			}else{
462
				translate.x = -mouseVel.x * sensitivityTranslate.x * 0.5f * (getDistance() + std::numeric_limits<float>::epsilon())/ area.width;
463
				translate.y = vFlip * mouseVel.y * sensitivityTranslate.y* 0.5f * (getDistance() + std::numeric_limits<float>::epsilon())/ area.height;
464
			}
465
			break;
466
		case TRANSFORM_TRANSLATE_Z:
467
			mouseVel = mouse  - prevMouse;
468
			if (getOrtho()) {
469
				translate.z = mouseVel.y * sensitivityScroll / area.height;
470
			}else{
471
				translate.z = mouseVel.y * (sensitivityTranslate.z * 0.7f) * (getDistance() + std::numeric_limits<float>::epsilon())/ area.height;
472
			}
473
			break;
474
        default:
475
			break;
476
	}
477
	prevMouse = mouse;
478
}
479
//----------------------------------------
480
void ofEasyCam::addInteraction(TransformType type, int mouseButton, int key){
481
	if(!hasInteraction(mouseButton, key)){
482
		interactions.push_back(interaction(type, mouseButton, key));
483
	}else{
484
		ofLogNotice("ofEasyCam") << "Can not add interaction. It already exists";
485
	}
486
}
487
//----------------------------------------
488
void ofEasyCam::removeInteraction(TransformType type, int mouseButton, int key){
489
	ofRemove(interactions, [&](interaction & i){ return i.transformType == type && i.mouseButton == mouseButton && i.key ==key;});
490
}
491
//----------------------------------------
492
bool ofEasyCam:: hasInteraction(int mouseButton, int key){
493
	for(const auto& i : interactions){
494
		if(i.mouseButton == mouseButton && i.key == key){
495
			return true;
496
		}
497
	}
498
	return false;
499
}
500
//----------------------------------------
501
bool ofEasyCam:: hasInteraction(TransformType type, int mouseButton, int key){
502
	for(const auto& i : interactions){
503
		if(i.transformType == type && i.mouseButton == mouseButton && i.key == key){
504
			return true;
505
		}
506
	}
507
	return false;
508
}
509
//----------------------------------------
510
void ofEasyCam::removeAllInteractions(){
511
	interactions.clear();
512
}
513
//----------------------------------------
514
void ofEasyCam::onPositionChanged(){
515
	if(!bDistanceSet && bAutoDistance){
516
		bDistanceSet = true;
517
	}
518
}
519

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.