enum {
    trut_Ok,              // no error
    trut_Error,           // generic error (trut kaput)
    trut_NoPaths,         // no supported paths found (init error)
    trut_NoShaders,       // no shaders found (init error)
    trut_NoRenderBuffers, // no offscreen render buffers supported (init error)
    trut_NoMeomory,       // not enough memory
    trut_GLError          // OpenGL error
};

enum {
    tt_Simple, tt_Geometrical // transform type
};

class CTrUt {
protected:
    CTransform *m_p_transform;
    CGLRenderBuffer *m_p_render_buffer;
    int m_n_transform_type;
    int m_n_state, m_n_transform_state;
    int m_n_last_error;
    float m_f_texel_w, m_f_texel_h;

public:
    CTrUt();
    ~CTrUt();

    int n_GetLastError();
    int b_Ready();

    int Init(int n_transform_type, int n_source_width,
        int n_source_height, int n_transform_width, int n_transform_height,
        int (*Load_RenderPaths)(std::vector<TPath*>&) = std_Load_RenderPaths,
        int (*load_sh_function)(std::vector<CShaderInfo*>&, const char*) = std_Load_Shaders);
    // init transformation, when succesful, b_Ready() returns true (otherwise false)

    int Upload_ImageStruct(ImageStruct *p_image);
    // p_image should contain image of size n_source_width per n_source_height pixels
    // otherwise OpenGL errors may occur (depends on graphics card, opengl driver, etc)

    int Set_SimpleParams(float f_inner_r, float f_outer_r, float f_center_x, float f_center_y,
        float f_angular_offset, float f_image_ratio);
    // call for simple transform only, otherwise returns false

    int Set_GeomParams(float f_inner_r, float f_outer_r, float f_center_x, float f_center_y,
        float f_angular_offset, float f_image_ratio, float f_a2_mm, float f_b2_mm,
        float f_mirror_radius_mm, float f_delta_mm, float f_bottom_y_mm, float f_top_y_mm);
    // call for geom transform only, otherwise returns false
    // parameters followed by "_mm" can be passed in milimeters

    int Transform();
    // begin transforming image, process is CPU-independent, after the call, you can do some usefull
    // stuff and let graphics card work. function switches drawing context to offscreen buffer

    int Download_ImageStruct(ImageStruct *p_image, float f_angle, int n_y);
    // download image from current bound framebuffer. p_image is already allocated,
    // f_angle is angle in radians (fmod-ed to interval 0 .. 2Pi), specifying angle where mid
    // of the image rectangle should lie, n_y is vertical position (of top border) in raster
    // when angle is near 0 or 2Pi so image rectangle crosses left or right border of unfolded image,
    // it is wrapped over (similar to GL_REPEAT texture clamp mode), when image rectangle crosses
    // bottom or tom border of unfolded image, it's part, lying outside isn't modified

    int ReleaseFramebuffer();
    // if you want to draw to your opengl window, it's necessary to un-bind transformation's
    // off-screen framebuffer. however, it's not necessary before next Transform() so if you're
    // not going to use open-gl between transformations, you don't have to call this

    unsigned int n_TransformedTexture_OpenGL_Id();
    // return OpenGL handle of texture, containing transformed image
    // can be freely used for displaying results on screen, bud
    // must not be deleted

    int ShutDown();
    // free all open-gl resources, b_Ready() returns false

    static int std_Load_RenderPaths(std::vector<TPath*> &r_path_list);
    static int std_Load_Shaders(std::vector<CShaderInfo*> &r_shader_list);
    // here you can get standard shaders (you can also dig them out this way and rewrite
    // them to satisfy your needs)
};


CTrUt *p_tr_ut = new CTrUt;

if(!p_tr_ut->Init(tt_Simple, 1440, 1080, 1024, 1024 / Pi) ||
   !p_tr_ut->Set_SimpleParams(.01f, .4f, .5f, .5f, .0f, 1.333f)) {
    switch(p_tr_ut->n_GetLastError()) {
        ...
    }
    delete(p_tr_ut);
    exit(-1);
}
// init simple transformation, sourcing some HDTV video, outputing
// 1024 x 1024 / Pi ~ 326 pixels and set transform parameters (have
// to tweak for every individual video until mirror fits)

ImageStruct *p_people[4];

CreateFourImageStructs(&p_people, 1024 / Pi, 1024 / Pi);
// alloc four imagestructs to store images of people,
// sitting arround table (squares of size 1024 / Pi pixels)

while(!kbhit()) {
    ImageStruct *p_image = GrabFrame();
    // get an image to process

    if(!p_tr_ut->Upload_ImageStruct(p_image) ||
       !p_tr_ut->Transform()) {
        switch(p_tr_ut->n_GetLastError()) {
            ...
        }
        delete(p_tr_ut);
        exit(-1);
    }
    // upload image to graphics card (theoretical transfer rates ~4GB/s, meassured ~800Mb/s (AGP, PCI Express should be much faster),
    // that's 5 miliseconds for our 4 MB HDTV frame)
    // you can adjust parameters here by calling CTrUt::Set_SimpleParams()
    // transform an image (binds offscreen rendering buffer!), next 1.5 miliseconds (or 2.6 for geom transform)

    HandleEvents(); // do something useful at here

    for(int i = 0; i < 4; i ++) {
        if(!p_tr_ut->Download_ImageStruct(p_people(i), Pi / 2.0 * i, 0)) {
            switch(p_tr_ut->n_GetLastError()) {
                ...
            }
            delete(p_tr_ut);
            exit(-1);
        }
    }
    // download four images with four people, sitting arround the table (next 5 miliseconds, assuming we're downloading the whole transformed image, that is 11.5 - 12.6 miliseconds per frame ~ 87 - 79 frames per second)

    DoSomethingWithFourImages(&p_people);
    // put it somewhere

    if(want_draw_something_to_window_with_opengl) {
        if(!p_tr_ut->ReleaseFramebuffer()) {
            switch(p_tr_ut->n_GetLastError()) {
                ...
            }
            delete(p_tr_ut);
            exit(-1);
        }
    }
    // if you need to draw something by opengl, release offscreen rendering buffer, bound by CTrUt::Transform
}

p_tr_ut->Shutdown();
delete p_tr_ut;
// cleanup