framework2

Форк
0
278 строк · 8.3 Кб
1
#include "ofCamera.h"
2
#include "ofGraphics.h"
3

4
#define GLM_FORCE_CTOR_INIT
5
#include "glm/gtx/transform.hpp"
6
#include "glm/gtc/quaternion.hpp"
7
#include "of3dGraphics.h"
8

9
using std::shared_ptr;
10

11
//----------------------------------------
12
ofCamera::ofCamera() :
13
isOrtho(false),
14
fov(60),
15
nearClip(0),
16
farClip(0),
17
lensOffset(0.0f, 0.0f),
18
forceAspectRatio(false),
19
aspectRatio(4./3.),
20
vFlip(false)
21
{
22
}
23

24
//----------------------------------------
25
ofCamera::~ofCamera() {}
26

27
//----------------------------------------
28
void ofCamera::setFov(float f) {
29
	fov = f;
30
}
31

32
//----------------------------------------
33
void ofCamera::setNearClip(float f) {
34
	nearClip = f;
35
}
36

37
//----------------------------------------
38
void ofCamera::setFarClip(float f) {
39
	farClip = f;
40
}
41

42
//----------------------------------------
43
void ofCamera::setLensOffset(const glm::vec2 & lensOffset){
44
	this->lensOffset = lensOffset;
45
}
46

47
//----------------------------------------
48
void ofCamera::setAspectRatio(float aspectRatio){
49
	this->aspectRatio = aspectRatio;
50
	setForceAspectRatio(true);
51
}
52

53
//----------------------------------------
54
void ofCamera::setForceAspectRatio(bool forceAspectRatio){
55
	this->forceAspectRatio = forceAspectRatio;
56
}
57

58
//----------------------------------------
59
void ofCamera::setupPerspective(bool _vFlip, float fov, float nearDist, float farDist, const glm::vec2 & lensOffset){
60
	ofRectangle orientedViewport = getRenderer()->getNativeViewport();
61
	float eyeX = orientedViewport.width / 2;
62
	float eyeY = orientedViewport.height / 2;
63
	float halfFov = glm::pi<float>() * fov / 360.0f;
64
	float theTan = tanf(halfFov);
65
	float dist = eyeY / theTan;
66

67
	if(nearDist == 0) nearDist = dist / 10.0f;
68
	if(farDist == 0) farDist = dist * 10.0f;
69

70
	setFov(fov);
71
	setNearClip(nearDist);
72
	setFarClip(farDist);
73
	setLensOffset(lensOffset);
74
	setForceAspectRatio(false);
75

76
	setPosition(eyeX,eyeY,dist);
77
	lookAt({eyeX,eyeY,0.f}, {0.f,1.f,0.f});
78
	vFlip = _vFlip;
79
}
80

81
//----------------------------------------
82
void ofCamera::setupOffAxisViewPortal(const glm::vec3 & topLeft, const glm::vec3 & bottomLeft, const glm::vec3 & bottomRight){
83
	auto bottomEdge = bottomRight - bottomLeft; // plane x axis
84
	auto leftEdge = topLeft - bottomLeft; // plane y axis
85
	auto bottomEdgeNorm = glm::normalize(bottomEdge);
86
	auto leftEdgeNorm = glm::normalize(leftEdge);
87
	auto bottomLeftToCam = this->getPosition() - bottomLeft;
88
	
89
	auto cameraLookVector = glm::cross(leftEdgeNorm, bottomEdgeNorm);
90
	auto cameraUpVector = glm::cross(bottomEdgeNorm, cameraLookVector);
91
	
92
	lookAt(cameraLookVector + this->getPosition(), cameraUpVector);
93

94
	//lensoffset
95
	glm::vec2 lensOffset;
96
	lensOffset.x = -glm::dot(bottomLeftToCam, bottomEdgeNorm) * 2.0f / glm::length(bottomEdge) + 1.0f;
97
	lensOffset.y = -glm::dot(bottomLeftToCam, leftEdgeNorm) * 2.0f / glm::length(leftEdge) + 1.0f;
98
	setLensOffset(lensOffset);
99
	setAspectRatio( glm::length(bottomEdge) / glm::length(leftEdge) );
100
	auto distanceAlongOpticalAxis = fabs(glm::dot(bottomLeftToCam, cameraLookVector));
101
	setFov(2.0f * glm::degrees( atan( (glm::length(leftEdge) / 2.0f) / distanceAlongOpticalAxis) ) );
102
}
103

