#include "client/Game.h" #include "client/utils/Utils.h" #include "rendering/Renderer.h" #include "common/utils/String.h" #include "common/utils/Random.h" #include "math/Quaternion.h" Game::Game(const Control& control, const Camera& camera, Ray& ray, const Clock& fps, const Clock& tps, RenderSettings& renderSettings) : control(control), camera(camera), ray(ray), fps(fps), tps(tps), renderSettings(renderSettings), lengthAngle(240.0f), widthAngle(20.0f), world(blockRegistry), worldRenderer(world), pointIndex(0), moveSpeed(0.25f), movedLength(0.0f), mode(Mode::AUTO) { Random r(0); float h = World::WORLD_SIZE * 0.75f; float mid = World::WORLD_SIZE * 0.5f; float randLength = World::WORLD_SIZE * 0.125f; pos.set(0, h, 0); for(uint i = 0; i < cameraPoints.getCapacity(); i++) { Vector offset(mid, h, mid); offset.add(Vector(r.nextFloat(randLength), r.nextFloat(randLength), r.nextFloat(randLength))); Vector v; v.setAngles(i * 360.0f / cameraPoints.getCapacity(), 0.0f).mul(mid * 0.5f).add(offset); cameraPoints.add( {v, 0.0f}); } for(uint i = 0; i < cameraPoints.getLength(); i++) { cameraPoints[i].distance = distance(i, 20); } } void Game::tick() { if(mode == Mode::PLAYER) { const float speed = 0.25f; if(control.keys.down.isDown()) { pos.addMul(camera.getFlatBack(), speed); } if(control.keys.up.isDown()) { pos.addMul(camera.getFlatBack(), -speed); } if(control.keys.left.isDown()) { pos.addMul(camera.getFlatRight(), -speed); } if(control.keys.right.isDown()) { pos.addMul(camera.getFlatRight(), speed); } if(control.keys.jump.isDown()) { pos.addMul(camera.getFlatUp(), speed); } if(control.keys.sneak.isDown()) { pos.addMul(camera.getFlatUp(), -speed); } const float rotation = 5.0f; if(control.keys.camLeft.isDown()) { lengthAngle += rotation; } if(control.keys.camRight.isDown()) { lengthAngle -= rotation; } if(control.keys.camUp.isDown() && widthAngle - rotation > -90.0f) { widthAngle -= rotation; } if(control.keys.camDown.isDown() && widthAngle + rotation < 90.0f) { widthAngle += rotation; } ray.store(); ray.set(pos, lengthAngle, widthAngle); } else if(mode == Mode::AUTO) { movedLength += moveSpeed; } if(control.keys.test.getDownTime() == 1) { mode = Mode::PLAYER; } if(control.keys.test2.getDownTime() == 1) { mode = Mode::AUTO; } if(control.keys.test5.getDownTime() == 1) { renderSettings.ssao = !renderSettings.ssao; } } void Game::renderWorld(float lag, Renderer& renderer) const { if(mode == Mode::AUTO) { float leftLength = (movedLength - moveSpeed) + moveSpeed * lag; uint index = 0; while(leftLength >= cameraPoints[index].distance) { leftLength -= cameraPoints[index].distance; index = (index + 1) % cameraPoints.getLength(); } uint prev = index; float t = leftLength / cameraPoints[index].distance; if(prev == 0) { prev = cameraPoints.getLength() - 1; } else { prev = (prev - 1) % cameraPoints.getLength(); } uint currentA = (prev + 1) % cameraPoints.getLength(); uint currentB = (prev + 2) % cameraPoints.getLength(); uint next = (prev + 3) % cameraPoints.getLength(); Vector tangentA = splineTangent(cameraPoints[prev].pos, cameraPoints[currentA].pos, cameraPoints[currentB].pos); Vector tangentB = splineTangent(cameraPoints[currentA].pos, cameraPoints[currentB].pos, cameraPoints[next].pos); Vector interpolatedPos = interpolate(cameraPoints[currentA].pos, cameraPoints[currentB].pos, tangentA, tangentB, t); pos.set(interpolatedPos); ray.set(interpolatedPos, lengthAngle, widthAngle); ray.store(); } worldRenderer.render(lag, renderer); } void Game::renderTextOverlay(float lag, Renderer& renderer, FontRenderer& fr) const { (void) lag; renderer.scale(1.0f).update(); String s; fr.drawString(10, 10, s.append("FPS: ").append(fps.getUpdatesPerSecond())); fr.drawString(10, 19, s.clear().append("TPS: ").append(tps.getUpdatesPerSecond())); s.clear(); pos.toString(s); fr.drawString(10, 28, s); for(uint i = 0; i < cameraPoints.getLength(); i++) { s.clear().append(i + 1).append(": "); cameraPoints[i].pos.toString(s); fr.drawString(10, i * 9 + 37, s); } } bool Game::isRunning() const { return true; } Vector Game::splineTangent(const Vector& prev, const Vector& current, const Vector& next) const { Vector v(current); v.sub(prev).mul(0.5f).addMul(next, 0.5f).addMul(current, -0.5f); return v; } Vector Game::interpolate(const Vector& a, const Vector& b, const Vector& tanA, const Vector& tanB, float t) const { float t2 = t * t; float t3 = t2 * t; Vector v; v.addMul(a, 2.0f * t3 - 3.0f * t2 + 1.0f).addMul(b, -2.0f * t3 + 3.0f * t2) .addMul(tanA, t3 - 2.0f * t2 + t).addMul(tanB, t3 - t2); return v; } float Game::distance(uint index, uint splits) const { uint prev = index == 0 ? cameraPoints.getLength() - 1 : index - 1; uint currentA = (prev + 1) % cameraPoints.getLength(); uint currentB = (prev + 2) % cameraPoints.getLength(); uint next = (prev + 3) % cameraPoints.getLength(); Vector tangentA = splineTangent(cameraPoints[prev].pos, cameraPoints[currentA].pos, cameraPoints[currentB].pos); Vector tangentB = splineTangent(cameraPoints[currentA].pos, cameraPoints[currentB].pos, cameraPoints[next].pos); Vector currentPos; Vector currentNext = interpolate(cameraPoints[currentA].pos, cameraPoints[currentB].pos, tangentA, tangentB, 0.0f); float sum = 0.0f; for(uint i = 0; i <= splits; i++) { currentPos = currentNext; float t = (i + 1.0f) / (splits + 1.0f); currentNext = interpolate(cameraPoints[currentA].pos, cameraPoints[currentB].pos, tangentA, tangentB, t); sum += currentPos.sub(currentNext).length(); } return sum; }