mirror of https://github.com/mode777/rayjs.git
				
				
				
			add lightmapper
This commit is contained in:
		
							parent
							
								
									aa2c2ecd15
								
							
						
					
					
						commit
						9b69016ce8
					
				|  | @ -7,4 +7,7 @@ rayjs.exe | |||
| include/generated/ | ||||
| .DS_Store | ||||
| controls_test | ||||
| controls_test.exe | ||||
| controls_test.exe | ||||
| debug_interpolation.tga | ||||
| result.tga | ||||
| lightmapper_example | ||||
|  |  | |||
|  | @ -9,3 +9,6 @@ | |||
| 	path = thirdparty/raygui | ||||
| 	url = https://github.com/raysan5/raygui.git | ||||
| 	branch = tags/3.6 | ||||
| [submodule "thirdparty/lightmapper"] | ||||
| 	path = thirdparty/lightmapper | ||||
| 	url = https://github.com/ands/lightmapper.git | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
|         "vs.sc.mtl.bin.h": "c", | ||||
|         "embedded_shader.h": "c", | ||||
|         "common.h": "c", | ||||
|         "raylib.h": "c" | ||||
|         "raylib.h": "c", | ||||
|         "glad.h": "c" | ||||
|     } | ||||
| } | ||||
|  | @ -31,6 +31,9 @@ target_include_directories(quickjs | |||
| 		${quickjs_sources_root} | ||||
| ) | ||||
| 
 | ||||
| add_library(lightmapper INTERFACE) | ||||
| target_include_directories(lightmapper INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/lightmapper) | ||||
| 
 | ||||
