1
\$\begingroup\$

So I've made this struct that encapsulates a glm::vec4. This is because vec3s are treated as 16-byte in GLSL, but I don't want to assign a vec4 if I only really need 3 components. Anyway, my question isn't really OpenGL/GLSL specific, but since it is in this context it would be easier to post it here.

Below is my "overloaded vec3", and some code for how I use it:

struct GpuVec3
{
public:
    GpuVec3() {}

    GpuVec3& GpuVec3::operator=(const vec3& other) 
    {
        v.x = other.x;
        v.y = other.y;
        v.z = other.z;
        v.w = 1.0f;

        return *this;
    }

private:
    vec4 v;
};

// ...

// Struct using GpuVec3 members
struct Material
{
public:
    GpuVec3 Emissive;
    GpuVec3 Ambient; 
    GpuVec3 Diffuse;
    GpuVec4 SpecularAndShininess;
};

// Assign vec3 to GpuVec3 (works fine)
material->Emissive = vec3(0.1f, 0.0f, 0.0f);

// ... initializing uniform block and stuff ...

// Passing Emissive to function that expects vec3 (doesn't work, of course)
// UniformBlock3fv(GLchar* uniformBlockName, GLchar* uniformName, glm::vec3 value)
program->UniformBlock3fv("uniMaterial", "Emissive", material->Emissive);

In the last line of code I want to be able to pass my GpuVec3 to a function that expects a vec3, this is not possible of course.

My question is, is there some way in C/C++ possible to overload how an object instance of a class is treated when used? A pseudo-code example below what I want to happen if I use my object:

if (material->Emissive is called) { return v.xyz; }
\$\endgroup\$
2
\$\begingroup\$

I think you would do to keep your GPUVec3 and your vec3 as two separate classes, not one containing the other. Here's an example:

struct vec3
{
    float x,y,z;
};

struct GPUVec3
{
    float x,y,z, pad;

    GPUVec3() { }
    GPUVec3( float x, float y, float z ) { }
    GPUVec3( const vec3 & v ) { }

    operator vec3 const & () const { return *(vec3*)this; }

} __attribute__ ((aligned(16)));

I have also provided an overload that lets you assign from vec3 to GPUVec3 and back again. This isn't a complete implementation, but you get the idea - using constructors and overloads it is possible to get the conversions to be called automatically. The takeaway here is that vec3 doesn't need to be 16 byte aligned, but can be. Since you can assign a GPUVec3 back to a reference to a vec3, you can read back and pass around a GPUVec3's xyz without copying. The same is not true for vec3 - it is not always 16 byte aligned (but could be) so you can't safely just cast it to a GPUVec3. This lets you put vec3s wherever you need and only have to worry about the 16-byte alignment interchange with the GPU in gpu-centric variables.

This stack exchange has a great overview of the C++ operator idioms and includes something on assignment idioms. MOre can be found, fore sure: https://stackoverflow.com/questions/4421706/operator-overloading

Here's an example in use and compiles fine under xcode:

int main (int argc, char * const argv[]) 
{

    GPUVec3 gpuVec3;

    vec3 from;

    gpuVec3 = from;

    vec3 to = gpuVec3;


    // insert code here...
    return 0;
}
\$\endgroup\$
  • \$\begingroup\$ Thanks this did it! However, I'm wondering why you use both a float pad and __attribute__ ((aligned(16)))? Right now I'm running with just the float and it's working nice. \$\endgroup\$ – Unresolved External Dec 1 '14 at 19:01
0
\$\begingroup\$

You could use an user-defined conversion, something like this:

struct GpuVec3
{
public:
    GpuVec3() {}

    GpuVec3& GpuVec3::operator=(const vec3& other) 
    {
        v.x = other.x;
        v.y = other.y;
        v.z = other.z;
        v.w = 1.0f;

        return *this;
    }

    operator glm::vec3() const { return v.xyz; }

private:
    vec4 v;
};

You can read more about it here: http://en.cppreference.com/w/cpp/language/cast_operator

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.