Browse Source

improved quaternions

Kajetan Johannes Hammerle 3 years ago
parent
commit
9bcf0e0833
4 changed files with 96 additions and 59 deletions
  1. 54 39
      client/Game.cpp
  2. 3 0
      client/Game.h
  3. 35 16
      client/math/Quaternion.cpp
  4. 4 4
      client/math/Quaternion.h

+ 54 - 39
client/Game.cpp

@@ -1,3 +1,5 @@
+#include <cmath>
+
 #include "client/Game.h"
 #include "client/utils/Utils.h"
 #include "rendering/Renderer.h"
@@ -11,9 +13,12 @@ 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;
+    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;
 
     for(uint i = 0; i < cameraPoints.getCapacity(); i++) {
         Vector offset(mid, h, mid);
@@ -28,11 +33,17 @@ pointIndex(0), moveSpeed(0.25f), movedLength(0.0f), mode(Mode::AUTO) {
 }
 
 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()) {
@@ -53,31 +64,31 @@ void Game::tick() {
         if(control.keys.sneak.isDown()) {
             pos.addMul(up, -speed);
         }
-
-        /*const float rotation = 5.0f;
+        
+        const float rotationSpeed = 5.0f;
         if(control.keys.camLeft.isDown()) {
-            lengthAngle += rotation;
+            rotation.mul(Quaternion(up, rotationSpeed));
         }
         if(control.keys.camRight.isDown()) {
-            lengthAngle -= rotation;
+            rotation.mul(Quaternion(up, -rotationSpeed));
         }
-        if(control.keys.camUp.isDown() && widthAngle - rotation > -90.0f) {
-            widthAngle -= rotation;
+        if(control.keys.camUp.isDown()) {
+            rotation.mul(Quaternion(right, rotationSpeed));
+        }
+        if(control.keys.camDown.isDown()) {
+            rotation.mul(Quaternion(right, -rotationSpeed));
         }
-        if(control.keys.camDown.isDown() && widthAngle + rotation < 90.0f) {
-            widthAngle += rotation;
-        }*/
     } 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;
     }
@@ -94,29 +105,21 @@ void Game::renderWorld(float lag, Renderer& renderer) const {
             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);
 
+        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, Quaternion(Vector(1.0f, 0.0f, 0.0f), -30.0f));
         pos = interpolatedPos;
     } else if(mode == Mode::PLAYER) {
         Vector v(lastPos);
         v.addMul(pos, lag).addMul(lastPos, -lag);
-        renderer.update(v, Quaternion(Vector(1.0f, 0.0f, 0.0f), -80.0f));
+        renderer.update(v, lastRotation.slerp(lag, rotation));
     }
     worldRenderer.render(lag, renderer);
 }
@@ -127,7 +130,7 @@ void Game::renderTextOverlay(float lag, Renderer& renderer, FontRenderer& fr) co
 
     String s;
     fr.drawString(10, 10, s.append("FPS: ").append(fps.getUpdatesPerSecond())
-    .append(" ").append("%0.8f", renderSettings.testBias).append(" ").append("%0.8f", renderSettings.testRadius));
+            .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);
@@ -159,23 +162,35 @@ Vector Game::interpolate(const Vector& a, const Vector& b, const Vector& tanA, c
 }
 
 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 a;
+    Vector b;
+    Vector tanA;
+    Vector tanB;
+    getPointsAndTangents(index, a, b, tanA, tanB);
 
     Vector currentPos;
-    Vector currentNext = interpolate(cameraPoints[currentA].pos, cameraPoints[currentB].pos, tangentA, tangentB, 0.0f);
+    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(cameraPoints[currentA].pos, cameraPoints[currentB].pos, tangentA, tangentB, t);
-        sum += currentPos.sub(currentNext).length();
+        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);
+}

+ 3 - 0
client/Game.h

@@ -25,6 +25,7 @@ public:
     Vector splineTangent(const Vector& prev, const Vector& current, const Vector& next) const;
     Vector interpolate(const Vector& a, const Vector& b, const Vector& tanA, const Vector& tanB, float t) const;
     float distance(uint index, uint splits) const;
+    void getPointsAndTangents(uint index, Vector& a, Vector& b, Vector& tanA, Vector& tanB) const;
 
 private:
     const Control& control;
@@ -34,6 +35,8 @@ private:
 
     Vector lastPos;
     mutable Vector pos;