| set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
| file(GLOB files src/*.c) | ||||
| #add_executable(${CMAKE_PROJECT_NAME} MACOSX_BUNDLE ${files}) | ||||
|  | @ -40,6 +43,5 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE src) | |||
| target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE thirdparty/raygui/src) | ||||
| target_link_libraries(${CMAKE_PROJECT_NAME} quickjs raylib) | ||||
| 
 | ||||
| add_executable(controls_test thirdparty/raygui/examples/controls_test_suite/controls_test_suite.c) | ||||
| target_include_directories(controls_test PRIVATE thirdparty/raygui/src) | ||||
| target_link_libraries(controls_test raylib) | ||||
| add_executable(lightmapper_example src/lightmapper_example.c) | ||||
| target_link_libraries(lightmapper_example raylib lightmapper) | ||||
|  |  | |||
|  | @ -0,0 +1,564 @@ | |||
| #define _USE_MATH_DEFINES | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <math.h> | ||||
| #include <assert.h> | ||||
| //#include "glad/glad.h"
 | ||||
| #define GLAD_MALLOC(sz) malloc(sz) | ||||
| #define GLAD_FREE(sz) free(sz) | ||||
| //#define GLAD_GL_IMPLEMENTATION
 | ||||
| #include "../thirdparty/raylib/src/external/glad.h" | ||||
| #include "GLFW/glfw3.h" | ||||
| #include "raylib.h" | ||||
| 
 | ||||
| #define LIGHTMAPPER_IMPLEMENTATION | ||||
| #define LM_DEBUG_INTERPOLATION | ||||
| #include "lightmapper.h" | ||||
| 
 | ||||
| #ifndef M_PI // even with _USE_MATH_DEFINES not always available
 | ||||
| #define M_PI 3.14159265358979323846 | ||||
| #endif | ||||
| 
 | ||||
| typedef struct { | ||||
| 	float p[3]; | ||||
| 	float t[2]; | ||||
| } vertex_t; | ||||
| 
 | ||||
| typedef struct | ||||
| { | ||||
| 	GLuint program; | ||||
| 	GLint u_lightmap; | ||||
| 	GLint u_projection; | ||||
| 	GLint u_view; | ||||
| 
 | ||||
| 	GLuint lightmap; | ||||
| 	int w, h; | ||||
|     Model raylib_model; | ||||
| 	GLuint vao, vbo, ibo; | ||||
| 	vertex_t *vertices; | ||||
| 	unsigned short *indices; | ||||
| 	unsigned int vertexCount, indexCount; | ||||
| } scene_t; | ||||
| 
 | ||||
| static int initScene(scene_t *scene); | ||||
| static void drawScene(scene_t *scene, float *view, float *projection); | ||||
| static void destroyScene(scene_t *scene); | ||||
| 
 | ||||
| static int bake(scene_t *scene) | ||||
| { | ||||
| 	lm_context *ctx = lmCreate( | ||||
| 		64,               // hemisphere resolution (power of two, max=512)
 | ||||
| 		0.001f, 100.0f,   // zNear, zFar of hemisphere cameras
 | ||||
| 		1.0f, 1.0f, 1.0f, // background color (white for ambient occlusion)
 | ||||
| 		2, 0.01f,         // lightmap interpolation threshold (small differences are interpolated rather than sampled)
 | ||||
| 						  // check debug_interpolation.tga for an overview of sampled (red) vs interpolated (green) pixels.
 | ||||
| 		0.0f);            // modifier for camera-to-surface distance for hemisphere rendering.
 | ||||
| 		                  // tweak this to trade-off between interpolated normals quality and other artifacts (see declaration).
 | ||||
| 
 | ||||
| 	if (!ctx) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Error: Could not initialize lightmapper.\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	int w = scene->w, h = scene->h; | ||||
| 	float *data = calloc(w * h * 4, sizeof(float)); | ||||
| 	lmSetTargetLightmap(ctx, data, w, h, 4); | ||||
| 
 | ||||
| 	lmSetGeometry(ctx, NULL,                                                                 // no transformation in this example
 | ||||
| 		LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, p), sizeof(vertex_t), | ||||
| 		LM_NONE , NULL                                                   , 0               , // no interpolated normals in this example
 | ||||
| 		LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, t), sizeof(vertex_t), | ||||
| 		scene->indexCount, LM_UNSIGNED_SHORT, scene->indices); | ||||
| 
 | ||||
| 	int vp[4]; | ||||
| 	float view[16], projection[16]; | ||||
| 	double lastUpdateTime = 0.0; | ||||
| 	while (lmBegin(ctx, vp, view, projection)) | ||||
| 	{ | ||||
| 		// render to lightmapper framebuffer
 | ||||
| 		glViewport(vp[0], vp[1], vp[2], vp[3]); | ||||
| 		drawScene(scene, view, projection); | ||||
| 
 | ||||
| 		// display progress every second (printf is expensive)
 | ||||
| 		double time = glfwGetTime(); | ||||
| 		if (time - lastUpdateTime > 1.0) | ||||
| 		{ | ||||
| 			lastUpdateTime = time; | ||||
| 			printf("\r%6.2f%%", lmProgress(ctx) * 100.0f); | ||||
| 			fflush(stdout); | ||||
| 		} | ||||
| 
 | ||||
| 		lmEnd(ctx); | ||||
| 	} | ||||
| 	printf("\rFinished baking %d triangles.\n", scene->indexCount / 3); | ||||
| 
 | ||||
| 	lmDestroy(ctx); | ||||
| 
 | ||||
| 	// postprocess texture
 | ||||
| 	float *temp = calloc(w * h * 4, sizeof(float)); | ||||
| 	for (int i = 0; i < 16; i++) | ||||
| 	{ | ||||
| 		lmImageDilate(data, temp, w, h, 4); | ||||
| 		lmImageDilate(temp, data, w, h, 4); | ||||
| 	} | ||||
| 	lmImageSmooth(data, temp, w, h, 4); | ||||
| 	lmImageDilate(temp, data, w, h, 4); | ||||
| 	lmImagePower(data, w, h, 4, 1.0f / 2.2f, 0x7); // gamma correct color channels
 | ||||
| 	free(temp); | ||||
| 
 | ||||
| 	// save result to a file
 | ||||
| 	if (lmImageSaveTGAf("result.tga", data, w, h, 4, 1.0f)) | ||||
| 		printf("Saved result.tga\n"); | ||||
| 
 | ||||
| 	// upload result
 | ||||
| 	glBindTexture(GL_TEXTURE_2D, scene->lightmap); | ||||
| 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_FLOAT, data); | ||||
| 	free(data); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static void error_callback(int error, const char *description) | ||||
| { | ||||
| 	fprintf(stderr, "Error: %s\n", description); | ||||
| } | ||||
| 
 | ||||
| static void fpsCameraViewMatrix(GLFWwindow *window, float *view); | ||||
| static void perspectiveMatrix(float *out, float fovy, float aspect, float zNear, float zFar); | ||||
| 
 | ||||
| static void mainLoop(GLFWwindow *window, scene_t *scene) | ||||
| { | ||||
| 	glfwPollEvents(); | ||||
| 	if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) | ||||
| 		bake(scene); | ||||
| 
 | ||||
| 	int w, h; | ||||
| 	glfwGetFramebufferSize(window, &w, &h); | ||||
| 	glViewport(0, 0, w, h); | ||||
| 
 | ||||
| 	// camera for glfw window
 | ||||
| 	float view[16], projection[16]; | ||||
| 	fpsCameraViewMatrix(window, view); | ||||
| 	perspectiveMatrix(projection, 45.0f, (float)w / (float)h, 0.01f, 100.0f); | ||||
| 
 | ||||
| 	// draw to screen with a blueish sky
 | ||||
| 	glClearColor(0.6f, 0.8f, 1.0f, 1.0f); | ||||
| 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||||
| 	drawScene(scene, view, projection); | ||||
| 
 | ||||
| 	glfwSwapBuffers(window); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char* argv[]) | ||||
| { | ||||
| 	glfwSetErrorCallback(error_callback); | ||||
| 
 | ||||
| 	if (!glfwInit()) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not initialize GLFW.\n"); | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
| 
 | ||||
| 	glfwWindowHint(GLFW_RED_BITS, 8); | ||||
| 	glfwWindowHint(GLFW_GREEN_BITS, 8); | ||||
| 	glfwWindowHint(GLFW_BLUE_BITS, 8); | ||||
| 	glfwWindowHint(GLFW_ALPHA_BITS, 8); | ||||
| 	glfwWindowHint(GLFW_DEPTH_BITS, 32); | ||||
| 	glfwWindowHint(GLFW_STENCIL_BITS, GLFW_DONT_CARE); | ||||
| 	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | ||||
| 	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | ||||
| 	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | ||||
| 	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | ||||
| 	glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); | ||||
| 	glfwWindowHint(GLFW_SAMPLES, 4); | ||||
| 
 | ||||
| 	GLFWwindow *window = glfwCreateWindow(1024, 768, "Lightmapping Example", NULL, NULL); | ||||
| 	if (!window) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not create window.\n"); | ||||
| 		glfwTerminate(); | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
| 
 | ||||
| 	glfwMakeContextCurrent(window); | ||||
|     gladLoadGL((GLADloadfunc)glfwGetProcAddress); | ||||
| 	glfwSwapInterval(1); | ||||
| 
 | ||||
| 	scene_t scene = {0}; | ||||
| 	if (!initScene(&scene)) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not initialize scene.\n"); | ||||
| 		glfwDestroyWindow(window); | ||||
| 		glfwTerminate(); | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
| 
 | ||||
| 	printf("Ambient Occlusion Baking Example.\n"); | ||||
| 	printf("Use your mouse and the W, A, S, D, E, Q keys to navigate.\n"); | ||||
| 	printf("Press SPACE to start baking one light bounce!\n"); | ||||
| 	printf("This will take a few seconds and bake a lightmap illuminated by:\n"); | ||||
| 	printf("1. The mesh itself (initially black)\n"); | ||||
| 	printf("2. A white sky (1.0f, 1.0f, 1.0f)\n"); | ||||
| 
 | ||||
| 	while (!glfwWindowShouldClose(window)) | ||||
| 	{ | ||||
| 		mainLoop(window, &scene); | ||||
| 	} | ||||
| 
 | ||||
| 	destroyScene(&scene); | ||||
| 	glfwDestroyWindow(window); | ||||
| 	glfwTerminate(); | ||||
| 	return EXIT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| // helpers ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| static int loadSimpleObjFile(const char *filename, vertex_t **vertices, unsigned int *vertexCount, unsigned short **indices, unsigned int *indexCount); | ||||
| static GLuint loadProgram(const char *vp, const char *fp, const char **attributes, int attributeCount); | ||||
| 
 | ||||
| static int initScene(scene_t *scene) | ||||
| { | ||||
| 	// load mesh
 | ||||
|     scene->raylib_model = LoadModel("thirdparty/lightmapper/example/gazebo.obj"); | ||||
|     //scene->vertices = myModel.meshes[0].
 | ||||
| 	if (!loadSimpleObjFile("thirdparty/lightmapper/example/gazebo.obj", &scene->vertices, &scene->vertexCount, &scene->indices, &scene->indexCount)) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Error loading obj file\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	glGenVertexArrays(1, &scene->vao); | ||||
| 	glBindVertexArray(scene->vao); | ||||
| 
 | ||||
| 	glGenBuffers(1, &scene->vbo); | ||||
| 	glBindBuffer(GL_ARRAY_BUFFER, scene->vbo); | ||||
| 	glBufferData(GL_ARRAY_BUFFER, scene->vertexCount * sizeof(vertex_t), scene->vertices, GL_STATIC_DRAW); | ||||
| 
 | ||||
| 	glGenBuffers(1, &scene->ibo); | ||||
| 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene->ibo); | ||||
| 	glBufferData(GL_ELEMENT_ARRAY_BUFFER, scene->indexCount * sizeof(unsigned short), scene->indices, GL_STATIC_DRAW); | ||||
| 
 | ||||
| 	glEnableVertexAttribArray(0); | ||||
| 	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, p)); | ||||
| 	glEnableVertexAttribArray(1); | ||||
| 	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, t)); | ||||
| 
 | ||||
| 	// create lightmap texture
 | ||||
| 	scene->w = 654; | ||||
| 	scene->h = 654; | ||||
| 	glGenTextures(1, &scene->lightmap); | ||||
| 	glBindTexture(GL_TEXTURE_2D, scene->lightmap); | ||||
| 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
| 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
| 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||
| 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||
| 	unsigned char emissive[] = { 0, 0, 0, 255 }; | ||||
| 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive); | ||||
| 
 | ||||
| 	// load shader
 | ||||
| 	const char *vp = | ||||
| 		"#version 150 core\n" | ||||
| 		"in vec3 a_position;\n" | ||||
| 		"in vec2 a_texcoord;\n" | ||||
| 		"uniform mat4 u_view;\n" | ||||
| 		"uniform mat4 u_projection;\n" | ||||
| 		"out vec2 v_texcoord;\n" | ||||
| 
 | ||||
| 		"void main()\n" | ||||
| 		"{\n" | ||||
| 		"gl_Position = u_projection * (u_view * vec4(a_position, 1.0));\n" | ||||
| 		"v_texcoord = a_texcoord;\n" | ||||
| 		"}\n"; | ||||
| 
 | ||||
| 	const char *fp = | ||||
| 		"#version 150 core\n" | ||||
| 		"in vec2 v_texcoord;\n" | ||||
| 		"uniform sampler2D u_lightmap;\n" | ||||
| 		"out vec4 o_color;\n" | ||||
| 
 | ||||
| 		"void main()\n" | ||||
| 		"{\n" | ||||
| 		"o_color = vec4(texture(u_lightmap, v_texcoord).rgb, gl_FrontFacing ? 1.0 : 0.0);\n" | ||||
| 		"}\n"; | ||||
| 
 | ||||
| 	const char *attribs[] = | ||||
| 	{ | ||||
| 		"a_position", | ||||
| 		"a_texcoord" | ||||
| 	}; | ||||
| 
 | ||||
| 	scene->program = loadProgram(vp, fp, attribs, 2); | ||||
| 	if (!scene->program) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Error loading shader\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	scene->u_view = glGetUniformLocation(scene->program, "u_view"); | ||||
| 	scene->u_projection = glGetUniformLocation(scene->program, "u_projection"); | ||||
| 	scene->u_lightmap = glGetUniformLocation(scene->program, "u_lightmap"); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static void drawScene(scene_t *scene, float *view, float *projection) | ||||
| { | ||||
| 	glEnable(GL_DEPTH_TEST); | ||||
| 
 | ||||
| 	glUseProgram(scene->program); | ||||
| 	glUniform1i(scene->u_lightmap, 0); | ||||
| 	glUniformMatrix4fv(scene->u_projection, 1, GL_FALSE, projection); | ||||
| 	glUniformMatrix4fv(scene->u_view, 1, GL_FALSE, view); | ||||
| 
 | ||||
| 	glBindTexture(GL_TEXTURE_2D, scene->lightmap); | ||||
| 
 | ||||
| 	glBindVertexArray(scene->vao); | ||||
| 	glDrawElements(GL_TRIANGLES, scene->indexCount, GL_UNSIGNED_SHORT, 0); | ||||
| } | ||||
| 
 | ||||
| static void destroyScene(scene_t *scene) | ||||
| { | ||||
| 	free(scene->vertices); | ||||
| 	free(scene->indices); | ||||
| 	glDeleteVertexArrays(1, &scene->vao); | ||||
| 	glDeleteBuffers(1, &scene->vbo); | ||||
| 	glDeleteBuffers(1, &scene->ibo); | ||||
| 	glDeleteTextures(1, &scene->lightmap); | ||||
| 	glDeleteProgram(scene->program); | ||||
| } | ||||
| 
 | ||||
| static int loadSimpleObjFile(const char *filename, vertex_t **vertices, unsigned int *vertexCount, unsigned short **indices, unsigned int *indexCount) | ||||
| { | ||||
| 	FILE *file = fopen(filename, "rt"); | ||||
| 	if (!file) | ||||
| 		return 0; | ||||
| 	char line[1024]; | ||||
| 
 | ||||
| 	// first pass
 | ||||
| 	unsigned int np = 0, nn = 0, nt = 0, nf = 0; | ||||
| 	while (!feof(file)) | ||||
| 	{ | ||||
| 		fgets(line, 1024, file); | ||||
| 		if (line[0] == '#') continue; | ||||
| 		if (line[0] == 'v') | ||||
| 		{ | ||||
| 			if (line[1] == ' ') { np++; continue; } | ||||
| 			if (line[1] == 'n') { nn++; continue; } | ||||
| 			if (line[1] == 't') { nt++; continue; } | ||||
| 			assert(!"unknown vertex attribute"); | ||||
| 		} | ||||
| 		if (line[0] == 'f') { nf++; continue; } | ||||
| 		assert(!"unknown identifier"); | ||||
| 	} | ||||
| 	assert(np && np == nn && np == nt && nf); // only supports obj files without separately indexed vertex attributes
 | ||||
| 
 | ||||
| 	// allocate memory
 | ||||
| 	*vertexCount = np; | ||||
| 	*vertices = calloc(np, sizeof(vertex_t)); | ||||
| 	*indexCount = nf * 3; | ||||
| 	*indices = calloc(nf * 3, sizeof(unsigned short)); | ||||
| 
 | ||||
| 	// second pass
 | ||||
| 	fseek(file, 0, SEEK_SET); | ||||
| 	unsigned int cp = 0, cn = 0, ct = 0, cf = 0; | ||||
| 	while (!feof(file)) | ||||
| 	{ | ||||
| 		fgets(line, 1024, file); | ||||
| 		if (line[0] == '#') continue; | ||||
| 		if (line[0] == 'v') | ||||
| 		{ | ||||
| 			if (line[1] == ' ') { float *p = (*vertices)[cp++].p; char *e1, *e2; p[0] = (float)strtod(line + 2, &e1); p[1] = (float)strtod(e1, &e2); p[2] = (float)strtod(e2, 0); continue; } | ||||
| 			if (line[1] == 'n') { /*float *n = (*vertices)[cn++].n; char *e1, *e2; n[0] = (float)strtod(line + 3, &e1); n[1] = (float)strtod(e1, &e2); n[2] = (float)strtod(e2, 0);*/ continue; } // no normals needed
 | ||||
