├── .gitignore ├── Readme.md ├── bsp-loader.gyp ├── makefile └── src ├── Entity.cpp ├── Font.cpp ├── Logger.cpp ├── Model.cpp ├── Renderer.cpp ├── World.cpp ├── bezier.cpp ├── bezier.hpp ├── bot_input_component.cpp ├── bot_input_component.hpp ├── bsp.cpp ├── bsp.hpp ├── component.hpp ├── entity.hpp ├── font.hpp ├── frustum.cpp ├── frustum.hpp ├── input.cpp ├── input.hpp ├── logger.hpp ├── main.cpp ├── message.hpp ├── messenger.cpp ├── messenger.hpp ├── model.hpp ├── player_animation_component.cpp ├── player_animation_component.hpp ├── player_input_component.cpp ├── player_input_component.hpp ├── player_physics_component.cpp ├── player_physics_component.hpp ├── q3_shader.cpp ├── q3_shader.hpp ├── renderer.hpp ├── shader.cpp ├── shader.hpp ├── shader_loader.cpp ├── shader_loader.hpp ├── texture_loader.cpp ├── texture_loader.hpp ├── util.hpp └── world.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .clang_complete 3 | .dir-locals.el -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | bsp-renderer 2 | ============ 3 | 4 | Supports: 5 | * Quake 3 shaders 6 | * Frustum culling 7 | * Quake 3 models with animations 8 | * Collision detection 9 | * Simple Bitmap font rendering 10 | 11 | Dependencies 12 | ------------ 13 | 14 | * SDL 2.x 15 | * SDL_image 16 | * Boost filesystem 17 | * GLEW 18 | * GLM -------------------------------------------------------------------------------- /bsp-loader.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'make_global_settings': [ 3 | ['CXX','/usr/bin/clang++'], 4 | ['LINK','/usr/bin/clang++'], 5 | ], 6 | 'target_defaults': { 7 | 'xcode_settings': { 8 | 'OTHER_CFLAGS': [ 9 | '-std=c++11', 10 | '-g', 11 | '-D_THREAD_SAFE', 12 | ], 13 | 'OTHER_LDFLAGS': [ 14 | '-L/usr/local/lib', 15 | '-lSDLmain', 16 | '-Wl,-framework,Cocoa', 17 | ], 18 | }, 19 | 'cflags': [ 20 | '-std=c++11', 21 | ], 22 | 'product_dir': '../../resources/' 23 | }, 24 | 'targets': [ 25 | { 26 | 'target_name': 'osx', 27 | 'product_name': 'bsp-renderer', 28 | 'type': 'executable', 29 | 'sources': [ 30 | 'src/bot_input_component.cpp', 31 | 'src/shader_loader.cpp', 32 | 'src/entity.cpp', 33 | 'src/texture_loader.cpp', 34 | 'src/font.cpp', 35 | 'src/world.cpp', 36 | 'src/logger.cpp', 37 | 'src/bezier.cpp', 38 | 'src/model.cpp', 39 | 'src/bsp.cpp', 40 | 'src/player_animation_component.cpp', 41 | 'src/frustum.cpp', 42 | 'src/player_input_component.cpp', 43 | 'src/input.cpp', 44 | 'src/player_physics_component.cpp', 45 | 'src/main.cpp', 46 | 'src/q3_shader.cpp', 47 | 'src/messenger.cpp', 48 | 'src/renderer.cpp', 49 | 'src/shader.cpp', 50 | ], 51 | 'include_dirs': [ 52 | '/usr/local/include', 53 | '/usr/local/include/SDL2' 54 | ], 55 | 'ldflags': [ 56 | '-L/usr/local/lib', 57 | ], 58 | 'libraries': [ 59 | '-framework OpenGL', 60 | '-lSDL2', 61 | '-lSDL2_image', 62 | '-lGlew', 63 | '-lboost_system', 64 | '-lboost_filesystem', 65 | ], 66 | }], 67 | } 68 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | osx: 2 | GYP_GENERATORS=ninja gyp bsp-loader.gyp --toplevel-dir=`pwd` --depth=0 3 | ninja -C out/Default/ osx 4 | 5 | clean: 6 | ninja -C out/Default/ -t clean 7 | -------------------------------------------------------------------------------- /src/Entity.cpp: -------------------------------------------------------------------------------- 1 | #include "entity.hpp" 2 | 3 | #include "component.hpp" 4 | 5 | Entity::Entity(void) 6 | : up_(glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)), 7 | right_(glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)), 8 | look_(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)) {} 9 | 10 | Entity::~Entity(void) {} 11 | 12 | void Entity::AddComponent(Component *component) { 13 | components_.push_back(component); 14 | component->set_entity(this); 15 | } 16 | 17 | void Entity::SendMessage(Message &message) {} 18 | 19 | void Entity::Update(unsigned int time) { 20 | for (Component *component : components_) { 21 | component->Update(time); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Font.cpp: -------------------------------------------------------------------------------- 1 | #include "font.hpp" 2 | 3 | extern glm::mat4 orthomatrix; 4 | 5 | Font::Font(void) 6 | { 7 | } 8 | 9 | 10 | Font::~Font(void) 11 | { 12 | } 13 | 14 | int Font::LoadFont(std::string font) 15 | { 16 | return 0; 17 | } 18 | 19 | void Font::PrintString(std::string text, glm::vec2& position, glm::vec4& color) 20 | { 21 | //std::vector vertices; 22 | //std::vector uvcoords; 23 | 24 | //for (int i = 0; i < text.length(); ++i) 25 | //{ 26 | // // create big quad from triangles 27 | // glm::vec2 upper_left, upper_right, lower_left, lower_right; 28 | 29 | // upper_left.x = position.x + i * 16.0f; 30 | // upper_left.y = position.y + 16.0f; 31 | 32 | // upper_right.x = position.x + i * 16.0f + 16.0f; 33 | // upper_right.y = upper_left.y; 34 | 35 | // lower_left.x = upper_left.x; 36 | // lower_left.y = position.y; 37 | 38 | // lower_right.x = upper_right.x; 39 | // lower_right.y = lower_left.y; 40 | 41 | // vertices.push_back(lower_left); 42 | // vertices.push_back(upper_left); 43 | // vertices.push_back(upper_right); 44 | 45 | // vertices.push_back(lower_right); 46 | // vertices.push_back(upper_right); 47 | // vertices.push_back(lower_left); 48 | 49 | // // set texture coordinates to match string 50 | // char character = text.at(i); 51 | // float col = (character % 16) / 16.0f; 52 | // float row = (character / 16) / 16.0f; 53 | 54 | // upper_left.x = col; 55 | // upper_left.y = row; 56 | 57 | // upper_right.x = upper_left.x + 1.0f / 16.0f; 58 | // upper_right.y = upper_left.y; 59 | 60 | // lower_left.x = upper_left.x; 61 | // lower_left.y = (row + 1.0f / 16.0f); 62 | 63 | // lower_right.x = lower_left.x + 1.0f / 16.0f; 64 | // lower_right.y = (row + 1.0f / 16.0f); 65 | 66 | // uvcoords.push_back(lower_left); 67 | // uvcoords.push_back(upper_left); 68 | // uvcoords.push_back(upper_right); 69 | 70 | // uvcoords.push_back(lower_right); 71 | // uvcoords.push_back(upper_right); 72 | // uvcoords.push_back(lower_left); 73 | //} 74 | 75 | //GLuint vboId; 76 | //GLuint uvboId; 77 | 78 | //glDisableVertexAttribArray(2); 79 | 80 | //glUseProgram(shader_); 81 | 82 | //glGenBuffers(1, &vboId); 83 | //glBindBuffer(GL_ARRAY_BUFFER, vboId); 84 | //glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(glm::vec2), 85 | // vertices.data(), 86 | // GL_STATIC_DRAW); 87 | 88 | //glVertexAttribPointer(position_idx_, 2, GL_FLOAT, GL_FALSE, 0, 89 | // BUFFER_OFFSET(0)); 90 | 91 | //glGenBuffers(1, &uvboId); 92 | //glBindBuffer(GL_ARRAY_BUFFER, uvboId); 93 | //glBufferData(GL_ARRAY_BUFFER, uvcoords.size()*sizeof(glm::vec2), 94 | // uvcoords.data(), 95 | // GL_STATIC_DRAW); 96 | 97 | //glVertexAttribPointer(tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, 0, 98 | // BUFFER_OFFSET(0)); 99 | 100 | //glEnable(GL_TEXTURE_2D); 101 | //glActiveTexture(GL_TEXTURE0); 102 | //glBindTexture(GL_TEXTURE_2D, texture_); 103 | 104 | //glUniformMatrix4fv(projection_idx_, 1, false, glm::value_ptr(orthomatrix)); 105 | //glUniform4fv(color_idx_, 1, glm::value_ptr(color)); 106 | // 107 | //glEnable(GL_BLEND); 108 | //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 109 | //glDrawArrays(GL_TRIANGLES, 0, vertices.size()); 110 | //glDisable(GL_BLEND); 111 | 112 | //glUseProgram(0); 113 | } 114 | -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace logger { 8 | namespace { 9 | Level max_level_ = DEBUG; 10 | } 11 | 12 | void set_max_level(Level max_level) {} 13 | 14 | void Log(Level level, const char *fmt, va_list arg) { 15 | if (level > max_level_) 16 | return; 17 | vprintf(fmt, arg); 18 | std::cout << std::endl; 19 | } 20 | 21 | void Log(Level level, const char *fmt, ...) { 22 | va_list arg; 23 | va_start(arg, fmt); 24 | Log(level, fmt, arg); 25 | va_end(arg); 26 | } 27 | 28 | void Debug(const char *fmt, ...) { 29 | va_list arg; 30 | va_start(arg, fmt); 31 | Log(DEBUG, fmt, arg); 32 | va_end(arg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Model.cpp: -------------------------------------------------------------------------------- 1 | #include "model.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | Model::Model(void) { 10 | // header_ = NULL; 11 | frames_ = nullptr; 12 | tags_ = nullptr; 13 | surfaces_ = nullptr; 14 | shaders_ = nullptr; 15 | triangles_ = nullptr; 16 | texcoords_ = nullptr; 17 | normals_ = nullptr; 18 | vertices_ = nullptr; 19 | } 20 | 21 | Model::~Model(void) { 22 | if (shaders_ != nullptr) { 23 | for (int i = 0; i < header_.num_surfaces; i++) { 24 | delete[] shaders_[i]; 25 | } 26 | delete[] shaders_; 27 | } 28 | 29 | if (triangles_ != nullptr) { 30 | for (int i = 0; i < header_.num_surfaces; i++) { 31 | delete[] triangles_[i]; 32 | } 33 | delete[] triangles_; 34 | } 35 | 36 | if (texcoords_ != nullptr) { 37 | for (int i = 0; i < header_.num_surfaces; i++) { 38 | delete[] texcoords_[i]; 39 | } 40 | delete[] texcoords_; 41 | } 42 | 43 | if (normals_ != nullptr) { 44 | for (int i = 0; i < header_.num_surfaces; i++) { 45 | delete[] normals_[i]; 46 | } 47 | delete[] normals_; 48 | } 49 | 50 | if (vertices_ != nullptr) { 51 | for (int i = 0; i < header_.num_surfaces; i++) { 52 | delete[] vertices_[i]; 53 | } 54 | delete[] vertices_; 55 | } 56 | 57 | // if (header_ != NULL) delete header_; 58 | if (frames_ != nullptr) 59 | delete[] frames_; 60 | if (tags_ != nullptr) 61 | delete[] tags_; 62 | if (surfaces_ != nullptr) 63 | delete[] surfaces_; 64 | } 65 | 66 | // TODO: maybe free unused data like files' index and vertexes! (got my own 67 | // structure for that) 68 | Model::Model(std::string filename) { 69 | std::ifstream fin(filename.c_str(), std::ios::binary); 70 | 71 | fin.read((char *)(&header_), sizeof(md3_header)); 72 | 73 | frames_ = new md3_frame[header_.num_frames]; 74 | tags_ = new md3_tag[header_.num_tags * header_.num_frames]; 75 | surfaces_ = new md3_surface[header_.num_surfaces]; 76 | shaders_ = new md3_shader *[header_.num_surfaces]; 77 | triangles_ = new md3_triangle *[header_.num_surfaces]; 78 | texcoords_ = new md3_texcoord *[header_.num_surfaces]; 79 | normals_ = new md3_vertex *[header_.num_surfaces]; 80 | vertices_ = new my_vertex *[header_.num_surfaces]; 81 | 82 | fin.seekg(header_.ofs_frames); 83 | for (int i = 0; i < header_.num_frames; ++i) { 84 | fin.read((char *)(&frames_[i]), sizeof(md3_frame)); 85 | } 86 | 87 | fin.seekg(header_.ofs_tags); 88 | for (int i = 0; i < header_.num_tags * header_.num_frames; ++i) { 89 | fin.read((char *)(&tags_[i]), sizeof(md3_tag)); 90 | } 91 | 92 | fin.seekg(header_.ofs_surfaces); 93 | for (int i = 0; i < header_.num_surfaces; ++i) { 94 | fin.read((char *)(&surfaces_[i]), sizeof(md3_surface)); 95 | 96 | shaders_[i] = new md3_shader[surfaces_[i].num_shaders]; 97 | triangles_[i] = new md3_triangle[surfaces_[i].num_triangles]; 98 | texcoords_[i] = 99 | new md3_texcoord[(surfaces_[i].num_verts) * (surfaces_[i].num_frames)]; 100 | normals_[i] = 101 | new md3_vertex[(surfaces_[i].num_verts) * (surfaces_[i].num_frames)]; 102 | vertices_[i] = 103 | new my_vertex[(surfaces_[i].num_verts) * (surfaces_[i].num_frames)]; 104 | 105 | fin.seekg(header_.ofs_surfaces + surfaces_[i].ofs_shaders); 106 | for (int j = 0; j < surfaces_[i].num_shaders; ++j) { 107 | fin.read((char *)(&shaders_[i][j]), sizeof(md3_shader)); 108 | } 109 | 110 | fin.seekg(header_.ofs_surfaces + surfaces_[i].ofs_triangles); 111 | for (int j = 0, k = 0; j < surfaces_[i].num_triangles; ++j, k += 3) { 112 | fin.read((char *)(&triangles_[i][j]), sizeof(md3_triangle)); 113 | } 114 | 115 | fin.seekg(header_.ofs_surfaces + surfaces_[i].ofs_st); 116 | for (int j = 0; j < surfaces_[i].num_verts; ++j) { 117 | fin.read((char *)(&texcoords_[i][j]), sizeof(md3_texcoord)); 118 | } 119 | 120 | for (int j = 0, k = 0; j < surfaces_[i].num_frames * surfaces_[i].num_verts; 121 | ++j, ++k) { 122 | if (k >= surfaces_[i].num_verts) { 123 | k = 0; 124 | } 125 | vertices_[i][j].u = texcoords_[i][k].st[0]; 126 | vertices_[i][j].v = texcoords_[i][k].st[1]; 127 | } 128 | 129 | fin.seekg(header_.ofs_surfaces + surfaces_[i].ofs_xyznormal); 130 | for (int j = 0; j < surfaces_[i].num_frames * surfaces_[i].num_verts; ++j) { 131 | fin.read((char *)(&normals_[i][j]), sizeof(md3_vertex)); 132 | 133 | float scale = (1.0f / 64.0f); 134 | 135 | vertices_[i][j].position.x = (float)(normals_[i][j].coord[0]) * scale; 136 | vertices_[i][j].position.y = (float)(normals_[i][j].coord[1]) * scale; 137 | vertices_[i][j].position.z = (float)(normals_[i][j].coord[2]) * scale; 138 | 139 | // extracting normals with code from q3source :) 140 | float lat; 141 | float lng; 142 | lat = (normals_[i][j].normal >> 8) & 0xff; 143 | lng = (normals_[i][j].normal & 0xff); 144 | lat *= Q_PI / 128; 145 | lng *= Q_PI / 128; 146 | 147 | vertices_[i][j].normal.x = cos(lat) * sin(lng); 148 | vertices_[i][j].normal.y = sin(lat) * sin(lng); 149 | vertices_[i][j].normal.z = cos(lng); 150 | } 151 | 152 | fin.seekg(header_.ofs_surfaces + surfaces_[i].ofs_end); 153 | header_.ofs_surfaces = header_.ofs_surfaces + surfaces_[i].ofs_end; 154 | } 155 | 156 | glGenBuffers(1, &vboId_); 157 | glBindBuffer(GL_ARRAY_BUFFER, vboId_); 158 | 159 | glBufferData(GL_ARRAY_BUFFER, 160 | (surfaces_[0].num_verts) * (surfaces_[0].num_frames) * 161 | sizeof(my_vertex), 162 | NULL, GL_STATIC_DRAW); 163 | 164 | glBufferSubData(GL_ARRAY_BUFFER, 0, 165 | (surfaces_[0].num_verts) * (surfaces_[0].num_frames) * 166 | sizeof(my_vertex), 167 | vertices_[0]); 168 | 169 | glGenBuffers(1, &iboId_); 170 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId_); 171 | 172 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, 173 | (surfaces_[0].num_triangles) * sizeof(md3_triangle), NULL, 174 | GL_STATIC_DRAW); 175 | 176 | glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 177 | (surfaces_[0].num_triangles) * sizeof(md3_triangle), 178 | triangles_[0]); 179 | 180 | // load model animation file 181 | } 182 | 183 | PlayerModel::PlayerModel(std::string path, std::string name) 184 | : path_{path}, name_{name} { 185 | LoadAnimations(); 186 | } 187 | 188 | void PlayerModel::LoadAnimations() { 189 | // sex m 190 | // headoffset 0 0 0 191 | // footsteps normal 192 | // first frame, num frames, looping frames, frames per second 193 | // 0 30 0 25 // BOTH_DEATH1 194 | 195 | // TODO: file not found handling 196 | auto file_name = path_ + name_ + "/animation.cfg"; 197 | auto file_size = boost::filesystem::file_size(file_name); 198 | 199 | char *buffer = new char[file_size + 1]; 200 | std::ifstream fin(file_name); 201 | fin.read(buffer, file_size); 202 | buffer[file_size] = 0; 203 | 204 | ParseAnimations(buffer); 205 | 206 | delete[] buffer; 207 | } 208 | 209 | /* 210 | unsigned int GetNewLinePosition(const char *buffer, unsigned int offset) { 211 | while (1) { 212 | switch (buffer[offset]) { 213 | case 0x0A: 214 | return offset; 215 | case 0x0D: 216 | return offset; 217 | } 218 | ++offset; 219 | } 220 | } 221 | 222 | unsigned int GetTokenEndPosition(const char *buffer, unsigned int offset) { 223 | while (1) // while buffer[offset] != nullptr 224 | { 225 | switch (buffer[offset]) { 226 | case ' ': 227 | return offset; 228 | case 0x09: 229 | return offset; 230 | case 0x0A: 231 | return offset; 232 | case 0x0D: 233 | return offset; 234 | } 235 | ++offset; 236 | } 237 | } 238 | */ 239 | 240 | void SkipLine(char *&buffer) { 241 | while (1) { 242 | switch (buffer[0]) { 243 | case '\n': 244 | ++buffer; 245 | return; 246 | default: 247 | ++buffer; 248 | } 249 | } 250 | } 251 | 252 | void SkipWhitespace(char *&buffer) { 253 | while (1) { 254 | switch (buffer[0]) { 255 | case '/': 256 | if (buffer[1] == '/') { 257 | SkipLine(buffer); 258 | } 259 | break; 260 | case ' ': 261 | case '\t': 262 | case '\n': 263 | case '\r': 264 | ++buffer; 265 | break; 266 | default: 267 | return; 268 | } 269 | } 270 | } 271 | 272 | void ReadToken(char *token, char *&buffer) { 273 | SkipWhitespace(buffer); 274 | token[0] = 0; 275 | auto i = 0u; 276 | while (1) { 277 | switch (buffer[0]) { 278 | case ' ': 279 | case '\t': 280 | case '\n': 281 | case '\0': 282 | token[i] = 0; 283 | return; 284 | default: 285 | token[i++] = buffer[0]; 286 | buffer++; 287 | } 288 | } 289 | } 290 | 291 | void PlayerModel::ParseAnimations(char *buffer) { 292 | char token[255]; 293 | while (1) { 294 | ReadToken(token, buffer); 295 | if (!strcmp(token, "sex")) { 296 | ReadToken(token, buffer); 297 | // if m {} else {} 298 | } else if (!strcmp(token, "headoffset")) { 299 | ReadToken(token, buffer); 300 | // x = atoi(token); 301 | ReadToken(token, buffer); 302 | // y = atoi(token); 303 | ReadToken(token, buffer); 304 | // z = atoi(token); 305 | } else if (!strcmp(token, "footsteps")) { 306 | ReadToken(token, buffer); 307 | // if token == normal 308 | } else if (token[0] >= '0' && token[0] <= '9') { 309 | // beginning of animation info 310 | auto skip = 0u; 311 | for (auto i = 0u; i < MAX_ANIMATIONS; ++i) { 312 | 313 | if (!token[0]) { 314 | // parse special cases 315 | if (i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE) { 316 | animations_[i].first_frame = animations_[TORSO_GESTURE].first_frame; 317 | // animations_[i].frame_lerp = 318 | // animations_[TORSO_GESTURE].frame_lerp; 319 | // animations_[i].initial_lerp = 320 | // animations_[TORSO_GESTURE].initial_lerp; 321 | animations_[i].loop_frames = animations_[TORSO_GESTURE].loop_frames; 322 | animations_[i].num_frames = animations_[TORSO_GESTURE].num_frames; 323 | animations_[i].reversed = false; 324 | continue; 325 | } 326 | logger::Debug("Error while loading animation.cfg"); 327 | break; 328 | } 329 | 330 | animations_[i].first_frame = atoi(token); 331 | 332 | if (i == LEGS_WALKCR) { 333 | skip = animations_[LEGS_WALKCR].first_frame - 334 | animations_[TORSO_GESTURE].first_frame; 335 | } 336 | if (i >= LEGS_WALKCR && i < TORSO_GETFLAG) { 337 | animations_[i].first_frame -= skip; 338 | } 339 | 340 | ReadToken(token, buffer); 341 | animations_[i].num_frames = atoi(token); 342 | ReadToken(token, buffer); 343 | animations_[i].loop_frames = atoi(token); 344 | 345 | ReadToken(token, buffer); 346 | int fps = atoi(token); 347 | if (fps == 0) { 348 | fps = 1; 349 | } 350 | animations_[i].frames_per_second = 1000.0 / fps; 351 | 352 | ReadToken(token, buffer); 353 | 354 | logger::Debug("animation: %d %d %d %f", animations_[i].first_frame, 355 | animations_[i].num_frames, animations_[i].loop_frames, 356 | animations_[i].frames_per_second); 357 | } 358 | 359 | memcpy(&animations_[LEGS_BACKCR], &animations_[LEGS_WALKCR], 360 | sizeof(animation_info)); 361 | animations_[LEGS_BACKCR].reversed = true; 362 | memcpy(&animations_[LEGS_BACKWALK], &animations_[LEGS_WALK], 363 | sizeof(animation_info)); 364 | animations_[LEGS_BACKWALK].reversed = true; 365 | 366 | break; 367 | } 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /src/Renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "renderer.hpp" 2 | 3 | #define GLM_FORCE_RADIANS 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bezier.hpp" 9 | #include "entity.hpp" 10 | #include "model.hpp" 11 | #include "q3_shader.hpp" 12 | #include "shader.hpp" 13 | #include "shader_loader.hpp" 14 | #include "texture_loader.hpp" 15 | #include "world.hpp" 16 | 17 | extern World world; 18 | extern Model *lower; 19 | extern Model *upper; 20 | extern Model *head; 21 | 22 | Renderer::Renderer(void) 23 | : screen_width_(WIDTH), screen_height_(HEIGHT), current_shader_(nullptr) {} 24 | 25 | Renderer::~Renderer(void) {} 26 | 27 | void Renderer::AddRenderables(std::vector renderables) { 28 | renderables_ = renderables; 29 | } 30 | 31 | glm::mat4 Renderer::GetCameraMatrixFromEntity(Entity &entity) { 32 | // this is quake3 coordinate system. 33 | entity.look_ = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); 34 | entity.right_ = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); 35 | entity.up_ = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); 36 | 37 | glm::mat4 matrix(1.0f); 38 | 39 | matrix = glm::rotate(matrix, entity.yaw_, glm::vec3(entity.up_)); 40 | 41 | entity.right_ = matrix * entity.right_; 42 | entity.look_ = matrix * entity.look_; 43 | 44 | matrix = glm::mat4(1.0f); 45 | matrix = glm::rotate(matrix, entity.pitch_, glm::vec3(entity.right_)); 46 | 47 | entity.look_ = matrix * entity.look_; 48 | entity.up_ = matrix * entity.up_; 49 | 50 | // switch to opengl coordinate system for view matrix calculation 51 | glm::vec4 right_ogl = quake2ogl * entity.right_; 52 | glm::vec4 look_ogl = quake2ogl * entity.look_; 53 | glm::vec4 up_ogl = quake2ogl * entity.up_; 54 | glm::vec4 position_ogl = quake2ogl * entity.position_; 55 | position_ogl.y += 30.0f; 56 | 57 | // return glm::mat4(look_.x, right_.x, up_.x, 0.0f, 58 | // look_.y, right_.y, up_.y, 0.0f, 59 | // look_.z, right_.z, up_.z, 0.0f, 60 | // glm::dot(-position_, look_), glm::dot(-position_, right_), 61 | // glm::dot(-position_, up_), 1.0f); 62 | 63 | return glm::mat4(right_ogl.x, up_ogl.x, look_ogl.x, 0.0f, right_ogl.y, 64 | up_ogl.y, look_ogl.y, 0.0f, right_ogl.z, up_ogl.z, 65 | look_ogl.z, 0.0f, glm::dot(-position_ogl, right_ogl), 66 | glm::dot(-position_ogl, up_ogl), 67 | glm::dot(-position_ogl, look_ogl), 1.0f); 68 | } 69 | 70 | void Renderer::Initialize() { 71 | glEnable(GL_DEPTH_TEST); 72 | 73 | glEnable(GL_CULL_FACE); 74 | glFrontFace(GL_CW); 75 | 76 | glViewport(0, 0, screen_width_, screen_height_); 77 | 78 | projectionmatrix_ = glm::perspective( 79 | 45.0f, (float)screen_width_ / (float)screen_height_, 1.0f, 10000.f); 80 | orthomatrix_ = glm::ortho(0.0f, (float)screen_width_, 0.0f, 81 | (float)screen_height_, -1.0f, 1.0f); 82 | } 83 | 84 | void Renderer::SetupFrame() { 85 | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 86 | glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 87 | } 88 | 89 | void Renderer::Setup3DRendering() { 90 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 91 | 92 | modelmatrix_ = GetCameraMatrixFromEntity(*world.player_); 93 | 94 | // modelmatrix_ = GetCameraMatrixFromEntity(*world.enemy_); 95 | // modelmatrix_ = glm::lookAt(glm::vec3( 50.0f, 50.0f, 50.0f ), 96 | // glm::vec3(-15.0, -15.0, -15.0), glm::vec3(0.0, 1.0, 0.0)); 97 | 98 | modelmatrix_ *= quake2ogl; 99 | // g_frustum.extract_planes(modelmatrix, projectionmatrix); 100 | 101 | glEnable(GL_CULL_FACE); 102 | // glEnable(GL_BLEND); 103 | // glBlendFunc(GL_ONE, GL_ZERO); // WAS ONE before 104 | } 105 | 106 | void Renderer::Setup2DRendering() { glDisable(GL_CULL_FACE); } 107 | 108 | void Renderer::RenderFrame(float time) { 109 | time_ = time; 110 | 111 | SetupFrame(); 112 | 113 | // draw scene 114 | Setup3DRendering(); 115 | 116 | glBindBuffer(GL_ARRAY_BUFFER, world.map_->vboId); 117 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, world.map_->iboId); 118 | glEnableVertexAttribArray(2); 119 | for (unsigned int i = 0; i < renderables_.size(); ++i) { 120 | RenderFace(renderables_[i]); 121 | } 122 | 123 | RenderModel(); 124 | // draw gui and overlays 125 | // Setup2DRendering(); 126 | 127 | // font.PrintString("", glm::vec2(10.0f, 10.0f), 128 | // glm::vec4(1.0, 0.0, 0.0, 1.0)); 129 | 130 | // if (delta == 0) delta = 1; 131 | // 132 | // std::stringstream fps; 133 | // fps << "frametime in ms: " << delta << " fps: " << 1000 / delta; 134 | // font.PrintString(fps.str(), glm::vec2(10.0f, 135 | // (float)screen_height_-20.0f), glm::vec4(1.0, 1.0, 1.0, 1.0)); 136 | } 137 | 138 | void Renderer::Blend(bool enable) { 139 | if (blend_enabled_ != enable) { 140 | blend_enabled_ = enable; 141 | if (enable) { 142 | glEnable(GL_BLEND); 143 | } else { 144 | glDisable(GL_BLEND); 145 | } 146 | } 147 | } 148 | 149 | void Renderer::TexEnvMode(unsigned int texture_unit, unsigned int new_mode) { 150 | if (tex_env_mode_[texture_unit] != new_mode) { 151 | tex_env_mode_[texture_unit] = new_mode; 152 | glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, new_mode); 153 | } 154 | } 155 | 156 | void Renderer::BindTexture(unsigned int texture_unit, 157 | unsigned int new_texture) { 158 | if (texture_[texture_unit] != new_texture) { 159 | texture_[texture_unit] = new_texture; 160 | ActiveTexture(GL_TEXTURE0 + texture_unit); 161 | glBindTexture(GL_TEXTURE_2D, new_texture); 162 | } 163 | } 164 | 165 | void Renderer::BlendFunc(unsigned int new_blend_func0, 166 | unsigned int new_blend_func1) { 167 | if (blend_func_[0] != new_blend_func0 || blend_func_[1] != new_blend_func1) { 168 | blend_func_[0] = new_blend_func0; 169 | blend_func_[1] = new_blend_func1; 170 | glBlendFunc(new_blend_func0, new_blend_func1); 171 | } 172 | } 173 | 174 | void Renderer::AlphaFunc(unsigned int new_alpha_func, 175 | unsigned int new_alpha_val) { 176 | if (alpha_func_ != new_alpha_func) { 177 | alpha_func_ = new_alpha_func; 178 | glAlphaFunc(new_alpha_func, new_alpha_val); 179 | } 180 | } 181 | 182 | void Renderer::ActiveTexture(unsigned int new_active_texture) { 183 | if (active_texture_ != new_active_texture) { 184 | active_texture_ = new_active_texture; 185 | glActiveTexture(new_active_texture); 186 | } 187 | } 188 | 189 | void Renderer::FinishShader() { 190 | // Blend(false); 191 | } 192 | 193 | void Renderer::SetupShader(Shader *shader, int lm_index) { 194 | // the light map could change even though we have the same shaders 195 | // check and set new lightmap, leave everthing else the same 196 | if (shader == current_shader_ && lm_index != -1) { 197 | if (lm_index == current_lightmap_) { 198 | return; 199 | } 200 | 201 | if (shader->lightmap_stage_ == -1) { 202 | return; 203 | } 204 | 205 | current_lightmap_ = lm_index; 206 | 207 | BindTexture(shader->lightmap_stage_, textureLoader::GetLightmap(lm_index)); 208 | ++num_skipped_shaders_; 209 | return; 210 | } 211 | 212 | // THIS ONLY DISABLES BLENDING BUT WE WANT TO ALWAYS BLEND.. maybe 213 | // if (current_shader_ != 0) 214 | //{ 215 | // FinishShader(*current_shader_); 216 | //} 217 | 218 | // current_shader_ = shader; 219 | 220 | // JUST ENABLE BLENDING ALL THE TIME AND BLEND NON TRANSLUCENT TEXTURES WITH 221 | // ONE ZERO 222 | // only enable blending if stage 0 wants to blend with background 223 | // shaders can only blend with textures 224 | // if (i == 0 && stage.blendfunc[0] == GL_ONE && stage.blendfunc[1] == GL_ONE) 225 | //{ 226 | 227 | if (shader->q3_shader_.stages_.size() > 0) { 228 | Blend(true); 229 | BlendFunc(shader->q3_shader_.stages_[0].blendfunc[0], 230 | shader->q3_shader_.stages_[0].blendfunc[1]); 231 | } else { 232 | Blend(false); 233 | } 234 | 235 | // BlendFunc(GL_ONE, GL_ZERO); 236 | //} 237 | 238 | for (unsigned int i = 0; i < shader->q3_shader_.stages_.size(); ++i) { 239 | // maybe put lightmap directly into stage so we dont need this if 240 | // seems we can optimize it by first only checking lm_index 241 | if (i == shader->lightmap_stage_ && lm_index != -1) { 242 | BindTexture(i, textureLoader::GetLightmap(lm_index)); 243 | current_lightmap_ = lm_index; 244 | } else { 245 | BindTexture(i, shader->texture_id_[i]); 246 | } 247 | } 248 | } 249 | 250 | void Renderer::RenderFace(bsp_face *face) { 251 | const bsp_face ¤t_face = *face; 252 | 253 | Shader *shader = shaderLoader::GetShader(current_face.texture); 254 | 255 | if (shader->q3_shader_.stages_.size() == 256 | 0) // skip no shader / no draw. should be sorted out before. 257 | { 258 | return; 259 | } 260 | 261 | // does everything in here need to be done every time? move into the 262 | // conditional below? 263 | SetupShader(shader, current_face.lm_index); 264 | 265 | if (current_shader_ != shader) { 266 | glUseProgram(shader->shader_); 267 | current_shader_ = shader; 268 | } 269 | 270 | if (shader->time_idx_ != -1) { 271 | glUniform1f(shader->time_idx_, time_); 272 | } 273 | 274 | glUniformMatrix4fv(shader->projection_idx_, 1, false, 275 | glm::value_ptr(projectionmatrix_)); 276 | glUniformMatrix4fv(shader->model_idx_, 1, false, 277 | glm::value_ptr(modelmatrix_)); 278 | 279 | if (current_face.type == POLYGON || current_face.type == MESH) { 280 | // RenderPolygon(face); 281 | } else if (current_face.type == PATCH) { 282 | RenderPatch(face); 283 | } else if (current_face.type == BILLBOARD) { 284 | // RenderBillboard(); 285 | } 286 | } 287 | 288 | void Renderer::RenderModel() { 289 | glBindBuffer(GL_ARRAY_BUFFER, lower->vboId_); 290 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lower->iboId_); 291 | 292 | Shader *shader = lower->shader_; 293 | 294 | SetupShader(shader, -1); 295 | 296 | glUseProgram(shader->shader_); 297 | current_shader_ = shader; 298 | 299 | modelmatrix_ = glm::translate( 300 | modelmatrix_, 301 | glm::vec3(world.enemy_->position_)); // glm::vec3(-589.0f, -275.0f, 25.0f) 302 | 303 | float temp_pitch = world.enemy_->pitch_; 304 | world.enemy_->pitch_ = 0.0f; 305 | GetCameraMatrixFromEntity(*world.enemy_); 306 | world.enemy_->pitch_ = temp_pitch; 307 | 308 | glm::mat4 test = glm::mat4( 309 | world.enemy_->look_.x, world.enemy_->look_.y, world.enemy_->look_.z, 0.0f, 310 | world.enemy_->right_.x, world.enemy_->right_.y, world.enemy_->right_.z, 311 | 0.0f, world.enemy_->up_.x, world.enemy_->up_.y, world.enemy_->up_.z, 0.0f, 312 | 1, 1, 1, 1.0f); 313 | 314 | glm::mat4 modelmatrix_legs = modelmatrix_ * test; 315 | 316 | int lower_frame = world.enemy_->lower_frame; 317 | 318 | glUniformMatrix4fv(shader->projection_idx_, 1, false, 319 | glm::value_ptr(projectionmatrix_)); 320 | glUniformMatrix4fv(shader->model_idx_, 1, false, 321 | glm::value_ptr(modelmatrix_legs)); 322 | 323 | glVertexAttribPointer( 324 | shader->position_idx_, 3, GL_FLOAT, GL_FALSE, sizeof(my_vertex), 325 | reinterpret_cast(lower_frame * lower->surfaces_[0].num_verts * 326 | sizeof(my_vertex))); 327 | 328 | glVertexAttribPointer( 329 | shader->tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, sizeof(my_vertex), 330 | reinterpret_cast(lower_frame * lower->surfaces_[0].num_verts * 331 | sizeof(my_vertex) + 332 | sizeof(glm::vec3) * 2)); 333 | 334 | glDrawElements(GL_TRIANGLES, lower->surfaces_[0].num_triangles * 3, 335 | GL_UNSIGNED_INT, reinterpret_cast(0)); 336 | 337 | GetCameraMatrixFromEntity(*world.enemy_); 338 | 339 | test = glm::mat4(world.enemy_->look_.x, world.enemy_->look_.y, 340 | world.enemy_->look_.z, 0.0f, world.enemy_->right_.x, 341 | world.enemy_->right_.y, world.enemy_->right_.z, 0.0f, 342 | world.enemy_->up_.x, world.enemy_->up_.y, 343 | world.enemy_->up_.z, 0.0f, 1, 1, 1, 1.0f); 344 | 345 | modelmatrix_ = modelmatrix_ * test; 346 | 347 | int ofs_tag = lower_frame * (lower->header_.num_tags); 348 | glm::mat4 rotationMatrix1( 349 | lower->tags_[0 + ofs_tag].axis[0].x, lower->tags_[0 + ofs_tag].axis[0].y, 350 | lower->tags_[0 + ofs_tag].axis[0].z, 0, 351 | lower->tags_[0 + ofs_tag].axis[1].x, lower->tags_[0 + ofs_tag].axis[1].y, 352 | lower->tags_[0 + ofs_tag].axis[1].z, 0, 353 | lower->tags_[0 + ofs_tag].axis[2].x, lower->tags_[0 + ofs_tag].axis[2].y, 354 | lower->tags_[0 + ofs_tag].axis[2].z, 0, 355 | lower->tags_[0 + ofs_tag].origin.x, lower->tags_[0 + ofs_tag].origin.y, 356 | lower->tags_[0 + ofs_tag].origin.z, 1); 357 | 358 | modelmatrix_ = modelmatrix_ * rotationMatrix1; 359 | 360 | glBindBuffer(GL_ARRAY_BUFFER, upper->vboId_); 361 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, upper->iboId_); 362 | 363 | int upper_frame = world.enemy_->upper_frame; 364 | 365 | glUniformMatrix4fv(shader->projection_idx_, 1, false, 366 | glm::value_ptr(projectionmatrix_)); 367 | glUniformMatrix4fv(shader->model_idx_, 1, false, 368 | glm::value_ptr(modelmatrix_)); 369 | 370 | glVertexAttribPointer( 371 | shader->position_idx_, 3, GL_FLOAT, GL_FALSE, sizeof(my_vertex), 372 | reinterpret_cast(upper_frame * upper->surfaces_[0].num_verts * 373 | sizeof(my_vertex))); 374 | 375 | glVertexAttribPointer( 376 | shader->tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, sizeof(my_vertex), 377 | reinterpret_cast(upper_frame * upper->surfaces_[0].num_verts * 378 | sizeof(my_vertex) + 379 | sizeof(glm::vec3) * 2)); 380 | 381 | glDrawElements(GL_TRIANGLES, upper->surfaces_[0].num_triangles * 3, 382 | GL_UNSIGNED_INT, reinterpret_cast(0)); 383 | 384 | ofs_tag = upper_frame * (upper->header_.num_tags); 385 | glm::mat4 rotationMatrix0( 386 | upper->tags_[0 + ofs_tag].axis[0].x, upper->tags_[0 + ofs_tag].axis[0].y, 387 | upper->tags_[0 + ofs_tag].axis[0].z, 0, 388 | upper->tags_[0 + ofs_tag].axis[1].x, upper->tags_[0 + ofs_tag].axis[1].y, 389 | upper->tags_[0 + ofs_tag].axis[1].z, 0, 390 | upper->tags_[0 + ofs_tag].axis[2].x, upper->tags_[0 + ofs_tag].axis[2].y, 391 | upper->tags_[0 + ofs_tag].axis[2].z, 0, 392 | upper->tags_[0 + ofs_tag].origin.x, upper->tags_[0 + ofs_tag].origin.y, 393 | upper->tags_[0 + ofs_tag].origin.z, 1); 394 | 395 | modelmatrix_ = modelmatrix_ * rotationMatrix0; 396 | 397 | glBindBuffer(GL_ARRAY_BUFFER, head->vboId_); 398 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, head->iboId_); 399 | 400 | int head_frame = 0; 401 | 402 | glUniformMatrix4fv(shader->projection_idx_, 1, false, 403 | glm::value_ptr(projectionmatrix_)); 404 | glUniformMatrix4fv(shader->model_idx_, 1, false, 405 | glm::value_ptr(modelmatrix_)); 406 | 407 | glVertexAttribPointer( 408 | shader->position_idx_, 3, GL_FLOAT, GL_FALSE, sizeof(my_vertex), 409 | reinterpret_cast(head_frame * head->surfaces_[0].num_verts * 410 | sizeof(my_vertex))); 411 | 412 | glVertexAttribPointer( 413 | shader->tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, sizeof(my_vertex), 414 | reinterpret_cast(head_frame * head->surfaces_[0].num_verts * 415 | sizeof(my_vertex) + 416 | sizeof(glm::vec3) * 2)); 417 | 418 | glDrawElements(GL_TRIANGLES, head->surfaces_[0].num_triangles * 3, 419 | GL_UNSIGNED_INT, reinterpret_cast(0)); 420 | } 421 | 422 | void Renderer::RenderPolygon(bsp_face *face) { 423 | const bsp_face ¤t_face = *face; 424 | const int offset = current_face.vertex; 425 | 426 | if (offset >= world.map_->num_vertexes_) 427 | return; 428 | 429 | Shader &shader = *current_shader_; 430 | 431 | glVertexAttribPointer(shader.position_idx_, 3, GL_FLOAT, GL_FALSE, 432 | sizeof(bsp_vertex), 433 | reinterpret_cast(offset * sizeof(bsp_vertex))); 434 | 435 | glVertexAttribPointer(shader.tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, 436 | sizeof(bsp_vertex), 437 | reinterpret_cast(offset * sizeof(bsp_vertex) + 438 | sizeof(glm::vec3))); 439 | 440 | glVertexAttribPointer( 441 | shader.lm_coord_idx_, 2, GL_FLOAT, GL_FALSE, sizeof(bsp_vertex), 442 | reinterpret_cast(offset * sizeof(bsp_vertex) + sizeof(glm::vec3) + 443 | sizeof(glm::vec2))); 444 | 445 | glVertexAttribPointer( 446 | shader.color_idx_, 4, GL_BYTE, GL_FALSE, sizeof(bsp_vertex), 447 | reinterpret_cast(offset * sizeof(bsp_vertex) + sizeof(glm::vec3) + 448 | sizeof(glm::vec2) + sizeof(glm::vec2) + 449 | sizeof(glm::vec3))); 450 | 451 | glDrawElements( 452 | GL_TRIANGLES, current_face.num_meshverts, GL_UNSIGNED_INT, 453 | reinterpret_cast(current_face.meshvert * sizeof(bsp_meshvert))); 454 | } 455 | 456 | void Renderer::RenderPatch(bsp_face *face) { 457 | Shader &shader = *current_shader_; 458 | auto offset = face->vertex; 459 | 460 | glUniform1ui(shader.patchWidth_, face->size[0]); 461 | glUniform1ui(shader.patchHeight_, face->size[1]); 462 | 463 | if ((face->size[0] == 15 || face->size[1] == 15) || 464 | (face->size[0] == 9 && face->size[1] == 5)) { 465 | logger::Debug("patch size: %d %d %d", face->size[0], face->size[1], 466 | face->num_vertices); 467 | return; 468 | } 469 | 470 | if (face->size[0] * face->size[1] != face->num_vertices) { 471 | logger::Debug("VERY BAD VERY BAD"); 472 | return; 473 | } 474 | 475 | glVertexAttribPointer(shader.position_idx_, 3, GL_FLOAT, GL_FALSE, 476 | sizeof(bsp_vertex), 477 | reinterpret_cast(offset * sizeof(bsp_vertex))); 478 | 479 | glVertexAttribPointer(shader.tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, 480 | sizeof(bsp_vertex), 481 | reinterpret_cast(offset * sizeof(bsp_vertex) + 482 | sizeof(glm::vec3))); 483 | 484 | glVertexAttribPointer( 485 | shader.lm_coord_idx_, 2, GL_FLOAT, GL_FALSE, sizeof(bsp_vertex), 486 | reinterpret_cast(offset * sizeof(bsp_vertex) + sizeof(glm::vec3) + 487 | sizeof(glm::vec2))); 488 | 489 | glVertexAttribPointer( 490 | shader.color_idx_, 4, GL_BYTE, GL_FALSE, sizeof(bsp_vertex), 491 | reinterpret_cast(offset * sizeof(bsp_vertex) + sizeof(glm::vec3) + 492 | sizeof(glm::vec2) + sizeof(glm::vec2) + 493 | sizeof(glm::vec3))); 494 | 495 | float subdivisions[] = {10.0f, 10.0f, 10.0f, 10.0f}; 496 | glPatchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, subdivisions); 497 | glPatchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, subdivisions); 498 | glPatchParameteri(GL_PATCH_VERTICES, face->num_vertices); 499 | glDrawArrays(GL_PATCHES, 0, face->num_vertices); 500 | 501 | /* 502 | std::vector patches = world.map_->patches_[face]; 503 | 504 | for (int i = 0; i < patches.size(); ++i) { 505 | const bezier *b = patches[i]; 506 | 507 | glVertexAttribPointer(current_shader_->position_idx_, 3, GL_FLOAT, 508 | GL_FALSE, 509 | sizeof(bsp_vertex), 510 | reinterpret_cast(b->m_vertex_offset)); 511 | 512 | glVertexAttribPointer( 513 | current_shader_->tex_coord_idx_, 2, GL_FLOAT, GL_FALSE, 514 | sizeof(bsp_vertex), 515 | reinterpret_cast(b->m_vertex_offset + sizeof(glm::vec3))); 516 | 517 | glVertexAttribPointer(current_shader_->lm_coord_idx_, 2, GL_FLOAT, 518 | GL_FALSE, 519 | sizeof(bsp_vertex), 520 | reinterpret_cast(b->m_vertex_offset + 521 | sizeof(glm::vec3) + 522 | sizeof(glm::vec2))); 523 | 524 | glVertexAttribPointer( 525 | current_shader_->color_idx_, 4, GL_BYTE, GL_FALSE, 526 | sizeof(bsp_vertex), 527 | reinterpret_cast(b->m_vertex_offset + sizeof(float) * 10)); 528 | 529 | // double work for each bezier, doesnt seem to be needed.. or maybe it 530 | does 531 | // because of vertex colors! then 0 shouldnt be there 532 | // prepare_shader(shader, 0, current_face.lm_index); 533 | 534 | unsigned int count[10] = {22, 22, 22, 22, 22, 22, 22, 22, 22, 22}; 535 | GLvoid *indices[10]; 536 | for (int k = 0; k < 10; k++) { 537 | indices[k] = 538 | (GLvoid *)(b->m_index_offset + sizeof(unsigned int) * k * 11 * 2); 539 | } 540 | 541 | glMultiDrawElements(GL_TRIANGLE_STRIP, (const GLsizei *)count, 542 | GL_UNSIGNED_INT, (const GLvoid **)indices, 10); 543 | } 544 | 545 | */ 546 | } 547 | 548 | void Renderer::RenderBillboard() {} 549 | -------------------------------------------------------------------------------- /src/World.cpp: -------------------------------------------------------------------------------- 1 | #include "world.hpp" 2 | 3 | #include 4 | 5 | #include "bsp.hpp" 6 | #include "entity.hpp" 7 | 8 | World::World(void) 9 | { 10 | } 11 | 12 | 13 | World::~World(void) 14 | { 15 | } 16 | 17 | void World::LoadLevel(std::string name) 18 | { 19 | std::string path = "maps/" + name + ".bsp"; 20 | map_ = new Bsp(path); 21 | } 22 | 23 | void World::Update(unsigned int time) 24 | { 25 | for (Entity* player : players_) 26 | { 27 | player->Update(time); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/bezier.cpp: -------------------------------------------------------------------------------- 1 | #include "bezier.hpp" 2 | 3 | bezier::bezier(void) 4 | { 5 | m_vertexes = nullptr; 6 | m_indexes = nullptr; 7 | } 8 | 9 | bezier::~bezier(void) 10 | { 11 | if (m_vertexes) 12 | { 13 | delete[] m_vertexes; 14 | } 15 | 16 | if (m_indexes) 17 | { 18 | delete[] m_indexes; 19 | } 20 | } 21 | 22 | 23 | // TODO: for adding color interpolation maybe check for t < 0.5 and use 1+2 or 2+3 vertex accordingly 24 | bsp_vertex bezier::calculate_quadratic_bezier(float t, const bsp_vertex* control_vertexes) 25 | { 26 | // http://de.wikipedia.org/wiki/B%C3%A9zierkurve#Kubische_B.C3.A9zierkurven_.28n.3D3.29 27 | // quadratic bezier curve (n=2) 28 | 29 | return (control_vertexes[0])*(1-t)*(1-t)+(control_vertexes[1])*2*t*(1-t)+(control_vertexes[2])*t*t; 30 | } 31 | 32 | void bezier::tessellate(int subdivisions) 33 | { 34 | bsp_vertex temp[3]; 35 | int subdivisions1 = subdivisions+1; 36 | 37 | if (m_vertexes != NULL) delete [] m_vertexes; 38 | m_vertexes = new bsp_vertex[subdivisions1*subdivisions1]; 39 | 40 | for (int i = 0; i <= subdivisions; ++i) 41 | { 42 | float l = (float)i/subdivisions; 43 | 44 | for (int j = 0; j < 3; ++j) 45 | { 46 | int k = 3 * j; 47 | temp[j] = calculate_quadratic_bezier(l, &(m_control_vertexes[k])); 48 | } 49 | 50 | int col = 0; 51 | for(int j = 0; j <= subdivisions; ++j) 52 | { 53 | float a = (float)j / subdivisions; 54 | 55 | m_vertexes[i * subdivisions1 + j] = calculate_quadratic_bezier(a, temp); 56 | m_vertexes[i * subdivisions1 + j].color[0] = 0xff; 57 | m_vertexes[i * subdivisions1 + j].color[1] = 0xff; 58 | m_vertexes[i * subdivisions1 + j].color[2] = 0xff; 59 | m_vertexes[i * subdivisions1 + j].color[3] = 0xff; 60 | } 61 | } 62 | 63 | if (m_indexes != NULL) delete [] m_indexes; 64 | m_indexes = new unsigned int[subdivisions * subdivisions1 * 2]; 65 | 66 | // maybe use degenerated triangle strips to merge 67 | for (int row = 0; row < subdivisions; ++row) 68 | { 69 | for(int col = 0; col <= subdivisions; ++col) { 70 | int g = (row * (subdivisions1) + col) * 2 + 1; 71 | int h = (row * (subdivisions1) + col) * 2; 72 | m_indexes[g] = row * subdivisions1 + col; 73 | m_indexes[h] = (row + 1) * subdivisions1 + col; 74 | } 75 | } 76 | 77 | for (int row = 0; row < subdivisions; ++row) 78 | { 79 | m_tri_per_row[row] = 2 * subdivisions1; 80 | m_row_indexes[row] = &(m_indexes[row * 2 * subdivisions1]); 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/bezier.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BEZIER_HPP_ 2 | #define BEZIER_HPP_ 3 | 4 | #include "bsp.hpp" 5 | 6 | class bezier { 7 | public: 8 | bezier(); 9 | ~bezier(); 10 | 11 | bsp_vertex calculate_quadratic_bezier(float t, const bsp_vertex* control_vertexes); 12 | void tessellate(int subdivisions); 13 | 14 | bsp_vertex* m_vertexes; 15 | bsp_vertex m_control_vertexes[9]; 16 | unsigned int* m_indexes; 17 | unsigned int* m_row_indexes[10]; 18 | unsigned int m_tri_per_row[10]; 19 | 20 | unsigned int m_vertex_offset; 21 | unsigned int m_index_offset; 22 | }; 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /src/bot_input_component.cpp: -------------------------------------------------------------------------------- 1 | #include "bot_input_component.hpp" 2 | 3 | 4 | BotInputComponent::BotInputComponent(void) 5 | { 6 | } 7 | 8 | 9 | BotInputComponent::~BotInputComponent(void) 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/bot_input_component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOT_INPUT_COMPONENT_HPP_ 2 | #define BOT_INPUT_COMPONENT_HPP_ 3 | 4 | #include "component.hpp" 5 | 6 | class BotInputComponent : public Component 7 | { 8 | public: 9 | BotInputComponent(void); 10 | ~BotInputComponent(void); 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/bsp.cpp: -------------------------------------------------------------------------------- 1 | #include "bsp.hpp" 2 | 3 | #include 4 | 5 | #include "bezier.hpp" 6 | #include "frustum.hpp" 7 | #include "logger.hpp" 8 | #include "q3_shader.hpp" 9 | #include "shader.hpp" 10 | #include "shader_loader.hpp" 11 | #include "texture_loader.hpp" 12 | 13 | Bsp::Bsp(void) {} 14 | 15 | Bsp::~Bsp(void) { 16 | // TODO: Stop leaking all that memory ;) 17 | /* 18 | SAFE_DELETE_ARRAY(entities_->ents); 19 | SAFE_DELETE(entities_); 20 | SAFE_DELETE_ARRAY(textures_); 21 | SAFE_DELETE_ARRAY(planes_); 22 | SAFE_DELETE_ARRAY(nodes_); 23 | SAFE_DELETE_ARRAY(planes_); 24 | SAFE_DELETE_ARRAY(leafs_); 25 | SAFE_DELETE_ARRAY(leaffaces_); 26 | SAFE_DELETE_ARRAY(leafbrushes_); 27 | SAFE_DELETE_ARRAY(models_); 28 | SAFE_DELETE_ARRAY(brushes_); 29 | SAFE_DELETE_ARRAY(brushsides_); 30 | SAFE_DELETE_ARRAY(vertexes_); 31 | SAFE_DELETE_ARRAY(meshverts_); 32 | SAFE_DELETE_ARRAY(effects_); 33 | SAFE_DELETE_ARRAY(faces_); 34 | SAFE_DELETE_ARRAY(lightmaps_); 35 | SAFE_DELETE_ARRAY(lightvols_); 36 | SAFE_DELETE_ARRAY(visdata_->vecs); 37 | SAFE_DELETE(visdata_); 38 | 39 | { 40 | std::map >::iterator it; 41 | for (it = patches_.begin(); it != patches_.end(); ++it) 42 | for (unsigned int i = 0; i < (*it).second.size(); ++i) 43 | SAFE_DELETE(((*it).second)[i]); 44 | } 45 | */ 46 | } 47 | 48 | Bsp::Bsp(std::string filename) { 49 | std::ifstream fin(filename.c_str(), std::ios::binary); 50 | 51 | fin.read((char *)&header_, sizeof(bsp_header)); 52 | 53 | entities_ = new bsp_entities; 54 | entities_->ents = new char[header_.direntries[LUMP_ENTITIES].length]; 55 | 56 | num_textures_ = 57 | header_.direntries[LUMP_TEXTURES].length / sizeof(bsp_texture); 58 | num_planes_ = header_.direntries[LUMP_PLANES].length / sizeof(bsp_plane); 59 | num_nodes_ = header_.direntries[LUMP_NODES].length / sizeof(bsp_node); 60 | num_planes_ = header_.direntries[LUMP_PLANES].length / sizeof(bsp_plane); 61 | num_leafs_ = header_.direntries[LUMP_LEAFS].length / sizeof(bsp_leaf); 62 | num_leaffaces_ = 63 | header_.direntries[LUMP_LEAFFACES].length / sizeof(bsp_leafface); 64 | num_leafbrushes_ = 65 | header_.direntries[LUMP_LEAFBRUSHES].length / sizeof(bsp_leafbrush); 66 | num_models_ = header_.direntries[LUMP_MODELS].length / sizeof(bsp_model); 67 | num_brushes_ = header_.direntries[LUMP_BRUSHES].length / sizeof(bsp_brush); 68 | num_brushsides_ = 69 | header_.direntries[LUMP_BRUSHSIDES].length / sizeof(bsp_brushside); 70 | num_vertexes_ = header_.direntries[LUMP_VERTEXES].length / sizeof(bsp_vertex); 71 | num_meshverts_ = 72 | header_.direntries[LUMP_MESHVERTS].length / sizeof(bsp_meshvert); 73 | num_effects_ = header_.direntries[LUMP_EFFECTS].length / sizeof(bsp_effect); 74 | num_faces_ = header_.direntries[LUMP_FACES].length / sizeof(bsp_face); 75 | num_lightmaps_ = 76 | header_.direntries[LUMP_LIGHTMAPS].length / sizeof(bsp_lightmap); 77 | num_lightvols_ = 78 | header_.direntries[LUMP_LIGHTVOLS].length / sizeof(bsp_lightvol); 79 | 80 | textures_ = new bsp_texture[num_textures_]; 81 | nodes_ = new bsp_node[num_nodes_]; 82 | planes_ = new bsp_plane[num_planes_]; 83 | leafs_ = new bsp_leaf[num_leafs_]; 84 | leaffaces_ = new bsp_leafface[num_leaffaces_]; 85 | leafbrushes_ = new bsp_leafbrush[num_leafbrushes_]; 86 | models_ = new bsp_model[num_models_]; 87 | brushes_ = new bsp_brush[num_brushes_]; 88 | brushsides_ = new bsp_brushside[num_brushsides_]; 89 | vertexes_ = new bsp_vertex[num_vertexes_]; 90 | meshverts_ = new bsp_meshvert[num_meshverts_]; 91 | effects_ = new bsp_effect[num_effects_]; 92 | faces_ = new bsp_face[num_faces_]; 93 | lightmaps_ = new bsp_lightmap[num_lightmaps_]; 94 | lightvols_ = new bsp_lightvol[num_lightvols_]; 95 | visdata_ = new bsp_visdata; 96 | 97 | fin.seekg(header_.direntries[LUMP_ENTITIES].offset); 98 | fin.read((char *)entities_->ents, header_.direntries[LUMP_ENTITIES].length); 99 | 100 | fin.seekg(header_.direntries[LUMP_TEXTURES].offset); 101 | fin.read((char *)textures_, header_.direntries[LUMP_TEXTURES].length); 102 | 103 | fin.seekg(header_.direntries[LUMP_PLANES].offset); 104 | fin.read((char *)planes_, header_.direntries[LUMP_PLANES].length); 105 | 106 | fin.seekg(header_.direntries[LUMP_NODES].offset); 107 | fin.read((char *)nodes_, header_.direntries[LUMP_NODES].length); 108 | 109 | fin.seekg(header_.direntries[LUMP_LEAFS].offset); 110 | fin.read((char *)leafs_, header_.direntries[LUMP_LEAFS].length); 111 | 112 | fin.seekg(header_.direntries[LUMP_LEAFFACES].offset); 113 | fin.read((char *)leaffaces_, header_.direntries[LUMP_LEAFFACES].length); 114 | 115 | fin.seekg(header_.direntries[LUMP_LEAFBRUSHES].offset); 116 | fin.read((char *)leafbrushes_, header_.direntries[LUMP_LEAFBRUSHES].length); 117 | 118 | fin.seekg(header_.direntries[LUMP_MODELS].offset); 119 | fin.read((char *)models_, header_.direntries[LUMP_MODELS].length); 120 | 121 | fin.seekg(header_.direntries[LUMP_BRUSHES].offset); 122 | fin.read((char *)brushes_, header_.direntries[LUMP_BRUSHES].length); 123 | 124 | fin.seekg(header_.direntries[LUMP_BRUSHSIDES].offset); 125 | fin.read((char *)brushsides_, header_.direntries[LUMP_BRUSHSIDES].length); 126 | 127 | fin.seekg(header_.direntries[LUMP_VERTEXES].offset); 128 | fin.read((char *)vertexes_, header_.direntries[LUMP_VERTEXES].length); 129 | 130 | fin.seekg(header_.direntries[LUMP_MESHVERTS].offset); 131 | fin.read((char *)meshverts_, header_.direntries[LUMP_MESHVERTS].length); 132 | 133 | fin.seekg(header_.direntries[LUMP_EFFECTS].offset); 134 | fin.read((char *)effects_, header_.direntries[LUMP_EFFECTS].length); 135 | 136 | fin.seekg(header_.direntries[LUMP_FACES].offset); 137 | fin.read((char *)faces_, header_.direntries[LUMP_FACES].length); 138 | 139 | fin.seekg(header_.direntries[LUMP_LIGHTMAPS].offset); 140 | fin.read((char *)lightmaps_, header_.direntries[LUMP_LIGHTMAPS].length); 141 | 142 | fin.seekg(header_.direntries[LUMP_LIGHTVOLS].offset); 143 | fin.read((char *)lightvols_, header_.direntries[LUMP_LIGHTVOLS].length); 144 | 145 | fin.seekg(header_.direntries[LUMP_VISDATA].offset); 146 | fin.read((char *)visdata_, 2 * sizeof(int)); 147 | visdata_->vecs = new unsigned char[visdata_->num_vecs * visdata_->size_vecs]; 148 | fin.read((char *)visdata_->vecs, 149 | sizeof(unsigned char) * visdata_->num_vecs * visdata_->size_vecs); 150 | 151 | fin.close(); 152 | // prepare patch data + tesselate 153 | for (int i = 0; i < num_faces_; ++i) { 154 | if (faces_[i].type == PATCH) { 155 | bsp_face *face = &(faces_[i]); 156 | 157 | int width = faces_[i].size[0]; 158 | int height = faces_[i].size[1]; 159 | int widthCount = (width - 1) / 2; 160 | int heightCount = (height - 1) / 2; 161 | 162 | patches_[face].resize(widthCount * heightCount); 163 | for (int j = 0; j < widthCount * heightCount; ++j) { 164 | patches_[face][j] = new bezier(); 165 | } 166 | 167 | for (int y = 0; y < heightCount; y++) { 168 | for (int x = 0; x < widthCount; x++) { 169 | for (int row = 0; row < 3; row++) { 170 | for (int col = 0; col < 3; col++) { 171 | patches_[face][y * widthCount + x] 172 | ->m_control_vertexes[row * 3 + col] = 173 | vertexes_[faces_[i].vertex + (y * 2 * width + x * 2) + 174 | row * width + col]; 175 | } 176 | } 177 | patches_[face][y * widthCount + x]->tessellate(10); 178 | } 179 | } 180 | } 181 | } 182 | 183 | // logger::Log(logger::DEBUG, "Checking if all needed textures are loaded"); 184 | 185 | // HEEEEEEEEEEEEEEEEEEEREEEEEEEEE 186 | // TODO 187 | // make call to shaders.getShader() with texture name. If not already loaded 188 | // will create default texture. 189 | // map texture id to an array containing all the shaders. 190 | // map textures refer to shaders. 191 | // so all shaders get actually managed by the renderer. 192 | // just pass vbo ids to renderer (later, let renderer create vbos) 193 | // beziers dont need to be loaded in a loop, just memcopy the control 194 | // vertices. 195 | // or just use tesselation shader and don't create vertices 196 | // most references in message and component code need to be pointers because 197 | // of object lifetime 198 | // change string ids to int ids for speedup 199 | // camera system still fishy... write down coord systems step by step 200 | // instead of removing the lightmap stage, make 2 shaders one for lightmapped 201 | // faces and one without lightmap 202 | 203 | for (int i = 0; i < num_textures_; ++i) { 204 | // request each shader 1 time so they are mapped to the right id.. change 205 | // this.. 206 | // logger::Log(logger::DEBUG, "Checking texture: %s, %i, %i", 207 | // textures_[i].name, textures_[i].flags, textures_[i].contents); 208 | shaderLoader::GetShader(textures_[i].name); 209 | } 210 | 211 | // logger::Log(logger::DEBUG, "Finished loading all needed textures"); 212 | 213 | // remove lightmap stage for faces without lightmap. 214 | for (int i = 0; i < num_faces_; ++i) { 215 | if (faces_[i].lm_index >= 0) { 216 | continue; 217 | } 218 | 219 | Shader *shader = shaderLoader::GetShader(faces_[i].texture); 220 | 221 | for (int j = 0; j < shader->q3_shader_.stages_.size(); ++j) { 222 | if (shader->q3_shader_.stages_[j].map == "$lightmap") { 223 | shader->q3_shader_.stages_.pop_back(); 224 | // logger::Log(logger::ERROR, "Removed lightmap from shader %s", 225 | // textures_[faces_[i].texture].name); 226 | } 227 | } 228 | } 229 | 230 | shaderLoader::CompileAllShaders(); 231 | 232 | // logger::Log(logger::DEBUG, "Loading lightmaps..."); 233 | load_lightmaps(); 234 | // logger::Log(logger::DEBUG, "Finished loading lightmaps"); 235 | 236 | // calculate correct buffer size 237 | // all vertexes + all tesselated beziers 238 | int num_bezier_vertexes = 0; 239 | int num_bezier_indexes = 0; 240 | 241 | std::map>::iterator it; 242 | 243 | for (it = patches_.begin(); it != patches_.end(); ++it) { 244 | num_bezier_vertexes += 11 * 11 * it->second.size(); 245 | num_bezier_indexes += 10 * 11 * 2 * it->second.size(); 246 | } 247 | 248 | glGenBuffers(1, &vboId); 249 | glBindBuffer(GL_ARRAY_BUFFER, vboId); 250 | 251 | glBufferData(GL_ARRAY_BUFFER, header_.direntries[LUMP_VERTEXES].length + 252 | num_bezier_vertexes * sizeof(bsp_vertex), 253 | NULL, GL_STATIC_DRAW); 254 | 255 | glBufferSubData(GL_ARRAY_BUFFER, 0, header_.direntries[LUMP_VERTEXES].length, 256 | vertexes_); 257 | 258 | glGenBuffers(1, &iboId); 259 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId); 260 | 261 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, 262 | header_.direntries[LUMP_MESHVERTS].length + 263 | num_bezier_indexes * sizeof(unsigned int), 264 | NULL, GL_STATIC_DRAW); 265 | 266 | glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 267 | header_.direntries[LUMP_MESHVERTS].length, meshverts_); 268 | 269 | int offset_verts = 0; 270 | int offset_idx = 0; 271 | for (it = patches_.begin(); it != patches_.end(); ++it) { 272 | for (unsigned int j = 0; j < it->second.size(); ++j) { 273 | glBufferSubData(GL_ARRAY_BUFFER, 274 | header_.direntries[LUMP_VERTEXES].length + offset_verts, 275 | 11 * 11 * sizeof(bsp_vertex), it->second[j]->m_vertexes); 276 | 277 | glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 278 | header_.direntries[LUMP_MESHVERTS].length + offset_idx, 279 | 10 * 11 * 2 * sizeof(unsigned int), 280 | it->second[j]->m_indexes); 281 | 282 | it->second[j]->m_vertex_offset = 283 | header_.direntries[LUMP_VERTEXES].length + offset_verts; 284 | it->second[j]->m_index_offset = 285 | header_.direntries[LUMP_MESHVERTS].length + offset_idx; 286 | 287 | offset_verts += sizeof(bsp_vertex) * 11 * 11; 288 | offset_idx += sizeof(unsigned int) * 10 * 11 * 2; 289 | } 290 | } 291 | } 292 | 293 | void Bsp::load_lightmaps() { 294 | for (int i = 0; i < num_lightmaps_; ++i) { 295 | textureLoader::LoadLightmap(lightmaps_[i]); 296 | } 297 | } 298 | 299 | int Bsp::FindLeaf(const glm::vec4 &camera_position) { 300 | int index = 0; 301 | 302 | while (index >= 0) { 303 | const bsp_node &node = nodes_[index]; 304 | const bsp_plane &plane = planes_[node.plane]; 305 | 306 | // TODO: multiplicate the plane with our transformation matrices (inverse 307 | // and transpose needed for plane transformation) 308 | // TODO: maybe do this during load time to enhance framerate! 309 | 310 | // Distance from point to a plane 311 | 312 | glm::vec4 pos = camera_position; 313 | 314 | /* if (plane.type < 3) // type < 3 -> axial plane 315 | { 316 | const float distance = pos[plane->type] - plane.distance; 317 | } 318 | else 319 | */ 320 | const float distance = 321 | glm::dot(plane.normal, glm::vec3(pos)) - plane.distance; 322 | 323 | if (distance >= 0) { 324 | index = node.front; 325 | } else { 326 | index = node.back; 327 | } 328 | } 329 | 330 | return -index - 1; 331 | } 332 | 333 | bool Bsp::IsClusterVisible(int cluster, int test_cluster) { 334 | if ((visdata_->vecs == NULL) || (cluster < 0)) 335 | return true; 336 | 337 | int i = (cluster * visdata_->size_vecs) + (test_cluster >> 3); 338 | unsigned char visSet = visdata_->vecs[i]; 339 | 340 | if (!(visSet & (1 << (test_cluster & 7)))) 341 | return false; 342 | return true; 343 | } 344 | 345 | bool faceSort(const bsp_face *left, const bsp_face *right) { 346 | if (left->texture == right->texture) { 347 | if (left->lm_index < right->lm_index) 348 | return true; 349 | return false; 350 | } else if (left->texture < right->texture) 351 | return true; 352 | return false; 353 | } 354 | 355 | std::vector 356 | Bsp::ComputeVisibleFaces(const glm::vec4 &camera_position) { 357 | std::bitset<10000> already_visible; 358 | std::vector visible_faces; 359 | 360 | num_cluster_not_visible_ = 0; 361 | num_not_in_frustum_ = 0; 362 | num_skipped_faces_ = 0; 363 | num_skipped_shaders_ = 0; 364 | 365 | int leafindex = FindLeaf(camera_position); 366 | int cluster = leafs_[leafindex].cluster; 367 | 368 | for (int i = num_leafs_ - 1; i >= 0; --i) { 369 | if (!IsClusterVisible(cluster, leafs_[i].cluster)) { 370 | ++num_cluster_not_visible_; 371 | continue; 372 | } 373 | 374 | // glm::vec3 min((float)leafs_[i].mins[0], (float)leafs_[i].mins[1], 375 | // (float)leafs_[i].mins[2]); 376 | // glm::vec3 max((float)leafs_[i].maxs[0], (float)leafs_[i].maxs[1], 377 | // (float)leafs_[i].maxs[2]); 378 | 379 | // if (!g_frustum.box_in_frustum(min, max)) 380 | //{ 381 | // ++num_not_in_frustum_; 382 | // continue; 383 | //} 384 | 385 | for (int j = leafs_[i].leafface + leafs_[i].num_leaffaces - 1; 386 | j >= leafs_[i].leafface; --j) { 387 | int face = leaffaces_[j].face; 388 | if (already_visible.test(face)) { 389 | ++num_skipped_faces_; 390 | continue; 391 | } 392 | already_visible.set(face); 393 | 394 | // if (g_textures[faces[face].texture] == NULL) continue; 395 | // it = shaders.find(textures[faces[face].texture].name); 396 | // if (it->second->translucent) 397 | //{ 398 | // m_translucent_faces.push_back(&(faces[face])); 399 | //} 400 | // else 401 | { visible_faces.push_back(&(faces_[face])); } 402 | } 403 | } 404 | 405 | std::sort(visible_faces.begin(), visible_faces.end(), faceSort); 406 | return visible_faces; 407 | } 408 | 409 | #define EPSILON 0.125f 410 | //#define SURFACE_CLIP_EPSILON (0.125) QUAKE3 411 | 412 | float pm_stopspeed = 100.0f; 413 | float pm_duckScale = 0.25f; 414 | 415 | float pm_accelerate = 10.0f; 416 | float pm_airaccelerate = 1.0f; 417 | 418 | float pm_friction = 6.0f; 419 | float pm_spectatorfriction = 5.0f; 420 | 421 | bool output_starts_out; 422 | bool output_all_solid; 423 | float output_fraction; 424 | glm::vec4 output_plane; 425 | 426 | float trace_radius = 32.0f; 427 | 428 | glm::vec4 real_start; 429 | glm::vec4 real_end; 430 | 431 | float Bsp::trace(glm::vec4 &start, glm::vec4 &end, trace_info &trace) { 432 | // remove all those globals they fuck everything 433 | real_start = start; 434 | real_end = end; 435 | 436 | output_plane.x = 0.0; 437 | output_plane.y = 0.0; 438 | output_plane.z = 0.0; 439 | output_plane.w = 0.0; 440 | 441 | output_starts_out = true; 442 | output_all_solid = false; 443 | output_fraction = 1.0f; 444 | 445 | check_node(0, 0.0f, 1.0f, start, end); 446 | 447 | trace.plane = output_plane; 448 | trace.all_solid = output_all_solid; 449 | trace.starts_out = output_starts_out; 450 | trace.fraction = output_fraction; 451 | 452 | return output_fraction; 453 | 454 | if (output_fraction == 1.0f) { 455 | end = end; 456 | } else { 457 | end = start + output_fraction * (end - start); 458 | } 459 | } 460 | 461 | void Bsp::check_node(int index, float start_fraction, float end_fraction, 462 | glm::vec4 start, glm::vec4 end) { 463 | if (output_fraction <= start_fraction) { 464 | return; // already hit something nearer 465 | } 466 | 467 | if (index < 0) { 468 | // TODO: move code to check_leaf / TraceThroughLeaf 469 | const bsp_leaf &leaf = leafs_[-(index + 1)]; 470 | 471 | for (int i = 0; i < leaf.num_leafbrushes; ++i) { 472 | const bsp_brush &brush = brushes_[leafbrushes_[leaf.leafbrush + i].brush]; 473 | 474 | // TODO: CONTENTS_SOLID should be a parameter like MASK_PLAYERSOLID 475 | if (brush.num_brushsides > 0 && 476 | textures_[brush.texture].contents & CONTENTS_SOLID) { 477 | check_brush(brush, real_start, real_end); 478 | } 479 | 480 | if (output_fraction == 0.0) { 481 | break; 482 | } 483 | } 484 | 485 | // if fraction == 0 486 | return; 487 | 488 | /* 489 | // TODO: loop surfaces until numLeafSurfaces and TraceThroughPatch 490 | for (auto i = 0u; i < leaf.num_leaffaces; ++i) { 491 | const bsp_leafface *face = leaffaces_[] 492 | } 493 | */ 494 | } 495 | 496 | const bsp_node &node = nodes_[index]; 497 | const bsp_plane &plane = planes_[node.plane]; 498 | 499 | /* 500 | if (plane.type < 3) { // axial plane 501 | 502 | } else { // non axial plane 503 | // offset is weirdly 2048 check q3 source 504 | } 505 | */ 506 | glm::vec4 normal(plane.normal, 0.0f); 507 | 508 | const float start_distance = glm::dot(normal, start) - plane.distance; 509 | const float end_distance = glm::dot(normal, end) - plane.distance; 510 | 511 | float offset = trace_radius; 512 | 513 | // TODO: there is offset + 1 and offset -1 in quake source. why? 514 | // both in front of plane 515 | if (start_distance >= offset + 1 && end_distance >= offset + 1) { 516 | check_node(node.front, start_fraction, end_fraction, start, end); 517 | return; 518 | } 519 | 520 | // both behind the plane 521 | if (start_distance < -offset - 1 && end_distance < -offset - 1) { 522 | check_node(node.back, start_fraction, end_fraction, start, end); 523 | return; 524 | } 525 | 526 | int side; 527 | float fraction1, fraction2, middle_fraction; 528 | glm::vec4 middle; 529 | 530 | if (start_distance < end_distance) { 531 | side = 1; 532 | float inverse_distance = 1.0 / (start_distance - end_distance); 533 | fraction1 = (start_distance - offset + EPSILON) * inverse_distance; 534 | fraction2 = (start_distance + offset + EPSILON) * inverse_distance; 535 | } else if (end_distance < start_distance) { 536 | side = 0; 537 | float inverse_distance = 1.0 / (start_distance - end_distance); 538 | fraction1 = (start_distance + offset + EPSILON) * inverse_distance; 539 | fraction2 = (start_distance - offset - EPSILON) * inverse_distance; 540 | } else { 541 | side = 0; 542 | fraction1 = 1.0f; 543 | fraction2 = 0.0f; 544 | } 545 | 546 | if (fraction1 < 0.0f) { 547 | fraction1 = 0.0f; 548 | } 549 | if (fraction1 > 1.0f) { 550 | fraction1 = 1.0f; 551 | } 552 | 553 | middle_fraction = 554 | start_fraction + (end_fraction - start_fraction) * fraction1; 555 | middle = start + fraction1 * (end - start); 556 | 557 | // TODO: remove if by changing front back to array of 2 elements 558 | if (side == 0) { 559 | check_node(node.front, start_fraction, middle_fraction, start, middle); 560 | } else { 561 | check_node(node.back, start_fraction, middle_fraction, start, middle); 562 | } 563 | 564 | if (fraction2 < 0.0f) { 565 | fraction2 = 0.0f; 566 | } 567 | if (fraction2 > 1.0f) { 568 | fraction2 = 1.0f; 569 | } 570 | 571 | middle_fraction = 572 | start_fraction + (end_fraction - start_fraction) * fraction2; 573 | middle = start + fraction2 * (end - start); 574 | 575 | if ((!side) == 0) { 576 | check_node(node.front, middle_fraction, end_fraction, middle, end); 577 | } else { 578 | check_node(node.back, middle_fraction, end_fraction, middle, end); 579 | } 580 | } 581 | 582 | void Bsp::check_brush(const bsp_brush &brush, glm::vec4 start, glm::vec4 end) { 583 | float start_fraction = -1.0f; 584 | float end_fraction = 1.0f; 585 | bool starts_out = false; 586 | bool ends_out = false; 587 | 588 | glm::vec4 clip_plane; 589 | 590 | for (int i = 0; i < brush.num_brushsides; ++i) { 591 | bsp_brushside &brushside = brushsides_[brush.brushside + i]; 592 | bsp_plane &plane = planes_[brushside.plane]; 593 | 594 | glm::vec4 normal(plane.normal, 0.0); 595 | 596 | // const float start_distance = glm::dot(normal, start) - plane.distance; 597 | // const float end_distance = glm::dot(normal, end) - plane.distance; 598 | 599 | // TODO: maybe something is wrong here. quake has more code. why am i in a 600 | // solid? 601 | const float start_distance = 602 | glm::dot(normal, start) - (plane.distance + trace_radius); 603 | const float end_distance = 604 | glm::dot(normal, end) - (plane.distance + trace_radius); 605 | 606 | if (start_distance > 0) { 607 | starts_out = true; 608 | } 609 | if (end_distance > 0) { 610 | ends_out = true; 611 | } 612 | 613 | // TODO: here an epsilon check might be needed, check q3 614 | // both in front of plane, the whole brush is not relevant 615 | if (start_distance > 0 && 616 | (end_distance >= EPSILON || end_distance >= start_distance)) { 617 | return; 618 | } 619 | 620 | // doesn't cross the plane, so it's not relevant 621 | if (start_distance <= 0 && end_distance <= 0) // both behind the plane 622 | { 623 | continue; 624 | } 625 | 626 | // crossing the plane 627 | // we enter a brush (start distance positive is in front of plane) 628 | if (start_distance > end_distance) { 629 | float fraction = 630 | (start_distance - EPSILON) / (start_distance - end_distance); 631 | 632 | if (fraction < 0.0f) { 633 | fraction = 0.0f; 634 | } 635 | 636 | if (fraction > start_fraction) { 637 | start_fraction = fraction; 638 | // TODO: change this to bsp_plane so it's easier to use later on 639 | clip_plane = glm::vec4(plane.normal, plane.distance); 640 | } 641 | } else { // we leave the brush (start distance negative is behind the brush) 642 | float fraction = 643 | (start_distance + EPSILON) / (start_distance - end_distance); 644 | 645 | if (fraction > 1.0f) { 646 | fraction = 1.0f; 647 | } 648 | 649 | if (fraction < end_fraction) { 650 | end_fraction = fraction; 651 | } 652 | } 653 | } 654 | 655 | if (starts_out == false) { 656 | // TODO: maybe rename to starts_solid to make it clearer 657 | output_starts_out = false; 658 | if (ends_out == false) { 659 | output_all_solid = true; 660 | output_fraction = 0.0f; 661 | // TODO: set output_contents to brush->contents 662 | } 663 | return; 664 | } 665 | 666 | if (start_fraction < end_fraction) { 667 | // covered some distance but less distance then before so update 668 | if (start_fraction > -1.0f && start_fraction < output_fraction) { 669 | if (start_fraction < 0.0f) { 670 | start_fraction = 0.0f; 671 | } 672 | output_fraction = start_fraction; 673 | output_plane = clip_plane; 674 | // TODO: set surface flags & content 675 | } 676 | } 677 | } 678 | -------------------------------------------------------------------------------- /src/bsp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BSP_H_ 2 | #define BSP_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | class bezier; 11 | class Q3Shader; 12 | 13 | #define CONTENTS_SOLID 1 // an eye is never valid in a solid 14 | #define CONTENTS_LAVA 8 15 | #define CONTENTS_SLIME 16 16 | #define CONTENTS_WATER 32 17 | #define CONTENTS_FOG 64 18 | 19 | #define CONTENTS_AREAPORTAL 0x8000 20 | 21 | #define CONTENTS_PLAYERCLIP 0x10000 22 | #define CONTENTS_MONSTERCLIP 0x20000 23 | // bot specific contents types 24 | #define CONTENTS_TELEPORTER 0x40000 25 | #define CONTENTS_JUMPPAD 0x80000 26 | #define CONTENTS_CLUSTERPORTAL 0x100000 27 | #define CONTENTS_DONOTENTER 0x200000 28 | 29 | #define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity 30 | 31 | #define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game 32 | #define CONTENTS_CORPSE 0x4000000 33 | #define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp 34 | #define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp 35 | #define CONTENTS_TRANSLUCENT \ 36 | 0x20000000 // don't consume surface fragments inside 37 | #define CONTENTS_TRIGGER 0x40000000 38 | #define CONTENTS_NODROP \ 39 | 0x80000000 // don't leave bodies or items (death fog, lava) 40 | 41 | #define SURF_NODAMAGE 0x1 // never give falling damage 42 | #define SURF_SLICK 0x2 // effects game physics 43 | #define SURF_SKY 0x4 // lighting from environment map 44 | #define SURF_LADDER 0x8 45 | #define SURF_NOIMPACT 0x10 // don't make missile explosions 46 | #define SURF_NOMARKS 0x20 // don't leave missile marks 47 | #define SURF_FLESH 0x40 // make flesh sounds and effects 48 | #define SURF_NODRAW 0x80 // don't generate a drawsurface at all 49 | #define SURF_HINT 0x100 // make a primary bsp splitter 50 | #define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes 51 | #define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap 52 | #define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes 53 | #define SURF_METALSTEPS 0x1000 // clanking footsteps 54 | #define SURF_NOSTEPS 0x2000 // no footstep sounds 55 | #define SURF_NONSOLID 0x4000 // don't collide against curves with this set 56 | #define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light 57 | #define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map 58 | #define SURF_NODLIGHT 0x20000 // never add dynamic lights 59 | 60 | // content masks 61 | #define MASK_ALL (-1) 62 | #define MASK_SOLID (CONTENTS_SOLID) 63 | #define MASK_PLAYERSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_BODY) 64 | #define MASK_DEADSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) 65 | #define MASK_WATER (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME) 66 | #define MASK_OPAQUE (CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA) 67 | #define MASK_SHOT (CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE) 68 | 69 | // plane types are used to speed some tests 70 | // 0-2 are axial planes 71 | #define PLANE_X 0 72 | #define PLANE_Y 1 73 | #define PLANE_Z 2 74 | #define PLANE_NON_AXIAL 3 75 | 76 | #define PlaneTypeForNormal(x) \ 77 | (x[0] == 1.0 \ 78 | ? PLANE_X \ 79 | : (x[1] == 1.0 ? PLANE_Y : (x[2] == 1.0 ? PLANE_Z : PLANE_NON_AXIAL))) 80 | 81 | enum lump { 82 | // Lump Name Description 83 | LUMP_ENTITIES, // Game-related object descriptions. 84 | LUMP_TEXTURES, // Surface descriptions. 85 | LUMP_PLANES, // Planes used by map geometry. 86 | LUMP_NODES, // BSP tree nodes. 87 | LUMP_LEAFS, // BSP tree leaves. 88 | LUMP_LEAFFACES, // Lists of face indices, one list per leaf. 89 | LUMP_LEAFBRUSHES, // Lists of brush indices, one list per leaf. 90 | LUMP_MODELS, // Descriptions of rigid world geometry in map. 91 | LUMP_BRUSHES, // Convex polyhedra used to describe solid space. 92 | LUMP_BRUSHSIDES, // Brush surfaces. 93 | LUMP_VERTEXES, // Vertices used to describe faces. 94 | LUMP_MESHVERTS, // Lists of offsets, one list per mesh. 95 | LUMP_EFFECTS, // List of special map effects. 96 | LUMP_FACES, // Surface geometry. 97 | LUMP_LIGHTMAPS, // Packed lightmap data. 98 | LUMP_LIGHTVOLS, // Local illumination data. 99 | LUMP_VISDATA // Cluster-cluster visibility data. 100 | }; 101 | 102 | enum faceType { POLYGON = 1, PATCH, MESH, BILLBOARD }; 103 | 104 | struct bsp_direntry { 105 | int offset; 106 | int length; 107 | }; 108 | 109 | struct bsp_header { 110 | char magic[4]; 111 | int version; 112 | bsp_direntry direntries[17]; 113 | }; 114 | 115 | struct bsp_entities { 116 | char *ents; 117 | }; 118 | 119 | struct bsp_texture { 120 | char name[64]; 121 | int flags; 122 | int contents; 123 | }; 124 | 125 | struct bsp_plane { 126 | glm::vec3 normal; 127 | float distance; 128 | }; 129 | 130 | struct bsp_node { 131 | int plane; 132 | int front; 133 | int back; 134 | int mins[3]; 135 | int maxs[3]; 136 | }; 137 | 138 | struct bsp_leaf { 139 | int cluster; 140 | int area; 141 | int mins[3]; 142 | int maxs[3]; 143 | int leafface; // first leafface 144 | int num_leaffaces; 145 | int leafbrush; // first leafbrush 146 | int num_leafbrushes; 147 | }; 148 | 149 | struct bsp_leafface { 150 | int face; 151 | }; 152 | 153 | struct bsp_leafbrush { 154 | int brush; 155 | }; 156 | 157 | struct bsp_model { 158 | float mins[3]; 159 | float maxs[3]; 160 | int face; // first face 161 | int num_faces; 162 | int brush; // first brush 163 | int num_brushes; 164 | }; 165 | 166 | struct bsp_brush { 167 | int brushside; // first brushside 168 | int num_brushsides; 169 | int texture; 170 | }; 171 | 172 | struct bsp_brushside { 173 | int plane; 174 | int texture; 175 | }; 176 | 177 | // maybe wrong? -> q3 source 178 | class bsp_vertex { 179 | public: 180 | glm::vec3 position; 181 | glm::vec2 texcoord; 182 | glm::vec2 lmcoord; 183 | glm::vec3 normal; 184 | unsigned char color[4]; 185 | 186 | /** 187 | Used for bezier patch tesselation 188 | */ 189 | bsp_vertex operator+(const bsp_vertex &v) const { 190 | bsp_vertex res; 191 | 192 | res.position = position + v.position; 193 | res.texcoord = texcoord + v.texcoord; 194 | res.lmcoord = lmcoord + v.lmcoord; 195 | res.normal = normal + v.normal; 196 | 197 | return res; 198 | } 199 | 200 | /** 201 | Used for bezier patch tesselation 202 | */ 203 | bsp_vertex operator*(float factor) const { 204 | bsp_vertex res; 205 | 206 | res.position = position * factor; 207 | res.texcoord = texcoord * factor; 208 | res.lmcoord = lmcoord * factor; 209 | res.normal = normal * factor; 210 | 211 | return res; 212 | } 213 | }; 214 | 215 | struct bsp_meshvert { 216 | unsigned int offset; 217 | }; 218 | 219 | struct bsp_effect { 220 | char name[64]; 221 | int brush; 222 | int unknown; 223 | }; 224 | 225 | // hier vllt fehler bei lm_vecs (q3 source..) 226 | struct bsp_face { 227 | int texture; 228 | int effect; 229 | int type; 230 | int vertex; // first vertex 231 | int num_vertices; 232 | int meshvert; // first (index?) meshvert 233 | int num_meshverts; 234 | int lm_index; 235 | int lm_start[2]; 236 | int lm_size[2]; 237 | glm::vec3 lm_origin; 238 | glm::vec3 lm_vecs[2]; 239 | glm::vec3 normal; 240 | int size[2]; 241 | }; 242 | 243 | struct bsp_lightmap { 244 | unsigned char map[128][128][3]; 245 | }; 246 | 247 | struct bsp_lightvol { 248 | unsigned char ambient[3]; 249 | unsigned char directional[3]; 250 | unsigned char dir[2]; 251 | }; 252 | 253 | struct bsp_visdata { 254 | int num_vecs; 255 | int size_vecs; 256 | unsigned char *vecs; // [num_vecs*size_vecs] 257 | }; 258 | 259 | struct trace_info { 260 | bool starts_out; 261 | bool all_solid; 262 | float fraction; 263 | glm::vec4 plane; 264 | }; 265 | 266 | class Bsp { 267 | public: 268 | Bsp(void); 269 | ~Bsp(void); 270 | 271 | Bsp(std::string filename); 272 | 273 | int FindLeaf(const glm::vec4 &camera_position); 274 | std::vector ComputeVisibleFaces(const glm::vec4 &camera_position); 275 | bool IsClusterVisible(int cluster, int test_cluster); 276 | 277 | void load_lightmaps(); 278 | 279 | float trace(glm::vec4 &start, glm::vec4 &end, trace_info &trace); 280 | void check_node(int index, float start_fraction, float end_fraction, 281 | glm::vec4 start, glm::vec4 end); 282 | void check_brush(const bsp_brush &brush, glm::vec4 start, glm::vec4 end); 283 | 284 | GLuint vboId; 285 | GLuint iboId; 286 | 287 | std::map> patches_; 288 | 289 | // some status variables for outputting info 290 | int num_cluster_not_visible_; 291 | int num_not_in_frustum_; 292 | int num_skipped_faces_; 293 | int num_skipped_shaders_; 294 | 295 | int num_entities_; 296 | int num_textures_; 297 | int num_planes_; 298 | int num_nodes_; 299 | int num_leafs_; 300 | int num_leaffaces_; 301 | int num_leafbrushes_; 302 | int num_models_; 303 | int num_brushes_; 304 | int num_brushsides_; 305 | int num_vertexes_; 306 | int num_meshverts_; 307 | int num_effects_; 308 | int num_faces_; 309 | int num_lightmaps_; 310 | int num_lightvols_; 311 | 312 | bsp_header header_; 313 | bsp_entities *entities_; 314 | bsp_texture *textures_; 315 | bsp_plane *planes_; 316 | bsp_node *nodes_; 317 | bsp_leaf *leafs_; 318 | bsp_leafface *leaffaces_; 319 | bsp_leafbrush *leafbrushes_; 320 | bsp_model *models_; 321 | bsp_brush *brushes_; 322 | bsp_brushside *brushsides_; 323 | bsp_vertex *vertexes_; 324 | bsp_meshvert *meshverts_; 325 | bsp_effect *effects_; 326 | bsp_face *faces_; 327 | bsp_lightmap *lightmaps_; 328 | bsp_lightvol *lightvols_; 329 | bsp_visdata *visdata_; 330 | }; 331 | 332 | #endif /* _BSP_H_ */ 333 | -------------------------------------------------------------------------------- /src/component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMPONENT_HPP_ 2 | #define COMPONENT_HPP_ 3 | 4 | class Message; 5 | class Entity; 6 | 7 | class Component 8 | { 9 | protected: 10 | Entity* entity_; 11 | 12 | bool is_active_ = true; 13 | 14 | public: 15 | 16 | virtual ~Component(void) {}; 17 | virtual void ReceiveMessage(Message& msg) = 0; 18 | virtual void Update(unsigned int time) = 0; 19 | 20 | inline void set_entity(Entity* entity) 21 | { 22 | entity_ = entity; 23 | } 24 | 25 | inline void set_active(bool is_active) 26 | { 27 | is_active_ = is_active; 28 | } 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/entity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ENTITY_H_ 2 | #define ENTITY_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "input.hpp" 9 | 10 | class Message; 11 | class Component; 12 | class Model; 13 | 14 | class Entity { 15 | std::vector components_; 16 | 17 | public: 18 | Entity(void); 19 | ~Entity(void); 20 | 21 | void AddComponent(Component *component); 22 | void SendMessage(Message &message); 23 | 24 | void Update(unsigned int time); 25 | 26 | // share state between components 27 | cmd_t cmds_; 28 | glm::vec4 velocity_; 29 | glm::vec4 position_; 30 | glm::vec2 orientation_; 31 | glm::vec4 up_; 32 | glm::vec4 right_; 33 | glm::vec4 look_; 34 | float pitch_; 35 | float yaw_; 36 | 37 | Model *upper, *lower, *head; 38 | unsigned int upper_frame, lower_frame; 39 | bool noclip_; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/font.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FONT_HPP_ 2 | #define FONT_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // in case we support multiple fonts, maybe extract print function. 10 | class Font 11 | { 12 | public: 13 | Font(void); 14 | ~Font(void); 15 | 16 | int LoadFont(std::string font); 17 | void PrintString(std::string, glm::vec2& position, glm::vec4& color); 18 | 19 | private: 20 | GLuint texture_; 21 | GLuint shader_; 22 | GLuint texture_idx_, projection_idx_, position_idx_, tex_coord_idx_, color_idx_; 23 | }; 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /src/frustum.cpp: -------------------------------------------------------------------------------- 1 | #include "frustum.hpp" 2 | 3 | #include "glm/gtc/matrix_access.hpp" 4 | 5 | Frustum::Frustum(void) 6 | { 7 | } 8 | 9 | Frustum::~Frustum(void) 10 | { 11 | } 12 | 13 | void Frustum::ExtractPlanes(glm::mat4& view_matrix, glm::mat4& projection_matrix) 14 | { 15 | combined_matrix_ = projection_matrix * view_matrix; 16 | 17 | planes_[LEFT] = glm::column(combined_matrix_, 3) + glm::column(combined_matrix_, 0); 18 | planes_[RIGHT] = glm::column(combined_matrix_, 3) - glm::column(combined_matrix_, 0); 19 | planes_[DOWN] = glm::column(combined_matrix_, 3) + glm::column(combined_matrix_, 1); 20 | planes_[UP] = glm::column(combined_matrix_, 3) - glm::column(combined_matrix_, 1); 21 | planes_[NEAR] = glm::column(combined_matrix_, 3) + glm::column(combined_matrix_, 2); 22 | planes_[FAR] = glm::column(combined_matrix_, 3) - glm::column(combined_matrix_, 2); 23 | 24 | // Normalize planes 25 | for (int i = 0; i < 6; ++i) 26 | { 27 | //planes_[i] = glm::vec4(glm::normalize(glm::vec3(m_planes[i])), m_planes[i].w); 28 | } 29 | } 30 | 31 | bool Frustum::IsBoxInFrustum(glm::vec3& min, glm::vec3& max) 32 | { 33 | for (int i = 0; i < 6; i++) 34 | { 35 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(min.x, min.y, min.z)) + planes_[i].w > 0) continue; 36 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(max.x, min.y, min.z)) + planes_[i].w > 0) continue; 37 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(min.x, max.y, min.z)) + planes_[i].w > 0) continue; 38 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(max.x, max.y, min.z)) + planes_[i].w > 0) continue; 39 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(min.x, min.y, max.z)) + planes_[i].w > 0) continue; 40 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(max.x, min.y, max.z)) + planes_[i].w > 0) continue; 41 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(min.x, max.y, max.z)) + planes_[i].w > 0) continue; 42 | if (glm::dot(glm::vec3(planes_[i]), glm::vec3(max.x, max.y, max.z)) + planes_[i].w > 0) continue; 43 | 44 | // If we get here, it isn't in the frustum because all points are behind of 1 plane 45 | return false; 46 | } 47 | 48 | // Return a true for the box being inside of the frustum, at least one point is in front of a plane 49 | return true; 50 | } 51 | -------------------------------------------------------------------------------- /src/frustum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FRUSTUM_HPP_ 2 | #define FRUSTUM_HPP_ 3 | 4 | #include 5 | 6 | class Frustum 7 | { 8 | public: 9 | Frustum(void); 10 | ~Frustum(void); 11 | 12 | void ExtractPlanes(glm::mat4& view_matrix, glm::mat4& projection_matrix); 13 | bool IsBoxInFrustum(glm::vec3& min, glm::vec3& max); 14 | 15 | private: 16 | enum { 17 | LEFT, 18 | RIGHT, 19 | UP, 20 | DOWN, 21 | NEAR, 22 | FAR 23 | }; 24 | 25 | glm::vec4 planes_[6]; 26 | glm::mat4 combined_matrix_; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/input.cpp: -------------------------------------------------------------------------------- 1 | #include "input.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "logger.hpp" 8 | #include "message.hpp" 9 | #include "messenger.hpp" 10 | 11 | namespace input { 12 | namespace { 13 | std::map keymap_; 14 | } 15 | 16 | void Initialize() { 17 | keymap_[SDL_SCANCODE_ESCAPE] = MESSAGE::QUIT; 18 | 19 | keymap_[SDL_SCANCODE_W] = MESSAGE::CMD_FORWARD; 20 | keymap_[SDL_SCANCODE_S] = MESSAGE::CMD_BACKWARD; 21 | keymap_[SDL_SCANCODE_A] = MESSAGE::CMD_LEFT; 22 | keymap_[SDL_SCANCODE_D] = MESSAGE::CMD_RIGHT; 23 | keymap_[SDL_SCANCODE_N] = MESSAGE::NOCLIP; 24 | keymap_[SDL_SCANCODE_C] = MESSAGE::NEXT_PLAYER; 25 | 26 | SDL_SetRelativeMouseMode(SDL_TRUE); 27 | } 28 | 29 | cmd_t Update() { 30 | SDL_PumpEvents(); 31 | auto keystate = SDL_GetKeyboardState(NULL); 32 | 33 | for (auto &kv : keymap_) { 34 | if (keystate[kv.first]) { 35 | messenger::BroadcastMessage(kv.second, nullptr); 36 | } 37 | } 38 | 39 | int x, y; 40 | SDL_GetRelativeMouseState(&x, &y); 41 | 42 | logger::Log(logger::DEBUG, "mouse x, y: %d, %d", x, y); 43 | 44 | // SDL_WarpMouseGlobal(400, 300); 45 | float rx = ((((float)x) - 400.f) / 100.f); 46 | float ry = ((((float)y) - 300.f) / 100.f); 47 | 48 | rx = x / 100.f; 49 | ry = y / 100.f; 50 | 51 | // messenger::BroadcastMessage(MESSAGE::CMD_MOUSELOOK, &MouseMoveMessage(rx, 52 | // ry)); 53 | cmd_t cmds; 54 | memset(&cmds, 0, sizeof(cmds)); 55 | if (keystate[SDL_SCANCODE_W]) 56 | cmds.forward_move -= 125; 57 | if (keystate[SDL_SCANCODE_S]) 58 | cmds.forward_move += 125; 59 | if (keystate[SDL_SCANCODE_D]) 60 | cmds.right_move += 125; 61 | if (keystate[SDL_SCANCODE_A]) 62 | cmds.right_move -= 125; 63 | if (keystate[SDL_SCANCODE_SPACE]) 64 | cmds.up_move += 125; 65 | cmds.mouse_dx = -rx; 66 | cmds.mouse_dy = -ry; 67 | 68 | return cmds; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/input.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_H_ 2 | #define INPUT_H_ 3 | 4 | struct cmd_t 5 | { 6 | char forward_move; 7 | char right_move; 8 | char up_move; 9 | float mouse_dx; 10 | float mouse_dy; 11 | }; 12 | 13 | namespace input 14 | { 15 | void Initialize(); 16 | cmd_t Update(); 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/logger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_HPP_ 2 | #define LOGGER_HPP_ 3 | 4 | namespace logger { 5 | enum Level { ERROR, DEFAULT, DEBUG }; 6 | 7 | void set_max_level(Level max_level); 8 | void Log(Level level, const char *fmt, ...); 9 | void Debug(const char *fmt, ...); 10 | }; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bsp.hpp" 6 | #include "input.hpp" 7 | #include "logger.hpp" 8 | #include "messenger.hpp" 9 | #include "model.hpp" 10 | #include "player_animation_component.hpp" 11 | #include "player_input_component.hpp" 12 | #include "player_physics_component.hpp" 13 | #include "renderer.hpp" 14 | #include "shader_loader.hpp" 15 | #include "texture_loader.hpp" 16 | #include "world.hpp" 17 | 18 | Renderer renderer; 19 | World world; 20 | 21 | Model *head; 22 | Model *upper; 23 | Model *lower; 24 | 25 | Entity player; 26 | PlayerInputComponent pic1; 27 | PlayerPhysicsComponent ppc1; 28 | 29 | Entity enemy; 30 | PlayerAnimationComponent pac2; 31 | PlayerPhysicsComponent ppc2; 32 | 33 | bool is_player = true; 34 | 35 | cmd_t g_cmds; 36 | 37 | bool g_running = true; 38 | 39 | void QuitCallback(Message *msg) { g_running = false; } 40 | 41 | void NoclipCallback(Message *msg) { 42 | logger::Log(logger::DEBUG, "NOCLIP SWITCH %d", !world.player_->noclip_); 43 | world.player_->noclip_ = !world.player_->noclip_; 44 | } 45 | 46 | void ChangePlayerCallback(Message *msg) { 47 | logger::Log(logger::DEBUG, "Switching players %d", !is_player); 48 | is_player = !is_player; 49 | 50 | if (is_player) { 51 | ppc1.set_active(true); 52 | ppc2.set_active(false); 53 | } else { 54 | ppc1.set_active(false); 55 | ppc2.set_active(true); 56 | } 57 | } 58 | 59 | int main(int argc, char **argv) { 60 | if (SDL_Init(SDL_INIT_VIDEO) != 0) { 61 | logger::Log(logger::DEBUG, "Could not initialize SDL (%s)", SDL_GetError()); 62 | } 63 | 64 | IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); 65 | 66 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 67 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); 68 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 69 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 70 | SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 71 | 72 | auto screen = 73 | SDL_CreateWindow("Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 74 | WIDTH, HEIGHT, SDL_WINDOW_OPENGL); 75 | 76 | auto gl_context = SDL_GL_CreateContext(screen); 77 | 78 | // TODO: after initializing glew check if all needed functions are 79 | // available... 80 | // fall back if not or just quit 81 | glewExperimental = GL_TRUE; 82 | GLenum err = glewInit(); 83 | if (err != GLEW_OK) { 84 | logger::Log(logger::ERROR, "Error: %s", glewGetErrorString(err)); 85 | } 86 | logger::Log(logger::ERROR, "Status: Using GLEW %s", 87 | glewGetString(GLEW_VERSION)); 88 | 89 | // font.LoadFont("gfx\\2d\\bigchars.tga"); 90 | messenger::RegisterReceiver(MESSAGE::QUIT, QuitCallback); 91 | messenger::RegisterReceiver(MESSAGE::NOCLIP, NoclipCallback); 92 | messenger::RegisterReceiver(MESSAGE::NEXT_PLAYER, ChangePlayerCallback); 93 | 94 | input::Initialize(); 95 | 96 | renderer.Initialize(); 97 | 98 | GLuint vao; 99 | glGenVertexArrays(1, &vao); 100 | glBindVertexArray(vao); 101 | 102 | logger::Log(logger::DEBUG, "Loading shader files"); 103 | shaderLoader::LoadAllShaders(); 104 | 105 | world.LoadLevel("q3dm6"); 106 | 107 | // PlayerModel *m = new PlayerModel("models/players/", "visor"); 108 | 109 | // Model model("tankjr"); 110 | head = new Model("models/players/visor/head.md3"); 111 | head->shader_ = 112 | shaderLoader::CreateModelShader("models/players/visor/red.tga"); 113 | upper = new Model("models/players/visor/upper.md3"); 114 | upper->shader_ = 115 | shaderLoader::CreateModelShader("models/players/visor/red.tga"); 116 | lower = new Model("models/players/visor/lower.md3"); 117 | lower->shader_ = 118 | shaderLoader::CreateModelShader("models/players/visor/red.tga"); 119 | 120 | // load weapon 121 | 122 | unsigned int ticks = 0; 123 | unsigned int delta = 0; 124 | 125 | player.AddComponent(&pic1); 126 | player.AddComponent(&ppc1); 127 | player.noclip_ = true; 128 | 129 | world.player_ = &player; 130 | world.players_.push_back(&player); 131 | 132 | enemy.lower = lower; 133 | enemy.upper = upper; 134 | enemy.head = head; 135 | enemy.noclip_ = false; 136 | 137 | enemy.AddComponent(&pac2); 138 | enemy.AddComponent(&ppc2); 139 | 140 | { 141 | int skip = 153 - 90; 142 | int g_lower_startFrame = 220 - skip; 143 | 144 | enemy.lower_frame = g_lower_startFrame; 145 | enemy.upper_frame = 90; 146 | } 147 | 148 | world.enemy_ = &enemy; 149 | world.players_.push_back(&enemy); 150 | 151 | player.position_ = glm::vec4(-589.0f, -275.0f, 128.0f, 1.0f); 152 | // enemy.position_ = glm::vec4(-589.0f, -275.0f, 100.0f, 1.0f); // center room 153 | enemy.position_ = glm::vec4(251.8f, -816.1f, 30.0f, 1.0f); // staircase 154 | 155 | while (g_running) { 156 | if (SDL_QuitRequested()) { 157 | g_running = false; 158 | } 159 | 160 | delta = SDL_GetTicks() - ticks; 161 | ticks = SDL_GetTicks(); 162 | 163 | g_cmds = input::Update(); 164 | world.Update(delta); 165 | 166 | renderer.AddRenderables(world.map_->ComputeVisibleFaces(player.position_)); 167 | renderer.RenderFrame((float)ticks / 1000.0f); 168 | 169 | SDL_GL_SwapWindow(screen); 170 | } 171 | 172 | shaderLoader::Deinitialize(); 173 | textureLoader::Deinitialize(); 174 | 175 | SDL_GL_DeleteContext(gl_context); 176 | SDL_DestroyWindow(screen); 177 | 178 | IMG_Quit(); 179 | SDL_Quit(); 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /src/message.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _MESSAGE_H_ 2 | #define _MESSAGE_H_ 3 | 4 | class Message 5 | { 6 | public: 7 | virtual ~Message(void) {}; 8 | }; 9 | 10 | class MouseMoveMessage : public Message 11 | { 12 | public: 13 | ~MouseMoveMessage() {}; 14 | MouseMoveMessage(float dx, float dy) : dx_(dx), dy_(dy) {}; 15 | 16 | float dx_; 17 | float dy_; 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/messenger.cpp: -------------------------------------------------------------------------------- 1 | #include "messenger.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "message.hpp" 7 | 8 | namespace messenger { 9 | namespace { 10 | 11 | // TODO: change to std::array using MESSAGE enum as indices 12 | std::map> receivers_; 13 | } 14 | 15 | int RegisterReceiver(MESSAGE msg, CallbackType callback) { 16 | receivers_[msg].push_back(callback); 17 | 18 | return receivers_[msg].size(); 19 | } 20 | 21 | int UnregisterReceiver() { return 0; } 22 | 23 | int BroadcastMessage(MESSAGE type, Message *msg) { 24 | for (CallbackType receiver : receivers_[type]) { 25 | receiver(msg); 26 | } 27 | 28 | return 0; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/messenger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MESSENGER_HPP_ 2 | #define MESSENGER_HPP_ 3 | 4 | #include "message.hpp" 5 | 6 | enum class MESSAGE { 7 | CMD_FORWARD, 8 | CMD_BACKWARD, 9 | CMD_RIGHT, 10 | CMD_LEFT, 11 | CMD_UP, 12 | CMD_DOWN, 13 | CMD_MOUSELOOK, 14 | QUIT, 15 | NOCLIP, 16 | NEXT_PLAYER, 17 | }; 18 | 19 | // eventually replace bool with message 20 | typedef void (*CallbackType)(Message *); 21 | 22 | namespace messenger { 23 | int RegisterReceiver(MESSAGE msg, CallbackType callback); 24 | int UnregisterReceiver(); 25 | 26 | int BroadcastMessage(MESSAGE type, Message *msg); 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/model.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H_ 2 | #define MODEL_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "logger.hpp" 9 | 10 | class Shader; 11 | 12 | #define Q_PI 3.14159265358979323846 13 | 14 | #define MD3_MAX_FRAMES 1024 15 | #define MD3_MAX_TAGS 16 16 | #define MD3_MAX_SURFACES 32 17 | #define MD3_MAX_SHADERS 256 18 | #define MD3_MAX_VERTS 4096 19 | #define MD3_MAX_TRIANGLES 8192 20 | 21 | struct md3_header { 22 | int ident; 23 | int version; 24 | char name[64]; 25 | int flags; 26 | int num_frames; 27 | int num_tags; 28 | int num_surfaces; 29 | int num_skins; 30 | int ofs_frames; 31 | int ofs_tags; 32 | int ofs_surfaces; 33 | int ofs_eof; 34 | }; 35 | 36 | struct md3_frame { 37 | glm::vec3 min_bounds; 38 | glm::vec3 max_bounds; 39 | glm::vec3 local_origin; 40 | float radius; 41 | char name[16]; 42 | }; 43 | 44 | struct md3_tag { 45 | char name[64]; 46 | glm::vec3 origin; 47 | glm::vec3 axis[3]; 48 | }; 49 | 50 | struct md3_surface { 51 | int ident; 52 | char name[64]; 53 | int flags; 54 | int num_frames; 55 | int num_shaders; 56 | int num_verts; 57 | int num_triangles; 58 | int ofs_triangles; 59 | int ofs_shaders; 60 | int ofs_st; 61 | int ofs_xyznormal; 62 | int ofs_end; 63 | }; 64 | 65 | struct md3_shader { 66 | char name[64]; 67 | int shader_index; 68 | }; 69 | 70 | struct md3_triangle { 71 | unsigned int indexes[3]; 72 | }; 73 | 74 | struct md3_texcoord { 75 | float st[2]; 76 | }; 77 | 78 | struct md3_vertex { 79 | short coord[3]; 80 | short normal; 81 | }; 82 | 83 | struct my_vertex { 84 | glm::vec3 position; 85 | glm::vec3 normal; 86 | float u, v; 87 | }; 88 | 89 | enum animation { 90 | BOTH_DEATH1, 91 | BOTH_DEAD1, 92 | BOTH_DEATH2, 93 | BOTH_DEAD2, 94 | BOTH_DEATH3, 95 | BOTH_DEAD3, 96 | 97 | TORSO_GESTURE, 98 | TORSO_ATTACK, 99 | TORSO_ATTACK2, 100 | TORSO_DROP, 101 | TORSO_RAISE, 102 | TORSO_STAND, 103 | TORSO_STAND2, 104 | 105 | LEGS_WALKCR, 106 | LEGS_WALK, 107 | LEGS_RUN, 108 | LEGS_BACK, 109 | LEGS_SWIM, 110 | LEGS_JUMP, 111 | LEGS_LAND, 112 | LEGS_JUMPB, 113 | LEGS_LANDB, 114 | LEGS_IDLE, 115 | LEGS_IDLECR, 116 | LEGS_TURN, 117 | 118 | TORSO_GETFLAG, 119 | TORSO_GUARDBASE, 120 | TORSO_PATROL, 121 | TORSO_FOLLOWME, 122 | TORSO_AFFIRMATIVE, 123 | TORSO_NEGATIVE, 124 | 125 | MAX_ANIMATIONS, 126 | 127 | LEGS_BACKCR, 128 | LEGS_BACKWALK, 129 | FLAG_RUN, 130 | FLAG_STAND, 131 | FLAG_STAND2RUN, 132 | 133 | MAX_TOTALANIMATIONS 134 | }; 135 | 136 | struct animation_info { 137 | unsigned int first_frame; 138 | unsigned int num_frames; 139 | unsigned int loop_frames; 140 | double frames_per_second; 141 | bool reversed; 142 | }; 143 | 144 | class Model { 145 | public: 146 | Model(void); 147 | ~Model(void); 148 | 149 | Model(std::string filename); 150 | 151 | glm::vec3 head_offset_; 152 | 153 | md3_header header_; 154 | 155 | md3_frame *frames_; 156 | md3_tag *tags_; 157 | md3_surface *surfaces_; 158 | 159 | md3_shader **shaders_; 160 | md3_triangle **triangles_; 161 | md3_texcoord **texcoords_; 162 | md3_vertex **normals_; 163 | 164 | my_vertex **vertices_; 165 | 166 | unsigned int vboId_; 167 | unsigned int iboId_; 168 | 169 | Shader *shader_; 170 | }; 171 | 172 | class PlayerModel { 173 | public: 174 | PlayerModel(std::string path, std::string name); 175 | void LoadAnimations(); 176 | void ParseAnimations(char *buffer); 177 | 178 | animation_info animations_[MAX_TOTALANIMATIONS]; 179 | 180 | private: 181 | glm::vec3 head_offset_; 182 | 183 | // sex 184 | // footsteps 185 | 186 | Model head_; 187 | Model body_; 188 | Model legs_; 189 | 190 | std::string path_; 191 | std::string name_; 192 | }; 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /src/player_animation_component.cpp: -------------------------------------------------------------------------------- 1 | #include "player_animation_component.hpp" 2 | 3 | #include "entity.hpp" 4 | 5 | #include "logger.hpp" 6 | 7 | PlayerAnimationComponent::PlayerAnimationComponent() 8 | : model_("models/players/", "visor") { 9 | upper_current_animation_ = TORSO_STAND; 10 | lower_current_animation_ = LEGS_WALK; 11 | } 12 | 13 | PlayerAnimationComponent::~PlayerAnimationComponent(void) {} 14 | 15 | void PlayerAnimationComponent::Update(unsigned int time) { 16 | /* int skip = 153 - 90; 17 | int g_lower_fps = 15; 18 | int g_lower_startFrame = 220 - skip; 19 | int g_lower_numFrames = 10; 20 | */ 21 | 22 | animation_info *lower_animation = 23 | &model_.animations_[lower_current_animation_]; 24 | 25 | auto g_lower_fps = lower_animation->frames_per_second; 26 | auto g_lower_startFrame = lower_animation->first_frame; 27 | auto g_lower_numFrames = lower_animation->num_frames; 28 | 29 | logger::Debug("lower animation: %f %d %d", g_lower_fps, g_lower_startFrame, 30 | g_lower_numFrames); 31 | 32 | static auto fLowerLastTime = 0.0f; 33 | static auto lowerFrame = 0u; 34 | 35 | fLowerLastTime += time; 36 | if (fLowerLastTime > g_lower_fps) { 37 | ++lowerFrame; 38 | if (lowerFrame >= g_lower_numFrames) { 39 | lowerFrame = 0; 40 | } 41 | 42 | if (lower_animation->reversed) { 43 | entity_->lower_frame = 44 | g_lower_startFrame + g_lower_numFrames - 1 - lowerFrame; 45 | } else { 46 | entity_->lower_frame = g_lower_startFrame + lowerFrame; 47 | } 48 | /* 49 | ++entity_->lower_frame; 50 | if (entity_->lower_frame >= g_lower_startFrame + g_lower_numFrames) { 51 | entity_->lower_frame = g_lower_startFrame; 52 | }*/ 53 | fLowerLastTime = 0.0; 54 | } 55 | 56 | /* 57 | int g_upper_fps = 20; 58 | int g_upper_startFrame = 90; 59 | int g_upper_numFrames = 40; 60 | */ 61 | 62 | animation_info *upper_animation = 63 | &model_.animations_[upper_current_animation_]; 64 | 65 | float g_upper_fps = upper_animation->frames_per_second; 66 | int g_upper_startFrame = upper_animation->first_frame; 67 | int g_upper_numFrames = upper_animation->num_frames; 68 | 69 | static float fUpperLastTime = 0.0f; 70 | 71 | fUpperLastTime += time; 72 | if (fUpperLastTime > g_upper_fps) { 73 | ++entity_->upper_frame; 74 | if (entity_->upper_frame >= g_upper_startFrame + g_upper_numFrames) { 75 | entity_->upper_frame = g_upper_startFrame; 76 | } 77 | fUpperLastTime = 0.0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/player_animation_component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_ANIMATION_COMPONENT_HPP_ 2 | #define PLAYER_ANIMATION_COMPONENT_HPP_ 3 | 4 | #include "component.hpp" 5 | #include "model.hpp" 6 | 7 | class PlayerAnimationComponent : public Component { 8 | public: 9 | PlayerAnimationComponent(); 10 | ~PlayerAnimationComponent(); 11 | 12 | void ReceiveMessage(Message &message){}; 13 | void Update(unsigned int time); 14 | 15 | private: 16 | int upper_current_animation_; 17 | int lower_current_animation_; 18 | PlayerModel model_; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/player_input_component.cpp: -------------------------------------------------------------------------------- 1 | #include "player_input_component.hpp" 2 | 3 | 4 | PlayerInputComponent::PlayerInputComponent() 5 | { 6 | //messenger::RegisterReceiver(MESSAGE::CMD_MOUSELOOK, &PlayerInputComponent::MouseMove); 7 | } 8 | 9 | 10 | PlayerInputComponent::~PlayerInputComponent(void) 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/player_input_component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_INPUT_COMPONENT_HPP_ 2 | #define PLAYER_INPUT_COMPONENT_HPP_ 3 | 4 | #include "component.hpp" 5 | #include "entity.hpp" 6 | #include "message.hpp" 7 | 8 | class PlayerInputComponent : public Component 9 | { 10 | public: 11 | PlayerInputComponent(); 12 | ~PlayerInputComponent(void); 13 | 14 | void ReceiveMessage(Message& message){}; 15 | void Update(unsigned int time){}; 16 | 17 | int MoveUp(Message& msg) { entity_->cmds_.forward_move += 1; }; 18 | int MoveDown(Message& msg) { entity_->cmds_.forward_move -= 1; }; 19 | int MoveLeft(Message& msg) { entity_->cmds_.right_move += 1; }; 20 | int MoveRight(Message& msg) { entity_->cmds_.right_move -= 1; }; 21 | int MouseMove(MouseMoveMessage& msg) 22 | { 23 | entity_->cmds_.mouse_dx = msg.dx_; 24 | entity_->cmds_.mouse_dy = msg.dy_; 25 | }; 26 | }; 27 | 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /src/player_physics_component.cpp: -------------------------------------------------------------------------------- 1 | #include "player_physics_component.hpp" 2 | 3 | #include "bsp.hpp" 4 | #include "entity.hpp" 5 | #include "input.hpp" 6 | #include "logger.hpp" 7 | #include "world.hpp" 8 | 9 | // TODO: get rid of those globals (pass to method) 10 | extern World world; 11 | extern cmd_t g_cmds; 12 | 13 | PlayerPhysicsComponent::PlayerPhysicsComponent() {} 14 | 15 | PlayerPhysicsComponent::~PlayerPhysicsComponent() {} 16 | 17 | // TODO: should be in input component 18 | // TODO: remove magic numbers 19 | void PlayerPhysicsComponent::UpdatePitch() { 20 | auto &pitch = entity_->pitch_; 21 | 22 | logger::Log(logger::DEBUG, "cmd dy: %f", g_cmds.mouse_dy); 23 | 24 | pitch += g_cmds.mouse_dy; 25 | if (pitch > 1.5707f) { 26 | pitch = 1.5707f; 27 | } 28 | if (pitch < -1.5707f) { 29 | pitch = -1.5707f; 30 | } 31 | } 32 | 33 | void PlayerPhysicsComponent::UpdateYaw() { 34 | auto &yaw = entity_->yaw_; 35 | 36 | logger::Log(logger::DEBUG, "cmd dx: %f", g_cmds.mouse_dx); 37 | 38 | yaw += g_cmds.mouse_dx; 39 | if (yaw > 2 * 3.1415f) { 40 | yaw = 0.0f; 41 | } 42 | if (yaw < 2 * -3.1415f) { 43 | yaw = 0.0f; 44 | } 45 | } 46 | 47 | #define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes 48 | #define STEPSIZE 18 49 | #define JUMP_VELOCITY 270 50 | #define OVERCLIP 1.001f 51 | #define GRAVITY 800.0f 52 | 53 | void ClipVelocity(glm::vec4 &in, glm::vec4 &out, glm::vec4 &plane) { 54 | // This should only clip if the velocity goes against the plane 55 | logger::Debug("clip plane: %f %f %f %f", plane.x, plane.y, plane.z, plane.w); 56 | logger::Debug("clip velocity: %f %f %f %f", in.x, in.y, in.z, in.w); 57 | float distance = glm::dot(glm::vec3(plane), glm::vec3(in)); 58 | logger::Debug("clip distance: %f", distance); 59 | 60 | if (distance < 0.0f) { 61 | distance *= 1.001f; 62 | } else { 63 | distance /= 1.001f; 64 | } 65 | 66 | out = in - distance * plane; 67 | } 68 | 69 | /* 70 | * Note: quake 'time' = pml.frametime 71 | * pml.frametime = pml.msec * 0.001; 72 | * msec = 66; when pmove (fixed frame time) 73 | * 1000/66 = 15 => 15 updates per second? seems low 74 | */ 75 | 76 | void PlayerPhysicsComponent::Update(unsigned int time) { 77 | if (!is_active_) { 78 | return; 79 | } 80 | 81 | trace_info trace; 82 | 83 | /* 84 | auto currentSpeed = glm::length(glm::vec3(entity_->velocity_)); 85 | if (currentSpeed < 1) { 86 | entity_->velocity_.x = 0; 87 | entity_->velocity_.y = 0; 88 | logger::Debug("======= current speed %f smaller than ========", 89 | currentSpeed); 90 | } 91 | */ 92 | 93 | // is this actually correct?? should be applied to right and look vectors 94 | // because not it seems like they lag a frame behind 95 | UpdatePitch(); 96 | UpdateYaw(); 97 | 98 | float dt = static_cast(time) * 0.001f; 99 | 100 | // GroundTrace(); 101 | // trace to ground and get plane 102 | glm::vec4 start = entity_->position_; 103 | glm::vec4 ground_plane; 104 | glm::vec4 ground = start; 105 | ground.z -= 0.25f; // 0.25f is from q3 source 106 | 107 | logger::Log(logger::DEBUG, "coords: %f %f %f %f", start.x, start.y, start.z, 108 | start.w); 109 | 110 | float ground_fraction = world.map_->trace(start, ground, trace); 111 | ground_plane = trace.plane; 112 | 113 | logger::Debug("ground plane trace results: %d %d %f", trace.starts_out, 114 | trace.all_solid, trace.fraction); 115 | 116 | logger::Debug("ground plane frac x y z: %f %f %f %f %f", ground_fraction, 117 | ground_plane.x, ground_plane.y, ground_plane.z, ground_plane.w); 118 | 119 | bool on_ground = false; 120 | // we hit ground 121 | if (ground_fraction < 1.0f) { 122 | entity_->velocity_.z = 0.0f; 123 | on_ground = true; 124 | logger::Log(logger::DEBUG, "HIT GROUND"); 125 | } 126 | 127 | logger::Debug("on ground: %d", on_ground); 128 | 129 | float speed = 0.1f; 130 | // speed = 1.0f; 131 | 132 | // ignore movement in Z direction or else we start flying 133 | // - maybe set z 0 134 | // - clip to ground plane 135 | // - renormalize 136 | // but not in spectator mode... 137 | 138 | glm::vec4 right = entity_->right_; 139 | 140 | logger::Log(logger::DEBUG, "right: %f %f %f %f", right.x, right.y, right.z, 141 | right.w); 142 | 143 | glm::vec4 look = entity_->look_; 144 | 145 | logger::Log(logger::DEBUG, "look: %f %f %f %f", look.x, look.y, look.z, 146 | look.w); 147 | 148 | // if we don't multiply the speed here then it's the direction 149 | glm::vec4 accel; 150 | accel += right * speed * static_cast(g_cmds.right_move); 151 | accel += look * speed * static_cast(g_cmds.forward_move); 152 | 153 | logger::Log(logger::DEBUG, "accel: %f %f %f %f", accel.x, accel.y, accel.z, 154 | accel.w); 155 | 156 | // accel.x = speed * static_cast(g_cmds.right_move); 157 | // accel.y = speed * static_cast(g_cmds.forward_move); 158 | 159 | // Accelerate(); 160 | 161 | float fraction = 0.0f; 162 | 163 | if (entity_->noclip_) { 164 | // set boolean for jump key reset 165 | if (g_cmds.up_move > 0) { 166 | accel.z = JUMP_VELOCITY; 167 | } 168 | // NoClipMove(); 169 | fraction = 1.0f; 170 | entity_->velocity_ += (accel - 5.0f * entity_->velocity_) * dt; 171 | entity_->position_ += entity_->velocity_ * fraction; 172 | } 173 | // else if (!on_ground) 174 | //{ 175 | // //AirMove(); 176 | // //GroundMove(); 177 | //} 178 | else { 179 | 180 | right.z = 0.0; 181 | right = glm::normalize(right); 182 | look.z = 0.0; 183 | look = glm::normalize(look); 184 | accel.z = 0.0f; 185 | 186 | // GroundMove(); 187 | 188 | // accel.z -= 10; 189 | 190 | // maybe we have to remove the ground-hitting velocity already? 191 | glm::vec4 wish_velocity = 192 | entity_->velocity_ + (accel - 6.0f * entity_->velocity_) * dt; 193 | 194 | // set boolean for jump key reset 195 | if (g_cmds.up_move > 0 && on_ground) { 196 | wish_velocity.z = JUMP_VELOCITY - 250; 197 | ground_plane = glm::vec4(); 198 | } 199 | 200 | wish_velocity.z = (wish_velocity.z + (wish_velocity.z - 800 * dt)) * 0.5; 201 | 202 | if (wish_velocity.x == 0.0 && wish_velocity.y == 0.0) { 203 | return; 204 | } 205 | 206 | ClipVelocity(wish_velocity, wish_velocity, ground_plane); 207 | 208 | glm::vec4 wish_velocity_2 = wish_velocity; 209 | 210 | logger::Debug("wish velocity before clip: %f %f %f %f", wish_velocity.x, 211 | wish_velocity.y, wish_velocity.z, wish_velocity.w); 212 | 213 | glm::vec4 wish_position; 214 | 215 | auto bump_count = 0u; 216 | for (; bump_count < 4; ++bump_count) { 217 | wish_position = entity_->position_ + wish_velocity; 218 | 219 | fraction = world.map_->trace(entity_->position_, wish_position, trace); 220 | 221 | logger::Debug("::: trace result ::: %d %d", trace.starts_out, 222 | trace.all_solid); 223 | 224 | if (trace.all_solid) { 225 | wish_velocity.z = 0.0; 226 | ++bump_count; 227 | break; 228 | } 229 | 230 | logger::Debug("fraction: %f", fraction); 231 | 232 | if (fraction == 1.0f) { 233 | break; 234 | } 235 | 236 | ClipVelocity(wish_velocity, wish_velocity, trace.plane); 237 | logger::Debug("wish velocity after clip: %f %f %f %f", wish_velocity.x, 238 | wish_velocity.y, wish_velocity.z, wish_velocity.w); 239 | } 240 | 241 | if (!bump_count) { 242 | entity_->velocity_ = wish_velocity; 243 | entity_->position_ = entity_->position_ + entity_->velocity_; 244 | return; 245 | } 246 | 247 | if (wish_velocity.z > 0.0) { 248 | logger::Debug("+++++++++++++ WE HAVE UPWARDS VELOCITY +++++++++++++"); 249 | } 250 | 251 | { 252 | 253 | auto down = start; 254 | down.z -= STEPSIZE; 255 | 256 | glm::vec4 up(0.0, 0.0, 1.0, 0.0); 257 | 258 | fraction = world.map_->trace(start, down, trace); 259 | 260 | auto mydot = glm::dot(trace.plane, up); 261 | 262 | logger::Debug("+++++++++++++ SHOULD WE LEAVE? %f %f +++++++++++++", 263 | fraction, mydot); 264 | 265 | if (wish_velocity.z > 0.0 && (fraction == 1.0 || mydot < 0.7)) { 266 | logger::Debug("+++++++++++++ LEAVE NOW +++++++++++++"); 267 | return; 268 | } 269 | } 270 | 271 | if (bump_count > 0) { 272 | // try stepping up 273 | logger::Debug("============ stepping up ============="); 274 | // trace up to see if there is a ceiling 275 | // if so we have to limit upwards motion until the ceiling 276 | // for now just pretend we can always go up a step 277 | entity_->position_.z += STEPSIZE; 278 | 279 | auto bump_count = 0u; 280 | for (; bump_count < 4; ++bump_count) { 281 | wish_position = entity_->position_ + wish_velocity_2; 282 | 283 | logger::Debug("[111] tracing from %f %f %f to %f %f %f", 284 | entity_->position_.x, entity_->position_.y, 285 | entity_->position_.z, wish_position.x, wish_position.y, 286 | wish_position.z); 287 | fraction = world.map_->trace(entity_->position_, wish_position, trace); 288 | 289 | logger::Debug("::: trace result 2 ::: %d %d", trace.starts_out, 290 | trace.all_solid); 291 | 292 | if (trace.all_solid) { 293 | wish_velocity_2.z = 0.0; 294 | break; 295 | } 296 | 297 | logger::Debug("step plane: %f %f %f %f", trace.plane.x, trace.plane.y, 298 | trace.plane.z, trace.plane.w); 299 | logger::Debug("fraction: %f", fraction); 300 | 301 | if (fraction == 1.0f) { 302 | break; 303 | } 304 | 305 | ClipVelocity(wish_velocity_2, wish_velocity_2, trace.plane); 306 | logger::Debug("step wish velocity after clip: %f %f %f %f", 307 | wish_velocity_2.x, wish_velocity_2.y, wish_velocity_2.z, 308 | wish_velocity_2.w); 309 | } 310 | 311 | logger::Debug("step final wish velocity after clip: %f %f %f %f", 312 | wish_velocity_2.x, wish_velocity_2.y, wish_velocity_2.z, 313 | wish_velocity_2.w); 314 | 315 | // trace down from new position to find how high we really need to step 316 | // needed to avoid stepping up into the air 317 | entity_->velocity_ = wish_velocity_2; 318 | entity_->position_ = entity_->position_ + entity_->velocity_; 319 | 320 | glm::vec4 down = entity_->position_; 321 | down.z -= STEPSIZE; 322 | // TODO: trace should return the final position to make calculation easier 323 | // TODO: PRINT FRACTION why are they only 1 and 0 324 | logger::Debug("tracing from %f %f %f to %f %f %f", entity_->position_.x, 325 | entity_->position_.y, entity_->position_.z, down.x, down.y, 326 | down.z); 327 | 328 | fraction = world.map_->trace(entity_->position_, down, trace); 329 | 330 | logger::Debug("step down trace plane: %f %f %f %f", trace.plane.x, 331 | trace.plane.y, trace.plane.z, trace.plane.w); 332 | logger::Debug("step down trace fraction startout allsolid: %f %d %d", 333 | fraction, trace.starts_out, trace.all_solid); 334 | 335 | entity_->position_ = 336 | entity_->position_ + fraction * (down - entity_->position_); 337 | 338 | if (fraction < 1.0) { 339 | ClipVelocity(entity_->velocity_, entity_->velocity_, trace.plane); 340 | logger::Debug("step down trace velocity after clip: %f %f %f %f", 341 | entity_->velocity_.x, entity_->velocity_.y, 342 | entity_->velocity_.z, entity_->velocity_.w); 343 | } 344 | logger::Debug("++++ position after second slide move +++ %f %f %f %f +++", 345 | entity_->position_.x, entity_->position_.y, 346 | entity_->position_.z, entity_->position_.w); 347 | 348 | // exit(-1); 349 | } 350 | } 351 | } 352 | 353 | // int NoClipMove() 354 | //{ 355 | //} 356 | // 357 | // int GroundMove() 358 | //{ 359 | //} 360 | 361 | // int GroundTrace() 362 | //{ 363 | //// trace to ground and get plane 364 | // glm::vec4 start = position_; 365 | // glm::vec4 ground_plane; 366 | // glm::vec4 ground = start; 367 | // ground.z -= 10.0f - 0.25f; 368 | 369 | // float ground_fraction = world.map_->trace(start, ground, &ground_plane); 370 | 371 | // bool on_ground = false; 372 | // if (ground_fraction < 1.0f) // we hit ground 373 | //{ 374 | // on_ground = true; 375 | //} 376 | //} 377 | // 378 | // int SlideMove(glm::vec4 accel) 379 | //{ 380 | // accel.z += -9.8f; 381 | 382 | // glm::vec4 wish_position; 383 | // glm::vec4 plane; 384 | 385 | // unsigned int bump_count; 386 | // for (bump_count = 0; bump_count < 4; ++bump_count) 387 | //{ 388 | // wish_position = position_ + accel; 389 | // fraction = world.map_->trace(start, wish_position, &plane); 390 | // if (fraction == 1.0f) 391 | // { 392 | // break; 393 | // } 394 | 395 | // float distance = glm::dot(plane, accel); 396 | // accel = accel - plane * distance; 397 | 398 | // wish_position = position_ + accel; 399 | //} 400 | // 401 | // 402 | // int SlideMoveStep() 403 | //{ 404 | // int bumps = SlideMove(); 405 | // 406 | // if (bumps == 0) 407 | // { 408 | // return; 409 | // } 410 | // 411 | // // step up stepsize 412 | // // trace / slidemove 413 | //} 414 | -------------------------------------------------------------------------------- /src/player_physics_component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_PHYSICS_COMPONENT_HPP_ 2 | #define PLAYER_PHYSICS_COMPONENT_HPP_ 3 | 4 | #include "component.hpp" 5 | 6 | class Message; 7 | 8 | class PlayerPhysicsComponent : public Component 9 | { 10 | public: 11 | PlayerPhysicsComponent(); 12 | ~PlayerPhysicsComponent(); 13 | 14 | void ReceiveMessage(Message& message){}; 15 | void Update(unsigned int time); 16 | 17 | void UpdatePitch(); 18 | void UpdateYaw(); 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/q3_shader.cpp: -------------------------------------------------------------------------------- 1 | #include "q3_shader.hpp" 2 | 3 | #include 4 | #include 5 | 6 | Q3Shader::~Q3Shader(void) 7 | { 8 | } 9 | 10 | unsigned int Q3Shader::ParseShaderStage(const std::string* shaders, unsigned int offset) 11 | { 12 | Q3ShaderStage stage; 13 | 14 | unsigned int i = offset; 15 | for (; i < shaders->length(); ++i) 16 | { 17 | switch ((*shaders)[i]) 18 | { 19 | case '/': 20 | if ((*shaders)[i+1] == '/') 21 | { 22 | i = GetNewLinePosition(shaders, i); 23 | break; 24 | } 25 | break; 26 | case '{': break; 27 | case '}': 28 | stages_.push_back(stage); 29 | return i; 30 | case ' ': break; 31 | case 0x09: break; 32 | case 0x0A: break; 33 | case 0x0D: break; 34 | default: 35 | std::string token = GetToken(shaders, i); 36 | 37 | if (strcasecmp("map", token.c_str()) == 0) 38 | { 39 | token = GetToken(shaders, i); 40 | // maybe trim another way, just skip all spaces until next letter after GetToken 41 | stage.map = token; 42 | stage.map.erase(0, stage.map.find_first_not_of(' ')); 43 | 44 | if (stage.map =="$lightmap") 45 | { 46 | stage.isLightmap = true; 47 | } 48 | } 49 | else if (strcasecmp("clampmap", token.c_str()) == 0) 50 | { 51 | stage.clamp = true; 52 | token = GetToken(shaders, i); 53 | stage.map = token; 54 | stage.map.erase(0, stage.map.find_first_not_of(' ')); 55 | } 56 | else if (strcasecmp("blendfunc", token.c_str()) == 0) 57 | { 58 | token = GetToken(shaders, i); 59 | if (token.compare("add") == 0 || 60 | token.compare("ADD") == 0 || 61 | token.compare("Add") == 0 || 62 | token.compare("GL_add") == 0) 63 | { 64 | stage.blendfunc[0] = GetBlendFunc("GL_ONE"); 65 | stage.blendfunc[1] = GetBlendFunc("GL_ONE"); 66 | } 67 | else if (token.compare("filter") == 0) 68 | { 69 | stage.blendfunc[0] = GetBlendFunc("GL_DST_COLOR"); 70 | stage.blendfunc[1] = GetBlendFunc("GL_ONE"); 71 | } 72 | else if (token.compare("blend") == 0) 73 | { 74 | stage.blendfunc[0] = GetBlendFunc("GL_SRC_ALPHA"); 75 | stage.blendfunc[1] = GetBlendFunc("GL_ONE_MINUS_SRC_ALPHA"); 76 | } 77 | else 78 | { 79 | stage.blendfunc[0] = GetBlendFunc(token); 80 | token = GetToken(shaders, i); 81 | stage.blendfunc[1] = GetBlendFunc(token); 82 | } 83 | } 84 | else if (strcasecmp("alphafunc", token.c_str()) == 0) 85 | { 86 | token = GetToken(shaders, i); 87 | stage.alphafunc = GetAlphaFunc(token); 88 | } 89 | else if (strcasecmp("rgbGen", token.c_str()) == 0) 90 | { 91 | token = GetToken(shaders, i); 92 | if (strcasecmp("identity", token.c_str()) == 0) 93 | { 94 | stage.rgbgen = RGBGEN::IDENTITY; 95 | } 96 | else if (strcasecmp("vertex", token.c_str()) == 0) 97 | { 98 | stage.rgbgen = RGBGEN::VERTEX; 99 | } 100 | else if (strcasecmp("wave", token.c_str()) == 0) 101 | { 102 | stage.rgbgen = RGBGEN::WAVE; 103 | 104 | token = GetToken(shaders, i); 105 | stage.rgbwave.type = GetWaveFunc(token); 106 | 107 | token = GetToken(shaders, i); 108 | stage.rgbwave.base = atof(token.c_str()); 109 | 110 | token = GetToken(shaders, i); 111 | stage.rgbwave.amplitude = atof(token.c_str()); 112 | 113 | token = GetToken(shaders, i); 114 | stage.rgbwave.phase = atof(token.c_str()); 115 | 116 | token = GetToken(shaders, i); 117 | stage.rgbwave.frequency = atof(token.c_str()); 118 | } 119 | } 120 | else if (strcasecmp("tcmod", token.c_str()) == 0) 121 | { 122 | token = GetToken(shaders, i); 123 | if (strcasecmp("scroll", token.c_str()) == 0) 124 | { 125 | stage.texmods[stage.num_texmods].type = TCMOD::SCROLL; 126 | 127 | token = GetToken(shaders, i); 128 | stage.texmods[stage.num_texmods].scroll[0] = atof(token.c_str()); 129 | 130 | token = GetToken(shaders, i); 131 | stage.texmods[stage.num_texmods].scroll[1] = atof(token.c_str()); 132 | } 133 | else if (strcasecmp("scale", token.c_str()) == 0) 134 | { 135 | stage.texmods[stage.num_texmods].type = TCMOD::SCALE; 136 | 137 | token = GetToken(shaders, i); 138 | stage.texmods[stage.num_texmods].scale[0] = atof(token.c_str()); 139 | 140 | token = GetToken(shaders, i); 141 | stage.texmods[stage.num_texmods].scale[1] = atof(token.c_str()); 142 | } 143 | else if (strcasecmp("turb", token.c_str()) == 0) 144 | { 145 | stage.texmods[stage.num_texmods].type = TCMOD::TURB; 146 | 147 | token = GetToken(shaders, i);; 148 | stage.texmods[stage.num_texmods].wave.base = atof(token.c_str()); 149 | 150 | token = GetToken(shaders, i); 151 | stage.texmods[stage.num_texmods].wave.amplitude = atof(token.c_str()); 152 | 153 | token = GetToken(shaders, i); 154 | stage.texmods[stage.num_texmods].wave.phase = atof(token.c_str()); 155 | 156 | token = GetToken(shaders, i); 157 | stage.texmods[stage.num_texmods].wave.frequency = atof(token.c_str()); 158 | } 159 | else if (strcasecmp("transform", token.c_str()) == 0) 160 | { 161 | stage.texmods[stage.num_texmods].type = TCMOD::TRANSFORM; 162 | 163 | token = GetToken(shaders, i); 164 | stage.texmods[stage.num_texmods].matrix[0][0] = atof(token.c_str()); 165 | 166 | token = GetToken(shaders, i); 167 | stage.texmods[stage.num_texmods].matrix[0][1] = atof(token.c_str()); 168 | 169 | token = GetToken(shaders, i); 170 | stage.texmods[stage.num_texmods].matrix[1][0] = atof(token.c_str()); 171 | 172 | token = GetToken(shaders, i); 173 | stage.texmods[stage.num_texmods].matrix[1][1] = atof(token.c_str()); 174 | 175 | token = GetToken(shaders, i); 176 | stage.texmods[stage.num_texmods].translate[0] = atof(token.c_str()); 177 | 178 | token = GetToken(shaders, i); 179 | stage.texmods[stage.num_texmods].translate[1] = atof(token.c_str()); 180 | 181 | } 182 | else if (strcasecmp("stretch", token.c_str()) == 0) 183 | { 184 | stage.texmods[stage.num_texmods].type = TCMOD::STRETCH; 185 | 186 | token = GetToken(shaders, i); 187 | stage.texmods[stage.num_texmods].wave.type = GetWaveFunc(token); 188 | 189 | token = GetToken(shaders, i); 190 | stage.texmods[stage.num_texmods].wave.base = atof(token.c_str()); 191 | 192 | token = GetToken(shaders, i); 193 | stage.texmods[stage.num_texmods].wave.amplitude = atof(token.c_str()); 194 | 195 | token = GetToken(shaders, i); 196 | stage.texmods[stage.num_texmods].wave.phase = atof(token.c_str()); 197 | 198 | token = GetToken(shaders, i); 199 | stage.texmods[stage.num_texmods].wave.frequency = atof(token.c_str()); 200 | } 201 | else if (strcasecmp("rotate", token.c_str()) == 0) 202 | { 203 | stage.texmods[stage.num_texmods].type = TCMOD::ROTATE; 204 | 205 | token = GetToken(shaders, i); 206 | stage.texmods[stage.num_texmods].rotate_speed = atof(token.c_str()); 207 | } 208 | stage.num_texmods++; 209 | } 210 | } 211 | } 212 | return i; 213 | } 214 | 215 | // make the buffer and offset a class members 216 | std::string Q3Shader::GetToken(const std::string* buffer, unsigned int& offset) 217 | { 218 | while ((*buffer)[offset] == ' ') ++offset; // skip spaces so that they wont be recognized as token end 219 | // could call token until its not empty. 220 | 221 | int end_pos = GetTokenEndPosition(buffer, offset); 222 | std::string token = buffer->substr(offset, end_pos-offset); 223 | offset = ++end_pos; 224 | return token; 225 | } 226 | 227 | unsigned int Q3Shader::GetTokenEndPosition(const std::string* buffer, unsigned int offset) 228 | { 229 | while (1) // while buffer[offset] != nullptr 230 | { 231 | switch ((*buffer)[offset]) 232 | { 233 | case ' ': 234 | return offset; 235 | case 0x09: 236 | return offset; 237 | case 0x0A: 238 | return offset; 239 | case 0x0D: 240 | return offset; 241 | } 242 | ++offset; 243 | } 244 | } 245 | 246 | unsigned int Q3Shader::GetNewLinePosition(const std::string* buffer, unsigned int offset) 247 | { 248 | while (1) 249 | { 250 | switch ((*buffer)[offset]) 251 | { 252 | case 0x0A: 253 | return offset; 254 | case 0x0D: 255 | return offset; 256 | } 257 | ++offset; 258 | } 259 | } 260 | 261 | WAVEFUNC Q3Shader::GetWaveFunc(std::string name) 262 | { 263 | if (strcasecmp("sin", name.c_str()) == 0) 264 | { 265 | return WAVEFUNC::SIN; 266 | } 267 | else if (strcasecmp("triangle", name.c_str()) == 0) 268 | { 269 | return WAVEFUNC::TRIANGLE; 270 | } 271 | else if (strcasecmp("square", name.c_str()) == 0) 272 | { 273 | return WAVEFUNC::SQUARE; 274 | } 275 | else if (strcasecmp("sawtooth", name.c_str()) == 0) 276 | { 277 | return WAVEFUNC::SAWTOOTH; 278 | } 279 | else if (strcasecmp("inversesawtooth", name.c_str()) == 0) 280 | { 281 | return WAVEFUNC::INVERSESAWTOOTH; 282 | } 283 | } 284 | 285 | int Q3Shader::GetBlendFunc(std::string name) 286 | { 287 | if (strcasecmp("GL_SRC_ALPHA", name.c_str()) == 0) 288 | { 289 | return GL_SRC_ALPHA; 290 | } 291 | else if (strcasecmp("GL_ONE_MINUS_SRC_ALPHA", name.c_str()) == 0) 292 | { 293 | return GL_ONE_MINUS_SRC_ALPHA; 294 | } 295 | else if (strcasecmp("GL_ONE", name.c_str()) == 0) 296 | { 297 | return GL_ONE; 298 | } 299 | else if (strcasecmp("GL_ZERO", name.c_str()) == 0) 300 | { 301 | return GL_ZERO; 302 | } 303 | else if (strcasecmp("GL_DST_COLOR", name.c_str()) == 0) 304 | { 305 | return GL_DST_COLOR; 306 | } 307 | else if (strcasecmp("GL_ONE_MINUS_DST_COLOR", name.c_str()) == 0) 308 | { 309 | return GL_ONE_MINUS_DST_COLOR; 310 | } 311 | else if (strcasecmp("GL_ONE_MINUS_SRC_COLOR", name.c_str()) == 0) 312 | { 313 | return GL_ONE_MINUS_SRC_COLOR; 314 | } 315 | else if (strcasecmp("GL_ONE_MINUS_DST_ALPHA", name.c_str()) == 0) 316 | { 317 | return GL_ONE_MINUS_DST_ALPHA; 318 | } 319 | else if (strcasecmp("GL_SRC_COLOR", name.c_str()) == 0) 320 | { 321 | return GL_SRC_COLOR; 322 | } 323 | 324 | return -1; 325 | } 326 | 327 | int Q3Shader::GetAlphaFunc(std::string name) 328 | { 329 | if (strcasecmp("GT0", name.c_str()) == 0) 330 | { 331 | return GL_GREATER; 332 | } 333 | else if (strcasecmp("LT128", name.c_str()) == 0) 334 | { 335 | return GL_LESS; 336 | } 337 | else if (strcasecmp("GE128", name.c_str()) == 0) 338 | { 339 | return GL_GEQUAL; 340 | } 341 | } 342 | 343 | std::string Q3Shader::BlendFuncToString(int blend_func) 344 | { 345 | switch (blend_func) 346 | { 347 | case GL_SRC_ALPHA: 348 | return std::string("GL_SRC_ALPHA"); 349 | break; 350 | case GL_ONE_MINUS_SRC_ALPHA: 351 | return std::string("GL_ONE_MINUS_SRC_ALPHA"); 352 | break; 353 | case GL_ONE: 354 | return std::string("GL_ONE"); 355 | break; 356 | case GL_ZERO: 357 | return std::string("GL_ZERO"); 358 | break; 359 | case GL_DST_COLOR: 360 | return std::string("GL_DST_COLOR"); 361 | break; 362 | case GL_ONE_MINUS_DST_COLOR: 363 | return std::string("GL_ONE_MINUS_DST_COLOR"); 364 | break; 365 | case GL_ONE_MINUS_SRC_COLOR: 366 | return std::string("GL_ONE_MINUS_SRC_COLOR"); 367 | break; 368 | case GL_ONE_MINUS_DST_ALPHA: 369 | return std::string("GL_ONE_MINUS_DST_ALPHA"); 370 | break; 371 | case GL_SRC_COLOR: 372 | return std::string("GL_SRC_COLOR"); 373 | break; 374 | default: 375 | return std::string("Unknown Blend Function"); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /src/q3_shader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Q3_SHADER_HPP_ 2 | #define Q3_SHADER_HPP_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define MAX_TEXMODS 4 10 | 11 | enum class WAVEFUNC 12 | { 13 | NONE, 14 | SIN, 15 | TRIANGLE, 16 | SQUARE, 17 | SAWTOOTH, 18 | INVERSESAWTOOTH 19 | }; 20 | 21 | enum class ALPHAFUNC 22 | { 23 | NONE, 24 | GREATER, 25 | LESS, 26 | GEQUAL 27 | }; 28 | 29 | enum class RGBGEN 30 | { 31 | WAVE, 32 | IDENTITY, 33 | VERTEX, 34 | EXACTVERTEX 35 | }; 36 | 37 | enum class TCMOD 38 | { 39 | NONE, 40 | SCROLL, 41 | SCALE, 42 | TURB, 43 | TRANSFORM, 44 | STRETCH, 45 | ROTATE 46 | }; 47 | 48 | struct WaveForm 49 | { 50 | WAVEFUNC type; 51 | float base; 52 | float amplitude; 53 | float phase; 54 | float frequency; 55 | }; 56 | 57 | struct TexMod 58 | { 59 | TCMOD type; 60 | 61 | WaveForm wave; 62 | 63 | float scale[2]; 64 | float scroll[2]; 65 | 66 | float matrix[2][2]; 67 | float translate[2]; 68 | 69 | float rotate_speed; 70 | 71 | TexMod() 72 | { 73 | type = TCMOD::NONE; 74 | }; 75 | }; 76 | 77 | struct Q3ShaderStage { 78 | unsigned int texture; 79 | bool isLightmap; 80 | std::string map; 81 | int blendfunc[2]; 82 | int alphafunc; 83 | bool depthwrite; 84 | bool clamp; 85 | 86 | RGBGEN rgbgen; 87 | WaveForm rgbwave; 88 | 89 | int num_texmods; 90 | TexMod texmods[MAX_TEXMODS]; 91 | 92 | Q3ShaderStage() 93 | { 94 | texture = 0; 95 | isLightmap = false; 96 | blendfunc[0] = GL_ONE; 97 | blendfunc[1] = GL_ZERO; 98 | alphafunc = 0; 99 | depthwrite = false; 100 | rgbgen = RGBGEN::IDENTITY; 101 | num_texmods = 0; 102 | clamp = false; 103 | }; 104 | }; 105 | 106 | class Q3Shader 107 | { 108 | public: 109 | Q3Shader(const std::string name) : translucent_(false), name_(name) {}; 110 | ~Q3Shader(void); 111 | 112 | void ParseShader(); 113 | unsigned int ParseShaderStage(const std::string* shaders, unsigned int offset); 114 | 115 | int GetAlphaFunc(std::string name); 116 | int GetBlendFunc(std::string name); 117 | WAVEFUNC GetWaveFunc(std::string name); 118 | 119 | std::string BlendFuncToString(int blend_func); 120 | 121 | std::string GetToken(const std::string* buffer, unsigned int& offset); 122 | 123 | static unsigned int GetNewLinePosition(const std::string* buffer, unsigned int offset); 124 | unsigned int GetTokenEndPosition(const std::string* buffer, unsigned int offset); 125 | 126 | // making this objects instead of pointers we have some additional copying during load time 127 | // but don't need to worry about freeing the memory 128 | std::vector stages_; 129 | 130 | bool translucent_; 131 | 132 | std::string name_; 133 | }; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/renderer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENDERER_HPP_ 2 | #define RENDERER_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #define WIDTH 1280 9 | #define HEIGHT 720 10 | 11 | class Shader; 12 | class Bsp; 13 | class Entity; 14 | struct bsp_face; 15 | 16 | static const glm::mat4 quake2ogl(0.0f, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); 17 | 18 | class Renderer 19 | { 20 | public: 21 | void RenderModel(); 22 | 23 | Renderer(void); 24 | ~Renderer(void); 25 | 26 | void Initialize(); 27 | 28 | void SetupFrame(); 29 | 30 | void Setup2DRendering(); 31 | void Setup3DRendering(); 32 | 33 | void SetupBuffers(); 34 | 35 | void RenderFrame(float time); 36 | void RenderFace(bsp_face* face); 37 | void RenderPolygon(bsp_face* face); 38 | void RenderPatch(bsp_face* face); 39 | void RenderBillboard(); 40 | 41 | //void AddRenderable(/* type */); 42 | void AddRenderables(std::vector renderables); 43 | 44 | void SetupShader(Shader* shader, int lm_index); 45 | void FinishShader(); 46 | 47 | // utilities to reduce state changes 48 | void Blend(bool enable); 49 | void TexEnvMode(unsigned int texture_unit, unsigned int new_mode); 50 | void BindTexture(unsigned int texture_unit, unsigned int new_texture); 51 | void BlendFunc(unsigned int new_blend_func0, unsigned int new_blend_func1); 52 | void AlphaFunc(unsigned int new_alpha_func, unsigned int new_alpha_val); 53 | void ActiveTexture(unsigned int new_active_texture); 54 | 55 | glm::mat4 GetCameraMatrixFromEntity(Entity& entity); 56 | 57 | private: 58 | bool blend_enabled_; 59 | unsigned int tex_env_mode_[8]; 60 | unsigned int texture_[8]; 61 | unsigned int blend_func_[2]; 62 | unsigned int alpha_func_; 63 | int active_texture_; // = -1; 64 | 65 | Shader* current_shader_; 66 | int current_lightmap_; // = -1; 67 | 68 | unsigned int num_skipped_shaders_; 69 | 70 | int screen_width_; 71 | int screen_height_; 72 | 73 | glm::mat4 modelmatrix_; 74 | glm::mat4 projectionmatrix_; 75 | glm::mat4 orthomatrix_; 76 | 77 | float time_; 78 | 79 | Bsp* bsp_; 80 | 81 | std::vector renderables_; 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/shader.cpp: -------------------------------------------------------------------------------- 1 | #include "shader.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "logger.hpp" 7 | #include "q3_shader.hpp" 8 | #include "texture_loader.hpp" 9 | 10 | Shader::~Shader(void) {} 11 | 12 | int Shader::SetupTextures() { 13 | logger::Log(logger::DEFAULT, 14 | "Shader (%i stages) for texture found. Loading texture...", 15 | q3_shader_.stages_.size()); 16 | 17 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 18 | const Q3ShaderStage &stage = q3_shader_.stages_[i]; 19 | if (stage.isLightmap) { 20 | lightmap_stage_ = i; 21 | } else { 22 | if (stage.map.compare("$whiteimage") == 0) { 23 | continue; 24 | } 25 | 26 | logger::Log(logger::DEFAULT, "%s", stage.map.c_str()); 27 | int ret = textureLoader::GetTexture(stage.map, stage.clamp); 28 | if (ret == -1) { 29 | return -1; 30 | } else { 31 | texture_id_[i] = ret; 32 | } 33 | } 34 | } 35 | return 0; 36 | } 37 | 38 | void Shader::CompileShader() { 39 | if (q3_shader_.stages_.size() == 0) 40 | return; 41 | 42 | if (SetupTextures() == -1) { 43 | logger::Log(logger::ERROR, "Could not load all textures for shader (%s)", 44 | q3_shader_.name_.c_str()); 45 | return; 46 | } 47 | 48 | CompileVertexShader(); 49 | CompileFragmentShader(); 50 | CompileTesselationShader(); 51 | 52 | std::string name = q3_shader_.name_; 53 | 54 | std::replace(name.begin(), name.end(), '/', '-'); 55 | 56 | std::ofstream ofs; 57 | 58 | std::string fname = "shaders/" + name + ".vert"; 59 | ofs.open(fname.c_str()); 60 | ofs << vertex_shader_.str(); 61 | ofs.close(); 62 | 63 | fname = "shaders/" + name + ".frag"; 64 | ofs.open(fname.c_str()); 65 | ofs << fragment_shader_.str(); 66 | ofs.close(); 67 | 68 | fname = "shaders/" + name + ".tess"; 69 | ofs.open(fname.c_str()); 70 | ofs << tesselation_shader_.str(); 71 | ofs.close(); 72 | 73 | std::vector shaders; 74 | shaders.push_back(CreateShader(GL_VERTEX_SHADER, vertex_shader_.str())); 75 | shaders.push_back(CreateShader(GL_FRAGMENT_SHADER, fragment_shader_.str())); 76 | 77 | shaders.push_back( 78 | CreateShader(GL_TESS_EVALUATION_SHADER, tesselation_shader_.str())); 79 | 80 | shader_ = CreateProgram(shaders); 81 | 82 | texture_idx_[0] = glGetUniformLocation(shader_, "texture0"); 83 | texture_idx_[1] = glGetUniformLocation(shader_, "texture1"); 84 | texture_idx_[2] = glGetUniformLocation(shader_, "texture2"); 85 | texture_idx_[3] = glGetUniformLocation(shader_, "texture3"); 86 | texture_idx_[4] = glGetUniformLocation(shader_, "texture4"); 87 | texture_idx_[5] = glGetUniformLocation(shader_, "texture5"); 88 | texture_idx_[6] = glGetUniformLocation(shader_, "texture6"); 89 | texture_idx_[7] = glGetUniformLocation(shader_, "texture7"); 90 | 91 | patchWidth_ = glGetUniformLocation(shader_, "patchWidth"); 92 | patchHeight_ = glGetUniformLocation(shader_, "patchHeight"); 93 | 94 | projection_idx_ = glGetUniformLocation(shader_, "inProjectionMatrix"); 95 | model_idx_ = glGetUniformLocation(shader_, "inModelMatrix"); 96 | time_idx_ = glGetUniformLocation(shader_, "inTime"); 97 | 98 | position_idx_ = glGetAttribLocation(shader_, "inPosition"); 99 | glEnableVertexAttribArray(position_idx_); 100 | tex_coord_idx_ = glGetAttribLocation(shader_, "inTexCoord"); 101 | glEnableVertexAttribArray(tex_coord_idx_); 102 | lm_coord_idx_ = glGetAttribLocation(shader_, "inLmCoord"); 103 | glEnableVertexAttribArray(lm_coord_idx_); 104 | color_idx_ = glGetAttribLocation(shader_, "inColor"); 105 | glEnableVertexAttribArray(color_idx_); 106 | 107 | glUseProgram(shader_); 108 | for (int i = 0; i < 8; ++i) { 109 | glUniform1i(texture_idx_[i], i); 110 | } 111 | glUseProgram(0); 112 | 113 | compiled_ = true; 114 | } 115 | 116 | void Shader::CompileVertexShader() { 117 | vertex_shader_ << "#version 410\n" 118 | << "uniform mat4 inProjectionMatrix;\n" 119 | << "uniform mat4 inModelMatrix;\n" 120 | << "uniform float inTime;\n" 121 | << "layout(location = 0) in vec4 inPosition;\n" 122 | << "layout(location = 1) in vec2 inTexCoord;\n" 123 | << "layout(location = 2) in vec2 inLmCoord;\n" 124 | << "layout(location = 3) in vec4 inColor;\n" 125 | << "layout(location = 4) out vec2 fLmCoord;\n" 126 | << "layout(location = 5) out vec4 fColor;\n"; 127 | 128 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 129 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) 130 | continue; 131 | vertex_shader_ << "layout(location = " << 6 + i << ") out vec2 outTexCoord" 132 | << i << ";\n"; 133 | } 134 | 135 | vertex_shader_ 136 | << "void main() {\n" 137 | << "\tfLmCoord = inLmCoord;\n" 138 | << "\tfColor = inColor;\n" 139 | << "\tgl_Position = inProjectionMatrix * inModelMatrix * inPosition;\n"; 140 | 141 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 142 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) 143 | continue; 144 | 145 | vertex_shader_ << "\toutTexCoord" << i << " = inTexCoord;\n"; 146 | } 147 | 148 | vertex_shader_ << "\n\t// texture coordinate modifications\n"; 149 | vertex_shader_ << "\tfloat sinval;\n"; 150 | vertex_shader_ << "\tfloat cosval;\n"; 151 | vertex_shader_ << "\tfloat s;\n"; 152 | vertex_shader_ << "\tfloat t;\n"; 153 | vertex_shader_ << "\tfloat stretch;\n"; 154 | 155 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 156 | for (int j = 0; j < MAX_TEXMODS; ++j) { 157 | if (q3_shader_.stages_[i].texmods[j].type == TCMOD::SCALE) { 158 | vertex_shader_ << "\toutTexCoord" << i 159 | << ".s *= " << q3_shader_.stages_[i].texmods[j].scale[0] 160 | << ";\n"; 161 | vertex_shader_ << "\toutTexCoord" << i 162 | << ".t *= " << q3_shader_.stages_[i].texmods[j].scale[1] 163 | << ";\n"; 164 | } 165 | 166 | if (q3_shader_.stages_[i].texmods[j].type == TCMOD::SCROLL) { 167 | vertex_shader_ << "\toutTexCoord" << i << ".s += inTime * " 168 | << q3_shader_.stages_[i].texmods[j].scroll[0] 169 | << " - floor(inTime * " 170 | << q3_shader_.stages_[i].texmods[j].scroll[0] << ")" 171 | << ";\n"; 172 | vertex_shader_ << "\toutTexCoord" << i << ".t += inTime * " 173 | << q3_shader_.stages_[i].texmods[j].scroll[1] 174 | << " - floor(inTime * " 175 | << q3_shader_.stages_[i].texmods[j].scroll[1] << ")" 176 | << ";\n"; 177 | } 178 | 179 | if (q3_shader_.stages_[i].texmods[j].type == TCMOD::ROTATE) { 180 | vertex_shader_ << "sinval = sin(radians(inTime * " 181 | << q3_shader_.stages_[i].texmods[j].rotate_speed 182 | << "));\n"; 183 | vertex_shader_ << "cosval = cos(radians(inTime * " 184 | << q3_shader_.stages_[i].texmods[j].rotate_speed 185 | << "));\n"; 186 | 187 | vertex_shader_ << "s = outTexCoord" << i << ".s;\n"; 188 | vertex_shader_ << "t = outTexCoord" << i << ".t;\n"; 189 | 190 | vertex_shader_ << "\toutTexCoord" << i 191 | << ".s = s * cosval + t * -sinval + (0.5 - 0.5 * cosval " 192 | "+ 0.5 * sinval)" 193 | << ";\n"; 194 | vertex_shader_ << "\toutTexCoord" << i 195 | << ".t = s * sinval + t * cosval + (0.5 - 0.5 * sinval " 196 | "- 0.5 * cosval)" 197 | << ";\n"; 198 | } 199 | 200 | if (q3_shader_.stages_[i].texmods[j].type == TCMOD::STRETCH) { 201 | vertex_shader_ << "sinval = " 202 | << q3_shader_.stages_[i].texmods[j].wave.amplitude 203 | << " * " 204 | << "sin(" 205 | << q3_shader_.stages_[i].texmods[j].wave.frequency 206 | << " * inTime + " 207 | << q3_shader_.stages_[i].texmods[j].wave.phase << ") + " 208 | << q3_shader_.stages_[i].texmods[j].wave.base << ";\n"; 209 | 210 | vertex_shader_ << "stretch = 1.0 / sinval;\n"; 211 | vertex_shader_ << "s = outTexCoord" << i << ".s;\n"; 212 | vertex_shader_ << "t = outTexCoord" << i << ".t;\n"; 213 | 214 | vertex_shader_ << "\toutTexCoord" << i 215 | << ".s = s * stretch + t * 0 + (0.5 - 0.5 * stretch)" 216 | << ";\n"; 217 | vertex_shader_ << "\toutTexCoord" << i 218 | << ".t = s * 0 + t * stretch + (0.5 - 0.5 * stretch)" 219 | << ";\n"; 220 | } 221 | 222 | if (q3_shader_.stages_[i].texmods[j].type == TCMOD::TRANSFORM) { 223 | vertex_shader_ << "s = outTexCoord" << i << ".s;\n"; 224 | vertex_shader_ << "t = outTexCoord" << i << ".t;\n"; 225 | 226 | vertex_shader_ << "\toutTexCoord" << i << ".s = s * " 227 | << q3_shader_.stages_[i].texmods[j].matrix[0][0] 228 | << " + t * " 229 | << q3_shader_.stages_[i].texmods[j].matrix[1][0] << " + " 230 | << q3_shader_.stages_[i].texmods[j].translate[0] 231 | << ";\n"; 232 | vertex_shader_ << "\toutTexCoord" << i << ".t = s * " 233 | << q3_shader_.stages_[i].texmods[j].matrix[0][1] 234 | << " + t * " 235 | << q3_shader_.stages_[i].texmods[j].matrix[1][1] << " + " 236 | << q3_shader_.stages_[i].texmods[j].translate[1] 237 | << ";\n"; 238 | } 239 | 240 | if (q3_shader_.stages_[i].texmods[j].type == TCMOD::TURB) { 241 | vertex_shader_ << "sinval = sin(inPosition.x*inPosition.z* (1.0/128) * " 242 | "0.125 + inTime * " 243 | << q3_shader_.stages_[i].texmods[j].wave.frequency 244 | << " + " << q3_shader_.stages_[i].texmods[j].wave.phase 245 | << ");\n"; 246 | 247 | vertex_shader_ 248 | << "cosval = sin(inPosition.y* (1.0/128) * 0.125 + inTime * " 249 | << q3_shader_.stages_[i].texmods[j].wave.frequency << " + " 250 | << q3_shader_.stages_[i].texmods[j].wave.phase << ");\n"; 251 | 252 | vertex_shader_ << "s = outTexCoord" << i << ".s;\n"; 253 | vertex_shader_ << "t = outTexCoord" << i << ".t;\n"; 254 | 255 | vertex_shader_ << "\toutTexCoord" << i << ".s = s + sinval * " 256 | << q3_shader_.stages_[i].texmods[j].wave.amplitude 257 | << ";\n"; 258 | vertex_shader_ << "\toutTexCoord" << i << ".t = t + cosval * " 259 | << q3_shader_.stages_[i].texmods[j].wave.amplitude 260 | << ";\n"; 261 | } 262 | } 263 | } 264 | vertex_shader_ << "}"; 265 | } 266 | /* 267 | void tess() { 268 | tesselation_shader_ 269 | << "void selectControlPoints4(in vec4 original[], out vec4 controls[], out 270 | " 271 | "float " 272 | "u, out float v) {" 273 | << "\tint numX = (patchWidth - 1) / 2;\n" 274 | << "int numY = (patchHeight - 1) / 2;\n" 275 | 276 | << "float thresholdX = 1.0f / numX;\n" 277 | << "float thresholdY = 1.0f / numY;\n" 278 | 279 | << "int activeX = std::floor(u / thresholdX);\n" 280 | << "int activeY = std::floor(v / thresholdY);\n" 281 | 282 | << "vec4 controls[9] = vec4[](original[(activeY * 2) * width + (activeX " 283 | "* 2)]," 284 | << "original[(activeY * 2) * width + (activeX * 2) + 1]," 285 | << "original[(activeY * 2) * width + (activeX * 2) + 2]," 286 | << "original[((activeY * 2) + 1) * width + (activeX * 2)]," 287 | << "original[((activeY * 2) + 1) * width + (activeX * 2) + 1]," 288 | << "original[((activeY * 2) + 1) * width + (activeX * 2) + 2]," 289 | << "original[((activeY * 2) + 2) * width + (activeX * 2)]," 290 | << "original[((activeY * 2) + 2) * width + (activeX * 2) + 1]," 291 | << "original[((activeY * 2) + 2) * width + (activeX * 2) + 2]);" 292 | 293 | << "// correct u,v for this patch" 294 | << "float correctU = mod(u, thresholdX);" 295 | << "float correctV = mod(v, thresholdY);" 296 | << "}" 297 | 298 | tesselation_shader_ 299 | << "selectControlPoints2(in vec2 original[], out vec2 controls[], out " 300 | "float " 301 | "u, out float v) {" 302 | << "int numX = (patchWidth - 1) / 2;\n" 303 | << "int numY = (patchHeight - 1) / 2;\n" 304 | 305 | << "float thresholdX = 1.0f / numX;\n" 306 | << "float thresholdY = 1.0f / numY;\n" 307 | 308 | << "int activeX = std::floor(u / thresholdX);\n" 309 | << "int activeY = std::floor(v / thresholdY);\n" 310 | 311 | << "vec4 controls[9] = vec4[](original[(activeY * 2) * width + (activeX " 312 | "* 2)]," 313 | << "original[(activeY * 2) * width + (activeX * 2) + 1]," 314 | << "original[(activeY * 2) * width + (activeX * 2) + 2]," 315 | << "original[((activeY * 2) + 1) * width + (activeX * 2)]," 316 | << "original[((activeY * 2) + 1) * width + (activeX * 2) + 1]," 317 | << "original[((activeY * 2) + 1) * width + (activeX * 2) + 2]," 318 | << "original[((activeY * 2) + 2) * width + (activeX * 2)]," 319 | << "original[((activeY * 2) + 2) * width + (activeX * 2) + 1]," 320 | << "original[((activeY * 2) + 2) * width + (activeX * 2) + 2]);" 321 | 322 | << "// correct u,v for this patch" 323 | << "float correctU = mod(u, thresholdX);" 324 | << "float correctV = mod(v, thresholdY);" 325 | << "}" 326 | } 327 | */ 328 | void Shader::CompileTesselationShader() { 329 | tesselation_shader_ << "#version 410\n" 330 | << "layout(quads, cw) in;\n" 331 | << "uniform mat4 inProjectionMatrix;\n" 332 | << "uniform mat4 inModelMatrix;\n" 333 | << "uniform uint patchWidth;\n" 334 | << "uniform uint patchHeight;\n" 335 | << "layout(location = 4) in vec2 fLmCoord[];\n" 336 | << "layout(location = 5) in vec4 fColor[];\n" 337 | << "layout(location = 4) out vec2 toutLmCoord;\n" 338 | << "layout(location = 5) out vec4 toutColor;\n"; 339 | 340 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 341 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) 342 | continue; 343 | 344 | tesselation_shader_ << "in vec2 outTexCoord" << i << "[];\n"; 345 | } 346 | 347 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 348 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) 349 | continue; 350 | tesselation_shader_ << "layout(location = " << 6 + i 351 | << ") out vec2 toutTexCoord" << i << ";\n"; 352 | } 353 | 354 | tesselation_shader_ 355 | << "\nvoid selectControlPoints4(in vec4 original[150], out vec4 " 356 | "controls[9], " 357 | "inout " 358 | "double " 359 | "u, inout double v) {\n" 360 | << "\tuint numX = (patchWidth - 1) / 2;\n" 361 | << "\tuint numY = (patchHeight - 1) / 2;\n\n" 362 | 363 | << "\tdouble thresholdX = 1.0 / numX;\n" 364 | << "\tdouble thresholdY = 1.0 / numY;\n\n" 365 | 366 | << "\tint activeX = int(min(floor(u / thresholdX), numX - 1.0));\n" 367 | << "\tint activeY = int(min(floor(v / thresholdY), numY - 1.0));\n\n" 368 | 369 | << "\t controls = vec4[](original[(activeY * 2) * patchWidth + " 370 | "(activeX * 2)],\n" 371 | << "\t\toriginal[(activeY * 2) * patchWidth + (activeX * 2) + 1],\n" 372 | << "\t\toriginal[(activeY * 2) * patchWidth + (activeX * 2) + 2],\n" 373 | << "\t\toriginal[((activeY * 2) + 1) * patchWidth + (activeX * 2)],\n" 374 | << "\t\toriginal[((activeY * 2) + 1) * patchWidth + (activeX * 2) + 1],\n" 375 | << "\t\toriginal[((activeY * 2) + 1) * patchWidth + (activeX * 2) + 2],\n" 376 | << "\t\toriginal[((activeY * 2) + 2) * patchWidth + (activeX * 2)],\n" 377 | << "\t\toriginal[((activeY * 2) + 2) * patchWidth + (activeX * 2) + 1],\n" 378 | << "\t\toriginal[((activeY * 2) + 2) * patchWidth + (activeX * 2) + " 379 | "2]);\n\n" 380 | 381 | << "\t// correct u,v for this patch\n" 382 | << "\tu = (1.0 / thresholdX) * (u - activeX * thresholdX);" 383 | << "\tv = (1.0 / thresholdY) * (v - activeY * thresholdY);" 384 | << "\n}\n"; 385 | 386 | tesselation_shader_ 387 | << "\nvoid selectControlPoints2(in vec2 original[150], out vec2 " 388 | "controls[9], " 389 | "inout " 390 | "float " 391 | "u, inout float v) {\n" 392 | << "\tuint numX = (patchWidth - 1) / 2;\n" 393 | << "\tuint numY = (patchHeight - 1) / 2;\n\n" 394 | 395 | << "\tfloat thresholdX = 1.0 / numX;\n" 396 | << "\tfloat thresholdY = 1.0 / numY;\n\n" 397 | 398 | << "\tint activeX = int(min(floor(u / thresholdX), numX - 1));\n" 399 | << "\tint activeY = int(min(floor(v / thresholdY), numY - 1));\n\n" 400 | 401 | << "\tcontrols = vec2[](original[(activeY * 2) * patchWidth + " 402 | "(activeX * 2)],\n" 403 | << "\t\toriginal[(activeY * 2) * patchWidth + (activeX * 2) + 1],\n" 404 | << "\t\toriginal[(activeY * 2) * patchWidth + (activeX * 2) + 2],\n" 405 | << "\t\toriginal[((activeY * 2) + 1) * patchWidth + (activeX * 2)],\n" 406 | << "\t\toriginal[((activeY * 2) + 1) * patchWidth + (activeX * 2) + 1],\n" 407 | << "\t\toriginal[((activeY * 2) + 1) * patchWidth + (activeX * 2) + 2],\n" 408 | << "\t\toriginal[((activeY * 2) + 2) * patchWidth + (activeX * 2)],\n" 409 | << "\t\toriginal[((activeY * 2) + 2) * patchWidth + (activeX * 2) + 1],\n" 410 | << "\t\toriginal[((activeY * 2) + 2) * patchWidth + (activeX * 2) + " 411 | "2]);\n\n" 412 | 413 | << "\t// correct u,v for this patch\n" 414 | << "\tu = (1.0 / thresholdX) * (u - activeX * thresholdX);" 415 | << "\tv = (1.0 / thresholdY) * (v - activeY * thresholdY);" 416 | << "\n}\n"; 417 | 418 | /* 419 | tesselation_shader_ << "vec4 bezier(in float t, in uint start) {\n"; 420 | tesselation_shader_ 421 | << "\treturn gl_in[start].gl_Position * (1 - t) * (1 - t) + " 422 | "(gl_in[start + 1].gl_Position) * 2 * t * (1 - t) + " 423 | "(gl_in[start + 2].gl_Position) * t * t;\n"; 424 | tesselation_shader_ << "}\n"; 425 | */ 426 | 427 | /* float newU = (1.0 / thresholdX) * (u - activeX * thresholdX); 428 | float newV = (1.0 / thresholdY) * (v - activeY * thresholdY); 429 | 430 | std::cout << "numX numY: " << numX << " " << numY 431 | << " threshX threshY: " << thresholdX << " " << thresholdY 432 | << " activeX activeY: " << activeX << " " << activeY << std::endl; 433 | 434 | std::cout << "newU newV: " << newU << " " << newV << std::endl; 435 | */ 436 | 437 | tesselation_shader_ << "vec2 bezier2(in float t, in vec2 controls[3]) {\n"; 438 | tesselation_shader_ << "\treturn controls[0] * (1 - t) * (1 - t) + " 439 | "controls[1] * 2 * t * (1 - t) + " 440 | "controls[2] * t * t;\n"; 441 | tesselation_shader_ << "}\n"; 442 | 443 | tesselation_shader_ << "vec4 bezier4(in float t, in vec4 controls[3]) {\n"; 444 | tesselation_shader_ << "\treturn controls[0] * (1 - t) * (1 - t) + " 445 | "controls[1] * 2 * t * (1 - t) + " 446 | "controls[2] * t * t;\n"; 447 | tesselation_shader_ << "}\n"; 448 | 449 | tesselation_shader_ << "void main() {\n"; 450 | 451 | tesselation_shader_ << "\tfloat u = gl_TessCoord.x;\n" 452 | << "\tfloat v = gl_TessCoord.y;\n"; 453 | tesselation_shader_ << "\tvec2 y;\n\tvec2 z;\n\tvec4 y4;\n\tvec4 z4;\n"; 454 | 455 | tesselation_shader_ << "\tvec2 control2[3];\n" 456 | << "\tvec2 tex1;\n" 457 | << "\tvec2 tex2;\n" 458 | << "\tvec2 tex3;\n"; 459 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 460 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) 461 | continue; 462 | 463 | tesselation_shader_ << "\tcontrol2 = vec2[3](outTexCoord" << i 464 | << "[0], outTexCoord" << i << "[1], outTexCoord" << i 465 | << "[2]);\n" 466 | << "\ttex1 = bezier2(u, control2);\n"; 467 | 468 | tesselation_shader_ << "\tcontrol2 = vec2[3](outTexCoord" << i 469 | << "[3], outTexCoord" << i << "[4], outTexCoord" << i 470 | << "[5]);\n" 471 | << "\ttex2 = bezier2(u, control2);\n"; 472 | 473 | tesselation_shader_ << "\tcontrol2 = vec2[3](outTexCoord" << i 474 | << "[6], outTexCoord" << i << "[7], outTexCoord" << i 475 | << "[8]);\n" 476 | << "\ttex3 = bezier2(u, control2);\n"; 477 | 478 | tesselation_shader_ << "\tcontrol2 = vec2[3](tex1, tex2, tex3);\n" 479 | << "\ttoutTexCoord" << i 480 | << " = bezier2(v, control2);\n"; 481 | } 482 | tesselation_shader_ << "\ty = mix(fLmCoord[0], fLmCoord[6], u);\n"; 483 | tesselation_shader_ << "\tz = mix(fLmCoord[2], fLmCoord[8], u);\n"; 484 | tesselation_shader_ << "\ttoutLmCoord = mix(y, z, v);\n"; 485 | 486 | tesselation_shader_ << "\tvec4 control4[3];\n"; 487 | tesselation_shader_ 488 | << "\tcontrol4 = vec4[3](fColor[0], fColor[1], fColor[2]);\n" 489 | << "\tvec4 color1 = bezier4(u, control4);\n"; 490 | tesselation_shader_ 491 | << "\tcontrol4 = vec4[3](fColor[3], fColor[4], fColor[5]);\n" 492 | << "\tvec4 color2 = bezier4(u, control4);\n"; 493 | tesselation_shader_ 494 | << "\tcontrol4 = vec4[3](fColor[6], fColor[7], fColor[8]);\n" 495 | << "\tvec4 color3 = bezier4(u, control4);\n"; 496 | tesselation_shader_ << "\tcontrol4 = vec4[3](color1, color2, color3);\n" 497 | << "\ttoutColor = bezier4(v, control4);\n"; 498 | 499 | tesselation_shader_ 500 | << "\tvec4 inputArr[150];\n" 501 | << "\tfor (int i = 0; i <= gl_PatchVerticesIn; i++) {\n" 502 | << "\t\tinputArr[i] = gl_in[i].gl_Position;\n" 503 | << "}\n" 504 | << "\tvec4 currentControl[9];\n" 505 | << "\tdouble nu = u; double nv = v;" 506 | << "\tselectControlPoints4(inputArr, currentControl, nu, nv);" 507 | << "u = float(nu); v = float(nv);"; 508 | 509 | tesselation_shader_ << "\tcontrol4 = vec4[3](currentControl[0], " 510 | "currentControl[1], " 511 | "currentControl[2]);\n"; 512 | tesselation_shader_ << "\tvec4 base1 = bezier4(u, control4);\n"; 513 | tesselation_shader_ << "\tcontrol4 = vec4[3](currentControl[3], " 514 | "currentControl[4], " 515 | "currentControl[5]);\n"; 516 | tesselation_shader_ << "\tvec4 base2 = bezier4(u, control4);\n"; 517 | tesselation_shader_ << "\tcontrol4 = vec4[3](currentControl[6], " 518 | "currentControl[7], " 519 | "currentControl[8]);\n"; 520 | 521 | /* 522 | tesselation_shader_ << "\tcontrol4 = vec4[3](gl_in[0].gl_Position, " 523 | "gl_in[1].gl_Position, " 524 | "gl_in[2].gl_Position);\n"; 525 | tesselation_shader_ << "\tvec4 base1 = bezier4(u, control4);\n"; 526 | tesselation_shader_ << "\tcontrol4 = vec4[3](gl_in[3].gl_Position, " 527 | "gl_in[4].gl_Position, " 528 | "gl_in[5].gl_Position);\n"; 529 | tesselation_shader_ << "\tvec4 base2 = bezier4(u, control4);\n"; 530 | tesselation_shader_ << "\tcontrol4 = vec4[3](gl_in[6].gl_Position, " 531 | "gl_in[7].gl_Position, " 532 | "gl_in[8].gl_Position);\n"; 533 | */ 534 | 535 | tesselation_shader_ << "\tvec4 base3 = bezier4(u, control4);\n"; 536 | 537 | tesselation_shader_ << "\tcontrol4 = vec4[3](base1, base2, base3);\n" 538 | << "\tvec4 pos = bezier4(v, control4);\n" 539 | << "\tgl_Position = pos;\n" 540 | << "}\n"; 541 | } 542 | 543 | void Shader::CompileFragmentShader() { 544 | fragment_shader_ << "#version 410\n"; 545 | 546 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 547 | fragment_shader_ << "uniform sampler2D texture" << i << "; //" 548 | << q3_shader_.stages_[i].map << "\n"; 549 | } 550 | 551 | fragment_shader_ << "uniform float inTime;\n"; 552 | fragment_shader_ << "layout(location = 4) in vec2 toutLmCoord;\n"; 553 | fragment_shader_ << "layout(location = 5) in vec4 fColor;\n"; 554 | 555 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 556 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) 557 | continue; 558 | 559 | fragment_shader_ << "layout(location = " << 6 + i 560 | << ") in vec2 toutTexCoord" << i << ";\n"; 561 | } 562 | 563 | fragment_shader_ << "layout(location = 0) out vec4 fragColor;\n"; 564 | 565 | fragment_shader_ << "void main() {\n"; 566 | 567 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 568 | if (!q3_shader_.stages_[i].map.compare("$lightmap")) { 569 | fragment_shader_ << "\tvec4 color" << i << " = texture(texture" << i 570 | << ", toutLmCoord.st);\n"; 571 | } else { 572 | fragment_shader_ << "\tvec4 color" << i << " = texture(texture" << i 573 | << ", toutTexCoord" << i << ".st);\n"; 574 | } 575 | } 576 | 577 | fragment_shader_ << "\tfloat sincolor;\n"; 578 | 579 | std::string dst; 580 | std::string src; 581 | 582 | for (unsigned int i = 0; i < q3_shader_.stages_.size(); ++i) { 583 | const Q3ShaderStage &stage = q3_shader_.stages_[i]; 584 | 585 | std::stringstream helper; 586 | helper << "color" << i; 587 | src = helper.str(); 588 | 589 | if (i == 0) { 590 | dst = "vec4(0.0, 0.0, 0.0, 0.0)"; 591 | } else { 592 | std::stringstream helper; 593 | helper << "color" << i - 1; 594 | dst = helper.str(); 595 | } 596 | 597 | // 0.5 equals 128 when normalized to 0-1 range 598 | if (stage.alphafunc == GL_GREATER) { 599 | fragment_shader_ << "\tif (" << src << ".a > 0) discard;\n"; 600 | } else if (stage.alphafunc == GL_LESS) { 601 | fragment_shader_ << "\tif (" << src << ".a > 0.5) discard;\n"; 602 | } else if (stage.alphafunc == GL_GEQUAL) { 603 | fragment_shader_ << "\tif (" << src << ".a <= 0.5) discard;\n"; 604 | } 605 | 606 | switch (stage.rgbgen) { 607 | case RGBGEN::IDENTITY: 608 | fragment_shader_ << "\t" << src << " *= "; 609 | fragment_shader_ << "vec4(1.0, 1.0, 1.0, 1.0);\n"; 610 | break; 611 | /*case RGBGEN::VERTEX: 612 | fragment_shader_ << "\t" << src << " *= "; 613 | fragment_shader_ << "outColor;\n"; 614 | break;*/ 615 | case RGBGEN::WAVE: 616 | fragment_shader_ << "\tsincolor = clamp(" 617 | << q3_shader_.stages_[i].rgbwave.amplitude << " * " 618 | << "sin(" << q3_shader_.stages_[i].rgbwave.frequency 619 | << " * inTime + " << q3_shader_.stages_[i].rgbwave.phase 620 | << ") + " << q3_shader_.stages_[i].rgbwave.base 621 | << ", 0.0, 1.0);\n"; 622 | 623 | fragment_shader_ << "\t" << src 624 | << " *= vec4(sincolor, sincolor, sincolor, 1.0);\n"; 625 | break; 626 | default: 627 | fragment_shader_ << "\t" << src << " *= "; 628 | fragment_shader_ << "vec4(1.0, 1.0, 1.0, 1.0);\n"; 629 | } 630 | 631 | fragment_shader_ << "\t" << src << " = (" << src << " * "; 632 | switch (stage.blendfunc[0]) { 633 | case GL_ONE: 634 | fragment_shader_ << "1"; 635 | break; 636 | case GL_ZERO: 637 | fragment_shader_ << "0"; 638 | break; 639 | case GL_DST_COLOR: 640 | fragment_shader_ << dst; 641 | break; 642 | case GL_SRC_COLOR: 643 | fragment_shader_ << src; 644 | break; 645 | case GL_DST_ALPHA: 646 | fragment_shader_ << dst << ".a"; 647 | break; 648 | case GL_SRC_ALPHA: 649 | fragment_shader_ << src << ".a"; 650 | break; 651 | case GL_ONE_MINUS_SRC_ALPHA: 652 | fragment_shader_ << "(1 - " << src << ".a)"; 653 | break; 654 | case GL_ONE_MINUS_DST_ALPHA: 655 | fragment_shader_ << "(1 - " << dst << ".a)"; 656 | break; 657 | default: 658 | logger::Log(logger::ERROR, "Unknown blendfunc: %s", stage.blendfunc[0]); 659 | } 660 | 661 | fragment_shader_ << ") + (" << dst << " * "; 662 | 663 | switch (stage.blendfunc[1]) { 664 | case GL_ONE: 665 | fragment_shader_ << "1"; 666 | break; 667 | case GL_ZERO: 668 | fragment_shader_ << "0"; 669 | break; 670 | case GL_DST_COLOR: 671 | fragment_shader_ << dst; 672 | break; 673 | case GL_SRC_COLOR: 674 | fragment_shader_ << src; 675 | break; 676 | case GL_DST_ALPHA: 677 | fragment_shader_ << dst << ".a"; 678 | break; 679 | case GL_SRC_ALPHA: 680 | fragment_shader_ << src << ".a"; 681 | break; 682 | case GL_ONE_MINUS_SRC_ALPHA: 683 | fragment_shader_ << "(1 - " << src << ".a)"; 684 | break; 685 | case GL_ONE_MINUS_DST_ALPHA: 686 | fragment_shader_ << "(1 - " << dst << ".a)"; 687 | break; 688 | default: 689 | logger::Log(logger::ERROR, "Unknown blendfunc: %s", stage.blendfunc[1]); 690 | } 691 | 692 | fragment_shader_ << ");\n"; 693 | } 694 | 695 | fragment_shader_ << "\t" 696 | << "fragColor = " << src << ";\n"; 697 | // fragment_shader_ << "\t" << "fragColor = fColor;\n"; 698 | fragment_shader_ << "}"; 699 | } 700 | 701 | // int Shader::CompileFontShader() 702 | //{ 703 | // SDL_Surface *image = IMG_Load(font.c_str()); 704 | // 705 | // GLenum texture_format; 706 | // GLint num_colors = image->format->BytesPerPixel; 707 | // 708 | // if (num_colors == 4) // contains an alpha channel 709 | // { 710 | // if (image->format->Rmask == 0x000000ff) 711 | // texture_format = GL_RGBA; 712 | // else 713 | // texture_format = GL_BGRA; 714 | // } 715 | // else if (num_colors == 3) // no alpha channel 716 | // { 717 | // if (image->format->Rmask == 0x000000ff) 718 | // texture_format = GL_RGB; 719 | // else 720 | // texture_format = GL_BGR; 721 | // } 722 | // 723 | // glGenTextures(1, &texture_); 724 | // glBindTexture(GL_TEXTURE_2D, texture_); 725 | // 726 | // // Set the texture's stretching properties 727 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 728 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 729 | // 730 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 731 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 732 | // 733 | // // Edit the texture object's image data using the information SDL_Surface 734 | // gives us 735 | // glTexImage2D(GL_TEXTURE_2D, 0, num_colors, image->w, image->h, 0, 736 | // texture_format, GL_UNSIGNED_BYTE, image->pixels); 737 | // 738 | // std::stringstream vertex_shader; 739 | // vertex_shader << "#version 130\n" 740 | // << "uniform mat4 inProjectionMatrix;\n" 741 | // << "in vec2 inPosition;\n" 742 | // << "in vec2 inTexCoord;\n" 743 | // << "out vec2 outTexCoord;\n"; 744 | // 745 | // vertex_shader << "void main() {\n" 746 | // << "\toutTexCoord = inTexCoord;\n" 747 | // << "\tgl_Position = inProjectionMatrix * vec4(inPosition, 0.0, 1.0);\n"; 748 | // vertex_shader << "}"; 749 | // 750 | // std::stringstream fragment_shader; 751 | // fragment_shader << "#version 130\n" 752 | // << "uniform sampler2D texture;\n" 753 | // << "uniform vec4 color;\n" 754 | // //<< "in vec4 outColor;\n" 755 | // << "in vec2 outTexCoord;\n" 756 | // << "out vec4 fragColor;\n"; 757 | // 758 | // fragment_shader << "void main() {\n"; 759 | // fragment_shader << "\tfragColor = texture2D(texture, outTexCoord) * 760 | // color;\n"; 761 | // fragment_shader << "}"; 762 | // 763 | // std::vector shaders; 764 | // shaders.push_back(CreateShader(GL_VERTEX_SHADER, vertex_shader.str())); 765 | // shaders.push_back(CreateShader(GL_FRAGMENT_SHADER, fragment_shader.str())); 766 | // 767 | // shader_ = CreateProgram(shaders); 768 | // 769 | // // setup texture input and model/projective matrix in shader 770 | // texture_idx_ = glGetUniformLocation(shader_, "texture"); 771 | // color_idx_ = glGetUniformLocation(shader_, "color"); 772 | // projection_idx_ = glGetUniformLocation(shader_, "inProjectionMatrix"); 773 | // 774 | // position_idx_ = glGetAttribLocation(shader_, "inPosition"); 775 | // glEnableVertexAttribArray(position_idx_); 776 | // tex_coord_idx_ = glGetAttribLocation(shader_, "inTexCoord"); 777 | // glEnableVertexAttribArray(tex_coord_idx_); 778 | // 779 | // glUseProgram(shader_); 780 | // glUniform1i(texture_idx_, 0); 781 | // glUseProgram(0); 782 | // 783 | // SDL_FreeSurface(image); 784 | //} 785 | 786 | GLuint Shader::CreateShader(GLenum eShaderType, 787 | const std::string &strShaderFile) { 788 | GLuint shader = glCreateShader(eShaderType); 789 | const char *strFileData = strShaderFile.c_str(); 790 | glShaderSource(shader, 1, &strFileData, NULL); 791 | 792 | glCompileShader(shader); 793 | 794 | GLint status; 795 | glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 796 | if (status == GL_FALSE) { 797 | GLint infoLogLength; 798 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); 799 | 800 | GLchar *strInfoLog = new GLchar[infoLogLength + 1]; 801 | glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog); 802 | 803 | const char *strShaderType = NULL; 804 | switch (eShaderType) { 805 | case GL_VERTEX_SHADER: 806 | strShaderType = "vertex"; 807 | break; 808 | case GL_GEOMETRY_SHADER: 809 | strShaderType = "geometry"; 810 | break; 811 | case GL_FRAGMENT_SHADER: 812 | strShaderType = "fragment"; 813 | break; 814 | case GL_TESS_EVALUATION_SHADER: 815 | strShaderType = "tesselation evaluation"; 816 | break; 817 | } 818 | 819 | fprintf(stdout, "Compile failure in %s shader:\n%s\n", strShaderType, 820 | strInfoLog); 821 | delete[] strInfoLog; 822 | } 823 | 824 | return shader; 825 | } 826 | 827 | GLuint Shader::CreateProgram(const std::vector &shaderList) { 828 | GLuint program = glCreateProgram(); 829 | 830 | for (size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) 831 | glAttachShader(program, shaderList[iLoop]); 832 | 833 | glLinkProgram(program); 834 | 835 | GLint status; 836 | glGetProgramiv(program, GL_LINK_STATUS, &status); 837 | if (status == GL_FALSE) { 838 | GLint infoLogLength; 839 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); 840 | 841 | GLchar *strInfoLog = new GLchar[infoLogLength + 1]; 842 | glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog); 843 | fprintf(stdout, "Linker failure: %s\n", strInfoLog); 844 | delete[] strInfoLog; 845 | } 846 | 847 | return program; 848 | } 849 | -------------------------------------------------------------------------------- /src/shader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_H_ 2 | #define SHADER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class Q3Shader; 11 | 12 | class Shader { 13 | public: 14 | Shader(Q3Shader &q3_shader) 15 | : q3_shader_(q3_shader), shader_(0), lightmap_stage_(-1), 16 | compiled_(false){}; 17 | ~Shader(void); 18 | 19 | void CompileShader(); 20 | void CompileVertexShader(); 21 | void CompileTesselationShader(); 22 | void CompileFragmentShader(); 23 | void CompileFontShader(); 24 | 25 | int SetupTextures(); 26 | 27 | GLuint CreateShader(GLenum shader_type, const std::string &shader_file); 28 | GLuint CreateProgram(const std::vector &shader_list); 29 | 30 | Q3Shader &q3_shader_; 31 | 32 | std::stringstream vertex_shader_; 33 | std::stringstream tesselation_shader_; 34 | std::stringstream fragment_shader_; 35 | 36 | unsigned int shader_; 37 | 38 | unsigned int texture_idx_[8]; 39 | unsigned int projection_idx_; 40 | unsigned int model_idx_; 41 | unsigned int time_idx_; 42 | unsigned int position_idx_; 43 | unsigned int tex_coord_idx_; 44 | unsigned int lm_coord_idx_; 45 | unsigned int color_idx_; 46 | 47 | unsigned int patchWidth_, patchHeight_; 48 | 49 | int lightmap_stage_; 50 | unsigned int texture_id_[8]; 51 | 52 | bool compiled_; 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/shader_loader.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codesuki/bsp-renderer/94a739e06278632c5442954442664f7b26fd4643/src/shader_loader.cpp -------------------------------------------------------------------------------- /src/shader_loader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_LOADER_H_ 2 | #define SHADER_LOADER_H_ 3 | 4 | #include 5 | 6 | class Shader; 7 | 8 | namespace shaderLoader 9 | { 10 | void Deinitialize(); 11 | int LoadAllShaders(); 12 | int GetShader(std::string name); 13 | Shader* GetShader(unsigned int id); 14 | void CompileAllShaders(); 15 | Shader* CreateModelShader(std::string name); 16 | } 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /src/texture_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "texture_loader.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "logger.hpp" 11 | 12 | namespace textureLoader 13 | { 14 | namespace 15 | { 16 | std::map textures_; 17 | std::vector lightmaps_; 18 | } 19 | 20 | int LoadTexture(std::string name, bool clamp) 21 | { 22 | if (name.length() == 0) 23 | { 24 | return -1; 25 | } 26 | 27 | std::string oldname = name; 28 | 29 | if (name.find('.') != std::string::npos) 30 | { 31 | name.erase(name.end()-4, name.end()); 32 | } 33 | 34 | std::string filename_tga = name; 35 | filename_tga.append(".tga"); 36 | 37 | std::string filename_jpg = name; 38 | filename_jpg.append(".jpg"); 39 | 40 | SDL_Surface *image; 41 | image = IMG_Load(filename_jpg.c_str()); 42 | 43 | logger::Log(logger::DEBUG, "loading texture: %s\n", filename_jpg.c_str()); 44 | 45 | if (!image) 46 | { 47 | logger::Log(logger::DEBUG, "%s", IMG_GetError()); 48 | 49 | image = IMG_Load(filename_tga.c_str()); 50 | if (!image) 51 | { 52 | logger::Log(logger::ERROR, "%s", IMG_GetError()); 53 | return -1; 54 | } 55 | } 56 | 57 | GLenum texture_format; 58 | GLenum internal_format; 59 | GLint num_colors = image->format->BytesPerPixel; 60 | 61 | if (num_colors == 4) // contains an alpha channel 62 | { 63 | internal_format = GL_RGBA; 64 | 65 | if (image->format->Rmask == 0x000000ff) 66 | texture_format = GL_RGBA; 67 | else 68 | texture_format = GL_BGRA; 69 | } 70 | else if (num_colors == 3) // no alpha channel 71 | { 72 | internal_format = GL_RGB; 73 | 74 | if (image->format->Rmask == 0x000000ff) 75 | texture_format = GL_RGB; 76 | else 77 | texture_format = GL_BGR; 78 | } 79 | else 80 | { 81 | logger::Log(logger::ERROR, "Image has unsupported number of colors"); 82 | } 83 | 84 | // Have OpenGL generate a texture object handle for us 85 | unsigned int texture; 86 | glGenTextures(1, &texture); 87 | 88 | // Bind the texture object 89 | glBindTexture(GL_TEXTURE_2D, texture); 90 | 91 | if (clamp) 92 | { 93 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 94 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 95 | } 96 | else 97 | { 98 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 99 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 100 | } 101 | 102 | // Set the texture's stretching properties 103 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 104 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 105 | 106 | // Edit the texture object's image data using the information SDL_Surface gives us 107 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, image->w, image->h, 0, 108 | texture_format, GL_UNSIGNED_BYTE, image->pixels); 109 | 110 | SDL_FreeSurface(image); 111 | 112 | textures_[oldname] = texture; 113 | 114 | return 0; 115 | } 116 | 117 | int LoadLightmap(bsp_lightmap& lightmap) 118 | { 119 | SDL_Surface *image = SDL_CreateRGBSurfaceFrom(static_cast(&lightmap), 128, 128, 24, 128*3, 0, 0, 0, 0); 120 | 121 | // Have OpenGL generate a texture object handle for us 122 | unsigned int texture; 123 | glGenTextures(1, &texture); 124 | 125 | // Bind the texture object 126 | glBindTexture(GL_TEXTURE_2D, texture); 127 | 128 | // Set the texture's stretching properties 129 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 130 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 131 | 132 | // Edit the texture object's image data using the information SDL_Surface gives us 133 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, image->w, image->h, 0, 134 | GL_RGB, GL_UNSIGNED_BYTE, image->pixels ); 135 | 136 | SDL_FreeSurface(image); 137 | 138 | lightmaps_.push_back(texture); 139 | 140 | return 0; 141 | } 142 | unsigned int skipped_textures_ = 0; 143 | 144 | unsigned int skipped_textures() 145 | { 146 | return skipped_textures_; 147 | } 148 | 149 | int GetTexture(std::string texture, bool clamp) 150 | { 151 | auto it = textures_.find(texture); 152 | if (it == textures_.end()) 153 | { 154 | int ret = LoadTexture(texture, clamp); 155 | if (ret == -1) 156 | { 157 | return -1; 158 | } 159 | } 160 | else 161 | { 162 | ++skipped_textures_; 163 | } 164 | 165 | int texture_id = textures_[texture]; 166 | 167 | return texture_id; 168 | } 169 | 170 | int GetLightmap(unsigned int id) 171 | { 172 | return lightmaps_[id]; 173 | } 174 | 175 | void Deinitialize() 176 | { 177 | for (auto texture : textures_) 178 | { 179 | glDeleteTextures(1, &texture.second); 180 | } 181 | 182 | for (auto texture : lightmaps_) 183 | { 184 | glDeleteTextures(1, &texture); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/texture_loader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURELOADER_H_ 2 | #define TEXTURELOADER_H_ 3 | 4 | #include 5 | 6 | struct bsp_lightmap; 7 | 8 | namespace textureLoader 9 | { 10 | unsigned int skipped_textures(); 11 | void Deinitialize(); 12 | int LoadTexture(std::string name, bool clamp); 13 | int LoadLightmap(bsp_lightmap& lightmap); 14 | int GetTexture(std::string name, bool clamp); 15 | int GetLightmap(unsigned int id); 16 | } 17 | 18 | #endif /* _TEXTURELOADER_H_ */ 19 | 20 | -------------------------------------------------------------------------------- /src/util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __UTIL_H__ 2 | #define __UTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "GL/glew.h" 15 | 16 | #define GLM_FORCE_RADIANS 17 | #include "glm/glm.hpp" 18 | #include "glm/gtc/matrix_transform.hpp" 19 | #include "glm/gtc/matrix_access.hpp" 20 | #include "glm/gtc/type_ptr.hpp" 21 | 22 | #define GL_GLEXT_PROTOTYPES 23 | 24 | #include "SDL.h" 25 | #include "SDL_image.h" 26 | 27 | #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } } 28 | #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } 29 | #define PRINT_VAR(x) std::cout << #x << " " << x << std::endl 30 | #define BUFFER_OFFSET(i) ((char *)NULL + (i)) 31 | 32 | #define glError() { \ 33 | GLenum err = glGetError(); \ 34 | while (err != GL_NO_ERROR) { \ 35 | fprintf(stderr, "glError: %s caught at %s:%u\n", (char *)gluErrorString(err), __FILE__, __LINE__); \ 36 | err = glGetError(); \ 37 | } \ 38 | } 39 | 40 | // convert from our coordinate system (looking down X) 41 | // to OpenGL's coordinate system (looking down -Z) 42 | static const GLfloat quake2oglMatrix[16] = 43 | {0, 0, -1, 0, 44 | -1, 0, 0, 0, 45 | 0, 1, 0, 0, 46 | 0, 0, 0, 1}; 47 | 48 | //static const glm::mat4 quake2ogl(0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); 49 | 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/world.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_HPP_ 2 | #define WORLD_HPP_ 3 | 4 | #include 5 | 6 | class Bsp; 7 | class Entity; 8 | 9 | class World 10 | { 11 | public: 12 | World(void); 13 | ~World(void); 14 | 15 | void LoadLevel(std::string name); 16 | 17 | void Update(unsigned int time); 18 | 19 | Bsp* map_; 20 | Entity* player_; 21 | Entity* enemy_; 22 | 23 | std::vector entities_; 24 | std::vector players_; 25 | }; 26 | 27 | #endif 28 | 29 | --------------------------------------------------------------------------------