1 // ----------------------------------------------------------------------------
2 // Another Assimp OpenGL sample including texturing.
3 // Note that it is very basic and will only read and apply the model's diffuse
4 // textures (by their material ids)
5 //
6 // Don't worry about the "Couldn't load Image: ...dwarf2.jpg" Message.
7 // It's caused by a bad texture reference in the model file (I guess)
8 //
9 // If you intend to _use_ this code sample in your app, do yourself a favour
10 // and replace immediate mode calls with VBOs ...
11 //
12 // Thanks to NeHe on whose OpenGL tutorials this one's based on! :)
13 // http://nehe.gamedev.net/
14 // ----------------------------------------------------------------------------
15 #include <windows.h>
16 #include <shellapi.h>
17 #include <stdio.h>
18 #include <GL/gl.h>
19 #include <GL/glu.h>
20 
21 #ifdef _MSC_VER
22 #pragma warning(disable: 4100) // Disable warning 'unreferenced formal parameter'
23 #endif // _MSC_VER
24 
25 #define STB_IMAGE_IMPLEMENTATION
26 #include "contrib/stb/stb_image.h"
27 
28 #ifdef _MSC_VER
29 #pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter'
30 #endif // _MSC_VER
31 
32 #include <fstream>
33 
34 //to map image filenames to textureIds
35 #include <string.h>
36 #include <map>
37 
38 // assimp include files. These three are usually needed.
39 #include <assimp/Importer.hpp>
40 #include <assimp/postprocess.h>
41 #include <assimp/scene.h>
42 #include <assimp/DefaultLogger.hpp>
43 #include <assimp/LogStream.hpp>
44 #include "UTFConverter.h"
45 
46 // The default hard-coded path. Can be overridden by supplying a path through the command line.
47 static std::string modelpath = "../../test/models/OBJ/spider.obj";
48 
49 HGLRC       hRC=nullptr;            // Permanent Rendering Context
50 HDC         hDC=nullptr;            // Private GDI Device Context
51 HWND        g_hWnd=nullptr;         // Holds Window Handle
52 HINSTANCE   g_hInstance=nullptr;    // Holds The Instance Of The Application
53 
54 bool		keys[256];			// Array used for Keyboard Routine;
55 bool		active=TRUE;		// Window Active Flag Set To TRUE by Default
56 bool		fullscreen=TRUE;	// full-screen Flag Set To full-screen By Default
57 
58 GLfloat		xrot;
59 GLfloat		yrot;
60 GLfloat		zrot;
61 
62 
63 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// Declaration For WndProc
64 GLboolean abortGLInit(const char*);
65 
66 const char* windowTitle = "OpenGL Framework";
67 
68 GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };
69 GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };
70 GLfloat LightPosition[]= { 0.0f, 0.0f, 15.0f, 1.0f };
71 
72 
73 
74 // the global Assimp scene object
75 const aiScene* g_scene = nullptr;
76 GLuint scene_list = 0;
77 aiVector3D scene_min, scene_max, scene_center;
78 
79 // images / texture
80 std::map<std::string, GLuint*> textureIdMap;	// map image filenames to textureIds
81 GLuint*		textureIds;							// pointer to texture Array
82 
83 // Create an instance of the Importer class
84 Assimp::Importer importer;
85 
86 using namespace AssimpSamples::SharedCode;
87 
createAILogger()88 void createAILogger()
89 {
90     // Change this line to normal if you not want to analyse the import process
91 	//Assimp::Logger::LogSeverity severity = Assimp::Logger::NORMAL;
92 	Assimp::Logger::LogSeverity severity = Assimp::Logger::VERBOSE;
93 
94 	// Create a logger instance for Console Output
95 	Assimp::DefaultLogger::create("",severity, aiDefaultLogStream_STDOUT);
96 
97 	// Create a logger instance for File Output (found in project folder or near .exe)
98 	Assimp::DefaultLogger::create("assimp_log.txt",severity, aiDefaultLogStream_FILE);
99 
100 	// Now I am ready for logging my stuff
101 	Assimp::DefaultLogger::get()->info("this is my info-call");
102 }
103 
destroyAILogger()104 void destroyAILogger()
105 {
106 	// Kill it after the work is done
107 	Assimp::DefaultLogger::kill();
108 }
109 
logInfo(std::string logString)110 void logInfo(std::string logString)
111 {
112 	// Will add message to File with "info" Tag
113 	Assimp::DefaultLogger::get()->info(logString.c_str());
114 }
115 
logDebug(const char * logString)116 void logDebug(const char* logString)
117 {
118 	// Will add message to File with "debug" Tag
119 	Assimp::DefaultLogger::get()->debug(logString);
120 }
121 
122 
Import3DFromFile(const std::string & pFile)123 bool Import3DFromFile( const std::string& pFile)
124 {
125 	// Check if file exists
126 	std::ifstream fin(pFile.c_str());
127 	if(!fin.fail())
128 	{
129 		fin.close();
130 	}
131 	else
132 	{
133 		MessageBox(nullptr, UTFConverter("Couldn't open file: " + pFile).c_wstr() , TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
134 		logInfo( importer.GetErrorString());
135 		return false;
136 	}
137 
138 	g_scene = importer.ReadFile(pFile, aiProcessPreset_TargetRealtime_Quality);
139 
140 	// If the import failed, report it
141 	if(!g_scene)
142 	{
143 		logInfo( importer.GetErrorString());
144 		return false;
145 	}
146 
147 	// Now we can access the file's contents.
148 	logInfo("Import of scene " + pFile + " succeeded.");
149 
150 	// We're done. Everything will be cleaned up by the importer destructor
151 	return true;
152 }
153 
154 // Resize And Initialize The GL Window
ReSizeGLScene(GLsizei width,GLsizei height)155 void ReSizeGLScene(GLsizei width, GLsizei height)
156 {
157     // Prevent A Divide By Zero By
158 	if (height==0)
159 	{
160         // Making Height Equal One
161         height=1;
162 	}
163 
164 	glViewport(0, 0, width, height);					// Reset The Current Viewport
165 
166 	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
167 	glLoadIdentity();							// Reset The Projection Matrix
168 
169 	// Calculate The Aspect Ratio Of The Window
170 	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
171 
172 	glMatrixMode(GL_MODELVIEW);						// Select The Modelview Matrix
173 	glLoadIdentity();							// Reset The Modelview Matrix
174 }
175 
176 
getBasePath(const std::string & path)177 std::string getBasePath(const std::string& path)
178 {
179 	size_t pos = path.find_last_of("\\/");
180 	return (std::string::npos == pos) ? "" : path.substr(0, pos + 1);
181 }
182 
freeTextureIds()183 void freeTextureIds()
184 {
185 	textureIdMap.clear(); //no need to delete pointers in it manually here. (Pointers point to textureIds deleted in next step)
186 
187 	if (textureIds)
188 	{
189 		delete[] textureIds;
190 		textureIds = nullptr;
191 	}
192 }
193 
LoadGLTextures(const aiScene * scene)194 int LoadGLTextures(const aiScene* scene)
195 {
196 	freeTextureIds();
197 
198 	//ILboolean success;
199 
200 	/* Before calling ilInit() version should be checked. */
201 	/*if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION)
202 	{
203 		/// wrong DevIL version ///
204 		std::string err_msg = "Wrong DevIL version. Old devil.dll in system32/SysWow64?";
205 		char* cErr_msg = (char *) err_msg.c_str();
206 		abortGLInit(cErr_msg);
207 		return -1;
208 	}*/
209 
210 	//ilInit(); /* Initialization of DevIL */
211 
212     if (scene->HasTextures()) return 1;
213         //abortGLInit("Support for meshes with embedded textures is not implemented");
214 
215 	/* getTexture Filenames and Numb of Textures */
216 	for (unsigned int m=0; m<scene->mNumMaterials; m++)
217 	{
218 		int texIndex = 0;
219 		aiReturn texFound = AI_SUCCESS;
220 
221 		aiString path;	// filename
222 
223 		while (texFound == AI_SUCCESS)
224 		{
225 			texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
226 			textureIdMap[path.data] = nullptr; //fill map with textures, pointers still NULL yet
227 			texIndex++;
228 		}
229 	}
230 
231 	const size_t numTextures = textureIdMap.size();
232 
233 
234 	/* array with DevIL image IDs */
235 	//ILuint* imageIds = NULL;
236 //	imageIds = new ILuint[numTextures];
237 
238 	/* generate DevIL Image IDs */
239 //	ilGenImages(numTextures, imageIds); /* Generation of numTextures image names */
240 
241 	/* create and fill array with GL texture ids */
242 	textureIds = new GLuint[numTextures];
243 	glGenTextures(static_cast<GLsizei>(numTextures), textureIds); /* Texture name generation */
244 
245 	/* get iterator */
246 	std::map<std::string, GLuint*>::iterator itr = textureIdMap.begin();
247 
248 	std::string basepath = getBasePath(modelpath);
249 	for (size_t i=0; i<numTextures; i++)
250 	{
251 
252 		//save IL image ID
253 		std::string filename = (*itr).first;  // get filename
254 		(*itr).second =  &textureIds[i];	  // save texture id for filename in map
255 		itr++;								  // next texture
256 
257 
258 		//ilBindImage(imageIds[i]); /* Binding of DevIL image name */
259 		std::string fileloc = basepath + filename;	/* Loading of image */
260 		//success = ilLoadImage(fileloc.c_str());
261         int x, y, n;
262         unsigned char *data = stbi_load(fileloc.c_str(), &x, &y, &n, STBI_rgb_alpha);
263 
264 		if (nullptr != data )
265 		{
266             // Convert every colour component into unsigned byte.If your image contains
267             // alpha channel you can replace IL_RGB with IL_RGBA
268             //success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);
269 			/*if (!success)
270 			{
271 				abortGLInit("Couldn't convert image");
272 				return -1;
273 			}*/
274             // Binding of texture name
275             glBindTexture(GL_TEXTURE_2D, textureIds[i]);
276 			// redefine standard texture values
277             // We will use linear interpolation for magnification filter
278             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
279             // We will use linear interpolation for minifying filter
280             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
281             // Texture specification
282             glTexImage2D(GL_TEXTURE_2D, 0, n, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);// Texture specification.
283 
284             // we also want to be able to deal with odd texture dimensions
285             glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
286             glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
287             glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
288             glPixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
289             stbi_image_free(data);
290         }
291 		else
292 		{
293 			/* Error occurred */
294 			MessageBox(nullptr, UTFConverter("Couldn't load Image: " + fileloc).c_wstr(), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
295 		}
296 	}
297     // Because we have already copied image data into texture data  we can release memory used by image.
298 //	ilDeleteImages(numTextures, imageIds);
299 
300 	// Cleanup
301 	//delete [] imageIds;
302 	//imageIds = NULL;
303 
304 	return TRUE;
305 }
306 
307 // All Setup For OpenGL goes here
InitGL()308 int InitGL()
309 {
310 	if (!LoadGLTextures(g_scene))
311 	{
312 		return FALSE;
313 	}
314 
315 
316 	glEnable(GL_TEXTURE_2D);
317 	glShadeModel(GL_SMOOTH);		 // Enables Smooth Shading
318 	glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
319 	glClearDepth(1.0f);				// Depth Buffer Setup
320 	glEnable(GL_DEPTH_TEST);		// Enables Depth Testing
321 	glDepthFunc(GL_LEQUAL);			// The Type Of Depth Test To Do
322 	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculation
323 
324 
325 	glEnable(GL_LIGHTING);
326 	glEnable(GL_LIGHT0);    // Uses default lighting parameters
327 	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
328 	glEnable(GL_NORMALIZE);
329 
330 	glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
331 	glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
332 	glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
333 	glEnable(GL_LIGHT1);
334 
335 	return TRUE;					// Initialization Went OK
336 }
337 
338 
339 // Can't send color down as a pointer to aiColor4D because AI colors are ABGR.
Color4f(const aiColor4D * color)340 void Color4f(const aiColor4D *color)
341 {
342 	glColor4f(color->r, color->g, color->b, color->a);
343 }
344 
set_float4(float f[4],float a,float b,float c,float d)345 void set_float4(float f[4], float a, float b, float c, float d)
346 {
347 	f[0] = a;
348 	f[1] = b;
349 	f[2] = c;
350 	f[3] = d;
351 }
352 
color4_to_float4(const aiColor4D * c,float f[4])353 void color4_to_float4(const aiColor4D *c, float f[4])
354 {
355 	f[0] = c->r;
356 	f[1] = c->g;
357 	f[2] = c->b;
358 	f[3] = c->a;
359 }
360 
apply_material(const aiMaterial * mtl)361 void apply_material(const aiMaterial *mtl)
362 {
363 	float c[4];
364 
365 	GLenum fill_mode;
366 	int ret1, ret2;
367 	aiColor4D diffuse;
368 	aiColor4D specular;
369 	aiColor4D ambient;
370 	aiColor4D emission;
371 	ai_real shininess, strength;
372 	int two_sided;
373 	int wireframe;
374 	unsigned int max;	// changed: to unsigned
375 
376 	int texIndex = 0;
377 	aiString texPath;	//contains filename of texture
378 
379 	if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath))
380 	{
381 		//bind texture
382 		unsigned int texId = *textureIdMap[texPath.data];
383 		glBindTexture(GL_TEXTURE_2D, texId);
384 	}
385 
386 	set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
387 	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
388 		color4_to_float4(&diffuse, c);
389 	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, c);
390 
391 	set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
392 	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
393 		color4_to_float4(&specular, c);
394 	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
395 
396 	set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
397 	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
398 		color4_to_float4(&ambient, c);
399 	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, c);
400 
401 	set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
402 	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
403 		color4_to_float4(&emission, c);
404 	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, c);
405 
406 	max = 1;
407 	ret1 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
408 	max = 1;
409 	ret2 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
410 	if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS))
411 		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);
412 	else {
413 		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);
414 		set_float4(c, 0.0f, 0.0f, 0.0f, 0.0f);
415 		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
416 	}
417 
418 	max = 1;
419 	if(AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
420 		fill_mode = wireframe ? GL_LINE : GL_FILL;
421 	else
422 		fill_mode = GL_FILL;
423 	glPolygonMode(GL_FRONT_AND_BACK, fill_mode);
424 
425 	max = 1;
426 	if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)
427 		glEnable(GL_CULL_FACE);
428 	else
429 		glDisable(GL_CULL_FACE);
430 }
431 
432 
recursive_render(const struct aiScene * sc,const struct aiNode * nd,float scale)433 void recursive_render (const struct aiScene *sc, const struct aiNode* nd, float scale)
434 {
435 	unsigned int i;
436 	unsigned int n=0, t;
437 	aiMatrix4x4 m = nd->mTransformation;
438 
439 	aiMatrix4x4 m2;
440 	aiMatrix4x4::Scaling(aiVector3D(scale, scale, scale), m2);
441 	m = m * m2;
442 
443 	// update transform
444 	m.Transpose();
445 	glPushMatrix();
446 	glMultMatrixf((float*)&m);
447 
448 	// draw all meshes assigned to this node
449 	for (; n < nd->mNumMeshes; ++n)
450 	{
451 		const struct aiMesh* mesh = sc->mMeshes[nd->mMeshes[n]];
452 
453 		apply_material(sc->mMaterials[mesh->mMaterialIndex]);
454 
455 
456 		if(mesh->mNormals == nullptr)
457 		{
458 			glDisable(GL_LIGHTING);
459 		}
460 		else
461 		{
462 			glEnable(GL_LIGHTING);
463 		}
464 
465 		if(mesh->mColors[0] != nullptr)
466 		{
467 			glEnable(GL_COLOR_MATERIAL);
468 		}
469 		else
470 		{
471 			glDisable(GL_COLOR_MATERIAL);
472 		}
473 
474 		for (t = 0; t < mesh->mNumFaces; ++t) {
475 			const struct aiFace* face = &mesh->mFaces[t];
476 			GLenum face_mode;
477 
478 			switch(face->mNumIndices)
479 			{
480 				case 1: face_mode = GL_POINTS; break;
481 				case 2: face_mode = GL_LINES; break;
482 				case 3: face_mode = GL_TRIANGLES; break;
483 				default: face_mode = GL_POLYGON; break;
484 			}
485 
486 			glBegin(face_mode);
487 
488 			for(i = 0; i < face->mNumIndices; i++)		// go through all vertices in face
489 			{
490 				int vertexIndex = face->mIndices[i];	// get group index for current index
491 				if(mesh->mColors[0] != nullptr)
492 					Color4f(&mesh->mColors[0][vertexIndex]);
493 				if(mesh->mNormals != nullptr)
494 
495 					if(mesh->HasTextureCoords(0))		//HasTextureCoords(texture_coordinates_set)
496 					{
497 						glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x, 1 - mesh->mTextureCoords[0][vertexIndex].y); //mTextureCoords[channel][vertex]
498 					}
499 
500 					glNormal3fv(&mesh->mNormals[vertexIndex].x);
501 					glVertex3fv(&mesh->mVertices[vertexIndex].x);
502 			}
503 			glEnd();
504 		}
505 	}
506 
507 	// draw all children
508 	for (n = 0; n < nd->mNumChildren; ++n)
509 	{
510 		recursive_render(sc, nd->mChildren[n], scale);
511 	}
512 
513 	glPopMatrix();
514 }
515 
516 
drawAiScene(const aiScene * scene)517 void drawAiScene(const aiScene* scene)
518 {
519 	logInfo("drawing objects");
520 
521 	recursive_render(scene, scene->mRootNode, 0.5);
522 
523 }
524 
DrawGLScene()525 int DrawGLScene()				//Here's where we do all the drawing
526 {
527 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
528 	glLoadIdentity();				// Reset MV Matrix
529 
530 
531 	glTranslatef(0.0f, -10.0f, -40.0f);	// Move 40 Units And Into The Screen
532 
533 
534 	glRotatef(xrot, 1.0f, 0.0f, 0.0f);
535 	glRotatef(yrot, 0.0f, 1.0f, 0.0f);
536 	glRotatef(zrot, 0.0f, 0.0f, 1.0f);
537 
538 	drawAiScene(g_scene);
539 
540 	//xrot+=0.3f;
541 	yrot+=0.2f;
542 	//zrot+=0.4f;
543 
544 	return TRUE;					// okay
545 }
546 
547 
KillGLWindow()548 void KillGLWindow()			// Properly Kill The Window
549 {
550 	if (fullscreen)					// Are We In Fullscreen Mode?
551 	{
552 		ChangeDisplaySettings(nullptr, 0);	// If So Switch Back To The Desktop
553 		ShowCursor(TRUE);					// Show Mouse Pointer
554 	}
555 
556 	if (hRC)					// Do We Have A Rendering Context?
557 	{
558 		if (!wglMakeCurrent(nullptr, nullptr))	// Are We Able To Release The DC And RC Contexts?
559 		{
560 			MessageBox(nullptr, TEXT("Release Of DC And RC Failed."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
561 		}
562 
563 		if (!wglDeleteContext(hRC))			// Are We Able To Delete The RC?
564 		{
565 			MessageBox(nullptr, TEXT("Release Rendering Context Failed."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
566 		}
567 		hRC = nullptr;
568 	}
569 
570 	if (hDC)
571 	{
572 		if (!ReleaseDC(g_hWnd, hDC)) // Are We able to Release The DC?
573 			MessageBox(nullptr, TEXT("Release Device Context Failed."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
574 		hDC = nullptr;
575 	}
576 
577 	if (g_hWnd)
578 	{
579 		if (!DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window
580 			MessageBox(nullptr, TEXT("Could Not Release hWnd."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
581 		g_hWnd = nullptr;
582 	}
583 
584 	if (g_hInstance)
585 	{
586 		if (!UnregisterClass(TEXT("OpenGL"), g_hInstance)) // Are We Able To Unregister Class
587 			MessageBox(nullptr, TEXT("Could Not Unregister Class."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
588 		g_hInstance = nullptr;
589 	}
590 }
591 
abortGLInit(const char * abortMessage)592 GLboolean abortGLInit(const char* abortMessage)
593 {
594 	KillGLWindow();									// Reset Display
595 	MessageBox(nullptr, UTFConverter(abortMessage).c_wstr(), TEXT("ERROR"), MB_OK|MB_ICONEXCLAMATION);
596 	return FALSE;									// quit and return False
597 }
598 
CreateGLWindow(const char * title,int width,int height,int bits,bool fullscreenflag)599 BOOL CreateGLWindow(const char* title, int width, int height, int bits, bool fullscreenflag)
600 {
601 	GLuint		PixelFormat;		// Hold the result after searching for a match
602 	WNDCLASS	wc;					// Window Class Structure
603 	DWORD		dwExStyle;			// Window Extended Style
604 	DWORD		dwStyle;			// Window Style
605 	RECT		WindowRect;			// Grabs Rectangle Upper Left / Lower Right Values
606 	WindowRect.left		= (long)0;
607 	WindowRect.right	= (long)width;
608 	WindowRect.top		= (long)0;
609 	WindowRect.bottom	= (long)height;
610 
611 	fullscreen = fullscreenflag;
612 
613 	g_hInstance = GetModuleHandle(nullptr);	// Grab An Instance For Our Window
614 	wc.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Move, And Own DC For Window
615 	wc.lpfnWndProc	= (WNDPROC) WndProc;		// WndProc handles Messages
616 	wc.cbClsExtra	= 0;	// No Extra Window Data
617 	wc.cbWndExtra	= 0;	// No Extra Window Data
618 	wc.hInstance	= g_hInstance;
619 	wc.hIcon		= LoadIcon(nullptr, IDI_WINLOGO);	// Load The Default Icon
620 	wc.hCursor		= LoadCursor(nullptr, IDC_ARROW);	// Load the default arrow
621 	wc.hbrBackground= nullptr;						// No Background required for OpenGL
622 	wc.lpszMenuName	= nullptr;						// No Menu
623 	wc.lpszClassName= TEXT("OpenGL");		        // Class Name
624 
625 	if (!RegisterClass(&wc))
626 	{
627 		MessageBox(nullptr, TEXT("Failed to register the window class"), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
628 		return FALSE;		//exit and return false
629 	}
630 
631 	if (fullscreen)		// attempt fullscreen mode
632 	{
633 		DEVMODE dmScreenSettings;								// Device Mode
634 		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));	// Make Sure Memory's Cleared
635 		dmScreenSettings.dmSize = sizeof(dmScreenSettings);		// Size Of the devmode structure
636 		dmScreenSettings.dmPelsWidth	= width;				// Selected Screen Width
637 		dmScreenSettings.dmPelsHeight	= height;				// Selected Screen Height
638 		dmScreenSettings.dmBitsPerPel	= bits;					// bits per pixel
639 		dmScreenSettings.dmFields		= DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
640 
641 		// Try To Set Selected Mode and Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
642 		if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
643 		{
644 			// If The Mode Fails, Offer Two Options.  Quit Or Run In A Window.
645 			if (MessageBox(nullptr,TEXT("The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?"),TEXT("NeHe GL"),MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
646 			{
647 				fullscreen = FALSE;		// Select Windowed Mode (Fullscreen = FALSE)
648 			}
649 			else
650 			{
651 				//Popup Messagebox: Closing
652 				MessageBox(nullptr, TEXT("Program will close now."), TEXT("ERROR"), MB_OK|MB_ICONSTOP);
653 				return FALSE; //exit, return false
654 			}
655 		}
656 	}
657 
658 	if (fullscreen)		// when mode really succeeded
659 	{
660 		dwExStyle=WS_EX_APPWINDOW;		// Window Extended Style
661 		dwStyle=WS_POPUP;
662 		ShowCursor(FALSE);
663 	}
664 	else
665 	{
666 		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;	// Window extended style
667 		dwStyle=WS_OVERLAPPEDWINDOW;					// Windows style
668 	}
669 
670 	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);		// Adjust Window To True Requested Size
671 
672 	if (nullptr == (g_hWnd=CreateWindowEx(dwExStyle,			// Extended Style For The Window
673 								TEXT("OpenGL"),						// Class Name
674 								UTFConverter(title).c_wstr(),							// Window Title
675 								WS_CLIPSIBLINGS |				// Required Window Style
676 								WS_CLIPCHILDREN |				// Required Window Style
677 								dwStyle,						// Selected WIndow Style
678 								0, 0,							// Window Position
679 								WindowRect.right-WindowRect.left, // Calc adjusted Window Width
680 								WindowRect.bottom-WindowRect.top, // Calc adjustes Window Height
681 								nullptr,						// No Parent Window
682 								nullptr,						// No Menu
683 								g_hInstance,					// Instance
684 								nullptr )))						// Don't pass anything To WM_CREATE
685 	{
686 		abortGLInit("Window Creation Error.");
687 		return FALSE;
688 	}
689 
690 	static	PIXELFORMATDESCRIPTOR pfd=					// pfd Tells Windows How We Want Things To Be
691 	{
692 		sizeof(PIXELFORMATDESCRIPTOR),					// Size Of This Pixel Format Descriptor
693 		1,												// Version Number
694 		PFD_DRAW_TO_WINDOW |							// Format Must Support Window
695 		PFD_SUPPORT_OPENGL |							// Format Must Support OpenGL
696 		PFD_DOUBLEBUFFER,								// Must Support Double Buffering
697 		PFD_TYPE_RGBA,									// Request An RGBA Format
698 		BYTE(bits),											// Select Our Color Depth
699 		0, 0, 0, 0, 0, 0,								// Color Bits Ignored
700 		0,												// No Alpha Buffer
701 		0,												// Shift Bit Ignored
702 		0,												// No Accumulation Buffer
703 		0, 0, 0, 0,										// Accumulation Bits Ignored
704 		16,												// 16Bit Z-Buffer (Depth Buffer)
705 		0,												// No Stencil Buffer
706 		0,												// No Auxiliary Buffer
707 		PFD_MAIN_PLANE,									// Main Drawing Layer
708 		0,												// Reserved
709 		0, 0, 0											// Layer Masks Ignored
710 	};
711 
712 	if (nullptr == (hDC=GetDC(g_hWnd)))					// Did we get the Device Context?
713 	{
714 		abortGLInit("Can't Create A GL Device Context.");
715 		return FALSE;
716 	}
717 
718 	if (0 == (PixelFormat=ChoosePixelFormat(hDC, &pfd))) // Did We Find a matching pixel Format?
719 	{
720 		abortGLInit("Can't Find Suitable PixelFormat");
721 		return FALSE;
722 	}
723 
724 	if (!SetPixelFormat(hDC, PixelFormat, &pfd))
725 	{
726 		abortGLInit("Can't Set The PixelFormat");
727 		return FALSE;
728 	}
729 
730 	if (nullptr == (hRC=wglCreateContext(hDC)))
731 	{
732 		abortGLInit("Can't Create A GL Rendering Context.");
733 		return FALSE;
734 	}
735 
736 	if (!(wglMakeCurrent(hDC,hRC)))						// Try to activate the rendering context
737 	{
738 		abortGLInit("Can't Activate The Rendering Context");
739 		return FALSE;
740 	}
741 
742 	//// *** everything okay ***
743 
744 	ShowWindow(g_hWnd, SW_SHOW);	// Show The Window
745 	SetForegroundWindow(g_hWnd);	// Slightly Higher Prio
746 	SetFocus(g_hWnd);				// Sets Keyboard Focus To The Window
747 	ReSizeGLScene(width, height);	// Set Up Our Perspective GL Screen
748 
749 	if (!InitGL())
750 	{
751 		abortGLInit("Initialization failed");
752 		return FALSE;
753 	}
754 
755 	return TRUE;
756 }
757 
cleanup()758 void cleanup()
759 {
760 	freeTextureIds();
761 
762 	destroyAILogger();
763 
764 	if (g_hWnd)
765 		KillGLWindow();
766 }
767 
WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)768 LRESULT CALLBACK WndProc(HWND hWnd,				// Handles for this Window
769 						 UINT uMsg,				// Message for this Window
770 						 WPARAM wParam,			// additional message Info
771 						 LPARAM lParam)			// additional message Info
772 {
773 	switch (uMsg)				// check for Window Messages
774 	{
775 		case WM_ACTIVATE:				// Watch For Window Activate Message
776 			{
777 				if (!HIWORD(wParam))	// Check Minimization State
778 				{
779 					active=TRUE;
780 				}
781 				else
782 				{
783 					active=FALSE;
784 				}
785 
786 				return 0;				// return To The Message Loop
787 			}
788 
789 		case WM_SYSCOMMAND:			// Interrupt System Commands
790 			{
791 				switch (wParam)
792 				{
793 					case SC_SCREENSAVE:		// Screen-saver trying to start
794 					case SC_MONITORPOWER:	// Monitor trying to enter power-safe
795 					return 0;
796 				}
797 				break;
798 			}
799 
800 		case WM_CLOSE:			// close message received?
801 			{
802 				PostQuitMessage(0);	// Send WM_QUIT quit message
803 				return 0;			// Jump Back
804 			}
805 
806 		case WM_KEYDOWN:		// Is a key pressed?
807 			{
808 				keys[wParam] = TRUE;	// If so, Mark it as true
809 				return 0;
810 			}
811 
812 		case WM_KEYUP:			// Has Key Been released?
813 			{
814 				keys[wParam] = FALSE;	// If so, Mark It As FALSE
815 				return 0;
816 			}
817 
818 		case WM_SIZE:			// Resize The OpenGL Window
819 			{
820 				ReSizeGLScene(LOWORD(lParam), HIWORD(lParam));	// LoWord-Width, HiWord-Height
821 				return 0;
822 			}
823 	}
824 
825 	// Pass All unhandled Messaged To DefWindowProc
826 	return DefWindowProc(hWnd, uMsg, wParam, lParam);
827 }
828 
WinMain(HINSTANCE,HINSTANCE,LPSTR,int)829 int WINAPI WinMain( HINSTANCE /*hInstance*/,     // The instance
830 				   HINSTANCE /*hPrevInstance*/,  // Previous instance
831 				   LPSTR /*lpCmdLine*/,          // Command Line Parameters
832 				   int /*nShowCmd*/ )            // Window Show State
833 {
834 	MSG msg = {};
835 	BOOL done=FALSE;
836 
837 	createAILogger();
838 	logInfo("App fired!");
839 
840 	// Check the command line for an override file path.
841 	int argc;
842 	LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
843 	if (argv != nullptr && argc > 1)
844 	{
845 		std::wstring modelpathW(argv[1]);
846 		modelpath = UTFConverter(modelpathW).str();
847 	}
848 
849 	if (!Import3DFromFile(modelpath))
850 	{
851 		cleanup();
852 		return 0;
853 	}
854 
855 	logInfo("=============== Post Import ====================");
856 
857 	if (MessageBox(nullptr, TEXT("Would You Like To Run In Fullscreen Mode?"), TEXT("Start Fullscreen?"), MB_YESNO|MB_ICONEXCLAMATION)==IDNO)
858 	{
859 		fullscreen=FALSE;
860 	}
861 
862 	if (!CreateGLWindow(windowTitle, 640, 480, 16, fullscreen))
863 	{
864 		cleanup();
865 		return 0;
866 	}
867 
868 	while(!done)	// Game Loop
869 	{
870 		if (PeekMessage(&msg, nullptr, 0,0, PM_REMOVE))
871 		{
872 			if (msg.message==WM_QUIT)
873 			{
874 				done=TRUE;
875 			}
876 			else
877 			{
878 				TranslateMessage(&msg);
879 				DispatchMessage(&msg);
880 			}
881 		}
882 		else
883 		{
884 			// Draw The Scene. Watch For ESC Key And Quit Messaged From DrawGLScene()
885 			if (active)
886 			{
887 				if (keys[VK_ESCAPE])
888 				{
889 					done=TRUE;
890 				}
891 				else
892 				{
893 					DrawGLScene();
894 					SwapBuffers(hDC);
895 				}
896 			}
897 
898 			if (keys[VK_F1])
899 			{
900 				keys[VK_F1]=FALSE;
901 				KillGLWindow();
902 				fullscreen=!fullscreen;
903 				if (!CreateGLWindow(windowTitle, 640, 480, 16, fullscreen))
904 				{
905 					cleanup();
906 					return 0;
907 				}
908 			}
909 		}
910 	}
911 
912 	// *** cleanup ***
913 	cleanup();
914 	return static_cast<int>(msg.wParam);
915 }
916