| 			if (line[1] == 't') { float *t = (*vertices)[ct++].t; char *e1;      t[0] = (float)strtod(line + 3, &e1); t[1] = (float)strtod(e1, 0);                                continue; } | ||||
| 			assert(!"unknown vertex attribute"); | ||||
| 		} | ||||
| 		if (line[0] == 'f') | ||||
| 		{ | ||||
| 			unsigned short *tri = (*indices) + cf; | ||||
| 			cf += 3; | ||||
| 			char *e1, *e2, *e3 = line + 1; | ||||
| 			for (int i = 0; i < 3; i++) | ||||
| 			{ | ||||
| 				unsigned long pi = strtoul(e3 + 1, &e1, 10); | ||||
| 				assert(e1[0] == '/'); | ||||
| 				unsigned long ti = strtoul(e1 + 1, &e2, 10); | ||||
| 				assert(e2[0] == '/'); | ||||
| 				unsigned long ni = strtoul(e2 + 1, &e3, 10); | ||||
| 				assert(pi == ti && pi == ni); | ||||
| 				tri[i] = (unsigned short)(pi - 1); | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		assert(!"unknown identifier"); | ||||
| 	} | ||||
| 
 | ||||
| 	fclose(file); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static GLuint loadShader(GLenum type, const char *source) | ||||
| { | ||||
| 	GLuint shader = glCreateShader(type); | ||||
| 	if (shader == 0) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not create shader!\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	glShaderSource(shader, 1, &source, NULL); | ||||
| 	glCompileShader(shader); | ||||
| 	GLint compiled; | ||||
| 	glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); | ||||
| 	if (!compiled) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not compile shader!\n"); | ||||
| 		GLint infoLen = 0; | ||||
| 		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); | ||||
| 		if (infoLen) | ||||
| 		{ | ||||
| 			char* infoLog = (char*)malloc(infoLen); | ||||
| 			glGetShaderInfoLog(shader, infoLen, NULL, infoLog); | ||||
| 			fprintf(stderr, "%s\n", infoLog); | ||||
| 			free(infoLog); | ||||
| 		} | ||||
| 		glDeleteShader(shader); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return shader; | ||||
| } | ||||
| static GLuint loadProgram(const char *vp, const char *fp, const char **attributes, int attributeCount) | ||||
| { | ||||
| 	GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vp); | ||||
| 	if (!vertexShader) | ||||
| 		return 0; | ||||
| 	GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fp); | ||||
| 	if (!fragmentShader) | ||||
| 	{ | ||||
| 		glDeleteShader(vertexShader); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	GLuint program = glCreateProgram(); | ||||
| 	if (program == 0) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not create program!\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	glAttachShader(program, vertexShader); | ||||
| 	glAttachShader(program, fragmentShader); | ||||
| 
 | ||||
| 	for (int i = 0; i < attributeCount; i++) | ||||
| 		glBindAttribLocation(program, i, attributes[i]); | ||||
| 
 | ||||
| 	glLinkProgram(program); | ||||
| 	glDeleteShader(vertexShader); | ||||
| 	glDeleteShader(fragmentShader); | ||||
| 	GLint linked; | ||||
| 	glGetProgramiv(program, GL_LINK_STATUS, &linked); | ||||
| 	if (!linked) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not link program!\n"); | ||||
| 		GLint infoLen = 0; | ||||
| 		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); | ||||
| 		if (infoLen) | ||||
| 		{ | ||||
| 			char* infoLog = (char*)malloc(sizeof(char) * infoLen); | ||||
| 			glGetProgramInfoLog(program, infoLen, NULL, infoLog); | ||||
| 			fprintf(stderr, "%s\n", infoLog); | ||||
| 			free(infoLog); | ||||
| 		} | ||||
| 		glDeleteProgram(program); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return program; | ||||
| } | ||||
| 
 | ||||
