1 /////////////////////////////////////////////////////////////////////////////
2 // Name: oglstuff.cpp
3 // Purpose: OpenGL manager for pyramid sample
4 // Author: Manuel Martin
5 // Created: 2015/01/31
6 // Copyright: (c) 2015 Manuel Martin
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #include <cmath>
11
12 #include "oglstuff.h"
13
14 // External function for GL errors
15 myOGLErrHandler* externalMyOGLErrHandler = NULL;
16
17 // Allow GL errors to be handled in other part of the app.
MyOnGLError(int err,const GLchar * glMsg=NULL)18 bool MyOnGLError(int err, const GLchar* glMsg = NULL)
19 {
20 GLenum GLErrorVal = glGetError();
21
22 if ( err == myoglERR_CLEAR )
23 {
24 // Clear previous errors
25 while ( GLErrorVal != GL_NO_ERROR )
26 GLErrorVal = glGetError();
27 return true;
28 }
29
30 if ( (GLErrorVal == GL_NO_ERROR) && (glMsg == NULL) )
31 return true;
32
33 if ( externalMyOGLErrHandler )
34 {
35 // Use the external error message handler. We pass our err-enum value.
36 externalMyOGLErrHandler(err, GLErrorVal, glMsg);
37 }
38
39 return err == myoglERR_JUSTLOG ? true : false;
40 }
41
42
43 // We do calculations with 'doubles'. We pass 'GLFloats' to the shaders
44 // because OGL added 'doubles' since OGL 4.0, and this sample is for 3.2
45 // Due to asynchronous nature of OGL, we can't not trust in the passed matrix
46 // to be stored by GPU before the passing-function returns. So we don't use
47 // temporary storage, but dedicated matrices
SetAsGLFloat4x4(double * matD,GLfloat * matF,int msize)48 void SetAsGLFloat4x4(double *matD, GLfloat *matF, int msize)
49 {
50 for (int i = 0; i < msize; i++)
51 {
52 matF[i] = (GLfloat) matD[i];
53 }
54 }
55
56 // ----------------------------------------------------------------------------
57 // Data for a regular tetrahedron with edge length 200, centered at the origin
58 // ----------------------------------------------------------------------------
59 const GLfloat gVerts[] = { 100.0f, -40.8248f, -57.7350f,
60 0.0f, -40.8248f, 115.4704f,
61 -100.0f, -40.8248f, -57.7350f,
62 0.0f, 122.4745f, 0.0f };
63
64 // Transparency (to see also inner faces) is in the last triangle only,
65 // so that glEnable(GL_BLEND) works well
66 const GLfloat gColours[] = { 0.0f, 1.0f, 0.0f, 1.0f,
67 1.0f, 0.0f, 0.0f, 1.0f,
68 0.0f, 0.0f, 1.0f, 1.0f,
69 1.0f, 1.0f, 0.0f, 0.3f }; //With transparency
70
71 // Normals heading outside of the tetrahedron
72 const GLfloat gNormals[] = { 0.0f, -1.0f, 0.0f, /* face 0 1 2 */
73 -0.81650f, 0.33333f, 0.47140f, /* face 1 2 3 */
74 0.0f, 0.33333f, -0.94281f, /* face 2 3 0 */
75 0.81650f, 0.33333f, 0.47140f /* face 3 0 1 */ };
76
77 // Order would be important if we were using face culling
78 const GLushort gIndices[] = { 0, 1, 2, 3, 0, 1 };
79
80
81 // ----------------------------------------------------------------------------
82 // Shaders
83 // ----------------------------------------------------------------------------
84 // Note: We use GLSL 1.50 which is the minimum starting with OpenGL >= 3.2 (2009)
85 // Apple supports OpenGL 3.2 since OS X 10.7 "Lion" (2011)
86
87 // Vertex shader for the triangles
88 const GLchar* triangVertexShader =
89 {
90 "#version 150 \n"
91
92 "in vec3 in_Position; \n"
93 "in vec4 in_Colour; \n"
94 "in vec3 in_Normal; \n"
95 "uniform mat4 mMVP; \n"
96 "uniform mat4 mToViewSpace; \n"
97
98 "flat out vec4 theColour; \n"
99 "flat out vec3 theNormal; \n"
100 "out vec3 pointPos; \n"
101
102 "void main(void) \n"
103 "{\n"
104 " gl_Position = mMVP * vec4(in_Position, 1.0); \n"
105 " theColour = in_Colour; \n"
106
107 " // Operations in View Space \n"
108 " vec4 temp4 = mToViewSpace * vec4(in_Position, 1.0); \n"
109 " pointPos = temp4.xyz; \n"
110 " temp4 = mToViewSpace * vec4(in_Normal, 0.0); \n"
111 " theNormal = normalize(temp4.xyz); \n"
112 "}\n"
113 };
114
115 // Common function for fragment shaders
116 const GLchar* illuminationShader =
117 {
118 "#version 150 \n"
119
120 "vec3 Illuminate(in vec4 LiProps, in vec3 LiColour, in vec4 PColour, \n"
121 " in vec3 PNormal, in vec3 PPos) \n"
122 "{\n"
123 " // Ambient illumination. Hardcoded \n"
124 " vec3 liAmbient = vec3(0.2, 0.2, 0.2); \n"
125
126 " // Operations in View Space \n"
127 " vec3 lightDirec = LiProps.xyz - PPos; \n"
128 " float lightDist = length(lightDirec); \n"
129 " // Normalize. Attention: No lightDist > 0 check \n"
130 " lightDirec = lightDirec / lightDist; \n"
131 " // Attenuation. Hardcoded for this sample distances \n"
132 " float attenu = 260.0 / lightDist; \n"
133 " attenu = attenu * attenu; \n"
134
135 " // Lambertian diffuse illumination \n"
136 " float diffuse = dot(lightDirec, PNormal); \n"
137 " diffuse = max(0.0, diffuse); \n"
138 " vec3 liDiffuse = LiColour * LiProps.w * diffuse * attenu; \n"
139
140 " // Gaussian specular illumination. Harcoded values again \n"
141 " // We avoid it for interior faces \n"
142 " vec3 viewDir = vec3(0.0, 0.0, 1.0); \n"
143 " vec3 halfDir = normalize(lightDirec + viewDir); \n"
144 " float angleHalf = acos(dot(halfDir, PNormal)); \n"
145 " float exponent = angleHalf / 0.05; \n"
146 " float specular = 0.0; \n"
147 " if (diffuse > 0.0) \n"
148 " specular = exp(-exponent * exponent); \n"
149
150 " vec3 lightRes = PColour.rgb * ( liAmbient + liDiffuse ); \n"
151 " // Specular colour is quite similar as light colour \n"
152 " lightRes += (0.2 * PColour.xyz + 0.8 * LiColour) * specular * attenu; \n"
153 " lightRes = clamp(lightRes, 0.0, 1.0); \n"
154
155 " return lightRes; \n"
156 "}\n"
157 };
158
159 // Fragment shader for the triangles
160 const GLchar* triangFragmentShader =
161 {
162 "#version 150 \n"
163
164 "uniform vec4 lightProps; // Position in View space, and intensity \n"
165 "uniform vec3 lightColour; \n"
166
167 "flat in vec4 theColour; \n"
168 "flat in vec3 theNormal; \n"
169 "in vec3 pointPos; \n"
170
171 "out vec4 fragColour; \n"
172
173 "// Declare this function \n"
174 "vec3 Illuminate(in vec4 LiProps, in vec3 LiColour, in vec4 PColour, \n"
175 " in vec3 PNormal, in vec3 PPos); \n"
176
177 "void main(void) \n"
178 "{\n"
179 " vec3 lightRes = Illuminate(lightProps, lightColour, theColour, \n"
180 " theNormal, pointPos); \n "
181
182 " fragColour = vec4(lightRes, theColour.a); \n"
183 "}\n"
184 };
185
186 // Vertex shader for strings (textures) with illumination
187 const GLchar* stringsVertexShader =
188 {
189 "#version 150 \n"
190
191 "in vec3 in_sPosition; \n"
192 "in vec3 in_sNormal; \n"
193 "in vec2 in_TextPos; \n"
194 "uniform mat4 mMVP; \n"
195 "uniform mat4 mToViewSpace; \n"
196
197 "flat out vec3 theNormal; \n"
198 "out vec3 pointPos; \n"
199 "out vec2 textCoord; \n"
200
201 "void main(void) \n"
202 "{\n"
203 " gl_Position = mMVP * vec4(in_sPosition, 1.0); \n"
204 " textCoord = in_TextPos; \n"
205
206 " // Operations in View Space \n"
207 " vec4 temp4 = mToViewSpace * vec4(in_sPosition, 1.0); \n"
208 " pointPos = temp4.xyz; \n"
209 " temp4 = mToViewSpace * vec4(in_sNormal, 0.0); \n"
210 " theNormal = normalize(temp4.xyz); \n"
211 "}\n"
212 };
213
214 // Fragment shader for strings (textures) with illumination
215 const GLchar* stringsFragmentShader =
216 {
217 "#version 150 \n"
218
219 "uniform vec4 lightProps; // Position in View space, and intensity \n"
220 "uniform vec3 lightColour; \n"
221 "uniform sampler2D stringTexture; \n"
222
223 "flat in vec3 theNormal; \n"
224 "in vec3 pointPos; \n"
225 "in vec2 textCoord; \n"
226
227 "out vec4 fragColour; \n"
228
229 "// Declare this function \n"
230 "vec3 Illuminate(in vec4 LiProps, in vec3 LiColour, in vec4 PColour, \n"
231 " in vec3 PNormal, in vec3 PPos); \n"
232
233 "void main(void) \n"
234 "{\n"
235 " vec4 colo4 = texture(stringTexture, textCoord); \n"
236 " vec3 lightRes = Illuminate(lightProps, lightColour, colo4, \n"
237 " theNormal, pointPos); \n "
238
239 " fragColour = vec4(lightRes, colo4.a); \n"
240 "}\n"
241 };
242
243 // Vertex shader for immutable strings (textures)
244 const GLchar* stringsImmutableVS =
245 {
246 "#version 150 \n"
247
248 "in vec3 in_sPosition; \n"
249 "in vec2 in_TextPos; \n"
250 "uniform mat4 mMVP; \n"
251 "out vec2 textCoord; \n"
252
253 "void main(void) \n"
254 "{\n"
255 " gl_Position = mMVP * vec4(in_sPosition, 1.0); \n"
256 " textCoord = in_TextPos; \n"
257 "}\n"
258 };
259
260 // Fragment shader for immutable strings (textures)
261 const GLchar* stringsImmutableFS =
262 {
263 "#version 150 \n"
264
265 "uniform sampler2D stringTexture; \n"
266 "in vec2 textCoord; \n"
267 "out vec4 fragColour; \n"
268
269 "void main(void) \n"
270 "{\n"
271 " fragColour= texture(stringTexture, textCoord); \n"
272 "}\n"
273 };
274
275
276 // ----------------------------------------------------------------------------
277 // myOGLShaders
278 // ----------------------------------------------------------------------------
279
myOGLShaders()280 myOGLShaders::myOGLShaders()
281 {
282 m_proId = 0;
283 m_SHAinitializated = false;
284 }
285
~myOGLShaders()286 myOGLShaders::~myOGLShaders()
287 {
288 if ( m_proId )
289 CleanUp();
290 }
291
CleanUp()292 void myOGLShaders::CleanUp()
293 {
294 StopUse();
295
296 glDeleteProgram(m_proId);
297
298 glFlush();
299 }
300
AddCode(const GLchar * shaString,GLenum shaType)301 void myOGLShaders::AddCode(const GLchar* shaString, GLenum shaType)
302 {
303 // The code is a null-terminated string
304 shaShas sv = {0, shaType, shaString};
305 m_shaCode.push_back(sv);
306 }
307
AddAttrib(const std::string & name)308 void myOGLShaders::AddAttrib(const std::string& name)
309 {
310 shaVars sv = {0, name}; //We will set the location later
311 m_shaAttrib.push_back(sv);
312 // We don't check the max number of attribute locations (usually 16)
313 }
314
AddUnif(const std::string & name)315 void myOGLShaders::AddUnif(const std::string& name)
316 {
317 shaVars sv = {0, name};
318 m_shaUnif.push_back(sv);
319 }
320
321 // Inform GL of the locations in program for the vars for buffers used to feed
322 // the shader. We use glBindAttribLocation (before linking the gl program) with
323 // the location we want.
324 // Since GL 3.3 we could avoid this using in the shader "layout(location=x)...".
325 // The same names as in the shader must be previously set with AddAttrib()
SetAttribLocations()326 void myOGLShaders::SetAttribLocations()
327 {
328 GLuint loc = 0;
329 for(shaVars_v::iterator it = m_shaAttrib.begin(); it != m_shaAttrib.end(); ++it)
330 {
331 it->loc = loc++;
332 glBindAttribLocation(m_proId, it->loc, it->name.c_str());
333 }
334 }
335
GetAttribLoc(const std::string & name)336 GLuint myOGLShaders::GetAttribLoc(const std::string& name)
337 {
338 for (shaVars_v::iterator it = m_shaAttrib.begin(); it != m_shaAttrib.end(); ++it)
339 {
340 if ( it->name == name && it->loc != (GLuint)-1 )
341 return it->loc;
342 }
343
344 return (GLuint) -1;
345 }
346
347 // Store the locations in program for uniforms vars
AskUnifLocations()348 bool myOGLShaders::AskUnifLocations()
349 {
350 for (shaVars_v::iterator it = m_shaUnif.begin(); it != m_shaUnif.end(); ++it)
351 {
352 GLint glret = glGetUniformLocation(m_proId, it->name.c_str());
353 if ( glret == -1 )
354 {
355 // Return now, this GPU program can not be used because we will
356 // pass data to unknown/unused uniform locations
357 return false;
358 }
359 it->loc = glret;
360 }
361
362 return true;
363 }
364
GetUnifLoc(const std::string & name)365 GLuint myOGLShaders::GetUnifLoc(const std::string& name)
366 {
367 for (shaVars_v::iterator it = m_shaUnif.begin(); it != m_shaUnif.end(); ++it)
368 {
369 if ( it->name == name && it->loc != (GLuint)-1 )
370 return it->loc;
371 }
372
373 return (GLuint) -1;
374 }
375
376 // Create a GPU program from the given shaders
Init()377 void myOGLShaders::Init()
378 {
379 MyOnGLError(myoglERR_CLEAR); //clear error stack
380
381 bool resC = false;
382 bool resL = false;
383
384 // GLSL code load and compilation
385 for (shaShas_v::iterator it = m_shaCode.begin(); it != m_shaCode.end(); ++it)
386 {
387 it->shaId = glCreateShader(it->typeSha);
388 glShaderSource(it->shaId, 1, &(it->scode), NULL);
389 MyOnGLError(myoglERR_SHADERCREATE);
390
391 resC = Compile(it->shaId);
392 if ( !resC )
393 break;
394 }
395
396 if ( resC )
397 {
398 // The program in the GPU
399 m_proId = glCreateProgram();
400 for (shaShas_v::iterator it = m_shaCode.begin(); it != m_shaCode.end(); ++it)
401 {
402 glAttachShader(m_proId, it->shaId);
403 }
404
405 SetAttribLocations(); //Before linking
406
407 resL = LinkProg(m_proId);
408 }
409
410 // We don't need them any more
411 for (shaShas_v::iterator it = m_shaCode.begin(); it != m_shaCode.end(); ++it)
412 {
413 if ( resC && it->shaId )
414 {
415 glDetachShader(m_proId, it->shaId);
416 }
417 glDeleteShader(it->shaId);
418 }
419
420 if ( !resC || !resL )
421 return;
422
423 // Log that shaders are OK
424 MyOnGLError(myoglERR_JUSTLOG, "Shaders successfully compiled and linked.");
425
426 // After linking, we can get locations for uniforms
427 m_SHAinitializated = AskUnifLocations();
428 if ( !m_SHAinitializated )
429 MyOnGLError(myoglERR_SHADERLOCATION, " Unused or unrecognized uniform.");
430 }
431
432 // Useful while developing: show shader compilation errors
Compile(GLuint shaId)433 bool myOGLShaders::Compile(GLuint shaId)
434 {
435 glCompileShader(shaId);
436
437 GLint Param = 0;
438 glGetShaderiv(shaId, GL_COMPILE_STATUS, &Param);
439
440 if ( Param == GL_FALSE )
441 {
442 glGetShaderiv(shaId, GL_INFO_LOG_LENGTH, &Param);
443
444 if ( Param > 0 )
445 {
446 GLchar* InfoLog = new GLchar[Param];
447 int nChars = 0;
448 glGetShaderInfoLog(shaId, Param, &nChars, InfoLog);
449 MyOnGLError(myoglERR_SHADERCOMPILE, InfoLog);
450 delete [] InfoLog;
451 }
452 return false;
453 }
454 return true;
455 }
456
457 // Useful while developing: show shader program linkage errors
LinkProg(GLuint proId)458 bool myOGLShaders::LinkProg(GLuint proId)
459 {
460 glLinkProgram(proId);
461
462 GLint Param = 0;
463 glGetProgramiv(proId, GL_LINK_STATUS, &Param);
464
465 if ( Param == GL_FALSE )
466 {
467 glGetProgramiv(proId, GL_INFO_LOG_LENGTH, &Param);
468
469 if ( Param > 0 )
470 {
471 GLchar* InfoLog = new GLchar[Param];
472 int nChars = 0;
473 glGetProgramInfoLog(proId, Param, &nChars, InfoLog);
474 MyOnGLError(myoglERR_SHADERLINK, InfoLog);
475 delete [] InfoLog;
476 }
477 return false;
478 }
479 return true;
480 }
481
Use()482 bool myOGLShaders::Use()
483 {
484 if ( !m_SHAinitializated )
485 return false;
486
487 glUseProgram(m_proId);
488 return true;
489 }
490
StopUse()491 void myOGLShaders::StopUse()
492 {
493 glUseProgram(0);
494 }
495
496 // Disable generic attributes from VAO.
497 // This should be needed only for some old card, which uses generic into VAO
DisableGenericVAA()498 void myOGLShaders::DisableGenericVAA()
499 {
500 for(shaVars_v::iterator it = m_shaAttrib.begin(); it != m_shaAttrib.end(); ++it)
501 {
502 glDisableVertexAttribArray(it->loc);
503 }
504 }
505
506
507 // ----------------------------------------------------------------------------
508 // A point light
509 // ----------------------------------------------------------------------------
510
Set(const myVec3 & position,GLfloat intensity,GLfloat R,GLfloat G,GLfloat B)511 void myLight::Set(const myVec3& position, GLfloat intensity,
512 GLfloat R, GLfloat G, GLfloat B)
513 {
514 m_PosAndIntensisty[0] = (GLfloat) position.x;
515 m_PosAndIntensisty[1] = (GLfloat) position.y;
516 m_PosAndIntensisty[2] = (GLfloat) position.z;
517 m_PosAndIntensisty[3] = (GLfloat) intensity;
518 m_Colour[0] = R;
519 m_Colour[1] = G;
520 m_Colour[2] = B;
521 }
522
523
524 // ----------------------------------------------------------------------------
525 // myOGLTriangles
526 // ----------------------------------------------------------------------------
myOGLTriangles()527 myOGLTriangles::myOGLTriangles()
528 {
529 m_triangVAO = m_bufVertId = m_bufColNorId = m_bufIndexId = 0;
530 m_triangShaders = NULL;
531 }
532
~myOGLTriangles()533 myOGLTriangles::~myOGLTriangles()
534 {
535 Clear();
536 }
537
Clear()538 void myOGLTriangles::Clear()
539 {
540 if ( m_triangShaders )
541 m_triangShaders->DisableGenericVAA();
542
543 // Clear graphics card memory
544 glBindBuffer(GL_ARRAY_BUFFER, 0);
545 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
546 if ( m_bufIndexId )
547 glDeleteBuffers(1, &m_bufIndexId);
548 if ( m_bufColNorId )
549 glDeleteBuffers(1, &m_bufColNorId);
550 if ( m_bufVertId )
551 glDeleteBuffers(1, &m_bufVertId);
552
553 // Unbind from context
554 glBindVertexArray(0);
555 if ( m_triangVAO )
556 glDeleteVertexArrays(1, &m_triangVAO);
557
558 glFlush(); //Tell GL to execute those commands now, but we don't wait for them
559
560 m_triangShaders = NULL;
561 m_triangVAO = m_bufIndexId = m_bufColNorId = m_bufVertId = 0;
562 }
563
SetBuffers(myOGLShaders * theShader,GLsizei nuPoints,GLsizei nuTriangs,const GLfloat * vert,const GLfloat * colo,const GLfloat * norm,const GLushort * indices)564 void myOGLTriangles::SetBuffers(myOGLShaders* theShader,
565 GLsizei nuPoints, GLsizei nuTriangs,
566 const GLfloat* vert, const GLfloat* colo,
567 const GLfloat* norm, const GLushort* indices)
568 {
569 MyOnGLError(myoglERR_CLEAR); //clear error stack
570
571 // NOTE: have you realized that I fully trust on parameters being != 0 and != NULL?
572
573 // Part 1: Buffers - - - - - - - - - - - - - - - - - - -
574
575 // Graphics card buffer for vertices.
576 // Not shared buffer with colours and normals, why not? Just for fun.
577 glGenBuffers(1, &m_bufVertId);
578 glBindBuffer(GL_ARRAY_BUFFER, m_bufVertId);
579 // Populate the buffer with the array "vert"
580 GLsizeiptr nBytes = nuPoints * 3 * sizeof(GLfloat); //3 components {x,y,z}
581 glBufferData(GL_ARRAY_BUFFER, nBytes, vert, GL_STATIC_DRAW);
582
583 if ( ! MyOnGLError(myoglERR_BUFFER) )
584 {
585 // Likely the GPU got out of memory
586 Clear();
587 return;
588 }
589
590 // Graphics card buffer for colours and normals.
591 glGenBuffers(1, &m_bufColNorId);
592 glBindBuffer(GL_ARRAY_BUFFER, m_bufColNorId);
593 // Allocate space for both arrays
594 nBytes = (nuPoints * 4 + nuTriangs * 3) * sizeof(GLfloat);
595 glBufferData(GL_ARRAY_BUFFER, nBytes, NULL, GL_STATIC_DRAW);
596 if ( ! MyOnGLError(myoglERR_BUFFER) )
597 {
598 // Likely the GPU got out of memory
599 Clear();
600 return;
601 }
602 // Populate part of the buffer with the array "colo"
603 nBytes = nuPoints * 4 * sizeof(GLfloat); // rgba components
604 glBufferSubData(GL_ARRAY_BUFFER, 0, nBytes, colo);
605 // Add the array "norm" to the buffer
606 GLsizeiptr bufoffset = nBytes;
607 nBytes = nuTriangs * 3 * sizeof(GLfloat);
608 glBufferSubData(GL_ARRAY_BUFFER, bufoffset, nBytes, norm);
609
610 // Graphics card buffer for indices.
611 glGenBuffers(1, &m_bufIndexId);
612 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bufIndexId);
613 // Populate the buffer with the array "indices"
614 // We use "triangle strip". An index for each additional vertex.
615 nBytes = (3 + nuTriangs - 1) * sizeof(GLushort); //Each triangle needs 3 indices
616 glBufferData(GL_ELEMENT_ARRAY_BUFFER, nBytes, indices, GL_STATIC_DRAW);
617
618 if ( ! MyOnGLError(myoglERR_BUFFER) )
619 {
620 // Likely the GPU got out of memory
621 Clear();
622 return;
623 }
624
625 // Unbind buffers. We will bind them one by one just now, at VAO creation
626 glBindBuffer(GL_ARRAY_BUFFER, 0);
627 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
628
629 m_nuTriangs = nuTriangs;
630 m_triangShaders = theShader;
631
632 MyOnGLError(myoglERR_CLEAR); //clear error stack
633
634 // Part 2: VAO - - - - - - - - - - - - - - - - - - -
635
636 // Vertex Array Object (VAO) that stores the relationship between the
637 // buffers and the shader input attributes
638 glGenVertexArrays(1, &m_triangVAO);
639 glBindVertexArray(m_triangVAO);
640
641 // Set the way of reading (blocks of n floats each) from the current bound
642 // buffer and passing data to the shader (through the index of an attribute).
643 // Vertices positions
644 glBindBuffer(GL_ARRAY_BUFFER, m_bufVertId);
645 GLuint loc = m_triangShaders->GetAttribLoc("in_Position");
646 glEnableVertexAttribArray(loc);
647 glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)0);
648 // Colours
649 glBindBuffer(GL_ARRAY_BUFFER, m_bufColNorId);
650 loc = m_triangShaders->GetAttribLoc("in_Colour");
651 glEnableVertexAttribArray(loc);
652 glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid *)0);
653 // Normals. Their position in buffer starts at bufoffset
654 loc = m_triangShaders->GetAttribLoc("in_Normal");
655 glEnableVertexAttribArray(loc);
656 glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)bufoffset);
657 // Indices
658 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bufIndexId);
659
660 // Unbind
661 glBindVertexArray(0);
662 glBindBuffer(GL_ARRAY_BUFFER, 0);
663 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
664
665 // Some log
666 MyOnGLError(myoglERR_JUSTLOG, "Triangles data loaded into GPU.");
667 }
668
Draw(const GLfloat * unifMvp,const GLfloat * unifToVw,const myLight * theLight)669 void myOGLTriangles::Draw(const GLfloat* unifMvp, const GLfloat* unifToVw,
670 const myLight* theLight)
671 {
672 if ( !m_triangVAO )
673 return;
674
675 MyOnGLError(myoglERR_CLEAR); //clear error stack
676
677 if ( ! m_triangShaders->Use() )
678 return;
679
680 // Bind the source data for the shader
681 glBindVertexArray(m_triangVAO);
682
683 // Pass matrices to the shader in column-major order
684 glUniformMatrix4fv(m_triangShaders->GetUnifLoc("mMVP"), 1, GL_FALSE, unifMvp);
685 glUniformMatrix4fv(m_triangShaders->GetUnifLoc("mToViewSpace"), 1, GL_FALSE, unifToVw);
686 // Pass the light, in View coordinates in this sample
687 glUniform4fv(m_triangShaders->GetUnifLoc("lightProps"), 1, theLight->GetFLightPos());
688 glUniform3fv(m_triangShaders->GetUnifLoc("lightColour"), 1, theLight->GetFLightColour());
689
690 // We have a flat shading, and we want the first vertex data as the flat value
691 glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
692
693 // Indexed drawing the triangles in strip mode, using 6 indices
694 glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, (GLvoid *)0);
695
696 MyOnGLError(myoglERR_DRAWING_TRI);
697
698 // Unbind
699 glBindVertexArray(0);
700 m_triangShaders->StopUse();
701 }
702
703
704 // ----------------------------------------------------------------------------
705 // myOGLString
706 // ----------------------------------------------------------------------------
myOGLString()707 myOGLString::myOGLString()
708 {
709 m_bufPosId = m_textureId = m_stringVAO = m_textureUnit = 0;
710 m_stringShaders = NULL;
711 }
712
~myOGLString()713 myOGLString::~myOGLString()
714 {
715 Clear();
716 }
717
Clear()718 void myOGLString::Clear()
719 {
720 if ( m_stringShaders )
721 m_stringShaders->DisableGenericVAA();
722
723 // Clear graphics card memory
724 glBindBuffer(GL_ARRAY_BUFFER, 0);
725 if ( m_bufPosId )
726 glDeleteBuffers(1, &m_bufPosId);
727
728 // Unbind from context
729 glBindVertexArray(0);
730 glDeleteVertexArrays(1, &m_stringVAO);
731
732 if ( m_textureUnit && m_textureId )
733 {
734 glActiveTexture(GL_TEXTURE0 + m_textureUnit);
735 glBindTexture(GL_TEXTURE_2D, 0);
736 glDeleteTextures(1, &m_textureId);
737 }
738 glActiveTexture(GL_TEXTURE0);
739
740 glFlush(); //Tell GL to execute those commands now, but we don't wait for them
741
742 m_bufPosId = m_textureId = m_stringVAO = m_textureUnit = 0;
743 m_stringShaders = NULL;
744 }
745
SetStringWithVerts(myOGLShaders * theShader,const unsigned char * tImage,int tWidth,int tHeigh,const GLfloat * vert,const GLfloat * norm)746 void myOGLString::SetStringWithVerts(myOGLShaders* theShader,
747 const unsigned char* tImage, int tWidth, int tHeigh,
748 const GLfloat* vert, const GLfloat* norm)
749 {
750 MyOnGLError(myoglERR_CLEAR); //clear error stack
751
752 if ( !tImage )
753 return;
754
755 // Part 1: Buffers - - - - - - - - - - - - - - - - - - -
756
757 // Graphics card buffer for vertices, normals, and texture coords
758 glGenBuffers(1, &m_bufPosId);
759 glBindBuffer(GL_ARRAY_BUFFER, m_bufPosId);
760 // (4+4) (vertices + normals) x 3 components + 4 text-vertices x 2 components
761 GLsizeiptr nBytes = (8 * 3 + 4 * 2) * sizeof(GLfloat);
762 glBufferData(GL_ARRAY_BUFFER, nBytes, NULL, GL_STATIC_DRAW);
763
764 if ( ! MyOnGLError(myoglERR_BUFFER) )
765 {
766 // Likely the GPU got out of memory
767 Clear();
768 return;
769 }
770
771 // Populate part of the buffer with the array "vert"
772 nBytes = 12 * sizeof(GLfloat);
773 glBufferSubData(GL_ARRAY_BUFFER, 0, nBytes, vert);
774 // Add the array "norm" to the buffer
775 GLsizeiptr bufoffset = nBytes;
776 if ( norm )
777 {
778 // Just for string on face, not immutable string
779 glBufferSubData(GL_ARRAY_BUFFER, bufoffset, nBytes, norm);
780 }
781
782 // Add the array of texture coordinates to the buffer.
783 // Order is set accordingly with the vertices
784 // See myOGLManager::SetStringOnPyr()
785 GLfloat texcoords[8] = { 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0 };
786 bufoffset += nBytes;
787 nBytes = 8 * sizeof(GLfloat);
788 glBufferSubData(GL_ARRAY_BUFFER, bufoffset, nBytes, texcoords);
789
790 m_stringShaders = theShader;
791
792 MyOnGLError(myoglERR_CLEAR); //clear error stack
793
794 // Part 2: VAO - - - - - - - - - - - - - - - - - - -
795
796 // Vertex Array Object (VAO) that stores the relationship between the
797 // buffers and the shader input attributes
798 glGenVertexArrays(1, &m_stringVAO);
799 glBindVertexArray(m_stringVAO);
800
801 // Set the way of reading (blocks of n floats each) from the current bound
802 // buffer and passing data to the shader (through the index of an attribute).
803 // Vertices positions
804 GLuint loc = m_stringShaders->GetAttribLoc("in_sPosition");
805 glEnableVertexAttribArray(loc);
806 glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)0);
807 // Normals. Their position in buffer starts at bufoffset
808 bufoffset = 12 * sizeof(GLfloat);
809 if ( norm )
810 {
811 // Just for string on face, not immutable string
812 loc = m_stringShaders->GetAttribLoc("in_sNormal");
813 glEnableVertexAttribArray(loc);
814 glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)bufoffset);
815 }
816 // Texture coordinates
817 bufoffset *= 2; //Normals take same amount of space as vertices
818 loc = m_stringShaders->GetAttribLoc("in_TextPos");
819 glEnableVertexAttribArray(loc);
820 glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid *)bufoffset);
821
822 // Part 3: The texture with the string as an image - - - - - - - -
823
824 // Create the bind for the texture
825 // Same unit for both textures (strings) since their characteristics are the same.
826 m_textureUnit = 1;
827 glActiveTexture(GL_TEXTURE0 + m_textureUnit);
828 glGenTextures(1, &m_textureId); //"Name" of the texture object
829 glBindTexture(GL_TEXTURE_2D, m_textureId);
830 // Avoid some artifacts
831 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
832 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
833 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
834 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
835 // Do this before glTexImage2D because we only have 1 level, no mipmap
836 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
837 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
838 // For RGBA default alignment (4) is good. In other circumstances, we may
839 // need glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
840 // Load texture into card. No mipmap, so 0-level
841 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
842 (GLsizei)tWidth, (GLsizei)tHeigh, 0,
843 GL_RGBA, GL_UNSIGNED_BYTE, tImage);
844 if ( ! MyOnGLError(myoglERR_TEXTIMAGE) )
845 {
846 // Likely the GPU got out of memory
847 Clear();
848 return;
849 }
850
851 // Unbind
852 glBindVertexArray(0);
853 glBindBuffer(GL_ARRAY_BUFFER, 0);
854 glBindTexture(GL_TEXTURE_2D, 0);
855 glActiveTexture(GL_TEXTURE0);
856
857 // Some log
858 MyOnGLError(myoglERR_JUSTLOG, "Texture for string loaded into GPU.");
859 }
860
Draw(const GLfloat * unifMvp,const GLfloat * unifToVw,const myLight * theLight)861 void myOGLString::Draw(const GLfloat* unifMvp, const GLfloat* unifToVw,
862 const myLight* theLight)
863 {
864 if ( !m_stringVAO )
865 return;
866
867 MyOnGLError(myoglERR_CLEAR); //clear error stack
868
869 if ( ! m_stringShaders->Use() )
870 return;
871
872 // Bind the source data for the shader
873 glBindVertexArray(m_stringVAO);
874
875 // Pass matrices to the shader in column-major order
876 glUniformMatrix4fv(m_stringShaders->GetUnifLoc("mMVP"), 1, GL_FALSE, unifMvp);
877 if ( unifToVw && theLight )
878 {
879 // Just for string on face, not immutable string
880 glUniformMatrix4fv(m_stringShaders->GetUnifLoc("mToViewSpace"), 1, GL_FALSE, unifToVw);
881 // Pass the light, in View coordinates in this sample
882 glUniform4fv(m_stringShaders->GetUnifLoc("lightProps"), 1, theLight->GetFLightPos());
883 glUniform3fv(m_stringShaders->GetUnifLoc("lightColour"), 1, theLight->GetFLightColour());
884
885 // We have a flat shading, and we want the first vertex normal as the flat value
886 glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
887 }
888
889 // Use our texture unit
890 glActiveTexture(GL_TEXTURE0 + m_textureUnit);
891 glBindTexture(GL_TEXTURE_2D, m_textureId);
892 // The fragment shader will read texture values (pixels) from the texture
893 // currently active
894 glUniform1i(m_stringShaders->GetUnifLoc("stringTexture"), m_textureUnit);
895
896 // Draw the rectangle made up of two triangles
897 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
898
899 MyOnGLError(myoglERR_DRAWING_STR);
900
901 // Unbind
902 glBindVertexArray(0);
903 glBindTexture(GL_TEXTURE_2D, 0);
904 glActiveTexture(GL_TEXTURE0);
905
906 m_stringShaders->StopUse();
907 }
908
909 // ----------------------------------------------------------------------------
910 // myOGLImmutString
911 // ----------------------------------------------------------------------------
SetImmutString(myOGLShaders * theShader,const unsigned char * tImage,int tWidth,int tHeigh)912 void myOGLImmutString::SetImmutString(myOGLShaders* theShader,
913 const unsigned char* tImage, int tWidth, int tHeigh)
914 {
915 // Make a rectangle of the same size as the image. Order of vertices matters.
916 // Set a 2 pixels margin
917 double imaVerts[12];
918 imaVerts[0] = 2.0 ; imaVerts[1] = 2.0 ; imaVerts[2] = -1.0;
919 imaVerts[3] = 2.0 ; imaVerts[4] = 2.0 + tHeigh; imaVerts[5] = -1.0;
920 imaVerts[6] = 2.0 + tWidth; imaVerts[7] = 2.0 ; imaVerts[8] = -1.0;
921 imaVerts[9] = 2.0 + tWidth; imaVerts[10] = 2.0 + tHeigh; imaVerts[11] = -1.0;
922
923 // GLFloat version
924 GLfloat fimaVerts[12];
925 SetAsGLFloat4x4(imaVerts, fimaVerts, 12);
926
927 // Call the base class without normals, it will handle this case
928 SetStringWithVerts(theShader, tImage, tWidth, tHeigh, fimaVerts, NULL);
929 }
930
SetOrtho(int winWidth,int winHeight)931 void myOGLImmutString::SetOrtho(int winWidth, int winHeight)
932 {
933 // We want an image always of the same size, regardless of window size.
934 // The orthogonal projection with the whole window achieves it.
935 MyOrtho(0.0, winWidth, 0.0, winHeight, -1.0, 1.0, m_dOrtho);
936 // Store the 'float' matrix
937 SetAsGLFloat4x4(m_dOrtho, m_fOrtho, 16);
938 }
939
940
941 // ----------------------------------------------------------------------------
942 // myOGLCamera
943 // ----------------------------------------------------------------------------
myOGLCamera()944 myOGLCamera::myOGLCamera()
945 {
946 m_needMVPUpdate = true; //Matrix must be updated
947 InitPositions();
948 }
949
InitPositions()950 void myOGLCamera::InitPositions()
951 {
952 // We have a tetrahedron centered at origin and edge length = 200
953 m_centerOfWorld.x = m_centerOfWorld.y = m_centerOfWorld.z = 0.0;
954
955 // The radius of the bounding sphere
956 m_radiusOfWorld = 122.4745;
957
958 // From degrees to radians
959 double degToRad = (double) 4.0 * atan(1.0) / 180.0;
960
961 // Angle of the field of view
962 m_fov = 40.0 * degToRad; //radians
963
964 // Position the camera far enough so we can see the whole world.
965 // The camera is between X and Z axis, below the pyramid
966 double tmpv = m_radiusOfWorld / sin(m_fov/2.0);
967 tmpv *= 1.05; // 5% margin
968 m_camPosition.x = m_centerOfWorld.x + tmpv * cos(75.0 * degToRad);
969 m_camPosition.z = m_centerOfWorld.z + tmpv * sin(75.0 * degToRad);
970 m_camPosition.y = m_centerOfWorld.y - m_radiusOfWorld;
971
972 // This camera looks always at center
973 m_camTarget = m_centerOfWorld;
974
975 // A vector perpendicular to Position-Target heading Y+
976 myVec3 vper = MyNormalize(m_camTarget - m_camPosition);
977 m_camUp = myVec3(0.0, 1.0, 0.0);
978 m_camUp = MyCross(m_camUp, vper);
979 m_camUp = MyNormalize( MyCross(vper, m_camUp) );
980
981 tmpv = MyDistance(m_camPosition, m_centerOfWorld);
982 // Calculate distances, not coordinates, with some margins
983 // Near clip-plane distance to the camera
984 m_nearD = tmpv - 1.10 * m_radiusOfWorld - 5.0;
985 // Far clip-plane distance to the camera
986 m_farD = tmpv + 1.10 * m_radiusOfWorld + 5.0;
987
988 // The "View" matrix. We will not change it any more in this sample
989 MyLookAt(m_camPosition, m_camUp, m_camTarget, m_dView);
990
991 // The initial "Model" matrix is the Identity matrix
992 MyRotate(myVec3(0.0, 0.0, 1.0), 0.0, m_dMode);
993
994 // Nothing else. "View" matrix is calculated at ViewSizeChanged()
995 }
996
ViewSizeChanged(int newWidth,int newHeight)997 void myOGLCamera::ViewSizeChanged(int newWidth, int newHeight)
998 {
999 // These values are also used for MouseRotation()
1000 m_winWidth = newWidth;
1001 m_winHeight = newHeight;
1002
1003 // Calculate the projection matrix
1004 double aspect = (double) newWidth / newHeight;
1005 MyPerspective(m_fov, aspect, m_nearD, m_farD, m_dProj);
1006
1007 // Inform we need to calculate MVP matrix
1008 m_needMVPUpdate = true;
1009 }
1010
GetFloatMVP()1011 const GLfloat* myOGLCamera::GetFloatMVP()
1012 {
1013 UpdateMatrices();
1014
1015 return m_fMVP;
1016 }
1017
GetFloatToVw()1018 const GLfloat* myOGLCamera::GetFloatToVw()
1019 {
1020 UpdateMatrices();
1021
1022 return m_fToVw;
1023 }
1024
UpdateMatrices()1025 void myOGLCamera::UpdateMatrices()
1026 {
1027 if ( m_needMVPUpdate )
1028 {
1029 MyMatMul4x4(m_dView, m_dMode, m_dToVw);
1030 MyMatMul4x4(m_dProj, m_dToVw, m_dMVP);
1031 // Store the 'float' matrices
1032 SetAsGLFloat4x4(m_dToVw, m_fToVw, 16);
1033 SetAsGLFloat4x4(m_dMVP, m_fMVP, 16);
1034 m_needMVPUpdate = false;
1035 }
1036 }
1037
MouseRotation(int fromX,int fromY,int toX,int toY)1038 void myOGLCamera::MouseRotation(int fromX, int fromY, int toX, int toY)
1039 {
1040 if ( fromX == toX && fromY == toY )
1041 return; //no rotation
1042
1043 // 1. Obtain axis of rotation and angle simulating a virtual trackball "r"
1044
1045 // 1.1. Calculate normalized coordinates (2x2x2 box).
1046 // The trackball is a part of sphere of radius "r" (the rest is hyperbolic)
1047 // Use r= 0.8 for better maximum rotation (more-less 150 degrees)
1048 double xw1 = (2.0 * fromX - m_winWidth) / m_winWidth;
1049 double yw1 = (2.0 * fromY - m_winHeight) / m_winHeight;
1050 double xw2 = (2.0 * toX - m_winWidth) / m_winWidth;
1051 double yw2 = (2.0 * toY - m_winHeight) / m_winHeight;
1052 double z1 = GetTrackballZ(xw1, yw1, 0.8);
1053 double z2 = GetTrackballZ(xw2, yw2, 0.8);
1054
1055 // 1.2. With normalized vectors, compute axis from 'cross' and angle from 'dot'
1056 myVec3 v1(xw1, yw1, z1);
1057 myVec3 v2(xw2, yw2, z2);
1058 v1 = MyNormalize(v1);
1059 v2 = MyNormalize(v2);
1060 myVec3 axis(MyCross(v1, v2));
1061
1062 // 'axis' is in camera coordinates. Transform it to world coordinates.
1063 double mtmp[16];
1064 MyMatInverse(m_dView, mtmp);
1065 myVec4 res = MyMatMul4x1(mtmp, myVec4(axis));
1066 axis.x = res.x;
1067 axis.y = res.y;
1068 axis.z = res.z;
1069 axis = MyNormalize(axis);
1070
1071 double angle = AngleBetween(v1, v2);
1072
1073 // 2. Compute the model transformation (rotate the model) matrix
1074 MyRotate(axis, angle, mtmp);
1075 // Update "Model" matrix
1076 double mnew[16];
1077 MyMatMul4x4(mtmp, m_dMode, mnew);
1078 for (size_t i = 0; i<16; ++i)
1079 m_dMode[i] = mnew[i];
1080
1081 // Inform we need to calculate MVP matrix
1082 m_needMVPUpdate = true;
1083 }
1084
1085 // Return the orthogonal projection of (x,y) into a sphere centered on the screen
1086 // and radius 'r'. This makes some (x,y) to be outside of circle r='r'. We avoid
1087 // this issue by using a hyperbolic sheet for (x,y) outside of r = 0.707 * 'r'.
GetTrackballZ(double x,double y,double r)1088 double myOGLCamera::GetTrackballZ(double x, double y, double r)
1089 {
1090 double d = x*x + y*y;
1091 double r2 = r*r;
1092 return (d < r2/2.0) ? sqrt(r2 - d) : r2/2.0/sqrt(d);
1093 }
1094
1095
1096 // ----------------------------------------------------------------------------
1097 // myOGLManager
1098 // ----------------------------------------------------------------------------
1099
myOGLManager(myOGLErrHandler * extErrHnd)1100 myOGLManager::myOGLManager(myOGLErrHandler* extErrHnd)
1101 {
1102 externalMyOGLErrHandler = extErrHnd;
1103 MyOnGLError(myoglERR_CLEAR); //clear error stack
1104 }
1105
~myOGLManager()1106 myOGLManager::~myOGLManager()
1107 {
1108 MyOnGLError(myoglERR_CLEAR); //clear error stack
1109
1110 // Force GPU finishing before the context is deleted
1111 glFinish();
1112 }
1113
1114 /* Static */
Init()1115 bool myOGLManager::Init()
1116 {
1117 // Retrieve the pointers to OGL functions we use in this sample
1118 return MyInitGLPointers();
1119 }
1120
GetGLVersion()1121 const GLubyte* myOGLManager::GetGLVersion()
1122 {
1123 return glGetString(GL_VERSION);
1124 }
1125
GetGLVendor()1126 const GLubyte* myOGLManager::GetGLVendor()
1127 {
1128 return glGetString(GL_VENDOR);
1129 }
1130
GetGLRenderer()1131 const GLubyte* myOGLManager::GetGLRenderer()
1132 {
1133 return glGetString(GL_RENDERER);
1134 }
1135
SetShadersAndTriangles()1136 void myOGLManager::SetShadersAndTriangles()
1137 {
1138 // The shaders attributes and uniforms
1139 m_TriangShaders.AddAttrib("in_Position");
1140 m_TriangShaders.AddAttrib("in_Colour");
1141 m_TriangShaders.AddAttrib("in_Normal");
1142 m_TriangShaders.AddUnif("mMVP");
1143 m_TriangShaders.AddUnif("mToViewSpace");
1144 m_TriangShaders.AddUnif("lightProps");
1145 m_TriangShaders.AddUnif("lightColour");
1146 m_TriangShaders.AddCode(triangVertexShader, GL_VERTEX_SHADER);
1147 m_TriangShaders.AddCode(illuminationShader, GL_FRAGMENT_SHADER);
1148 m_TriangShaders.AddCode(triangFragmentShader, GL_FRAGMENT_SHADER);
1149 m_TriangShaders.Init();
1150 m_StringShaders.AddAttrib("in_sPosition");
1151 m_StringShaders.AddAttrib("in_sNormal");
1152 m_StringShaders.AddAttrib("in_TextPos");
1153 m_StringShaders.AddUnif("mMVP");
1154 m_StringShaders.AddUnif("mToViewSpace");
1155 m_StringShaders.AddUnif("lightProps");
1156 m_StringShaders.AddUnif("lightColour");
1157 m_StringShaders.AddUnif("stringTexture");
1158 m_StringShaders.AddCode(stringsVertexShader, GL_VERTEX_SHADER);
1159 m_StringShaders.AddCode(illuminationShader, GL_FRAGMENT_SHADER);
1160 m_StringShaders.AddCode(stringsFragmentShader, GL_FRAGMENT_SHADER);
1161 m_StringShaders.Init();
1162 m_ImmutStringSha.AddAttrib("in_sPosition");
1163 m_ImmutStringSha.AddAttrib("in_TextPos");
1164 m_ImmutStringSha.AddUnif("mMVP");
1165 m_ImmutStringSha.AddUnif("stringTexture");
1166 m_ImmutStringSha.AddCode(stringsImmutableVS, GL_VERTEX_SHADER);
1167 m_ImmutStringSha.AddCode(stringsImmutableFS, GL_FRAGMENT_SHADER);
1168 m_ImmutStringSha.Init();
1169 // The point light. Set its color as full white.
1170 // In this sample we set the light position same as the camera position
1171 // In View space, camera position is {0, 0, 0}
1172 m_Light.Set(myVec3(0.0, 0.0, 0.0), 1.0, 1.0, 1.0, 1.0);
1173 // The triangles data
1174 m_Triangles.SetBuffers(&m_TriangShaders, 4, 4, gVerts, gColours, gNormals, gIndices);
1175 }
1176
SetStringOnPyr(const unsigned char * strImage,int iWidth,int iHeigh)1177 void myOGLManager::SetStringOnPyr(const unsigned char* strImage, int iWidth, int iHeigh)
1178 {
1179 // Some geometry. We want a rectangle close to face 0-1-2 (X-Z plane).
1180 // The rectangle must preserve strImage proportions. If the height of the
1181 // rectangle is "h" and we want to locate it with its largest side parallel
1182 // to the edge of the face and at distance= h/2, then the rectangle width is
1183 // rw = edgeLength - 2 * ((h/2 + h + h/2)/tan60) = edgeLength - 4*h/sqrt(3)
1184 // If h/rw = Prop then
1185 // rw = edgeLength / (1+4/sqrt(3)*Prop) and h = Prop * rw
1186
1187 double edgeLen = MyDistance(myVec3(gVerts[0], gVerts[1], gVerts[2]),
1188 myVec3(gVerts[6], gVerts[7], gVerts[8]));
1189 GLfloat prop = ((GLfloat) iHeigh) / ((GLfloat) iWidth);
1190 GLfloat rw = float(edgeLen) / (1 + 4 * prop / std::sqrt(3.0f));
1191 GLfloat h = prop * rw;
1192 GLfloat de = 2 * h / std::sqrt(3.0f);
1193 // A bit of separation of the face so as to avoid z-fighting
1194 GLfloat rY = gVerts[1] - 0.01f; // Towards outside
1195 GLfloat sVerts[12];
1196 // The image was created top to bottom, but OpenGL axis are bottom to top.
1197 // The image would display upside down. We avoid it choosing the right
1198 // order of vertices and texture coords. See myOGLString::SetStringWithVerts()
1199 sVerts[0] = gVerts[6] + de; sVerts[1] = rY; sVerts[2] = gVerts[8] + h / 2;
1200 sVerts[3] = sVerts[0] ; sVerts[4] = rY; sVerts[5] = sVerts[2] + h;
1201 sVerts[6] = sVerts[0] + rw; sVerts[7] = rY; sVerts[8] = sVerts[2];
1202 sVerts[9] = sVerts[6] ; sVerts[10] = rY; sVerts[11] = sVerts[5];
1203
1204 // Normals for the rectangle illumination, same for the four vertices
1205 const GLfloat strNorms[] = { gNormals[0], gNormals[1], gNormals[2],
1206 gNormals[0], gNormals[1], gNormals[2],
1207 gNormals[0], gNormals[1], gNormals[2],
1208 gNormals[0], gNormals[1], gNormals[2]};
1209
1210 // The texture data for the string on the face of the pyramid
1211 m_StringOnPyr.SetStringWithVerts(&m_StringShaders, strImage, iWidth, iHeigh,
1212 sVerts, strNorms);
1213 }
1214
SetImmutableString(const unsigned char * strImage,int iWidth,int iHeigh)1215 void myOGLManager::SetImmutableString(const unsigned char* strImage,
1216 int iWidth, int iHeigh)
1217 {
1218 m_ImmString.SetImmutString(&m_ImmutStringSha, strImage, iWidth, iHeigh);
1219 }
1220
SetViewport(int x,int y,int width,int height)1221 void myOGLManager::SetViewport(int x, int y, int width, int height)
1222 {
1223 if (width < 1) width = 1;
1224 if (height < 1) height = 1;
1225
1226 glViewport(x, y, (GLsizei)width, (GLsizei)height);
1227
1228 // The camera handles perspective projection
1229 m_Camera.ViewSizeChanged(width, height);
1230 // And this object handles its own orthogonal projection
1231 m_ImmString.SetOrtho(width, height);
1232 }
1233
Render()1234 void myOGLManager::Render()
1235 {
1236 glEnable(GL_DEPTH_TEST);
1237 glDepthFunc(GL_LEQUAL);
1238 glEnable(GL_BLEND);
1239 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1240
1241 glClearColor((GLfloat)0.15, (GLfloat)0.15, 0.0, (GLfloat)1.0); // Dark, but not black.
1242 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1243
1244 m_Triangles.Draw(m_Camera.GetFloatMVP(), m_Camera.GetFloatToVw(), &m_Light);
1245 m_StringOnPyr.Draw(m_Camera.GetFloatMVP(), m_Camera.GetFloatToVw(), &m_Light);
1246 // This string is at the very front, whatever z-coords are given
1247 glDisable(GL_DEPTH_TEST);
1248 m_ImmString.Draw(m_ImmString.GetFloatMVP(), NULL, NULL);
1249 }
1250
OnMouseButDown(int posX,int posY)1251 void myOGLManager::OnMouseButDown(int posX, int posY)
1252 {
1253 // Just save mouse position
1254 m_mousePrevX = posX;
1255 m_mousePrevY = posY;
1256 }
1257
OnMouseRotDragging(int posX,int posY)1258 void myOGLManager::OnMouseRotDragging(int posX, int posY)
1259 {
1260 m_Camera.MouseRotation(m_mousePrevX, m_mousePrevY, posX, posY);
1261 m_mousePrevX = posX;
1262 m_mousePrevY = posY;
1263 }
1264