#include #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 Clock& fps, const Clock& tps, RenderSettings& renderSettings) : control(control), fps(fps), tps(tps), renderSettings(renderSettings), world(blockRegistry), worldRenderer(world), pointIndex(0), moveSpeed(0.25f), movedLength(0.0f), mode(Mode::AUTO) { Random r(0); float h = World::WORLD_SIZE * 0.6f; float mid = World::WORLD_SIZE * 0.5f; float randLength = World::WORLD_SIZE * 0.125f * 0.5f; pos.set(0, h, 0); lastPos.set(pos); rotation = Quaternion(Vector(1, 0, 0), -80); lastRotation = rotation; Quaternion q(0.0f, 0.0f, 0.0f, 1.0f); 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); q.mul(Quaternion(Vector(r.nextFloat() * 360.0f, r.nextFloat() * -90.0f), -5.0f)); cameraPoints.add({v, q, 0.0f}); } for(uint i = 0; i < cameraPoints.getLength(); i++) { cameraPoints[i].distance = distance(i, 20); } Quaternion testQ = Quaternion(0, 0, 0, 1); Quaternion rotTest = Quaternion(Vector(1, 0, 0), 5.0f); testQ.mul(rotTest); std::cout << "TEST: " << testQ.xyz.getX() << " " << testQ.xyz.getY() << " " << testQ.xyz.getZ() << " " << testQ.w << "\n"; } void Game::tick() { lastRotation = rotation; lastPos = pos; if(mode == Mode::PLAYER) { Vector right(1.0f, 0.0f, 0.0f); Vector up(0.0f, 1.0f, 0.0f); Vector back(0.0f, 0.0f, -1.0f); Matrix m = rotation.toMatrix(); right.mul(m); up.mul(m); back.mul(m); const float speed = 1.0f; if(control.keys.down.isDown()) { pos.addMul(back, speed); } if(control.keys.up.isDown()) { pos.addMul(back, -speed); } if(control.keys.left.isDown()) { pos.addMul(right, -speed); } if(control.keys.right.isDown()) { pos.addMul(right, speed); } if(control.keys.jump.isDown()) { pos.addMul(up, speed); } if(control.keys.sneak.isDown()) { pos.addMul(up, -speed); } const float rotationSpeed = 5.0f; if(control.keys.camLeft.isDown()) { rotation.mul(Quaternion(up, rotationSpeed)); } if(control.keys.camRight.isDown()) { rotation.mul(Quaternion(up, -rotationSpeed)); } if(control.keys.camUp.isDown()) { rotation.mul(Quaternion(right, rotationSpeed)); } if(control.keys.camDown.isDown()) { rotation.mul(Quaternion(right, -rotationSpeed)); } } else if(mode == Mode::AUTO) { movedLength += moveSpeed; } if(control.keys.test.isDown()) { mode = Mode::PLAYER; } if(control.keys.test2.isDown()) { mode = Mode::AUTO; } if(control.keys.test4.getDownTime() == 1) { renderSettings.shadows = !renderSettings.shadows; } 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(); } float t = leftLength / cameraPoints[index].distance; Vector a; Vector b; Vector tanA; Vector tanB; getPointsAndTangents(index, a, b, tanA, tanB); Vector interpolatedPos = interpolate(a, b, tanA, tanB, t); renderer.update(interpolatedPos, cameraPoints[index].q.slerp(t, cameraPoints[(index + 1) % cameraPoints.getLength()].q)); pos = interpolatedPos; } else if(mode == Mode::PLAYER) { Vector v(lastPos); v.addMul(pos, lag).addMul(lastPos, -lag); renderer.update(v, lastRotation.slerp(lag, rotation)); } worldRenderer.render(lag, renderer); } void Game::renderTextOverlay(float lag, Renderer& renderer, FontRenderer& fr) const { (void) lag; renderer.scale(2.0f).update(); String s; fr.drawString(10, 10, s.append("FPS: ").append(fps.getUpdatesPerSecond()) .append(" ").append("%0.8f", renderSettings.testBias).append(" ").append("%0.8f", renderSettings.testRadius)); 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 { Vector a; Vector b; Vector tanA; Vector tanB; getPointsAndTangents(index, a, b, tanA, tanB); Vector currentPos; Vector currentNext = interpolate(a, b, tanA, tanB, 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(a, b, tanA, tanB, t); float l = currentPos.sub(currentNext).length(); sum += l; } return sum; } void Game::getPointsAndTangents(uint index, Vector& a, Vector& b, Vector& tanA, Vector& tanB) 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(); a.set(cameraPoints[currentA].pos); b.set(cameraPoints[currentB].pos); tanA = splineTangent(cameraPoints[prev].pos, a, b); tanB = splineTangent(a, b, cameraPoints[next].pos); }