+    Quaternion lastRotation;
+    Quaternion rotation;
 
     BlockRegistry blockRegistry;
     World world;

+ 35 - 16
client/math/Quaternion.cpp

@@ -2,46 +2,58 @@
 
 #include "client/math/Quaternion.h"
 
-Quaternion::Quaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {
+Quaternion::Quaternion() : xyz(0.0f, 0.0f, 0.0f), w(0.0f) {
 }
 
-Quaternion::Quaternion(Vector unitAxis, float angle) {
+Quaternion::Quaternion(float x, float y, float z, float w) : xyz(x, y, z), w(w) {
+    normalize();
+}
+
+Quaternion::Quaternion(const Vector& axis, float angle) {
+    Vector unit(axis);
+    xyz.set(unit.normalize());
     float sin;
     sincosf(angle * (M_PI / 360.0f), &sin, &w);
-    x = unitAxis.getX() * sin;
-    y = unitAxis.getY() * sin;
-    z = unitAxis.getZ() * sin;
+    xyz.mul(sin);
 }
 
 Quaternion Quaternion::lerp(float f, const Quaternion& other) const {
-    Quaternion q = interpolate(1 - f, f, other);
-    q.normalize();
-    return q;
+    return interpolate(1 - f, f, other);
 }
 
 Quaternion Quaternion::slerp(float f, const Quaternion& other) const {
-    float arccos = acosf(x * other.x + y * other.y + z * other.z + w * other.w);
+    float angle = xyz.dot(other.xyz) + w * other.w;
+    if(angle > 1.0f) {
+        return *this;
+    } else if(angle < -1.0f) {
+        angle = -1.0f;
+    }
+    float arccos = acosf(angle);
     float sin = 1.0f / sinf(arccos);
     return interpolate(sinf((1 - f) * arccos) * sin, sinf(f * arccos) * sin, other);
 }
 
 Quaternion Quaternion::interpolate(float a, float b, const Quaternion& other) const {
-    return Quaternion(x * a + other.x * b, y * a + other.y * b, z * a + other.z * b, w * a + other.w * b);
+    Vector interpolated(xyz);
+    interpolated.mul(a).addMul(other.xyz, b);
+    return Quaternion(interpolated.getX(), interpolated.getY(), interpolated.getZ(), w * a + other.w * b);
 }
 
 void Quaternion::normalize() {
-    float f = 1.0f / sqrtf(x * x + y * y + z * z + w * w);
-    x *= f;
-    y *= f;
-    z *= f;
+    float f = 1.0f / sqrtf(xyz.dot(xyz) + w * w);
+    xyz.mul(f);
     w *= f;
 }
 
 Matrix Quaternion::toMatrix() const {
+    float x = xyz.getX();
+    float y = xyz.getY();
+    float z = xyz.getZ();
+
     float x2 = 2 * x * x;
     float y2 = 2 * y * y;
-    float z2 = 2 * z * z; 
-    
+    float z2 = 2 * z * z;
+
     float xy = 2 * x * y;
     float xz = 2 * x * z;
     float xw = 2 * x * w;
@@ -54,4 +66,11 @@ Matrix Quaternion::toMatrix() const {
     m.set(4, xy + zw).set(5, 1 - x2 - z2).set(6, yz - xw);
     m.set(8, xz - yw).set(9, yz + xw).set(10, 1 - x2 - y2);
     return m;
+}
+
+void Quaternion::mul(const Quaternion& other) {
+    Vector v;
+    v.addMul(other.xyz, w).addMul(xyz, other.w).add(Vector(xyz).cross(other.xyz));
+    w = w * other.w - xyz.dot(other.xyz);
+    xyz.set(v);
 }

+ 4 - 4
client/math/Quaternion.h

@@ -6,20 +6,20 @@
 
 class Quaternion {
 public:
+    Quaternion();
     Quaternion(float x, float y, float z, float w);
-    Quaternion(Vector unitAxis, float angle);
+    Quaternion(const Vector& axis, float angle);
     
     Quaternion lerp(float f, const Quaternion& other) const;
     Quaternion slerp(float f, const Quaternion& other) const;
     void normalize();
     Matrix toMatrix() const;
+    void mul(const Quaternion& other);
 
 private:
     Quaternion interpolate(float a, float b, const Quaternion& other) const;
     
-    float x;
-    float y;
-    float z;
+    Vector xyz;
     float w;
 };