| static void multiplyMatrices(float *out, float *a, float *b) | ||||
| { | ||||
| 	for (int y = 0; y < 4; y++) | ||||
| 		for (int x = 0; x < 4; x++) | ||||
| 			out[y * 4 + x] = a[x] * b[y * 4] + a[4 + x] * b[y * 4 + 1] + a[8 + x] * b[y * 4 + 2] + a[12 + x] * b[y * 4 + 3]; | ||||
| } | ||||
| static void translationMatrix(float *out, float x, float y, float z) | ||||
| { | ||||
| 	out[ 0] = 1.0f; out[ 1] = 0.0f; out[ 2] = 0.0f; out[ 3] = 0.0f; | ||||
| 	out[ 4] = 0.0f; out[ 5] = 1.0f; out[ 6] = 0.0f; out[ 7] = 0.0f; | ||||
| 	out[ 8] = 0.0f; out[ 9] = 0.0f; out[10] = 1.0f; out[11] = 0.0f; | ||||
| 	out[12] = x;    out[13] = y;    out[14] = z;    out[15] = 1.0f; | ||||
| } | ||||
| static void rotationMatrix(float *out, float angle, float x, float y, float z) | ||||
| { | ||||
| 	angle *= (float)M_PI / 180.0f; | ||||
| 	float c = cosf(angle), s = sinf(angle), c2 = 1.0f - c; | ||||
| 	out[ 0] = x*x*c2 + c;   out[ 1] = y*x*c2 + z*s; out[ 2] = x*z*c2 - y*s; out[ 3] = 0.0f; | ||||
| 	out[ 4] = x*y*c2 - z*s; out[ 5] = y*y*c2 + c;   out[ 6] = y*z*c2 + x*s; out[ 7] = 0.0f; | ||||
| 	out[ 8] = x*z*c2 + y*s; out[ 9] = y*z*c2 - x*s; out[10] = z*z*c2 + c;   out[11] = 0.0f; | ||||
| 	out[12] = 0.0f;         out[13] = 0.0f;         out[14] = 0.0f;         out[15] = 1.0f; | ||||
| } | ||||
| static void transformPosition(float *out, float *m, float *p) | ||||
| { | ||||
| 	float d = 1.0f / (m[3] * p[0] + m[7] * p[1] + m[11] * p[2] + m[15]); | ||||
| 	out[2] =     d * (m[2] * p[0] + m[6] * p[1] + m[10] * p[2] + m[14]); | ||||
| 	out[1] =     d * (m[1] * p[0] + m[5] * p[1] + m[ 9] * p[2] + m[13]); | ||||
| 	out[0] =     d * (m[0] * p[0] + m[4] * p[1] + m[ 8] * p[2] + m[12]); | ||||
| } | ||||
| static void transposeMatrix(float *out, float *m) | ||||
| { | ||||
| 	out[ 0] = m[0]; out[ 1] = m[4]; out[ 2] = m[ 8]; out[ 3] = m[12]; | ||||
| 	out[ 4] = m[1]; out[ 5] = m[5]; out[ 6] = m[ 9]; out[ 7] = m[13]; | ||||
| 	out[ 8] = m[2]; out[ 9] = m[6]; out[10] = m[10]; out[11] = m[14]; | ||||
| 	out[12] = m[3]; out[13] = m[7]; out[14] = m[11]; out[15] = m[15]; | ||||
| } | ||||
| static void perspectiveMatrix(float *out, float fovy, float aspect, float zNear, float zFar) | ||||
| { | ||||
| 	float f = 1.0f / tanf(fovy * (float)M_PI / 360.0f); | ||||
| 	float izFN = 1.0f / (zNear - zFar); | ||||
| 	out[ 0] = f / aspect; out[ 1] = 0.0f; out[ 2] = 0.0f;                       out[ 3] = 0.0f; | ||||
| 	out[ 4] = 0.0f;       out[ 5] = f;    out[ 6] = 0.0f;                       out[ 7] = 0.0f; | ||||
| 	out[ 8] = 0.0f;       out[ 9] = 0.0f; out[10] = (zFar + zNear) * izFN;      out[11] = -1.0f; | ||||
| 	out[12] = 0.0f;       out[13] = 0.0f; out[14] = 2.0f * zFar * zNear * izFN; out[15] = 0.0f; | ||||
| } | ||||
| 
 | ||||
