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