#ifdef QB64_MACOSX # include #endif // trigger recompilation = 3 int32 displayorder_screen = 1; int32 displayorder_hardware = 2; int32 displayorder_glrender = 3; int32 displayorder_hardware1 = 4; // sub__displayorder( 1 , 2 , 4 , 3 ); // id.specialformat = // "[{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}[,{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}[,{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}[,{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}]]]]" void sub__displayorder(int32 method1, int32 method2, int32 method3, int32 method4) { // check no value has been used twice if (method1 != 0) if (method1 == method2 || method1 == method3 || method1 == method4) { error(5); return; } if (method2 != 0) if (method2 == method1 || method2 == method3 || method2 == method4) { error(5); return; } if (method3 != 0) if (method3 == method1 || method3 == method2 || method3 == method4) { error(5); return; } if (method4 != 0) if (method4 == method1 || method4 == method2 || method4 == method3) { error(5); return; } displayorder_screen = 0; displayorder_hardware = 0; displayorder_hardware1 = 0; displayorder_glrender = 0; static int32 i, method; for (i = 1; i <= 4; i++) { if (i == 1) method = method1; if (i == 2) method = method2; if (i == 3) method = method3; if (i == 4) method = method4; if (method == 1) displayorder_screen = i; if (method == 2) displayorder_hardware = i; if (method == 3) displayorder_hardware1 = i; if (method == 4) displayorder_glrender = i; } } // int32 gl_render_method=2; //1=behind, 2=ontop[default], 3=only void sub__glrender(int32 method) { // gl_render_method=method; if (method == 1) sub__displayorder(4, 1, 2, 3); if (method == 2) sub__displayorder(1, 2, 4, 3); if (method == 3) sub__displayorder(4, 0, 0, 0); } #ifndef QB64_GUI // begin stubs #else // end stubs void GLUT_RESHAPE_FUNC(int width, int height) { resize_event_x = width; resize_event_y = height; resize_event = -1; display_x_prev = display_x, display_y_prev = display_y; display_x = width; display_y = height; resize_pending = 0; os_resize_event = 1; set_view(VIEW_MODE__UNKNOWN); //***glutReshapeWindow(...) has no effect if called // within GLUT_RESHAPE_FUNC*** } // displaycall is the window of time to update our display # ifdef DEPENDENCY_GL extern void SUB__GL(); # endif # define GL_BGR 0x80E0 # define GL_BGRA 0x80E1 /* reference struct hardware_img_struct{ int32 w; int32 h; int32 texture_handle; int32 dest_context_handle;//used when rendering other images onto this image int32 temp;//if =1, delete immediately after use } list *hardware_img_handles=NULL; */ void free_hardware_img(int32 handle, int32 caller_id) { hardware_img_struct *hardware_img; hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle); if (hardware_img == NULL) { alert("free_hardware_img: image does not exist"); } if (hardware_img->dest_context_handle) { GLuint context = (GLuint)hardware_img->dest_context_handle; glDeleteFramebuffersEXT(1, &context); } if (hardware_img->depthbuffer_handle) { GLuint depthbuffer_handle = (GLuint)hardware_img->depthbuffer_handle; glDeleteFramebuffersEXT(1, &depthbuffer_handle); } GLuint texture = (GLuint)hardware_img->texture_handle; glDeleteTextures(1, &texture); // test reasset of hardware+img // hardware_img=(hardware_img_struct*)list_get(hardware_img_handles,handle); // if (hardware_img==NULL) alert("free_hardware_img: image does not exist"); // if image has not been used, it may still have buffered pixel content if (hardware_img->software_pixel_buffer != NULL) { free(hardware_img->software_pixel_buffer); } list_remove(hardware_img_handles, handle); } /* int32 new_hardware_frame(int32 x, int32 y){ int32 handle=new_hardware_frame_handle(); glBindTexture (GL_TEXTURE_2D, handle); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); return handle; } void free_hardware_frame(int32 handle){ GLuint texture=(GLuint)handle; glDeleteTextures(1, &texture); } */ void prepare_environment_2d() { // called prior to rendering 2D content // precalculate critical dimensions, offsets & ratios static int32 can_scale; // can the screen be scaled on the window can_scale = 0; static int32 need_square_pixels; // do we need square_pixels? if not we can // stretch the screen need_square_pixels = 0; environment_2d__screen_smooth = 0; environment_2d__letterbox = 0; if (full_screen > 0) { // in full screen // reference: ---int32 // full_screen_set=-1;//0(windowed),1(stretched/closest),2(1:1)--- can_scale = 1; if (full_screen == 2) need_square_pixels = 1; // note: 'letter-boxing' is only requred where the size of the window // cannot be controlled, and the only place where this is the // case is full screen mode _SQUAREPIXELS environment_2d__screen_smooth = fullscreen_smooth; } else { // windowed if (resize_auto > 0) { // 1=STRETCH,2=SMOOTH can_scale = 1; if (resize_auto == 2) environment_2d__screen_smooth = 1; // note: screen will fix its aspect ratio automatically, so there is // no need to enforce squarepixels } } if (environment_2d__screen_width == environment__window_width && environment_2d__screen_height == environment__window_height) { // screen size matches window environment_2d__screen_x1 = 0; environment_2d__screen_y1 = 0; environment_2d__screen_x2 = environment_2d__screen_width - 1; environment_2d__screen_y2 = environment_2d__screen_height - 1; environment_2d__screen_x_scale = 1.0f; environment_2d__screen_y_scale = 1.0f; environment_2d__screen_scaled_width = environment_2d__screen_width; environment_2d__screen_scaled_height = environment_2d__screen_height; environment_2d__screen_smooth = 0; // no smoothing required } else { // screen size does not match // calculate ratios static float window_ratio; static float screen_ratio; window_ratio = (float)environment__window_width / (float)environment__window_height; screen_ratio = (float)environment_2d__screen_width / (float)environment_2d__screen_height; if (can_scale == 0) { // screen will appear in the top-left of the window with blank space // on the bottom & right environment_2d__screen_x1 = 0; environment_2d__screen_y1 = 0; environment_2d__screen_x2 = environment_2d__screen_width - 1; environment_2d__screen_y2 = environment_2d__screen_height - 1; goto cant_scale; } if (need_square_pixels == 0 || (window_ratio == screen_ratio)) { // can stretch, no 'letter-box' required environment_2d__screen_x1 = 0; environment_2d__screen_y1 = 0; environment_2d__screen_x2 = environment__window_width - 1; environment_2d__screen_y2 = environment__window_height - 1; } else { //'letter-box' required // this section needs revision static float x_scale, y_scale; static int32 x1, y1, x2, y2, z, x_limit, y_limit, x_offset, y_offset; // x_scale=(float)environment_2d__screen_width/(float)environment__window_width; // y_scale=(float)environment_2d__screen_height/(float)environment__window_height; // x_offset=0; y_offset=0; x1 = 0; y1 = 0; x2 = environment__window_width - 1; y2 = environment__window_height - 1; // x_limit=x2; y_limit=y2; if (window_ratio > screen_ratio) { // pad sides z = (float)environment__window_height * screen_ratio; // new width x1 = environment__window_width / 2 - z / 2; x2 = x1 + z - 1; environment_2d__letterbox = 1; // vertical black stripes // required // x_offset=-x1; // x_scale=(float)environment_2d__screen_width/(float)z; // x_limit=z-1; } else { // pad top/bottom z = (float)environment__window_width / screen_ratio; // new height y1 = environment__window_height / 2 - z / 2; y2 = y1 + z - 1; environment_2d__letterbox = 2; // horizontal black stripes required // y_offset=-y1; // y_scale=(float)environment_2d__screen_height/(float)z; // y_limit=z-1; } environment_2d__screen_x1 = x1; environment_2d__screen_y1 = y1; environment_2d__screen_x2 = x2; environment_2d__screen_y2 = y2; } cant_scale: environment_2d__screen_scaled_width = environment_2d__screen_x2 - environment_2d__screen_x1 + 1; environment_2d__screen_scaled_height = environment_2d__screen_y2 - environment_2d__screen_y1 + 1; environment_2d__screen_x_scale = (float)environment_2d__screen_scaled_width / (float)environment_2d__screen_width; environment_2d__screen_y_scale = (float)environment_2d__screen_scaled_height / (float)environment_2d__screen_height; } } // prepare_environment_2d int32 environment_2d__get_window_x1_coord(int32 x) { return qbr_float_to_long(((float)x) * environment_2d__screen_x_scale) + environment_2d__screen_x1; } int32 environment_2d__get_window_y1_coord(int32 y) { return qbr_float_to_long((float)y * environment_2d__screen_y_scale) + environment_2d__screen_y1; } int32 environment_2d__get_window_x2_coord(int32 x) { return qbr_float_to_long(((float)x + 1.0f) * environment_2d__screen_x_scale - 1.0f) + environment_2d__screen_x1; } int32 environment_2d__get_window_y2_coord(int32 y) { return qbr_float_to_long(((float)y + 1.0f) * environment_2d__screen_y_scale - 1.0f) + environment_2d__screen_y1; } struct environment_2d__window_rect_struct { int32 x1; int32 y1; int32 x2; int32 y2; }; // this functions returns a constant rect dimensions to stop warping of image environment_2d__window_rect_struct tmp_rect; environment_2d__window_rect_struct *environment_2d__screen_to_window_rect(int32 x1, int32 y1, int32 x2, int32 y2) { tmp_rect.x1 = qbr_float_to_long(((float)x1) * environment_2d__screen_x_scale) + environment_2d__screen_x1; tmp_rect.y1 = qbr_float_to_long(((float)y1) * environment_2d__screen_y_scale) + environment_2d__screen_y1; static int32 w, h; w = abs(x2 - x1) + 1; h = abs(y2 - y1) + 1; // force round upwards to correct gaps when tiling w = ((float)w) * environment_2d__screen_x_scale + 0.99f; h = ((float)h) * environment_2d__screen_y_scale + 0.99f; tmp_rect.x2 = w - 1 + tmp_rect.x1; tmp_rect.y2 = h - 1 + tmp_rect.y1; //(code which doesn't support tiling) // tmp_rect.x2=qbr_float_to_long(((float)w)*environment_2d__screen_x_scale-1.0f)+tmp_rect.x1; // tmp_rect.y2=qbr_float_to_long(((float)h)*environment_2d__screen_y_scale-1.0f)+tmp_rect.y1; return &tmp_rect; } float *hardware_buffer_vertices = (float *)malloc(sizeof(float) * 1); int32 hardware_buffer_vertices_max = 1; int32 hardware_buffer_vertices_count = 0; float *hardware_buffer_texcoords = (float *)malloc(sizeof(float) * 1); int32 hardware_buffer_texcoords_max = 1; int32 hardware_buffer_texcoords_count = 0; void hardware_buffer_flush() { if (hardware_buffer_vertices_count) { // ref: // http://stackoverflow.com/questions/5009014/draw-square-with-opengl-es-for-ios if (hardware_buffer_vertices_count == hardware_buffer_texcoords_count) { glVertexPointer(2, GL_FLOAT, 2 * sizeof(GL_FLOAT), hardware_buffer_vertices); // http://www.opengl.org/sdk/docs/man2/xhtml/glVertexPointer.xml glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GL_FLOAT), hardware_buffer_texcoords); // http://www.opengl.org/sdk/docs/man2/xhtml/glTexCoordPointer.xml glDrawArrays(GL_TRIANGLES, 0, hardware_buffer_vertices_count / 2); // start index, number of indexes } else { glVertexPointer(3, GL_FLOAT, 3 * sizeof(GL_FLOAT), hardware_buffer_vertices); // http://www.opengl.org/sdk/docs/man2/xhtml/glVertexPointer.xml glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GL_FLOAT), hardware_buffer_texcoords); // http://www.opengl.org/sdk/docs/man2/xhtml/glTexCoordPointer.xml glDrawArrays(GL_TRIANGLES, 0, hardware_buffer_vertices_count / 3); // start index, number of indexes } hardware_buffer_vertices_count = 0; hardware_buffer_texcoords_count = 0; } } void set_smooth(int32 new_mode_shrunk, int32 new_mode_stretched) { static int32 current_mode_shrunk; current_mode_shrunk = render_state.source->smooth_shrunk; static int32 current_mode_stretched; current_mode_stretched = render_state.source->smooth_stretched; if (new_mode_shrunk == current_mode_shrunk && new_mode_stretched == current_mode_stretched) return; hardware_buffer_flush(); if (new_mode_shrunk == SMOOTH_MODE__DONT_SMOOTH) { if (render_state.source->PO2_fix == PO2_FIX__MIPMAPPED) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Use _MAPTRIANGLE's _SMOOTHSHRUNK to // apply linear filtering here } } if (new_mode_shrunk == SMOOTH_MODE__SMOOTH) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } if (new_mode_stretched == SMOOTH_MODE__DONT_SMOOTH) { if (render_state.source->PO2_fix == PO2_FIX__MIPMAPPED) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } if (new_mode_stretched == SMOOTH_MODE__SMOOTH) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } render_state.source->smooth_shrunk = new_mode_shrunk; render_state.source->smooth_stretched = new_mode_stretched; } void set_texture_wrap(int32 new_mode) { static int32 current_mode; current_mode = render_state.source->texture_wrap; if (new_mode == current_mode) return; hardware_buffer_flush(); if (new_mode == TEXTURE_WRAP_MODE__DONT_WRAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } if (new_mode == TEXTURE_WRAP_MODE__WRAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } render_state.source->texture_wrap = new_mode; } void set_alpha(int32 new_mode) { static int32 current_mode; current_mode = render_state.use_alpha; if (new_mode == current_mode) return; hardware_buffer_flush(); if (new_mode == ALPHA_MODE__DONT_BLEND) { glDisable(GL_BLEND); } if (new_mode == ALPHA_MODE__BLEND) { glEnable(GL_BLEND); if (framebufferobjects_supported) { # ifndef QB64_GLES // glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, // GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); # else glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); # endif } else { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } render_state.use_alpha = new_mode; } void set_depthbuffer(int32 new_mode) { static int32 current_mode; current_mode = render_state.depthbuffer_mode; if (new_mode == current_mode) return; hardware_buffer_flush(); if (new_mode == DEPTHBUFFER_MODE__OFF) { glDisable(GL_DEPTH_TEST); glAlphaFunc(GL_ALWAYS, 0); } if (new_mode == DEPTHBUFFER_MODE__ON) { glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glAlphaFunc(GL_GREATER, 0.001); glEnable(GL_ALPHA_TEST); } if (new_mode == DEPTHBUFFER_MODE__LOCKED) { glEnable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glAlphaFunc(GL_ALWAYS, 0); } render_state.depthbuffer_mode = new_mode; } void set_cull_mode(int32 new_mode) { static int32 current_mode; current_mode = render_state.cull_mode; if (new_mode == current_mode) return; hardware_buffer_flush(); if (new_mode == CULL_MODE__NONE) { glDisable(GL_CULL_FACE); } if (new_mode == CULL_MODE__CLOCKWISE_ONLY) { glFrontFace(GL_CW); if (current_mode != CULL_MODE__ANTICLOCKWISE_ONLY) glEnable(GL_CULL_FACE); } if (new_mode == CULL_MODE__ANTICLOCKWISE_ONLY) { glFrontFace(GL_CCW); if (current_mode != CULL_MODE__CLOCKWISE_ONLY) glEnable(GL_CULL_FACE); } render_state.cull_mode = new_mode; } void set_view(int32 new_mode) { // set view can only be called after the correct // destination is chosen static int32 current_mode; current_mode = render_state.view_mode; if (new_mode == current_mode) return; hardware_buffer_flush(); if (new_mode == VIEW_MODE__RESET) { glDisable(GL_TEXTURE_2D); glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); glDisable(GL_COLOR_MATERIAL); glDisable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDisable(GL_LIGHTING); glFrontFace(GL_CCW); glCullFace(GL_BACK); glDisable(GL_CULL_FACE); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glAlphaFunc(GL_ALWAYS, 0); if (framebufferobjects_supported) glBindFramebufferEXT(GL_FRAMEBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); glClear(GL_DEPTH_BUFFER_BIT); glColor4f(1.f, 1.f, 1.f, 1.f); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // invalidate current states set_alpha(ALPHA_MODE__UNKNOWN); set_depthbuffer(DEPTHBUFFER_MODE__UNKNOWN); set_cull_mode(CULL_MODE__UNKNOWN); set_render_source(INVALID_HARDWARE_HANDLE); set_render_dest(INVALID_HARDWARE_HANDLE); new_mode = VIEW_MODE__UNKNOWN; // resets are performed before unknown // operations are executed } if (new_mode == VIEW_MODE__2D) { if (current_mode != VIEW_MODE__3D) { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); set_alpha(ALPHA_MODE__BLEND); glEnable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glCullFace(GL_BACK); } if (render_state.dest_handle == 0) { static int32 dst_w, dst_h; static int32 scale_factor = 0; # ifdef QB64_MACOSX if (scale_factor == 0) { // by default scale_factor should be 1, but in macOS Catalina // (10.15.*) scale_factor must be setted in 2 // * in cases where the app is executed on system with Retina // Display scale_factor = 1; // by default // lookup for retina/5k output from system_profiler (storing all // outpun in stream) bool b_isRetina, b_is5k; FILE *consoleStream = popen("system_profiler SPDisplaysDataType", "r"); if (consoleStream) { char buffer[128]; while (!feof(consoleStream)) { if (fgets(buffer, 128, consoleStream) != NULL) { string szBuffer(buffer); if (!b_isRetina) b_isRetina = (szBuffer.rfind("Retina") != ULONG_MAX); if (!b_is5k) b_is5k = (szBuffer.rfind("5K") != ULONG_MAX); } } } pclose(consoleStream); if (b_isRetina || b_is5k) { // apply only factor = 2 if macOS is Catalina (11.15.* // // kern.osrelease 19.*) char str[256]; size_t size = sizeof(str); int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0); string sz_osrelease(str); if (sz_osrelease.rfind("19.") == 0) scale_factor = 2; } } # else scale_factor = 1; # endif dst_w = environment__window_width; dst_h = environment__window_height; // alert(dst_w); // alert(dst_h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, dst_w, 0.0, dst_h, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glScalef(1, -1, 1); // flip vertically glTranslatef(0, -dst_h, 0); // move to new vertical position glViewport(0, 0, dst_w * scale_factor, dst_h * scale_factor); } else { static hardware_img_struct *hardware_img; hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, render_state.dest_handle); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, hardware_img->w, 0, hardware_img->h); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0, 0, hardware_img->w, hardware_img->h); } } if (new_mode == VIEW_MODE__3D) { if (current_mode != VIEW_MODE__2D) { glColor4f(1.f, 1.f, 1.f, 1.f); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); set_alpha(ALPHA_MODE__BLEND); glEnable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glCullFace(GL_BACK); } if (render_state.dest_handle == 0) { static int32 dst_w, dst_h; dst_w = environment__window_width; dst_h = environment__window_height; glViewport(0, 0, (GLsizei)dst_w, (GLsizei)dst_h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // note: the max FOV is 90-degrees (this maximum applies to the // longest screen dimension) float fov; if (environment_2d__screen_scaled_width > environment_2d__screen_scaled_height) { fov = 90.0f * ((float)environment__window_width / (float)environment_2d__screen_scaled_width); // convert fov from horizontal to vertical fov = fov * ((float)dst_h / (float)dst_w); } else { fov = 90.0f * ((float)environment__window_height / (float)environment_2d__screen_scaled_height); } gluPerspective(fov, (GLfloat)dst_w / (GLfloat)dst_h, 0.1, 10000.0); // Set the Field of view angle (in // degrees), the aspect ratio of our // window, and the new and far planes glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } else { static hardware_img_struct *hardware_img; hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, render_state.dest_handle); static int32 dst_w, dst_h; dst_w = hardware_img->w; dst_h = hardware_img->h; glViewport(0, 0, (GLsizei)dst_w, (GLsizei)dst_h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glScalef(1.0, -1.0, 1.0); // note: the max FOV is 90-degrees (this maximum applies to the // longest screen dimension) float fov; if (dst_w > dst_h) { fov = 90.0f; // convert fov from horizontal to vertical fov = fov * ((float)dst_h / (float)dst_w); } else { fov = 90.0f; } gluPerspective(fov, (GLfloat)dst_w / (GLfloat)dst_h, 0.1, 10000.0); // Set the Field of view angle (in // degrees), the aspect ratio of our // window, and the new and far planes glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // alert("3D rendering onto FBO not supported yet"); } } render_state.view_mode = new_mode; } // change_render_state void set_render_source(int32 new_handle) { if (new_handle == INVALID_HARDWARE_HANDLE) { hardware_buffer_flush(); render_state.source_handle = INVALID_HARDWARE_HANDLE; return; } int32 current_handle; current_handle = render_state.source_handle; if (current_handle == new_handle) return; hardware_buffer_flush(); hardware_img_struct *hardware_img; hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, new_handle); if (hardware_img->texture_handle == 0) hardware_img_buffer_to_texture(new_handle); glBindTexture(GL_TEXTURE_2D, hardware_img->texture_handle); render_state.source_handle = new_handle; render_state.source = &hardware_img->source_state; // note: some older systems require calling glTexParameterf after textures // are rebound if (framebufferobjects_supported == 0) { render_state.source->smooth_shrunk = SMOOTH_MODE__UNKNOWN; render_state.source->smooth_stretched = SMOOTH_MODE__UNKNOWN; } } void set_render_dest(int32 new_handle) { if (new_handle == INVALID_HARDWARE_HANDLE) { hardware_buffer_flush(); render_state.dest_handle = INVALID_HARDWARE_HANDLE; set_view(VIEW_MODE__UNKNOWN); return; } // 0=primary surface static int32 current_handle; current_handle = render_state.dest_handle; if (new_handle == current_handle) return; hardware_buffer_flush(); set_view(VIEW_MODE__UNKNOWN); if (new_handle == 0) { if (framebufferobjects_supported) glBindFramebufferEXT(GL_FRAMEBUFFER, 0); render_state.dest = &dest_render_state0; } else { static hardware_img_struct *hardware_img; hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, new_handle); // convert to regular texture first if necessary if (hardware_img->texture_handle == 0) hardware_img_buffer_to_texture(new_handle); // does it have a dest context/FBO? if not create one if (hardware_img->dest_context_handle == 0) { static GLuint framebuffer_handle; framebuffer_handle = 0; glGenFramebuffersEXT(1, &framebuffer_handle); glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_handle); hardware_img->dest_context_handle = framebuffer_handle; glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hardware_img->texture_handle, 0); // glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // glClear(GL_COLOR_BUFFER_BIT); glColor4f(1.f, 1.f, 1.f, 1.f); set_render_source(INVALID_HARDWARE_HANDLE); } else { glBindFramebufferEXT(GL_FRAMEBUFFER, hardware_img->dest_context_handle); } render_state.dest = &hardware_img->dest_state; } render_state.dest_handle = new_handle; } void hardware_img_put(int32 dst_x1, int32 dst_y1, int32 dst_x2, int32 dst_y2, int32 src_img, int32 dst_img, int32 src_x1, int32 src_y1, int32 src_x2, int32 src_y2, int32 use_alpha, int32 smooth) { if (dst_img < 0) dst_img = 0; // both layers render to the primary context // ensure dst_x1/y1 represent top-left co-ordinate of destination static int32 swap_tmp; if (dst_x2 < dst_x1) { swap_tmp = dst_x2; dst_x2 = dst_x1; dst_x1 = swap_tmp; swap_tmp = src_x2; src_x2 = src_x1; src_x1 = swap_tmp; } if (dst_y2 < dst_y1) { swap_tmp = dst_y2; dst_y2 = dst_y1; dst_y1 = swap_tmp; swap_tmp = src_y2; src_y2 = src_y1; src_y1 = swap_tmp; } set_render_dest(dst_img); set_view(VIEW_MODE__2D); if (dst_img) { // static hardware_img_struct* dst_hardware_img; // dst_hardware_img=(hardware_img_struct*)list_get(hardware_img_handles,dst_img); //(no specific action required here --area reserved for future use) } else { // dest is 0 environment_2d__window_rect_struct *rect; rect = environment_2d__screen_to_window_rect(dst_x1, dst_y1, dst_x2, dst_y2); dst_x1 = rect->x1; dst_y1 = rect->y1; dst_x2 = rect->x2; dst_y2 = rect->y2; } set_render_source(src_img); static hardware_img_struct *src_hardware_img; static int32 src_h, src_w; src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src_img); src_h = src_hardware_img->h; src_w = src_hardware_img->w; if (smooth) { set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__SMOOTH); } else { set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__DONT_SMOOTH); } if (use_alpha) { set_alpha(ALPHA_MODE__BLEND); } else { set_alpha(ALPHA_MODE__DONT_BLEND); } set_depthbuffer(DEPTHBUFFER_MODE__OFF); set_cull_mode(CULL_MODE__NONE); set_texture_wrap(TEXTURE_WRAP_MODE__DONT_WRAP); // adjust for render (x2 & y2 need to be one greater than the destination // offset) dst_x2++; dst_y2++; if (src_hardware_img->source_state.PO2_fix) { src_w = src_hardware_img->PO2_w; src_h = src_hardware_img->PO2_h; } // calc source texture co-ordinates static float x1f, y1f, x2f, y2f; if (src_x1 <= src_x2) { x1f = ((float)src_x1 + 0.01f) / (float)src_w; x2f = ((float)src_x2 + 0.99f) / (float)src_w; } else { x2f = ((float)src_x2 + 0.01f) / (float)src_w; x1f = ((float)src_x1 + 0.99f) / (float)src_w; } if (src_y1 <= src_y2) { y1f = ((float)src_y1 + 0.01f) / (float)src_h; y2f = ((float)src_y2 + 0.99f) / (float)src_h; } else { y2f = ((float)src_y2 + 0.01f) / (float)src_h; y1f = ((float)src_y1 + 0.99f) / (float)src_h; } // expand buffers if necessary if ((hardware_buffer_vertices_count + 18) > hardware_buffer_vertices_max) { hardware_buffer_vertices_max = hardware_buffer_vertices_max * 2 + 18; hardware_buffer_vertices = (float *)realloc(hardware_buffer_vertices, hardware_buffer_vertices_max * sizeof(float)); } if ((hardware_buffer_texcoords_count + 12) > hardware_buffer_texcoords_max) { hardware_buffer_texcoords_max = hardware_buffer_texcoords_max * 2 + 12; hardware_buffer_texcoords = (float *)realloc(hardware_buffer_texcoords, hardware_buffer_texcoords_max * sizeof(float)); } // clockwise hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f; // hardware_buffer_flush(); //uncomment for debugging only } void hardware_img_tri2d(float dst_x1, float dst_y1, float dst_x2, float dst_y2, float dst_x3, float dst_y3, int32 src_img, int32 dst_img, float src_x1, float src_y1, float src_x2, float src_y2, float src_x3, float src_y3, int32 use_alpha, int32 smooth) { if (dst_img < 0) dst_img = 0; // both layers render to the primary context set_render_dest(dst_img); set_view(VIEW_MODE__2D); if (dst_img) { static hardware_img_struct *dst_hardware_img; dst_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, dst_img); static int32 dst_w, dst_h; dst_w = dst_hardware_img->w; dst_h = dst_hardware_img->h; // SEAMLESS adjustments: // reduce texture co-ordinates (maintaining top-left) //(todo) // NON-SEAMLESS adjustments: // Extend rhs/bottom row to fill extra pixel space // calculate extents int32 rx1; int32 rx2; rx1 = dst_x1; if (dst_x2 < rx1) { rx1 = dst_x2; } if (dst_x3 < rx1) { rx1 = dst_x3; } rx2 = dst_x1; if (dst_x2 > rx2) { rx2 = dst_x2; } if (dst_x3 > rx2) { rx2 = dst_x3; } float xr; // the multiplier for where we should be (1=no change) if (rx1 == rx2) { xr = 1.0f; } else { xr = ((float)rx2 - (float)rx1 + 1.0) / ((float)rx2 - (float)rx1); } int32 ry1; int32 ry2; ry1 = dst_y1; if (dst_y2 < ry1) { ry1 = dst_y2; } if (dst_y3 < ry1) { ry1 = dst_y3; } ry2 = dst_y1; if (dst_y2 > ry2) { ry2 = dst_y2; } if (dst_y3 > ry2) { ry2 = dst_y3; } float yr; // the multiplier for where we should be (1=no change) if (ry1 == ry2) { yr = 1.0f; } else { yr = ((float)ry2 - (float)ry1 + 1.0f) / ((float)ry2 - (float)ry1); } // apply multipliers so right-most and bottom-most rows will be filled static int32 basex; basex = rx1; dst_x1 = qbr_float_to_long(((float)(dst_x1 - rx1)) * xr + (float)basex); dst_x2 = qbr_float_to_long(((float)(dst_x2 - rx1)) * xr + (float)basex); dst_x3 = qbr_float_to_long(((float)(dst_x3 - rx1)) * xr + (float)basex); static int32 basey; basey = ry1; dst_y1 = qbr_float_to_long(((float)(dst_y1 - ry1)) * yr + (float)basey); dst_y2 = qbr_float_to_long(((float)(dst_y2 - ry1)) * yr + (float)basey); dst_y3 = qbr_float_to_long(((float)(dst_y3 - ry1)) * yr + (float)basey); } else { // dest is 0 static int32 dst_w, dst_h; dst_w = environment__window_width; dst_h = environment__window_height; // SEAMLESS adjustments: // reduce texture co-ordinates (maintaining top-left) //(todo) // NON-SEAMLESS adjustments: // Extend rhs/bottom row to fill extra pixel space // calculate extents int32 rx1; int32 rx2; rx1 = dst_x1; if (dst_x2 < rx1) { rx1 = dst_x2; } if (dst_x3 < rx1) { rx1 = dst_x3; } rx2 = dst_x1; if (dst_x2 > rx2) { rx2 = dst_x2; } if (dst_x3 > rx2) { rx2 = dst_x3; } float xr; // the multiplier for where we should be (1=no change) if (rx1 == rx2) { xr = 1.0f; } else { xr = ((float)rx2 - (float)rx1 + 1.0) / ((float)rx2 - (float)rx1); } int32 ry1; int32 ry2; ry1 = dst_y1; if (dst_y2 < ry1) { ry1 = dst_y2; } if (dst_y3 < ry1) { ry1 = dst_y3; } ry2 = dst_y1; if (dst_y2 > ry2) { ry2 = dst_y2; } if (dst_y3 > ry2) { ry2 = dst_y3; } float yr; // the multiplier for where we should be (1=no change) if (ry1 == ry2) { yr = 1.0f; } else { yr = ((float)ry2 - (float)ry1 + 1.0f) / ((float)ry2 - (float)ry1); } // apply multipliers so right-most and bottom-most rows will be filled static int32 basex; basex = qbr_float_to_long(((float)(rx1)) * environment_2d__screen_x_scale + (float)environment_2d__screen_x1); dst_x1 = basex + qbr_float_to_long(((float)(dst_x1 - rx1)) * environment_2d__screen_x_scale * xr); dst_x2 = basex + qbr_float_to_long(((float)(dst_x2 - rx1)) * environment_2d__screen_x_scale * xr); dst_x3 = basex + qbr_float_to_long(((float)(dst_x3 - rx1)) * environment_2d__screen_x_scale * xr); static int32 basey; basey = qbr_float_to_long(((float)(ry1)) * environment_2d__screen_y_scale + (float)environment_2d__screen_y1); dst_y1 = basey + qbr_float_to_long(((float)(dst_y1 - ry1)) * environment_2d__screen_y_scale * yr); dst_y2 = basey + qbr_float_to_long(((float)(dst_y2 - ry1)) * environment_2d__screen_y_scale * yr); dst_y3 = basey + qbr_float_to_long(((float)(dst_y3 - ry1)) * environment_2d__screen_y_scale * yr); } set_render_source(src_img); static hardware_img_struct *src_hardware_img; static int32 src_h, src_w; src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src_img); src_h = src_hardware_img->h; src_w = src_hardware_img->w; if (smooth == 0) { set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__DONT_SMOOTH); } if (smooth == 1) { set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__SMOOTH); } if (smooth == 2) { set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__DONT_SMOOTH); } if (smooth == 3) { set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__SMOOTH); } set_texture_wrap(TEXTURE_WRAP_MODE__WRAP); if (use_alpha) { set_alpha(ALPHA_MODE__BLEND); } else { set_alpha(ALPHA_MODE__DONT_BLEND); } set_depthbuffer(DEPTHBUFFER_MODE__OFF); set_cull_mode(CULL_MODE__NONE); if (src_hardware_img->source_state.PO2_fix) { src_w = src_hardware_img->PO2_w; src_h = src_hardware_img->PO2_h; } // calc source texture co-ordinates static float x1f, y1f, x2f, y2f, x3f, y3f; x1f = ((float)src_x1 + 0.5f) / (float)src_w; x2f = ((float)src_x2 + 0.5f) / (float)src_w; x3f = ((float)src_x3 + 0.5f) / (float)src_w; y1f = ((float)src_y1 + 0.5f) / (float)src_h; y2f = ((float)src_y2 + 0.5f) / (float)src_h; y3f = ((float)src_y3 + 0.5f) / (float)src_h; // expand buffers if necessary if ((hardware_buffer_vertices_count + 9) > hardware_buffer_vertices_max) { hardware_buffer_vertices_max = hardware_buffer_vertices_max * 2 + 9; hardware_buffer_vertices = (float *)realloc(hardware_buffer_vertices, hardware_buffer_vertices_max * sizeof(float)); } if ((hardware_buffer_texcoords_count + 6) > hardware_buffer_texcoords_max) { hardware_buffer_texcoords_max = hardware_buffer_texcoords_max * 2 + 6; hardware_buffer_texcoords = (float *)realloc(hardware_buffer_texcoords, hardware_buffer_texcoords_max * sizeof(float)); } // clockwise hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x3; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y3; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x3f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y3f; // hardware_buffer_flush(); //uncomment for debugging only } void clear_depthbuffer(int32 dst_img) { hardware_buffer_flush(); if (dst_img < 0) dst_img = 0; // both layers render to the primary context set_render_dest(dst_img); if (dst_img > 0) { hardware_img_requires_depthbuffer((hardware_img_struct *)list_get(hardware_img_handles, dst_img)); } glClear(GL_DEPTH_BUFFER_BIT); } void hardware_img_tri3d(float dst_x1, float dst_y1, float dst_z1, float dst_x2, float dst_y2, float dst_z2, float dst_x3, float dst_y3, float dst_z3, int32 src_img, int32 dst_img, float src_x1, float src_y1, float src_x2, float src_y2, float src_x3, float src_y3, int32 use_alpha, int32 smooth, int32 cull_mode, int32 depthbuffer_mode) { if (dst_img < 0) dst_img = 0; // both layers render to the primary context set_render_dest(dst_img); set_view(VIEW_MODE__3D); if (dst_img) { static hardware_img_struct *dst_hardware_img; dst_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, dst_img); hardware_img_requires_depthbuffer(dst_hardware_img); } else { // dest is 0 } set_render_source(src_img); static hardware_img_struct *src_hardware_img; static int32 src_h, src_w; src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src_img); src_h = src_hardware_img->h; src_w = src_hardware_img->w; if (smooth == 0) { set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__DONT_SMOOTH); } if (smooth == 1) { set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__SMOOTH); } if (smooth == 2) { set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__DONT_SMOOTH); } if (smooth == 3) { set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__SMOOTH); } set_texture_wrap(TEXTURE_WRAP_MODE__WRAP); if (use_alpha) { set_alpha(ALPHA_MODE__BLEND); } else { set_alpha(ALPHA_MODE__DONT_BLEND); } set_depthbuffer(depthbuffer_mode); // on frame buffers the 3D perspective is flipped vertically reversing the // cull direction if (dst_img > 0) { if (cull_mode == CULL_MODE__CLOCKWISE_ONLY) { cull_mode = CULL_MODE__ANTICLOCKWISE_ONLY; } else { if (cull_mode == CULL_MODE__ANTICLOCKWISE_ONLY) cull_mode = CULL_MODE__CLOCKWISE_ONLY; } } set_cull_mode(cull_mode); if (src_hardware_img->source_state.PO2_fix) { src_w = src_hardware_img->PO2_w; src_h = src_hardware_img->PO2_h; } // calc source texture co-ordinates static float x1f, y1f, x2f, y2f, x3f, y3f; x1f = ((float)src_x1 + 0.5f) / (float)src_w; x2f = ((float)src_x2 + 0.5f) / (float)src_w; x3f = ((float)src_x3 + 0.5f) / (float)src_w; y1f = ((float)src_y1 + 0.5f) / (float)src_h; y2f = ((float)src_y2 + 0.5f) / (float)src_h; y3f = ((float)src_y3 + 0.5f) / (float)src_h; // expand buffers if necessary if ((hardware_buffer_vertices_count + 9) > hardware_buffer_vertices_max) { hardware_buffer_vertices_max = hardware_buffer_vertices_max * 2 + 9; hardware_buffer_vertices = (float *)realloc(hardware_buffer_vertices, hardware_buffer_vertices_max * sizeof(float)); } if ((hardware_buffer_texcoords_count + 6) > hardware_buffer_texcoords_max) { hardware_buffer_texcoords_max = hardware_buffer_texcoords_max * 2 + 6; hardware_buffer_texcoords = (float *)realloc(hardware_buffer_texcoords, hardware_buffer_texcoords_max * sizeof(float)); } hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_z1; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_z2; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x3; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y3; hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_z3; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x3f; hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y3f; // hardware_buffer_flush(); //uncomment for debugging only } static int32 software_screen_hardware_frame = 0; static int32 in_GLUT_DISPLAY_REQUEST = 0; void GLUT_DISPLAY_REQUEST() { if (in_GLUT_DISPLAY_REQUEST) { return; } in_GLUT_DISPLAY_REQUEST = 1; # ifdef QB64_MACOSX if (temp_window_title_set == 1) { glutSetWindowTitle((char *)window_title); temp_window_title_set = 0; } # endif // general use variables static int32 i, i2, i3; static int32 x, y, x2, y2; // determine which software frame to display static int32 last_i; // the last software frame displayed last_i = -1; for (i2 = 0; i2 <= 2; i2++) { if (display_frame[i2].state == DISPLAY_FRAME_STATE__DISPLAYING) { last_i = i2; } } i = -1; static int64 highest_order; highest_order = 0; if (last_i != -1) highest_order = display_frame[last_i].order; // avoid any frames below the current one for (i2 = 0; i2 <= 2; i2++) { if (display_frame[i2].state == DISPLAY_FRAME_STATE__READY && display_frame[i2].order > highest_order) { highest_order = display_frame[i2].order; i = i2; } } if (i == -1) i = last_i; if (i == -1) { in_GLUT_DISPLAY_REQUEST = 0; return; // no frames exist yet, so screen size cannot be determined, // therefore no action possible } if (i != last_i) { for (i2 = 0; i2 <= 2; i2++) { if (display_frame[i2].order < display_frame[i].order && (display_frame[i2].state == DISPLAY_FRAME_STATE__DISPLAYING || display_frame[i2].state == DISPLAY_FRAME_STATE__READY)) display_frame[i2].state = DISPLAY_FRAME_STATE__EMPTY; } display_frame[i].state = DISPLAY_FRAME_STATE__DISPLAYING; } static int64 order; order = last_hardware_display_frame_order; static int32 first_command_prev_order = 0; static int32 rerender_prev_hardware_frame = 0; rerender_prev_hardware_frame = 0; // if no new software frame, only proceed if there is _GL content to render if (last_rendered_hardware_display_frame_order == last_hardware_display_frame_order) { if (i == last_i) { if (full_screen_set == -1) { // no pending full-screen changes if (os_resize_event == 0) { // no resize events # ifndef DEPENDENCY_GL // we aren't using SUB _GL in_GLUT_DISPLAY_REQUEST = 0; return; # endif if (displayorder_glrender == 0) { in_GLUT_DISPLAY_REQUEST = 0; return; } if (first_command_prev_order) { rerender_prev_hardware_frame = 1; // reset next command to prev hardware frame's handle // (if any) last_hardware_command_rendered = first_command_prev_order; } } } } } first_command_prev_order = 0; // set environment variables environment_2d__screen_width = display_frame[i].w; environment_2d__screen_height = display_frame[i].h; os_resize_event = 0; // turn off flag which forces a render to take place // even if no content has changed if ((full_screen == 0) && (full_screen_set == -1)) { // not in (or attempting to enter) full screen display_required_x = display_frame[i].w; display_required_y = display_frame[i].h; static int32 framesize_changed; framesize_changed = 0; if ((display_required_x != resize_snapback_x) || (display_required_y != resize_snapback_y)) framesize_changed = 1; resize_auto_ideal_aspect = (float)display_frame[i].w / (float)display_frame[i].h; resize_snapback_x = display_required_x; resize_snapback_y = display_required_y; if (resize_auto) { // maintain aspect ratio static float ar; ar = (float)display_x / (float)display_y; if ((ar != resize_auto_accept_aspect) && (ar != resize_auto_ideal_aspect)) { // set new size static int32 x, y; if (display_x_prev == display_x) { y = display_y; x = (float)y * resize_auto_ideal_aspect; } if (display_y_prev == display_y) { x = display_x; y = (float)x / resize_auto_ideal_aspect; } if ((display_y_prev != display_y) && (display_x_prev != display_x)) { if (abs(display_y_prev - display_y) < abs(display_x_prev - display_x)) { x = display_x; y = (float)x / resize_auto_ideal_aspect; } else { y = display_y; x = (float)y * resize_auto_ideal_aspect; } } resize_auto_accept_aspect = (float)x / (float)y; resize_pending = 1; glutReshapeWindow(x, y); glutPostRedisplay(); goto auto_resized; } } // resize_auto if ((display_required_x != display_x) || (display_required_y != display_y)) { if (resize_snapback || framesize_changed) { glutReshapeWindow(display_required_x, display_required_y); glutPostRedisplay(); resize_pending = 1; } } auto_resized:; } // not in (or attempting to enter) full screen // Pseudo-Fullscreen if (!resize_pending) { // avoid switching to fullscreen before resize // operations take effect if (full_screen_set != -1) { // full screen mode change requested if (full_screen_set == 0) { if (full_screen != 0) { // exit full screen resize_pending = 1; glutReshapeWindow(display_frame[i].w, display_frame[i].h); glutPostRedisplay(); } full_screen = 0; full_screen_set = -1; } else { if (full_screen == 0) { glutFullScreen(); } full_screen = full_screen_set; full_screen_set = -1; } // enter full screen } // full_screen_set check } // size pending check // This code is deprecated but kept for reference purposes // 1) It was found to be unstable // 2) Switching modes means a high chance of losing pre-loaded OpenGL // hardware textures/surfaces /* static int32 glut_window; //fullscreen if (!resize_pending){//avoid switching to fullscreen before resize operations take effect if (full_screen_set!=-1){//full screen mode change requested if (full_screen_set==0){ //exit full screen glutLeaveGameMode(); glutSetWindow(glut_window); reinit_glut_callbacks(); full_screen=0; full_screen_set=-1; return; }else{ static char game_mode_string[1000]; static int32 game_mode_string_i; game_mode_string_i=0; game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d", display_frame[i].w); game_mode_string[game_mode_string_i++]=120;//"x" game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d", display_frame[i].h); game_mode_string[game_mode_string_i++]=58;//":" game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d", 32); glutGameModeString(game_mode_string); if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)){ //full screen using native dimensions which match the frame size if (full_screen==0) glut_window=glutGetWindow(); glutEnterGameMode(); fullscreen_width=display_frame[i].w; fullscreen_height=display_frame[i].h; reinit_glut_callbacks(); full_screen=full_screen_set;//it's currently irrelavent if it is stretched or 1:1 full_screen_set=-1; return; }else{ //native dimensions not possible //attempt full screen using desktop dimensions static int32 w; w=glutGet(GLUT_SCREEN_WIDTH); static int32 h; h=glutGet(GLUT_SCREEN_HEIGHT); game_mode_string_i=0; game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d", w); game_mode_string[game_mode_string_i++]=120;//"x" game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d", h); game_mode_string[game_mode_string_i++]=58;//":" game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d", 32); glutGameModeString(game_mode_string); if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)){ //full screen using desktop dimensions if (full_screen==0) glut_window=glutGetWindow(); glutEnterGameMode(); fullscreen_width=w; fullscreen_height=h; reinit_glut_callbacks(); screen_scale=full_screen_set; full_screen=full_screen_set; full_screen_set=-1; return; }else{ //cannot enter full screen full_screen=0; full_screen_set=-1; } } }//enter full screen }//full_screen_set check }//size pending check */ // set window environment variables environment__window_width = display_x; environment__window_height = display_y; prepare_environment_2d(); // need a few variables here static int32 first_hardware_layer_rendered; static int32 first_hardware_layer_command; first_hardware_layer_rendered = 0; first_hardware_layer_command = 0; static int32 level; for (level = 0; level <= 5; level++) { static int32 x1, y1, x2, y2; if (level == 0) { set_render_dest(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { if (level == displayorder_glrender) { # ifdef DEPENDENCY_GL set_view(VIEW_MODE__RESET); if (close_program || dont_call_sub_gl || suspend_program || stop_program) goto abort_gl; display_lock_request++; while (display_lock_confirmed < display_lock_request) { if (close_program || dont_call_sub_gl || suspend_program || stop_program) goto abort_gl; qbevent = 1; Sleep(0); } sub_gl_called = 1; SUB__GL(); sub_gl_called = 0; abort_gl:; display_lock_released = display_lock_confirmed; # endif // DEPENDENCY_GL } // level==displayorder_glrender if (level == displayorder_screen) { // defaults to 1 if (software_screen_hardware_frame != 0 && i != last_i) { free_hardware_img(software_screen_hardware_frame, 847001); } if (i != last_i || software_screen_hardware_frame == 0) { software_screen_hardware_frame = new_hardware_img(display_frame[i].w, display_frame[i].h, display_frame[i].bgra, NULL); } static hardware_img_struct *f1; f1 = (hardware_img_struct *)list_get(hardware_img_handles, software_screen_hardware_frame); if (software_screen_hardware_frame == 0) { alert("Invalid software_screen_hardware_frame!!"); } if (f1 == NULL) alert("Invalid software_screen_hardware_frame!"); static int32 use_alpha; use_alpha = 0; if (level > 1) use_alpha = 1; // put the software screen hardware_img_put(0, 0, environment_2d__screen_width - 1, environment_2d__screen_height - 1, software_screen_hardware_frame, 0, 0, 0, f1->w - 1, f1->h - 1, use_alpha, environment_2d__screen_smooth); hardware_buffer_flush(); } // level==displayorder_screen if (level == displayorder_hardware || level == displayorder_hardware1) { static int32 dst; dst = 0; if (level == displayorder_hardware1) dst = -1; static int32 command; command = 0; static int32 caller_flag; caller_flag = 0; if (first_hardware_layer_rendered == 0) { if (first_hardware_command) { if (last_hardware_command_rendered) { if (rerender_prev_hardware_frame) { command = last_hardware_command_rendered; caller_flag = 100; } else { hardware_graphics_command_struct *last_hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, last_hardware_command_rendered); if (last_hgc == NULL) alert("Rendering: Last HGC is NULL!"); command = last_hgc->next_command; caller_flag = 200; } } else { command = first_hardware_command; caller_flag = 300; } // process/skip pending hardware puts before this // frame's order value while (command) { hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, command); if (hgc->order < order) { if (hgc->command == HARDWARE_GRAPHICS_COMMAND__FREEIMAGE) { free_hardware_img(hgc->src_img, 847002 + caller_flag); } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__PUTIMAGE) { if (hgc->dst_img > 0) { // note: rendering to the old // default surface is pointless, but // renders onto maintained hardware // images are still required hardware_img_put(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->use_alpha, hgc->smooth); } } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE) { if (hgc->dst_img > 0) { // note: rendering to the old // default surface is pointless, but // renders onto maintained hardware // images are still required hardware_img_tri2d(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->dst_x3, hgc->dst_y3, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3, hgc->src_y3, hgc->use_alpha, hgc->smooth); } } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE3D) { if (hgc->dst_img > 0) { // note: rendering to the old // default surface is pointless, but // renders onto maintained hardware // images are still required hardware_img_tri3d(hgc->dst_x1, hgc->dst_y1, hgc->dst_z1, hgc->dst_x2, hgc->dst_y2, hgc->dst_z2, hgc->dst_x3, hgc->dst_y3, hgc->dst_z3, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3, hgc->src_y3, hgc->use_alpha, hgc->smooth, hgc->cull_mode, hgc->depthbuffer_mode); } } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__CLEAR_DEPTHBUFFER) { if (hgc->dst_img > 0) { // note: rendering to the old // default surface is pointless, but // renders onto maintained hardware // images are still required clear_depthbuffer(hgc->dst_img); } } last_hardware_command_rendered = command; if (next_hardware_command_to_remove == 0) next_hardware_command_to_remove = command; command = hgc->next_command; hgc->remove = 1; } else { goto found_command_from_current_order; } } found_command_from_current_order:; } // first_hardware_command first_hardware_layer_command = command; } else { command = first_hardware_layer_command; } // process pending hardware puts for this frame's order value while (command) { hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, command); if (hgc == NULL) { hardware_graphics_command_struct *hgcx = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, next_hardware_command_to_remove); alert(order); alert(hgcx->order); alert(command); alert("Renderer: Command does not exist."); } if (hgc->order == order) { if (first_command_prev_order == 0) first_command_prev_order = command; if (hgc->command == HARDWARE_GRAPHICS_COMMAND__FREEIMAGE && rerender_prev_hardware_frame == 0 && first_hardware_layer_rendered == 0) { free_hardware_img(hgc->src_img, 847003); } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__PUTIMAGE) { if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) { if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) { hardware_img_put(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->use_alpha, hgc->smooth); } } } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE) { if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) { if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) { hardware_img_tri2d(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->dst_x3, hgc->dst_y3, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3, hgc->src_y3, hgc->use_alpha, hgc->smooth); } } } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE3D) { if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) { if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) { hardware_img_tri3d(hgc->dst_x1, hgc->dst_y1, hgc->dst_z1, hgc->dst_x2, hgc->dst_y2, hgc->dst_z2, hgc->dst_x3, hgc->dst_y3, hgc->dst_z3, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3, hgc->src_y3, hgc->use_alpha, hgc->smooth, hgc->cull_mode, hgc->depthbuffer_mode); } } } if (hgc->command == HARDWARE_GRAPHICS_COMMAND__CLEAR_DEPTHBUFFER) { if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) { if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) { clear_depthbuffer(hgc->dst_img); } } } last_hardware_command_rendered = command; if (next_hardware_command_to_remove == 0) next_hardware_command_to_remove = command; //!!!! should be prev to this command command = hgc->next_command; hgc->remove = 1; } else { goto finished_all_commands_for_current_frame; } } finished_all_commands_for_current_frame:; first_hardware_layer_rendered = 1; hardware_buffer_flush(); } // level==displayorder_hardware||level==displayorder_hardware1 if (level == 5) { if (environment_2d__letterbox) { // create a black texture (if not yet created) static uint32 black_pixel = 0x00000000; static int32 black_texture = 0; if (black_texture == 0) { black_texture = new_hardware_img(1, 1, &black_pixel, NULL); } if (environment_2d__letterbox == 1) { // vertical stripes hardware_img_put(((float)-environment_2d__screen_x1) / environment_2d__screen_x_scale - 1.0f, 0, -1, environment_2d__screen_height - 1, black_texture, 0, 0, 0, 0, 0, 0, 0); hardware_img_put(environment_2d__screen_width, 0, (((float)-environment_2d__screen_x1) + (float)environment__window_width - 1.0f) / environment_2d__screen_x_scale + 1.0f, environment_2d__screen_height - 1, black_texture, 0, 0, 0, 0, 0, 0, 0); } else { // horizontal stripes hardware_img_put(0, ((float)-environment_2d__screen_y1) / environment_2d__screen_y_scale - 1.0f, environment_2d__screen_width - 1, -1, black_texture, 0, 0, 0, 0, 0, 0, 0); hardware_img_put(0, environment_2d__screen_height, environment_2d__screen_width - 1, (((float)-environment_2d__screen_y1) + (float)environment__window_height - 1.0f) / environment_2d__screen_y_scale + 1.0f, black_texture, 0, 0, 0, 0, 0, 0, 0); } hardware_buffer_flush(); } // letterbox } // level==5 } // level!=0 } // level loop if (requestedKeyboardOverlayImage) { int32 src = requestedKeyboardOverlayImage - HARDWARE_IMG_HANDLE_OFFSET; hardware_img_struct *src_hardware_img; src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src); /* hardware_img_put(0,0,src_hardware_img->w-1,src_hardware_img->h-1, src, 0, 0,0,src_hardware_img->w-1,src_hardware_img->h-1, 1,0); */ hardware_img_put(0, 0, environment_2d__screen_width - 1, environment_2d__screen_height - 1, src, 0, 0, 0, src_hardware_img->w - 1, src_hardware_img->h - 1, 1, 0); hardware_buffer_flush(); } last_rendered_hardware_display_frame_order = last_hardware_display_frame_order; if (suspend_program) { // Otherwise skipped SUB__GL content becomes // "invisible" //... } else { glutSwapBuffers(); } in_GLUT_DISPLAY_REQUEST = 0; } // GLUT_DISPLAY_REQUEST void GLUT_MouseButton_Up(int button, int x, int y) { # ifdef QB64_GLUT int32 i; int32 handle; handle = mouse_message_queue_first; mouse_message_queue_struct *queue = (mouse_message_queue_struct *)list_get(mouse_message_queue_handles, handle); i = queue->last + 1; if (i > queue->lastIndex) i = 0; if (i == queue->current) { int32 nextIndex = queue->last + 1; if (nextIndex > queue->lastIndex) nextIndex = 0; queue->current = nextIndex; } queue->queue[i].x = x; queue->queue[i].y = y; queue->queue[i].movementx = 0; queue->queue[i].movementy = 0; queue->queue[i].buttons = queue->queue[queue->last].buttons; if (queue->queue[i].buttons & (1 << (button - 1))) queue->queue[i].buttons ^= (1 << (button - 1)); queue->last = i; if (device_last) { // core devices required? if ((button >= 1) && (button <= 3)) { button--; static device_struct *d; d = &devices[2]; // mouse int32 eventIndex = createDeviceEvent(d); setDeviceEventButtonValue(d, eventIndex, button, 0); commitDeviceEvent(d); } // valid range } // core devices required # endif } void GLUT_MouseButton_Down(int button, int x, int y) { # ifdef QB64_GLUT int32 i; int32 handle; handle = mouse_message_queue_first; mouse_message_queue_struct *queue = (mouse_message_queue_struct *)list_get(mouse_message_queue_handles, handle); i = queue->last + 1; if (i > queue->lastIndex) i = 0; if (i == queue->current) { int32 nextIndex = queue->last + 1; if (nextIndex > queue->lastIndex) nextIndex = 0; queue->current = nextIndex; } queue->queue[i].x = x; queue->queue[i].y = y; queue->queue[i].movementx = 0; queue->queue[i].movementy = 0; queue->queue[i].buttons = queue->queue[queue->last].buttons; queue->queue[i].buttons |= (1 << (button - 1)); queue->last = i; if (device_last) { // core devices required? if ((button >= 1) && (button <= 3)) { button--; static device_struct *d; d = &devices[2]; // mouse int32 eventIndex = createDeviceEvent(d); setDeviceEventButtonValue(d, eventIndex, button, 1); commitDeviceEvent(d); // 1-3 } else { // not 1-3 // mouse wheel? if ((button >= 4) && (button <= 5)) { static float f; if (button == 4) f = -1; else f = 1; static device_struct *d; d = &devices[2]; // mouse int32 eventIndex = createDeviceEvent(d); setDeviceEventWheelValue(d, eventIndex, 2, f); commitDeviceEvent(d); eventIndex = createDeviceEvent(d); setDeviceEventWheelValue(d, eventIndex, 2, 0); commitDeviceEvent(d); } // 4-5 } // not 1-3 } // core devices required # endif } void GLUT_MOUSE_FUNC(int glut_button, int state, int x, int y) { # ifdef QB64_GLUT if (state == GLUT_DOWN) GLUT_MouseButton_Down(glut_button + 1, x, y); if (state == GLUT_UP) GLUT_MouseButton_Up(glut_button + 1, x, y); # endif } void GLUT_MOTION_FUNC(int x, int y) { int32 i, last_i; int32 handle; int32 xrel, yrel; handle = mouse_message_queue_first; mouse_message_queue_struct *queue = (mouse_message_queue_struct *)list_get(mouse_message_queue_handles, handle); // message #1 last_i = queue->last; i = queue->last + 1; if (i > queue->lastIndex) i = 0; // wrap around if (i == queue->current) { int32 nextIndex = queue->last + 1; if (nextIndex > queue->lastIndex) nextIndex = 0; queue->current = nextIndex; } # ifdef QB64_WINDOWS // Windows calculates relative movement by intercepting WM_INPUT events // instead xrel = 0; yrel = 0; # else xrel = x - queue->queue[queue->last].x; yrel = y - queue->queue[queue->last].y; # endif queue->queue[i].x = x; queue->queue[i].y = y; queue->queue[i].movementx = xrel; queue->queue[i].movementy = yrel; queue->queue[i].buttons = queue->queue[last_i].buttons; queue->last = i; // message #2 (clears movement values to avoid confusion) last_i = queue->last; i = queue->last + 1; if (i > queue->lastIndex) i = 0; if (i == queue->current) { int32 nextIndex = queue->last + 1; if (nextIndex > queue->lastIndex) nextIndex = 0; queue->current = nextIndex; } queue->queue[i].x = x; queue->queue[i].y = y; queue->queue[i].movementx = 0; queue->queue[i].movementy = 0; queue->queue[i].buttons = queue->queue[last_i].buttons; queue->last = i; if (device_last) { // core devices required? if (!device_mouse_relative) { static device_struct *d; d = &devices[2]; // mouse int32 eventIndex = createDeviceEvent(d); static float fx, fy; static int32 z; fx = x; fx -= x_offset; z = x_monitor - x_offset * 2; if (fx < 0) fx = 0; if (fx >= z) fx = z - 1; fx = fx / (float)(z - 1); // 0 to 1 fx *= 2.0; // 0 to 2 fx -= 1.0; //-1 to 1 fy = y; fy -= y_offset; z = y_monitor - y_offset * 2; if (fy < 0) fy = 0; if (fy >= z) fy = z - 1; fy = fy / (float)(z - 1); // 0 to 1 fy *= 2.0; // 0 to 2 fy -= 1.0; //-1 to 1 setDeviceEventAxisValue(d, eventIndex, 0, fx); setDeviceEventAxisValue(d, eventIndex, 1, fy); commitDeviceEvent(d); } else { static device_struct *d; d = &devices[2]; // mouse int32 eventIndex = createDeviceEvent(d); static float fx, fy; static int32 z; fx = xrel; fy = yrel; setDeviceEventWheelValue(d, eventIndex, 0, fx); setDeviceEventWheelValue(d, eventIndex, 1, fy); commitDeviceEvent(d); eventIndex = createDeviceEvent(d); fx = 0; fy = 0; setDeviceEventWheelValue(d, eventIndex, 0, fx); setDeviceEventWheelValue(d, eventIndex, 1, fy); commitDeviceEvent(d); } } // core devices required } void GLUT_PASSIVEMOTION_FUNC(int x, int y) { GLUT_MOTION_FUNC(x, y); } void GLUT_MOUSEWHEEL_FUNC(int wheel, int direction, int x, int y) { # ifdef QB64_GLUT // Note: freeglut specific, limited documentation existed so the following // research was done: // qbs_print(qbs_str(wheel),NULL); <-- was always 0 [could 1 indicate // horizontal wheel?] qbs_print(qbs_str(direction),NULL); <-- 1(up) or // -1(down) qbs_print(qbs_str(x),NULL); <--mouse x,y co-ordinates // qbs_print(qbs_str(y),1); < if (direction > 0) { GLUT_MouseButton_Down(4, x, y); GLUT_MouseButton_Up(4, x, y); } if (direction < 0) { GLUT_MouseButton_Down(5, x, y); GLUT_MouseButton_Up(5, x, y); } # endif } #endif