-
Notifications
You must be signed in to change notification settings - Fork 3
/
MD5Camera.hpp
471 lines (416 loc) · 18.2 KB
/
MD5Camera.hpp
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
/**
* @file MD5Camera.hpp
* @brief Concrete MD5 Camera implementation with Perspective Projection
* @author Dr. Jeffrey Paone
*
* @copyright MIT License Copyright (c) 2023 Dr. Jeffrey Paone
*
* These functions, classes, and constants help minimize common
* code that needs to be written.
*/
#ifndef CSCI441_MD5_CAMERA_HPP
#define CSCI441_MD5_CAMERA_HPP
#include "Camera.hpp"
#include <ctime>
#include <fstream>
#include <string>
namespace CSCI441 {
/**
* @class MD5Camera
* @brief A camera that implements the MD5Camera specification
* @note camera direction is controlled by file contents
*/
class MD5Camera final : public CSCI441::Camera {
public:
/**
* @brief what to do when the end of a cut is reached
*/
enum class AdvancementStrategy {
/**
* @brief run through just the initial specified cut, stop when end is reached
*/
RUN_SINGLE_CUT,
/**
* @brief run through just the initial specified cut, looping back to the beginning when the end is reached
*/
LOOP_SINGLE_CUT,
/**
* @brief run through all cuts beginning at initial specified cut, advancing to next cut when the end of the current
* cut is reached and stopping at the end of the last cut
*/
RUN_ALL_CUTS,
/**
* @brief run through all cuts beginning at initial specified cut, advancing to next cut when the end of the current
* cut is reached and looping to the first cut when the last cut is completed
*/
LOOP_ALL_CUTS
};
/**
* @brief must create camera object from parameterized constructor
*/
MD5Camera() = delete;
/**
* creates a MD5Camera object with the specified initial perspective projection
* @param MD5CAMERA_FILE filename of .md5camera file to load
* @param advancementStrategy what to do after last frame of cut - one of RUN_SINGLE_CUT, LOOP_SINGLE_CUT, RUN_ALL_CUTS, LOOP_ALL_CUTS
* @param firstCutToRun index of first cut scene to run (defaults to 0)
* @param aspectRatio aspect ratio of view plane (defaults to 1.0f)
* @param fovy vertical field of view (defaults to 45.0f)
* @param nearClipPlane near z clip plane (defaults to 0.001f)
* @param farClipPlane far z clip plane (defaults to 1000.0f)
* @param INFO if file loading information should be printed to standard out (defaults to true)
* @param ERRORS if file loading errors should be printed to standard error (defaults to true)
* @note field of view specified in degrees
*/
explicit MD5Camera(const char* MD5CAMERA_FILE, AdvancementStrategy advancementStrategy, GLuint firstCutToRun = 0, GLfloat aspectRatio = 1.0f, GLfloat fovy = 45.0f, GLfloat nearClipPlane = 0.001f, GLfloat farClipPlane = 1000.0f, GLboolean INFO = true, GLboolean ERRORS = true);
/**
* @brief deep copy another MD5Camera
*/
MD5Camera(const MD5Camera&);
/**
* @brief deep copy another MD5Camera
* @return
*/
MD5Camera& operator=(const MD5Camera&);
/**
* @brief delete cuts and frames
*/
~MD5Camera() final;
void recomputeOrientation() final {}
void moveForward(GLfloat unused) final;
void moveBackward(GLfloat unused) final;
private:
bool _loadMD5CameraFromFile(const char * MD5CAMERA_FILE, GLboolean INFO = true, GLboolean ERRORS = true);
void _copy(const MD5Camera&);
void _free();
bool _isInitialized{};
struct Frame {
glm::vec3 cameraPosition;
glm::vec3 cameraQuaternion;
GLfloat fieldOfView;
};
GLuint _frameRate{};
GLuint _numFrames{};
GLuint _numCuts{};
GLuint* _cutPositions{};
Frame* _frames{};
GLuint _currentFrameIndex{};
GLuint _currentCutIndex{};
AdvancementStrategy _advancementStrategy;
void _updateCameraAttributesForCurrentFrame();
// vertical field of view stored in degrees
GLfloat _fovy{};
GLfloat _aspectRatio{};
GLfloat _nearClipPlane{};
GLfloat _farClipPlane{};
};
}
inline CSCI441::MD5Camera::MD5Camera(
const char * const MD5CAMERA_FILE,
const AdvancementStrategy advancementStrategy,
const GLuint firstCutToRun,
const GLfloat aspectRatio,
const GLfloat fovy,
const GLfloat nearClipPlane,
const GLfloat farClipPlane,
const GLboolean INFO,
const GLboolean ERRORS
) : _fovy(fovy),
_aspectRatio(aspectRatio),
_nearClipPlane(nearClipPlane),
_farClipPlane(farClipPlane),
_frameRate(60),
_numFrames(0),
_numCuts(0),
_cutPositions(nullptr),
_frames(nullptr),
_currentFrameIndex(0),
_currentCutIndex(firstCutToRun),
_advancementStrategy(advancementStrategy)
{
mProjectionMatrix = glm::perspective(_fovy, _aspectRatio, _nearClipPlane, _farClipPlane);
_isInitialized = _loadMD5CameraFromFile(MD5CAMERA_FILE, INFO, ERRORS);
}
[[maybe_unused]]
inline CSCI441::MD5Camera::MD5Camera(
const MD5Camera& OTHER
) : Camera(OTHER),
_fovy(45.0f),
_aspectRatio(1.0f),
_nearClipPlane(0.001f),
_farClipPlane(1000.0f),
_frameRate(60),
_numFrames(0),
_numCuts(0),
_cutPositions(nullptr),
_frames(nullptr),
_currentFrameIndex(0),
_currentCutIndex(0),
_advancementStrategy(AdvancementStrategy::RUN_SINGLE_CUT)
{
this->_copy(OTHER);
}
inline CSCI441::MD5Camera& CSCI441::MD5Camera::operator=(const MD5Camera& OTHER) {
// guard against self-assignment
if(this != &OTHER) {
this->_free();
this->_copy(OTHER);
}
return *this;
}
inline CSCI441::MD5Camera::~MD5Camera() {
this->_free();
}
inline void CSCI441::MD5Camera::_copy(const MD5Camera& OTHER) {
_fovy = OTHER._fovy;
_aspectRatio = OTHER._aspectRatio;
_nearClipPlane = OTHER._nearClipPlane;
_farClipPlane = OTHER._farClipPlane;
_frameRate = OTHER._frameRate;
_currentFrameIndex = OTHER._currentFrameIndex;
_currentCutIndex = OTHER._currentCutIndex;
_advancementStrategy = OTHER._advancementStrategy;
mProjectionMatrix = OTHER.mProjectionMatrix;
_isInitialized = OTHER._isInitialized;
_numCuts = OTHER._numCuts;
_cutPositions = new GLuint[_numCuts];
for(unsigned int i = 0; i < _numCuts; i++) {
_cutPositions[i] = OTHER._cutPositions[i];
}
_numFrames = OTHER._numFrames;
_frames = new Frame[_numFrames];
for(unsigned int i = 0; i < _numFrames; i++) {
_frames[i] = OTHER._frames[i];
}
}
inline void CSCI441::MD5Camera::_free() {
free( _frames );
free( _cutPositions );
}
inline bool CSCI441::MD5Camera::_loadMD5CameraFromFile(
const char * const MD5CAMERA_FILE,
const GLboolean INFO,
const GLboolean ERRORS
) {
if ( INFO ) fprintf( stdout, "[.md5camera]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=- \n", MD5CAMERA_FILE );
time_t start, end;
time(&start);
std::ifstream md5CameraFile(MD5CAMERA_FILE);
if( !md5CameraFile.is_open() ) {
if (ERRORS) fprintf( stderr, "[.md5camera]: [ERROR]: Could not open \"%s\"\n", MD5CAMERA_FILE );
if ( INFO ) fprintf( stdout, "[.md5camera]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", MD5CAMERA_FILE );
return false;
}
std::string sectionLabel, commandLineStr, brace;
int md5version;
Frame frame = { glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f), 45.0f };
// MD5Version 10
md5CameraFile >> sectionLabel >> md5version;
if(sectionLabel != "MD5Version" || md5version != 10) {
if (ERRORS) fprintf (stderr, "[.md5camera]: [ERROR]: improper MD5Camera version found \"%s %d\"\n", sectionLabel.c_str(), md5version );
if ( INFO ) fprintf( stdout, "[.md5camera]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", MD5CAMERA_FILE );
return false;
}
// commandline "string"
md5CameraFile >> sectionLabel;
getline(md5CameraFile, commandLineStr);
for(unsigned short i = 0; i < 5; i++) {
md5CameraFile >> sectionLabel;
if(sectionLabel == "numFrames") {
// numFrames <integer>
md5CameraFile >> _numFrames;
_frames = (Frame*)malloc(sizeof(Frame) * _numFrames);
} else if(sectionLabel == "frameRate") {
// frameRate <integer>
md5CameraFile >> _frameRate;
} else if(sectionLabel == "numCuts") {
// numCuts <integer>
md5CameraFile >> _numCuts;
_cutPositions = (GLuint*)malloc(sizeof(GLuint) * _numCuts);
} else if(sectionLabel == "cuts") {
// cuts {
// [frameNumber]
// [frameNumber]
// }
md5CameraFile >> brace;
for(unsigned int cutNumber = 0; cutNumber < _numCuts; cutNumber++) {
md5CameraFile >> _cutPositions[cutNumber];
}
md5CameraFile >> brace;
_currentFrameIndex = _cutPositions[_currentCutIndex];
} else if(sectionLabel == "camera") {
// camera {
// ( [x] [y] [z] ) ( [orientation] ) [FOV]
// }
md5CameraFile >> brace;
for(unsigned int frameNumber = 0; frameNumber < _numFrames; frameNumber++) {
md5CameraFile >> brace >> frame.cameraPosition.x >> frame.cameraPosition.y >> frame.cameraPosition.z >> brace;
md5CameraFile >> brace >> frame.cameraQuaternion.x >> frame.cameraQuaternion.y >> frame.cameraQuaternion.z >> brace;
md5CameraFile >> frame.fieldOfView;
}
md5CameraFile >> brace;
} else {
if (ERRORS) fprintf( stderr, "[.md5camera]: [ERROR]: unknown section label found \"%s\"\n", sectionLabel.c_str() );
if ( INFO ) fprintf( stdout, "[.md5camera]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", MD5CAMERA_FILE );
return false;
}
}
if (INFO) {
printf( "[.md5camera]: Camera Stats:\n" );
printf( "[.md5camera]: Num Frames:\t%u\tFrame Rate:\t%u\tNum Cuts: \t%u\n", _numFrames, _frameRate, _numCuts );
}
time(&end);
double seconds = difftime( end, start );
if (INFO) {
printf( "[.md5camera]: Completed in %.3fs\n", seconds );
printf( "[.md5camera]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n\n", MD5CAMERA_FILE );
}
return true;
}
inline void CSCI441::MD5Camera::moveForward(const GLfloat unused) {
// prevent memory errors by checking if file loaded correctly
if( !_isInitialized ) return;
// clamp to current cut ? loop ? advance ?
// check if current frame is at end of overall list of frames
if( _currentFrameIndex == _numFrames - 1) {
switch(_advancementStrategy) {
case AdvancementStrategy::LOOP_ALL_CUTS:
// go back to start of all cuts
_currentCutIndex = 0;
case AdvancementStrategy::LOOP_SINGLE_CUT:
// go to start of current cut
_currentFrameIndex = _cutPositions[ _currentCutIndex ];
break;
case AdvancementStrategy::RUN_SINGLE_CUT:
case AdvancementStrategy::RUN_ALL_CUTS:
// do nothing, at end and not looping
return;
}
}
// check if in last cut, but can't be at the end of the last cut because then we'd be at the end of the overall frames
else if( _currentCutIndex == _numCuts - 1 ) {
// in the middle of the last cut, move forward
_currentFrameIndex++;
}
// otherwise not on final overall frame and not in the last cut
else {
// check if at end of current cut
if( _currentFrameIndex == _cutPositions[_currentCutIndex + 1] - 1 ) {
switch(_advancementStrategy) {
case AdvancementStrategy::RUN_ALL_CUTS:
case AdvancementStrategy::LOOP_ALL_CUTS:
// go to next cut
_currentCutIndex++;
case AdvancementStrategy::LOOP_SINGLE_CUT:
// go to start of current cut
_currentFrameIndex = _cutPositions[ _currentCutIndex ];
break;
case AdvancementStrategy::RUN_SINGLE_CUT:
// do nothing, at end and not looping nor advancing
return;
}
} else {
// in the middle of a cut, move forward
_currentFrameIndex++;
}
}
_updateCameraAttributesForCurrentFrame();
}
inline void CSCI441::MD5Camera::moveBackward(const GLfloat unused) {
// prevent memory errors by checking if file loaded correctly
if( !_isInitialized ) return;
// clamp to current cut ? loop ? advance ?
// check if current frame is at beginning of overall list of frames
if( _currentFrameIndex == 0) {
switch(_advancementStrategy) {
case AdvancementStrategy::LOOP_ALL_CUTS:
// go back to end of all frames
_currentCutIndex = _numFrames-1;
break;
case AdvancementStrategy::LOOP_SINGLE_CUT:
if( _numCuts == 1 ) {
// go back to end of all frames
_currentCutIndex = _numFrames-1;
} else {
// go to end of current cut, which is the frame before the next cut
_currentFrameIndex = _cutPositions[ _currentCutIndex+1 ] - 1;
}
break;
case AdvancementStrategy::RUN_SINGLE_CUT:
case AdvancementStrategy::RUN_ALL_CUTS:
// do nothing, at end and not looping
return;
}
}
// check if in first cut, but can't be at the end of the first cut because then we'd be at the end of the overall frames
else if( _currentCutIndex == 0 ) {
// in the middle of the first cut, move backward
_currentFrameIndex--;
}
// otherwise not on initial overall frame and not in the first cut
else {
// check if at beginning of current cut
if( _currentFrameIndex == _cutPositions[_currentCutIndex] ) {
switch(_advancementStrategy) {
case AdvancementStrategy::RUN_ALL_CUTS:
case AdvancementStrategy::LOOP_ALL_CUTS:
// go to previous cut
_currentCutIndex--;
case AdvancementStrategy::LOOP_SINGLE_CUT:
// go to end of current cut, which is the frame before the next cut
_currentFrameIndex = _cutPositions[ _currentCutIndex+1 ] - 1;
break;
case AdvancementStrategy::RUN_SINGLE_CUT:
// do nothing, at end and not looping nor advancing
return;
}
} else {
// in the middle of a cut, move backward
_currentFrameIndex--;
}
}
_updateCameraAttributesForCurrentFrame();
}
inline void CSCI441::MD5Camera::_updateCameraAttributesForCurrentFrame() {
// get and set camera position for current frame
mCameraPosition = _frames[_currentFrameIndex].cameraPosition;
// get and set camera orientation for current frame
// compute W of quaternion
glm::vec4 q(_frames[_currentFrameIndex].cameraQuaternion, 0.0f);
GLfloat t = 1.0f - (q.x * q.x) - (q.y * q.y) - (q.z * q.z);
if (t < 0.0f)
q.w = 0.0f;
else
q.w = -glm::sqrt(t);
// set direction and look at point
glm::vec3 defaultCameraDirection(0.0f, 0.0f, -1.0f);
glm::vec4 inverseQ(-q.x, -q.y, -q.z, q.w);
inverseQ = glm::normalize(inverseQ);
glm::vec4 tmp( (q.w * defaultCameraDirection.x) + (q.y * defaultCameraDirection.z) - (q.z * defaultCameraDirection.y),
(q.w * defaultCameraDirection.y) + (q.z * defaultCameraDirection.x) - (q.x * defaultCameraDirection.z),
(q.w * defaultCameraDirection.z) + (q.x * defaultCameraDirection.y) - (q.y * defaultCameraDirection.x),
-(q.w * defaultCameraDirection.x) - (q.y * defaultCameraDirection.y) - (q.z * defaultCameraDirection.z) );
glm::vec4 rotatedCameraDirection((tmp.x * inverseQ.w) + (tmp.w * inverseQ.x) + (tmp.y * inverseQ.z) - (tmp.z * inverseQ.y),
(tmp.y * inverseQ.w) + (tmp.w * inverseQ.y) + (tmp.z * inverseQ.x) - (tmp.x * inverseQ.z),
(tmp.z * inverseQ.w) + (tmp.w * inverseQ.z) + (tmp.x * inverseQ.y) - (tmp.y * inverseQ.x),
(tmp.w * inverseQ.w) - (tmp.x * inverseQ.x) - (tmp.y * inverseQ.y) - (tmp.z * inverseQ.z) );
mCameraDirection = glm::vec3( rotatedCameraDirection.x, rotatedCameraDirection.y, rotatedCameraDirection.z );
mCameraLookAtPoint = mCameraPosition + mCameraDirection;
// set up vector
glm::vec3 defaultCameraUpVector(0.0f, 1.0f, 0.0f);
glm::vec4 tmp2( (q.w * defaultCameraUpVector.x) + (q.y * defaultCameraUpVector.z) - (q.z * defaultCameraUpVector.y),
(q.w * defaultCameraUpVector.y) + (q.z * defaultCameraUpVector.x) - (q.x * defaultCameraUpVector.z),
(q.w * defaultCameraUpVector.z) + (q.x * defaultCameraUpVector.y) - (q.y * defaultCameraUpVector.x),
-(q.w * defaultCameraUpVector.x) - (q.y * defaultCameraUpVector.y) - (q.z * defaultCameraUpVector.z) );
glm::vec4 rotatedCameraUpVector((tmp.x * inverseQ.w) + (tmp.w * inverseQ.x) + (tmp.y * inverseQ.z) - (tmp.z * inverseQ.y),
(tmp.y * inverseQ.w) + (tmp.w * inverseQ.y) + (tmp.z * inverseQ.x) - (tmp.x * inverseQ.z),
(tmp.z * inverseQ.w) + (tmp.w * inverseQ.z) + (tmp.x * inverseQ.y) - (tmp.y * inverseQ.x),
(tmp.w * inverseQ.w) - (tmp.x * inverseQ.x) - (tmp.y * inverseQ.y) - (tmp.z * inverseQ.z) );
mCameraUpVector = glm::vec3(rotatedCameraUpVector.x, rotatedCameraUpVector.y, rotatedCameraUpVector.z);
// compute and set view matrix
computeViewMatrix();
// get and set field of view for perspective projection matrix
_fovy = _frames[_currentFrameIndex].fieldOfView;
mProjectionMatrix = glm::perspective(_fovy, _aspectRatio, _nearClipPlane, _farClipPlane);
}
#endif//CSCI441_MD5_CAMERA_HPP