1
1
Fork 0
mirror of https://github.com/QB64Official/qb64.git synced 2024-07-03 10:01:21 +00:00
qb64/internal/c/libqb/gui.cpp

2192 lines
87 KiB
C++

#ifdef QB64_MACOSX
#include <sys/sysctl.h>
#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