#include "../include/xcv_license.h" //PCH #include "../include/xcv_arcball.h" //"arcball.h" namespace Widgets95 {//-. //<-' //TODO: Scale relative to ui_mouse_iaction_span. enum{ xcv_rotate_stacks=16 }; //32 static resource xcv_rotate_texture; /************** WIDGETS_95_Rotation::WIDGETS_95_Rotation() ********************/ void ui::rotate::_rotate() { delete _ball; //gluDeleteQuadric(quadObj); } /********************** WIDGETS_95_Rotation::_copy_float_array_to_ball() *********/ void ui::rotate::_update_live() { /* inline void ui::rotate::_copy_float_array_to_ball() { double *fp_src = &float_array_val[0]; double *fp_dst = &(*_ball->rot_ptr)[0][0]; for(int i=0;i<16;i++) *fp_dst++ = *fp_src++; }*/ _ball->q_now.set(*(mat4*)_float_array_val); redraw(); } /********************** WIDGETS_95_Rotation::_copy_ball_to_float_array() *********/ inline void ui::rotate::_copy_ball_to_float_array() { //mouse_interface is responsible for all but setting the value. //set_float_array_val(&(*_ball->rot_ptr)[0][0]); //_ball->q_now.set e::set_edited(this); stage_live(&(*_ball->rot_ptr)[0][0],16); } /*************************** WIDGETS_95_Rotation::iaction_mouse() ***********/ void ui::rotate::_iaction_mouse(int stage, int x, int y) { if(1==stage) { _ball->mouse_down(x,y); /* printf("%d %d - %f %f\n",x,y,_ball->center[0],_ball->center[1]); */ } else if(2==stage) { //_copy_float_array_to_ball(); //2019 /* printf("%d %d\n",x,y); */ int cm = e.curr_modifiers; double shift = 0; if(~cm&GLUT_ACTIVE_SHIFT) { //4/M_PI*10 just increases 10x from the original formulation. //It was quadrupling the radius. Pi instead of 4 is best fit. shift = 4/M_PI*10; } //REMINDER: ui::_special is blocking GLUT_ACTIVE_ALT //for hotkeys, but this is mouse input only? _ball->mouse_motion(x,y,shift,cm&GLUT_ACTIVE_ALT,cm&GLUT_ACTIVE_CTRL); _copy_ball_to_float_array(); //if(can_spin) spinning = true; } else if(3==stage) { _ball->mouse_up(); } } /******************** WIDGETS_95_Rotation::iaction_draw(1) **************/ void ui::rotate::_iaction_draw(int persp) { if(1==persp) { //draw_ball(1.35f); //1.96f //Want 2px separation so odd pixels don't touch. draw_ball(1.25f,true,true); } else if(0==persp) //ortho { //NOTE: This is basically one big bubble frame. //float radius = (h-22)/2.0f; /*std::min(w/2.0f,h/2.0f); */ float radius = (_h-ui_mouse_iaction_head-4)/2.0f; /********* Draw emboss circles around arcball control *********/ radius+=1; //pass #1 //NOTE: 192 is background's color. int c1 = _enabled?0:180, c2 = 192; for(int pass=1;pass<=3;pass++) { gl::glBegin(GL_LINE_LOOP); enum{ kN=2*xcv_rotate_stacks }; //60 for(int k=0;kset_params(vec2(w/2.0f,(h-ui_mouse_iaction_head)/2.0f),radius); _ball->radius = radius; } /****************************** WIDGETS_95_Rotation::draw_ball() **************/ extern void xcv_rotate_gluSphere(float r, GLint slices, GLint stacks) { //This code comes from here: //https://stackoverflow.com/questions/7687148/ //I had taken code from here: //https://cgit.freedesktop.org/mesa/glu/tree/src/libutil/quad.c#n150 //But in 2021 it became apparent the code //did not correctly generate a sphere, or //I'd botched it. It'd cached cosine/sine //for the previous slices/stacks combo to //help with the "rotate" widgets. //There's a backward facing disc on one end //visible in GL_LINE mode. Adjusting s0 and //s1 would work also. //for(int i=0;i<=stacks;i++) for(int i=1;i<=stacks;i++) { float s0 = (float) (i - 1) / stacks; float lat0 = (float)M_PI * (-0.5f + s0); float z0 = std::sin(lat0); float zr0 = std::cos(lat0); float s1 = (float) i / stacks; float lat1 = (float)M_PI * (-0.5f + s1); float z1 = std::sin(lat1); float zr1 = std::cos(lat1); //I'm having to concoct UVs for this code. //This just mirrors the UVs for the rotate //widget's checkerboard. It might be wrong //for a non symmetrical texture. //Note, in theory whether the checkerboard //colors are swithed doesn't matter but it //might because of its shadow and it's the //way it's always been. s0 = 1-s0; s1 = 1-s1; //NOTE: Quads look best in wire frame. //I had to add them to xcv_window.cpp. gl::glBegin(GL_QUAD_STRIP); for(int j=slices+1;j-->0;) { float t = (float) (j - 1) / slices; float lng = 2 * (float)M_PI * t; float x = std::cos(lng); float y = std::sin(lng); gl::glTexCoord2f(s0,t); gl::glNormal3f(x * zr0, y * zr0, z0); gl::glVertex3f(r * x * zr0, r * y * zr0, r * z0); gl::glTexCoord2f(s1,t); gl::glNormal3f(x * zr1, y * zr1, z1); gl::glVertex3f(r * x * zr1, r * y * zr1, r * z1); } gl::glEnd(); } } bool ui::rotate::draw_ball(float radius, bool t, bool l) { enum{ t2d=GL_TEXTURE_2D }; if(t) //setup_texture { gl::glEnable(t2d); gl::glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); window::draw_color(255); //2019: Well this is embarrassing! //static GLuint tex = 0u; GLuint tex; if(xcv_rotate_texture.get(_ui,tex)) { /* (OSL 2006/06) Just use glBindTexture to avoid having to re-upload the whole checkerboard every frame. */ gl::glBindTexture(t2d,tex); //return true goto l; } else /* need to make a new checkerboard texture */ { gl::glGenTextures(1,&tex); if(!xcv_rotate_texture.set(_ui,tex)) { //Overflow? I don't know if GLUT shares contexts with the user //drawing areas. assert(tex<256); //TODO: This could use an internal system for loading textures //and their error output. gl::glDeleteTextures(1,&tex); return false; } } int err = gl::glGetError(); //flushing... gl::glBindTexture(t2d,tex); enum { dark = 110, /*** Dark and light colors for _ball checkerboard ***/ light = 220, /* Note: you can change the number of checkers across there sphere in draw_ball */ //2021: Removing need for texture matrix below. I'm not //sure if GL->GLES should include texture matrix support. //Not, just for this anyway. //NOTE: Halving "repeat" also works but is much softer at //ui_mouse_iaction_span. Really it should adapt to the size. // //checkboard_size = 64, /* pixels across whole texture */ checkboard_size = 64*2, checkboard_repeat = 32, /* pixels across one black/white sector */ }; unsigned char c, texture_image[checkboard_size][checkboard_size]; for(int i=0;irot_ptr)[0][0]); //2019 gl::glMultMatrixd(_float_array_val); //2021: I've adjusted the texture above to avoid using //GL_TEXTURE. I don't want to add it to the shaders in //xcv_window.cpp without more cause than this. #if 0 //draw_ball { gl::glMatrixMode(GL_TEXTURE); gl::glLoadIdentity(); /* black-white checker tiles across whole sphere */ gl::glScalef(2,2,1); //gluSphere(quadObj,radius,32,16); xcv_rotate_gluSphere(radius,xcv_rotate_stacks*3/2,xcv_rotate_stacks); gl::glLoadIdentity(); gl::glMatrixMode(GL_MODELVIEW); } #else xcv_rotate_gluSphere(radius,xcv_rotate_stacks*3/2,xcv_rotate_stacks); #endif gl::glPopMatrix(); gl::glDisable(GL_CULL_FACE); } if(t) /* unhook checkerboard texture */ { gl::glBindTexture(t2d,0); gl::glDisable(t2d); } if(l) { gl::glDisable(GL_LIGHTING); } return true; } /****************************** WIDGETS_95_Rotation::reset() **********/ void ui::rotate::reset() { //2019: WIDGETS_95_LIVE sets ptr_val before calling reset. //Arcball is an incomplete type in the local header. bool new_Arcball = !_ball; if(new_Arcball) _ball = new Arcball; _ball->init(); /** reset quaternion, etc. **/ //Does same? //_ball->set_params(vec2(w/2.0f,(h-ui_mouse_iaction_head)/2.0f),2.0f*(h-18)); _update_area(); set_spin(_spin); _copy_ball_to_float_array(); if(!new_Arcball) { output_live(); /*** Output live and draw main gfx window ***/ execute_callback(); //2019 } } /****************************** WIDGETS_95_Rotation::needs_idle() *********/ bool ui::rotate::_needs_idle() { //I think this puts the ui in constant idle? //return can_spin; //I think mouse_down is gumming the _ball up. //return _ball?_ball->is_mouse_down||_ball->is_spinning:false; return _ball&&_ball->is_spinning||this==e::get_pressed(); } /****************************** WIDGETS_95_Rotation::idle() ***************/ void ui::rotate::_idle() { //spinning = _ball->is_spinning?true:false; //if(can_spin&&spinning) { //_copy_float_array_to_ball(); //2019 _ball->idle(); //2019: This way is unstable... setting by idle(). //*_ball->rot_ptr = *_ball->rot_ptr*_ball->rot_increment; _copy_ball_to_float_array(); //NOTE: mouse_interface::_mouse_held_down_handler does //this logic for mouse events, but not for idle spinning { redraw(); output_live(); /** output live and update gfx **/ execute_callback(); //2019 } } } /************************ WIDGETS_95_Rotation::set_spin() **********************/ ui::rotate &ui::rotate::set_spin(double damp_factor) { _ball->set_damping(/*1-*/damp_factor); _spin = damp_factor; return *this; } //---. }//<-'