Hazel game engine (074) code maintenance and imgui events

If there are errors in the code, terminology, etc. in the text, please correct me

code maintenance

The abstract parent class needs to add a virtual destructor

  • A brief description

    Because shared_ptr is a pointer, and the type of the pointer is the parent class

    If the parent class does not write a virtual destructor (the program will give a default destructor), then delete will not execute the "runtime" function of selecting the destructor of the derived class and then running the destructor of the parent class

    The destructor of the parent class will be called directly, ignoring the destructor of the subclass .

  • example

    1. No virtual destructor, just a normal destructor

      #include <iostream>
      using namespace std;
      class Parent {
              
              
      public:
      	 ~Parent() {
              
              
      		cout << "~Parent()" << endl;
      	}
      };
      class Child : public Parent {
              
              
      public:
      	// 写了virtual的话,delete p 会报错。。。不写virtual也能说明问题
      	~Child() {
              
              
      		cout << "~Child()" << endl;
      	}
      };
      void main() {
              
              
      	Parent* p = new Child;
      	delete p; // 只会执行父类的析构函数
      }
      

    2. added virtual

      #include <iostream>
      using namespace std;
      class Parent {
              
              
      public:
      	virtual ~Parent() {
              
              
      		cout << "~Parent()" << endl;
      	}
      };
      class Child : public Parent {
              
              
      public:
      	virtual ~Child() {
              
              
      		cout << "~Child()" << endl;
      	}
      };
      void main() {
              
              
      	Parent* p = new Child;
      	delete p; // 执行子类再是父类的析构函数
      }
      

  • in conclusion

    So add virtual destructors to: Event, Input, Framebuffer, GraphicsContext, RendererAPI.

input class modification

  • A brief description

    Input on one platform does not need to select another platform input at runtime, there is no such dynamic situation

    For example: It is impossible for me to use window input on Android, only input on Android .

    But rendering needs to choose which api to run at runtime, opengl or directx, vulkan, because the operation of windows supports these three rendering apis .

  • A similar structure for the current item Input

    Ref<Input> Input::Create()
    {
          
          
        switch (Renderer::GetPlatform())
        {
          
          
            case RendererAPI::API::None: HZ_CORE_ASSERT(false, "RendererAPI:None is currently not supported!"); return nullptr;
            case RendererAPI::API::Window: return std::make_shared<WindowInput>();
            case RendererAPI::API::Linux: return std::make_shared<LinuxInput>();
            case RendererAPI::API::Mac: return std::make_shared<MacInput>();
        }
        HZ_CORE_ASSERT(false, "UnKnown RendererAPI!");
        return nullptr;
    }
    

    Can push:

    One input.h, one input.cpp, there are windowsinput.cpp, linuxinput.cpp, macinput.cpp files.

    After compilation: input.obj, windowsinput.obj, linuxinput.obj, macinput.obj

    Is virtual function used, which subclass to use when running dynamically

  • After modifying the Input class in the project

    An input.h, there are windowsinput.cpp, linuxinput.cpp, macinput.cpp files, the macro decides which cpp file to compile before compiling

    After compilation, there is: input.obj file, which reduces the obj file of three cpp

    Static functions are declared in input, and static functions are defined in the other three cpps

  • example

    #include <iostream>
    using namespace std;
    
    class Input {
          
          
    public: 
    	static void GetMouseX(); // 静态函数 声明
    };
    #if LinuxInput
    using namespace std;
    void Input::GetMouseX() {
          
          // Input的GetMouseX函数定义
    	cout << "Windows GetMouseX" << endl;
    }
    #endif
    
    #define WindowsInput 1
    #if WindowsInput
    using namespace std;
    void Input::GetMouseX() {
          
          // Input的GetMouseX函数定义
    	cout << "Windows GetMouseX" << endl;
    }
    #endif
    
    void main() {
          
          
    	Input::GetMouseX();
    }
    
  • How items are modified

    • Delete input.cpp, windowsinput.h
    • The functions declared by input.h are defined in windowsinput.cpp, linuxinput.cpp, and macinput.cpp (currently only supports window, and the latter two cpps can be used)
  • Minor bugs when recording project modifications

    error LNK2019: unresolved external symbol "public: static bool __cdecl Hazel::Input::IsKeyPressed(int)

    input.h:
    inline static bool IsKeyPressed(int keycode);
    
    WindowsInput.cpp
    bool Input::IsKeyPressed(int keycode)  {
          
          
        auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
        auto state = glfwGetKey(window, keycode);
        return state == GLFW_PRESS || state == GLFW_REPEAT;
    }
    

    Because the IsKeyPressed function of input.h uses inline , and WindowsInput.cpp defines the IsKeyPressed function, an error is reported (need to delete the inline statement).

