LCOV - code coverage report
Current view: top level - test - camera.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 119 119
Test Date: 2026-04-03 02:26:39 Functions: 100.0 % 2 2

            Line data    Source code
       1              : #include "catch.hpp"
       2              : #include "Camera.h"
       3              : #include "Matrix.h"
       4              : #include "Vector.h"
       5              : 
       6           11 : SCENARIO( "Camera state without GL or Lua", "[Camera]" ) {
       7           19 :   GIVEN( "A default camera" ) {
       8            9 :     Camera cam;
       9              : 
      10              :     // Camera::update always rebuilds rotationMatrix unconditionally (Camera.cpp:121),
      11              :     // unlike Object which gates the rebuild on rotationChanged_.
      12              : 
      13           11 :     WHEN( "first constructed" ) {
      14            3 :       THEN( "frustum pointer is the embedded instance" ) {
      15            1 :         REQUIRE(cam.getFrustum() == &cam.frustum);
      16            1 :       }
      17              : 
      18              :       // Camera::initialize calls worldRotation.buildFromEuler(0,0,0) — identity.
      19            3 :       THEN( "worldRotation is identity" ) {
      20            1 :         REQUIRE(cam.worldRotation.getW() == Approx(1.0f));
      21            1 :         REQUIRE(cam.worldRotation.getX() == Approx(0.0f));
      22            1 :         REQUIRE(cam.worldRotation.getY() == Approx(0.0f));
      23            1 :         REQUIRE(cam.worldRotation.getZ() == Approx(0.0f));
      24            1 :       }
      25            2 :     }
      26              : 
      27           10 :     WHEN( "setCamera is called with kinematic values" ) {
      28            1 :       Vector pos(3.0f, -1.0f, 2.0f);
      29            1 :       Vector vel(-4.0f, 0.5f, 0.25f);
      30            1 :       Quaternion rot;
      31            1 :       rot.buildFromEuler(0.2f, -0.3f, 0.4f);
      32            1 :       rot.normalize();
      33            1 :       Vector rotVel(0.01f, 0.02f, 0.03f);
      34            1 :       cam.setCamera(pos, vel, rot, rotVel);
      35              : 
      36            2 :       THEN( "position, velocity, rotation, and rotationVelocity are copied" ) {
      37            1 :         Vector p = cam.getPosition();
      38            1 :         REQUIRE(p.x == Approx(3.0f));
      39            1 :         REQUIRE(p.y == Approx(-1.0f));
      40            1 :         REQUIRE(p.z == Approx(2.0f));
      41              : 
      42            1 :         Vector v = cam.getVelocity();
      43            1 :         REQUIRE(v.x == Approx(-4.0f));
      44            1 :         REQUIRE(v.y == Approx(0.5f));
      45            1 :         REQUIRE(v.z == Approx(0.25f));
      46              : 
      47            1 :         Quaternion r = cam.getRotation();
      48            1 :         REQUIRE(r.getX() == Approx(rot.getX()));
      49            1 :         REQUIRE(r.getY() == Approx(rot.getY()));
      50            1 :         REQUIRE(r.getZ() == Approx(rot.getZ()));
      51            1 :         REQUIRE(r.getW() == Approx(rot.getW()));
      52              : 
      53            1 :         Vector rv = cam.getRotationVelocity();
      54            1 :         REQUIRE(rv.x == Approx(0.01f));
      55            1 :         REQUIRE(rv.y == Approx(0.02f));
      56            1 :         REQUIRE(rv.z == Approx(0.03f));
      57            1 :       }
      58            1 :     }
      59              : 
      60              :     // setCamera assigns rotation_ directly without setting rotationChanged_, like
      61              :     // Object::setRotationQuaternion. Camera::update rebuilds unconditionally so this
      62              :     // does not affect rendering, but callers using isRotationChanged() as a change
      63              :     // signal should use setRotation() instead.
      64           10 :     WHEN( "setCamera is called after clearing rotationChanged" ) {
      65            1 :       Quaternion q;
      66            1 :       q.buildFromEuler(0.1f, 0.0f, 0.0f);
      67            1 :       cam.setRotationChanged(false);
      68            2 :       cam.setCamera(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f), q,
      69            1 :                     Vector(0.0f, 0.0f, 0.0f));
      70              : 
      71            2 :       THEN( "rotationChanged remains false" ) {
      72            1 :         REQUIRE(cam.isRotationChanged() == false);
      73            1 :       }
      74            1 :     }
      75              : 
      76           10 :     WHEN( "update is called with zero elapsed time" ) {
      77            1 :       Quaternion q;
      78            1 :       q.buildFromEuler(0.1f, 0.15f, -0.05f);
      79            1 :       q.normalize();
      80            2 :       cam.setCamera(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f), q,
      81            1 :                     Vector(0.0f, 0.0f, 0.0f));
      82            1 :       cam.update(0.0f);
      83              : 
      84            2 :       THEN( "rotation matrix matches the quaternion" ) {
      85            1 :         Matrix expected;
      86            1 :         q.buildRotationMatrix(expected);
      87            1 :         Matrix actual;
      88            1 :         cam.getRotationMatrix(actual);
      89           17 :         for (int i = 0; i < NUM_CELLS; ++i) {
      90           16 :           REQUIRE(actual.data[i] == Approx(expected.data[i]));
      91           16 :         }
      92            1 :       }
      93            1 :     }
      94              : 
      95           10 :     WHEN( "update is called with a non-zero velocity" ) {
      96            1 :       Quaternion q;
      97            1 :       q.buildFromEuler(0.0f, 0.0f, 0.0f);
      98            2 :       cam.setCamera(Vector(0.0f, 0.0f, 0.0f), Vector(2.0f, 0.0f, -1.0f), q,
      99            1 :                     Vector(0.0f, 0.0f, 0.0f));
     100            1 :       cam.update(0.5f);
     101              : 
     102            2 :       THEN( "position advances by velocity * elapsed time" ) {
     103            1 :         Vector p = cam.getPosition();
     104            1 :         REQUIRE(p.x == Approx(1.0f));   // 0 + 2.0 * 0.5
     105            1 :         REQUIRE(p.y == Approx(0.0f));   // velocity_.y == 0, unchanged
     106            1 :         REQUIRE(p.z == Approx(-0.5f));  // 0 + (-1.0) * 0.5
     107            1 :       }
     108            1 :     }
     109              : 
     110           11 :     WHEN( "update is called with a non-zero rotation velocity" ) {
     111            2 :       Quaternion q;
     112            2 :       q.buildFromEuler(0.0f, 0.0f, 0.0f);
     113            4 :       cam.setCamera(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f), q,
     114            2 :                     Vector(0.0f, 1.0f, 0.0f));
     115            2 :       cam.setRotationChanged(false);
     116            2 :       cam.update(0.5f);
     117              : 
     118            3 :       THEN( "rotationChanged is set" ) {
     119            1 :         REQUIRE(cam.isRotationChanged() == true);
     120            1 :       }
     121              : 
     122            3 :       THEN( "rotation is no longer identity" ) {
     123            1 :         REQUIRE(cam.getRotation().getW() < 1.0f);
     124            1 :       }
     125            2 :     }
     126              : 
     127              :     // Camera::update rotates baseDirection_ by rotationMatrix then negates direction_.z
     128              :     // (Camera.cpp:124). With identity rotation and baseDirection_ (0,0,1), the result
     129              :     // is direction_ = (0, 0, -1). See docs/MATH_AND_TESTING_CONVENTIONS.md.
     130           10 :     WHEN( "update is called with identity rotation" ) {
     131            1 :       Quaternion q;
     132            1 :       q.buildFromEuler(0.0f, 0.0f, 0.0f);
     133            2 :       cam.setCamera(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f), q,
     134            1 :                     Vector(0.0f, 0.0f, 0.0f));
     135            1 :       cam.update(0.0f);
     136              : 
     137            2 :       THEN( "direction z is negated relative to the rotated base direction" ) {
     138            1 :         Vector d = cam.getDirection();
     139            1 :         REQUIRE(d.x == Approx(0.0f));
     140            1 :         REQUIRE(d.y == Approx(0.0f));
     141            1 :         REQUIRE(d.z == Approx(-1.0f));
     142            1 :       }
     143            1 :     }
     144            9 :   }
     145              : 
     146           11 :   GIVEN( "A camera constructed with an id" ) {
     147            1 :     Camera cam(42);
     148              : 
     149            2 :     WHEN( "first constructed" ) {
     150            2 :       THEN( "id is set" ) {
     151            1 :         REQUIRE(cam.id_ == 42);
     152            1 :       }
     153            1 :     }
     154            1 :   }
     155           10 : }
        

Generated by: LCOV version 2.4-0