[Refac] Separate rendering, game and application.
This commit is contained in:
		
							parent
							
								
									e4b66aa68f
								
							
						
					
					
						commit
						9615c39e67
					
				@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
 | 
			
		||||
# include directories..
 | 
			
		||||
include_directories(libbettola/include)
 | 
			
		||||
include_directories(src)
 | 
			
		||||
 | 
			
		||||
# Deps.
 | 
			
		||||
find_package(SDL3   REQUIRED)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										329
									
								
								src/bettola.cpp
									
									
									
									
									
								
							
							
						
						
									
										329
									
								
								src/bettola.cpp
									
									
									
									
									
								
							@ -1,6 +1,5 @@
 | 
			
		||||
#include <GL/glew.h>
 | 
			
		||||
#include <SDL3/SDL_error.h>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
/* FINE LSP!! I'll play your games!!!! */
 | 
			
		||||
#include <SDL3/SDL_events.h> /* ~HJAPPY?!?!?! */
 | 
			
		||||
#include <SDL3/SDL_stdinc.h>
 | 
			
		||||
@ -10,17 +9,9 @@
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "bettola.h"
 | 
			
		||||
#include "bettola/network/net_common.h"
 | 
			
		||||
#include "game/remote_player.h"
 | 
			
		||||
#include "math/mat4.h"
 | 
			
		||||
#include "network/message.h"
 | 
			
		||||
#include "network/game_state_message.h"
 | 
			
		||||
#include "network/player_input_message.h"
 | 
			
		||||
 | 
			
		||||
/* Dacav's resolution ;) */
 | 
			
		||||
const int SCREEN_WIDTH  = 800;
 | 
			
		||||
@ -29,59 +20,39 @@ const int SCREEN_HEIGHT = 600;
 | 
			
		||||
Bettola::Bettola(void) :
 | 
			
		||||
    _is_running(false),
 | 
			
		||||
    _window(nullptr),
 | 
			
		||||
    _gl_context(nullptr),
 | 
			
		||||
    _vao(0),
 | 
			
		||||
    _vbo(0),
 | 
			
		||||
    _our_player_id(0),
 | 
			
		||||
    _input_sequence_number(0) {
 | 
			
		||||
 | 
			
		||||
  memset(&_server_addr, 0, sizeof(_server_addr));
 | 
			
		||||
}
 | 
			
		||||
    _gl_context(nullptr) {}
 | 
			
		||||
 | 
			
		||||