| static void fpsCameraViewMatrix(GLFWwindow *window, float *view) | ||||
| { | ||||
| 	// initial camera config
 | ||||
| 	static float position[] = { 0.0f, 0.3f, 1.5f }; | ||||
| 	static float rotation[] = { 0.0f, 0.0f }; | ||||
| 
 | ||||
| 	// mouse look
 | ||||
| 	static double lastMouse[] = { 0.0, 0.0 }; | ||||
| 	double mouse[2]; | ||||
| 	glfwGetCursorPos(window, &mouse[0], &mouse[1]); | ||||
| 	if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) | ||||
| 	{ | ||||
| 		rotation[0] += (float)(mouse[1] - lastMouse[1]) * -0.2f; | ||||
| 		rotation[1] += (float)(mouse[0] - lastMouse[0]) * -0.2f; | ||||
| 	} | ||||
| 	lastMouse[0] = mouse[0]; | ||||
| 	lastMouse[1] = mouse[1]; | ||||
| 
 | ||||
| 	float rotationY[16], rotationX[16], rotationYX[16]; | ||||
| 	rotationMatrix(rotationX, rotation[0], 1.0f, 0.0f, 0.0f); | ||||
| 	rotationMatrix(rotationY, rotation[1], 0.0f, 1.0f, 0.0f); | ||||
| 	multiplyMatrices(rotationYX, rotationY, rotationX); | ||||
| 
 | ||||
