summaryrefslogblamecommitdiffstats
path: root/src/Render.cpp
blob: a5f1e8b4d94f0e58d2b64da0f35ab0353f46876b (plain) (tree)
1
2
3
4
5
6
7
8
9

                     



                               


                           
                        
                          


                            
 
                                                                                                                                     





                                                     

                                                                              


                   



                                    



                                                                                                      
















                                                                                                                                                               
                                   

                                         









                                                                                              


                                                    
                                    
                                
 
                               
                            
                            
 

                                                          
                       


                                                                                   


                                   

                                     
                                                                             
                                                      

                                  

 
                               




                                                                                                                 
                               
                                      












                                                                                      
                                
                                                       
 

                                                  
                    
                                   

                                                  



                                            
 


                              


                             




                                              
                             
                      


                                                                  







                                                                
                      

                                              
                                
                      
                                            


                                                                                                                                                       
                      





                                                


                                                         
                          

                                                         
                          

                                                          
                          
                                     

                                                               
                          
                 
                      
                                


                                                            
                          

                                                          



                                










                                                              
                      
             
                          






                                                                                                        




                  

 
                                       


                                               













                                                                


                                  
                               
 
                                                                                                
                                                                           
                                      



                                                                                          
                                           
                                                                             


                                                                                              
                                
                           
                                              
                                    


                                                                                       
                                                                                               

                            
                                               
                                          
       
 
                                                                                   
                                                                                      

                            
                                               
                                          


                                                                                 
                                                    
                                              
       
 





                                                                                              
















                                                                                   
        

                               
                                                                                    



                                                          


                                                    

                               
                                                           
 









                                                                                                                                                                                                  
                              





                                                                  

                                                                                                    
                


                                                                                                                
                                                                                                   
                                                                                                                                                                                                                            
                                                                                                                                                                      

                                                                                              


                 

                                      
                                        
                                             

                                            
                                               
                                       
                                                                                                                                            
         
                                                    
                                               
                                       





                                  
                        
              
                       














                                                                                               
                            
                                                                       








                                                                                                                                                         
 



































                                                    

                              









                                                    

                              






                                                    
             
                              




                                                    

                              




                                                    

                              
         

                     
              
     
                         


                                                   
                                                  







                                                                      

                                                                   
 



                                                 




                                                                                                       
 

                                     

                                    
                                                                                                      

                           
 





                                                                                                       
                               




                    
#include "Render.hpp"

#include <imgui.h>
#include <easylogging++.h>

#include "imgui_impl_sdl_gl3.h"
#include "Shader.hpp"
#include "AssetManager.hpp"
#include "Event.hpp"
#include "DebugInfo.hpp"
#include "GlobalState.hpp"
#include "World.hpp"
#include "GameState.hpp"
#include "RendererWorld.hpp"

Render::Render(unsigned int windowWidth, unsigned int windowHeight, std::string windowTitle) : timer(std::chrono::milliseconds(16)) {
    InitSfml(windowWidth, windowHeight, windowTitle);
    glCheckError();
    InitGlew();
    glCheckError();
    PrepareToRendering();
    glCheckError();

    LOG(INFO) << "Supported threads: " << std::thread::hardware_concurrency();
}

