#include "externals.hh" #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui_impl_sdl.h" #include "demo.hh" #include "globals.hh" #include "profiling.hh" #include "window.hh" #include "shaders.hh" #include "odbg.hh" #include "ops.hh" #include "rendertarget.hh" #include "sync.hh" using ebcl::T_Optional; /*= T_Main ===================================================================*/ struct T_Main { static constexpr uint32_t ResizeDelay = 50; T_Main( ); ~T_Main( ); void mainLoop( ); private: bool done = false; bool capture = false; ImVec2 mouseInitial; ImVec2 mouseMove; uint32_t stopResize = 0; ImVec2 prevSize; T_Optional< T_Demo > demo; void initDemo( ); void startIteration( ); void handleCapture( ); void makeUI( ); void render( ); }; /*----------------------------------------------------------------------------*/ T_Main::T_Main( ) { Globals::Init( ); prevSize = ImVec2( -1 , -1 ); } void T_Main::mainLoop( ) { auto& p( Globals::Profiler( ) ); while ( !done ) { auto const& dspSize( ImGui::GetIO( ).DisplaySize ); if ( prevSize.x != dspSize.x || prevSize.y != dspSize.y ) { const auto doit( prevSize.x > 0 ); prevSize = dspSize; if ( doit ) { stopResize = ResizeDelay; } } bool needInit( !demo ); if ( stopResize > 0 ) { stopResize --; if ( stopResize == 0 ) { needInit = true; } } if ( needInit ) { initDemo( ); } Globals::Watcher( ).check( ); Globals::Shaders( ).update( ); Globals::Sync( ).checkCurveFile( ); glFinish( ); p.startFrame( ); p.start( "Full frame" ); startIteration( ); if ( !done ) { handleCapture( ); makeUI( ); render( ); p.end( "Full frame" ); Globals::Window( ).swap( ); p.endFrame( ); } } } T_Main::~T_Main( ) { demo.clear( ); Globals::Shutdown( ); } /*----------------------------------------------------------------------------*/ void T_Main::initDemo( ) { auto const& dspSize( ImGui::GetIO( ).DisplaySize ); if ( dspSize.x < 0 || dspSize.y < 0 ) { return; } if ( !demo ) { demo.setNew( ); } printf( "init w/ dspsize %dx%d\n" , int( dspSize.x ) , int( dspSize.y ) ); if ( demo->initialise( (uint32_t) dspSize.x , (uint32_t) dspSize.y ) ) { Globals::Profiler( ).clear( ); } } void T_Main::startIteration( ) { SDL_Event event; mouseMove = ImVec2( ); while ( SDL_PollEvent( &event ) ) { ImGui_ImplSdl_ProcessEvent( &event ); if ( event.type == SDL_QUIT ) { done = true; return; } if ( capture && event.type == SDL_MOUSEMOTION ) { mouseMove.x += event.motion.xrel; mouseMove.y += event.motion.yrel; } } Globals::Window( ).startFrame( capture , mouseInitial ); ImGui::GetIO( ).MouseDrawCursor = true; } void T_Main::handleCapture( ) { auto const& io( ImGui::GetIO( ) ); const bool lmb( ImGui::IsMouseDown( 0 ) ); const bool mb( lmb || ImGui::IsMouseDown( 1 ) ); const bool appCanGrab( !( ImGui::IsMouseHoveringAnyWindow( ) || io.WantCaptureMouse || io.WantCaptureKeyboard ) ); const bool shift( io.KeyShift ); const bool ctrl( io.KeyCtrl ); if ( capture && !mb ) { capture = false; ImGui::CaptureMouseFromApp( false ); SDL_SetRelativeMouseMode( SDL_FALSE ); Globals::Window( ).warpMouse( mouseInitial ); ImGui::SetMouseCursor( ImGuiMouseCursor_Arrow ); } else if ( capture ) { ImGui::SetMouseCursor( ImGuiMouseCursor_Move ); if ( demo ) { demo->handleDND( mouseMove , ctrl , shift , lmb ); } } else if ( appCanGrab && mb ) { capture = true; mouseInitial = ImGui::GetMousePos( ); ImGui::CaptureMouseFromApp( true ); SDL_SetRelativeMouseMode( SDL_TRUE ); ImGui::SetMouseCursor( ImGuiMouseCursor_Move ); } if ( ( appCanGrab || capture ) && io.MouseWheel && demo ) { demo->handleWheel( io.MouseWheel , ctrl , shift ); } } bool CGCModeButton_( char const* const name , const bool disabled ) { using namespace ImGui; if ( disabled ) { PushItemFlag( ImGuiItemFlags_Disabled , true ); PushStyleVar( ImGuiStyleVar_Alpha , GetStyle( ).Alpha * 0.5f ); } const bool rv( Button( name ) ); if ( disabled ) { PopItemFlag( ); PopStyleVar( ); } return rv; } bool CGCComponentBar_( float* const value , const float base , const float unit , const ImVec4 color , char const* const label ) noexcept { using namespace ImGui; const float BarWidth = 24.f; const float BarHeight = 180.f; const float fullWidth = CalcItemWidth( ); const ImVec2 labelSize = CalcTextSize( label ); const ImVec2 maxValueSize = CalcTextSize( "-9.99" ); // Compute bounding boxes auto* const win( GetCurrentWindow( ) ); const ImVec2 cPos( win->DC.CursorPos ); const ImRect bbBar( cPos + ImVec2( ( fullWidth - BarWidth ) * .5f , 0 ) , cPos + ImVec2( ( fullWidth + BarWidth ) * .5f , BarHeight ) ); const ImRect bbLabel( cPos + ImVec2( 0 , BarHeight + 2 ) , cPos + ImVec2( fullWidth , BarHeight + labelSize.y + 2 ) ); const ImRect bbValue( ImVec2( cPos.x , bbLabel.Max.y + 2 ) , bbLabel.Max + ImVec2( 0 , maxValueSize.y + 2 ) ); const ImRect bbAll( cPos , bbValue.Max ); auto& style( GetStyle( ) ); auto id( win->GetID( label ) ); ItemSize( bbAll , style.FramePadding.y ); if ( !ItemAdd( bbAll , id ) ) { return false; } const bool tabFocus{ FocusableItemRegister( win , id ) }; const bool hovered{ ItemHoverable( bbBar , id ) }; auto* const ctx( GetCurrentContext( ) ); if ( tabFocus || ( hovered && ctx->IO.MouseClicked[ 0 ] ) ) { SetActiveID( id , win ); FocusWindow( win ); } float nValue{ *value }; const bool active{ ctx->ActiveId == id }; if ( active ) { if ( ctx->IO.MouseDown[ 0 ] ) { const float mPos{ ctx->IO.MousePos.y }; const float clickPos{ 1.0f - ImClamp( ( mPos - bbBar.Min.y - 1 ) / ( BarHeight - 2 ) , 0.0f , 1.0f ) }; nValue = base + unit * 2.f * clickPos; } else { ClearActiveID( ); } } else if ( hovered && ctx->IO.KeyCtrl && ctx->IO.MouseWheel != 0.f ) { nValue += unit * .01f * ctx->IO.MouseWheel; ctx->IO.MouseWheel = 0.f; } //- DRAW --------------------------------------------------------------- auto* const dl( GetWindowDrawList( ) ); const auto dispColor{ GetColorU32( ImVec4( color.x * ( ( hovered || active ) ? 1.f : .5f ) , color.y * ( ( hovered || active ) ? 1.f : .5f ) , color.z * ( ( hovered || active ) ? 1.f : .5f ) , 1 ) ) }; // Draw bar body const ImU32 bgCol{ GetColorU32( ( ctx->ActiveId == id || hovered ) ? ImVec4( .25f , .25f , .25f , 1.f ) : ImVec4( .1f , .1f , .1f , 1.f ) ) }; const ImU32 fCol{ GetColorU32( ( ctx->ActiveId == id || hovered ) ? ImVec4( 1.f , 1.f , 1.f , 1.f ) : ImVec4( 0.f , 0.f , 0.f , 1.f ) ) }; dl->AddRectFilled( bbBar.Min , bbBar.Max , bgCol ); dl->AddRect( bbBar.Min , bbBar.Max , fCol ); // Draw colored area on bar const float val( std::max( base , std::min( unit * 2 , nValue ) ) ); const float vy2( ( BarHeight - 2 ) * ( 1 - val / ( unit * 2 ) ) ); dl->AddRectFilled( bbBar.Min + ImVec2( 1 , BarHeight * .5 ) , bbBar.Min + ImVec2( BarWidth - 1 , 1 + vy2 ) , dispColor ); dl->AddLine( bbBar.Min + ImVec2( 1 , BarHeight * .5 ) , bbBar.Min + ImVec2( BarWidth - 1 , BarHeight * .5 ) , dispColor ); // Draw label & value const char* tStart = &ctx->TempBuffer[ 0 ]; const char* tEnd = tStart + ImFormatString( ctx->TempBuffer, IM_ARRAYSIZE( ctx->TempBuffer ), "%.2f" , *value ); PushStyleColor( ImGuiCol_Text , dispColor ); RenderTextClipped( bbLabel.Min , bbLabel.Max , label , nullptr , nullptr , ImVec2( .5f , .5f ) ); RenderTextClipped( bbValue.Min , bbValue.Max , tStart , tEnd , nullptr , ImVec2( .5f , .5f ) ); PopStyleColor( ); if ( nValue != *value ) { *value = val; return true; } return false; } bool HueSaturationPad( char const* const name , float* const hue , float* const saturation , const float size = 0.f ) noexcept { using namespace ImGui; // Check/set bounding box const auto wSize{ ImMax( size , CalcItemWidth( ) ) }; auto* const win( GetCurrentWindow( ) ); const ImVec2 cPos( win->DC.CursorPos ); const ImRect bb{ cPos , cPos + ImVec2( wSize , wSize ) }; auto& style( GetStyle( ) ); auto id{ win->GetID( name ) }; ItemSize( bb , style.FramePadding.y ); if ( !ItemAdd( bb , id ) ) { return false; } const ImVec2 wCenter{ cPos + ImVec2( wSize * .5f , wSize * .5f ) }; const float wThickness = wSize * 0.08f; const float wOuterRadius = wSize * 0.50f; const float wInnerRadius = wOuterRadius - wThickness; auto* const ctx( GetCurrentContext( ) ); const auto mPos{ ctx->IO.MousePos }; const auto rmPos{ mPos - wCenter }; const auto mcSqDist{ rmPos.x * rmPos.x + rmPos.y * rmPos.y }; const bool hovered{ mcSqDist <= wSize * wSize * .25f }; const bool tabFocus{ FocusableItemRegister( win , id ) }; if ( tabFocus || ( hovered && ctx->IO.MouseClicked[ 0 ] ) ) { SetActiveID( id , win ); FocusWindow( win ); } const bool active{ ctx->ActiveId == id }; float nHue{ *hue } , nSat{ *saturation }; if ( active ) { if ( ctx->IO.MouseDown[ 0 ] ) { nHue = ( 1.f + atan2f( -rmPos.y , -rmPos.x ) / IM_PI ) * .5f; nSat = ImSaturate( sqrtf( mcSqDist ) / wInnerRadius ); } else { ClearActiveID( ); } } else if ( hovered && ctx->IO.KeyCtrl && ctx->IO.MouseWheel != 0.f ) { const auto change{ ctx->IO.MouseWheel * .005f }; if ( ctx->IO.KeyShift ) { nSat = ImSaturate( nSat + change ); } else { nHue = fmodf( nHue + change + 1.f , 1.f ); } } //- DRAW --------------------------------------------------------------- auto* const dl( GetWindowDrawList( ) ); const float aeps = 1.5f / wOuterRadius; // Half a pixel arc length in radians (2pi cancels out). const int segmentPerArc = ImMax( 4 , (int) wOuterRadius / 12 ); const ImU32 hue_colors[] = { IM_COL32( 255 , 0 , 0 , 255 ) , IM_COL32( 255 , 255 , 0 , 255 ) , IM_COL32( 0 , 255 , 0 , 255 ) , IM_COL32( 0 , 255 , 255 , 255 ) , IM_COL32( 0 , 0 , 255 , 255 ) , IM_COL32( 255 , 0 , 255 , 255 ) , IM_COL32( 255 , 0 , 0 , 255 ) }; const auto avgRadius( ( wInnerRadius + wOuterRadius ) * .5f ); // Pad background and cross const auto bgColor{ GetColorU32( ( hovered || active ) ? ImVec4( .4f , .4f , .4f , 1 ) : ImVec4( .25f , .25f , .25f , 1 ) ) }; dl->AddCircleFilled( wCenter , avgRadius , bgColor ); dl->AddLine( wCenter - ImVec2( 0 , avgRadius ) , wCenter + ImVec2( 0 , avgRadius ) , GetColorU32( ImVec4( .1 , .1 , .1 , 1 ) ) ); dl->AddLine( wCenter - ImVec2( avgRadius , 0 ) , wCenter + ImVec2( avgRadius , 0 ) , GetColorU32( ImVec4( .1 , .1 , .1 , 1 ) ) ); // Color wheel for ( int n = 0 ; n < 6 ; n ++ ) { const float a0 = ( n ) / 6.f * 2.f * IM_PI - aeps; const float a1 = ( n + 1.f ) / 6.f * 2.f * IM_PI + aeps; const int vStart = dl->_VtxCurrentIdx; dl->PathArcTo( wCenter , avgRadius , a0 , a1 , segmentPerArc ); dl->PathStroke( IM_COL32_WHITE , false, wThickness); // Paint colors over existing vertices const ImVec2 gradientP0( wCenter.x + cosf( a0 ) * wInnerRadius , wCenter.y + sinf( a0 ) * wInnerRadius ); const ImVec2 gradientP1( wCenter.x + cosf( a1 ) * wInnerRadius , wCenter.y + sinf( a1 ) * wInnerRadius ); ShadeVertsLinearColorGradientKeepAlpha( dl->_VtxWritePtr - ( dl->_VtxCurrentIdx - vStart ), dl->_VtxWritePtr , gradientP0 , gradientP1 , hue_colors[ n ] , hue_colors[ n + 1 ] ); } // Cursor from hue and saturation const float cAngle{ nHue * 2 * IM_PI }; const float cDist{ nSat * wInnerRadius }; ImVec4 cColor{ 0 , 0 , 0 , 1 }; ColorConvertHSVtoRGB( *hue , *saturation , 1 , cColor.x , cColor.y , cColor.z ); dl->AddCircleFilled( wCenter + ImVec2( cos( cAngle ) * cDist , sin( cAngle ) * cDist ) , ( wOuterRadius - wInnerRadius ) * .5f , GetColorU32( cColor ) ); dl->AddCircle( wCenter + ImVec2( cos( cAngle ) * cDist , sin( cAngle ) * cDist ) , ( wOuterRadius - wInnerRadius ) * .5f , GetColorU32( ImVec4( 1 , 1 , 1 , 1 ) ) ); // Update values if ( *hue != nHue || *saturation != nSat ) { *hue = nHue; *saturation = nSat; return true; } return false; } bool ColorGradingControls( char const* const name , float* const red , float* const green , float* const blue , const float base = 0.f , const float unit = 1.f ) noexcept { using namespace ImGui; assert( red && green && blue ); assert( unit > 0.f && "invalid unit" ); PushID( name ); BeginGroup( ); ImGuiWindow* const window{ GetCurrentWindow() }; ImGuiStorage* const storage{ window->DC.StateStorage }; const bool wheelMode{ storage->GetBool( window->GetID( name ) , true ) }; // Mode selection bool modeChanged{ false }; modeChanged = CGCModeButton_( "Color wheel" , wheelMode ); SameLine( ); modeChanged = CGCModeButton_( "Components" , !wheelMode ) || modeChanged; if ( modeChanged ) { storage->SetBool( window->GetID( name ) , !wheelMode ); } bool changed; if ( wheelMode ^ modeChanged ) { const float scRed { ImSaturate( ( *red - base ) / ( unit * 2 ) ) } , scGreen{ ImSaturate( ( *green - base ) / ( unit * 2 ) ) } , scBlue { ImSaturate( ( *blue - base ) / ( unit * 2 ) ) }; float hue , saturation , value; ColorConvertRGBtoHSV( scRed , scGreen , scBlue , hue , saturation , value ); PushMultiItemsWidths( 2 ); changed = HueSaturationPad( "##wheel" , &hue , &saturation , 180.f ); PopItemWidth( ); SameLine( 0 , GetStyle( ).ItemInnerSpacing.x ); ImVec4 updated{ 0 , 0 , 0 , 1 }; ColorConvertHSVtoRGB( hue , saturation * .5f , 1 , updated.x , updated.y , updated.z ); changed = CGCComponentBar_( &value , 0 , .5 , updated , "V" ) || changed; if ( changed ) { ColorConvertHSVtoRGB( hue , saturation , value , updated.x , updated.y , updated.z ); *red = updated.x * unit * 2 + base; *green = updated.y * unit * 2 + base; *blue = updated.z * unit * 2 + base; } } else { PushMultiItemsWidths( 3 ); changed = CGCComponentBar_( red , base , unit , ImVec4( 1 , 0 , 0 , 0 ) , "R" ); PopItemWidth( ); SameLine( 0 , GetStyle( ).ItemInnerSpacing.x ); changed = CGCComponentBar_( green , base , unit , ImVec4( 0 , 1 , 0 , 0 ) , "G" ) || changed; PopItemWidth( ); SameLine( 0 , GetStyle( ).ItemInnerSpacing.x ); changed = CGCComponentBar_( blue , base , unit , ImVec4( .3 , .3 , 1 , 0 ) , "B" ) || changed; } PopItemWidth( ); EndGroup( ); PopID( ); return changed; } void T_Main::makeUI( ) { using namespace ImGui; if ( BeginMainMenuBar( ) ) { if ( BeginMenu( "File" ) ) { if ( MenuItem( "Quit" ) ) { done = true; } EndMenu( ); } if ( BeginMenu( "Views" ) ) { MenuItemCheckbox( "Input overrides" , &Globals::Sync( ).overridesWindowEnabled( ) ); MenuItemCheckbox( "Output debugger" , &Globals::ODbg( ).uiEnabled( ) ); MenuItemCheckbox( "Profiler" , &Globals::Profiler( ).uiEnabled( ) ); MenuItemCheckbox( "Sequencer" , &Globals::Sync( ).sequencerWindowEnabled( ) ); MenuItemCheckbox( "Shaders" , &Globals::Shaders( ).uiEnabled( ) ); EndMenu( ); } EndMainMenuBar( ); } Globals::Profiler( ).makeUI( ); Globals::ODbg( ).makeUI( ); Globals::Shaders( ).makeUI( ); Globals::Sync( ).makeOverridesWindow( ); Globals::Sync( ).makeSequencerWindow( ); #warning color grading widget test static float cgRed = 1 , cgGreen = 1 , cgBlue = 1; static float wtf[ 3 ] = { 0 , 0 , 0 }; auto const& dspSize( GetIO( ).DisplaySize ); SetNextWindowSize( ImVec2( 300 , 300 ) , ImGuiSetCond_Appearing ); SetNextWindowPos( ImVec2( ( dspSize.x - 300 ) / 2 , (dspSize.y - 300)/2 ) , ImGuiSetCond_Appearing ); Begin( "Test!" , nullptr , ImGuiWindowFlags_NoCollapse ); ColorEdit3( "yo" , wtf ); ColorGradingControls( "lolwut" , &cgRed , &cgGreen , &cgBlue ); End( ); } void T_Main::render( ) { if ( demo ) { demo->render( ); Globals::Profiler( ).start( "Debug" ); T_Rendertarget::MainOutput( ); if ( Globals::ODbg( ).isActive( ) ) { Globals::ODbg( ).debugOutput( ); } glFinish( ); Globals::Profiler( ).end( "Debug" ); } else { T_Rendertarget::MainOutput( ); glClearColor( 0 , 0 , 0 , 1 ); glClear( GL_COLOR_BUFFER_BIT ); } glUseProgram( 0 ); glBindProgramPipeline( 0 ); Globals::Textures( ).reset( ); glClearColor( 0 , 0 , 0 , 1 ); ImGui::Render( ); } /*============================================================================*/ int main( int , char** ) { T_Main m; m.mainLoop( ); return 0; }