#include #include #include int main() { using namespace sf; // Create the main window RenderWindow window(VideoMode(3840, 2160), "Hello", Style::Default, ContextSettings(24,0,2)); window.setVerticalSyncEnabled(true); // Create a sprite for the background Texture tx[2]; tx[0].loadFromFile("resources/top.jpg"); tx[1].loadFromFile("resources/wall3.jpg"); tx[1].generateMipmap(); Sprite background(tx[0]); // Set up geometry std::vector tri; auto rmap = [rem=0,p=0]() mutable { if(!rem--) rem=u"\0\1\2\3\4\5\6\7\u01A6\u01A7"[ // RLE compression with dictionary "lidjehfhfhhideiefedefedefekedeiefedefedefejfdeiefedefedefejeeieefedef" "edefeiekedefedefedefeiekedefedefedefeiefefedefedefedefeieghfhfhfhm"[p++]-'d']; return p&1; // Odd/even coding }; auto add = [&](std::initializer_list v) { tri.insert(tri.end(), v.begin(), v.end()); }; float s=20, u=.2, d=14300, p=d*2; // Create floor plane add({s*-d,0,s*-d, 0,0,u,u,u, s* d,0,s*-d, p,0,u,u,u, s* d,0,s* d, p,p,u,u,u, s* d,0,s* d, p,p,u,u,u, s*-d,0,s* d, 0,p,u,u,u, s*-d,0,s*-d, 0,0,u,u,u}); for(int p=0; p<42*29; ++p) if(int h = (rmap() ? std::rand() % 2 : 16*(4+std::rand()%8))) // Create random "buildings" { float m=s/2, q = h/s, y0=0, y1=h, d=.2+(rand()%1000)*.4e-3, u=.3+(h>2); float x = s*(p%42-21), z=s*(p/42-14), x0 = x-m, x1=x+m, z0=z-m, z1=z+m; add({x0,y1,z0, 0,0,u,u,u, x0,y1,z1, 0,1,u,u,u, x1,y1,z1, 1,1,u,u,u, //cap x1,y1,z1, 1,1,u,u,u, x1,y1,z0, 1,0,u,u,u, x0,y1,z0, 0,0,u,u,u, x0,y0,z0, 0,0,d,d,d, x0,y1,z0, 0,q,1,1,1, x1,y1,z0, 1,q,1,1,1, //side1 (constant z0) x1,y1,z0, 1,q,1,1,1, x1,y0,z0, 1,0,d,d,d, x0,y0,z0, 0,0,d,d,d, x0,y0,z1, 0,0,d,d,d, x0,y1,z1, 0,q,1,1,1, x1,y1,z1, 1,q,1,1,1, //side2 (constant z1) x1,y1,z1, 1,q,1,1,1, x1,y0,z1, 1,0,d,d,d, x0,y0,z1, 0,0,d,d,d, x0,y0,z0, 0,0,d,d,d, x0,y1,z0, 0,q,1,1,1, x0,y1,z1, 1,q,1,1,1, //side3 (constant x0) x0,y1,z1, 1,q,1,1,1, x0,y0,z1, 1,0,d,d,d, x0,y0,z0, 0,0,d,d,d, x1,y0,z0, 0,0,d,d,d, x1,y1,z0, 0,q,1,1,1, x1,y1,z1, 1,q,1,1,1, //side4 (constant x1) x1,y1,z1, 1,q,1,1,1, x1,y0,z1, 1,0,d,d,d, x1,y0,z0, 0,0,d,d,d}); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 8*sizeof(GLfloat), &tri[0]); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 8*sizeof(GLfloat), &tri[3]); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_FLOAT, 8*sizeof(GLfloat), &tri[5]); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glClearDepth(1.f); // Setup a perspective projection and view port glMatrixMode(GL_PROJECTION); glViewport(0, 0, window.getSize().x, window.getSize().y); GLfloat ratio = float(window.getSize().x) / window.getSize().y; glFrustum(-ratio, ratio, -1.f, 1.f, 1.f, 5500.f); // Start game loop float rx=0,ry=0,rz=0, mx=0,my=0,mz=-4e1f, lx=0,ly=-8,lz=250, aa=1,ab=0,ac=0,ad=0; for(bool S={},keys[17]={},exit = false; window.isOpen() && !exit; window.display()) { // Process events for(Event event; window.pollEvent(event); ) switch(event.type) { case Event::Closed: goto e; case Event::KeyReleased: S=false; goto k; case Event::KeyPressed: S=true; k: switch(event.key.code) { #define k(sym1,sym2,index) case Keyboard::sym1: \ case Keyboard::sym2: keys[index]=S; break; k(Up, Numpad8, 0) k(A, S, 10) k(Down, Numpad2, 1) k(Z, X, 11) k(Left, Numpad4, 3) k(Q, W, 12) k(Right,Numpad6, 2) k(E, R, 13) k(T, Numpad7, 4) k(B, Numpad1, 6) k(U, Numpad9, 5) k(M, Numpad3, 7) k(Num0, Subtract,8) k(P, Add, 9) k(LAlt, RAlt, 14) k(LShift,RShift,15) k(D,V,16) case Keyboard::Escape: e: exit=true; } } if(keys[16]) for(std::size_t p=0; p0.1)tri[p+1] *= 0.95; // Apply rotation delta with hysteresis rx = rx*.8f + .2f*(keys[14]?0:(keys[0]-keys[1])); ry = ry*.8f + .2f*(keys[14]?0:(keys[2]-keys[3])); rz = rz*.8f + .2f*(int(keys[4]||keys[12])-(keys[5]||keys[13])); // Desired movement delta float Mx=int(keys[6] || (keys[14]&&keys[3])) - (keys[7] || (keys[14]&&keys[2])), My=int(keys[9] || (keys[14]&&keys[1])) - (keys[8] || (keys[14]&&keys[0])), Mz=int(keys[10]) - keys[11]; // Create rotation matrix from inverted player angle (unit quaternion) float aia=aa, aib=-ab, aic=-ac, aid=-ad; GLfloat transform[16]{ 1-2*(aic*aic+aid*aid), 2*(aib*aic-aia*aid), 2*(aib*aid+aia*aic), 0, 2*(aib*aic+aia*aid), 1-2*(aib*aib+aid*aid), 2*(aic*aid-aia*aib), 0, 2*(aib*aid-aia*aic), 2*(aic*aid+aia*aib), 1-2*(aib*aib+aic*aic), 0, 0,0,0,1 }; if(float rlen = std::sqrt(rx*rx+ry*ry+rz*rz); rlen > 1e-3f) { // Calculate real rotation delta taking player angle to account float rnx=rx/rlen, rny=ry/rlen, rnz=rz/rlen; // normalized rotation delta vector float rax=transform[0]*rnx + transform[1]*rny + transform[2]*rnz; float ray=transform[4]*rnx + transform[5]*rny + transform[6]*rnz; float raz=transform[8]*rnx + transform[9]*rny + transform[10]*rnz; // Create rotation quaternion (r) float theta = rlen*0.03f, c = std::cos(theta*.5f), s = std::sin(theta*.5f); float ra=1*c, rb=s*rax, rc=s*ray, rd=s*raz; // Update player angle (a) by multiplying it by the rotation quaternion (r) float naa = ra*aa - rb*ab - rc*ac - rd*ad; float nab = rb*aa + ra*ab + rd*ac - rc*ad; float nac = rc*aa - rd*ab + ra*ac + rb*ad; float nad = rd*aa + rc*ab - rb*ac + ra*ad; float len = std::sqrt(naa*naa + nab*nab + nac*nac + nad*nad); if(len<1e-6) len=1; aa=naa/len; ab=nab/len; ac=nac/len; ad=nad/len; // Note: The above cos() and sin() were the ONLY trigonometric calculations // in this rotation code. And they only control the rate of turning, // not the angle of turning. You could replace them with compile-time // constants and the only downside would be a constant rate of turning // (either on or off). Such is the power of quaternions. } // Apply player movement with hystesis float mlen = std::sqrt(Mx*Mx + My*My + Mz*Mz)/0.7; if(mlen > 1e-3f) { Mx /= mlen; My /= mlen; Mz /= mlen; } mx = mx*.9f + .1f*(transform[0]*Mx + transform[1]*My + transform[2]*Mz); my = my*.9f + .1f*(transform[4]*Mx + transform[5]*My + transform[6]*Mz); mz = mz*.9f + .1f*(transform[8]*Mx + transform[9]*My + transform[10]*Mz); // Update player position lx += mx; ly += my; lz += mz; // Instruct OpenGL about the new view transform[12] = lx*transform[0]+ly*transform[4]+lz*transform[8]; transform[13] = lx*transform[1]+ly*transform[5]+lz*transform[9]; transform[14] = lx*transform[2]+ly*transform[6]+lz*transform[10]; glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(transform); // Draw the background window.pushGLStates(); window.draw(background); window.popGLStates(); // Draw everything else Texture::bind(&tx[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glDrawArrays(GL_TRIANGLES, 0, tri.size()/8); } }