Render::~Render() {
    ImGui_ImplSdlGL3_Shutdown();
    SDL_GL_DeleteContext(glContext);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

void Render::InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle) {
	LOG(INFO) << "Creating window: " << WinWidth << "x" << WinHeight << " \"" << WinTitle << "\"";
	
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        throw std::runtime_error("SDL initalization failed: " + std::string(SDL_GetError()));

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    window = SDL_CreateWindow(WinTitle.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WinWidth, WinHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    if (!window)
        throw std::runtime_error("Window creation failed: " + std::string(SDL_GetError()));

    glContext = SDL_GL_CreateContext(window);
    if (!glContext)
        throw std::runtime_error("OpenGl context creation failed: " + std::string(SDL_GetError()));

	SetMouseCapture(false);    
    renderState.WindowWidth = WinWidth;
    renderState.WindowHeight = WinHeight;
}

void Render::InitGlew() {
	LOG(INFO) << "Initializing GLEW";
	glewExperimental = GL_TRUE;
	GLenum glewStatus = glewInit();
	glCheckError();
	if (glewStatus != GLEW_OK) {
		LOG(FATAL) << "Failed to initialize GLEW: " << glewGetErrorString(glewStatus);
	}
    int width, height;
    SDL_GL_GetDrawableSize(window, &width, &height);
    glViewport(0, 0, width, height);
    glClearColor(0.8,0.8,0.8, 1.0f);
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glFrontFace(GL_CCW);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glCheckError();
    if (glActiveTexture == nullptr) {
        throw std::runtime_error("GLEW initialization failed with unknown reason");
    }
}

void Render::PrepareToRendering() {
	//TextureAtlas texture
	glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, AssetManager::Instance().GetTextureAtlas());
    AssetManager::Instance().GetTextureAtlasIndexes();

    ImGui_ImplSdlGL3_Init(window);
}

void Render::UpdateKeyboard() {
    if (ImGui::GetIO().WantCaptureKeyboard)
        return;

    SDL_Scancode toUpdate[] = { SDL_SCANCODE_A,SDL_SCANCODE_W,SDL_SCANCODE_S,SDL_SCANCODE_D,SDL_SCANCODE_SPACE };
    const Uint8 *kbState = SDL_GetKeyboardState(0);
    for (auto key : toUpdate) {
        bool isPressed = kbState[key];
        if (!isKeyPressed[key] && isPressed) {
            EventAgregator::PushEvent(EventType::KeyPressed, KeyPressedData{ key });
        }
        if (isKeyPressed[key] && isPressed) {
            //KeyHeld
        }
        if (isKeyPressed[key] && !isPressed) {
            EventAgregator::PushEvent(EventType::KeyReleased, KeyReleasedData{ key });
        }
        isKeyPressed[key] = isPressed;
    }
}

void Render::RenderFrame() {	
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if (isWireframe)
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    if (renderWorld)
        world->Render(renderState);
    if (isWireframe)
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    if (world) {
        world->Update(timer.RemainTimeMs());
    }

    RenderGui();

    SDL_GL_SwapWindow(window);
}

void Render::HandleEvents() {
    SDL_PumpEvents();
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        ImGui_ImplSdlGL3_ProcessEvent(&event);

        switch (event.type) {
        case SDL_QUIT:
            LOG(INFO) << "Received close event by window closing";
            isRunning = false;
            break;
        case SDL_WINDOWEVENT: {
            switch (event.window.event) {
            case SDL_WINDOWEVENT_RESIZED: {
                int width, height;
                SDL_GL_GetDrawableSize(window, &width, &height);
                glViewport(0, 0, width, height);
                renderState.WindowWidth = width;
                renderState.WindowHeight = height;
                break;
            }
            case SDL_WINDOWEVENT_FOCUS_GAINED:
                HasFocus = true;
                break;
            case SDL_WINDOWEVENT_FOCUS_LOST:
                HasFocus = false;                
                if (GlobalState::GetState() == State::Inventory || GlobalState::GetState() == State::Playing || GlobalState::GetState() == State::Chat)
                    GlobalState::SetState(State::Paused);
                break;
            }
            break;
        }
        case SDL_KEYDOWN:
            switch (event.key.keysym.scancode) {
            case SDL_SCANCODE_ESCAPE:
                switch (GlobalState::GetState()) {
                case State::Playing:
                    GlobalState::SetState(State::Paused);
                    break;
                case State::Inventory:
                    GlobalState::SetState(State::Paused);
                    break;
                case State::Paused:
                    GlobalState::SetState(State::Playing);
                    break;
                case State::MainMenu:
                    LOG(INFO) << "Received close event by esc";
                    isRunning = false;
                    break;
                }
                break;
            case SDL_SCANCODE_E:
                switch (GlobalState::GetState()) {
                case State::Playing:
                    GlobalState::SetState(State::Inventory);
                    break;
                case State::Inventory:
                    GlobalState::SetState(State::Playing);
                    break;
                }
                break;
            case SDL_SCANCODE_T:
                if (!ImGui::GetIO().WantCaptureKeyboard)
                    switch (GlobalState::GetState()) {
                    case State::Playing:
                        GlobalState::SetState(State::Chat);
                        SetMouseCapture(false);
                        break;
                    case State::Chat:
                        GlobalState::SetState(State::Playing);
                        SetMouseCapture(true);
                        break;
                    }
                break;
            }
            break;        
        case SDL_MOUSEMOTION:
            if (isMouseCaptured) {
                double deltaX = event.motion.xrel;
                double deltaY = event.motion.yrel;                
                deltaX *= sensetivity;
                deltaY *= sensetivity * -1;
                EventAgregator::DirectEventCall(EventType::MouseMoved, MouseMovedData{ deltaX,deltaY });
            }
        default:
            break;
        }
    }
}