104

105
//----------------------------------------
106
void ofCamera::setVFlip(bool vflip){
107
	vFlip = vflip;
108
}
109

110
//----------------------------------------
111
bool ofCamera::isVFlipped() const{
112
	return vFlip;
113
}
114

115
//----------------------------------------
116
void ofCamera::enableOrtho() {
117
	isOrtho = true;
118
}
119

120
//----------------------------------------
121
void ofCamera::disableOrtho() {
122
	isOrtho = false;
123
}
124

125
//----------------------------------------
126
bool ofCamera::getOrtho() const {
127
	return isOrtho;
128
}
129

130
//----------------------------------------
131
float ofCamera::getImagePlaneDistance(const ofRectangle & viewport) const {
132
	return viewport.height / (2.0f * tanf(glm::pi<float>() * fov / 360.0f));
133
}
134

135
//----------------------------------------
136
void ofCamera::begin(const ofRectangle & viewport) {
137
	ofGetCurrentRenderer()->bind(*this,viewport);
138
}
139

140
// if begin(); pushes first, then we need an end to pop
141
//----------------------------------------
142
void ofCamera::end() {
143
	ofGetCurrentRenderer()->unbind(*this);
144
}
145

146
//----------------------------------------
147
glm::mat4 ofCamera::getProjectionMatrix(const ofRectangle & viewport) const {
148
	// autocalculate near/far clip planes if not set by user
149
	const_cast<ofCamera*>(this)->calcClipPlanes(viewport);
150

151
	if(isOrtho) {
152
		return glm::translate(glm::mat4(1.0), {-lensOffset.x, -lensOffset.y, 0.f}) * glm::ortho(
153
			- viewport.width/2,
154
			+ viewport.width/2,
155
			- viewport.height/2,
156
			+ viewport.height/2,
157
			nearClip,
158
			farClip
159
		);
160
	}else{
161
		float aspect = forceAspectRatio ? aspectRatio : viewport.width/viewport.height;
162
		auto projection = glm::perspective(glm::radians(fov), aspect, nearClip, farClip);
163
		projection = glm::translate(glm::mat4(1.0), {-lensOffset.x, -lensOffset.y, 0.f}) * projection;
164
		return projection;
165
	}
166
}
167

168
//----------------------------------------
169
glm::mat4 ofCamera::getModelViewMatrix() const {
170
	return glm::inverse(getGlobalTransformMatrix());
171
}
172

173
//----------------------------------------
174
glm::mat4 ofCamera::getModelViewProjectionMatrix(const ofRectangle & viewport) const {
175
	return getProjectionMatrix(viewport) * getModelViewMatrix();
176
}
177

178
//----------------------------------------
179
glm::vec3 ofCamera::worldToScreen(glm::vec3 WorldXYZ, const ofRectangle & viewport) const {
180
	auto CameraXYZ = worldToCamera(WorldXYZ, viewport);
181
	
182
	glm::vec3 ScreenXYZ;
183
	ScreenXYZ.x = (CameraXYZ.x + 1.0f) / 2.0f * viewport.width + viewport.x;
184
	ScreenXYZ.y = (1.0f - CameraXYZ.y) / 2.0f * viewport.height + viewport.y;
185
	ScreenXYZ.z = CameraXYZ.z;
186

187
	return ScreenXYZ;
188
}
189

