Pastebin
API
tools
faq
paste
Login
Sign up
Please fix the following errors:
New Paste
Syntax Highlighting
/******************************************************************************/ /******************************************************************************/ //"sdl2rnd\src\file.cpp": #include <stdio.h> #include <scene.hpp> //#include <file.hpp> //#include <memory.hpp> namespace file { bool exists(const char* filePath){ if(filePath == nullptr) throw "filePath = nullptr"; FILE* file = fopen(filePath, "rb"); if(file != nullptr){ fclose(file); return true; } else { return false; } } s32 getSize(const char* filePath){ if(!exists(filePath)) throw fstr("file \"%s\" does not exist", filePath); FILE* file = fopen(filePath, "rb"); if(file == nullptr) throw "failed to open file"; fseek(file, 0L, SEEK_END); s32 fileSize = ftell(file); fclose(file); return fileSize; } }; /* namespace file */ BinaryData::BinaryData(const char* filePath){ s64 _fileSize = file::getSize(filePath); //also checks to see if file exists size_t fileSize = (size_t)_fileSize; if(_fileSize < 0 ) throw "file size < 0"; if(_fileSize == 0 ) throw "file size = 0"; if(_fileSize > INT_S32_MAX) throw "file size > INT_MAX"; *(char**)&data = (char*)memory::alloc(fileSize); if(data == nullptr) throw "failed to allocate memory for data"; FILE* file = fopen(filePath, "rb"); if(file == nullptr) throw "failed to open file"; size_t bytesRead = fread(data, sizeof(char), fileSize, file); const char* err_msg = nullptr; if(bytesRead != fileSize){ if( feof(file)) err_msg = "unexpected EOF"; else if(ferror(file)) err_msg = "failed to read file"; else err_msg = "bytes read != file size"; char* _data = (char*)data; memory::free(&_data); } fclose(file); *(size_t*)&data_len = fileSize; if(err_msg != nullptr) throw err_msg; } BinaryData::~BinaryData(){ char* _data = (char*)data; memory::free(&_data); } void BinaryData::loadFromMemory(const void* data, size_t dataSize){ if(!dataSize) throw "dataSize = 0"; char* _data = (char*)data; char* _data_new = (char*)memory::realloc(&_data, dataSize); if(_data_new == nullptr) throw "failed to allocate memory for data"; if(_data != nullptr) memcpy(_data_new, _data, MIN(dataSize, data_len)); *(char**)data = _data_new; } /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\src\main.cpp": #define SDL_MAIN_HANDLED #include <scene.hpp> int sceneMain(int argc, char** argv); int main(int argc, char** argv){ int returnStatus = -1; try { //i will uncomment this when my dumb ass gets around to putting in SIMD functionality //if(!SDL_HasAVX()) throw "this program requires AVX, which this CPU doesn't support"; if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)<0) throw SDL_GetError(); //should be redundant, but just in case SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); if(file::exists("geotex.bmp")){ geometrySrf = SDL_LoadBMP("geotex.bmp"); if(!geometrySrf) throw SDL_GetError(); } // /* else throw "\"geotex.bmp\" does not exist!"; // */ canvas = new Canvas(WIN_W,WIN_H, CNV_W,CNV_H, WIN_TITLE " " TRAP_PROMPT); //SDL_ShowWindow(canvas->getWin()); //done inside sceneMain if(geometrySrf){ geometryTex = SDL_CreateTextureFromSurface(canvas->getRnd(), geometrySrf); if(!geometryTex) throw SDL_GetError(); } text_p = new Text("font8x8.bmp"); returnStatus = sceneMain(argc, argv); SAFE_DELETE(text_p); if(geometryTex) SDL_DestroyTexture(geometryTex); delete canvas; if(geometrySrf) SDL_FreeSurface(geometrySrf); scene_ori.freeAll(); scene_wka.freeAll(); scene_wkb.freeAll(); } catch(const char* errorText){ #ifdef _DEBUG Log("Error = \"%s\"", errorText); #else SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "THE GAME CRASHED!", errorText, nullptr); #endif } Log("# OF ALLOCATIONS = %lu", (unsigned)memory::getNumAllocations()); SDL_Quit(); return returnStatus; } /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\src\memory.cpp": #include <stdlib.h> #include <memory.hpp> #ifdef free #undef free #endif /* free */ #ifdef realloc #undef realloc #endif /* realloc */ //turns something into a void** //(this makes some code here easier for me to read) #define VPP(_ptr_p) ((void**)(_ptr_p)) static size_t numAllocations = 0; void* memory::alloc(size_t size){ void* newHeapMemory = malloc(size); if(newHeapMemory != nullptr) ++numAllocations; return newHeapMemory; } void memory::_free_real(void* ptr_p){ if(VPP(ptr_p) != nullptr && *VPP(ptr_p) != nullptr){ --numAllocations; free(*VPP(ptr_p)); *VPP(ptr_p) = nullptr; } } void* memory::_realloc_real(void* ptr_p, size_t newSize){ void* ptr_new = nullptr; if(VPP(ptr_p) != nullptr){ ptr_new = realloc(*VPP(ptr_p), newSize); if(ptr_new != nullptr){ if(*VPP(ptr_p) == nullptr) ++numAllocations; *VPP(ptr_p) = ptr_new; } } return ptr_new; } size_t memory::getNumAllocations(){ return numAllocations; } void* memory::allocSIMD(size_t size){ void* newHeapMemory = SDL_SIMDAlloc(size); if(newHeapMemory != nullptr) ++numAllocations; return newHeapMemory; } void memory::freeSIMD(void* ptr_p){ if(VPP(ptr_p) != nullptr && *VPP(ptr_p) != nullptr){ --numAllocations; SDL_SIMDFree(*VPP(ptr_p)); *VPP(ptr_p) = nullptr; } } void* memory::reallocSIMD(void* ptr_p, size_t newSize){ void* ptr_new = nullptr; if(VPP(ptr_p) != nullptr){ ptr_new = SDL_SIMDRealloc(*VPP(ptr_p), newSize); if(ptr_new != nullptr){ if(*VPP(ptr_p) == nullptr) ++numAllocations; *VPP(ptr_p) = ptr_new; } } return ptr_new; } size_t memory::getSIMDAlignment(){ return SDL_SIMDGetAlignment(); } #define DONT_USE_AVX void* memory::set(void* _dst, char v, size_t size){ #ifdef DONT_USE_AVX return memset(_dst, (int)v, size); #else //tbd if(!size) return _dst; size_t offset_fnt = size size_t offset_bck = size //skip if there's nothing left to set if(size >= 32){ __m256i write_block = _mm256_set1_epi8(v); } return _dst; #endif } void* memory::copy(void* dst, const void* src, size_t size){ #ifdef DONT_USE_AVX return memcpy(dst, src, size); #else #endif } /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\src\sceneMain.cpp": #include <scene.hpp> //1 ship, 1 'skybox', 1 gear icon, 1 door, <bullet_count> bullets, //4 asteroids, 16 mini-asteroids, and 64 micro-asteroids #define bullet_count 4 #define asteroid_count (4 + 4*4 + 4*4*4) MeshSimple* meshes[1 + 1 + 1 + 1 + bullet_count + asteroid_count]; #define meshes_len (sizeof(meshes)/sizeof(MeshSimple*)) #define bullet_offset 4 #define asteroid_offset (bullet_offset+bullet_count) #define _mesh_ship (meshes[0]) #define _mesh_skybox (meshes[1]) #define _mesh_gear (meshes[2]) #define _mesh_door (meshes[3]) #define _mesh_bullet(which) (meshes[bullet_offset+(which)]) #define _mesh_asteroid(which) (meshes[asteroid_offset+(which)]) #define mesh_ship (*_mesh_ship) #define mesh_skybox (*_mesh_skybox) #define mesh_gear (*_mesh_gear) #define mesh_door (*_mesh_door) #define mesh_bullet(which) (*_mesh_bullet(which)) #define mesh_asteroid(which) (*_mesh_asteroid(which)) #define asteroid_normal(which) mesh_asteroid( which ) #define asteroid_mini(which) mesh_asteroid(4+ (which)) #define asteroid_micro(which) mesh_asteroid(4+16+(which)) #define asteroid_menu asteroid_micro #define state_normal(which) state.asteroids[ which ] #define state_mini(which) state.asteroids[4+ (which)] #define state_micro(which) state.asteroids[4+16+(which)] #define bullet_size 0.25f #define nrml_siz 1.00f #define mini_siz 0.75f #define mcro_siz 0.50f #define menu_siz mcro_siz //application of values are as follows: //camera's current position = old*mod + new*(1.0f-mod) //old = camera's current position #define camera_mod 0.85f #define camera_lpf(_member) \ camera_old._member*camera_mod + camera_new._member*(1.0f-camera_mod) Location camera_old; Location camera_cur; //subject to different rules to that of scene_camera Location camera_new; #define bullet_speed 0.60f #define ship_speed_x 0.15f #define ship_speed_y 0.15f #define ship_speed_z 0.15f #define ship_camera_distance 1.3f #define ship_camera_y_offset 0.25f #define ship_camera_down_tilt 0.02f #define ship_decay_multiplier_a 0.88f #define ship_decay_multiplier_b 0.5f #define CFG_FILENAME "./config.bin" #define CFG_VERSION 5 struct _cfg_s { int version = CFG_VERSION; float high_score = 0.0f; #define BIND_COUNT 6 SDL_Scancode bind_right = SDL_SCANCODE_D; SDL_Scancode bind_left = SDL_SCANCODE_A; SDL_Scancode bind_forward = SDL_SCANCODE_W; SDL_Scancode bind_back = SDL_SCANCODE_S; SDL_Scancode bind_up = SDL_SCANCODE_LSHIFT; SDL_Scancode bind_down = SDL_SCANCODE_LCTRL; int win_w = WIN_W, win_h = WIN_H; float lastrand = 0.9f; //used to modify srand when starting a game //maximum number of normal asteroids that can exist at once //(takes into account mini and micro asteroids, //assuming one or more normal ones have split) int num_asteroids = 4; float sensitivity = 1.0f; //mouse look sensitivity bool invert_y = false; //inverts vertical mouse input bool maximized = false; bool fullscreen = false; }; _cfg_s cfg; //WEIRD POINTER STUFF LMAOOO SDL_Scancode* bind_array = (SDL_Scancode*)(&cfg.high_score+1); SDL_Scancode bind_array_defaults[BIND_COUNT]; const char* bind_names[] = { "right", "left", "forward", "back", "up", "down", }; #define MENU_REBINDING -3 #define MENU_DEAD -2 #define MENU_PLAYING -1 #define MENU_QUIT 0 #define MENU_PLAY 1 #define MENU_ASTEROIDS 2 #define MENU_REBIND 3 #define MENU_MAX MENU_REBIND struct bulletElement { Vec3 vel = {0,0,0}; bool active = false; }; enum asteroidTypeEnum { ATYPE_NORMAL = 0, ATYPE_MINI, ATYPE_MICRO, }; struct asteroidElement { asteroidElement* children = nullptr; Vec3 vel = {0,0,0}; char parent_id = -1; char children_active = -1; char type = -1; //asteroidTypeEnum bool active = false; }; struct _state_s { bulletElement bullets[bullet_count]; int active_bullets = 0; asteroidElement asteroids[asteroid_count]; int active_asteroids = 0; int menu = MENU_PLAY; int which_rebind = 0; bool pressed_right = false; bool pressed_left = false; bool pressed_forward = false; bool pressed_back = false; bool pressed_up = false; bool pressed_down = false; bool quit = false; }; _state_s state; bool* pressed_array = (bool*)(&state.which_rebind+1); #define skybox_bounds ((CLIP_FAR/32) - 1.0f) static inline bool keepInBounds(Location& loc){ bool wrappedAround = false; if(fabsf(loc.x)>skybox_bounds){ loc.x = (1-2*(loc.x>=0))*skybox_bounds; wrappedAround = true; } if(fabsf(loc.y)>skybox_bounds){ loc.y = (1-2*(loc.y>=0))*skybox_bounds; wrappedAround = true; } if(fabsf(loc.z)>skybox_bounds){ loc.z = (1-2*(loc.z>=0))*skybox_bounds; wrappedAround = true; } return wrappedAround; } template<class T> inline T umod(T a, int b){ a %= b; if(a < 0) a += b; return a; } #define SQR(thing) ((thing)*(thing)) static inline float locHypot(const Location& loc){ return sqrtf(SQR(loc.x)+SQR(loc.y)+SQR(loc.z)); } static inline float locDistance(const Location& l_a, const Location& l_b){ return sqrtf(SQR(l_b.x-l_a.x)+SQR(l_b.y-l_a.y)+SQR(l_b.z-l_a.z)); } //assumes RAND_MAX is 32767 static inline float randf(){ return (float)(rand()<<15|rand())/0x3fffffff; } static inline float randf2(){ return randf()*2.0f - 1.0f; } bool cursorIsTrapped = false; void trapCursor(bool enable){ if(enable == cursorIsTrapped) return; SDL_SetWindowTitle(canvas->getWin(), fstr(WIN_TITLE " %s", (enable) ? UNTRAP_PROMPT : TRAP_PROMPT)); if(SDL_SetRelativeMouseMode((enable) ? SDL_TRUE : SDL_FALSE)<0) throw SDL_GetError(); cursorIsTrapped = enable; } void setFullscreen(bool enable){ SDL_SetWindowFullscreen(canvas->getWin(), (enable) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); cfg.fullscreen = enable; } void mouseMoved(s32 dx, s32 dy); void mouseButton(u8 button, bool clicked); void keyPress(SDL_Keysym key, bool pressed, u16 modifiers); //returns false if state.quit gets flipped bool handleEvents(){ SDL_Event evt; while(SDL_PollEvent(&evt)) switch(evt.type){ case SDL_QUIT: state.quit = true; SDL_ShowCursor(true); return false; case SDL_WINDOWEVENT: { switch(evt.window.event){ case SDL_WINDOWEVENT_FOCUS_LOST: trapCursor(false); break; case SDL_WINDOWEVENT_RESIZED: cfg.win_w = evt.window.data1, cfg.win_h = evt.window.data2; break; case SDL_WINDOWEVENT_RESTORED: cfg.maximized = false; break; case SDL_WINDOWEVENT_MAXIMIZED: cfg.maximized = true; break; default: ;//Log("%X", evt.window.event); } } break; case SDL_KEYDOWN: { if(evt.key.repeat) break; if(evt.key.keysym.sym == SDLK_ESCAPE){ trapCursor(false); break; } if(evt.key.keysym.sym == SDLK_F11){ setFullscreen(!cfg.fullscreen); break; } } SDL_FALLTHROUGH; case SDL_KEYUP: { keyPress(evt.key.keysym, (evt.key.state == SDL_PRESSED) ? true : false, evt.key.keysym.mod); } break; case SDL_MOUSEBUTTONDOWN:{ if(cursorIsTrapped) mouseButton(evt.button.button, evt.button.state == SDL_PRESSED); if(evt.button.button == SDL_BUTTON_LEFT) trapCursor(true); } break; case SDL_MOUSEMOTION: { if(cfg.invert_y) evt.motion.yrel = -evt.motion.yrel; if(cursorIsTrapped) mouseMoved(evt.motion.xrel, evt.motion.yrel); } break; default: ;//Log("%X", evt.type); } return !state.quit; } Vec3 rotatePRY(float pitch, float roll, float yaw, float x_0, float y_0, float z_0) { const float cosPitch = cosf(pitch); const float sinPitch = sinf(pitch); const float cosRoll = cosf(roll ); const float sinRoll = sinf(roll ); const float cosYaw = cosf(yaw ); const float sinYaw = sinf(yaw ); float y_1 = cosPitch*y_0 - sinPitch*z_0; float z_1 = sinPitch*y_0 + cosPitch*z_0; float x_2 = cosRoll*x_0 - sinRoll*y_1; float y_2 = sinRoll*x_0 + cosRoll*y_1; float x_3 = cosYaw*x_2 + sinYaw*z_1; float z_2 = -sinYaw*x_2 + cosYaw*z_1; return Vec3(x_3, y_2, z_2); } //motion is relative to camera's current direction static inline void moveCamera(float x_0, float y_0, float z_0){ Vec3 motion = rotatePRY(-camera_cur.pitch, -camera_cur.roll, -camera_cur.yaw, x_0, y_0, z_0); camera_cur.x += motion.x; camera_cur.y += motion.y; camera_cur.z += motion.z; } bool fireBullet(){ if(state.active_bullets >= bullet_count) return false; //max bullets reached int which = -1; for(int i=0; i<bullet_count; ++i){ if(!state.bullets[i].active){ which = i; break; } } if(which == -1) throw "could not find empty spot in bullet queue, despite active bullets being < maximum"; //return false; Vec3 direction = rotatePRY(-camera_cur.pitch, -camera_cur.roll, -camera_cur.yaw, 0.0f, 0.0f, 1.0f); mesh_bullet(which).p.x = -camera_cur.x + direction.x; mesh_bullet(which).p.y = -camera_cur.y + direction.y; mesh_bullet(which).p.z = -camera_cur.z + direction.z; mesh_bullet(which).p.pitch = -camera_cur.pitch; mesh_bullet(which).p.roll = -camera_cur.roll; mesh_bullet(which).p.yaw = -camera_cur.yaw; state.bullets[which].vel = direction; state.bullets[which].vel *= bullet_speed; //lol #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" state.bullets[which].active = true; #pragma GCC diagnostic pop ++state.active_bullets; return true; } #define max_asteroid_speed 0.1 void asteroidCreate(int which){ Location newloc = mesh_asteroid(which).p; do { newloc.x = randf()*skybox_bounds*2 - skybox_bounds; newloc.y = randf()*skybox_bounds*2 - skybox_bounds; newloc.z = randf()*skybox_bounds*2 - skybox_bounds; } while( locDistance(newloc, camera_cur) < (skybox_bounds/3) ); float max_type_speed = max_asteroid_speed*(1+state.asteroids[which].type); mesh_asteroid(which).p = newloc; state.asteroids[which].vel.x = randf2() * max_type_speed; state.asteroids[which].vel.y = randf2() * max_type_speed; state.asteroids[which].vel.z = randf2() * max_type_speed; state.asteroids[which].active = true; } void asteroidSplit(int which){ /* asteroidElement* ast = state.asteroids[which]; while(ast->children){ } */ /* if(ast->type == ATYPE_MICRO){ ast-> } else if(ast->type == ATYPE_MINI){ } else { }*/ } // #define mdelta_divisor_a 200 #define mdelta_divisor_b 200 #define mdelta_divisor_c 1000 void mouseMoved(s32 dx, s32 dy){ if(state.menu != MENU_PLAYING) return; #define rotmod(value) fmodf(value, _2pi) #define rotclamp(value) CLAMP(value, -_pi, _pi) #define rotclamph(value) CLAMP(value, -(_pi/2), (_pi/2)) camera_cur.yaw = rotmod( camera_cur.yaw -((float)dx/mdelta_divisor_a)*cfg.sensitivity); camera_cur.pitch = rotclamph(camera_cur.pitch+((float)dy/mdelta_divisor_a)*cfg.sensitivity); mesh_ship.p.roll = rotclamph(mesh_ship.p.roll -((float)dx/mdelta_divisor_b)*cfg.sensitivity); mesh_ship.p.pitch = rotclamph(mesh_ship.p.pitch-((float)dy/mdelta_divisor_b)*cfg.sensitivity); } void mouseButton(u8 button, bool clicked){ if(state.menu != MENU_PLAYING) return; if(button == SDL_BUTTON_LEFT && clicked) fireBullet(); } static inline bool bindingIsValid(SDL_Scancode scancode, unsigned index){ if(index > INT_S32_MAX) //just in case throw "bindingIsValid(): index > 2147483647"; //bindings shouldn't share the same scancode, //so check current bindings until right before current index (not BIND_COUNT) for(unsigned i=0; i<index; ++i) //loop shouldn't start at all if index is 0 if(bind_array[i] == scancode) return false; return true; } void keyPress(SDL_Keysym key, bool pressed, u16 modifiers){ //Log("0x%02X, %i, 0x%04X", key.scancode, pressed, modifiers); if(state.menu == MENU_PLAYING){ for(unsigned i=0; i<BIND_COUNT; ++i){ if(key.scancode == bind_array[i]) pressed_array[i] = pressed; } if(pressed){ if(key.sym == SDLK_SPACE ) fireBullet(); if(key.sym == SDLK_BACKSPACE) state.menu = MENU_PLAY; } } else if(state.menu == MENU_REBINDING){ if(!pressed) return; if(key.sym == SDLK_RETURN || key.sym == SDLK_SPACE) return; if( key.sym == SDLK_UP || key.sym == SDLK_DOWN || key.sym == SDLK_LEFT || key.sym == SDLK_RIGHT ) { return; } if(key.sym == SDLK_BACKSPACE){ for(int i=0; i<BIND_COUNT; ++i) bind_array[i] = bind_array_defaults[i]; state.menu = MENU_REBIND; return; } if(bindingIsValid(key.scancode, state.which_rebind)){ bind_array[state.which_rebind] = key.scancode; ++state.which_rebind; } if(state.which_rebind >= BIND_COUNT) state.menu = MENU_REBIND; } else if(state.menu >= 0){ if(!pressed) return; if(key.sym == SDLK_RETURN || key.sym == SDLK_SPACE){ switch(state.menu){ case MENU_QUIT : state.quit = true; break; case MENU_PLAY : state.menu = MENU_PLAYING; break; case MENU_ASTEROIDS: cfg.num_asteroids = cfg.num_asteroids%4+1; break; case MENU_REBIND : state.menu = MENU_REBINDING; state.which_rebind = 0; break; default:; } } else if(key.scancode == cfg.bind_left || key.scancode == SDL_SCANCODE_LEFT){ state.menu = umod(state.menu-1, MENU_MAX+1); } else if(key.scancode == cfg.bind_right || key.scancode == SDL_SCANCODE_RIGHT){ state.menu = umod(state.menu+1, MENU_MAX+1); } else switch(state.menu){ #ifdef _DEBUG case MENU_QUIT: { if(key.scancode == SDL_SCANCODE_RSHIFT) mesh_ship.p.yaw = 0.0f; } break; #endif case MENU_ASTEROIDS: { if(key.scancode == cfg.bind_back || key.scancode == SDL_SCANCODE_DOWN) cfg.num_asteroids = MAX(cfg.num_asteroids-1, 1); else if(key.scancode == cfg.bind_forward || key.scancode == SDL_SCANCODE_UP) cfg.num_asteroids = MIN(cfg.num_asteroids+1, 4); } break; default:; } } else { } } int sceneMain(int argc, char** argv){ /************************************ INIT ************************************/ _mesh_ship = new MeshSimple("ship.obj"); _mesh_skybox = new MeshSimple("skybox.obj"); _mesh_gear = new MeshSimple("gear.obj"); _mesh_door = new MeshSimple("door.obj"); for(unsigned i=0; i<bullet_count; ++i) _mesh_bullet(i) = new MeshSimple("bullet.obj"); for(unsigned i=0; i<asteroid_count; ++i) _mesh_asteroid(i) = new MeshSimple("asteroid.obj"); for(int i=0; i<BIND_COUNT; ++i) bind_array_defaults[i] = bind_array[i]; cfg.version = CFG_VERSION; if(file::exists(CFG_FILENAME)){ BinaryData _cfg_file(CFG_FILENAME); _cfg_s* cfg_file = (_cfg_s*)_cfg_file.data; //always make sure high score carries over, since i won't be changing its //placement between changes to the overall structure of _cfg_s cfg.high_score = cfg_file->high_score; if( _cfg_file.data_len == sizeof(cfg) && cfg_file->version == cfg.version ) { cfg = *cfg_file; } } sceneRebuild(meshes, meshes_len); for(unsigned i=0; i<bullet_count; ++i) mesh_bullet(i).p.scale = bullet_size; for(unsigned i=0; i< 4; ++i) mesh_asteroid( i).p.scale = nrml_siz; //normal for(unsigned i=0; i<16; ++i) mesh_asteroid( 4+i).p.scale = mini_siz; //mini for(unsigned i=0; i<64; ++i) mesh_asteroid(16+4+i).p.scale = mcro_siz; //micro state.menu = -1; if(cfg.maximized) SDL_MaximizeWindow(canvas->getWin()); else SDL_SetWindowSize(canvas->getWin(), cfg.win_w, cfg.win_h); setFullscreen(cfg.fullscreen); SDL_ShowWindow(canvas->getWin()); canvas->clear(); canvas->present(0); Uint64 startTime; SDL_RWops* cfg_file; float viewport_fov = 75; //degrees of vertical field of view float viewport_mod = tan(viewport_fov/(360.0f/_pi)); SDL_Rect crosshair = {CNV_W/2-1, CNV_H/2-1, 2, 2}; for(int n=0; n<4; ++n){ state_normal(n).children = &state_mini(n*4); state_normal(n).vel = Vec3(0.0f, 0.0f, 0.0f); state_normal(n).parent_id = -1; state_normal(n).children_active = 0; state_normal(n).type = ATYPE_NORMAL; state_normal(n).active = false; } for(int m=0; m<16; ++m){ state_mini(m).children = &state_micro(m*4); state_mini(m).vel = Vec3(0.0f, 0.0f, 0.0f); state_mini(m).parent_id = m>>2; state_mini(m).children_active = 0; state_mini(m).type = ATYPE_MINI; state_mini(m).active = false; } for(int u=0; u<64; ++u){ state_micro(u).children = nullptr; state_micro(u).vel = Vec3(0.0f, 0.0f, 0.0f); state_micro(u).parent_id = 4 + (u>>2); state_micro(u).children_active = -1; state_micro(u).type = ATYPE_MICRO; state_micro(u).active = false; } /**********************************/ _MENU: /**********************************/ #define menu_mesh_spacing 3.0f memory::set(&scene_camera, 0, sizeof(scene_camera)); scene_camera.scale = 1.0f/16; scene_camera.pitch = 0.3f; scene_camera.y = 0.25f; scene_camera.z = -0.25f; memory::set(&mesh_ship.p, 0, sizeof(mesh_ship.p)); mesh_ship.p.scale = 1.0f; mesh_ship.p.x = menu_mesh_spacing*MENU_PLAY; mesh_ship.p.z = 1.0f; memory::set(&mesh_skybox.p, 0, sizeof(mesh_skybox.p)); memory::set(&mesh_gear.p, 0, sizeof(mesh_gear.p)); mesh_gear.p.scale = 1.0f; mesh_gear.p.x = menu_mesh_spacing*MENU_REBIND; mesh_gear.p.z = 1.0f; memory::set(&mesh_door.p, 0, sizeof(mesh_door.p)); mesh_door.p.scale = 1.0f; mesh_door.p.x = menu_mesh_spacing*MENU_QUIT; mesh_door.p.z = 1.0f; for(unsigned i=0; i<bullet_count; ++i){ mesh_bullet(i).p.y = CLIP_FAR*2; state.bullets[i].active = false; } state.active_bullets = 0; for(unsigned i=0; i<asteroid_count; ++i){ mesh_asteroid(i).p.x = menu_mesh_spacing*MENU_ASTEROIDS; mesh_asteroid(i).p.y = CLIP_FAR*2; mesh_asteroid(i).p.z = 1.0f; } asteroid_menu(0).p.x += -menu_siz/2; //bottom-left asteroid_menu(0).p.y = -menu_siz/2; // asteroid_menu(1).p.x += menu_siz/2; //bottom-right asteroid_menu(1).p.y = -menu_siz/2; // asteroid_menu(2).p.x += -menu_siz/2; //top-left asteroid_menu(2).p.y = menu_siz/2; // asteroid_menu(3).p.x += menu_siz/2; //top-right asteroid_menu(3).p.y = menu_siz/2; // if(state.menu < 0) state.menu = MENU_PLAY; camera_old.x = menu_mesh_spacing * state.menu; camera_new.x = camera_old.x; scene_camera.x = camera_old.x; while(!state.quit && state.menu != MENU_PLAYING){ startTime = SDL_GetTicks64(); if(!handleEvents()) break; if(state.menu >= 0){ mesh_ship.p.yaw += _2pi/60/8; mesh_gear.p.yaw = mesh_ship.p.yaw; mesh_door.p.yaw = mesh_ship.p.yaw; for(unsigned i=0; i<4; ++i) asteroid_menu(i).p.yaw = mesh_ship.p.yaw; camera_new.x = menu_mesh_spacing * state.menu; scene_camera.x = camera_lpf(x); camera_old.x = scene_camera.x; asteroid_menu(0).p.z = (cfg.num_asteroids >= 1) ? 1.0f : -100.0f; asteroid_menu(1).p.z = (cfg.num_asteroids >= 2) ? 1.0f : -100.0f; asteroid_menu(2).p.z = (cfg.num_asteroids >= 3) ? 1.0f : -100.0f; asteroid_menu(3).p.z = (cfg.num_asteroids >= 4) ? 1.0f : -100.0f; //putting this in this code block technically causes a graphical glitch //where the framebuffer elements related to geometry gradually fade //out when rebinding controls, but i'll include it until something //inexplicably breaks, just because the effect looks so cool scene_wka.setAll(scene_ori); for(unsigned i=0; i<meshes_len; ++i) if(meshes[i]) sceneApplyTransform(meshes[i]); sceneApplyTransform(nullptr); //(to fix this, simply move the previous 4 lines out of this code block) } canvas->clear(); sceneRender(viewport_mod*((float)cfg.win_w/cfg.win_h), viewport_mod); #define text_margin (16/(CNV_DIV)) text_box(CENTER_TEXT, text_margin, 0, WIN_TITLE); switch(state.menu){ case MENU_QUIT : { //text_box(CENTER_TEXT, CENTER_TEXT, 0, "(there's supposed to be a\nspinning door here, but i\nhaven't modeled that yet)"); text_box(CENTER_TEXT, -text_margin-1, 0, " \n- QUIT GAME >\n"); } break; case MENU_PLAY : { text_box(CENTER_TEXT, -text_margin-1, 0, " \n< START GAME >\n"); } break; case MENU_ASTEROIDS: { textf_box(CENTER_TEXT, -text_margin-1, " ^\n< # OF ASTEROIDS: %i >\n V", cfg.num_asteroids); } break; case MENU_REBIND : { text_box(CENTER_TEXT, -text_margin-1, 0, " \n< REBIND CONTROLS -\n"); } break; case MENU_REBINDING: { textf_box(CENTER_TEXT, CENTER_TEXT, "Press a key for \"%s\"\n(backspace will reset all bindings to their defaults!)", bind_names[state.which_rebind]); } break; default:; } canvas->present((unsigned)(MAX(1, (16-(int)(SDL_GetTicks64()-startTime)) ))); } if(state.quit) goto _QUIT; while(!cursorIsTrapped){ if(!handleEvents()) break; text_box(CENTER_TEXT, CENTER_TEXT, 0, "(click in the window to start)"); canvas->present(16); } srand((unsigned)( SDL_GetTicks64() + (unsigned)(cfg.lastrand*10000) )); cfg.lastrand = randf(); if(!state.quit) goto _GAME; /**********************************/ _QUIT: /**********************************/ cfg_file = SDL_RWFromFile(CFG_FILENAME, "wb"); if(!cfg_file) throw SDL_GetError(); if(SDL_RWwrite(cfg_file, &cfg, 1, sizeof(cfg))<sizeof(cfg)) throw SDL_GetError(); if(SDL_RWclose(cfg_file)<0) throw SDL_GetError(); for(unsigned i=0; i<meshes_len; ++i) SAFE_DELETE(meshes[i]); return 0; /**********************************/ _GAME: /**********************************/ mesh_skybox.p.scale = skybox_bounds*2; mesh_gear.p.scale = 0.0f; mesh_door.p.scale = 0.0f; asteroid_menu(0).p.y = CLIP_FAR*2; asteroid_menu(1).p.y = CLIP_FAR*2; asteroid_menu(2).p.y = CLIP_FAR*2; asteroid_menu(3).p.y = CLIP_FAR*2; memory::set(&camera_old, 0, sizeof(camera_old)); memory::set(&camera_cur, 0, sizeof(camera_cur)); memory::set(&camera_new, 0, sizeof(camera_new)); memory::set(&mesh_ship.p, 0, sizeof(mesh_ship.p)); mesh_ship.p.scale = 1.0f; scene_camera.yaw = 0.0f; scene_camera.pitch = ship_camera_down_tilt; scene_camera.roll = 0.0f; scene_camera.x = 0.0f; scene_camera.y = ship_camera_y_offset; scene_camera.z = -ship_camera_distance; unsigned ship_offset = mesh_ship.verts_len; unsigned num_normal = cfg.num_asteroids ; unsigned num_mini = cfg.num_asteroids* 4; unsigned num_micro = cfg.num_asteroids*16; state.active_asteroids = 0; for(int i=0; i<asteroid_count; ++i) state.asteroids[i].active = false; _out_of_game_loop: //jumped to for the purpose of forcefully breaking the loop while(!state.quit && state.menu == MENU_PLAYING){ startTime = SDL_GetTicks64(); if(!handleEvents()) break; for(unsigned i=0; i<bullet_count; ++i){ if(state.bullets[i].active){ mesh_bullet(i).p.x += state.bullets[i].vel.x; mesh_bullet(i).p.y += state.bullets[i].vel.y; mesh_bullet(i).p.z += state.bullets[i].vel.z; if(keepInBounds(mesh_bullet(i).p)){ mesh_bullet(i).p.y = CLIP_FAR*2; state.bullets[i].active = false; --state.active_bullets; } } } Vec3 moveVector(0.0f, 0.0f, 0.0f); if(state.pressed_right ) moveVector.x += ship_speed_x; if(state.pressed_left ) moveVector.x -= ship_speed_x; if(state.pressed_forward) moveVector.z += ship_speed_z; if(state.pressed_back ) moveVector.z -= ship_speed_z; if(state.pressed_up ) moveVector.y += ship_speed_y; if(state.pressed_down ) moveVector.y -= ship_speed_y; moveCamera(-moveVector.x, -moveVector.y, -moveVector.z); keepInBounds(camera_cur); for(int b=-1; b<bullet_count; ++b){ if(b >= 0 && !state.bullets[b].active) continue; Location loc = (b>=0) ? mesh_bullet(b).p : camera_cur; float l_dist = (b>=0) ? bullet_size/2 : 0.5f; for(unsigned n=0; n<num_normal; ++n){ if(!state_normal(n).active) continue; asteroid_normal(n).p.x += state_normal(n).vel.x; asteroid_normal(n).p.y += state_normal(n).vel.y; asteroid_normal(n).p.z += state_normal(n).vel.z; if(locDistance(loc,asteroid_normal(n).p)<(l_dist+nrml_siz)){ if(b>=0){ asteroidSplit(n); mesh_bullet(b).p.y = CLIP_FAR*2; state.bullets[b].active = false; --state.active_bullets; } else { state.menu = MENU_DEAD; goto _out_of_game_loop; } } } for(unsigned m=0; m<num_mini; ++m){ if(!state_mini(m).active) continue; asteroid_mini(m).p.x += state_mini(m).vel.x; asteroid_mini(m).p.y += state_mini(m).vel.y; asteroid_mini(m).p.z += state_mini(m).vel.z; if(locDistance(loc,asteroid_mini(m).p)<(l_dist+mini_siz)){ if(b>=0){ asteroidSplit(4+m); mesh_bullet(b).p.y = CLIP_FAR*2; state.bullets[b].active = false; --state.active_bullets; } else { state.menu = MENU_DEAD; goto _out_of_game_loop; } } } for(unsigned u=0; u<num_micro; ++u){ if(!state_micro(u).active) continue; asteroid_micro(u).p.x += state_micro(u).vel.x; asteroid_micro(u).p.y += state_micro(u).vel.y; asteroid_micro(u).p.z += state_micro(u).vel.z; if(locDistance(loc,asteroid_micro(u).p)<(l_dist+mcro_siz)){ if(b>=0){ asteroidSplit(4+16+u); mesh_bullet(b).p.y = CLIP_FAR*2; state.bullets[b].active = false; --state.active_bullets; } else { state.menu = MENU_DEAD; goto _out_of_game_loop; } } } } mesh_ship.p.roll *= ship_decay_multiplier_a; mesh_ship.p.pitch *= ship_decay_multiplier_a; scene_wka.setAll(scene_ori); for(unsigned i=1; i<meshes_len; ++i) if(meshes[i]) sceneApplyTransform(meshes[i]); //rotate all but the ship's verts translateWka(camera_cur, ship_offset, LOOP_END_MAX); rotateWka(camera_cur, ship_offset, LOOP_END_MAX); meshTransform(mesh_ship); sceneApplyTransform(nullptr); canvas->clear(); sceneRender(viewport_mod*((float)cfg.win_w/cfg.win_h), viewport_mod); canvas->renderFillRect(&crosshair, 0xffffffff); #define brr_width 20 #define brr_height 40 #define brr_height_b (int)( (1.0f-((float)(state.active_bullets)/bullet_count))*brr_height ) SDL_Rect bullets_remaining_rect = {CNV_W-brr_width, CNV_H-brr_height, brr_width, brr_height}; canvas->renderFillRectBordered(bullets_remaining_rect); bullets_remaining_rect.w -= 2; bullets_remaining_rect.h = MAX(brr_height_b-2, 0); bullets_remaining_rect.x += 1; bullets_remaining_rect.y = CNV_H-bullets_remaining_rect.h-1; canvas->renderFillRect(&bullets_remaining_rect, 0x00ff00); textf(4-brr_width/2, 3-brr_height/2, "%u", bullet_count-state.active_bullets); //textf_box(0,0,"(backspace returns\nto the main menu)"); #ifdef _DEBUG //bullet queue int b=0; for(int i=0; i<bullet_count; ++i) b += state.bullets[i].active; textf_box(0,10, "actually active bullets = %u", b); #endif canvas->present((unsigned)(MAX(1, (16-(int)(SDL_GetTicks64()-startTime)) ))); } //while(!state.quit && state.menu == MENU_DEAD){ } //if(state.quit) goto _QUIT; if(state.quit) goto _QUIT; else goto _MENU; } /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\src\scene_stuff.cpp": #include <scene.hpp> Canvas* canvas = nullptr; SDL_Surface* geometrySrf = nullptr; SDL_Texture* geometryTex = nullptr; GeometryBuffer scene_ori; //original state GeometryBuffer scene_wka; //work buffer a GeometryBuffer scene_wkb; //work buffer b Location scene_camera; //camera's position, rotation, and scale //does not clean up already allocated stuff on throwing here bc i'm lazy Canvas::Canvas(int winW, int winH, int cnvW, int cnvH, const char* title){ win = SDL_CreateWindow((title) ? title : "", SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED, winW, winH, SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE); if(!win){ _throw_sdlerr: throw SDL_GetError(); } SDL_SetWindowMinimumSize(win, cnvW, cnvH); rnd = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE /*| SDL_RENDERER_PRESENTVSYNC*/); if(!rnd) goto _throw_sdlerr; cnv = SDL_CreateTexture(rnd, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_TARGET, cnvW, cnvH); if(!cnv) goto _throw_sdlerr; //SDL_ShowWindow(win); //done inside sceneMain } Canvas::~Canvas(){ if(cnv){ SDL_DestroyTexture (cnv); cnv = nullptr; } if(rnd){ SDL_DestroyRenderer(rnd); rnd = nullptr; } if(win){ SDL_DestroyWindow (win); win = nullptr; } } //only triangle faces are allowed! also, any material data is ignored! MeshSimple::MeshSimple(const char* fileName){ if(!file::exists(fileName)) throw fstr("obj file \"%s\" does not exist", fileName); //note: vertex data counts include the padding at index 0, //so a single triangle would actually have a count of 4 //furthermore, the positions array has a length of count*3, //so the first vertex would actually start at index 3 //(this goes for texcoords and normals too, though // texcoords has a length of count*2 instead of count*3) //(THIS ALSO GOES FOR INDICES) fastObjMesh* msh = fast_obj_read(fileName); if(!msh) throw "failed to read obj file"; first_index = -1; //placeholder p.yaw = 0.0f; p.pitch = 0.0f; p.roll = 0.0f; p.scale = 1.0f; p.x=p.y=p.z = 0.0f; verts_len = msh->face_count * 3; verts = (Vertex*)memory::allocSIMD(verts_len * sizeof(Vertex)); memory::set(verts, 255, verts_len * sizeof(Vertex)); int faces_len = msh->face_count; Triangle* faces = (Triangle*)verts; const fastObjIndex* indices = msh->indices; const Vec3* positions = (Vec3*)msh->positions; const Vec2* texcoords = (Vec2*)msh->texcoords; const Vec3* normals = (Vec3*)msh->normals; for(int i=0; i<faces_len; ++i){ if(msh->face_vertices[i] != 3) throw "obj cannot have non-triangle faces"; fastObjIndex index_a = indices[i*3 + 0]; fastObjIndex index_b = indices[i*3 + 1]; fastObjIndex index_c = indices[i*3 + 2]; faces[i].a.p = positions[index_a.p]; faces[i].b.p = positions[index_b.p]; faces[i].c.p = positions[index_c.p]; faces[i].a.t = texcoords[index_a.t]; faces[i].b.t = texcoords[index_b.t]; faces[i].c.t = texcoords[index_c.t]; faces[i].a.n = normals[index_a.n]; faces[i].b.n = normals[index_b.n]; faces[i].c.n = normals[index_c.n]; } fast_obj_destroy(msh); } MeshSimple::~MeshSimple(){ SAFE_FREE_SIMD(verts); } void sceneRebuild(MeshSimple** meshes, int meshes_len){ if(!meshes) throw "meshes = nullptr"; unsigned vert_count_total = 0; for(int m=0; m<meshes_len; ++m){ if(!meshes[m]) continue; meshes[m]->first_index = vert_count_total; vert_count_total += meshes[m]->verts_len; } scene_ori.reallocAll(vert_count_total); scene_wka.reallocAll(vert_count_total); scene_wkb.reallocAll(vert_count_total); scene_camera.yaw = 0.0f; scene_camera.pitch = 0.0f; scene_camera.roll = 0.0f; scene_camera.scale = 1.0f; scene_camera.x = 0.0f; scene_camera.y = 0.0f; scene_camera.z = 0.0f; int vo = 0; for(int m=0; m<meshes_len; ++m){ MeshSimple* mesh = meshes[m]; if(!mesh) continue; Vertex* verts = mesh->verts; unsigned verts_len = mesh->verts_len; for(unsigned v=0; v<verts_len; ++v){ Vertex vert = verts[v]; scene_ori.p[vo].x = vert.px; scene_ori.p[vo].y = vert.py; scene_ori.colors[vo] = vert.color; scene_ori.t[vo].x = vert.t.x; //the uv coords seem to be flipped on the y axis for some reason scene_ori.t[vo].y = 1.0f-vert.t.y; scene_ori.p_z[vo] = vert.pz; scene_ori.n[vo] = vert.n; scene_ori.indices[vo] = vo; //make uv coords wrap around if outside range of 0.0f -> 1.0f #if UV_COORD_WRAPAROUND == 1 scene_ori.t[vo].x = fmodf(scene_ori.t[vo].x, 1.0f); scene_ori.t[vo].y = fmodf(scene_ori.t[vo].y, 1.0f); if(scene_ori.t[vo].x < 0.0f) scene_ori.t[vo].x += 1.0f; if(scene_ori.t[vo].y < 0.0f) scene_ori.t[vo].y += 1.0f; #endif ++vo; } } } void rotateWka(Location p, unsigned loop_start, unsigned loop_end){ loop_end = MIN(loop_end, scene_wka.num_vertices); loop_start = MIN(loop_start, loop_end); const float cosYaw = cosf(p.yaw ); const float sinYaw = sinf(p.yaw ); const float cosPitch = cosf(p.pitch); const float sinPitch = sinf(p.pitch); const float cosRoll = cosf(p.roll ); const float sinRoll = sinf(p.roll ); for(unsigned i=loop_start; i<loop_end; ++i){ //vertex positions float x0 = scene_wka.p[i].x; float y0 = scene_wka.p[i].y; float z0 = scene_wka.p_z[i]; float x1 = cosYaw *x0 + sinYaw *z0; float z1 = -sinYaw *x0 + cosYaw *z0; float y1 = cosPitch*y0 - sinPitch*z1; scene_wka.p_z[i] = sinPitch*y0 + cosPitch*z1; scene_wka.p[i].x = cosRoll *x1 - sinRoll *y1; scene_wka.p[i].y = sinRoll *x1 + cosRoll *y1; //vertex normals (currently unused) /* tbd: uncomment these once they start being used x0 = scene_wka.n[i].x; y0 = scene_wka.n[i].y; z0 = scene_wka.n[i].z; x1 = cosYaw *x0 + sinYaw *z0; z1 = -sinYaw *x0 + cosYaw *z0; y1 = cosPitch*y0 - sinPitch*z1; scene_wka.n[i].x = sinPitch*y0 + cosPitch*z1; scene_wka.n[i].y = cosRoll *x1 - sinRoll *y1; scene_wka.n[i].z = sinRoll *x1 + cosRoll *y1; */ } } void scaleWka(Location p, unsigned loop_start, unsigned loop_end){ loop_end = MIN(loop_end, scene_wka.num_vertices); loop_start = MIN(loop_start, loop_end); if(p.scale != 0.0f){ for(unsigned i=loop_start; i<loop_end; ++i){ scene_wka.p[i].x *= p.scale; scene_wka.p[i].y *= p.scale; scene_wka.p_z[i] *= p.scale; } } else { memory::set(&scene_wka.p[loop_start], 0, (loop_end-loop_start)*sizeof(scene_wka.p[loop_start])); memory::set(&scene_wka.p_z[loop_start], 0, (loop_end-loop_start)*sizeof(scene_wka.p_z[loop_start])); } } void translateWka(Location p, unsigned loop_start, unsigned loop_end){ loop_end = MIN(loop_end, scene_wka.num_vertices); loop_start = MIN(loop_start, loop_end); for(unsigned i=loop_start; i<loop_end; ++i){ scene_wka.p[i].x += p.x; scene_wka.p[i].y += p.y; scene_wka.p_z[i] += p.z; } } //operates on scene_wka, while assuming that //the contents of scene_ori were just copied //if mesh == nullptr, translate camera (AKA the entirety of wka) void sceneApplyTransform(MeshSimple* mesh){ if(mesh != nullptr){ const Location p = mesh->p; const unsigned loop_start = mesh->first_index; const unsigned loop_end = loop_start + mesh->verts_len; const bool shouldRotate = p.yaw || p.pitch || p.roll; const bool shouldScale = p.scale != 1.0f; const bool shouldTranslate = p.x || p.y || p.z; //if mesh is to be transformed, order is: rotate, scale, translate if(shouldRotate ) rotateWka (p, loop_start, loop_end); if(shouldScale ) scaleWka (p, loop_start, loop_end); if(shouldTranslate) translateWka(p, loop_start, loop_end); } else { Location _p = scene_camera; _p.yaw = -_p.yaw; _p.pitch = -_p.pitch; _p.roll = -_p.roll; if(_p.scale != 0.0f) _p.scale = 1.0f/_p.scale; _p.x = -_p.x; _p.y = -_p.y; _p.z = -_p.z; const Location p = _p; const unsigned loop_start = 0; const unsigned loop_end = scene_wka.num_vertices; const bool shouldRotate = p.yaw || p.pitch || p.roll; const bool shouldScale = p.scale != 1.0f; const bool shouldTranslate = p.x || p.y || p.z; //if camera is to be transformed, order is: translate, rotate, scale if(shouldTranslate) translateWka(p, loop_start, loop_end); if(shouldRotate ) rotateWka (p, loop_start, loop_end); if(shouldScale ) scaleWka (p, loop_start, loop_end); } } #define CBF_ADD_VERT { \ scene_wkb.p [scene_wkb.num_vertices] = scene_wka.p[i]; \ scene_wkb.colors [scene_wkb.num_vertices] = scene_wka.colors[i]; \ scene_wkb.t [scene_wkb.num_vertices] = scene_wka.t[i]; \ /*redundant; indices are set at sceneClipPlanesAndLumFade*/ \ /*scene_wkb.indices[scene_wkb.num_vertices] = scene_wkb.num_vertices;*/ \ scene_wkb.p_z [scene_wkb.num_vertices] = scene_wka.p_z[i]; \ scene_wkb.n [scene_wkb.num_vertices] = scene_wka.n[i]; \ scene_wkb.mids [scene_wkb.num_vertices] = midpoint; \ ++scene_wkb.num_vertices; } //reminder, dot product is: "(a.x * b.x) + (a.y * b.y) + (a.z * b.z)" static inline bool isBackFace(Vec3 midpoint, Vec3 surface_normal){ return ( (-midpoint.x*surface_normal.x) + (-midpoint.y*surface_normal.y) + (-midpoint.z*surface_normal.z) ) < 0.0f; } //wkb = backface_cull(wka) void sceneCullBackFaces(){ scene_wkb.num_vertices = 0; for(unsigned i=0; i<scene_wka.num_vertices;){ unsigned _i = i; Vec3 a(scene_wka.p[_i].x, scene_wka.p[_i].y, scene_wka.p_z[_i]); ++_i; Vec3 b(scene_wka.p[_i].x, scene_wka.p[_i].y, scene_wka.p_z[_i]); ++_i; Vec3 c(scene_wka.p[_i].x, scene_wka.p[_i].y, scene_wka.p_z[_i]); ++_i; Vec3 u(b.x-a.x, b.y-a.y, b.z-a.z); Vec3 v(c.x-a.x, c.y-a.y, c.z-a.z); Vec3 midpoint; midpoint.x = (a.x + b.x + c.x) * 0.333333333f; midpoint.y = (a.y + b.y + c.y) * 0.333333333f; midpoint.z = (a.z + b.z + c.z) * 0.333333333f; Vec3 surface_normal; surface_normal.x = u.y*v.z - u.z*v.y; surface_normal.y = u.z*v.x - u.x*v.z; surface_normal.z = u.x*v.y - u.y*v.x; if(!isBackFace(midpoint, surface_normal)){ CBF_ADD_VERT; ++i; CBF_ADD_VERT; ++i; CBF_ADD_VERT; ++i; } else { i += 3; } } } #define CPLF_ADD_VERT(z_) { \ luminosity = 1.0f - ((z_-CLIP_NEAR)/(CLIP_FAR-CLIP_NEAR)); \ color = (scene_wkb.colors[i].v&255) * luminosity; \ color |= color<< 8; \ color |= color<<16; \ \ scene_wka.p [scene_wka.num_vertices] = scene_wkb.p[i]; \ scene_wka.colors [scene_wka.num_vertices].v = color; \ scene_wka.t [scene_wka.num_vertices] = scene_wkb.t[i]; \ scene_wka.indices[scene_wka.num_vertices] = scene_wka.num_vertices; \ scene_wka.p_z [scene_wka.num_vertices] = scene_wkb.p_z[i]; \ scene_wka.n [scene_wka.num_vertices] = scene_wkb.n[i]; \ scene_wka.mids [scene_wka.num_vertices] = scene_wkb.mids[i]; \ ++scene_wka.num_vertices; } //wka = plane_clip(wkb) //wka = apply_luminosity_fade(wka) void sceneClipPlanesAndLumFade(){ #if PRECISE_CLIPPING == 0 scene_wka.num_vertices = 0; for(unsigned i=0; i<scene_wkb.num_vertices;){ unsigned _i = i; float za = scene_wkb.p_z[_i]; ++_i; float zb = scene_wkb.p_z[_i]; ++_i; float zc = scene_wkb.p_z[_i]; ++_i; #ifndef CLIP_NEAR #define CLIP_NEAR 1.0f #define CLIP_NEAR_WAS_UNDEF #endif //if triangle is out of bounds z-wise, don't append it to wka if(za < CLIP_NEAR || za >= CLIP_FAR || zb < CLIP_NEAR || zb >= CLIP_FAR || zc < CLIP_NEAR || zc >= CLIP_FAR ) { i += 3; } else { float luminosity; unsigned color; CPLF_ADD_VERT(za); ++i; CPLF_ADD_VERT(zb); ++i; CPLF_ADD_VERT(zc); ++i; } #ifdef CLIP_NEAR_WAS_UNDEF #undef CLIP_NEAR #undef CLIP_NEAR_WAS_UNDEF #endif } #else scene_wka.setAll(scene_wkb); #endif } #define SP_ADD_VERT(_p) { \ scene_wkb.p [scene_wkb.num_vertices].x = _p.x; \ scene_wkb.p [scene_wkb.num_vertices].y = _p.y; \ scene_wkb.colors [scene_wkb.num_vertices] = scene_wka.colors[i]; \ scene_wkb.t [scene_wkb.num_vertices] = scene_wka.t[i]; \ scene_wkb.indices[scene_wkb.num_vertices] = scene_wkb.num_vertices; \ scene_wkb.p_z [scene_wkb.num_vertices] = _p.z; \ scene_wkb.n [scene_wkb.num_vertices] = scene_wka.n[i]; \ scene_wkb.mids [scene_wkb.num_vertices] = scene_wka.mids[i]; \ ++scene_wkb.num_vertices; } #define SIMPLE_PROJECTION 0 //wkb = project(wka) (also culls triangles that are off-screen) void sceneProject(float viewportMultiplierX, float viewportMultiplierY){ #if SIMPLE_PROJECTION == 1 scene_wkb.setAll(scene_wka); // /=z for(unsigned i=0; i<scene_wkb.num_vertices; ++i){ scene_wkb.p[i].x /= scene_wkb.p_z[i]; scene_wkb.p[i].y /= scene_wkb.p_z[i]; } // move to screenspace for(unsigned i=0; i<scene_wkb.num_vertices; ++i){ scene_wkb.p[i].x *= CNV_W/2; scene_wkb.p[i].x += CNV_W/2; scene_wkb.p[i].y *= -CNV_H/2; scene_wkb.p[i].y += CNV_H/2; } #else scene_wkb.num_vertices = 0; //for dividing via multiplying by the inverse of the divisor float v_x_inv = 1.0f/viewportMultiplierX; float v_y_inv = 1.0f/viewportMultiplierY; for(unsigned i=0; i<scene_wka.num_vertices;){ unsigned _i = i; Vec3 a(scene_wka.p[_i].x, scene_wka.p[_i].y, scene_wka.p_z[_i]); ++_i; Vec3 b(scene_wka.p[_i].x, scene_wka.p[_i].y, scene_wka.p_z[_i]); ++_i; Vec3 c(scene_wka.p[_i].x, scene_wka.p[_i].y, scene_wka.p_z[_i]); ++_i; // (P*d)/z = P' // *= d (near plane) #ifdef CLIP_NEAR //if undefined, CLIP_NEAR is assumed to equal 1.0f a.x *= CLIP_NEAR, a.y *= CLIP_NEAR; b.x *= CLIP_NEAR, b.y *= CLIP_NEAR; c.x *= CLIP_NEAR, c.y *= CLIP_NEAR; #endif // /= z a.x /= a.z, a.y /= a.z; b.x /= b.z, b.y /= b.z; c.x /= c.z, c.y /= c.z; // (P'*C)/V // *C a.x *= CNV_W/2, a.y *= -CNV_H/2; //(-canvas_height, otherwise things will b.x *= CNV_W/2, b.y *= -CNV_H/2; //appear upside-down!) c.x *= CNV_W/2, c.y *= -CNV_H/2; // /V a.x *= v_x_inv, a.y *= v_y_inv; //remember, this is actually a division b.x *= v_x_inv, b.y *= v_y_inv; c.x *= v_x_inv, c.y *= v_y_inv; //push right and down into screen space a.x += CNV_W/2, a.y += CNV_H/2; b.x += CNV_W/2, b.y += CNV_H/2; c.x += CNV_W/2, c.y += CNV_H/2; //copy triangle to wkb, unless all 3 points are off-screen if( (a.x < 0 || a.x >= CNV_W || a.y < 0 || a.y >= CNV_H) && (b.x < 0 || b.x >= CNV_W || b.y < 0 || b.y >= CNV_H) && (c.x < 0 || c.x >= CNV_W || c.y < 0 || c.y >= CNV_H) ) { i += 3; } else { SP_ADD_VERT(a); ++i; SP_ADD_VERT(b); ++i; SP_ADD_VERT(c); ++i; } } #endif } void sceneZSort(){ //compiler complains if i try to capture scene_wkb directly GeometryBuffer wkb_copy = scene_wkb; std::sort(wkb_copy.indices_v, wkb_copy.indices_v+wkb_copy.num_vertices/3, [&wkb_copy](Vec3Int a, Vec3Int b){ return wkb_copy.mids[b.x].z<wkb_copy.mids[a.x].z; }); } void scenePS1Geometry(GeometryBuffer& buffer){ #if PS1_SNAP > 0 unsigned num_vertices = buffer.num_vertices; Vec2* xy = buffer.p; float* z = buffer.p_z; for(unsigned i=0; i<num_vertices; ++i){ #define _PS1_SNAP (PS1_SNAP PS1_SNAP_MOD) #define VERT_SNAP(value) ( (float)((s64)((value)*_PS1_SNAP)) / _PS1_SNAP ) xy[i].x = VERT_SNAP(xy[i].x); xy[i].y = VERT_SNAP(xy[i].y); z[i] = VERT_SNAP(z[i] ); } #endif } /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\src\Text.cpp": #include <scene.hpp> char _fstr_failure[] = "(FSTR FAILED)"; char _fstr[2][FSTR_LEN]; int _fstr_which = 0; Text* text_p = nullptr; //assumes that font image uses accepted dimensions and formatting, //and that black is used as the transparent color Text::Text(const char* bmpPath){ if(!file::exists(bmpPath)) throw fstr("font bitmap \"%s\" doesn't exist", bmpPath); SDL_Surface* _input = SDL_LoadBMP(bmpPath); if(!_input) throw SDL_GetError(); SDL_Surface* input = SDL_ConvertSurfaceFormat(_input, SDL_PIXELFORMAT_ABGR8888, 0); SDL_FreeSurface(_input); //shouldn't overwrite sdl error if(!input) throw SDL_GetError(); SDL_SetColorKey(input, SDL_TRUE, 0xff000000); w = (input->w/16)-1; h = (input->h/16)-1; if(!canvas) throw "canvas object doesn't exist"; atlas = SDL_CreateTextureFromSurface(canvas->getRnd(), input); SDL_FreeSurface(input); if(!atlas) throw SDL_GetError(); } SDL_Point Text::getTextSize(const char* text, float scale){ if(!text) throw "string given to Text was nullptr"; SDL_Point scaled_dimensions = { MAX((int)(w*scale),1), MAX((int)(h*scale),1) }; SDL_Point size = { 0, scaled_dimensions.y*(*text!=0) }; char chr; int max_width = 0; while( (chr=*(text++)) ) if(chr == '\n'){ if(max_width < size.x) max_width = size.x; size.x = 0; size.y += scaled_dimensions.y; } else { size.x += scaled_dimensions.x; } if(size.x < max_width) size.x = max_width; return size; } void Text::draw(int x, int y, const char* _text, unsigned maxLen, float scale){ if(!_text) throw "string given to Text was nullptr"; bool cnvWasTarget = canvas->isCnvTarget(); SDL_Renderer* rnd = canvas->getRnd(); SDL_Texture* cnv = canvas->getCnv(); if(!cnvWasTarget && SDL_SetRenderTarget(rnd,cnv)<0) throw SDL_GetError(); //if high bit of maxLen is set, ignore automatic origin rules if(!(maxLen&0x80000000)){ SDL_Point textSize; bool getTextSize_called = false; if(x < 0){ textSize = getTextSize(_text, scale); getTextSize_called = true; if(x == CENTER_TEXT) x = CNV_W/2 - textSize.x/2; else x = CNV_W - textSize.x + x + 1; } if(y < 0){ if(!getTextSize_called) textSize = getTextSize(_text, scale); if(y == CENTER_TEXT) y = CNV_H/2 - textSize.y/2; else y = CNV_H - textSize.y + y + 1; } } else { maxLen &= 0x7fffffff; } SDL_Rect src = {0,0, w,h}; SDL_Rect dst = {x,y, MAX((int)(w*scale), 1), MAX((int)(h*scale), 1)}; int src_stride_w = src.w + 1; int src_stride_h = src.h + 1; const unsigned char* text = (unsigned char*)_text; unsigned char chr; if(!maxLen) --maxLen; while( (chr=*(text++)) && (maxLen--) ) if(chr == '\n'){ dst.x = x; dst.y += dst.h; } else { src.x = ( chr &15)*src_stride_w + 1; src.y = ((chr>>4)&15)*src_stride_h + 1; if(SDL_RenderCopy(rnd, atlas, &src, &dst)<0) throw SDL_GetError(); dst.x += dst.w; } if(!cnvWasTarget && SDL_SetRenderTarget(rnd,nullptr)<0) throw SDL_GetError(); } void Text::drawBox(int x, int y, const char* text, unsigned maxLen, float scale){ SDL_Point textSize = getTextSize(text, scale); SDL_Rect dst = {x,y, textSize.x+3,textSize.y+3}; if(dst.x == CENTER_TEXT) dst.x = CNV_W/2 - dst.w/2; else if(dst.x < 0) dst.x = CNV_W - dst.w + dst.x + 1; if(dst.y == CENTER_TEXT) dst.y = CNV_H/2 - dst.h/2; else if(dst.y < 0) dst.y = CNV_H - dst.h + dst.y + 1; #define B_C 0xc0 #define BORDER_COLOR ABGR(B_C,B_C,B_C,255) canvas->renderFillRectBordered(dst, BORDER_COLOR, 0xff000000); draw(dst.x+2,dst.y+2, text, 0x80000000|maxLen, scale); } /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\include\common.hpp": #ifndef _UTILS_COMMON_HPP #define _UTILS_COMMON_HPP #include <SDL2/SDL.h> //#include <immintrin.h> #include <algorithm> //for std::sort #define INT_S32_MAX (0x7fffffff) #define INT_U64_MSb (0x8000000000000000) #define _pi (3.1415926535897932384626433f) //overkill for single-precision lol #define _2pi (_pi*2) #define _pi2 (_pi/2) #define _pi4 (_pi/4) #define _pi8 (_pi/8) #define MIN(_a, _b) ( ((_a)<(_b)) ? (_a) : (_b) ) #define MAX(_a, _b) ( ((_a)>(_b)) ? (_a) : (_b) ) #define MIN3(_a, _b, _c) MIN(MIN(_a, _b), _c) #define MAX3(_a, _b, _c) MAX(MAX(_a, _b), _c) #define CLAMP(_n, _mn, _mx) MIN(MAX(_n, _mn),_mx) #define SAFE_FREE(ptr) if(ptr){ memory::free(&ptr); ptr = nullptr; } #define SAFE_FREE_SIMD(ptr) if(ptr){ memory::freeSIMD(&ptr); ptr = nullptr; } #define SAFE_DELETE(ptr) if(ptr){ delete ptr; ptr = nullptr; } #ifdef _DEBUG #define Log(...) SDL_Log(__VA_ARGS__) #else #define Log(...) #endif /* _DEBUG */ #define loghere Log("\"%s\": %3i",__FILE__,__LINE__); typedef Uint8 u8; typedef Uint16 u16; typedef Uint32 u32; typedef Uint64 u64; typedef Sint8 s8; typedef Sint16 s16; typedef Sint32 s32; typedef Sint64 s64; typedef float f32; typedef double f64; namespace shape { typedef SDL_Point point; typedef SDL_FPoint pointf; typedef SDL_Rect rect; typedef SDL_FRect rectf; }; #endif /* _UTILS_COMMON_HPP */ /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\include\file.hpp": #ifndef _UTILS_FILE_HPP #define _UTILS_FILE_HPP #include "common.hpp" namespace file { bool exists(const char* filePath); s32 getSize(const char* filePath); }; struct BinaryData { //16B union { void* const ptr; char* const data = nullptr; }; const size_t data_len = 0; BinaryData(const char* filePath); BinaryData(const void* data, size_t dataSize){ loadFromMemory(data, dataSize); } ~BinaryData(); //will (re)allocate dataSize bytes for _data, before copying data to _data //(if data is nullptr, this behaves as if you were realloc'ing _data) //(also, the actual num of bytes copied is equal to MIN(dataSize, data_len)) void loadFromMemory(const void* data, size_t dataSize); }; #endif /* _UTILS_FILEDATA_HPP */ /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\include\memory.hpp": #ifndef _UTILS_MEMORY_HPP #define _UTILS_MEMORY_HPP #include "common.hpp" namespace memory { void* alloc(size_t size); //this version of free is especially useful, because it's safe to pass nullptr to! //this rule goes for both ptr_p and *ptr_p #ifndef free #define free _free_real #endif /* free */ void _free_real(void* ptr_p); //^^i would call this just free, but that seems to cause conflicts //if calling free from memory::free, resulting in recursion >:( #ifndef realloc #define realloc _realloc_real #endif /* realloc */ void* _realloc_real(void* ptr_p, size_t newSize); //^^(for both free and realloc, a void** declared in function as void*, // so you don't need to explicitly cast it to void**) size_t getNumAllocations(); void* allocSIMD(size_t size); void freeSIMD(void* ptr_p); void* reallocSIMD(void* ptr_p, size_t newSize); size_t getSIMDAlignment(); struct wrapper { void* ptr = nullptr; inline wrapper(size_t size){ if(!(ptr=alloc(size))) throw "Failed to allocate memory"; } inline ~wrapper(){ _free_real(&ptr); } inline void* reallocate(size_t newSize){ return _realloc_real(&ptr, newSize); } }; struct wrapperSIMD { void* ptr = nullptr; inline wrapperSIMD(size_t size){ if(!(ptr=allocSIMD(size))) throw "Failed to allocate SIMD memory"; } inline ~wrapperSIMD(){ freeSIMD(&ptr); } inline void* reallocate(size_t newSize){ return reallocSIMD(&ptr, newSize); } }; void* set(void* dst, char v, size_t size); void* copy(void* dst, const void* src, size_t size); }; /* namespace memory */ #endif /* _UTILS_MEMORY_HPP */ /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\include\scene.hpp": #ifndef _SCENE_HPP #define _SCENE_HPP #include <common.hpp> #include <file.hpp> #include <memory.hpp> #include <Text.hpp> #include <fast_obj.hpp> /* //fov (in degrees) to viewport dimensions, assuming near plane = 1 //( v=tan(f/(360/pi)), f=atan(v*(360/pi)) ) #define fx_360_pi (360.0/pi) #define FOV_TO_VIEWPORT(_fov ) (tan((_fov)/fx_360_pi)) #define VIEWPORT_TO_FOV(_view) (atan(_view)*fx_360_pi) */ #define WIN_W 512*2 #define WIN_H 288*2 #define CNV_DIV 2 #ifdef _DEBUG #define WIN_TITLE "Not Asteroids (DEBUG BUILD)" #else #define WIN_TITLE "Not Asteroids" #endif #define TRAP_PROMPT "(press f11 to enable fullscreen)" #define UNTRAP_PROMPT "(press f11 to enable fullscreen; esc to free mouse)" #define CNV_W ((WIN_W)/(CNV_DIV)) #define CNV_H ((WIN_H)/(CNV_DIV)) //#define CLIP_NEAR 0.8f //if undefined, near plane is assumed to be 1.0f #define CLIP_FAR 768.0f//512.0f //will simplify vertex positions to 1/PS1_SNAP'th //(if 0, feature is disabled) #define PS1_SNAP 8 #define PS1_SNAP_MOD #define cosf cosf #define sinf sinf //will make any texture coordinates wrap around to fit within the 0.0-1.0 range //(will turn 1.3 to 0.3, -0.3 to 0.7, and so on) #define UV_COORD_WRAPAROUND 1 //0&1 to disable and enable respectively //if 1, proper clip plane handling will be enabled, instead of triangles simply //disappearing the moment a triangle passes through a clip plane boundary #define PRECISE_CLIPPING 0 //(currently not implemented; keep this equal to 0) struct Vec2 { float x, y; inline Vec2(float _x, float _y) : x(_x), y(_y) {} inline Vec2() : x(0), y(0) {} }; struct Vec3 { float x, y, z; inline Vec3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} inline Vec3() : x(0), y(0), z(0) {} inline Vec3& operator*=(const float& m){ x*=m, y*=m, z*=m; return *this; } } __attribute__((packed)); struct Vec3Int { int x, y, z; } __attribute__((packed)); struct Vec4 { float x, y, z, w; }; union ABGR { unsigned v; struct { unsigned char r; unsigned char g; unsigned char b; unsigned char a; }; inline ABGR() : v(0) {} inline ABGR(unsigned _v) : v(_v) {} inline ABGR(char _r, char _g, char _b, char _a) : r(_r), g(_g), b(_b), a(_a) {} }; struct Vertex { //40B; 3D vertex union { Vec3 p; struct { float px, py, pz; } __attribute__((packed)); }; ABGR color; union { Vec2 t; struct { float tx, ty; }; }; union { Vec3 n; struct { float nx, ny, nz; } __attribute__((packed)); }; unsigned _padding32; }; struct Triangle { Vertex a, b, c; }; struct Location { //(transformations are applied in order of how they appear here) float yaw, pitch, roll; float scale; float x, y, z; } __attribute__((packed)); class Canvas; struct GeometryBuffer { //fed into SDL_RenderGeometryRaw Vec2* p = nullptr; //vertex positions ABGR* colors = nullptr; Vec2* t = nullptr; //uv coords union { Vec3Int* indices_v; int* indices = nullptr; }; //(z axis is separated, since SDL_RenderGeometryRaw doesn't use it) float* p_z = nullptr; Vec3* n = nullptr; //normals Vec3* mids = nullptr; //face midpoints size_t num_vertices = 0; void reallocAll(size_t n_v){ if(0){ _oom_error: throw "failed to reallocate memory for GeometryBuffer"; } if(!memory::reallocSIMD(&p, n_v * sizeof(p[0] ))) goto _oom_error; if(!memory::reallocSIMD(&colors, n_v * sizeof(colors[0] ))) goto _oom_error; if(!memory::reallocSIMD(&t, n_v * sizeof(t[0] ))) goto _oom_error; if(!memory::reallocSIMD(&indices, n_v * sizeof(indices[0]))) goto _oom_error; if(!memory::reallocSIMD(&p_z, n_v * sizeof(p_z[0] ))) goto _oom_error; if(!memory::reallocSIMD(&n, n_v * sizeof(n[0] ))) goto _oom_error; if(!memory::reallocSIMD(&mids, n_v * sizeof(mids[0] ))) goto _oom_error; num_vertices = n_v; } void freeAll(){ SAFE_FREE_SIMD(p ); SAFE_FREE_SIMD(colors ); SAFE_FREE_SIMD(t ); SAFE_FREE_SIMD(indices); SAFE_FREE_SIMD(p_z ); SAFE_FREE_SIMD(n ); SAFE_FREE_SIMD(mids ); num_vertices = 0; } //assumes both this and src have the same or larger allocated sizes, //otherwise this will probably break catastrophically void setAll(GeometryBuffer& src){ memory::copy(p, src.p, src.num_vertices * sizeof(p[0] )); memory::copy(colors, src.colors, src.num_vertices * sizeof(colors[0] )); memory::copy(t, src.t, src.num_vertices * sizeof(t[0] )); memory::copy(indices, src.indices, src.num_vertices * sizeof(indices[0])); memory::copy(p_z, src.p_z, src.num_vertices * sizeof(p_z[0] )); memory::copy(n, src.n, src.num_vertices * sizeof(n[0] )); memory::copy(mids, src.mids, src.num_vertices * sizeof(mids[0] )); num_vertices = src.num_vertices; } }; extern Canvas* canvas; extern SDL_Surface* geometrySrf; extern SDL_Texture* geometryTex; //pipeline goes as follows: // //wka = ori (deep copy) //transform meshes in wka (in order of operation): // rotate // scale // translate //wka = camera_rotate(wka): // translate // rotate // scale // //wkb = backface_cull(wka) // //wka = plane_clip(wkb) //wka = apply_luminosity_fade(wka) // //wkb = project(wka) (also culls triangles that are off-screen) //wkb = z_sort(wkb) (make sure to reset indices before doing this!) //render(wkb) extern GeometryBuffer scene_ori; //original state extern GeometryBuffer scene_wka; //work buffer a extern GeometryBuffer scene_wkb; //work buffer b //camera's position, rotation, and scale //(unlike meshes, the camera's scale is the inverse of the scene scale result, // meaning that the scene's scale is 1.0f/scene_camera.scale. For example, // a scene_camera.scale of 0.25f will grow the scene by 4x) extern Location scene_camera; class Canvas { SDL_Window* win = nullptr; SDL_Renderer* rnd = nullptr; SDL_Texture* cnv = nullptr; bool cnvIsTarget = false; public: Canvas(int winW, int winH, int cnvW, int cnvH, const char* title = nullptr); ~Canvas(); inline SDL_Window* getWin(){ return win; }; inline SDL_Renderer* getRnd(){ return rnd; }; inline SDL_Texture* getCnv(){ return cnv; }; inline bool isCnvTarget(){ return cnvIsTarget; } inline void clear(ABGR c = 0xff000000){ if(!cnvIsTarget){ if(SDL_SetRenderTarget(rnd, cnv)<0) throw SDL_GetError(); cnvIsTarget = true; } SDL_SetRenderDrawColor(rnd, c.r, c.g, c.b, c.a); SDL_RenderClear(rnd); } inline void renderGeometry(const GeometryBuffer& buffer){ if(!buffer.num_vertices) return; if(!cnvIsTarget){ if(SDL_SetRenderTarget(rnd, cnv)<0) throw SDL_GetError(); cnvIsTarget = true; } if(SDL_RenderGeometryRaw(rnd, geometryTex, (float*)buffer.p, sizeof(buffer.p[0]), (SDL_Color*)buffer.colors, sizeof(buffer.colors[0]), (float*)buffer.t, sizeof(buffer.t[0]), buffer.num_vertices, buffer.indices, buffer.num_vertices, sizeof(buffer.indices[0])) < 0) { throw SDL_GetError(); } } inline void renderFillRect(const SDL_Rect* dst, ABGR c){ if(!cnvIsTarget){ if(SDL_SetRenderTarget(rnd, cnv)<0) throw SDL_GetError(); cnvIsTarget = true; } if(SDL_SetRenderDrawColor(rnd, c.r, c.g, c.b, c.a)<0) throw SDL_GetError(); if(SDL_RenderFillRect(rnd, dst)<0) throw SDL_GetError(); } inline void renderFillRectBordered(SDL_Rect dst, ABGR c_bdr = 0xffffffff, ABGR c_bkg = 0xff000000) { if(!cnvIsTarget){ if(SDL_SetRenderTarget(rnd, cnv)<0) throw SDL_GetError(); cnvIsTarget = true; } if(SDL_SetRenderDrawColor(rnd, c_bkg.r, c_bkg.g, c_bkg.b, c_bkg.a)<0) throw SDL_GetError(); if(SDL_RenderFillRect(rnd, &dst)<0) throw SDL_GetError(); if(SDL_SetRenderDrawColor(rnd, c_bdr.r, c_bdr.g, c_bdr.b, c_bdr.a)<0) throw SDL_GetError(); if(SDL_RenderDrawRect(rnd, &dst)<0) throw SDL_GetError(); } inline void renderCopy(SDL_Texture& texture, const SDL_Rect* src, SDL_Rect* dst){ if(!cnvIsTarget){ if(SDL_SetRenderTarget(rnd, cnv)<0) throw SDL_GetError(); cnvIsTarget = true; } if(SDL_RenderCopy(rnd, &texture, src, dst)<0) throw SDL_GetError(); } inline void present(unsigned delayMS = 16){ if(cnvIsTarget){ if(SDL_SetRenderTarget(rnd, nullptr)<0) throw SDL_GetError(); cnvIsTarget = false; } SDL_RenderCopy(rnd, cnv, nullptr, nullptr); SDL_RenderPresent(rnd); if(delayMS > 0) SDL_Delay(delayMS); } }; struct MeshSimple { //every 3 verts makes a triangle (no quads nor n-gons) Vertex* verts; unsigned verts_len; // = faces_len * 3 Location p; //index of the mesh's place within scene vertices unsigned first_index; unsigned _padding32; MeshSimple(const char* fileName); ~MeshSimple(); #if defined(_DEBUG) && 1 inline Vertex& vert(size_t i){ if(i >= verts_len) throw "vert index out of bounds"; return verts[i]; } #else inline Vertex& vert(size_t i){ return verts[i]; } #endif /* defined(_DEBUG) */ }; void sceneRebuild(MeshSimple** meshes, int meshes_len); #define LOOP_END_MAX 0xffffffff //operates on scene_wka, while assuming that //the contents of scene_ori were just copied //if mesh == nullptr, rotate entirety of wka void rotateWka(Location p, unsigned loop_start, unsigned loop_end); void scaleWka(Location p, unsigned loop_start, unsigned loop_end); void translateWka(Location p, unsigned loop_start, unsigned loop_end); void sceneApplyTransform(MeshSimple* mesh); static inline void meshRotate(MeshSimple& mesh){ rotateWka(mesh.p, mesh.first_index, mesh.first_index+mesh.verts_len); } static inline void meshScale(MeshSimple& mesh){ scaleWka(mesh.p, mesh.first_index, mesh.first_index+mesh.verts_len); } static inline void meshTranslate(MeshSimple& mesh){ translateWka(mesh.p, mesh.first_index, mesh.first_index+mesh.verts_len); } static inline void meshTransform(MeshSimple& mesh){ sceneApplyTransform(&mesh); } static inline void cameraTransform(){ sceneApplyTransform(nullptr); } //wkb = backface_cull(wka) void sceneCullBackFaces(); //wka = plane_clip(wkb) //wka = apply_luminosity_fade(wka) void sceneClipPlanesAndLumFade(); //wkb = project(wka) (also culls triangles that are off-screen) void sceneProject(float viewportMultiplierX, float viewportMultiplierY); //wkb = z_sort(wkb) (make sure to reset indices before doing this!) void sceneZSort(); //create a vertex snap effect on wkb (optional) void scenePS1Geometry(GeometryBuffer& buffer); //maybe next time i could do an xor to determine which //work buffer is used each operation... static inline void sceneRender(float viewportMultiplierX = ((float)CNV_W)/((float)CNV_H), float viewportMultiplierY = 1.0f) { sceneCullBackFaces(); sceneClipPlanesAndLumFade(); #if PS1_SNAP > 0 scenePS1Geometry(scene_wka); //(optional) #endif sceneProject(viewportMultiplierX, viewportMultiplierY); //Log("number of active triangles = %4u", (unsigned)(scene_wkb.num_vertices/3)); sceneZSort(); canvas->renderGeometry(scene_wkb); } #endif /* _SCENE_HPP */ /******************************************************************************/ /******************************************************************************/ //"sdl2rnd\include\Text.hpp": #ifndef _TEXT_HPP #define _TEXT_HPP #include <SDL2/SDL.h> class Text { SDL_Texture* atlas = nullptr; int w, h; //width of each glyph public: Text(const char* bmpPath); inline ~Text(){ if(atlas){ SDL_DestroyTexture(atlas); atlas = nullptr; } } //returns the bounding box, in pixels SDL_Point getTextSize(const char* text, float scale = 1.0f); //set x and/or y to this to center the text on-screen on that axis #define CENTER_TEXT (-2147483647) //aka 0x80000001 void draw(int x, int y, const char* text, unsigned maxLen = 0, float scale = 1.0f); //(x & y can also be other values <0, which will make the text start drawing at //CNV_W-text_width+x+1 & CNV_H-text_height+y+1 respectively) //(also, the high bit of maxLen must not be set, as that is used as a flag internally) //same rules apply, but the x & y are adjusted //to match a 2-pixel border around the text void drawBox(int x, int y, const char* text, unsigned maxLen = 0, float scale = 1.0f); }; #define FSTR_LEN 4096 extern char _fstr_failure[]; // = "(FSTR FAILED)" extern char _fstr[2][FSTR_LEN]; extern int _fstr_which; //(could potentially cast result to const char* if necessary) #define fstr(...) ( \ (snprintf(_fstr[(_fstr_which^=1)],FSTR_LEN,__VA_ARGS__)>=0) \ ? _fstr[_fstr_which] : _fstr_failure \ ) extern Text* text_p; #define text(_x, _y, _maxLen, _text) text_p->draw (_x, _y, _text, _maxLen, 1.0f) #define text_box(_x, _y, _maxLen, _text) text_p->drawBox(_x, _y, _text, _maxLen, 1.0f) #define textf(_x, _y, ...) text_p->draw (_x, _y, fstr(__VA_ARGS__), 0, 1.0f) #define textf_box(_x, _y, ...) text_p->drawBox(_x, _y, fstr(__VA_ARGS__), 0, 1.0f) #endif /* _TEXT_HPP */
Optional Paste Settings
Category:
None
Cryptocurrency
Cybersecurity
Fixit
Food
Gaming
Haiku
Help
History
Housing
Jokes
Legal
Money
Movies
Music
Pets
Photo
Science
Software
Source Code
Spirit
Sports
Travel
TV
Writing
Tags:
Syntax Highlighting:
None
Bash
C
C#
C++
CSS
HTML
JSON
Java
JavaScript
Lua
Markdown (PRO members only)
Objective C
PHP
Perl
Python
Ruby
Swift
4CS
6502 ACME Cross Assembler
6502 Kick Assembler
6502 TASM/64TASS
ABAP
AIMMS
ALGOL 68
APT Sources
ARM
ASM (NASM)
ASP
ActionScript
ActionScript 3
Ada
Apache Log
AppleScript
Arduino
Asymptote
AutoIt
Autohotkey
Avisynth
Awk
BASCOM AVR
BNF
BOO
Bash
Basic4GL
Batch
BibTeX
Blitz Basic
Blitz3D
BlitzMax
BrainFuck
C
C (WinAPI)
C Intermediate Language
C for Macs
C#
C++
C++ (WinAPI)
C++ (with Qt extensions)
C: Loadrunner
CAD DCL
CAD Lisp
CFDG
CMake
COBOL
CSS
Ceylon
ChaiScript
Chapel
Clojure
Clone C
Clone C++
CoffeeScript
ColdFusion
Cuesheet
D
DCL
DCPU-16
DCS
DIV
DOT
Dart
Delphi
Delphi Prism (Oxygene)
Diff
E
ECMAScript
EPC
Easytrieve
Eiffel
Email
Erlang
Euphoria
F#
FO Language
Falcon
Filemaker
Formula One
Fortran
FreeBasic
FreeSWITCH
GAMBAS
GDB
GDScript
Game Maker
Genero
Genie
GetText
Go
Godot GLSL
Groovy
GwBasic
HQ9 Plus
HTML
HTML 5
Haskell
Haxe
HicEst
IDL
INI file
INTERCAL
IO
ISPF Panel Definition
Icon
Inno Script
J
JCL
JSON
Java
Java 5
JavaScript
Julia
KSP (Kontakt Script)
KiXtart
Kotlin
LDIF
LLVM
LOL Code
LScript
Latex
Liberty BASIC
Linden Scripting
Lisp
Loco Basic
Logtalk
Lotus Formulas
Lotus Script
Lua
M68000 Assembler
MIX Assembler
MK-61/52
MPASM
MXML
MagikSF
Make
MapBasic
Markdown (PRO members only)
MatLab
Mercury
MetaPost
Modula 2
Modula 3
Motorola 68000 HiSoft Dev
MySQL
Nagios
NetRexx
Nginx
Nim
NullSoft Installer
OCaml
OCaml Brief
Oberon 2
Objeck Programming Langua
Objective C
Octave
Open Object Rexx
OpenBSD PACKET FILTER
OpenGL Shading
Openoffice BASIC
Oracle 11
Oracle 8
Oz
PARI/GP
PCRE
PHP
PHP Brief
PL/I
PL/SQL
POV-Ray
ParaSail
Pascal
Pawn
Per
Perl
Perl 6
Phix
Pic 16
Pike
Pixel Bender
PostScript
PostgreSQL
PowerBuilder
PowerShell
ProFTPd
Progress
Prolog
Properties
ProvideX
Puppet
PureBasic
PyCon
Python
Python for S60
QBasic
QML
R
RBScript
REBOL
REG
RPM Spec
Racket
Rails
Rexx
Robots
Roff Manpage
Ruby
Ruby Gnuplot
Rust
SAS
SCL
SPARK
SPARQL
SQF
SQL
SSH Config
Scala
Scheme
Scilab
SdlBasic
Smalltalk
Smarty
StandardML
StoneScript
SuperCollider
Swift
SystemVerilog
T-SQL
TCL
TeXgraph
Tera Term
TypeScript
TypoScript
UPC
Unicon
UnrealScript
Urbi
VB.NET
VBScript
VHDL
VIM
Vala
Vedit
VeriLog
Visual Pro Log
VisualBasic
VisualFoxPro
WHOIS
WhiteSpace
Winbatch
XBasic
XML
XPP
Xojo
Xorg Config
YAML
YARA
Z80 Assembler
ZXBasic
autoconf
jQuery
mIRC
newLISP
q/kdb+
thinBasic
Paste Expiration:
Never
Burn after read
10 Minutes
1 Hour
1 Day
1 Week
2 Weeks
1 Month
6 Months
1 Year
Paste Exposure:
Public
Unlisted
Private
Folder:
(members only)
Password
NEW
Enabled
Disabled
Burn after read
NEW
Paste Name / Title:
Create New Paste
Hello
Guest
Sign Up
or
Login
Sign in with Facebook
Sign in with Twitter
Sign in with Google
You are currently not logged in, this means you can not edit or delete anything you paste.
Sign Up
or
Login
Public Pastes
setups
4 hours ago | 0.16 KB
Untitled
4 hours ago | 0.36 KB
my-push Script
4 hours ago | 0.61 KB
cholibrium
1 day ago | 1.65 KB
SMB BIS - Dimble Woods - Virtual Piano
1 day ago | 3.15 KB
buscar-productos.blade.php
PHP | 2 days ago | 2.44 KB
simple youtube video to product
PHP | 2 days ago | 3.71 KB
OoT rando seed 6/1
2 days ago | 68.21 KB
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the
Cookies Policy
.
OK, I Understand
Not a member of Pastebin yet?
Sign Up
, it unlocks many cool features!