void Render::HandleMouseCapture() {    
}

void Render::SetMouseCapture(bool IsCaptured) {
    if (IsCaptured == isMouseCaptured)
        return;
    isMouseCaptured = IsCaptured;

    if (isMouseCaptured) {
        SDL_GetGlobalMouseState(&prevMouseX, &prevMouseY);
    }
    
    SDL_CaptureMouse(IsCaptured ? SDL_TRUE : SDL_FALSE);
    SDL_SetRelativeMouseMode(IsCaptured ? SDL_TRUE : SDL_FALSE);

    if (!isMouseCaptured) {
        SDL_WarpMouseGlobal(prevMouseX, prevMouseY);
    }
}

void Render::ExecuteRenderLoop() {
	EventListener listener;

	listener.RegisterHandler(EventType::ConnectionSuccessfull, [this](EventData eventData) {
		auto data = std::get<ConnectionSuccessfullData>(eventData);
        stateString = "Logging in...";
	});

	listener.RegisterHandler(EventType::PlayerConnected, [this](EventData eventData) {
		auto data = std::get<PlayerConnectedData>(eventData);
        stateString = "Loading terrain...";
        world = std::make_unique<RendererWorld>(GlobalState::GetGameState());
	});

	listener.RegisterHandler(EventType::RemoveLoadingScreen, [this](EventData eventData) {
        stateString = "Playing";
        renderWorld = true;
        GlobalState::SetState(State::Playing);
        glClearColor(0, 0, 0, 1.0f);
	});

    listener.RegisterHandler(EventType::ConnectionFailed, [this](EventData eventData) {
        stateString = "Connection failed: " + std::get<ConnectionFailedData>(eventData).reason;
        renderWorld = false;
        world.reset();
        GlobalState::SetState(State::MainMenu);
        glClearColor(0.8, 0.8, 0.8, 1.0f);
    });

    listener.RegisterHandler(EventType::Disconnected, [this](EventData eventData) {
        stateString = "Disconnected: " + std::get<DisconnectedData>(eventData).reason;
        renderWorld = false;
        world.reset();
        GlobalState::SetState(State::MainMenu);
        glClearColor(0.8, 0.8, 0.8, 1.0f);
    });

    listener.RegisterHandler(EventType::Connecting, [this](EventData eventData) {
        stateString = "Connecting to the server...";
        GlobalState::SetState(State::Loading);
    });

    listener.RegisterHandler(EventType::ChatMessageReceived, [this](EventData eventData) {
        auto data = std::get<ChatMessageReceivedData>(eventData);
        std::string msg = "(" + std::to_string((int)data.position) + ") " + data.message.text;
        chatMessages.push_back(msg);
    });

    listener.RegisterHandler(EventType::StateUpdated, [this](EventData eventData) {
        switch (GlobalState::GetState()) {
        case State::Playing:
            SetMouseCapture(true);
            break;
        case State::InitialLoading:
        case State::MainMenu:
        case State::Loading:
        case State::Paused:
        case State::Inventory:
        case State::Chat:
            SetMouseCapture(false);
            break;
        }
    });

    GlobalState::SetState(State::MainMenu);
	
	while (isRunning) {
		HandleEvents();
        if (HasFocus && GlobalState::GetState() == State::Playing) UpdateKeyboard();
		if (isMouseCaptured) HandleMouseCapture();
		glCheckError();

		RenderFrame();
        while (listener.IsEventsQueueIsNotEmpty()) {
            listener.HandleEvent();
        }
		timer.Update();
	}
    EventAgregator::PushEvent(EventType::Exit, ExitData{});
}