| 	// keyboard movement (WSADEQ)
 | ||||
| 	float speed = (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) ? 0.1f : 0.01f; | ||||
| 	float movement[3] = {0}; | ||||
| 	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) movement[2] -= speed; | ||||
| 	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) movement[2] += speed; | ||||
| 	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) movement[0] -= speed; | ||||
| 	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) movement[0] += speed; | ||||
| 	if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) movement[1] -= speed; | ||||
| 	if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) movement[1] += speed; | ||||
| 
 | ||||
| 	float worldMovement[3]; | ||||
| 	transformPosition(worldMovement, rotationYX, movement); | ||||
| 	position[0] += worldMovement[0]; | ||||
| 	position[1] += worldMovement[1]; | ||||
| 	position[2] += worldMovement[2]; | ||||
| 
 | ||||
| 	// construct view matrix
 | ||||
| 	float inverseRotation[16], inverseTranslation[16]; | ||||
| 	transposeMatrix(inverseRotation, rotationYX); | ||||
| 	translationMatrix(inverseTranslation, -position[0], -position[1], -position[2]); | ||||
| 	multiplyMatrices(view, inverseRotation, inverseTranslation); // = inverse(translation(position) * rotationYX);
 | ||||
| } | ||||
|  | @ -0,0 +1 @@ | |||
| Subproject commit 4fd3bf4e2c07263f85d5d875ebdef061bc512dd4 | ||||
		Loading…
	
		Reference in New Issue