/* * Libretro 3DEngine * Copyright (C) 2013-2014 - Hans-Kristian Arntzen * Copyright (C) 2013-2014 - Daniel De Matteis * * Libretro 3DEngine is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * Libretro 3DEngine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with Libretro 3DEngine. * If not, see . */ #include #include #include "libretro.h" #include "program.h" #include "rpng.h" #include "rtga.h" #include "picojpeg.h" #include #include #include #include "location_math.h" #include #define CUBE_VERTS 36 static bool first_init = true; static std::string texpath; extern bool camera_enable; struct Vertex { GLfloat vert[4]; GLfloat normal[4]; GLfloat tex[2]; }; struct Cube { struct Vertex vertices[CUBE_VERTS]; }; using namespace glm; static bool update; static GLuint prog; static GLuint background_prog; static GLuint vbo; static GLuint background_vbo; static float cube_stride = 4.0f; static unsigned cube_size = 1; static float light_r; static float light_g; static float light_b; static float ambient_light_r; static float ambient_light_g; static float ambient_light_b; static float ambient_light_a; static vec3 player_pos; static float camera_rot_x; static float camera_rot_y; static const char *vertex_shader[] = { "uniform mat4 uVP;", "uniform mat4 uM;", "attribute vec4 aVertex;", "attribute vec4 aNormal;", "attribute vec2 aTexCoord;", "varying vec3 normal;", "varying vec4 model_pos;", "varying vec2 tex_coord;", "void main() {", " model_pos = uM * aVertex;", " gl_Position = uVP * model_pos;", " vec4 trans_normal = uM * aNormal;", " normal = trans_normal.xyz;", " tex_coord = vec2(1.0 - aTexCoord.x, aTexCoord.y);", "}", }; static const char *fragment_shader[] = { #ifdef ANDROID "#extension GL_OES_EGL_image_external : require\n", #endif "#ifdef GL_ES\n", "precision mediump float; \n", "#endif\n", "varying vec3 normal;", "varying vec4 model_pos;", "varying vec2 tex_coord;", "uniform vec3 light_pos;", "uniform vec4 ambient_light;", #ifdef ANDROID "uniform samplerExternalOES uTexture;", #else "uniform sampler2D uTexture;", #endif "void main() {", " vec3 diff = light_pos - model_pos.xyz;", " float dist_mod = 100.0 * inversesqrt(dot(diff, diff));", " gl_FragColor = texture2D(uTexture, tex_coord) * (ambient_light + dist_mod * smoothstep(0.0, 1.0, dot(normalize(diff), normal)));", "}", }; static const char *background_vertex_shader[] = { "attribute vec2 TexCoord;\n", "attribute vec2 VertexCoord;\n", "varying vec2 tex_coord;\n", "void main() {\n", " gl_Position = vec4(VertexCoord, 1.0, 1.0);\n", " tex_coord = TexCoord;\n", "}", }; static const char *background_fragment_shader[] = { #ifdef ANDROID "#extension GL_OES_EGL_image_external : require\n", #endif "#ifdef GL_ES\n", "precision mediump float;\n", "#endif\n", #ifdef ANDROID "uniform samplerExternalOES Texture;", #else "uniform sampler2D Texture;\n", #endif "varying vec2 tex_coord;\n", "void main() {\n", " gl_FragColor = texture2D(Texture, tex_coord);\n", "}", }; static const GLfloat background_data[] = { -1, -1, // vertex 0, 0, // tex 1, -1, // vertex 1, 0, // tex -1, 1, // vertex 0, 1, // tex 1, 1, // vertex 1, 1, // tex }; static const Vertex vertex_data[] = { { { -1, -1, -1, 1 }, { 0, 0, -1, 0 }, { 0, 1 } }, // Front { { 1, -1, -1, 1 }, { 0, 0, -1, 0 }, { 1, 1 } }, { { -1, 1, -1, 1 }, { 0, 0, -1, 0 }, { 0, 0 } }, { { 1, 1, -1, 1 }, { 0, 0, -1, 0 }, { 1, 0 } }, { { 1, -1, 1, 1 }, { 0, 0, 1, 0 }, { 0, 1 } }, // Back { { -1, -1, 1, 1 }, { 0, 0, 1, 0 }, { 1, 1 } }, { { 1, 1, 1, 1 }, { 0, 0, 1, 0 }, { 0, 0 } }, { { -1, 1, 1, 1 }, { 0, 0, 1, 0 }, { 1, 0 } }, { { -1, -1, 1, 1 }, { -1, 0, 0, 0 }, { 0, 1 } }, // Left { { -1, -1, -1, 1 }, { -1, 0, 0, 0 }, { 1, 1 } }, { { -1, 1, 1, 1 }, { -1, 0, 0, 0 }, { 0, 0 } }, { { -1, 1, -1, 1 }, { -1, 0, 0, 0 }, { 1, 0 } }, { { 1, -1, -1, 1 }, { 1, 0, 0, 0 }, { 0, 1 } }, // Right { { 1, -1, 1, 1 }, { 1, 0, 0, 0 }, { 1, 1 } }, { { 1, 1, -1, 1 }, { 1, 0, 0, 0 }, { 0, 0 } }, { { 1, 1, 1, 1 }, { 1, 0, 0, 0 }, { 1, 0 } }, { { -1, 1, -1, 1 }, { 0, 1, 0, 0 }, { 0, 1 } }, // Top { { 1, 1, -1, 1 }, { 0, 1, 0, 0 }, { 1, 1 } }, { { -1, 1, 1, 1 }, { 0, 1, 0, 0 }, { 0, 0 } }, { { 1, 1, 1, 1 }, { 0, 1, 0, 0 }, { 1, 0 } }, { { -1, -1, 1, 1 }, { 0, -1, 0, 0 }, { 0, 1 } }, // Bottom { { 1, -1, 1, 1 }, { 0, -1, 0, 0 }, { 1, 1 } }, { { -1, -1, -1, 1 }, { 0, -1, 0, 0 }, { 0, 0 } }, { { 1, -1, -1, 1 }, { 0, -1, 0, 0 }, { 1, 0 } }, }; static const GLubyte indices[] = { 0, 1, 2, // Front 3, 2, 1, 4, 5, 6, // Back 7, 6, 5, 8, 9, 10, // Left 11, 10, 9, 12, 13, 14, // Right 15, 14, 13, 16, 17, 18, // Top 19, 18, 17, 20, 21, 22, // Bottom 23, 22, 21, }; static void display_cubes_array(void) { glBindBuffer(GL_ARRAY_BUFFER, vbo); int vloc = glGetAttribLocation(prog, "aVertex"); glVertexAttribPointer(vloc, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, vert))); glEnableVertexAttribArray(vloc); int nloc = glGetAttribLocation(prog, "aNormal"); glVertexAttribPointer(nloc, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, normal))); glEnableVertexAttribArray(nloc); int tcloc = glGetAttribLocation(prog, "aTexCoord"); glVertexAttribPointer(tcloc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, tex))); glEnableVertexAttribArray(tcloc); if (update) { unsigned x, y, z; update = false; std::vector cubes; cubes.resize(cube_size * cube_size * cube_size); for (x = 0; x < cube_size; x++) { for (y = 0; y < cube_size; y++) { for (z = 0; z < cube_size; z++) { Cube &cube = cubes[((cube_size * cube_size * z) + (cube_size * y) + x)]; float off_x = cube_stride * ((float)x - cube_size / 2); float off_y = cube_stride * ((float)y - cube_size / 2); float off_z = -100.0f + cube_stride * ((float)z - cube_size / 2); for (unsigned v = 0; v < CUBE_VERTS; v++) { cube.vertices[v] = vertex_data[indices[v]]; cube.vertices[v].vert[0] += off_x; cube.vertices[v].vert[1] += off_y; cube.vertices[v].vert[2] += off_z; } } } } glBufferData(GL_ARRAY_BUFFER, cube_size * cube_size * cube_size * sizeof(Cube), &cubes[0], GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } glDrawArrays(GL_TRIANGLES, 0, CUBE_VERTS * cube_size * cube_size * cube_size); glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableVertexAttribArray(vloc); glDisableVertexAttribArray(nloc); glDisableVertexAttribArray(tcloc); } #if 0 static bool check_closest_cube(vec3 cube_max, vec3 closest_cube) { return true;/*(((closest_cube.x > 0.0f) && (closest_cube.y > 0.0f) && (closest_cube.z > 0.0f)) && ((closest_cube.x < cube_max.x) && (closest_cube.y < cube_max.y) && (closest_cube.z < cube_max.z)));*/ } static bool check_cube_distance_per_dimension(vec3 cube) { return cube.x * cube.x + cube.y * cube.y + cube.z * cube.z < 25.0f; } static void hit(vec3 cube) { (void)cube; } static void check_collision_cube(void) { //float cube_origin = cube_stride * ((float)cube_size / -2.0f); // emulate cube origin at {0, 0, 0} vec3 shifted_player_pos = player_pos; shifted_player_pos.z += 100; vec3 closest_cube = vec3(0, 0, 0);//round((shifted_player_pos - cube_origin) / cube_stride); vec3 closest_cube_pos = vec3(0, 0, 0);//cube_origin + closest_cube * cube_stride; vec3 cube_distance = abs(shifted_player_pos - closest_cube_pos); vec3 cube_size_max = (vec3)((float)cube_size - 1); #if 0 if (log_cb) { log_cb(RETRO_LOG_INFO, "cube_origin: %f\n", cube_origin); log_cb(RETRO_LOG_INFO, "shifted_player_pos: %f %f %f\n", shifted_player_pos.x, shifted_player_pos.y, shifted_player_pos.z); log_cb(RETRO_LOG_INFO, "cube: %f %f %f\n", closest_cube.x, closest_cube.y, closest_cube.z); log_cb(RETRO_LOG_INFO, "cube_pos: %f %f %f\n", closest_cube_pos.x, closest_cube_pos.y, closest_cube_pos.z); log_cb(RETRO_LOG_INFO, "cube_distance: %f %f %f\n", cube_distance.x, cube_distance.y, cube_distance.z); } #endif if (check_closest_cube(cube_size_max, closest_cube) && check_cube_distance_per_dimension(cube_distance)) hit(closest_cube); } #endif static vec3 instancingviewer_check_input(void) { static unsigned select_timeout = 0; input_poll_cb(); int x = input_state_cb(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); int y = input_state_cb(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); int analog_x = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X); int analog_y = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y); int analog_ry = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); int analog_rx = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); if (sensor_enable) { if (sensor_cb.get_sensor_input) { analog_rx = sensor_cb.get_sensor_input(0, RETRO_SENSOR_ACCELEROMETER_X); analog_ry = sensor_cb.get_sensor_input(0, RETRO_SENSOR_ACCELEROMETER_Y); log_cb(RETRO_LOG_INFO, "Sensor accelerometer X: %d\n", analog_rx); log_cb(RETRO_LOG_INFO, "Sensor accelerometer Y: %d\n", analog_ry); } } if (abs(analog_rx) < 10000) analog_rx = 0; if (abs(analog_ry) < 10000) analog_ry = 0; if (abs(analog_x) < 10000) analog_x = 0; if (abs(analog_y) < 10000) analog_y = 0; if (analog_ry) y = analog_ry; if (analog_rx) x = analog_rx; x = std::max(std::min(x, 20), -20); y = std::max(std::min(y, 20), -20); camera_rot_x -= 0.20 * x; camera_rot_y -= 0.10 * y; camera_rot_y = std::max(std::min(camera_rot_y, 80.0f), -80.0f); mat4 look_rot_x = rotate(mat4(1.0), camera_rot_x, vec3(0, 1, 0)); mat4 look_rot_y = rotate(mat4(1.0), camera_rot_y, vec3(1, 0, 0)); vec3 look_dir = vec3(look_rot_x * look_rot_y * vec4(0, 0, -1, 0)); vec3 look_dir_side = vec3(look_rot_x * vec4(1, 0, 0, 0)); mat3 s = mat3(scale(mat4(1.0), vec3(0.25, 0.25, 0.25))); if (location_camera_control_enable) { if (loc_float_greater_than(current_location.longitude, previous_location.longitude)) player_pos += s * look_dir; else if (loc_float_lesser_than(current_location.longitude, previous_location.longitude)) player_pos -= s * look_dir; } if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) player_pos += s * look_dir; if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) player_pos -= s * look_dir; if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) player_pos -= s * look_dir_side; if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) player_pos += s * look_dir_side; else if (select_timeout != 0) select_timeout--; bool start_pressed = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START); if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2)) { if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) { if (start_pressed) light_r -= 0.01; else ambient_light_r -= 0.01; } else { if (start_pressed) light_r += 0.01; else ambient_light_r += 0.01; } } if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) { if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) { if (start_pressed) light_g -= 0.01; else ambient_light_g -= 0.01; } else { if (start_pressed) light_g += 0.01; else ambient_light_g += 0.01; } } if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3)) { if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) { if (start_pressed) light_b -= 0.01; else ambient_light_b -= 0.01; } else { if (start_pressed) light_b += 0.01; else ambient_light_b += 0.01; } } //check_collision_cube(); return look_dir; } static void print_shader_log(GLuint shader) { GLsizei len = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); if (!len) return; char *buffer = new char[len]; glGetShaderInfoLog(shader, len, &len, buffer); log_cb(RETRO_LOG_INFO, ":%s\n", buffer); delete[] buffer; } static GLuint load_texture(const char *path) { uint8_t *data; unsigned width, height; if (strstr(path, ".png")) { if (!rpng_load_image_rgba(path, &data, &width, &height)) { log_cb(RETRO_LOG_ERROR, "Couldn't load PNG texture: %s\n", path); return 0; } } else if (strstr(path, ".tga")) { if (!texture_image_load_tga(path, data, width, height)) { log_cb(RETRO_LOG_ERROR, "Couldn't load TGA texture: %s\n", path); return 0; } } else if (strstr(path, ".jpeg") || strstr(path, ".jpg")) { pjpeg_scan_type_t scan; int comps, reduce; reduce = 0; data = (uint8_t*)pjpeg_load_from_file(path, &width, &height, &comps, &scan, reduce); if (!data) { log_cb(RETRO_LOG_ERROR, "Couldn't load JPEG texture: %s\n", path); return 0; } } GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); free(data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); return tex; } static void instancingviewer_compile_shaders(void) { prog = glCreateProgram(); GLuint vert = glCreateShader(GL_VERTEX_SHADER); GLuint frag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(vert, ARRAY_SIZE(vertex_shader), vertex_shader, 0); glShaderSource(frag, ARRAY_SIZE(fragment_shader), fragment_shader, 0); glCompileShader(vert); glCompileShader(frag); int status = 0; glGetShaderiv(vert, GL_COMPILE_STATUS, &status); if (!status && log_cb) { log_cb(RETRO_LOG_ERROR, "Vertex shader failed to compile!\n"); print_shader_log(vert); } glGetShaderiv(frag, GL_COMPILE_STATUS, &status); if (!status && log_cb) { log_cb(RETRO_LOG_ERROR, "Fragment shader failed to compile!\n"); print_shader_log(frag); } glAttachShader(prog, vert); glAttachShader(prog, frag); glLinkProgram(prog); glGetProgramiv(prog, GL_LINK_STATUS, &status); if (!status && log_cb) log_cb(RETRO_LOG_ERROR, "Program failed to link!\n"); background_prog = glCreateProgram(); GLuint background_vert = glCreateShader(GL_VERTEX_SHADER); GLuint background_frag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(background_vert, ARRAY_SIZE(background_vertex_shader), background_vertex_shader, 0); glShaderSource(background_frag, ARRAY_SIZE(background_fragment_shader), background_fragment_shader, 0); glCompileShader(background_vert); glCompileShader(background_frag); glGetShaderiv(background_vert, GL_COMPILE_STATUS, &status); if (!status && log_cb) { log_cb(RETRO_LOG_ERROR, "Background vertex shader failed to compile!\n"); print_shader_log(background_vert); } glGetShaderiv(background_frag, GL_COMPILE_STATUS, &status); if (!status && log_cb) { log_cb(RETRO_LOG_ERROR, "Background fragment shader failed to compile!\n"); print_shader_log(background_frag); } glAttachShader(background_prog, background_vert); glAttachShader(background_prog, background_frag); glLinkProgram(background_prog); glGetProgramiv(background_prog, GL_LINK_STATUS, &status); if (!status && log_cb) log_cb(RETRO_LOG_ERROR, "Background program failed to link!\n"); glBindBuffer(GL_ARRAY_BUFFER, background_vbo); int vloc = glGetAttribLocation(background_prog, "VertexCoord"); glVertexAttribPointer(vloc, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (void*)(0)); glEnableVertexAttribArray(vloc); int tcloc = glGetAttribLocation(background_prog, "TexCoord"); glVertexAttribPointer(tcloc, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (void*)(sizeof(GLfloat) * 2)); glEnableVertexAttribArray(tcloc); glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(GLfloat) * 4, &background_data[0], GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableVertexAttribArray(tcloc); glDisableVertexAttribArray(vloc); tex = 0; if (!camera_enable) tex = load_texture(texpath.c_str()); } static void instancingviewer_context_reset(void) { if (log_cb) log_cb(RETRO_LOG_INFO, "Context reset!\n"); rglgen_resolve_symbols(hw_render.get_proc_address); glGenBuffers(1, &vbo); glGenBuffers(1, &background_vbo); instancingviewer_compile_shaders(); update = true; } static void instancingviewer_update_variables(retro_environment_t environ_cb) { struct retro_variable var; var.key = "3dengine-cube-size"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { cube_size = atoi(var.value); update = true; if (!first_init) instancingviewer_context_reset(); } var.key = "3dengine-cube-stride"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { cube_stride = atof(var.value); update = true; if (!first_init) instancingviewer_context_reset(); } } static void instancingviewer_run(void) { vec3 look_dir = instancingviewer_check_input(); glBindFramebuffer(GL_FRAMEBUFFER, hw_render.get_current_framebuffer()); glClearColor(0.1, 0.1, 0.1, 1.0); glViewport(0, 0, engine_width, engine_height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(background_prog); glDisable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); int texloc = glGetUniformLocation(background_prog, "Texture"); glUniform1i(texloc, 0); glActiveTexture(GL_TEXTURE0); glBindBuffer(GL_ARRAY_BUFFER, background_vbo); int vloc = glGetAttribLocation(background_prog, "VertexCoord"); glVertexAttribPointer(vloc, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (void*)(0)); glEnableVertexAttribArray(vloc); int tcloc = glGetAttribLocation(background_prog, "TexCoord"); glVertexAttribPointer(tcloc, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (void*)(sizeof(GLfloat) * 2)); glEnableVertexAttribArray(tcloc); glBindTexture(g_texture_target, tex); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(tcloc); glDisableVertexAttribArray(vloc); glUseProgram(prog); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); int tloc = glGetUniformLocation(prog, "uTexture"); glUniform1i(tloc, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(g_texture_target, tex); int lloc = glGetUniformLocation(prog, "light_pos"); vec3_t light_pos; light_pos[0] = light_r; light_pos[1] = light_g; light_pos[2] = light_b; glUniform3fv(lloc, 1, &light_pos[0]); vec4 ambient_light(ambient_light_r, ambient_light_g, ambient_light_b, ambient_light_a); lloc = glGetUniformLocation(prog, "ambient_light"); glUniform4fv(lloc, 1, &ambient_light[0]); int vploc = glGetUniformLocation(prog, "uVP"); mat4 view = lookAt(player_pos, player_pos + look_dir, vec3(0, 1, 0)); mat4 proj = scale(mat4(1.0), vec3(1, -1, 1)) * perspective(45.0f, 640.0f / 480.0f, 5.0f, 500.0f); mat4 vp = proj * view; glUniformMatrix4fv(vploc, 1, GL_FALSE, &vp[0][0]); int modelloc = glGetUniformLocation(prog, "uM"); mat4 model = mat4(1.0); glUniformMatrix4fv(modelloc, 1, GL_FALSE, &model[0][0]); display_cubes_array(); glUseProgram(0); glBindTexture(g_texture_target, 0); video_cb(RETRO_HW_FRAME_BUFFER_VALID, engine_width, engine_height, 0); } static void instancingviewer_load_game(const struct retro_game_info *info) { player_pos = vec3(0, 0, 0); texpath = info->path; first_init = false; light_r = 0; light_g = 150; light_b = 15; ambient_light_r = 0.2; ambient_light_g = 0.2; ambient_light_b = 0.2; ambient_light_a = 1.0; } const engine_program_t engine_program_instancingviewer = { instancingviewer_load_game, instancingviewer_run, instancingviewer_context_reset, instancingviewer_update_variables, instancingviewer_check_input, };