Bettola::~Bettola(void) {
 | 
			
		||||
  if(_gl_context) {
 | 
			
		||||
    SDL_GL_DestroyContext(_gl_context);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Explicitly close client socket. */
 | 
			
		||||
  _tcp_socket.close();
 | 
			
		||||
  _udp_socket.close();
 | 
			
		||||
 | 
			
		||||
  if(_window) {
 | 
			
		||||
    SDL_DestroyWindow(_window);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SDL_Quit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  if(_gl_context) {
 | 
			
		||||
    /* Should only be deleted if it was successfully created
 | 
			
		||||
     * and if _gl_context is valid.
 | 
			
		||||
     */
 | 
			
		||||
    glDeleteVertexArrays(1, &_vao);
 | 
			
		||||
    glDeleteBuffers(1, &_vbo);
 | 
			
		||||
bool Bettola::init_sdl(void) {
 | 
			
		||||
  if(!SDL_Init(SDL_INIT_VIDEO)) {
 | 
			
		||||
    fprintf(stderr, "Failed to init SDL! SDL ERROR: %s\n", SDL_GetError());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Bettola::run(void) {
 | 
			
		||||
  if(!init_sdl())               return -1;
 | 
			
		||||
  if(!create_window())          return -1;
 | 
			
		||||
  if(!create_gl_context())      return -1;
 | 
			
		||||
  if(!init_client_connection()) return -1;
 | 
			
		||||
  if(!init_glew())              return -1;
 | 
			
		||||
 | 
			
		||||
  if(!_shader.load_from_files("assets/shaders/simple.vert",
 | 
			
		||||
                             "assets/shaders/simple.frag")) {
 | 
			
		||||
  if(!_game_client.connect("127.0.0.1", 12345)) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if(!_renderer.init(SCREEN_WIDTH, SCREEN_HEIGHT)) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _projection = BettolaMath::Mat4::orthographic(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f);
 | 
			
		||||
  _shader.use();
 | 
			
		||||
  unsigned int proj_loc = glGetUniformLocation(_shader.get_id(), "projection");
 | 
			
		||||
  glUniformMatrix4fv(proj_loc, 1, GL_FALSE, _projection.get_ptr());
 | 
			
		||||
 | 
			
		||||
  setup_quad();
 | 
			
		||||
 | 
			
		||||
  glViewport(0,0,SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
			
		||||
 | 
			
		||||
  _is_running = true;
 | 
			
		||||
 | 
			
		||||
@ -95,7 +66,6 @@ int Bettola::run(void) {
 | 
			
		||||
    last_count = current_counter;
 | 
			
		||||
 | 
			
		||||
    process_events();
 | 
			
		||||
    process_network();
 | 
			
		||||
    update(delta_time);
 | 
			
		||||
    render();
 | 
			
		||||
  }
 | 
			
		||||
@ -126,108 +96,10 @@ void Bettola::process_events(void) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bettola::process_network(void) {
 | 
			
		||||
  fd_set read_fds;
 | 
			
		||||
  FD_ZERO(&read_fds);
 | 
			
		||||
 | 
			
		||||
  int tcp_fd = _tcp_socket.get_sockfd();
 | 
			
		||||
  int udp_fd = _udp_socket.get_sockfd();
 | 
			
		||||
 | 
			
		||||
  FD_SET(tcp_fd, &read_fds);
 | 
			
		||||
  FD_SET(udp_fd, &read_fds);
 | 
			
		||||
 | 
			
		||||
  int max_fd = std::max(tcp_fd, udp_fd);
 | 
			
		||||
 | 
			
		||||
  struct timeval tv;
 | 
			
		||||
  tv.tv_sec = 0;
 | 
			
		||||
  tv.tv_usec = 0; /* Non-blocking. */
 | 
			
		||||
 | 
			
		||||
  int activity = select(max_fd+1, &read_fds, nullptr, nullptr, &tv);
 | 
			
		||||
 | 
			
		||||
  if(activity > 0) {
 | 
			
		||||
    if(FD_ISSET(tcp_fd, &read_fds)) {
 | 
			
		||||
      /* We only expect player ID assignment for now. */
 | 
			
		||||
      if(_our_player_id == 0) {
 | 
			
		||||
        BettolaLib::Network::MessageHeader id_header;
 | 
			
		||||
        ssize_t id_bytes_received = _tcp_socket.recv(&id_header, sizeof(id_header));
 | 
			
		||||
        if(id_bytes_received > 0 && id_header.type == BettolaLib::Network::MessageType::PlayerId) {
 | 
			
		||||
          _tcp_socket.recv(&_our_player_id, sizeof(_our_player_id));
 | 
			
		||||
          printf("Bettola: Assigned player ID: %u\n", _our_player_id);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(FD_ISSET(udp_fd, &read_fds)) {
 | 
			
		||||
      process_udp_messages();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bettola::process_udp_messages(void) {
 | 
			
		||||
  char buffer[1024];
 | 
			
		||||
  sockaddr_in from_addr;
 | 
			
		||||
  ssize_t id_bytes_received;
 | 
			
		||||
 | 
			
		||||
  while((id_bytes_received = _udp_socket.recv_from(buffer,sizeof(buffer),from_addr))>0) {
 | 
			
		||||
    if(id_bytes_received < sizeof(BettolaLib::Network::MessageHeader)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BettolaLib::Network::MessageHeader header;
 | 
			
		||||
    memcpy(&header, buffer, sizeof(header));
 | 
			
		||||
 | 
			
		||||
    const char* payload = buffer + sizeof(BettolaLib::Network::MessageHeader);
 | 
			
		||||
    size_t payload_size = id_bytes_received-sizeof(BettolaLib::Network::MessageHeader);
 | 
			
		||||
 | 
			
		||||
    if(header.type == BettolaLib::Network::MessageType::GameState) {
 | 
			
		||||
      if(payload_size >= sizeof(BettolaLib::Network::GameStateMessage)) {
 | 
			
		||||
        BettolaLib::Network::GameStateMessage msg;
 | 
			
		||||
        memcpy(&msg, payload, sizeof(msg));
 | 
			
		||||
        process_game_state(msg);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bettola::update(double dt) {
 | 
			
		||||
  float dir_x = 0.0f;
 | 
			
		||||
  float dir_y = 0.0f;
 | 
			
		||||
  if(_input.up)     dir_y -= 1.0f;
 | 
			
		||||
  if(_input.down)   dir_y += 1.0f;
 | 
			
		||||
  if(_input.left)   dir_x -= 1.0f;
 | 
			
		||||
  if(_input.right)  dir_x += 1.0f;
 | 
			
		||||
  _player.set_velocity_direction(dir_x, dir_y);
 | 
			
		||||
 | 
			
		||||
  _player.update(dt);
 | 
			
		||||
 | 
			
		||||
  for(auto& remote_player : _remote_players) {
 | 
			
		||||
    remote_player.update(dt);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(_our_player_id > 0) {
 | 
			
		||||
    /* Sent our current input state to the server. */
 | 
			
		||||
    BettolaLib::Network::PlayerInputMessage input_msg;
 | 
			
		||||
    input_msg.player_id = _our_player_id;
 | 
			
		||||
    input_msg.sequence_number = ++_input_sequence_number;
 | 
			
		||||
    input_msg.up    = _input.up;
 | 
			
		||||
    input_msg.down  = _input.down;
 | 
			
		||||
    input_msg.left  = _input.left;
 | 
			
		||||
    input_msg.right = _input.right;
 | 
			
		||||
    input_msg.dt = dt;
 | 
			
		||||
 | 
			
		||||
    BettolaLib::Network::MessageHeader header;
 | 
			
		||||
    header.type = BettolaLib::Network::MessageType::PlayerInput;
 | 
			
		||||
    header.size = sizeof(input_msg);
 | 
			
		||||
 | 
			
		||||
    char buffer[sizeof(header) + sizeof(input_msg)];
 | 
			
		||||
    memcpy(buffer, &header, sizeof(header));
 | 
			
		||||
    memcpy(buffer+sizeof(header), &input_msg, sizeof(input_msg));
 | 
			
		||||
 | 
			
		||||
    _udp_socket.send_to(buffer, sizeof(buffer), _server_addr);
 | 
			
		||||
 | 
			
		||||
    /* Store for reconciliation. */
 | 
			
		||||
    _pending_inputs.push_back(input_msg);
 | 
			
		||||
  }
 | 
			
		||||
  _game_client.process_network_messages();
 | 
			
		||||
  _game_client.send_input(_input, dt);
 | 
			
		||||
  _game_client.update_players(dt);
 | 
			
		||||
 | 
			
		||||
  static char window_title[256];
 | 
			
		||||
  static double time_since_title_update = 0.0;
 | 
			
		||||
@ -241,120 +113,10 @@ void Bettola::update(double dt) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bettola::render(void) {
 | 
			
		||||
  glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
 | 
			
		||||
  glClear(GL_COLOR_BUFFER_BIT);
 | 
			
		||||
 | 
			
		||||
  _shader.use();
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(_player.get_x(),
 | 
			
		||||
                                                                  _player.get_y(), 0.0f);
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 scale_matrix = BettolaMath::Mat4::scale(_player.get_width(),
 | 
			
		||||
                                                            _player.get_height(), 1.0f);
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 model= trans_matrix.multiply(scale_matrix);
 | 
			
		||||
 | 
			
		||||
  unsigned int model_loc = glGetUniformLocation(_shader.get_id(), "model");
 | 
			
		||||
  glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr());
 | 
			
		||||
 | 
			
		||||
  glBindVertexArray(_vao);
 | 
			
		||||
  glDrawArrays(GL_TRIANGLES, 0, 6);
 | 
			
		||||
  glBindVertexArray(0);
 | 
			
		||||
 | 
			
		||||
  /* Render remote players. */
 | 
			
		||||
  for(const auto& remote_player : _remote_players) {
 | 
			
		||||
    BettolaMath::Mat4 remote_trans_matrix =
 | 
			
		||||
      BettolaMath::Mat4::translation(remote_player.get_x(),
 | 
			
		||||
                                     remote_player.get_y(),
 | 
			
		||||
                                     0.0f);
 | 
			
		||||
 | 
			
		||||
    BettolaMath::Mat4 remote_scale_matrix =
 | 
			
		||||
      /* Assuming remote players have same size as local player. */
 | 
			
		||||
      BettolaMath::Mat4::scale(50.0f, 50.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
    BettolaMath::Mat4 remote_model = remote_trans_matrix.multiply(remote_scale_matrix);
 | 
			
		||||
 | 
			
		||||
    glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr());
 | 
			
		||||
 | 
			
		||||
    glBindVertexArray(_vao);
 | 
			
		||||
    glDrawArrays(GL_TRIANGLES, 0, 6);
 | 
			
		||||
    glBindVertexArray(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _renderer.render(_game_client.get_player(), _game_client.get_remote_players());
 | 
			
		||||
  SDL_GL_SwapWindow(_window);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bettola::process_game_state(const BettolaLib::Network::GameStateMessage& msg) {
 | 
			
		||||
  /* Track remote players each update. */
 | 
			
		||||
  std::set<unsigned int> players_in_message;
 | 
			
		||||
 | 
			
		||||
  for(unsigned int i = 0; i < msg.num_players; ++i) {
 | 
			
		||||
    const auto& player_state = msg.players[i];
 | 
			
		||||
 | 
			
		||||
    if(player_state.player_id == _our_player_id) {
 | 
			
		||||
      /* This is our player. Reconcile. */
 | 
			
		||||
      _player.set_position(player_state.x, player_state.y);
 | 
			
		||||
 | 
			
		||||
      /* Remove all inputs from our history that the server has processed. */
 | 
			
		||||
      while(!_pending_inputs.empty() &&
 | 
			
		||||
          _pending_inputs.front().sequence_number <= player_state.last_processed_sequence) {
 | 
			
		||||
 | 
			
		||||
        _pending_inputs.pop_front();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* Now, re-apply any remaining inputs that the server hasn't
 | 
			
		||||
       * seen yet. This'll bring the client up to date with most recent inputs.
 | 
			
		||||
       */
 | 
			
		||||
      for(const auto& input : _pending_inputs) {
 | 
			
		||||
        float dir_x = (input.right ? 1.0f : 0.0f) - (input.left ? 1.0f : 0.0f);
 | 
			
		||||
        float dir_y = (input.down  ? 1.0f : 0.0f) - (input.up   ? 1.0f : 0.0f);
 | 
			
		||||
        _player.set_velocity_direction(dir_x, dir_y);
 | 
			
		||||
        _player.update(input.dt);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      /* Remote player. Find if we already know about them.. */
 | 
			
		||||
      players_in_message.insert(player_state.player_id);
 | 
			
		||||
      auto it = std::find_if(_remote_players.begin(), _remote_players.end(),
 | 
			
		||||
                             [&](const RemotePlayer& p) {
 | 
			
		||||
                                return p.get_id() == player_state.player_id;
 | 
			
		||||
                             });
 | 
			
		||||
 | 
			
		||||
      if(it != _remote_players.end()) {
 | 
			
		||||
        /* Found 'em! update their target pos. */
 | 
			
		||||
        it->set_target_position(player_state.x, player_state.y);
 | 
			
		||||
      } else {
 | 
			
		||||
        /* They are new, add them to our list. */
 | 
			
		||||
        _remote_players.emplace_back(player_state.player_id, player_state.x, player_state.y);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  _remote_players.erase(
 | 
			
		||||
    std::remove_if(_remote_players.begin(), _remote_players.end(),
 | 
			
		||||
                   [&](const RemotePlayer& p) {
 | 
			
		||||
                      return players_in_message.find(p.get_id()) == players_in_message.end();
 | 
			
		||||
                   }),
 | 
			
		||||
 | 
			
		||||
    _remote_players.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Bettola::init_sdl(void) {
 | 
			
		||||
  if(!SDL_Init(SDL_INIT_VIDEO)) {
 | 
			
		||||
    fprintf(stderr, "Failed to iniit SDL! SDL ERROR: %s\n", SDL_GetError());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Bettola::init_glew(void) {
 | 
			
		||||
  glewExperimental = GL_TRUE;
 | 
			
		||||
  GLenum glew_error = glewInit();
 | 
			
		||||
  if(glew_error != GLEW_OK) {
 | 
			
		||||
    fprintf(stderr, "Failed to init GLEW! %s\n", glewGetErrorString(glew_error));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Bettola::create_window(void) {
 | 
			
		||||
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
			
		||||
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
 | 
			
		||||
@ -381,64 +143,3 @@ bool Bettola::create_gl_context(void) {
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bettola::setup_quad(void) {
 | 
			
		||||
  float vertices[] = {
 | 
			
		||||
    /* Should be a quad??!?? */
 | 
			
		||||
    -0.5f, -0.5f, 0.0f,
 | 
			
		||||
     0.5f, -0.5f, 0.0f,
 | 
			
		||||
     0.5f,  0.5f, 0.0f,
 | 
			
		||||
     0.5f,  0.5f, 0.0f,
 | 
			
		||||
    -0.5f,  0.5f, 0.0f,
 | 
			
		||||
    -0.5f, -0.5f, 0.0f,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  glGenVertexArrays(1, &_vao);
 | 
			
		||||
  glGenBuffers(1, &_vbo);
 | 
			
		||||
 | 
			
		||||
  glBindVertexArray(_vao);
 | 
			
		||||
 | 
			
		||||
  glBindBuffer(GL_ARRAY_BUFFER, _vbo);
 | 
			
		||||
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 | 
			
		||||
 | 
			
		||||
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
 | 
			
		||||
  glEnableVertexAttribArray(0);
 | 
			
		||||
 | 
			
		||||
  glBindBuffer(GL_ARRAY_BUFFER, 0);
 | 
			
		||||
  glBindVertexArray(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Bettola::init_client_connection(void) {
 | 
			
		||||
  SDL_Delay(1000); /* Wait a second before trying to connect. */
 | 
			
		||||
  if(!_tcp_socket.create()) {
 | 
			
		||||
    printf("Bettola client: Failed to create socket.\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(!_tcp_socket.connect("127.0.0.1", BettolaLib::Network::DEFAULT_PORT)) {
 | 
			
		||||
    perror("Bettola Client: Failed to connect to server.\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(!_udp_socket.create()) {
 | 
			
		||||
    printf("Bettola Client: Failed to create UDP socket.\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Setup server address for UDP. */
 | 
			
		||||
  _server_addr.sin_family = AF_INET;
 | 
			
		||||
  _server_addr.sin_port = htons(BettolaLib::Network::DEFAULT_PORT);
 | 
			
		||||
  inet_pton(AF_INET, "127.0.0.1", &_server_addr.sin_addr);
 | 
			
		||||
 | 
			
		||||
  /* Set the socket to non-blocking. */
 | 
			
		||||
  int flags = fcntl(_tcp_socket.get_sockfd(), F_GETFL, 0);
 | 
			
		||||
  if(flags == -1) {
 | 
			
		||||
    perror("fcntl F_GETFL failed.");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  fcntl(_tcp_socket.get_sockfd(), F_SETFL, flags | O_NONBLOCK);
 | 
			
		||||
  fcntl(_udp_socket.get_sockfd(), F_SETFL, flags | O_NONBLOCK);
 | 
			
		||||
 | 
			
		||||
  printf("Bettola Client: Connected to server at 127.0.01.:%hu\n", BettolaLib::Network::DEFAULT_PORT);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,14 +5,8 @@
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "graphics/shader.h"
 | 
			
		||||
#include "game/player.h"
 | 
			
		||||
#include "game/remote_player.h"
 | 
			
		||||
#include "math/mat4.h"
 | 
			
		||||
#include "network/tcpsocket.h"
 | 
			
		||||
#include "network/player_input_message.h"
 | 
			
		||||
#include "network/udpsocket.h"
 | 
			
		||||
namespace BettolaLib { namespace Network { struct GameStateMessage; } }
 | 
			
		||||
#include "graphics/renderer.h"
 | 
			
		||||
#include "game_client.h"
 | 
			
		||||
 | 
			
		||||
class Bettola {
 | 
			
		||||
public:
 | 
			
		||||
@ -23,46 +17,19 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void process_events(void);
 | 
			
		||||
  void process_network(void);
 | 
			
		||||
  void process_udp_messages(void);
 | 
			
		||||
  void update(double dt);
 | 
			
		||||
  void render(void);
 | 
			
		||||
  void process_game_state(const BettolaLib::Network::GameStateMessage& msg);
 | 
			
		||||
 | 
			
		||||
  bool init_sdl(void);
 | 
			
		||||
  bool init_glew(void);
 | 
			
		||||
  bool create_window(void);
 | 
			
		||||
  bool create_gl_context(void);
 | 
			
		||||
 | 
			
		||||
  bool init_client_connection(void);
 | 
			
		||||
 | 
			
		||||
  void setup_quad(void);
 | 
			
		||||
 | 
			
		||||
  struct InputState {
 | 
			
		||||
    bool up     = false;
 | 
			
		||||
    bool down   = false;
 | 
			
		||||
    bool left   = false;
 | 
			
		||||
    bool right  = false;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  bool _is_running;
 | 
			
		||||
 | 
			
		||||
  unsigned int  _our_player_id;
 | 
			
		||||
  SDL_Window*   _window;
 | 
			
		||||
  SDL_GLContext _gl_context;
 | 
			
		||||
 | 
			
		||||
  Shader _shader;
 | 
			
		||||
  unsigned int _vao;
 | 
			
		||||
  unsigned int _vbo;
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 _projection;
 | 
			
		||||
  Player _player;
 | 
			
		||||
  InputState _input;
 | 
			
		||||
  std::vector<RemotePlayer> _remote_players;
 | 
			
		||||
  BettolaLib::Network::TCPSocket _tcp_socket;
 | 
			
		||||
  BettolaLib::Network::UDPSocket _udp_socket;
 | 
			
		||||
  sockaddr_in _server_addr;
 | 
			
		||||
 | 
			
		||||
  unsigned int _input_sequence_number;
 | 
			
		||||
  std::deque<BettolaLib::Network::PlayerInputMessage> _pending_inputs;
 | 
			
		||||
  Renderer _renderer;
 | 
			
		||||
  GameClient _game_client;
 | 
			
		||||
  Player::InputState _input;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include "bettola/math/vec2.h"
 | 
			
		||||
#include "network/player_input_message.h"
 | 
			
		||||
 | 
			
		||||
Player::Player(void) :
 | 
			
		||||
    _x(400.0f),
 | 
			
		||||
@ -19,6 +20,13 @@ void Player::update(double dt) {
 | 
			
		||||
  _y += _vy * dt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Player::apply_input(const BettolaLib::Network::PlayerInputMessage& input) {
 | 
			
		||||
  float dir_x = (input.right ? 1.0f : 0.0f) - (input.left ? 1.0f : 0.0f);
 | 
			
		||||
  float dir_y = (input.down  ? 1.0f : 0.0f) - (input.up   ? 1.0f : 0.0f);
 | 
			
		||||
  set_velocity_direction(dir_x, dir_y);
 | 
			
		||||
  update(input.dt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Player::set_velocity_direction(float dir_x, float dir_y) {
 | 
			
		||||
  BettolaMath::Vec2 direction = { dir_x, dir_y };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,21 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace BettolaLib { namespace Network { struct PlayerInputMessage; } }
 | 
			
		||||
 | 
			
		||||
class Player {
 | 
			
		||||
public:
 | 
			
		||||
  struct InputState {
 | 
			
		||||
    bool up     =false;
 | 
			
		||||
    bool down   =false;
 | 
			
		||||
    bool left   =false;
 | 
			
		||||
    bool right  =false;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Player(void);
 | 
			
		||||
 | 
			
		||||
  void update(double dt);
 | 
			
		||||
  void set_position(float x, float y);
 | 
			
		||||
  void apply_input(const BettolaLib::Network::PlayerInputMessage& input);
 | 
			
		||||
 | 
			
		||||
  void set_velocity_direction(float dir_x, float dir_y);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										172
									
								
								src/game_client.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/game_client.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,172 @@
 | 
			
		||||
#include <sys/select.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "game_client.h"
 | 
			
		||||
#include "game/remote_player.h"
 | 
			
		||||
#include "network/game_state_message.h"
 | 
			
		||||
#include "network/message.h"
 | 
			
		||||
 | 
			
		||||
GameClient::GameClient(void) : _our_player_id(0), _input_sequence_number(0) {
 | 
			
		||||
  memset(&_server_addr, 0, sizeof(_server_addr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GameClient::connect(const char* host, unsigned short port) {
 | 
			
		||||
  if(!_tcp_socket.create() || !_tcp_socket.connect(host,port)) {
 | 
			
		||||
    perror("BettolaClient: Failed to connect TCP socket.\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(!_udp_socket.create()) {
 | 
			
		||||
    perror("BettolaClient: Failed to create UDP socket.\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Setup server address. */
 | 
			
		||||
  _server_addr.sin_family = AF_INET;
 | 
			
		||||
  _server_addr.sin_port = htons(port);
 | 
			
		||||
  inet_pton(AF_INET, host, &_server_addr.sin_addr);
 | 
			
		||||
 | 
			
		||||
  /* Set the socket to non-blocking. */
 | 
			
		||||
  int tcp_flags = fcntl(_tcp_socket.get_sockfd(), F_GETFL, 0);
 | 
			
		||||
  fcntl(_tcp_socket.get_sockfd(), F_SETFL, tcp_flags | O_NONBLOCK);
 | 
			
		||||
 | 
			
		||||
  int udp_flags = fcntl(_udp_socket.get_sockfd(), F_GETFL, 0);
 | 
			
		||||
  fcntl(_udp_socket.get_sockfd(), F_SETFL, udp_flags | O_NONBLOCK);
 | 
			
		||||
 | 
			
		||||
  printf("BettolaClient: Connected to server at %s:%hu\n", host, port);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameClient::process_network_messages(void) {
 | 
			
		||||
  fd_set read_fds;
 | 
			
		||||
  FD_ZERO(&read_fds);
 | 
			
		||||
 | 
			
		||||
  int tcp_fd = _tcp_socket.get_sockfd();
 | 
			
		||||
  int udp_fd = _udp_socket.get_sockfd();
 | 
			
		||||
 | 
			
		||||
  FD_SET(tcp_fd, &read_fds);
 | 
			
		||||
  FD_SET(udp_fd, &read_fds);
 | 
			
		||||
 | 
			
		||||
  int max_fd = std::max(tcp_fd, udp_fd);
 | 
			
		||||
 | 
			
		||||
  struct timeval tv = {0,0}; /* Non-blocking. */
 | 
			
		||||
 | 
			
		||||
  if(select(max_fd+1, &read_fds, nullptr, nullptr, &tv) > 0) {
 | 
			
		||||
    if(FD_ISSET(tcp_fd, &read_fds) && _our_player_id == 0) {
 | 
			
		||||
      BettolaLib::Network::MessageHeader header;
 | 
			
		||||
      if(_tcp_socket.recv(&header, sizeof(header)) > 0 && header.type ==
 | 
			
		||||
         BettolaLib::Network::MessageType::PlayerId) {
 | 
			
		||||
        _tcp_socket.recv(&_our_player_id, sizeof(_our_player_id));
 | 
			
		||||
        printf("BetollaClient: Assigned player ID: %u\n", _our_player_id);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if(FD_ISSET(udp_fd, &read_fds)) {
 | 
			
		||||
      _process_udp_messages();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameClient::_process_udp_messages(void) {
 | 
			
		||||
  char buffer[1024];
 | 
			
		||||
  sockaddr_in from_addr;
 | 
			
		||||
  ssize_t bytes_received;
 | 
			
		||||
 | 
			
		||||
  while((bytes_received = _udp_socket.recv_from(buffer,sizeof(buffer),from_addr))>0) {
 | 
			
		||||
    if(bytes_received < sizeof(BettolaLib::Network::MessageHeader)) continue;
 | 
			
		||||
 | 
			
		||||
    BettolaLib::Network::MessageHeader header;
 | 
			
		||||
    memcpy(&header, buffer, sizeof(header));
 | 
			
		||||
 | 
			
		||||
    const char* payload = buffer + sizeof(BettolaLib::Network::MessageHeader);
 | 
			
		||||
    size_t payload_size = bytes_received-sizeof(BettolaLib::Network::MessageHeader);
 | 
			
		||||
 | 
			
		||||
    if(header.type == BettolaLib::Network::MessageType::GameState) {
 | 
			
		||||
      if(payload_size >= sizeof(BettolaLib::Network::GameStateMessage)) {
 | 
			
		||||
        BettolaLib::Network::GameStateMessage msg;
 | 
			
		||||
        memcpy(&msg, payload, sizeof(msg));
 | 
			
		||||
        _process_game_state(msg);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameClient::_process_game_state(const BettolaLib::Network::GameStateMessage& msg) {
 | 
			
		||||
  /* Track remote players each update. */
 | 
			
		||||
  std::set<unsigned int> players_in_message;
 | 
			
		||||
 | 
			
		||||
  for(unsigned int i = 0; i < msg.num_players; ++i) {
 | 
			
		||||
    const auto& ps = msg.players[i];
 | 
			
		||||
 | 
			
		||||
    if(ps.player_id == _our_player_id) {
 | 
			
		||||
      /* This is our player. Reconcile. */
 | 
			
		||||
      _player.set_position(ps.x, ps.y);
 | 
			
		||||
 | 
			
		||||
      /* Remove all inputs from our history that the server has processed. */
 | 
			
		||||
      while(!_pending_inputs.empty() &&
 | 
			
		||||
          _pending_inputs.front().sequence_number <= ps.last_processed_sequence) {
 | 
			
		||||
        _pending_inputs.pop_front();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* Now, re-apply any remaining inputs that the server hasn't
 | 
			
		||||
       * seen yet. This'll bring the client up to date with most recent inputs.
 | 
			
		||||
       */
 | 
			
		||||
      for(const auto& input : _pending_inputs) {
 | 
			
		||||
        _player.apply_input(input);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      /* Remote player. Find if we already know about them.. */
 | 
			
		||||
      players_in_message.insert(ps.player_id);
 | 
			
		||||
      auto it = std::find_if(_remote_players.begin(), _remote_players.end(), [&](const
 | 
			
		||||
                RemotePlayer&p) {return p.get_id() == ps.player_id; });
 | 
			
		||||
 | 
			
		||||
      if(it != _remote_players.end()) {
 | 
			
		||||
        /* Found 'em! update their target pos. */
 | 
			
		||||
        it->set_target_position(ps.x, ps.y);
 | 
			
		||||
      } else {
 | 
			
		||||
        /* They are new, add them to our list. */
 | 
			
		||||
        _remote_players.emplace_back(ps.player_id, ps.x, ps.y);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _remote_players.erase(std::remove_if(_remote_players.begin(), _remote_players.end(),
 | 
			
		||||
                       [&](const RemotePlayer& p) { return players_in_message.find(p.get_id()) ==
 | 
			
		||||
                        players_in_message.end(); }), _remote_players.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameClient::send_input(const Player::InputState& input, float dt) {
 | 
			
		||||
  if(_our_player_id > 0) {
 | 
			
		||||
    /* Sent our current input state to the server. */
 | 
			
		||||
    BettolaLib::Network::PlayerInputMessage input_msg;
 | 
			
		||||
    input_msg.player_id = _our_player_id;
 | 
			
		||||
    input_msg.sequence_number = ++_input_sequence_number;
 | 
			
		||||
    input_msg.up    = input.up;
 | 
			
		||||
    input_msg.down  = input.down;
 | 
			
		||||
    input_msg.left  = input.left;
 | 
			
		||||
    input_msg.right = input.right;
 | 
			
		||||
    input_msg.dt    = dt;
 | 
			
		||||
 | 
			
		||||
    BettolaLib::Network::MessageHeader header;
 | 
			
		||||
    header.type = BettolaLib::Network::MessageType::PlayerInput;
 | 
			
		||||
    header.size = sizeof(input_msg);
 | 
			
		||||
 | 
			
		||||
    char buffer[sizeof(header) + sizeof(input_msg)];
 | 
			
		||||
    memcpy(buffer, &header, sizeof(header));
 | 
			
		||||
    memcpy(buffer+sizeof(header), &input_msg, sizeof(input_msg));
 | 
			
		||||
 | 
			
		||||
    _udp_socket.send_to(buffer, sizeof(buffer), _server_addr);
 | 
			
		||||
    _pending_inputs.push_back(input_msg); /* Store for reconciliation. */
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameClient::update_players(float dt) {
 | 
			
		||||
  _player.update(dt);
 | 
			
		||||
  for(auto& p : _remote_players) {
 | 
			
		||||
    p.update(dt);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										40
									
								
								src/game_client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/game_client.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <deque>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#include "game/player.h"
 | 
			
		||||
#include "game/remote_player.h"
 | 
			
		||||
#include "network/tcpsocket.h"
 | 
			
		||||
#include "network/udpsocket.h"
 | 
			
		||||
#include "network/player_input_message.h"
 | 
			
		||||
namespace BettolaLib { namespace Network { struct GameStateMessage; } }
 | 
			
		||||
 | 
			
		||||
class GameClient {
 | 
			
		||||
public:
 | 
			
		||||
  GameClient(void);
 | 
			
		||||
 | 
			
		||||
  bool connect(const char* host, unsigned short port);
 | 
			
		||||
  void process_network_messages(void);
 | 
			
		||||
  void send_input(const Player::InputState& input, float dt);
 | 
			
		||||
 | 
			
		||||
  const Player& get_player(void) const { return _player; }
 | 
			
		||||
  const std::vector<RemotePlayer>& get_remote_players(void) const { return _remote_players; }
 | 
			
		||||
  void update_players(float dt);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _process_udp_messages(void);
 | 
			
		||||
  void _process_game_state(const BettolaLib::Network::GameStateMessage& msg);
 | 
			
		||||
 | 
			
		||||
  unsigned int _our_player_id;
 | 
			
		||||
  Player _player;
 | 
			
		||||
  std::vector<RemotePlayer> _remote_players;
 | 
			
		||||
 | 
			
		||||
  BettolaLib::Network::TCPSocket _tcp_socket;
 | 
			
		||||
  BettolaLib::Network::UDPSocket _udp_socket;
 | 
			
		||||
  sockaddr_in _server_addr;
 | 
			
		||||
 | 
			
		||||
  unsigned int _input_sequence_number;
 | 
			
		||||
  std::deque<BettolaLib::Network::PlayerInputMessage> _pending_inputs;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										116
									
								
								src/graphics/renderer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/graphics/renderer.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
			
		||||
#include <GL/glew.h>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
 | 
			
		||||
Renderer::Renderer(void) : _vao(0), _vbo(0) {}
 | 
			
		||||
 | 
			
		||||
Renderer::~Renderer(void) {
 | 
			
		||||
  if(_vao != 0) {
 | 
			
		||||
    glDeleteVertexArrays(1, &_vao);
 | 
			
		||||
  }
 | 
			
		||||
  if(_vbo != 0) {
 | 
			
		||||
    glDeleteBuffers(1, &_vbo);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Renderer::init(int screen_width, int screen_height) {
 | 
			
		||||
  glewExperimental = GL_TRUE;
 | 
			
		||||
  GLenum glew_error = glewInit();
 | 
			
		||||
  if(glew_error != GLEW_OK) {
 | 
			
		||||
    fprintf(stderr, "Failed to init GLEW! %s\n", glewGetErrorString(glew_error));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(!_init_shaders()) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _projection = BettolaMath::Mat4::orthographic(0.0f, (float)screen_width, (float)screen_height, 0.0f, -1.0f, 1.0f);
 | 
			
		||||
  _shader.use();
 | 
			
		||||
  unsigned int proj_loc = glGetUniformLocation(_shader.get_id(), "projection");
 | 
			
		||||
  glUniformMatrix4fv(proj_loc, 1, GL_FALSE, _projection.get_ptr());
 | 
			
		||||
 | 
			
		||||
  _setup_quad();
 | 
			
		||||
 | 
			
		||||
  glViewport(0,0,screen_width, screen_height);
 | 
			
		||||
  glEnable(GL_BLEND);
 | 
			
		||||
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::render(const Player& player, const std::vector<RemotePlayer>& remote_players) {
 | 
			
		||||
  glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
 | 
			
		||||
  glClear(GL_COLOR_BUFFER_BIT);
 | 
			
		||||
 | 
			
		||||
  _shader.use();
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(player.get_x(),
 | 
			
		||||
                                                                  player.get_y(), 0.0f);
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 scale_matrix = BettolaMath::Mat4::scale(player.get_width(),
 | 
			
		||||
                                                            player.get_height(), 1.0f);
 | 
			
		||||
 | 
			
		||||
  BettolaMath::Mat4 model= trans_matrix.multiply(scale_matrix);
 | 
			
		||||
 | 
			
		||||
  unsigned int model_loc = glGetUniformLocation(_shader.get_id(), "model");
 | 
			
		||||
  glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr());
 | 
			
		||||
 | 
			
		||||
  glBindVertexArray(_vao);
 | 
			
		||||
  glDrawArrays(GL_TRIANGLES, 0, 6);
 | 
			
		||||
  glBindVertexArray(0);
 | 
			
		||||
 | 
			
		||||
  /* Render remote players. */
 | 
			
		||||
  for(const auto& remote_player : remote_players) {
 | 
			
		||||
    BettolaMath::Mat4 remote_trans_matrix =
 | 
			
		||||
      BettolaMath::Mat4::translation(remote_player.get_x(),
 | 
			
		||||
                                     remote_player.get_y(),
 | 
			
		||||
                                     0.0f);
 | 
			
		||||
 | 
			
		||||
    BettolaMath::Mat4 remote_scale_matrix =
 | 
			
		||||
      /* Assuming remote players have same size as local player. */
 | 
			
		||||
      BettolaMath::Mat4::scale(50.0f, 50.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
    BettolaMath::Mat4 remote_model = remote_trans_matrix.multiply(remote_scale_matrix);
 | 
			
		||||
 | 
			
		||||
    glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr());
 | 
			
		||||
 | 
			
		||||
    glBindVertexArray(_vao);
 | 
			
		||||
    glDrawArrays(GL_TRIANGLES, 0, 6);
 | 
			
		||||
    glBindVertexArray(0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Renderer::_init_shaders(void) {
 | 
			
		||||
  if(!_shader.load_from_files("assets/shaders/simple.vert",
 | 
			
		||||
                              "assets/shaders/simple.frag")) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::_setup_quad(void) {
 | 
			
		||||
  float vertices[] = {
 | 
			
		||||
    /* Should be a quad??!?? */
 | 
			
		||||
    -0.5f, -0.5f, 0.0f,
 | 
			
		||||
     0.5f, -0.5f, 0.0f,
 | 
			
		||||
     0.5f,  0.5f, 0.0f,
 | 
			
		||||
     0.5f,  0.5f, 0.0f,
 | 
			
		||||
    -0.5f,  0.5f, 0.0f,
 | 
			
		||||
    -0.5f, -0.5f, 0.0f,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  glGenVertexArrays(1, &_vao);
 | 
			
		||||
  glGenBuffers(1, &_vbo);
 | 
			
		||||
 | 
			
		||||
  glBindVertexArray(_vao);
 | 
			
		||||
 | 
			
		||||
  glBindBuffer(GL_ARRAY_BUFFER, _vbo);
 | 
			
		||||
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 | 
			
		||||
 | 
			
		||||
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
 | 
			
		||||
  glEnableVertexAttribArray(0);
 | 
			
		||||
 | 
			
		||||
  glBindBuffer(GL_ARRAY_BUFFER, 0);
 | 
			
		||||
  glBindVertexArray(0);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/graphics/renderer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/graphics/renderer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "shader.h"
 | 
			
		||||
#include "game/player.h"
 | 
			
		||||
#include "game/remote_player.h"
 | 
			
		||||
#include "math/mat4.h"
 | 
			
		||||
 | 
			
		||||
class Renderer {
 | 
			
		||||
public:
 | 
			
		||||
  Renderer(void);
 | 
			
		||||
  ~Renderer(void);
 | 
			
		||||
 | 
			
		||||
  bool init(int screen_width, int screen_height);
 | 
			
		||||
  void render(const Player& player, const std::vector<RemotePlayer>& remote_players);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  bool _init_shaders();
 | 
			
		||||
  void _setup_quad();
 | 
			
		||||
 | 
			
		||||
  Shader _shader;
 | 
			
		||||
  unsigned int _vao;
 | 
			
		||||
  unsigned int _vbo;
 | 
			
		||||
  BettolaMath::Mat4 _projection;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user