#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.125f), 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.25f; pos.set(0, h, 0); lastPos = pos; rotation = Quaternion(Vector3(1, 0, 0), -80); lastRotation = rotation; Quaternion q; for(uint i = 0; i < cameraPoints.getCapacity(); i++) { Vector3 offset(mid, h, mid); offset += Vector3(r.nextFloat(randLength), r.nextFloat(randLength), r.nextFloat(randLength)); Vector3 v(i * 360.0f / cameraPoints.getCapacity(), 0.0f); v *= mid * 0.5f; v += offset; q.mul(Quaternion(Vector3(r.nextFloat() * 360.0f, r.nextFloat() * -90.0f), -10.0f)); cameraPoints.add({v, q, 0.0f}); } updateDistances(); } void Game::tick() { lastRotation = rotation; lastPos = pos; if(mode == Mode::PLAYER) { Matrix m = rotation.toMatrix(); Vector3 right = m * Vector3(1.0f, 0.0f, 0.0f); Vector3 up = m * Vector3(0.0f, 1.0f, 0.0f); Vector3 back = m * Vector3(0.0f, 0.0f, -1.0f); const float speed = 1.0f; if(control.keys.down.isDown()) { pos += back * speed; } if(control.keys.up.isDown()) { pos -= back * speed; } if(control.keys.left.isDown()) { pos -= right * speed; } if(control.keys.right.isDown()) { pos += right * speed; } if(control.keys.jump.isDown()) { pos += up * speed; } if(control.keys.sneak.isDown()) { pos -= 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)); } if(control.keys.test3.getDownTime() == 1) { cameraPoints.add({pos, rotation, 0.0f}); } } else if(mode == Mode::AUTO) { movedLength += moveSpeed; if(control.keys.camUp.isDown()) { moveSpeed += 0.0125f; if(moveSpeed > 1.0f) { moveSpeed = 1.0f; } } if(control.keys.camDown.isDown()) { moveSpeed -= 0.0125f; if(moveSpeed < 0.0f) { moveSpeed = 0.0f; } } if(control.keys.test3.isDown()) { mode = Mode::PLAYER; cameraPoints.clear(); } } if(control.keys.test.isDown()) { mode = Mode::PLAYER; } if(control.keys.test2.isDown() && cameraPoints.getLength() >= 3) { mode = Mode::AUTO; movedLength = 0.0f; updateDistances(); } if(control.keys.test4.getDownTime() == 1) { renderSettings.shadows = !renderSettings.shadows; } if(control.keys.test5.getDownTime() == 1) { renderSettings.ssao = !renderSettings.ssao; } if(control.keys.test6.getDownTime() == 1) { renderSettings.bump += 0.05f; if(renderSettings.bump > 1.0f) { renderSettings.bump = 0.0f; } } } 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; Vector3 interpolatedPos = pointUntilDistance(leftLength, index, 4000); uint a = index == 0 ? cameraPoints.getLength() - 1 : index - 1; uint b = (a + 1) % cameraPoints.getLength(); uint c = (a + 2) % cameraPoints.getLength(); uint d = (a + 3) % cameraPoints.getLength(); renderer.update(interpolatedPos, cameraPoints[b].q.squad(t, cameraPoints[a].q, cameraPoints[c].q, cameraPoints[d].q)); pos = interpolatedPos; } else if(mode == Mode::PLAYER) { Vector3 v = lastPos + (pos - 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(" TPS: ").append(tps.getUpdatesPerSecond())); fr.drawString(10, 19, s.clear().append("Speed: ").append(moveSpeed)); s.clear(); s += pos; fr.drawString(10, 28, s); for(uint i = 0; i < cameraPoints.getLength(); i++) { s.clear().append(i + 1).append(": "); s += cameraPoints[i].pos; fr.drawString(10, i * 9 + 37, s); } } bool Game::isRunning() const { return true; } Vector3 Game::splineTangent(const Vector3& prev, const Vector3& current, const Vector3& next) const { (void) current; //Vector3 v(current); //v.sub(prev).mul(0.5f).addMul(next, 0.5f).addMul(current, -0.5f); return (next - prev) * 0.5f; } Vector3 Game::interpolate(const Vector3& a, const Vector3& b, const Vector3& tanA, const Vector3& tanB, float t) const { float t2 = t * t; float t3 = t2 * t; return a * (2.0f * t3 - 3.0f * t2 + 1.0f) + b * (-2.0f * t3 + 3.0f * t2) + tanA * (t3 - 2.0f * t2 + t) + tanB * (t3 - t2); } float Game::distance(uint index, uint splits) const { Vector3 a; Vector3 b; Vector3 tanA; Vector3 tanB; getPointsAndTangents(index, a, b, tanA, tanB); Vector3 currentPos; Vector3 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 = static_cast (currentPos - currentNext).length(); sum += l; } return sum; } Vector3 Game::pointUntilDistance(float leftDistance, uint index, uint splits) const { Vector3 a; Vector3 b; Vector3 tanA; Vector3 tanB; getPointsAndTangents(index, a, b, tanA, tanB); Vector3 currentPos; Vector3 currentNext = interpolate(a, b, tanA, tanB, 0.0f); float sum = 0.0f; uint i = 0; while(leftDistance > sum) { currentPos = currentNext; float t = (i + 1.0f) / (splits + 1.0f); currentNext = interpolate(a, b, tanA, tanB, t); float l = static_cast (currentPos - currentNext).length(); sum += l; i++; } return currentNext; } void Game::getPointsAndTangents(uint index, Vector3& a, Vector3& b, Vector3& tanA, Vector3& 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 = cameraPoints[currentA].pos; b = cameraPoints[currentB].pos; tanA = splineTangent(cameraPoints[prev].pos, a, b); tanB = splineTangent(a, b, cameraPoints[next].pos); } void Game::updateDistances() { for(uint i = 0; i < cameraPoints.getLength(); i++) { cameraPoints[i].distance = distance(i, 10000); } }