My first 'proper' C++ program, would appreciate some feedback/criticism/etc about it.

Welcome! You are showing great initiative in learning C++ and asking for feedback says that you are very serious about it!

A few words on your repository.

  1. Don't include binary files in git. Git is rather bad at versioning binary files and they just bloat your repository.
  2. Including a file structure without VS solution stuff and adding a Makefile for those of us who don't use Windows is always appreciated. :)

A few items on the code.

It doesn't compile out-of-the-box with gcc. I understand that this is just a practice project that you probably won't be officially distributing and supporting, but it never hurts to get in the mindset of cross-platform development early. For non-Windows environments, gcc is pretty much the de facto target compiler (others include clang, icc, and open64). Hence, it is well worth the effort to ensure you compile (preferably warning free!) under gcc. The errors/warnings were

  1. In vec3d.h and quaternion.h, you need to #include <cmath> to use std::sqrt, std::cos, and std::sin. You might need a preprocessor guard around the include since VC didn't have a problem with it. Additionally, <cmath> gives you M_PI so you don't have to #define your own.
  2. no match for ‘operator*’ (operand types are ‘vec3’ and ‘quaternion’)

I have no idea how this compiled in VC

vec3 getForwardVector()
{
    return vec3(0, 0, 1) * *this;
}

inline vec3 operator*(vec3 &v, quaternion &q)
{
    quaternion result = quaternion();
    result = q * v * q.conjugate();
    return vec3(result.x, result.y, result.z);
}

When you say vec3(0, 0, 1), you are generating what is known as an r-value (its name comes from the fact that it is usually something that is on the right of an assignment statement). You are passing this r-value into a function that expects an l-value (a thing on the left of an assignment statement). In general, this is not allowed (and for good reason!). What would happen in the following:

void foo(int &i) {
    i = 2;
}
foo(3);

The value 3 is a perfectly good r-value, but what happens when I execute i = 2 in foo? Badness, that's what. How can I possibly change 3 to 2? I would break the universe. :p So it's just not allowed.

However, there are two ways that you can pass an r-value to an l-value parameter. The first is through a const l-value.

void foo(const int &i) {
    // I can't change i because it's const!
    std::cout << "i = " << i << std::endl;
}
foo(3);

The second is through what Scott Meyers calls "universal references." I need a slightly more complicated example to demonstrate this.

struct foo{ int x,y,z;};

foo bar(foo &lhs, foo &&rhs) {
    return foo(lhs.x+rhs.x,lhs.y,lhs.z*rhs.z);
}

foo f{1,2,3};
auto x = bar(f, foo(4,5,6));
std::cout << "x.x = " << x.x << std::endl;

What does this print? 5 You can use either of these forms to fix your inline vec3 operator* function. I strongly recommend a read through Scott's excellent book Effective Modern C++. It is just chock full of great nuggets of wisdom like these.

Wow. Another complete mystery as to why this compiles in VC (given what I just discussed, can you decipher the compiler error?)

quaternion &q = m_camera->getTransform().getRotation();

invalid initialization of non-const reference of type ‘quaternion&’ from an rvalue of type ‘quaternion’

Does Visual Studio Express allow you to upgrade VC++ separately from VS iteself? If so, you need an upgraded compiler because this one stinks. Once you have mastered the art of l-value vs. r-value, you have lots of code to update as there are many errors surrounding this concept in your current code.

While technically not an error, it is generally inefficient to pass primitive types by reference when you aren't going to be modifying them (the debate about using non-const references as in-out parameters rages to this day!). For example,

static vec3 refract(vec3 &I, vec3 &N, float &NdotI, float &eta, float &cos_t)

should really just be

static vec3 refract(vec3 &I, vec3 &N, float NdotI, float eta, float cos_t)

Ok. Errors out of the way. How can you make your code more C++-y? I promise I don't intend to sound pithy, but the Java is strong with you. :p Let's take that previous example.

struct vec3 {
    /* lots of stuff! */

    static vec3 refract(vec3 &I, vec3 &N, float NdotI, float eta, float cos_t)
}

In Java, this makes sense since you aren't allowed to have free functions. But this is C++ and we do have them and folks are going to anticipate you have them rather than class-static methods.

struct vec3 {
    /* lots of stuff! */
}

vec3 refract(const vec3 &I, const vec3 &N, float NdotI, float eta, float cos_t)

Much better! :D (NB: notice that I added const to the input parameters? This is good form, too.)

Alright. I think I have badgered you enough. Definitely keep us apprised of how the project evolves and don't be afraid to ask lots of questions!

/r/cpp_questions Thread