void Render::RenderGui() {
    ImGui_ImplSdlGL3_NewFrame(window);

    if (isMouseCaptured) {
        auto& io = ImGui::GetIO();
        io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
    }
    const ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;

    //ImGui::ShowTestWindow();

    ImGui::SetNextWindowPos(ImVec2(10, 10));
    ImGui::Begin("DebugInfo", 0, ImVec2(0, 0), 0.4f, windowFlags);
    ImGui::Text("Debug Info:");
    ImGui::Separator();
    ImGui::Text("State: %s", stateString.c_str());
    ImGui::Text("FPS: %.1f (%.3fms)", ImGui::GetIO().Framerate, 1000.0f / ImGui::GetIO().Framerate);
    float gameTime = DebugInfo::gameThreadTime / 100.0f;
    if (world) {
        ImGui::Text("TPS: %.1f (%.2fms)", 1000.0f / gameTime, gameTime);
        ImGui::Text("Sections loaded: %d", (int)DebugInfo::totalSections);
        ImGui::Text("SectionsRenderer: %d (%d)", (int)DebugInfo::renderSections, (int)DebugInfo::readyRenderer);
        ImGui::Text("Culled sections: %d", (int)DebugInfo::renderSections - world->culledSections);
        ImGui::Text("Player pos: %.1f  %.1f  %.1f  OnGround=%d", world->GameStatePtr()->player->pos.x, world->GameStatePtr()->player->pos.y, world->GameStatePtr()->player->pos.z, world->GameStatePtr()->player->onGround);
        ImGui::Text("Player vel: %.1f  %.1f  %.1f", world->GameStatePtr()->player->vel.x, world->GameStatePtr()->player->vel.y, world->GameStatePtr()->player->vel.z);
        ImGui::Text("Player health: %.1f/%.1f", world->GameStatePtr()->g_PlayerHealth, 20.0f);
    }
    ImGui::End();


    switch (GlobalState::GetState()) {
    case State::MainMenu: {
        ImGui::SetNextWindowPosCenter();
        ImGui::Begin("Menu", 0, windowFlags);
        static char buff[512] = "127.0.0.1";
        static int port = 25565;
        static char buffName[512] = "HelloOne";
        if (ImGui::Button("Connect")) {
            EventAgregator::PushEvent(EventType::ConnectToServer, ConnectToServerData{ std::string(buffName), buff, (unsigned short)port });
        }
        ImGui::InputText("Username", buffName, 512);
        ImGui::InputText("Address", buff, 512);
        ImGui::InputInt("Port", &port);
        ImGui::Separator();
        if (ImGui::Button("Exit"))
            isRunning = false;
        ImGui::End();
        break;
    }
    case State::Loading:
        break;
    case State::Chat: {
        ImGui::SetNextWindowPosCenter();
        ImGui::Begin("Chat", 0, windowFlags);
        for (const auto& msg : chatMessages) {
            ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,1,1));
            ImGui::TextWrapped("%s", msg.c_str());
        }
        static char buff[256];
        ImGui::InputText("", buff, 256);
        ImGui::SameLine();
        if (ImGui::Button("Send")) {
            EventAgregator::PushEvent(EventType::SendChatMessage, SendChatMessageData{ buff });
        }
        ImGui::End();
        break;
    }
    case State::Inventory: {
        auto renderSlot = [](const SlotDataType &slot, int i) -> bool {
            return ImGui::Button(((slot.BlockId == -1 ? "  ##" :
                AssetManager::Instance().GetAssetNameByBlockId(BlockId{ (unsigned short)slot.BlockId,0 }) + " x" + std::to_string(slot.ItemCount) + "##")
                + std::to_string(i)).c_str());
        };
        ImGui::SetNextWindowPosCenter();
        ImGui::Begin("Inventory", 0, windowFlags);
        Window& inventory = world->GameStatePtr()->playerInventory;
        //Hand and drop slots
        if (renderSlot(inventory.handSlot, -1)) {

        }
        ImGui::SameLine();
        if (ImGui::Button("Drop")) {
            inventory.MakeClick(-1, true, true);
        }
        ImGui::SameLine();
        ImGui::Text("Hand slot and drop mode");
        ImGui::Separator();
        //Crafting
        if (renderSlot(inventory.slots[1], 1)) {
            inventory.MakeClick(1, true);
        }
        ImGui::SameLine();
        if (renderSlot(inventory.slots[2], 2)) {
            inventory.MakeClick(2, true);
        }
        //Crafting result
        ImGui::SameLine();
        ImGui::Text("Result");
        ImGui::SameLine();
        if (renderSlot(inventory.slots[0], 0)) {
            inventory.MakeClick(0, true);
        }
        //Crafting second line
        if (renderSlot(inventory.slots[3], 3)) {
            inventory.MakeClick(3, true);
        }
        ImGui::SameLine();
        if (renderSlot(inventory.slots[4], 4)) {
            inventory.MakeClick(4, true);
        }
        ImGui::Separator();
        //Armor and offhand            
        for (int i = 5; i < 8 + 1; i++) {
            if (renderSlot(inventory.slots[i], i)) {
                inventory.MakeClick(i, true);
            }
            ImGui::SameLine();
        }
        if (renderSlot(inventory.slots[45], 45)) {
            inventory.MakeClick(45, true);
        }
        ImGui::SameLine();
        ImGui::Text("Armor and offhand");
        ImGui::Separator();
        for (int i = 36; i < 44 + 1; i++) {
            if (renderSlot(inventory.slots[i], i)) {
                inventory.MakeClick(i, true);
            }
            ImGui::SameLine();
        }
        ImGui::Text("Hotbar");
        ImGui::Separator();
        ImGui::Text("Main inventory");
        for (int i = 9; i < 17 + 1; i++) {
            if (renderSlot(inventory.slots[i], i)) {
                inventory.MakeClick(i, true);
            }
            ImGui::SameLine();
        }
        ImGui::Text("");
        for (int i = 18; i < 26 + 1; i++) {
            if (renderSlot(inventory.slots[i], i)) {
                inventory.MakeClick(i, true);
            }
            ImGui::SameLine();
        }
        ImGui::Text("");
        for (int i = 27; i < 35 + 1; i++) {
            if (renderSlot(inventory.slots[i], i)) {
                inventory.MakeClick(i, true);
            }
            ImGui::SameLine();
        }
        ImGui::End();

        break;
    }
    case State::Paused: {
        ImGui::SetNextWindowPosCenter();
        ImGui::Begin("Pause Menu", 0, windowFlags);
        if (ImGui::Button("Continue")) {
            GlobalState::SetState(State::Playing);
        }
        ImGui::Separator();
        static float distance = world->MaxRenderingDistance;
        ImGui::SliderFloat("Render distance", &distance, 1.0f, 16.0f);

        static float sense = sensetivity;
        ImGui::SliderFloat("Sensetivity", &sense, 0.01f, 1.0f);

        static float targetFps = 60.0f;
        ImGui::SliderFloat("Target FPS", &targetFps, 1.0f, 300.0f);

        static bool wireframe = isWireframe;

        ImGui::Checkbox("Wireframe", &wireframe);

        if (ImGui::Button("Apply settings")) {
            if (distance != world->MaxRenderingDistance) {
                world->MaxRenderingDistance = distance;
                EventAgregator::PushEvent(EventType::UpdateSectionsRender, UpdateSectionsRenderData{});
            }

            if (sense != sensetivity)
                sensetivity = sense;

            isWireframe = wireframe;
            timer.SetDelayLength(std::chrono::duration<double, std::milli>(1.0 / targetFps * 1000.0));
        }
        ImGui::Separator();

        if (ImGui::Button("Disconnect")) {
            EventAgregator::PushEvent(EventType::Disconnect, DisconnectData{ "Disconnected by user" });
        }
        ImGui::End();
        break;
    }
    case State::InitialLoading:
        break;
    }

    ImGui::Render();
}