190
//----------------------------------------
191
glm::vec3 ofCamera::screenToWorld(glm::vec3 ScreenXYZ, const ofRectangle & viewport) const {
192
	//convert from screen to camera
193
	glm::vec3 CameraXYZ;
194
	CameraXYZ.x = 2.0f * (ScreenXYZ.x - viewport.x) / viewport.width - 1.0f;
195
	CameraXYZ.y = 1.0f - 2.0f *(ScreenXYZ.y - viewport.y) / viewport.height;
196
	CameraXYZ.z = ScreenXYZ.z;
197

198
	return cameraToWorld(CameraXYZ, viewport);
199
}
200

201
//----------------------------------------
202
glm::vec3 ofCamera::worldToCamera(glm::vec3 WorldXYZ, const ofRectangle & viewport) const {
203
	auto MVPmatrix = getModelViewProjectionMatrix(viewport);
204
	if(vFlip){
205
		MVPmatrix = glm::scale(glm::mat4(1.0), glm::vec3(1.f,-1.f,1.f)) * MVPmatrix;
206
	}
207
	auto camera = MVPmatrix * glm::vec4(WorldXYZ, 1.0);
208
	return glm::vec3(camera) / camera.w;
209
	
210
}
211

212
//----------------------------------------
213
glm::vec3 ofCamera::cameraToWorld(glm::vec3 CameraXYZ, const ofRectangle & viewport) const {
214
	auto MVPmatrix = getModelViewProjectionMatrix(viewport);
215
	if(vFlip){
216
		MVPmatrix = glm::scale(glm::mat4(1.0), glm::vec3(1.f,-1.f,1.f)) * MVPmatrix;
217
	}
218
	auto world = glm::inverse(MVPmatrix) * glm::vec4(CameraXYZ, 1.0);
219
	return glm::vec3(world) / world.w;
220
}
221

222
//----------------------------------------
223
void ofCamera::calcClipPlanes(const ofRectangle & viewport) {
224
	// autocalculate near/far clip planes if not set by user
225
	if(nearClip == 0 || farClip == 0) {
226
		float dist = getImagePlaneDistance(viewport);
227
		nearClip = (nearClip == 0) ? dist / 100.0f : nearClip;
228
		farClip = (farClip == 0) ? dist * 10.0f : farClip;
229
	}
230
}
231

232
ofRectangle ofCamera::getViewport() const{
233
	return getRenderer()->getCurrentViewport();
234
}
235

236
shared_ptr<ofBaseRenderer> ofCamera::getRenderer() const{
237
	if(!renderer){
238
		return ofGetCurrentRenderer();
239
	}else{
240
		return renderer;
241
	}
242
}
243

244
void ofCamera::setRenderer(shared_ptr<ofBaseRenderer> _renderer){
245
	renderer = _renderer;
246
}
247

248
void ofCamera::drawFrustum(const ofRectangle & viewport) const {
249
	ofPushMatrix();
250

251
	// In World Space, the camera frustum is the "capped pyramid" which 
252
	// contains everything which can be seen from a camera. 
253
	//
254
	// In Clip Space, the frustum is defined as a box with dimensions: -1, -1, -1 to +1, +1, +1.
255
	//
256
	// Much simpler. 
257
	//
258
	// We therefore want to draw our frustum as if in Clip Space. 
259
	// For this, we must find a matrix which will transform Clip Space 
260
	// back to World Space. 
261

262

263
	// Note: globalTransformMatrix == inverse(modelViewMatrix)
264
	glm::mat4 clipSpaceToWorld = getGlobalTransformMatrix() * glm::inverse(getProjectionMatrix(viewport));
265

266
	// Move into Clip Space - this matrix will transform anything we 
267
	// draw from Clip Space to World Space
268
	ofMultMatrix(clipSpaceToWorld);
269

270
	// Anything we draw now is transformed from Clip Space back to World Space
271

272
	ofPushStyle();
273
	ofNoFill();
274
	ofDrawBox(0, 0, 0, 2.f, 2.f, 2.f); // In Clip Space, the frustum is standardised to be a box of dimensions -1,1 in each axis
275
	ofPopStyle();
276

277
	ofPopMatrix();
278
}
279

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

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

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

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