Imgui Event

There is a bug in the interface when ImGuiLayer has no OnEvent processing event function

  • Bug1

    In a viewport of ImGui wasd , the ImGui viewport (EditorLayer) of the output framebuffer scene will also respond

  • Bug2

    Scrolling in a viewport of ImGui , the ImGui viewport (EditorLayer) of the output framebuffer scene will also respond

Should be changed to respond only in the corresponding ImGui viewport.

  • Lift the OnEvent of ImGuiLayer

    • In Section 016, there was OnEvent to handle events

      Old ImGui version, so you need to manually write capture event processing

    • But in Section 022, the OnEvent function is deleted

      The new ImGui version, because it is hosted to ImGui to handle events, no manual processing is required

    • Now add to this section the

      Because if it is hosted to ImGui to handle events by itself, the above two bugs will be caused , so some events need to be handled manually .

WASD Bug

The OnUpdate of the camera is placed in the update, so it will be executed every frame

Because it has been updating, no matter which ImGui viewport is in the wasd camera, it will move.

void EditorLayer::OnUpdate(Timestep ts)
{
    
    
    HZ_PROFILE_FUNCTION();
    m_CameraController.OnUpdate(ts);

Scroll wheel bug

It should be mentioned that the EditorLayer layer renders two ImGui viewports in the OnImGuiRender function

  • This bug will be caused when two ImGui viewports are on the same plane of the window

    [External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-HCETIbD8-1690730651987)(https://raw.githubusercontent.com/liujianjie/Image/main/ImgFloder/202307302308319 .png)]

    • Text to explain the event flow

      1. When the user scrolls the mouse wheel

      2. The window sends the scroll event to the Application

        (Note: the window is registered in the ImGuiLayer class , so the ImGui viewport can also receive window events without using the OnEvent function (so in section 022, the OnEvent function of the ImGuiLayer class is deleted, and ImGui handles window events by itself ))

      3. Application is then processed by the EditorLayer layer

      4. And the OnEvent of the camera is called in the OnEvent of the EditorLayer layer

      5. The camera's Event will capture the mouse scroll event and process it (update the aspect ratio)

    • The code explains the event flow (review is to pave the way for solving this bug)

      1. level

        Application的LayerStack m_LayerStack;

        In the GameEngineEditorApp.cpp constructor PushLayer(new EditorLayer());

        ​ The EditorLayer level is inserted in front .

      2. Application.cpp handles events

        void Application::OnEvent(Event& e) {
                  
                  
            HZ_PROFILE_FUNCTION();
            ......
                // 从后往前
            for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) {
                  
                  
                if (e.Handled)
                   break;
                 (*--it)->OnEvent(e);
            }
        }
        
      3. The camera event is called in the OnEvent function of the EditorLayer layer class

        void EditorLayer::OnEvent(Event& event)
        {
                  
                  
            // 摄像机事件
            m_CameraController.OnEvent(event);
        }
        
      4. The OnEvent function in the OrthographicCameraController class captures the mouse wheel event and handles it

        void Hazel::OrthographicCameraController::OnEvent(Event& e)
        {
                  
                  
            HZ_PROFILE_FUNCTION();
        
            EventDispatcher dispatcher(e);
            dispatcher.Dispatch<MouseScrolledEvent>(HZ_BIND_EVENT_FN(OrthographicCameraController::OnMouseScrolled));
            dispatcher.Dispatch<WindowResizeEvent>(HZ_BIND_EVENT_FN(OrthographicCameraController::OnWindowResized));
        }
        bool Hazel::OrthographicCameraController::OnMouseScrolled(MouseScrolledEvent& e)
        {
                  
                  
            HZ_PROFILE_FUNCTION();
        
            m_ZoomLevel -= e.GetYOffset() * 0.25f;
            m_ZoomLevel = std::max(m_ZoomLevel, 0.25f);
            CalculateView();
            return false;
        }
        
  • But when the two ImGui viewports are not on the same plane of the window, this bug will not

    Please add a picture description

    That is, when scrolling the mouse on the setting viewport, the Viewport camera will not adjust the aspect ratio

    • need to know before explaining

      The window sends scroll events to Application , although not to ImGui

      However, it should be noted that the window is registered in the ImGuiLayer class , so the ImGui viewport can also receive window events without passing the OnEvent function.

      (So ​​delete the OnEvent function of the ImGuiLayer class in Section 022, and let ImGui handle window events by itself)

    • When the setting and Viewport are not on the same plane

      1. The user scrolls the scroll wheel in the setting, and the window has a scroll event

      2. The Setting viewport receives window scrolling events (the internal implementation of ImGui is not clear how to pass them to the ImGui viewport)

      3. Inside ImGui, you should let the Setting viewport handle this window scroll event

        Since the setting and viewport are not on the same plane, it is internally set as processed, and there is no next ImGui viewport to be processed, so it will not give the Viewport viewport (guess) (it can be considered that this event is blocked by the setting viewport )

In order to solve the above two bugs

  • Need to know in advance: LayerStack m_LayerStack of the application layer;

    In the constructor PushOverlay(m_ImGuiLayer);

    ​ put imguilayer at the end

    In the GameEngineEditorApp.cpp constructor PushLayer(new EditorLayer());

    ​ Inserted EditorLayer in front .

  • In order to solve the above two bugs: add OnEvent function in ImGuiLayer

    class HAZEL_API ImGuiLayer :public Layer
    {
          
          
        public:
        ImGuiLayer();
        ~ImGuiLayer();
    
        virtual void OnAttach() override;
        virtual void OnDetach() override;
        virtual void OnImGuiRender() override;
        virtual void OnEvent(Event& event) override;// 新添加
    
    void ImGuiLayer::OnEvent(Event& e) {
          
          
        ImGuiIO& io = ImGui::GetIO();
        e.Handled |= e.IsInCategory(EventCategoryMouse) & io.WantCaptureMouse; 
        e.Handled |= e.IsInCategory(EventCategoryKeyboard) & io.WantCaptureKeyboard;
    }
    /*
    imgui视口会阻塞鼠标滚轮事件,即ImGuiLayer视口处理了鼠标滚轮事件,不会传入给下一级Layer的OnEvent。
    for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) {
        if (e.Handled)
        	break;
        (*--it)->OnEvent(e);
    }
    */
    
  • The purpose of adding the OnEvent function is

    Press the wasd key in the setting viewport

    1. The window sends the wheel scroll event to the Application

    2. Application loops LayerStack and executes the OnEvent function of the layer

    3. Since the ImGuiLayer level is at the end , it is the first level to process events

    4. In the OnEvent function of the ImGuiLayer layer

      e.Handled = e.handled | true & true; the result is true

      Therefore, in the above-mentioned Application, the cyclic processing of the wheel scrolling event will jump out, and the onevent function of EditorLayer will not be executed .

      Therefore, the scroll wheel event cannot be received in the viewport, and the camera will not receive the scroll wheel event to adjust the aspect ratio.

  • but with new problems

    • Question 1

      Note that the last point is that the button press event is not written because it is processed every frame in the OnUpdate function and cannot be blocked by the event

    • new question is

      Regardless of scrolling in the viewport or setting viewport, the camera will not receive scrolling events to adjust the aspect ratio

      Because the wheel scrolling event is blocked by the OnEvent function of ImGuiLayer , it will not be passed to the EditorLayer level.

Solve the problem after ImGuiLayer has OnEvent function

  • Solutions

    The focus and mouse stay bool values ​​can be obtained from the ImGui viewport , and these two bool values ​​are used to handle new bugs and wasd bugs.

  • Specific code ideas for solving new bugs

    • Get focus and mouse stay bool, and set whether to block events through the combination of these two

      void EditorLayer::OnImGuiRender(){
              
              
      
      // 注意是获取Viewport视口下的bool值,来决定settings视口是否需要堵塞相应事件
      ImGui::Begin("Viewport");
      
      m_ViewportFocused = ImGui::IsWindowFocused();
      m_ViewportHovered = ImGui::IsWindowHovered();
      
      Application::Get().GetImGuiLayer()->BlockEvents(!m_ViewportFocused || !m_ViewportHovered);
      /*
      bool canshu = !m_ViewportFocused || !m_ViewportHovered;
      m_ViewportFocused = true,  m_ViewportHovered = true; canshu = false, m_BlockEvents:false-> viewport视口 能 接收滚轮事件(即用户在settings视口上操作时,viewport视口无法接收事件)
      m_ViewportFocused = false, m_ViewportHovered = true;canshu = true,   m_BlockEvents:true-> viewport视口 不 能接收滚轮事件
      m_ViewportFocused = true,  m_ViewportHovered = true; canshu = true,  m_BlockEvents:true-> viewport视口 不 能接收滚轮事件
      */
      
    • Use m_ViewportFocused to solve wasd in another window, and the viewport also responds

      void EditorLayer::OnUpdate(Timestep ts)
      {
              
              
          HZ_PROFILE_FUNCTION();
          // 当焦点聚焦,才能wasd
          if (m_ViewportFocused) {
              
              
              m_CameraController.OnUpdate(ts);
          }
      
    • In ImGuiLayer

      Write whether to block mouse scrolling boolean

      void BlockEvents(bool block) {
              
               m_BlockEvents = block; }
      

      This Boolean value determines whether the window blocks scrolling events, that is, whether the viewport can accept events

      void ImGuiLayer::OnEvent(Event& e) {
              
              
          /*
      		m_BlockEvents:false-> settings视口不阻塞事件,viewport面板能接收滚轮事件
      		m_BlockEvents:true->  		   视口阻塞事件,viewport视口不能接收滚轮事件
      	*/
          if (m_BlockEvents) {
              
              
              /*
              imgui窗口会阻塞鼠标滚轮事件,即ImGuiLayer窗口处理了鼠标滚轮事件,不会传入给下一级的OnEvent。
              因为Application的onevent处理了窗口滚动,就会跳出
              for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) {
                  if (e.Handled)
                      break;
                  (*--it)->OnEvent(e);
              }
              */
              ImGuiIO& io = ImGui::GetIO();
              e.Handled |= e.IsInCategory(EventCategoryMouse) & io.WantCaptureMouse; // e.Handled = e.handled | true & true; 结果为true,所以上述的application 的 event会跳出
              e.Handled |= e.IsInCategory(EventCategoryKeyboard) & io.WantCaptureKeyboard;
          }
      }
      
  • Solution to wasd bug ideas

    void EditorLayer::OnUpdate(Timestep ts)
    {
          
          
        HZ_PROFILE_FUNCTION();
        
        // 当焦点聚焦在viewport视口时,摄像机才能OnUpdate
        if (m_ViewportFocused) {
          
          
            m_CameraController.OnUpdate(ts);
        }
    

Guess you like

Origin blog.csdn.net/qq_34060370/article/details/132013429