#include #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, const WindowSize& size) : control(control), fps(fps), tps(tps), renderSettings(renderSettings), size(size), 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(); std::vector data; //generateSphere(data); generateRandom(data); for(KDTree::Triangle& t : data) { treeData.add(Triangle( Vertex(t[0], Vector2(8.0f / 16.0f, 0.0f)), Vertex(t[1], Vector2(9.0f / 16.0f, 0.0f)), Vertex(t[2], Vector2(9.0f / 16.0f, 1.0f / 16.0f)) )); } treeData.build(); kdTree.build(data); kdTree.fillLines(lines, data); } void Game::tick() { lastRotation = rotation; lastPos = pos; 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); if(mode == Mode::PLAYER) { 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; } } if(control.keys.factor.getDownTime() == 1) { if(renderSettings.factor == 1) { renderSettings.factor = 2; } else if(renderSettings.factor == 2) { renderSettings.factor = 3; } else { renderSettings.factor = 1; } renderSettings.dirtyFactor = true; } if(control.buttons.primary.getDownTime() == 1) { float hWidth = size.width * 0.5f; float hHeight = size.height * 0.5f; float x = (control.buttons.getX() - hWidth) / hWidth; float y = -(control.buttons.getY() - hHeight) / hHeight; float aspect = hWidth / hHeight; float tan = tanf((0.5f * 60.0f) * M_PI / 180.0f); float q = 1.0f / tan; Vector3 direction(x / (q / aspect), y / q, 1.0f); direction.normalize(); direction = m * direction; clickLine.clear(); clickLine.add(pos, pos + direction * 100.0f, 0xFF00FF); clickLine.build(); } } 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); treeData.draw(); } void Game::renderWorldLines(float lag, Renderer& renderer) const { (void) lag; renderer.translateTo(0.0f, 0.0f, 0.0f); renderer.update(); lines.draw(); clickLine.draw(); } 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); } } void Game::generateSphere(std::vector& data) { int fieldSize = 8; int quality = 3; float radius = 30; int triangles = fieldSize * quality; int layers = (2 + fieldSize) * quality; for(int l = 0; l < layers; l++) { float high1 = cosf((M_PI * l) / layers); float high2 = cosf((M_PI * (l + 1)) / layers); float r1 = sqrtf(1 - high1 * high1) * radius; float r2 = sqrtf(1 - high2 * high2) * radius; high1 *= radius; high2 *= radius; for(int i = 0; i < triangles; i++) { float first = 2 * M_PI * i / triangles; float second = 2 * M_PI * (i + 1) / triangles; data.push_back(KDTree::Triangle( Vector3(r2 * cosf(first), high2, r2 * sinf(first)), Vector3(r1 * cosf(first), high1, r1 * sinf(first)), Vector3(r1 * cosf(second), high1, r1 * sinf(second)))); data.push_back(KDTree::Triangle( Vector3(r2 * cosf(first), high2, r2 * sinf(first)), Vector3(r1 * cosf(second), high1, r1 * sinf(second)), Vector3(r2 * cosf(second), high2, r2 * sinf(second)))); } } } void Game::generateRandom(std::vector& data) { float radius = 25.0f; float diff = 5.0f; Random r(0); for(int i = 0; i < 7; i++) { Vector3 a(r.nextFloat() * radius, r.nextFloat() * radius, r.nextFloat() * radius); Vector3 b = a + Vector3(r.nextFloat() * diff, r.nextFloat() * diff, r.nextFloat() * diff); Vector3 c = a + Vector3(r.nextFloat() * diff, r.nextFloat() * diff, r.nextFloat() * diff); data.push_back(KDTree::Triangle(a, b, c)); } }